Skip to content

Commit f91edea

Browse files
committed
Implement completionItem/resolve
1 parent 468db6f commit f91edea

File tree

5 files changed

+205
-157
lines changed

5 files changed

+205
-157
lines changed

ghcide/src/Development/IDE/GHC/Compat.hs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ module Development.IDE.GHC.Compat(
2222
#else
2323
upNameCache,
2424
#endif
25+
lookupNameCache,
2526
disableWarningsAsErrors,
2627
reLoc,
2728
reLocA,
@@ -444,6 +445,25 @@ hieExportNames = nameListFromAvails . hie_exports
444445
#if MIN_VERSION_ghc(9,3,0)
445446
type NameCacheUpdater = NameCache
446447
#else
448+
449+
lookupNameCache :: Module -> OccName -> NameCache -> (NameCache, Name)
450+
-- Lookup up the (Module,OccName) in the NameCache
451+
-- If you find it, return it; if not, allocate a fresh original name and extend
452+
-- the NameCache.
453+
-- Reason: this may the first occurrence of (say) Foo.bar we have encountered.
454+
-- If we need to explore its value we will load Foo.hi; but meanwhile all we
455+
-- need is a Name for it.
456+
lookupNameCache mod occ name_cache =
457+
case lookupOrigNameCache (nsNames name_cache) mod occ of {
458+
Just name -> (name_cache, name);
459+
Nothing ->
460+
case takeUniqFromSupply (nsUniqs name_cache) of {
461+
(uniq, us) ->
462+
let
463+
name = mkExternalName uniq mod occ noSrcSpan
464+
new_cache = extendNameCache (nsNames name_cache) mod occ name
465+
in (name_cache{ nsUniqs = us, nsNames = new_cache }, name) }}
466+
447467
upNameCache :: IORef NameCache -> (NameCache -> (NameCache, c)) -> IO c
448468
#if MIN_VERSION_ghc(8,8,0)
449469
upNameCache = updNameCache

ghcide/src/Development/IDE/Plugin/Completions.hs

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,40 +10,40 @@ module Development.IDE.Plugin.Completions
1010

1111
import Control.Concurrent.Async (concurrently)
1212
import Control.Concurrent.STM.Stats (readTVarIO)
13-
import Control.Monad.Extra
1413
import Control.Monad.IO.Class
15-
import Control.Monad.Trans.Maybe
14+
import Control.Lens ((&), (.~))
1615
import Data.Aeson
1716
import qualified Data.HashMap.Strict as Map
1817
import qualified Data.HashSet as Set
19-
import Data.List (find)
2018
import Data.Maybe
2119
import qualified Data.Text as T
2220
import Development.IDE.Core.PositionMapping
21+
import Development.IDE.Core.Compile
2322
import Development.IDE.Core.RuleTypes
2423
import Development.IDE.Core.Service hiding (Log, LogShake)
2524
import Development.IDE.Core.Shake hiding (Log)
2625
import qualified Development.IDE.Core.Shake as Shake
2726
import Development.IDE.GHC.Compat
28-
import Development.IDE.GHC.Error (rangeToSrcSpan)
2927
import Development.IDE.GHC.Util (printOutputable)
3028
import Development.IDE.Graph
29+
import Development.IDE.Spans.Common
30+
import Development.IDE.Spans.Documentation
3131
import Development.IDE.Plugin.Completions.Logic
3232
import Development.IDE.Plugin.Completions.Types
3333
import Development.IDE.Types.Exports
34-
import Development.IDE.Types.HscEnvEq (HscEnvEq (envPackageExports),
34+
import Development.IDE.Types.HscEnvEq (HscEnvEq (envPackageExports, envVisibleModuleNames),
3535
hscEnv)
3636
import qualified Development.IDE.Types.KnownTargets as KT
3737
import Development.IDE.Types.Location
3838
import Development.IDE.Types.Logger (Pretty (pretty),
3939
Recorder,
4040
WithPriority,
4141
cmapWithPrio)
42-
import GHC.Exts (fromList, toList)
4342
import Ide.Plugin.Config (Config)
4443
import Ide.Types
4544
import qualified Language.LSP.Server as LSP
4645
import Language.LSP.Types
46+
import qualified Language.LSP.Types.Lens as J
4747
import qualified Language.LSP.VFS as VFS
4848
import Numeric.Natural
4949
import Text.Fuzzy.Parallel (Scored (..))
@@ -64,10 +64,12 @@ descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeSta
6464
descriptor recorder plId = (defaultPluginDescriptor plId)
6565
{ pluginRules = produceCompletions recorder
6666
, pluginHandlers = mkPluginHandler STextDocumentCompletion getCompletionsLSP
67+
<> mkPluginHandler SCompletionItemResolve resolveCompletion
6768
, pluginConfigDescriptor = defaultConfigDescriptor {configCustomConfig = mkCustomConfig properties}
6869
, pluginPriority = ghcideCompletionsPluginPriority
6970
}
7071

72+
7173
produceCompletions :: Recorder (WithPriority Log) -> Rules ()
7274
produceCompletions recorder = do
7375
define (cmapWithPrio LogShake recorder) $ \LocalCompletions file -> do
@@ -92,8 +94,9 @@ produceCompletions recorder = do
9294
(global, inScope) <- liftIO $ tcRnImportDecls env (dropListFromImportDecl <$> msrImports) `concurrently` tcRnImportDecls env msrImports
9395
case (global, inScope) of
9496
((_, Just globalEnv), (_, Just inScopeEnv)) -> do
97+
visibleMods <- liftIO $ fmap (fromMaybe []) $ envVisibleModuleNames sess
9598
let uri = fromNormalizedUri $ normalizedFilePathToUri file
96-
cdata <- liftIO $ cacheDataProducer uri sess (ms_mod msrModSummary) globalEnv inScopeEnv msrImports
99+
let cdata = cacheDataProducer uri visibleMods (ms_mod msrModSummary) globalEnv inScopeEnv msrImports
97100
return ([], Just cdata)
98101
(_diag, _) ->
99102
return ([], Nothing)
@@ -109,6 +112,44 @@ dropListFromImportDecl iDecl = let
109112
f x = x
110113
in f <$> iDecl
111114

115+
resolveCompletion :: IdeState -> PluginId -> CompletionItem -> LSP.LspM Config (Either ResponseError CompletionItem)
116+
resolveCompletion ide _ comp@CompletionItem{_detail,_documentation,_xdata}
117+
| Just resolveData <- _xdata
118+
, Success (uri, NameDetails mod occ) <- fromJSON resolveData
119+
, Just file <- uriToNormalizedFilePath $ toNormalizedUri uri = do
120+
mdkm <- liftIO $ runAction "Completion resolve" ide $ use GetDocMap file
121+
case mdkm of
122+
Nothing -> pure (Right comp)
123+
Just (DKMap dm km) -> liftIO $ runAction "Completion resolve" ide $ do
124+
let nc = ideNc $ shakeExtras ide
125+
#if MIN_VERSION_ghc(9,3,0)
126+
name <- liftIO $ lookupNameCache nc mod occ
127+
#else
128+
name <- liftIO $ upNameCache nc (lookupNameCache mod occ)
129+
#endif
130+
(ms,_) <- useWithStale_ GetModSummaryWithoutTimestamps file
131+
(sess,_) <- useWithStale_ GhcSessionDeps file
132+
let cur_mod = ms_mod $ msrModSummary ms
133+
doc <- case lookupNameEnv dm name of
134+
Just doc -> pure $ spanDocToMarkdown doc
135+
Nothing -> liftIO $ spanDocToMarkdown <$> getDocumentationTryGhc (hscEnv sess) cur_mod name
136+
typ <- case lookupNameEnv km name of
137+
Just ty -> pure (safeTyThingType ty)
138+
Nothing -> do
139+
(safeTyThingType =<<) <$> liftIO (lookupName (hscEnv sess) cur_mod name)
140+
let det1 = case typ of
141+
Just ty -> Just (":: " <> printOutputable ty <> "\n")
142+
Nothing -> Nothing
143+
doc1 = case _documentation of
144+
Just (CompletionDocMarkup (MarkupContent MkMarkdown old)) ->
145+
CompletionDocMarkup $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator (old:doc)
146+
_ -> CompletionDocMarkup $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator doc
147+
pure (Right $ comp & J.detail .~ (det1 <> _detail)
148+
& J.documentation .~ Just doc1
149+
)
150+
151+
resolveCompletion _ _ comp = pure (Right comp)
152+
112153
-- | Generate code actions.
113154
getCompletionsLSP
114155
:: IdeState
@@ -166,8 +207,7 @@ getCompletionsLSP ide plId
166207
let clientCaps = clientCapabilities $ shakeExtras ide
167208
plugins = idePlugins $ shakeExtras ide
168209
config <- getCompletionsConfig plId
169-
170-
allCompletions <- liftIO $ getCompletions plugins ideOpts cci' parsedMod astres bindMap pfix clientCaps config moduleExports
210+
allCompletions <- liftIO $ getCompletions plugins ideOpts cci' parsedMod astres bindMap pfix' clientCaps config moduleExports uri
171211
pure $ InL (List $ orderedCompletions allCompletions)
172212
_ -> return (InL $ List [])
173213
_ -> return (InL $ List [])

0 commit comments

Comments
 (0)