Skip to content
This repository was archived by the owner on Jan 2, 2021. It is now read-only.

Extend import list automatically #930

Merged
merged 31 commits into from
Dec 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c0c6295
Drop any items in explicit import list
gdevanla Nov 22, 2020
52896f1
Test if imports not included in explicit list show up in completions
gdevanla Nov 22, 2020
a1b337f
Merge branch 'master' into ignore-import-list-while-producing-complet…
gdevanla Nov 26, 2020
b557a50
Update CompItem to hold additionalTextEdit
gdevanla Nov 29, 2020
48a91b0
Add placeholder value for additionalTextEdit field
gdevanla Nov 30, 2020
72d68aa
Improvement completion tests.
gdevanla Nov 30, 2020
eaab73d
Use explicit fields while constructing CompletionItem
gdevanla Nov 30, 2020
3dd6c05
Add function that will extend an import list
gdevanla Nov 30, 2020
37751cb
Use externalImports to extend import list
gdevanla Nov 30, 2020
7733675
Make import list information available
gdevanla Nov 30, 2020
f286221
First working prototype of extending import list.
gdevanla Nov 30, 2020
7395818
Pass the original importDecl to cacheDataProducer
gdevanla Dec 1, 2020
c6f946a
Add tests for completions with addtional text edits
gdevanla Dec 1, 2020
bf23152
Hlinting
gdevanla Dec 1, 2020
d9d6ae9
Refine function name and signature
gdevanla Dec 1, 2020
0e3629f
Merge branch 'master' into extend-import-list
gdevanla Dec 1, 2020
3d74ff7
Pass the original importDecl to cacheDataProducer
gdevanla Dec 1, 2020
6ea3ada
Refactor code to use gaurds
gdevanla Dec 3, 2020
c683eb2
Exhaust patterns
gdevanla Dec 3, 2020
5798034
Handle empty import list
gdevanla Dec 3, 2020
4494b99
Use correct pattern
gdevanla Dec 3, 2020
870f623
Update expected values in TextEdit
gdevanla Dec 3, 2020
57c09e6
Add test adding imports to empty list
gdevanla Dec 3, 2020
6ef82e4
Remove old code
gdevanla Dec 3, 2020
1cd1cef
Handle names with underscore
gdevanla Dec 3, 2020
687aadc
Exhaust patterns
gdevanla Dec 3, 2020
1f39f15
Improve storing of import map
gdevanla Dec 3, 2020
9649cff
Add trailing comma to import list completions.
gdevanla Dec 3, 2020
a2199f4
Add support for Record snippets
gdevanla Dec 5, 2020
b9381b8
Add 8.8.4 support
gdevanla Dec 5, 2020
6b8549f
Code cleanup.
gdevanla Dec 5, 2020
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 src/Development/IDE/Plugin/Completions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,10 @@ produceCompletions = do
(Just (ms,imps), Just sess) -> do
let env = hscEnv sess
-- We do this to be able to provide completions of items that are not restricted to the explicit list
let imps' = map dropListFromImportDecl imps
res <- liftIO $ tcRnImportDecls env imps'
res <- liftIO $ tcRnImportDecls env (dropListFromImportDecl <$> imps)
case res of
(_, Just rdrEnv) -> do
cdata <- liftIO $ cacheDataProducer env (ms_mod ms) rdrEnv imps' parsedDeps
cdata <- liftIO $ cacheDataProducer env (ms_mod ms) rdrEnv imps parsedDeps
return ([], Just cdata)
(_diag, _) ->
return ([], Nothing)
Expand Down
96 changes: 74 additions & 22 deletions src/Development/IDE/Plugin/Completions/Logic.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{-# LANGUAGE CPP #-}

#include "ghc-api-version.h"
#if MIN_GHC_API_VERSION (8,8,4)
{-# LANGUAGE GADTs#-}
#endif
-- Mostly taken from "haskell-ide-engine"
module Development.IDE.Plugin.Completions.Logic (
CachedCompletions
Expand All @@ -11,7 +14,7 @@ module Development.IDE.Plugin.Completions.Logic (
) where

import Control.Applicative
import Data.Char (isUpper)
import Data.Char (isAlphaNum, isUpper)
import Data.Generics
import Data.List.Extra as List hiding (stripPrefix)
import qualified Data.Map as Map
Expand Down Expand Up @@ -144,21 +147,44 @@ occNameToComKind ty oc
showModName :: ModuleName -> T.Text
showModName = T.pack . moduleNameString

-- mkCompl :: IdeOptions -> CompItem -> CompletionItem
-- mkCompl IdeOptions{..} CI{compKind,insertText, importedFrom,typeText,label,docs} =
-- CompletionItem label kind (List []) ((colon <>) <$> typeText)
-- (Just $ CompletionDocMarkup $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator docs')
-- Nothing Nothing Nothing Nothing (Just insertText) (Just Snippet)
-- Nothing Nothing Nothing Nothing Nothing

mkCompl :: IdeOptions -> CompItem -> CompletionItem
mkCompl IdeOptions{..} CI{compKind,insertText, importedFrom,typeText,label,docs} =
CompletionItem label kind (List []) ((colon <>) <$> typeText)
(Just $ CompletionDocMarkup $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator docs')
Nothing Nothing Nothing Nothing (Just insertText) (Just Snippet)
Nothing Nothing Nothing Nothing Nothing
mkCompl IdeOptions{..} CI{compKind,insertText, importedFrom,typeText,label,docs, additionalTextEdits} =
CompletionItem {_label = label,
_kind = kind,
_tags = List [],
_detail = (colon <>) <$> typeText,
_documentation = documentation,
_deprecated = Nothing,
_preselect = Nothing,
_sortText = Nothing,
_filterText = Nothing,
_insertText = Just insertText,
_insertTextFormat = Just Snippet,
_textEdit = Nothing,
_additionalTextEdits = List <$> additionalTextEdits,
_commitCharacters = Nothing,
_command = Nothing,
_xdata = Nothing}

where kind = Just compKind
docs' = imported : spanDocToMarkdown docs
imported = case importedFrom of
Left pos -> "*Defined at '" <> ppr pos <> "'*\n'"
Right mod -> "*Defined in '" <> mod <> "'*\n"
colon = if optNewColonConvention then ": " else ":: "
documentation = Just $ CompletionDocMarkup $
MarkupContent MkMarkdown $
T.intercalate sectionSeparator docs'

mkNameCompItem :: Name -> ModuleName -> Maybe Type -> Maybe Backtick -> SpanDoc -> CompItem
mkNameCompItem origName origMod thingType isInfix docs = CI{..}
mkNameCompItem :: Name -> ModuleName -> Maybe Type -> Maybe Backtick -> SpanDoc -> Maybe (LImportDecl GhcPs) -> CompItem
mkNameCompItem origName origMod thingType isInfix docs !imp = CI{..}
where
compKind = occNameToComKind typeText $ occName origName
importedFrom = Right $ showModName origMod
Expand All @@ -174,7 +200,7 @@ mkNameCompItem origName origMod thingType isInfix docs = CI{..}
typeText
| Just t <- thingType = Just . stripForall $ T.pack (showGhc t)
| otherwise = Nothing

additionalTextEdits = imp >>= extendImportList (showGhc origName)

stripForall :: T.Text -> T.Text
stripForall t
Expand Down Expand Up @@ -236,11 +262,37 @@ mkPragmaCompl label insertText =
Nothing Nothing Nothing Nothing Nothing (Just insertText) (Just Snippet)
Nothing Nothing Nothing Nothing Nothing

extendImportList :: String -> LImportDecl GhcPs -> Maybe [TextEdit]
extendImportList name lDecl = let
f (Just range) ImportDecl {ideclHiding} = case ideclHiding of
Just (False, x)
| Set.notMember name (Set.fromList [show y| y <- unLoc x])
-> let
start_pos = _end range
new_start_pos = start_pos {_character = _character start_pos - 1}
-- use to same start_pos to handle situation where we do not have latest edits due to caching of Rules
new_range = Range new_start_pos new_start_pos
-- we cannot wrap mapM_ inside (mapM_) but we need to wrap (<$)
alpha = all isAlphaNum $ filter (\c -> c /= '_') name
result = if alpha then name ++ ", "
else "(" ++ name ++ "), "
in Just [TextEdit new_range (T.pack result)]
| otherwise -> Nothing
_ -> Nothing -- hiding import list and no list
f _ _ = Nothing
src_span = srcSpanToRange . getLoc $ lDecl
in f src_span . unLoc $ lDecl


cacheDataProducer :: HscEnv -> Module -> GlobalRdrEnv -> [LImportDecl GhcPs] -> [ParsedModule] -> IO CachedCompletions
cacheDataProducer packageState curMod rdrEnv limports deps = do
let dflags = hsc_dflags packageState
curModName = moduleName curMod

importMap = Map.fromList [
(getLoc imp, imp)
| imp <- limports ]

iDeclToModName :: ImportDecl name -> ModuleName
iDeclToModName = unLoc . ideclName

Expand All @@ -266,10 +318,11 @@ cacheDataProducer packageState curMod rdrEnv limports deps = do

getComplsForOne :: GlobalRdrElt -> IO ([CompItem],QualCompls)
getComplsForOne (GRE n _ True _) =
(, mempty) <$> toCompItem curMod curModName n
(, mempty) <$> toCompItem curMod curModName n Nothing
getComplsForOne (GRE n _ False prov) =
flip foldMapM (map is_decl prov) $ \spec -> do
compItem <- toCompItem curMod (is_mod spec) n
let originalImportDecl = Map.lookup (is_dloc spec) importMap
compItem <- toCompItem curMod (is_mod spec) n originalImportDecl
let unqual
| is_qual spec = []
| otherwise = compItem
Expand All @@ -280,8 +333,8 @@ cacheDataProducer packageState curMod rdrEnv limports deps = do
origMod = showModName (is_mod spec)
return (unqual,QualCompls qual)

toCompItem :: Module -> ModuleName -> Name -> IO [CompItem]
toCompItem m mn n = do
toCompItem :: Module -> ModuleName -> Name -> Maybe (LImportDecl GhcPs) -> IO [CompItem]
toCompItem m mn n imp' = do
docs <- getDocumentationTryGhc packageState curMod deps n
ty <- catchSrcErrors (hsc_dflags packageState) "completion" $ do
name' <- lookupName packageState m n
Expand All @@ -294,10 +347,10 @@ cacheDataProducer packageState curMod rdrEnv limports deps = do
let recordCompls = case either (const Nothing) id record_ty of
Just (ctxStr, flds) -> case flds of
[] -> []
_ -> [mkRecordSnippetCompItem ctxStr flds (ppr mn) docs]
_ -> [mkRecordSnippetCompItem ctxStr flds (ppr mn) docs imp']
Nothing -> []

return $ [mkNameCompItem n mn (either (const Nothing) id ty) Nothing docs] ++
return $ [mkNameCompItem n mn (either (const Nothing) id ty) Nothing docs imp'] ++
recordCompls

(unquals,quals) <- getCompls rdrElts
Expand Down Expand Up @@ -360,19 +413,17 @@ localCompletionsForParsedModule pm@ParsedModule{pm_parsed_source = L _ HsModule{
]

mkComp n ctyp ty =
CI ctyp pn (Right thisModName) ty pn Nothing doc (ctyp `elem` [CiStruct, CiClass])
CI ctyp pn (Right thisModName) ty pn Nothing doc (ctyp `elem` [CiStruct, CiClass]) Nothing
where
pn = ppr n
doc = SpanDocText (getDocumentation [pm] n) (SpanDocUris Nothing Nothing)

thisModName = ppr hsmodName

--recordCompls = localRecordSnippetProducer pm thisModName

findRecordCompl :: ParsedModule -> T.Text -> TyClDecl GhcPs -> [CompItem]
findRecordCompl pmod mn DataDecl {tcdLName, tcdDataDefn} = result
where
result = [mkRecordSnippetCompItem (T.pack . showGhc . unLoc $ con_name) field_labels mn doc
result = [mkRecordSnippetCompItem (T.pack . showGhc . unLoc $ con_name) field_labels mn doc Nothing
| ConDeclH98{..} <- unLoc <$> dd_cons tcdDataDefn
, Just con_details <- [getFlds con_args]
, let field_names = mapMaybe extract con_details
Expand Down Expand Up @@ -468,7 +519,7 @@ getCompletions ideOpts CC { allModNamesAsNS, unqualCompls, qualCompls, importabl
endLoc = upperRange oldPos
localCompls = map (uncurry localBindsToCompItem) $ getFuzzyScope localBindings startLoc endLoc
localBindsToCompItem :: Name -> Maybe Type -> CompItem
localBindsToCompItem name typ = CI ctyp pn thisModName ty pn Nothing emptySpanDoc (not $ isValOcc occ)
localBindsToCompItem name typ = CI ctyp pn thisModName ty pn Nothing emptySpanDoc (not $ isValOcc occ) Nothing
where
occ = nameOccName name
ctyp = occNameToComKind Nothing occ
Expand Down Expand Up @@ -665,8 +716,8 @@ safeTyThingForRecord (AConLike dc) =
Just (ctxStr, field_names)
safeTyThingForRecord _ = Nothing

mkRecordSnippetCompItem :: T.Text -> [T.Text] -> T.Text -> SpanDoc -> CompItem
mkRecordSnippetCompItem ctxStr compl mn docs = r
mkRecordSnippetCompItem :: T.Text -> [T.Text] -> T.Text -> SpanDoc -> Maybe (LImportDecl GhcPs) -> CompItem
mkRecordSnippetCompItem ctxStr compl mn docs imp = r
where
r = CI {
compKind = CiSnippet
Expand All @@ -677,6 +728,7 @@ mkRecordSnippetCompItem ctxStr compl mn docs = r
, isInfix = Nothing
, docs = docs
, isTypeCompl = False
, additionalTextEdits = imp >>= extendImportList (T.unpack ctxStr)
}

placeholder_pairs = zip compl ([1..]::[Int])
Expand Down
3 changes: 2 additions & 1 deletion src/Development/IDE/Plugin/Completions/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import qualified Data.Text as T
import SrcLoc

import Development.IDE.Spans.Common
import Language.Haskell.LSP.Types (CompletionItemKind)
import Language.Haskell.LSP.Types (TextEdit, CompletionItemKind)

-- From haskell-ide-engine/src/Haskell/Ide/Engine/LSP/Completions.hs

Expand All @@ -25,6 +25,7 @@ data CompItem = CI
-- in the context of an infix notation.
, docs :: SpanDoc -- ^ Available documentation.
, isTypeCompl :: Bool
, additionalTextEdits :: Maybe [TextEdit]
}
deriving (Eq, Show)

Expand Down
Loading