Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce max completions over all plugins #1256

Merged
merged 8 commits into from
Jan 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions ghcide/src/Development/IDE/Plugin/Completions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import Development.IDE.GHC.Util
import Development.IDE.LSP.Server
import TcRnDriver (tcRnImportDecls)
import Data.Maybe
import Ide.Plugin.Config (Config (completionSnippetsOn, maxCompletions))
import Ide.Plugin.Config (Config (completionSnippetsOn))
import Ide.PluginUtils (getClientConfig)

#if defined(GHC_LIB)
Expand Down Expand Up @@ -146,8 +146,7 @@ getCompletionsLSP lsp ide
config <- getClientConfig lsp
let snippets = WithSnippets . completionSnippetsOn $ config
allCompletions <- getCompletions ideOpts cci' parsedMod bindMap pfix' clientCaps snippets
let (topCompletions, rest) = splitAt (maxCompletions config) allCompletions
pure $ CompletionList (CompletionListType (null rest) (List topCompletions))
pure $ Completions (List allCompletions)
_ -> return (Completions $ List [])
_ -> return (Completions $ List [])
_ -> return (Completions $ List [])
Expand Down
54 changes: 33 additions & 21 deletions ghcide/src/Development/IDE/Plugin/HLS.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ module Development.IDE.Plugin.HLS
) where

import Control.Exception(SomeException, catch)
import Control.Lens ( (^.) )
import Control.Lens ((^.))
import Control.Monad
import qualified Data.Aeson as J
import qualified Data.DList as DList
import Data.Either
import qualified Data.List as List
import qualified Data.Map as Map
Expand All @@ -33,6 +34,7 @@ import Development.Shake (Rules)
import Ide.PluginUtils (getClientConfig, pluginEnabled, getPluginConfig, responseError, getProcessID)
import Development.IDE.Types.Logger (logInfo)
import Development.IDE.Core.Tracing
import Control.Concurrent.Async (mapConcurrently)

-- ---------------------------------------------------------------------

Expand Down Expand Up @@ -97,7 +99,7 @@ makeCodeAction cas lf ideState (CodeActionParams docId range context _) = do
if pluginEnabled pluginConfig plcCodeActionsOn
then otTracedProvider pid "codeAction" $ provider lf ideState pid docId range context
else return $ Right (List [])
r <- mapM makeAction cas
r <- mapConcurrently makeAction cas
let actions = filter wasRequested . foldMap unL $ rights r
res <- send caps actions
return $ Right res
Expand Down Expand Up @@ -171,7 +173,7 @@ makeCodeLens cas lf ideState params = do
doOneRight (pid, Right a) = [(pid,a)]
doOneRight (_, Left _) = []

r <- mapM makeLens cas
r <- mapConcurrently makeLens cas
case breakdown r of
([],[]) -> return $ Right $ List []
(es,[]) -> return $ Left $ ResponseError InternalError (T.pack $ "codeLens failed:" ++ show es) Nothing
Expand Down Expand Up @@ -306,7 +308,7 @@ makeHover hps lf ideState params
if pluginEnabled pluginConfig plcHoverOn
then otTracedProvider pid "hover" $ p ideState params
else return $ Right Nothing
mhs <- mapM makeHover hps
mhs <- mapConcurrently makeHover hps
-- TODO: We should support ServerCapabilities and declare that
-- we don't support hover requests during initialization if we
-- don't have any hover providers
Expand Down Expand Up @@ -361,7 +363,7 @@ makeSymbols sps lf ideState params
if pluginEnabled pluginConfig plcSymbolsOn
then otTracedProvider pid "symbols" $ p lf ideState params
else return $ Right []
mhs <- mapM makeSymbols sps
mhs <- mapConcurrently makeSymbols sps
case rights mhs of
[] -> return $ Left $ responseError $ T.pack $ show $ lefts mhs
hs -> return $ Right $ convertSymbols $ concat hs
Expand Down Expand Up @@ -391,7 +393,7 @@ renameWith providers lspFuncs state params = do
then otTracedProvider pid "rename" $ p lspFuncs state params
else return $ Right $ WorkspaceEdit Nothing Nothing
-- TODO:AZ: we need to consider the right way to combine possible renamers
results <- mapM makeAction providers
results <- mapConcurrently makeAction providers
case partitionEithers results of
(errors, []) -> return $ Left $ responseError $ T.pack $ show errors
(_, edits) -> return $ Right $ mconcat edits
Expand Down Expand Up @@ -436,22 +438,23 @@ makeCompletions :: [(PluginId, CompletionProvider IdeState)]
makeCompletions sps lf ideState params@(CompletionParams (TextDocumentIdentifier doc) pos _context _mt)
= do
mprefix <- getPrefixAtPos lf doc pos
_snippets <- WithSnippets . completionSnippetsOn <$> getClientConfig lf
pepeiborra marked this conversation as resolved.
Show resolved Hide resolved
maxCompletions <- maxCompletions <$> getClientConfig lf

let
combine :: [CompletionResponseResult] -> CompletionResponseResult
combine cs = go (Completions $ List []) cs
where
go acc [] = acc
go (Completions (List ls)) (Completions (List ls2):rest)
= go (Completions (List (ls <> ls2))) rest
go (Completions (List ls)) (CompletionList (CompletionListType complete (List ls2)):rest)
= go (CompletionList $ CompletionListType complete (List (ls <> ls2))) rest
go (CompletionList (CompletionListType complete (List ls))) (CompletionList (CompletionListType complete2 (List ls2)):rest)
= go (CompletionList $ CompletionListType (complete || complete2) (List (ls <> ls2))) rest
go (CompletionList (CompletionListType complete (List ls))) (Completions (List ls2):rest)
= go (CompletionList $ CompletionListType complete (List (ls <> ls2))) rest
makeAction (pid,p) = do
combine cs = go True mempty cs

go !comp acc [] =
CompletionList (CompletionListType comp (List $ DList.toList acc))
go comp acc (Completions (List ls) : rest) =
go comp (acc <> DList.fromList ls) rest
go comp acc (CompletionList (CompletionListType comp' (List ls)) : rest) =
go (comp && comp') (acc <> DList.fromList ls) rest

makeAction ::
(PluginId, CompletionProvider IdeState) ->
IO (Either ResponseError CompletionResponseResult)
makeAction (pid, p) = do
pluginConfig <- getPluginConfig lf pid
if pluginEnabled pluginConfig plcCompletionOn
then otTracedProvider pid "completions" $ p lf ideState params
Expand All @@ -460,10 +463,19 @@ makeCompletions sps lf ideState params@(CompletionParams (TextDocumentIdentifier
case mprefix of
Nothing -> return $ Right $ Completions $ List []
Just _prefix -> do
mhs <- mapM makeAction sps
mhs <- mapConcurrently makeAction sps
case rights mhs of
[] -> return $ Left $ responseError $ T.pack $ show $ lefts mhs
hs -> return $ Right $ combine hs
hs -> return $ Right $ snd $ consumeCompletionResponse maxCompletions $ combine hs

-- | Crops a completion response. Returns the final number of completions and the cropped response
consumeCompletionResponse :: Int -> CompletionResponseResult -> (Int, CompletionResponseResult)
consumeCompletionResponse limit it@(CompletionList (CompletionListType _ (List xx))) =
case splitAt limit xx of
(_, []) -> (limit - length xx, it)
(xx', _) -> (0, CompletionList (CompletionListType False (List xx')))
consumeCompletionResponse n (Completions (List xx)) =
consumeCompletionResponse n (CompletionList (CompletionListType False (List xx)))

getPrefixAtPos :: LSP.LspFuncs Config -> Uri -> Position -> IO (Maybe VFS.PosPrefixInfo)
getPrefixAtPos lf uri pos = do
Expand Down
1 change: 1 addition & 0 deletions haskell-language-server.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ common moduleName
common pragmas
if flag(pragmas) || flag(all-plugins)
hs-source-dirs: plugins/default/src
build-depends: fuzzy
other-modules: Ide.Plugin.Pragmas
cpp-options: -Dpragmas

Expand Down
12 changes: 6 additions & 6 deletions plugins/default/src/Ide/Plugin/Pragmas.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
module Ide.Plugin.Pragmas
(
descriptor
-- , commands -- TODO: get rid of this
) where

import Control.Lens hiding (List)
Expand All @@ -25,7 +24,8 @@ import qualified Language.Haskell.LSP.Types.Lens as J
import Control.Monad (join)
import Development.IDE.GHC.Compat
import qualified Language.Haskell.LSP.Core as LSP
import qualified Language.Haskell.LSP.VFS as VFS
import qualified Language.Haskell.LSP.VFS as VFS
import qualified Text.Fuzzy as Fuzzy

-- ---------------------------------------------------------------------

Expand Down Expand Up @@ -142,13 +142,13 @@ completion lspFuncs _ide complParams = do
position = complParams ^. J.position
contents <- LSP.getVirtualFileFunc lspFuncs $ toNormalizedUri uri
fmap Right $ case (contents, uriToFilePath' uri) of
(Just cnts, Just _path) -> do
pfix <- VFS.getCompletionPrefix position cnts
return $ result pfix
(Just cnts, Just _path) ->
result <$> VFS.getCompletionPrefix position cnts
where
result (Just pfix)
| "{-# LANGUAGE" `T.isPrefixOf` VFS.fullLine pfix
pepeiborra marked this conversation as resolved.
Show resolved Hide resolved
= Completions $ List $ map buildCompletion allPragmas
= Completions $ List $ map buildCompletion
(Fuzzy.simpleFilter (VFS.prefixText pfix) allPragmas)
| otherwise
= Completions $ List []
result Nothing = Completions $ List []
Expand Down
12 changes: 10 additions & 2 deletions test/functional/Completion.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import Test.Tasty
import Test.Tasty.ExpectedFailure (ignoreTestBecause)
import Test.Tasty.HUnit
import qualified Data.Text as T
import Data.Default (def)
import Ide.Plugin.Config (Config (maxCompletions))

tests :: TestTree
tests = testGroup "completions" [
Expand Down Expand Up @@ -102,7 +104,7 @@ tests = testGroup "completions" [
let te = TextEdit (Range (Position 0 13) (Position 0 31)) "Str"
_ <- applyEdit doc te

compls <- getCompletions doc (Position 0 24)
compls <- getCompletions doc (Position 0 16)
let item = head $ filter ((== "Strict") . (^. label)) compls
liftIO $ do
item ^. label @?= "Strict"
Expand All @@ -116,7 +118,7 @@ tests = testGroup "completions" [
let te = TextEdit (Range (Position 0 13) (Position 0 31)) "NoOverload"
_ <- applyEdit doc te

compls <- getCompletions doc (Position 0 24)
compls <- getCompletions doc (Position 0 23)
let item = head $ filter ((== "NoOverloadedStrings") . (^. label)) compls
pepeiborra marked this conversation as resolved.
Show resolved Hide resolved
liftIO $ do
item ^. label @?= "NoOverloadedStrings"
Expand Down Expand Up @@ -221,6 +223,12 @@ tests = testGroup "completions" [
liftIO $
item ^. detail @?= Just ":: (a -> b -> c) -> b -> a -> c"

, testCase "maxCompletions" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do
doc <- openDoc "Completion.hs" "haskell"

compls <- getCompletions doc (Position 5 7)
liftIO $ length compls @?= maxCompletions def

, contextTests
, snippetTests
]
Expand Down