Skip to content

Commit 40c24f8

Browse files
committed
Use a command to extend imports
1 parent 61b01bd commit 40c24f8

File tree

3 files changed

+174
-101
lines changed

3 files changed

+174
-101
lines changed

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

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ import Language.Haskell.LSP.Types
1212
import qualified Language.Haskell.LSP.Core as LSP
1313
import qualified Language.Haskell.LSP.VFS as VFS
1414

15+
import Control.Monad
16+
import Control.Monad.Trans.Maybe
17+
import Data.Aeson
18+
import Data.List (find)
19+
import Data.Maybe
20+
import qualified Data.Text as T
1521
import Development.Shake.Classes
1622
import Development.Shake
1723
import GHC.Generics
@@ -22,23 +28,24 @@ import Development.IDE.Types.Location
2228
import Development.IDE.Core.RuleTypes
2329
import Development.IDE.Core.Shake
2430
import Development.IDE.GHC.Compat
25-
31+
import Development.IDE.GHC.ExactPrint (Annotated (annsA), GetAnnotatedParsedSource (GetAnnotatedParsedSource))
2632
import Development.IDE.GHC.Util
27-
import TcRnDriver (tcRnImportDecls)
28-
import Data.Maybe
33+
import Development.IDE.Plugin.CodeAction.ExactPrint
34+
import Development.IDE.Plugin.Completions.Types
2935
import Ide.Plugin.Config (Config (completionSnippetsOn))
3036
import Ide.PluginUtils (getClientConfig)
3137
import Ide.Types
32-
38+
import TcRnDriver (tcRnImportDecls)
3339
#if defined(GHC_LIB)
3440
import Development.IDE.Import.DependencyInformation
3541
#endif
3642

3743
descriptor :: PluginId -> PluginDescriptor IdeState
3844
descriptor plId = (defaultPluginDescriptor plId)
39-
{ pluginRules = produceCompletions
40-
, pluginCompletionProvider = Just getCompletionsLSP
41-
}
45+
{ pluginRules = produceCompletions,
46+
pluginCompletionProvider = Just (getCompletionsLSP plId),
47+
pluginCommands = [extendImportCommand]
48+
}
4249

4350
produceCompletions :: Rules ()
4451
produceCompletions = do
@@ -48,10 +55,11 @@ produceCompletions = do
4855
let extract = fmap fst
4956
return ([], extract local <> extract nonLocal)
5057
define $ \LocalCompletions file -> do
58+
let uri = fromNormalizedUri $ normalizedFilePathToUri file
5159
pm <- useWithStale GetParsedModule file
5260
case pm of
5361
Just (pm, _) -> do
54-
let cdata = localCompletionsForParsedModule pm
62+
let cdata = localCompletionsForParsedModule uri pm
5563
return ([], Just cdata)
5664
_ -> return ([], Nothing)
5765
define $ \NonLocalCompletions file -> do
@@ -77,7 +85,8 @@ produceCompletions = do
7785
res <- liftIO $ tcRnImportDecls env (dropListFromImportDecl <$> imps)
7886
case res of
7987
(_, Just rdrEnv) -> do
80-
cdata <- liftIO $ cacheDataProducer env (ms_mod ms) rdrEnv imps parsedDeps
88+
let uri = fromNormalizedUri $ normalizedFilePathToUri file
89+
cdata <- liftIO $ cacheDataProducer uri env (ms_mod ms) rdrEnv imps parsedDeps
8190
return ([], Just cdata)
8291
(_diag, _) ->
8392
return ([], Nothing)
@@ -115,13 +124,15 @@ data NonLocalCompletions = NonLocalCompletions
115124
instance Hashable NonLocalCompletions
116125
instance NFData NonLocalCompletions
117126
instance Binary NonLocalCompletions
127+
118128
-- | Generate code actions.
119129
getCompletionsLSP
120-
:: LSP.LspFuncs Config
130+
:: PluginId
131+
-> LSP.LspFuncs Config
121132
-> IdeState
122133
-> CompletionParams
123134
-> IO (Either ResponseError CompletionResponseResult)
124-
getCompletionsLSP lsp ide
135+
getCompletionsLSP plId lsp ide
125136
CompletionParams{_textDocument=TextDocumentIdentifier uri
126137
,_position=position
127138
,_context=completionContext} = do
@@ -145,8 +156,53 @@ getCompletionsLSP lsp ide
145156
let clientCaps = clientCapabilities $ shakeExtras ide
146157
config <- getClientConfig lsp
147158
let snippets = WithSnippets . completionSnippetsOn $ config
148-
allCompletions <- getCompletions ideOpts cci' parsedMod bindMap pfix' clientCaps snippets
159+
allCompletions <- getCompletions plId ideOpts cci' parsedMod bindMap pfix' clientCaps snippets
149160
pure $ Completions (List allCompletions)
150161
_ -> return (Completions $ List [])
151162
_ -> return (Completions $ List [])
152163
_ -> return (Completions $ List [])
164+
165+
----------------------------------------------------------------------------------------------------
166+
167+
extendImportCommand :: PluginCommand IdeState
168+
extendImportCommand =
169+
PluginCommand (CommandId extendImportCommandId) "additional edits for a completion" extendImportHandler
170+
171+
extendImportHandler :: CommandFunction IdeState ExtendImport
172+
extendImportHandler _lsp ideState edit = do
173+
res <- runMaybeT $ extendImportHandler' ideState edit
174+
return (Right Null, res)
175+
176+
extendImportHandler' :: IdeState -> ExtendImport -> MaybeT IO (ServerMethod, ApplyWorkspaceEditParams)
177+
extendImportHandler' ideState ExtendImport {..}
178+
| Just fp <- uriToFilePath doc,
179+
nfp <- toNormalizedFilePath' fp =
180+
do
181+
(ms, ps, imps) <- MaybeT $
182+
runAction "extend import" ideState $
183+
runMaybeT $ do
184+
-- We want accurate edits, so do not use stale data here
185+
(ms, imps) <- MaybeT $ use GetModSummaryWithoutTimestamps nfp
186+
ps <- MaybeT $ use GetAnnotatedParsedSource nfp
187+
return (ms, ps, imps)
188+
let df = ms_hspp_opts ms
189+
wantedModule = mkModuleName (T.unpack importName)
190+
imp <- liftMaybe $ find (isWantedModule wantedModule) imps
191+
wedit <-
192+
liftEither $
193+
rewriteToEdit df doc (annsA ps) $
194+
extendImport (T.unpack <$> thingParent) (T.unpack newThing) imp
195+
return (WorkspaceApplyEdit, ApplyWorkspaceEditParams wedit)
196+
| otherwise =
197+
mzero
198+
199+
isWantedModule :: ModuleName -> GenLocated l (ImportDecl pass) -> Bool
200+
isWantedModule wantedModule (L _ ImportDecl {..}) = unLoc ideclName == wantedModule
201+
isWantedModule _ _ = False
202+
203+
liftMaybe :: Monad m => Maybe a -> MaybeT m a
204+
liftMaybe a = MaybeT $ pure a
205+
206+
liftEither :: Monad m => Either e a -> MaybeT m a
207+
liftEither (Left _) = mzero
208+
liftEither (Right x) = return x

0 commit comments

Comments
 (0)