Skip to content

Auto complete definitions within imports #2152

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

Merged
merged 35 commits into from
Sep 9, 2021
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
47fd19d
auto complete functions from imports
alexnaspo Sep 1, 2021
34e3e7a
Merge branch 'master' into import-func-completions
alexnaspo Sep 2, 2021
31471c7
Merge branch 'master' into import-func-completions
jneira Sep 2, 2021
8b9310e
address PR comments
alexnaspo Sep 2, 2021
dcfdbfc
Merge branch 'import-func-completions' of github.com:alexnaspo/haskel…
alexnaspo Sep 2, 2021
863a483
clean up
alexnaspo Sep 2, 2021
9306c91
Merge branch 'master' into import-func-completions
jneira Sep 3, 2021
21af666
remove duplicate HashMap import
alexnaspo Sep 3, 2021
2111b5e
Merge branch 'import-func-completions' of github.com:alexnaspo/haskel…
alexnaspo Sep 3, 2021
f899d00
use lookupDefault
alexnaspo Sep 3, 2021
987119b
Merge branch 'master' into import-func-completions
alexnaspoleap Sep 3, 2021
b3a0ad7
fuzzy match filter
alexnaspoleap Sep 4, 2021
b54678d
Merge branch 'import-func-completions' of github.com:alexnaspo/haskel…
alexnaspoleap Sep 4, 2021
7a73509
add field to exportsMap
alexnaspoleap Sep 5, 2021
10919e0
generate map from modIFace
alexnaspoleap Sep 5, 2021
2c519c4
Update ghcide/src/Development/IDE/Types/Exports.hs
alexnaspo Sep 5, 2021
387e5d0
module name text alias
alexnaspoleap Sep 5, 2021
feaa4e9
use hashset; enable local modules
alexnaspoleap Sep 5, 2021
5bfff3d
local module imports now working
alexnaspoleap Sep 6, 2021
8a167a0
derive map from exportMap
alexnaspo Sep 6, 2021
4f85abc
generate maps from list
alexnaspo Sep 6, 2021
daf1915
Merge branch 'master' into import-func-completions
alexnaspo Sep 6, 2021
1c1012d
clean up
alexnaspo Sep 6, 2021
80820bb
Merge branch 'import-func-completions' of github.com:alexnaspo/haskel…
alexnaspo Sep 6, 2021
b79af6b
addressing PR comments
alexnaspo Sep 7, 2021
7454e6c
Merge branch 'master' into import-func-completions
alexnaspo Sep 7, 2021
f0e25c3
clean up
alexnaspo Sep 7, 2021
fa7763e
Merge branch 'import-func-completions' of github.com:alexnaspo/haskel…
alexnaspo Sep 7, 2021
1077480
clean up
alexnaspo Sep 7, 2021
88f2c56
Merge branch 'master' into import-func-completions
alexnaspoleap Sep 8, 2021
110ce17
useWithStaleFast
alexnaspo Sep 8, 2021
63fb733
Merge branch 'import-func-completions' of github.com:alexnaspo/haskel…
alexnaspo Sep 8, 2021
6df51ae
Merge branch 'master' into import-func-completions
pepeiborra Sep 9, 2021
440fb82
Merge branch 'master' into import-func-completions
mergify[bot] Sep 9, 2021
689b1b3
Merge branch 'master' into import-func-completions
mergify[bot] Sep 9, 2021
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
22 changes: 18 additions & 4 deletions ghcide/src/Development/IDE/Plugin/Completions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -131,18 +131,19 @@ getCompletionsLSP ide plId
fmap Right $ case (contents, uriToFilePath' uri) of
(Just cnts, Just path) -> do
let npath = toNormalizedFilePath' path
(ideOpts, compls) <- liftIO $ runIdeAction "Completion" (shakeExtras ide) $ do
(ideOpts, compls, moduleExports) <- liftIO $ runIdeAction "Completion" (shakeExtras ide) $ do
opts <- liftIO $ getIdeOptionsIO $ shakeExtras ide
localCompls <- useWithStaleFast LocalCompletions npath
nonLocalCompls <- useWithStaleFast NonLocalCompletions npath
pm <- useWithStaleFast GetParsedModule npath
binds <- fromMaybe (mempty, zeroMapping) <$> useWithStaleFast GetBindings npath
exportsMapIO <- fmap(envPackageExports . fst) <$> useWithStaleFast GhcSession npath
exportsMap <- mapM liftIO exportsMapIO
let exportsCompItems = foldMap (map (fromIdentInfo uri) . Set.toList) . Map.elems . getExportsMap <$> exportsMap
let moduleExports = buildModuleExportMap exportsMap
exportsCompItems = foldMap (map (fromIdentInfo uri) . Set.toList) . Map.elems . getExportsMap <$> exportsMap
exportsCompls = mempty{anyQualCompls = fromMaybe [] exportsCompItems}
let compls = (fst <$> localCompls) <> (fst <$> nonLocalCompls) <> Just exportsCompls
pure (opts, fmap (,pm,binds) compls)
pure (opts, fmap (,pm,binds) compls, moduleExports)
case compls of
Just (cci', parsedMod, bindMap) -> do
pfix <- VFS.getCompletionPrefix position cnts
Expand All @@ -152,14 +153,27 @@ getCompletionsLSP ide plId
(Just pfix', _) -> do
let clientCaps = clientCapabilities $ shakeExtras ide
config <- getCompletionsConfig plId
allCompletions <- liftIO $ getCompletions plId ideOpts cci' parsedMod bindMap pfix' clientCaps config
allCompletions <- liftIO $ getCompletions plId ideOpts cci' parsedMod bindMap pfix' clientCaps config moduleExports
pure $ InL (List allCompletions)
_ -> return (InL $ List [])
_ -> return (InL $ List [])
_ -> return (InL $ List [])

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

identInfoToKeyVal :: IdentInfo -> (T.Text, T.Text)
identInfoToKeyVal IdentInfo {rendered, moduleNameText} =
(moduleNameText, rendered)

buildModuleExportMap:: Maybe ExportsMap -> Map.HashMap T.Text [T.Text]
buildModuleExportMap (Just exportsMap) = do
sortAndGroup $ map identInfoToKeyVal $
concatMap (Set.toList . snd) $ toList $ getExportsMap exportsMap
buildModuleExportMap Nothing = Map.empty

sortAndGroup :: [(T.Text, T.Text)] -> Map.HashMap T.Text [T.Text]
sortAndGroup assocs = Map.fromListWith (++) [(k, [v]) | (k, v) <- assocs]

extendImportCommand :: PluginCommand IdeState
extendImportCommand =
PluginCommand (CommandId extendImportCommandId) "additional edits for a completion" extendImportHandler
Expand Down
17 changes: 16 additions & 1 deletion ghcide/src/Development/IDE/Plugin/Completions/Logic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import Control.Monad
import Data.Aeson (ToJSON (toJSON))
import Data.Either (fromRight)
import Data.Functor
import qualified Data.HashMap.Strict as HM
import qualified Data.Set as Set
import Development.IDE.Core.Compile
import Development.IDE.Core.PositionMapping
Expand Down Expand Up @@ -285,6 +286,12 @@ mkModCompl label =
Nothing Nothing Nothing Nothing Nothing Nothing Nothing
Nothing Nothing Nothing Nothing Nothing Nothing

mkModuleFunctionImport :: T.Text -> T.Text -> CompletionItem
mkModuleFunctionImport moduleName label =
CompletionItem label (Just CiFunction) Nothing (Just moduleName)
Nothing Nothing Nothing Nothing Nothing Nothing Nothing
Nothing Nothing Nothing Nothing Nothing Nothing

mkImportCompl :: T.Text -> T.Text -> CompletionItem
mkImportCompl enteredQual label =
CompletionItem m (Just CiModule) Nothing (Just label)
Expand Down Expand Up @@ -525,9 +532,10 @@ getCompletions
-> VFS.PosPrefixInfo
-> ClientCapabilities
-> CompletionsConfig
-> HM.HashMap T.Text [T.Text]
-> IO [CompletionItem]
getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qualCompls, importableModules}
maybe_parsed (localBindings, bmapping) prefixInfo caps config = do
maybe_parsed (localBindings, bmapping) prefixInfo caps config exportsMap = do
let VFS.PosPrefixInfo { fullLine, prefixModule, prefixText } = prefixInfo
enteredQual = if T.null prefixModule then "" else prefixModule <> "."
fullPrefix = enteredQual <> prefixText
Expand Down Expand Up @@ -602,6 +610,13 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu


if
| "import " `T.isPrefixOf` fullLine
&& (List.length (words (T.unpack fullLine)) >= 2)
&& "(" `isInfixOf` T.unpack fullLine
-> do
let moduleName = words (T.unpack fullLine) !! 1
funcs = HM.lookupDefault [] (T.pack moduleName) exportsMap
return (map (mkModuleFunctionImport (T.pack moduleName)) funcs)
| "import " `T.isPrefixOf` fullLine
-> return filtImportCompls
-- we leave this condition here to avoid duplications and return empty list
Expand Down
25 changes: 25 additions & 0 deletions test/functional/Completion.hs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,31 @@ tests = testGroup "completions" [
compls <- getCompletions doc (Position 5 7)
liftIO $ length compls @?= maxCompletions def

, testCase "import function completions" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do
doc <- openDoc "FunctionCompletions.hs" "haskell"

let te = TextEdit (Range (Position 0 30) (Position 0 41)) "A"
_ <- applyEdit doc te

compls <- getCompletions doc (Position 0 31)
let item = head $ filter ((== "Alternative") . (^. label)) compls
liftIO $ do
item ^. label @?= "Alternative"
item ^. kind @?= Just CiFunction
item ^. detail @?= Just "Control.Applicative"

, testCase "import second function completion" $ runSession hlsCommand fullCaps "test/testdata/completion" $ do
doc <- openDoc "FunctionCompletions.hs" "haskell"

let te = TextEdit (Range (Position 0 41) (Position 0 42)) ", l"
_ <- applyEdit doc te

compls <- getCompletions doc (Position 0 41)
let item = head $ filter ((== "liftA") . (^. label)) compls
liftIO $ do
item ^. label @?= "liftA"
item ^. kind @?= Just CiFunction
item ^. detail @?= Just "Control.Applicative"
, contextTests
, snippetTests
]
Expand Down
8 changes: 8 additions & 0 deletions test/testdata/completion/FunctionCompletions.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Control.Applicative (Alternative)
import qualified Data.List

main :: IO ()
main = putStrLn "hello"

foo :: Either a b -> Either a b
foo = id