@@ -12,6 +12,12 @@ import Language.Haskell.LSP.Types
12
12
import qualified Language.Haskell.LSP.Core as LSP
13
13
import qualified Language.Haskell.LSP.VFS as VFS
14
14
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
15
21
import Development.Shake.Classes
16
22
import Development.Shake
17
23
import GHC.Generics
@@ -22,23 +28,24 @@ import Development.IDE.Types.Location
22
28
import Development.IDE.Core.RuleTypes
23
29
import Development.IDE.Core.Shake
24
30
import Development.IDE.GHC.Compat
25
-
31
+ import Development.IDE.GHC.ExactPrint ( Annotated ( annsA ), GetAnnotatedParsedSource ( GetAnnotatedParsedSource ))
26
32
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
29
35
import Ide.Plugin.Config (Config (completionSnippetsOn ))
30
36
import Ide.PluginUtils (getClientConfig )
31
37
import Ide.Types
32
-
38
+ import TcRnDriver ( tcRnImportDecls )
33
39
#if defined(GHC_LIB)
34
40
import Development.IDE.Import.DependencyInformation
35
41
#endif
36
42
37
43
descriptor :: PluginId -> PluginDescriptor IdeState
38
44
descriptor plId = (defaultPluginDescriptor plId)
39
- { pluginRules = produceCompletions
40
- , pluginCompletionProvider = Just getCompletionsLSP
41
- }
45
+ { pluginRules = produceCompletions,
46
+ pluginCompletionProvider = Just (getCompletionsLSP plId),
47
+ pluginCommands = [extendImportCommand]
48
+ }
42
49
43
50
produceCompletions :: Rules ()
44
51
produceCompletions = do
@@ -48,10 +55,11 @@ produceCompletions = do
48
55
let extract = fmap fst
49
56
return ([] , extract local <> extract nonLocal)
50
57
define $ \ LocalCompletions file -> do
58
+ let uri = fromNormalizedUri $ normalizedFilePathToUri file
51
59
pm <- useWithStale GetParsedModule file
52
60
case pm of
53
61
Just (pm, _) -> do
54
- let cdata = localCompletionsForParsedModule pm
62
+ let cdata = localCompletionsForParsedModule uri pm
55
63
return ([] , Just cdata)
56
64
_ -> return ([] , Nothing )
57
65
define $ \ NonLocalCompletions file -> do
@@ -77,7 +85,8 @@ produceCompletions = do
77
85
res <- liftIO $ tcRnImportDecls env (dropListFromImportDecl <$> imps)
78
86
case res of
79
87
(_, 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
81
90
return ([] , Just cdata)
82
91
(_diag, _) ->
83
92
return ([] , Nothing )
@@ -115,13 +124,15 @@ data NonLocalCompletions = NonLocalCompletions
115
124
instance Hashable NonLocalCompletions
116
125
instance NFData NonLocalCompletions
117
126
instance Binary NonLocalCompletions
127
+
118
128
-- | Generate code actions.
119
129
getCompletionsLSP
120
- :: LSP. LspFuncs Config
130
+ :: PluginId
131
+ -> LSP. LspFuncs Config
121
132
-> IdeState
122
133
-> CompletionParams
123
134
-> IO (Either ResponseError CompletionResponseResult )
124
- getCompletionsLSP lsp ide
135
+ getCompletionsLSP plId lsp ide
125
136
CompletionParams {_textDocument= TextDocumentIdentifier uri
126
137
,_position= position
127
138
,_context= completionContext} = do
@@ -145,8 +156,53 @@ getCompletionsLSP lsp ide
145
156
let clientCaps = clientCapabilities $ shakeExtras ide
146
157
config <- getClientConfig lsp
147
158
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
149
160
pure $ Completions (List allCompletions)
150
161
_ -> return (Completions $ List [] )
151
162
_ -> return (Completions $ List [] )
152
163
_ -> 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