From 47fd19d80346718891ebd616a6fdabebe6e25937 Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Wed, 1 Sep 2021 17:18:01 -0400 Subject: [PATCH 01/19] auto complete functions from imports --- .../src/Development/IDE/Plugin/Completions.hs | 25 +++++++++++++---- .../IDE/Plugin/Completions/Logic.hs | 27 ++++++++++++++++--- test/functional/Completion.hs | 25 +++++++++++++++++ .../completion/FunctionCompletions.hs | 8 ++++++ 4 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 test/testdata/completion/FunctionCompletions.hs diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 05f0b13837..989ee861f7 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -16,6 +16,7 @@ import Data.Aeson import qualified Data.HashMap.Strict as Map import qualified Data.HashSet as Set import Data.List (find) +import qualified Data.Map as DM (Map, fromListWith, empty) import Data.Maybe import qualified Data.Text as T import Development.IDE.Core.PositionMapping @@ -131,7 +132,7 @@ 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 @@ -139,12 +140,13 @@ getCompletionsLSP ide plId binds <- fromMaybe (mempty, zeroMapping) <$> useWithStaleFast GetBindings npath exportsMapIO <- fmap(envPackageExports . fst) <$> useWithStaleFast GhcSession npath exportsMap <- mapM liftIO exportsMapIO + let moduleExports = buildModouleExportMap exportsMap let 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) - case compls of - Just (cci', parsedMod, bindMap) -> do + pure (opts, fmap (,pm,binds) compls, moduleExports) + case (compls, moduleExports) of + (Just (cci', parsedMod, bindMap), mExports) -> do pfix <- VFS.getCompletionPrefix position cnts case (pfix, completionContext) of (Just (VFS.PosPrefixInfo _ "" _ _), Just CompletionContext { _triggerCharacter = Just "."}) @@ -152,7 +154,7 @@ 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 mExports pure $ InL (List allCompletions) _ -> return (InL $ List []) _ -> return (InL $ List []) @@ -160,6 +162,19 @@ getCompletionsLSP ide plId ---------------------------------------------------------------------------------------------------- +identInfoToKeyVal :: IdentInfo -> (T.Text, T.Text) +identInfoToKeyVal IdentInfo {rendered, moduleNameText} = + (moduleNameText, rendered) + +buildModouleExportMap:: Maybe (ExportsMap) -> DM.Map T.Text [T.Text] +buildModouleExportMap (Just exportsMap) = do + sortAndGroup $ map identInfoToKeyVal $ + concatMap (Set.toList . snd) $ toList $ getExportsMap exportsMap +buildModouleExportMap (Nothing) = DM.empty + +sortAndGroup :: [(T.Text, T.Text)] -> DM.Map T.Text [T.Text] +sortAndGroup assocs = DM.fromListWith (++) [(k, [v]) | (k, v) <- assocs] + extendImportCommand :: PluginCommand IdeState extendImportCommand = PluginCommand (CommandId extendImportCommandId) "additional edits for a completion" extendImportHandler diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index 5371583955..48e595827c 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -41,6 +41,7 @@ import Control.Monad import Data.Aeson (ToJSON (toJSON)) import Data.Either (fromRight) import Data.Functor +import qualified Data.Map as DM (Map) import qualified Data.Set as Set import Development.IDE.Core.Compile import Development.IDE.Core.PositionMapping @@ -54,7 +55,7 @@ import Development.IDE.Spans.LocalBindings import Development.IDE.Types.Exports import Development.IDE.Types.HscEnvEq import Development.IDE.Types.Options -import GhcPlugins (flLabel, unpackFS) +import GhcPlugins (flLabel, unpackFS, lookupWithDefaultUFM) import Ide.PluginUtils (mkLspCommand) import Ide.Types (CommandId (..), PluginId) @@ -64,6 +65,7 @@ import qualified Language.LSP.VFS as VFS import Outputable (Outputable) import TyCoRep + -- From haskell-ide-engine/hie-plugin-api/Haskell/Ide/Engine/Context.hs -- | A context of a declaration in the program @@ -285,6 +287,13 @@ 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) @@ -530,9 +539,10 @@ getCompletions -> VFS.PosPrefixInfo -> ClientCapabilities -> CompletionsConfig + -> DM.Map 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 @@ -619,9 +629,17 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu | s == c = ss | otherwise = s:ss - if + 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 = Map.findWithDefault [] (T.pack moduleName) exportsMap + return (map (mkModuleFunctionImport (T.pack moduleName)) funcs) | "import " `T.isPrefixOf` fullLine - -> return filtImportCompls + -> do + return filtImportCompls -- we leave this condition here to avoid duplications and return empty list -- since HLS implements this completion (#haskell-language-server/pull/662) | "{-# language" `T.isPrefixOf` T.toLower fullLine @@ -651,6 +669,7 @@ uniqueCompl x y = then EQ else compare (insertText x) (insertText y) other -> other + -- --------------------------------------------------------------------- -- helper functions for pragmas -- --------------------------------------------------------------------- diff --git a/test/functional/Completion.hs b/test/functional/Completion.hs index 2585a1e00d..94faa05f1e 100644 --- a/test/functional/Completion.hs +++ b/test/functional/Completion.hs @@ -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 ] diff --git a/test/testdata/completion/FunctionCompletions.hs b/test/testdata/completion/FunctionCompletions.hs new file mode 100644 index 0000000000..eeda925498 --- /dev/null +++ b/test/testdata/completion/FunctionCompletions.hs @@ -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 From 8b9310eee114605a5c3753a8417879b4af096a49 Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Thu, 2 Sep 2021 14:43:55 -0400 Subject: [PATCH 02/19] address PR comments --- .../src/Development/IDE/Plugin/Completions.hs | 22 +++++++++---------- .../IDE/Plugin/Completions/Logic.hs | 8 +++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 989ee861f7..2ed6b842db 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -16,7 +16,7 @@ import Data.Aeson import qualified Data.HashMap.Strict as Map import qualified Data.HashSet as Set import Data.List (find) -import qualified Data.Map as DM (Map, fromListWith, empty) +import qualified Data.HashMap.Strict as HM import Data.Maybe import qualified Data.Text as T import Development.IDE.Core.PositionMapping @@ -140,13 +140,13 @@ getCompletionsLSP ide plId binds <- fromMaybe (mempty, zeroMapping) <$> useWithStaleFast GetBindings npath exportsMapIO <- fmap(envPackageExports . fst) <$> useWithStaleFast GhcSession npath exportsMap <- mapM liftIO exportsMapIO - let moduleExports = buildModouleExportMap exportsMap - 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, moduleExports) - case (compls, moduleExports) of - (Just (cci', parsedMod, bindMap), mExports) -> do + case compls of + (Just (cci', parsedMod, bindMap)) -> do pfix <- VFS.getCompletionPrefix position cnts case (pfix, completionContext) of (Just (VFS.PosPrefixInfo _ "" _ _), Just CompletionContext { _triggerCharacter = Just "."}) @@ -154,7 +154,7 @@ 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 mExports + allCompletions <- liftIO $ getCompletions plId ideOpts cci' parsedMod bindMap pfix' clientCaps config moduleExports pure $ InL (List allCompletions) _ -> return (InL $ List []) _ -> return (InL $ List []) @@ -166,14 +166,14 @@ identInfoToKeyVal :: IdentInfo -> (T.Text, T.Text) identInfoToKeyVal IdentInfo {rendered, moduleNameText} = (moduleNameText, rendered) -buildModouleExportMap:: Maybe (ExportsMap) -> DM.Map T.Text [T.Text] -buildModouleExportMap (Just exportsMap) = do +buildModuleExportMap:: Maybe ExportsMap -> HM.HashMap T.Text [T.Text] +buildModuleExportMap (Just exportsMap) = do sortAndGroup $ map identInfoToKeyVal $ concatMap (Set.toList . snd) $ toList $ getExportsMap exportsMap -buildModouleExportMap (Nothing) = DM.empty +buildModuleExportMap Nothing = HM.empty -sortAndGroup :: [(T.Text, T.Text)] -> DM.Map T.Text [T.Text] -sortAndGroup assocs = DM.fromListWith (++) [(k, [v]) | (k, v) <- assocs] +sortAndGroup :: [(T.Text, T.Text)] -> HM.HashMap T.Text [T.Text] +sortAndGroup assocs = HM.fromListWith (++) [(k, [v]) | (k, v) <- assocs] extendImportCommand :: PluginCommand IdeState extendImportCommand = diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index 48e595827c..790cd434f6 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -41,7 +41,7 @@ import Control.Monad import Data.Aeson (ToJSON (toJSON)) import Data.Either (fromRight) import Data.Functor -import qualified Data.Map as DM (Map) +import qualified Data.HashMap.Strict as HM import qualified Data.Set as Set import Development.IDE.Core.Compile import Development.IDE.Core.PositionMapping @@ -539,7 +539,7 @@ getCompletions -> VFS.PosPrefixInfo -> ClientCapabilities -> CompletionsConfig - -> DM.Map T.Text [T.Text] + -> HM.HashMap T.Text [T.Text] -> IO [CompletionItem] getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qualCompls, importableModules} maybe_parsed (localBindings, bmapping) prefixInfo caps config exportsMap = do @@ -634,8 +634,8 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu && (List.length (words (T.unpack fullLine)) >= 2) && "(" `isInfixOf` T.unpack fullLine -> do - let moduleName = (words (T.unpack fullLine)) !! 1 - funcs = Map.findWithDefault [] (T.pack moduleName) exportsMap + let moduleName = words (T.unpack fullLine) !! 1 + funcs = HM.findWithDefault [] (T.pack moduleName) exportsMap return (map (mkModuleFunctionImport (T.pack moduleName)) funcs) | "import " `T.isPrefixOf` fullLine -> do From 863a48337c900df61d081b0be9dbf6f09bb8623b Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Thu, 2 Sep 2021 14:50:36 -0400 Subject: [PATCH 03/19] clean up --- ghcide/src/Development/IDE/Plugin/Completions.hs | 2 +- ghcide/src/Development/IDE/Plugin/Completions/Logic.hs | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 2ed6b842db..7000724234 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -146,7 +146,7 @@ getCompletionsLSP ide plId let compls = (fst <$> localCompls) <> (fst <$> nonLocalCompls) <> Just exportsCompls pure (opts, fmap (,pm,binds) compls, moduleExports) case compls of - (Just (cci', parsedMod, bindMap)) -> do + Just (cci', parsedMod, bindMap) -> do pfix <- VFS.getCompletionPrefix position cnts case (pfix, completionContext) of (Just (VFS.PosPrefixInfo _ "" _ _), Just CompletionContext { _triggerCharacter = Just "."}) diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index 790cd434f6..a4ae01c7ea 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -55,7 +55,7 @@ import Development.IDE.Spans.LocalBindings import Development.IDE.Types.Exports import Development.IDE.Types.HscEnvEq import Development.IDE.Types.Options -import GhcPlugins (flLabel, unpackFS, lookupWithDefaultUFM) +import GhcPlugins (flLabel, unpackFS) import Ide.PluginUtils (mkLspCommand) import Ide.Types (CommandId (..), PluginId) @@ -65,7 +65,6 @@ import qualified Language.LSP.VFS as VFS import Outputable (Outputable) import TyCoRep - -- From haskell-ide-engine/hie-plugin-api/Haskell/Ide/Engine/Context.hs -- | A context of a declaration in the program @@ -287,7 +286,6 @@ 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) @@ -629,7 +627,7 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu | s == c = ss | otherwise = s:ss - if + if | "import " `T.isPrefixOf` fullLine && (List.length (words (T.unpack fullLine)) >= 2) && "(" `isInfixOf` T.unpack fullLine @@ -638,8 +636,7 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu funcs = HM.findWithDefault [] (T.pack moduleName) exportsMap return (map (mkModuleFunctionImport (T.pack moduleName)) funcs) | "import " `T.isPrefixOf` fullLine - -> do - return filtImportCompls + -> return filtImportCompls -- we leave this condition here to avoid duplications and return empty list -- since HLS implements this completion (#haskell-language-server/pull/662) | "{-# language" `T.isPrefixOf` T.toLower fullLine @@ -669,7 +666,6 @@ uniqueCompl x y = then EQ else compare (insertText x) (insertText y) other -> other - -- --------------------------------------------------------------------- -- helper functions for pragmas -- --------------------------------------------------------------------- From 21af666d91c4f553886f417586b2e7c1a0c6e2cc Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Fri, 3 Sep 2021 06:56:45 -0400 Subject: [PATCH 04/19] remove duplicate HashMap import --- ghcide/src/Development/IDE/Plugin/Completions.hs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 7000724234..2037c53859 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -16,7 +16,6 @@ import Data.Aeson import qualified Data.HashMap.Strict as Map import qualified Data.HashSet as Set import Data.List (find) -import qualified Data.HashMap.Strict as HM import Data.Maybe import qualified Data.Text as T import Development.IDE.Core.PositionMapping @@ -166,14 +165,14 @@ identInfoToKeyVal :: IdentInfo -> (T.Text, T.Text) identInfoToKeyVal IdentInfo {rendered, moduleNameText} = (moduleNameText, rendered) -buildModuleExportMap:: Maybe ExportsMap -> HM.HashMap T.Text [T.Text] +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 = HM.empty +buildModuleExportMap Nothing = Map.empty -sortAndGroup :: [(T.Text, T.Text)] -> HM.HashMap T.Text [T.Text] -sortAndGroup assocs = HM.fromListWith (++) [(k, [v]) | (k, v) <- assocs] +sortAndGroup :: [(T.Text, T.Text)] -> Map.HashMap T.Text [T.Text] +sortAndGroup assocs = Map.fromListWith (++) [(k, [v]) | (k, v) <- assocs] extendImportCommand :: PluginCommand IdeState extendImportCommand = From f899d005ae40aa957ccc0bc097799d7eade77ba2 Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Fri, 3 Sep 2021 07:14:51 -0400 Subject: [PATCH 05/19] use lookupDefault --- ghcide/src/Development/IDE/Plugin/Completions/Logic.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index a4ae01c7ea..e9f8c316c7 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -633,7 +633,7 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu && "(" `isInfixOf` T.unpack fullLine -> do let moduleName = words (T.unpack fullLine) !! 1 - funcs = HM.findWithDefault [] (T.pack moduleName) exportsMap + funcs = HM.lookupDefault [] (T.pack moduleName) exportsMap return (map (mkModuleFunctionImport (T.pack moduleName)) funcs) | "import " `T.isPrefixOf` fullLine -> return filtImportCompls From b3a0ad72282c1ff1537c5361ad55c99bab43302b Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Fri, 3 Sep 2021 20:56:25 -0400 Subject: [PATCH 06/19] fuzzy match filter --- ghcide/src/Development/IDE/Plugin/Completions/Logic.hs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index e9f8c316c7..37712ad263 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -615,6 +615,7 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu ] filtImportCompls = filtListWith (mkImportCompl enteredQual) importableModules + filterModuleExports moduleName = filtListWith $mkModuleFunctionImport moduleName filtPragmaCompls = filtListWithSnippet mkPragmaCompl validPragmas filtOptsCompls = filtListWith mkExtCompl filtKeywordCompls @@ -631,10 +632,10 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu | "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) + -> do + let moduleName = T.pack $ words (T.unpack fullLine) !! 1 + funcs = HM.lookupDefault [] moduleName exportsMap + return $ filterModuleExports moduleName funcs | "import " `T.isPrefixOf` fullLine -> return filtImportCompls -- we leave this condition here to avoid duplications and return empty list From 7a7350940f23ed79027c0f0377f8c05d3b238c85 Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Sun, 5 Sep 2021 12:33:30 -0400 Subject: [PATCH 07/19] add field to exportsMap --- .../src/Development/IDE/Plugin/Completions.hs | 15 +---- ghcide/src/Development/IDE/Types/Exports.hs | 61 +++++++++++++------ 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 2037c53859..c4b85dc6ce 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -139,7 +139,7 @@ getCompletionsLSP ide plId binds <- fromMaybe (mempty, zeroMapping) <$> useWithStaleFast GetBindings npath exportsMapIO <- fmap(envPackageExports . fst) <$> useWithStaleFast GhcSession npath exportsMap <- mapM liftIO exportsMapIO - let moduleExports = buildModuleExportMap exportsMap + let moduleExports = maybe Map.empty getModuleExportsMap 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 @@ -161,19 +161,6 @@ getCompletionsLSP ide plId ---------------------------------------------------------------------------------------------------- -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 diff --git a/ghcide/src/Development/IDE/Types/Exports.hs b/ghcide/src/Development/IDE/Types/Exports.hs index 36594d2b56..8a7e1932da 100644 --- a/ghcide/src/Development/IDE/Types/Exports.hs +++ b/ghcide/src/Development/IDE/Types/Exports.hs @@ -30,15 +30,20 @@ import HieDb import Name import TcRnTypes (TcGblEnv (..)) -newtype ExportsMap = ExportsMap - {getExportsMap :: HashMap IdentifierText (HashSet IdentInfo)} - deriving newtype (Monoid, NFData, Show) +data ExportsMap = ExportsMap + {getExportsMap :: HashMap IdentifierText (HashSet IdentInfo) + , getModuleExportsMap :: Map.HashMap IdentifierText [Text] + } + deriving (Show) size :: ExportsMap -> Int size = sum . map length . elems . getExportsMap instance Semigroup ExportsMap where - ExportsMap a <> ExportsMap b = ExportsMap $ Map.unionWith (<>) a b + ExportsMap a b <> ExportsMap c d = ExportsMap (Map.unionWith (<>) a c) (Map.unionWith (<>) b d) + +instance Monoid ExportsMap where + mempty = ExportsMap Map.empty Map.empty type IdentifierText = Text @@ -90,26 +95,47 @@ mkIdentInfos mod (AvailTC _ nn flds) | n <- nn ++ map flSelector flds ] + +identInfoToKeyVal :: IdentInfo -> (Text, Text) +identInfoToKeyVal IdentInfo {rendered, moduleNameText} = + (moduleNameText, rendered) + +buildModuleExportMap:: HashMap IdentifierText (HashSet IdentInfo) -> Map.HashMap Text [Text] +buildModuleExportMap exportsMap = do + sortAndGroup $ map identInfoToKeyVal $ + concatMap (Set.toList . snd) $ Map.toList exportsMap + +sortAndGroup :: [(Text, Text)] -> Map.HashMap Text [Text] +sortAndGroup assocs = Map.fromListWith (++) [(k, [v]) | (k, v) <- assocs] + +-- reworked to enable second map +-- how do I create mod-name -> func? createExportsMap :: [ModIface] -> ExportsMap -createExportsMap = ExportsMap . Map.fromListWith (<>) . concatMap doOne +createExportsMap modIface = do + let exportsMap = (Map.fromListWith (<>) . concatMap doOne) modIface + ExportsMap exportsMap $ buildModuleExportMap exportsMap where - doOne mi = concatMap (fmap (second Set.fromList) . unpackAvail mn) (mi_exports mi) - where - mn = moduleName $ mi_module mi + doOne mi = do + let getModDetails = unpackAvail $ moduleName $ mi_module mi + concatMap (fmap (second Set.fromList) . getModDetails) (mi_exports mi) createExportsMapMg :: [ModGuts] -> ExportsMap -createExportsMapMg = ExportsMap . Map.fromListWith (<>) . concatMap doOne +createExportsMapMg modIface = do + let exportsMap = (Map.fromListWith (<>) . concatMap doOne) modIface + ExportsMap exportsMap $ buildModuleExportMap exportsMap where - doOne mi = concatMap (fmap (second Set.fromList) . unpackAvail mn) (mg_exports mi) - where - mn = moduleName $ mg_module mi + doOne mi = do + let getModuleName = moduleName $ mg_module mi + concatMap (fmap (second Set.fromList) . unpackAvail getModuleName) (mg_exports mi) createExportsMapTc :: [TcGblEnv] -> ExportsMap -createExportsMapTc = ExportsMap . Map.fromListWith (<>) . concatMap doOne +createExportsMapTc modIface = do + let exportsMap = (Map.fromListWith (<>) . concatMap doOne) modIface + ExportsMap exportsMap $ buildModuleExportMap exportsMap where - doOne mi = concatMap (fmap (second Set.fromList) . unpackAvail mn) (tcg_exports mi) - where - mn = moduleName $ tcg_mod mi + doOne mi = do + let getModuleName = moduleName $ tcg_mod mi + concatMap (fmap (second Set.fromList) . unpackAvail getModuleName) (tcg_exports mi) nonInternalModules :: ModuleName -> Bool nonInternalModules = not . (".Internal" `isSuffixOf`) . moduleNameString @@ -121,7 +147,8 @@ createExportsMapHieDb hiedb = do let mn = modInfoName $ hieModInfo m mText = pack $ moduleNameString mn fmap (wrap . unwrap mText) <$> getExportsForModule hiedb mn - return $ ExportsMap $ Map.fromListWith (<>) (concat idents) + let exportsMap = Map.fromListWith (<>) (concat idents) + return $ ExportsMap exportsMap $ buildModuleExportMap exportsMap where wrap identInfo = (rendered identInfo, Set.fromList [identInfo]) -- unwrap :: ExportRow -> IdentInfo From 10919e0c26b733f3a77f3c913e903417be7902a2 Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Sun, 5 Sep 2021 14:29:24 -0400 Subject: [PATCH 08/19] generate map from modIFace --- ghcide/src/Development/IDE/Types/Exports.hs | 35 ++++++++++----------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/ghcide/src/Development/IDE/Types/Exports.hs b/ghcide/src/Development/IDE/Types/Exports.hs index 8a7e1932da..118eac8c90 100644 --- a/ghcide/src/Development/IDE/Types/Exports.hs +++ b/ghcide/src/Development/IDE/Types/Exports.hs @@ -9,7 +9,7 @@ module Development.IDE.Types.Exports createExportsMapTc ,createExportsMapHieDb,size) where -import Avail (AvailInfo (..)) +import Avail (AvailInfo (..), availName) import Control.DeepSeq (NFData (..)) import Control.Monad import Data.Bifunctor (Bifunctor (second)) @@ -30,6 +30,7 @@ import HieDb import Name import TcRnTypes (TcGblEnv (..)) + data ExportsMap = ExportsMap {getExportsMap :: HashMap IdentifierText (HashSet IdentInfo) , getModuleExportsMap :: Map.HashMap IdentifierText [Text] @@ -95,25 +96,23 @@ mkIdentInfos mod (AvailTC _ nn flds) | n <- nn ++ map flSelector flds ] +buildModuleExportMap:: [ModIface] -> Map.HashMap Text [Text] +buildModuleExportMap modIfaces = do + let exports = map extractModuleExports modIfaces + Map.fromListWith (<>) exports -identInfoToKeyVal :: IdentInfo -> (Text, Text) -identInfoToKeyVal IdentInfo {rendered, moduleNameText} = - (moduleNameText, rendered) - -buildModuleExportMap:: HashMap IdentifierText (HashSet IdentInfo) -> Map.HashMap Text [Text] -buildModuleExportMap exportsMap = do - sortAndGroup $ map identInfoToKeyVal $ - concatMap (Set.toList . snd) $ Map.toList exportsMap - -sortAndGroup :: [(Text, Text)] -> Map.HashMap Text [Text] -sortAndGroup assocs = Map.fromListWith (++) [(k, [v]) | (k, v) <- assocs] +extractModuleExports :: ModIface -> (Text, [Text]) +extractModuleExports modIFace = do + let modName = pack $ moduleNameString $ moduleName $ mi_module modIFace + let ifaces = mi_exports modIFace + let functionSet = map (pack . getOccString . availName) ifaces + (modName, functionSet) --- reworked to enable second map --- how do I create mod-name -> func? createExportsMap :: [ModIface] -> ExportsMap createExportsMap modIface = do let exportsMap = (Map.fromListWith (<>) . concatMap doOne) modIface - ExportsMap exportsMap $ buildModuleExportMap exportsMap + moduleExportMap = buildModuleExportMap modIface + ExportsMap exportsMap moduleExportMap where doOne mi = do let getModDetails = unpackAvail $ moduleName $ mi_module mi @@ -122,7 +121,7 @@ createExportsMap modIface = do createExportsMapMg :: [ModGuts] -> ExportsMap createExportsMapMg modIface = do let exportsMap = (Map.fromListWith (<>) . concatMap doOne) modIface - ExportsMap exportsMap $ buildModuleExportMap exportsMap + ExportsMap exportsMap Map.empty where doOne mi = do let getModuleName = moduleName $ mg_module mi @@ -131,7 +130,7 @@ createExportsMapMg modIface = do createExportsMapTc :: [TcGblEnv] -> ExportsMap createExportsMapTc modIface = do let exportsMap = (Map.fromListWith (<>) . concatMap doOne) modIface - ExportsMap exportsMap $ buildModuleExportMap exportsMap + ExportsMap exportsMap Map.empty where doOne mi = do let getModuleName = moduleName $ tcg_mod mi @@ -148,7 +147,7 @@ createExportsMapHieDb hiedb = do mText = pack $ moduleNameString mn fmap (wrap . unwrap mText) <$> getExportsForModule hiedb mn let exportsMap = Map.fromListWith (<>) (concat idents) - return $ ExportsMap exportsMap $ buildModuleExportMap exportsMap + return $ ExportsMap exportsMap Map.empty where wrap identInfo = (rendered identInfo, Set.fromList [identInfo]) -- unwrap :: ExportRow -> IdentInfo From 2c519c4a8d5dad70d7545ea1a2f9d387b660aa69 Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Sun, 5 Sep 2021 14:32:33 -0400 Subject: [PATCH 09/19] Update ghcide/src/Development/IDE/Types/Exports.hs Co-authored-by: Pepe Iborra --- ghcide/src/Development/IDE/Types/Exports.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Types/Exports.hs b/ghcide/src/Development/IDE/Types/Exports.hs index 118eac8c90..4a42dff0ae 100644 --- a/ghcide/src/Development/IDE/Types/Exports.hs +++ b/ghcide/src/Development/IDE/Types/Exports.hs @@ -33,7 +33,7 @@ import TcRnTypes (TcGblEnv (..)) data ExportsMap = ExportsMap {getExportsMap :: HashMap IdentifierText (HashSet IdentInfo) - , getModuleExportsMap :: Map.HashMap IdentifierText [Text] + , getModuleExportsMap :: Map.HashMap ModuleNameText [Text] } deriving (Show) From 387e5d07e6465c9ee0aa22d89226f55db104a9fe Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Sun, 5 Sep 2021 14:37:32 -0400 Subject: [PATCH 10/19] module name text alias --- ghcide/src/Development/IDE/Types/Exports.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Types/Exports.hs b/ghcide/src/Development/IDE/Types/Exports.hs index 4a42dff0ae..791b65b37a 100644 --- a/ghcide/src/Development/IDE/Types/Exports.hs +++ b/ghcide/src/Development/IDE/Types/Exports.hs @@ -22,7 +22,7 @@ import Data.List (isSuffixOf) import Data.Text (Text, pack) import Development.IDE.GHC.Compat import Development.IDE.GHC.Orphans () -import Development.IDE.GHC.Util +import Development.IDE.GHC.Util import FieldLabel (flSelector) import GHC.Generics (Generic) import GhcPlugins (IfaceExport, ModGuts (..)) @@ -47,6 +47,7 @@ instance Monoid ExportsMap where mempty = ExportsMap Map.empty Map.empty type IdentifierText = Text +type ModuleNameText = Text data IdentInfo = IdentInfo { name :: !OccName From feaa4e90d58f5a129e08b1f67ca286f868de17e9 Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Sun, 5 Sep 2021 15:45:37 -0400 Subject: [PATCH 11/19] use hashset; enable local modules --- .../src/Development/IDE/Plugin/Completions/Logic.hs | 10 ++++++---- ghcide/src/Development/IDE/Types/Exports.hs | 11 +++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index 7911911b2f..35ef1528f1 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -43,6 +43,7 @@ import Data.Either (fromRight) import Data.Functor import qualified Data.HashMap.Strict as HM import qualified Data.Set as Set +import qualified Data.HashSet as HashSet import Development.IDE.Core.Compile import Development.IDE.Core.PositionMapping import Development.IDE.GHC.Compat as GHC @@ -532,10 +533,10 @@ getCompletions -> VFS.PosPrefixInfo -> ClientCapabilities -> CompletionsConfig - -> HM.HashMap T.Text [T.Text] + -> HM.HashMap T.Text (HashSet.HashSet IdentInfo) -> IO [CompletionItem] getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qualCompls, importableModules} - maybe_parsed (localBindings, bmapping) prefixInfo caps config exportsMap = do + maybe_parsed (localBindings, bmapping) prefixInfo caps config moduleExportsMap = do let VFS.PosPrefixInfo { fullLine, prefixModule, prefixText } = prefixInfo enteredQual = if T.null prefixModule then "" else prefixModule <> "." fullPrefix = enteredQual <> prefixText @@ -616,8 +617,9 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu && "(" `isInfixOf` T.unpack fullLine -> do let moduleName = T.pack $ words (T.unpack fullLine) !! 1 - funcs = HM.lookupDefault [] moduleName exportsMap - return $ filterModuleExports moduleName funcs + funcs = HM.lookupDefault HashSet.empty moduleName moduleExportsMap + funs = map (show . name) $ HashSet.toList funcs + return $ filterModuleExports moduleName $ map T.pack funs | "import " `T.isPrefixOf` fullLine -> return filtImportCompls -- we leave this condition here to avoid duplications and return empty list diff --git a/ghcide/src/Development/IDE/Types/Exports.hs b/ghcide/src/Development/IDE/Types/Exports.hs index 791b65b37a..6c45535ec3 100644 --- a/ghcide/src/Development/IDE/Types/Exports.hs +++ b/ghcide/src/Development/IDE/Types/Exports.hs @@ -9,7 +9,7 @@ module Development.IDE.Types.Exports createExportsMapTc ,createExportsMapHieDb,size) where -import Avail (AvailInfo (..), availName) +import Avail (AvailInfo (..)) import Control.DeepSeq (NFData (..)) import Control.Monad import Data.Bifunctor (Bifunctor (second)) @@ -33,7 +33,7 @@ import TcRnTypes (TcGblEnv (..)) data ExportsMap = ExportsMap {getExportsMap :: HashMap IdentifierText (HashSet IdentInfo) - , getModuleExportsMap :: Map.HashMap ModuleNameText [Text] + , getModuleExportsMap :: Map.HashMap ModuleNameText (HashSet IdentInfo) } deriving (Show) @@ -97,16 +97,15 @@ mkIdentInfos mod (AvailTC _ nn flds) | n <- nn ++ map flSelector flds ] -buildModuleExportMap:: [ModIface] -> Map.HashMap Text [Text] +buildModuleExportMap:: [ModIface] -> Map.HashMap Text (HashSet IdentInfo) buildModuleExportMap modIfaces = do let exports = map extractModuleExports modIfaces Map.fromListWith (<>) exports -extractModuleExports :: ModIface -> (Text, [Text]) +extractModuleExports :: ModIface -> (Text, HashSet IdentInfo) extractModuleExports modIFace = do let modName = pack $ moduleNameString $ moduleName $ mi_module modIFace - let ifaces = mi_exports modIFace - let functionSet = map (pack . getOccString . availName) ifaces + let functionSet = Set.fromList $ concatMap (mkIdentInfos modName) $ mi_exports modIFace (modName, functionSet) createExportsMap :: [ModIface] -> ExportsMap From 5bfff3d805ba3731eb4f98dcec62cded8349bed0 Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Sun, 5 Sep 2021 23:46:45 -0400 Subject: [PATCH 12/19] local module imports now working --- ghcide/src/Development/IDE/Core/Shake.hs | 3 +-- .../src/Development/IDE/Plugin/Completions.hs | 24 +++++++++++++++++-- .../IDE/Plugin/Completions/Logic.hs | 4 +++- ghcide/src/Development/IDE/Types/Exports.hs | 3 ++- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/ghcide/src/Development/IDE/Core/Shake.hs b/ghcide/src/Development/IDE/Core/Shake.hs index 5d6607a5da..37bfa9dc6a 100644 --- a/ghcide/src/Development/IDE/Core/Shake.hs +++ b/ghcide/src/Development/IDE/Core/Shake.hs @@ -109,8 +109,7 @@ import Development.IDE.Core.PositionMapping import Development.IDE.Core.ProgressReporting import Development.IDE.Core.RuleTypes import Development.IDE.Core.Tracing -import Development.IDE.GHC.Compat (NameCacheUpdater (..), - upNameCache) +import Development.IDE.GHC.Compat (NameCacheUpdater (..), upNameCache) import Development.IDE.GHC.Orphans () import Development.IDE.Graph hiding (ShakeValue) import qualified Development.IDE.Graph as Shake diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index c4b85dc6ce..9fff3de7dd 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -30,6 +30,7 @@ import Development.IDE.GHC.ExactPrint (Annotated (annsA) import Development.IDE.GHC.Util (prettyPrint) import Development.IDE.Graph import Development.IDE.Graph.Classes +import Development.IDE.Import.FindImports import Development.IDE.Plugin.CodeAction (newImport, newImportToEdit) import Development.IDE.Plugin.CodeAction.ExactPrint @@ -50,6 +51,8 @@ import qualified Language.LSP.VFS as VFS import GHC.Tc.Module (tcRnImportDecls) #else import TcRnDriver (tcRnImportDecls) + + #endif descriptor :: PluginId -> PluginDescriptor IdeState @@ -129,7 +132,7 @@ getCompletionsLSP ide plId ,_context=completionContext} = do contents <- LSP.getVirtualFile $ toNormalizedUri uri fmap Right $ case (contents, uriToFilePath' uri) of - (Just cnts, Just path) -> do + (Just cnts, Just path) -> do let npath = toNormalizedFilePath' path (ideOpts, compls, moduleExports) <- liftIO $ runIdeAction "Completion" (shakeExtras ide) $ do opts <- liftIO $ getIdeOptionsIO $ shakeExtras ide @@ -139,11 +142,13 @@ getCompletionsLSP ide plId binds <- fromMaybe (mempty, zeroMapping) <$> useWithStaleFast GetBindings npath exportsMapIO <- fmap(envPackageExports . fst) <$> useWithStaleFast GhcSession npath exportsMap <- mapM liftIO exportsMapIO + locatedImports <- fromMaybe (mempty, zeroMapping) <$> useWithStaleFast GetLocatedImports npath + localModuleExports <- liftIO $ buildLocalModuleExports ide locatedImports let moduleExports = maybe Map.empty getModuleExportsMap 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, moduleExports) + pure (opts, fmap (,pm,binds) compls, Map.unionWith (<>) localModuleExports moduleExports) case compls of Just (cci', parsedMod, bindMap) -> do pfix <- VFS.getCompletionPrefix position cnts @@ -160,6 +165,21 @@ getCompletionsLSP ide plId _ -> return (InL $ List []) ---------------------------------------------------------------------------------------------------- + +buildLocalModuleExports:: IdeState -> ([(Located ModuleName, Maybe ArtifactsLocation)], PositionMapping) -> IO (Map.HashMap T.Text (Set.HashSet IdentInfo)) +buildLocalModuleExports ide inMap = do + mModIfaces <- mapM (getModIface ide) $ fst inMap + pure (buildModuleExportMap $ catMaybes mModIfaces) + +getModIface :: IdeState -> (Located ModuleName, Maybe ArtifactsLocation) -> IO (Maybe ModIface) +getModIface ide item = do + let nPath = artifactFilePath <$> snd item + case nPath of + (Just path) -> do + file <- runIdeAction "Completion" (shakeExtras ide) $ do + fmap fst <$> useWithStaleFast GetModIface path + pure $ fmap hirModIface file + Nothing -> pure Nothing extendImportCommand :: PluginCommand IdeState extendImportCommand = diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index 35ef1528f1..140513d999 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -50,7 +50,7 @@ import Development.IDE.GHC.Compat as GHC import Development.IDE.GHC.Error import Development.IDE.GHC.Util import Development.IDE.Plugin.Completions.Types -import Development.IDE.Spans.Common +import Development.IDE.Spans.Common import Development.IDE.Spans.Documentation import Development.IDE.Spans.LocalBindings import Development.IDE.Types.Exports @@ -65,6 +65,7 @@ import Language.LSP.Types.Capabilities import qualified Language.LSP.VFS as VFS import Outputable (Outputable) import TyCoRep +import Development.IDE.Core.RuleTypes -- From haskell-ide-engine/hie-plugin-api/Haskell/Ide/Engine/Context.hs @@ -619,6 +620,7 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu let moduleName = T.pack $ words (T.unpack fullLine) !! 1 funcs = HM.lookupDefault HashSet.empty moduleName moduleExportsMap funs = map (show . name) $ HashSet.toList funcs + hello = GetLocatedImports return $ filterModuleExports moduleName $ map T.pack funs | "import " `T.isPrefixOf` fullLine -> return filtImportCompls diff --git a/ghcide/src/Development/IDE/Types/Exports.hs b/ghcide/src/Development/IDE/Types/Exports.hs index 6c45535ec3..fe2415f90a 100644 --- a/ghcide/src/Development/IDE/Types/Exports.hs +++ b/ghcide/src/Development/IDE/Types/Exports.hs @@ -6,7 +6,8 @@ module Development.IDE.Types.Exports ExportsMap(..), createExportsMap, createExportsMapMg, - createExportsMapTc + createExportsMapTc, + buildModuleExportMap ,createExportsMapHieDb,size) where import Avail (AvailInfo (..)) From 8a167a08bc2c5e5223b8a5557d5504fb235b9f30 Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Mon, 6 Sep 2021 12:55:08 -0400 Subject: [PATCH 13/19] derive map from exportMap --- .../src/Development/IDE/Plugin/Completions.hs | 2 +- ghcide/src/Development/IDE/Types/Exports.hs | 57 ++++++++++++------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 9fff3de7dd..798a6b543f 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -169,7 +169,7 @@ getCompletionsLSP ide plId buildLocalModuleExports:: IdeState -> ([(Located ModuleName, Maybe ArtifactsLocation)], PositionMapping) -> IO (Map.HashMap T.Text (Set.HashSet IdentInfo)) buildLocalModuleExports ide inMap = do mModIfaces <- mapM (getModIface ide) $ fst inMap - pure (buildModuleExportMap $ catMaybes mModIfaces) + pure (buildModuleExportMapFrom $ catMaybes mModIfaces) getModIface :: IdeState -> (Located ModuleName, Maybe ArtifactsLocation) -> IO (Maybe ModIface) getModIface ide item = do diff --git a/ghcide/src/Development/IDE/Types/Exports.hs b/ghcide/src/Development/IDE/Types/Exports.hs index fe2415f90a..0246578acd 100644 --- a/ghcide/src/Development/IDE/Types/Exports.hs +++ b/ghcide/src/Development/IDE/Types/Exports.hs @@ -7,7 +7,7 @@ module Development.IDE.Types.Exports createExportsMap, createExportsMapMg, createExportsMapTc, - buildModuleExportMap + buildModuleExportMapFrom ,createExportsMapHieDb,size) where import Avail (AvailInfo (..)) @@ -98,31 +98,19 @@ mkIdentInfos mod (AvailTC _ nn flds) | n <- nn ++ map flSelector flds ] -buildModuleExportMap:: [ModIface] -> Map.HashMap Text (HashSet IdentInfo) -buildModuleExportMap modIfaces = do - let exports = map extractModuleExports modIfaces - Map.fromListWith (<>) exports - -extractModuleExports :: ModIface -> (Text, HashSet IdentInfo) -extractModuleExports modIFace = do - let modName = pack $ moduleNameString $ moduleName $ mi_module modIFace - let functionSet = Set.fromList $ concatMap (mkIdentInfos modName) $ mi_exports modIFace - (modName, functionSet) - createExportsMap :: [ModIface] -> ExportsMap createExportsMap modIface = do let exportsMap = (Map.fromListWith (<>) . concatMap doOne) modIface - moduleExportMap = buildModuleExportMap modIface - ExportsMap exportsMap moduleExportMap + ExportsMap exportsMap $ buildModuleExportMap exportsMap where - doOne mi = do - let getModDetails = unpackAvail $ moduleName $ mi_module mi - concatMap (fmap (second Set.fromList) . getModDetails) (mi_exports mi) + doOne modIFace = do + let getModDetails = unpackAvail $ moduleName $ mi_module modIFace + concatMap (fmap (second Set.fromList) . getModDetails) (mi_exports modIFace) createExportsMapMg :: [ModGuts] -> ExportsMap -createExportsMapMg modIface = do - let exportsMap = (Map.fromListWith (<>) . concatMap doOne) modIface - ExportsMap exportsMap Map.empty +createExportsMapMg modGuts = do + let exportsMap = (Map.fromListWith (<>) . concatMap doOne) modGuts + ExportsMap exportsMap $ buildModuleExportMap exportsMap where doOne mi = do let getModuleName = moduleName $ mg_module mi @@ -131,7 +119,7 @@ createExportsMapMg modIface = do createExportsMapTc :: [TcGblEnv] -> ExportsMap createExportsMapTc modIface = do let exportsMap = (Map.fromListWith (<>) . concatMap doOne) modIface - ExportsMap exportsMap Map.empty + ExportsMap exportsMap $ buildModuleExportMap exportsMap where doOne mi = do let getModuleName = moduleName $ tcg_mod mi @@ -148,7 +136,7 @@ createExportsMapHieDb hiedb = do mText = pack $ moduleNameString mn fmap (wrap . unwrap mText) <$> getExportsForModule hiedb mn let exportsMap = Map.fromListWith (<>) (concat idents) - return $ ExportsMap exportsMap Map.empty + return $ ExportsMap exportsMap $ buildModuleExportMap exportsMap where wrap identInfo = (rendered identInfo, Set.fromList [identInfo]) -- unwrap :: ExportRow -> IdentInfo @@ -164,3 +152,28 @@ unpackAvail mn where !mod = pack $ moduleNameString mn f id@IdentInfo {..} = (pack (prettyPrint name), [id]) + + +identInfoToKeyVal :: IdentInfo -> (ModuleNameText, IdentInfo) +identInfoToKeyVal identInfo = + (moduleNameText identInfo, identInfo) + +buildModuleExportMap:: HashMap IdentifierText (HashSet IdentInfo) -> Map.HashMap ModuleNameText (HashSet IdentInfo) +buildModuleExportMap exportsMap = do + let lst = concatMap (Set.toList. snd) $ Map.toList exportsMap + let lstThree = map identInfoToKeyVal lst + sortAndGroup lstThree + +buildModuleExportMapFrom:: [ModIface] -> Map.HashMap Text (HashSet IdentInfo) +buildModuleExportMapFrom modIfaces = do + let exports = map extractModuleExports modIfaces + Map.fromListWith (<>) exports + +extractModuleExports :: ModIface -> (Text, HashSet IdentInfo) +extractModuleExports modIFace = do + let modName = pack $ moduleNameString $ moduleName $ mi_module modIFace + let functionSet = Set.fromList $ concatMap (mkIdentInfos modName) $ mi_exports modIFace + (modName, functionSet) + +sortAndGroup :: [(ModuleNameText, IdentInfo)] -> Map.HashMap ModuleNameText (HashSet IdentInfo) +sortAndGroup assocs = Map.fromListWith (<>) [(k, Set.fromList [v]) | (k, v) <- assocs] From 4f85abc77a8edaa9dd2e18828789dea3912e1b28 Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Mon, 6 Sep 2021 13:04:08 -0400 Subject: [PATCH 14/19] generate maps from list --- .../src/Development/IDE/Plugin/Completions.hs | 2 -- ghcide/src/Development/IDE/Types/Exports.hs | 19 ++++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 798a6b543f..70a258a2d3 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -51,8 +51,6 @@ import qualified Language.LSP.VFS as VFS import GHC.Tc.Module (tcRnImportDecls) #else import TcRnDriver (tcRnImportDecls) - - #endif descriptor :: PluginId -> PluginDescriptor IdeState diff --git a/ghcide/src/Development/IDE/Types/Exports.hs b/ghcide/src/Development/IDE/Types/Exports.hs index 0246578acd..694c9453f0 100644 --- a/ghcide/src/Development/IDE/Types/Exports.hs +++ b/ghcide/src/Development/IDE/Types/Exports.hs @@ -100,8 +100,8 @@ mkIdentInfos mod (AvailTC _ nn flds) createExportsMap :: [ModIface] -> ExportsMap createExportsMap modIface = do - let exportsMap = (Map.fromListWith (<>) . concatMap doOne) modIface - ExportsMap exportsMap $ buildModuleExportMap exportsMap + let exportList = concatMap doOne modIface + ExportsMap (Map.fromListWith (<>) exportList) (buildModuleExportMap exportList) where doOne modIFace = do let getModDetails = unpackAvail $ moduleName $ mi_module modIFace @@ -109,8 +109,8 @@ createExportsMap modIface = do createExportsMapMg :: [ModGuts] -> ExportsMap createExportsMapMg modGuts = do - let exportsMap = (Map.fromListWith (<>) . concatMap doOne) modGuts - ExportsMap exportsMap $ buildModuleExportMap exportsMap + let exportList = concatMap doOne modGuts + ExportsMap (Map.fromListWith (<>) exportList) (buildModuleExportMap exportList) where doOne mi = do let getModuleName = moduleName $ mg_module mi @@ -118,8 +118,9 @@ createExportsMapMg modGuts = do createExportsMapTc :: [TcGblEnv] -> ExportsMap createExportsMapTc modIface = do - let exportsMap = (Map.fromListWith (<>) . concatMap doOne) modIface - ExportsMap exportsMap $ buildModuleExportMap exportsMap + let exportList = concatMap doOne modIface + let exportsMap = Map.fromListWith (<>) exportList + ExportsMap exportsMap $ buildModuleExportMap exportList where doOne mi = do let getModuleName = moduleName $ tcg_mod mi @@ -136,7 +137,7 @@ createExportsMapHieDb hiedb = do mText = pack $ moduleNameString mn fmap (wrap . unwrap mText) <$> getExportsForModule hiedb mn let exportsMap = Map.fromListWith (<>) (concat idents) - return $ ExportsMap exportsMap $ buildModuleExportMap exportsMap + return $ ExportsMap exportsMap $ buildModuleExportMap (concat idents) where wrap identInfo = (rendered identInfo, Set.fromList [identInfo]) -- unwrap :: ExportRow -> IdentInfo @@ -158,9 +159,9 @@ identInfoToKeyVal :: IdentInfo -> (ModuleNameText, IdentInfo) identInfoToKeyVal identInfo = (moduleNameText identInfo, identInfo) -buildModuleExportMap:: HashMap IdentifierText (HashSet IdentInfo) -> Map.HashMap ModuleNameText (HashSet IdentInfo) +buildModuleExportMap:: [(Text, HashSet IdentInfo)] -> Map.HashMap ModuleNameText (HashSet IdentInfo) buildModuleExportMap exportsMap = do - let lst = concatMap (Set.toList. snd) $ Map.toList exportsMap + let lst = concatMap (Set.toList. snd) exportsMap let lstThree = map identInfoToKeyVal lst sortAndGroup lstThree From 1c1012d7f8b5bc1984131017f9e12cf9ac214d48 Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Mon, 6 Sep 2021 16:47:41 -0400 Subject: [PATCH 15/19] clean up --- ghcide/src/Development/IDE/Plugin/Completions/Logic.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index 140513d999..81f747062b 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -620,7 +620,6 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu let moduleName = T.pack $ words (T.unpack fullLine) !! 1 funcs = HM.lookupDefault HashSet.empty moduleName moduleExportsMap funs = map (show . name) $ HashSet.toList funcs - hello = GetLocatedImports return $ filterModuleExports moduleName $ map T.pack funs | "import " `T.isPrefixOf` fullLine -> return filtImportCompls From b79af6bd2e03d5b270661fb44626bceeb4052b87 Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Tue, 7 Sep 2021 17:17:26 -0400 Subject: [PATCH 16/19] addressing PR comments --- ghcide/src/Development/IDE/Plugin/Completions.hs | 15 +++------------ .../Development/IDE/Plugin/Completions/Logic.hs | 3 +-- ghcide/src/Development/IDE/Types/Exports.hs | 14 ++++++++------ 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 35079d46b8..86b5caf78c 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -166,18 +166,9 @@ getCompletionsLSP ide plId buildLocalModuleExports:: IdeState -> ([(Located ModuleName, Maybe ArtifactsLocation)], PositionMapping) -> IO (Map.HashMap T.Text (Set.HashSet IdentInfo)) buildLocalModuleExports ide inMap = do - mModIfaces <- mapM (getModIface ide) $ fst inMap - pure (buildModuleExportMapFrom $ catMaybes mModIfaces) - -getModIface :: IdeState -> (Located ModuleName, Maybe ArtifactsLocation) -> IO (Maybe ModIface) -getModIface ide item = do - let nPath = artifactFilePath <$> snd item - case nPath of - (Just path) -> do - file <- runIdeAction "Completion" (shakeExtras ide) $ do - fmap fst <$> useWithStaleFast GetModIface path - pure $ fmap hirModIface file - Nothing -> pure Nothing + let artifactLoctions = mapMaybe snd (fst inMap) + files <- runAction "Completion" ide $ usesWithStale GetModIface $ map artifactFilePath artifactLoctions + pure (buildModuleExportMapFrom $ map (hirModIface . fst) $ catMaybes files) extendImportCommand :: PluginCommand IdeState extendImportCommand = diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index 81f747062b..8e689724ee 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -65,7 +65,6 @@ import Language.LSP.Types.Capabilities import qualified Language.LSP.VFS as VFS import Outputable (Outputable) import TyCoRep -import Development.IDE.Core.RuleTypes -- From haskell-ide-engine/hie-plugin-api/Haskell/Ide/Engine/Context.hs @@ -611,8 +610,8 @@ getCompletions plId ideOpts CC {allModNamesAsNS, anyQualCompls, unqualCompls, qu | T.null prefixModule = filtListWith mkExtCompl (optKeywords ideOpts) | otherwise = [] - if + -- TODO: handle multiline imports | "import " `T.isPrefixOf` fullLine && (List.length (words (T.unpack fullLine)) >= 2) && "(" `isInfixOf` T.unpack fullLine diff --git a/ghcide/src/Development/IDE/Types/Exports.hs b/ghcide/src/Development/IDE/Types/Exports.hs index 694c9453f0..623bcef9fd 100644 --- a/ghcide/src/Development/IDE/Types/Exports.hs +++ b/ghcide/src/Development/IDE/Types/Exports.hs @@ -101,7 +101,8 @@ mkIdentInfos mod (AvailTC _ nn flds) createExportsMap :: [ModIface] -> ExportsMap createExportsMap modIface = do let exportList = concatMap doOne modIface - ExportsMap (Map.fromListWith (<>) exportList) (buildModuleExportMap exportList) + let exportsMap = Map.fromListWith (<>) $ map (\(a,_,c) -> (a, c)) exportList + ExportsMap exportsMap $ buildModuleExportMap $ map (\(_,b,c) -> (b, c)) exportList where doOne modIFace = do let getModDetails = unpackAvail $ moduleName $ mi_module modIFace @@ -110,7 +111,8 @@ createExportsMap modIface = do createExportsMapMg :: [ModGuts] -> ExportsMap createExportsMapMg modGuts = do let exportList = concatMap doOne modGuts - ExportsMap (Map.fromListWith (<>) exportList) (buildModuleExportMap exportList) + let exportsMap = Map.fromListWith (<>) $ map (\(a,_,c) -> (a, c)) exportList + ExportsMap exportsMap $ buildModuleExportMap $ map (\(_,b,c) -> (b, c)) exportList where doOne mi = do let getModuleName = moduleName $ mg_module mi @@ -119,8 +121,8 @@ createExportsMapMg modGuts = do createExportsMapTc :: [TcGblEnv] -> ExportsMap createExportsMapTc modIface = do let exportList = concatMap doOne modIface - let exportsMap = Map.fromListWith (<>) exportList - ExportsMap exportsMap $ buildModuleExportMap exportList + let exportsMap = Map.fromListWith (<>) $ map (\(a,_,c) -> (a, c)) exportList + ExportsMap exportsMap $ buildModuleExportMap $ map (\(_,b,c) -> (b, c)) exportList where doOne mi = do let getModuleName = moduleName $ tcg_mod mi @@ -146,13 +148,13 @@ createExportsMapHieDb hiedb = do n = pack (occNameString exportName) p = pack . occNameString <$> exportParent -unpackAvail :: ModuleName -> IfaceExport -> [(Text, [IdentInfo])] +unpackAvail :: ModuleName -> IfaceExport -> [(Text, Text, [IdentInfo])] unpackAvail mn | nonInternalModules mn = map f . mkIdentInfos mod | otherwise = const [] where !mod = pack $ moduleNameString mn - f id@IdentInfo {..} = (pack (prettyPrint name), [id]) + f id@IdentInfo {..} = (pack (prettyPrint name), moduleNameText,[id]) identInfoToKeyVal :: IdentInfo -> (ModuleNameText, IdentInfo) From f0e25c33496f5ad4f1362abb1cc5e79cef8f6064 Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Tue, 7 Sep 2021 17:38:18 -0400 Subject: [PATCH 17/19] clean up --- ghcide/src/Development/IDE/Plugin/Completions.hs | 2 +- ghcide/src/Development/IDE/Plugin/Completions/Logic.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index 86b5caf78c..e8ad4f8e76 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -130,7 +130,7 @@ getCompletionsLSP ide plId ,_context=completionContext} = do contents <- LSP.getVirtualFile $ toNormalizedUri uri fmap Right $ case (contents, uriToFilePath' uri) of - (Just cnts, Just path) -> do + (Just cnts, Just path) -> do let npath = toNormalizedFilePath' path (ideOpts, compls, moduleExports) <- liftIO $ runIdeAction "Completion" (shakeExtras ide) $ do opts <- liftIO $ getIdeOptionsIO $ shakeExtras ide diff --git a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs index 8e689724ee..c3f1de1a4a 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions/Logic.hs @@ -50,7 +50,7 @@ import Development.IDE.GHC.Compat as GHC import Development.IDE.GHC.Error import Development.IDE.GHC.Util import Development.IDE.Plugin.Completions.Types -import Development.IDE.Spans.Common +import Development.IDE.Spans.Common import Development.IDE.Spans.Documentation import Development.IDE.Spans.LocalBindings import Development.IDE.Types.Exports From 1077480870e8ec9300b06e04fc04d56cda3fce4d Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Tue, 7 Sep 2021 17:39:53 -0400 Subject: [PATCH 18/19] clean up --- ghcide/src/Development/IDE/Types/Exports.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Types/Exports.hs b/ghcide/src/Development/IDE/Types/Exports.hs index 623bcef9fd..58603efb1b 100644 --- a/ghcide/src/Development/IDE/Types/Exports.hs +++ b/ghcide/src/Development/IDE/Types/Exports.hs @@ -23,7 +23,7 @@ import Data.List (isSuffixOf) import Data.Text (Text, pack) import Development.IDE.GHC.Compat import Development.IDE.GHC.Orphans () -import Development.IDE.GHC.Util +import Development.IDE.GHC.Util import FieldLabel (flSelector) import GHC.Generics (Generic) import GhcPlugins (IfaceExport, ModGuts (..)) From 110ce178f58743e61ce634dafe934b520d7143bf Mon Sep 17 00:00:00 2001 From: Alex Naspo Date: Wed, 8 Sep 2021 17:24:03 -0400 Subject: [PATCH 19/19] useWithStaleFast --- ghcide/src/Development/IDE/Plugin/Completions.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Plugin/Completions.hs b/ghcide/src/Development/IDE/Plugin/Completions.hs index e8ad4f8e76..6aac585135 100644 --- a/ghcide/src/Development/IDE/Plugin/Completions.hs +++ b/ghcide/src/Development/IDE/Plugin/Completions.hs @@ -167,7 +167,9 @@ getCompletionsLSP ide plId buildLocalModuleExports:: IdeState -> ([(Located ModuleName, Maybe ArtifactsLocation)], PositionMapping) -> IO (Map.HashMap T.Text (Set.HashSet IdentInfo)) buildLocalModuleExports ide inMap = do let artifactLoctions = mapMaybe snd (fst inMap) - files <- runAction "Completion" ide $ usesWithStale GetModIface $ map artifactFilePath artifactLoctions + let afp = map artifactFilePath artifactLoctions + let queries = map (useWithStaleFast GetModIface) afp + files <- liftIO $ mapM (runIdeAction "Completion" (shakeExtras ide)) queries pure (buildModuleExportMapFrom $ map (hirModIface . fst) $ catMaybes files) extendImportCommand :: PluginCommand IdeState