@@ -18,7 +18,10 @@ import Data.Hashable
18
18
import Data.HashMap.Strict (HashMap )
19
19
import qualified Data.HashMap.Strict as HashMap
20
20
import qualified Data.List.NonEmpty as NE
21
+ import Data.Maybe (mapMaybe )
22
+ import qualified Data.Text as T
21
23
import qualified Data.Text.Encoding as Encoding
24
+ import Data.Text.Utf16.Rope.Mixed (Rope )
22
25
import Data.Typeable
23
26
import Development.IDE as D
24
27
import Development.IDE.Core.Shake (restartShakeSession )
@@ -32,8 +35,8 @@ import qualified Ide.Plugin.Cabal.Completion.Completer.Types as CompleterTypes
32
35
import qualified Ide.Plugin.Cabal.Completion.Completions as Completions
33
36
import qualified Ide.Plugin.Cabal.Completion.Types as Types
34
37
import qualified Ide.Plugin.Cabal.Diagnostics as Diagnostics
35
- import qualified Ide.Plugin.Cabal.LicenseSuggest as LicenseSuggest
36
38
import qualified Ide.Plugin.Cabal.FieldSuggest as FieldSuggest
39
+ import qualified Ide.Plugin.Cabal.LicenseSuggest as LicenseSuggest
37
40
import qualified Ide.Plugin.Cabal.Parse as Parse
38
41
import Ide.Types
39
42
import qualified Language.LSP.Protocol.Lens as JL
@@ -84,7 +87,7 @@ descriptor recorder plId =
84
87
mconcat
85
88
[ mkPluginHandler LSP. SMethod_TextDocumentCodeAction licenseSuggestCodeAction
86
89
, mkPluginHandler LSP. SMethod_TextDocumentCompletion $ completion recorder
87
- , mkPluginHandler LSP. SMethod_TextDocumentCodeAction fieldSuggestCodeAction
90
+ , mkPluginHandler LSP. SMethod_TextDocumentCodeAction $ fieldSuggestCodeAction recorder
88
91
]
89
92
, pluginNotificationHandlers =
90
93
mconcat
@@ -200,9 +203,27 @@ licenseSuggestCodeAction :: PluginMethodHandler IdeState 'LSP.Method_TextDocumen
200
203
licenseSuggestCodeAction _ _ (CodeActionParams _ _ (TextDocumentIdentifier uri) _range CodeActionContext {_diagnostics= diags}) =
201
204
pure $ InL $ diags >>= (fmap InR . LicenseSuggest. licenseErrorAction uri)
202
205
203
- fieldSuggestCodeAction :: PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
204
- fieldSuggestCodeAction _ _ (CodeActionParams _ _ (TextDocumentIdentifier uri) _range CodeActionContext {_diagnostics= diags}) =
205
- pure $ InL $ diags >>= (fmap InR . FieldSuggest. fieldErrorAction uri)
206
+ -- | CodeActions for misspelled fields in cabal files both for toplevel fields, and fields in stanzas.
207
+ -- Uses same logic as completions but reacts on diagnostics from cabal.
208
+ fieldSuggestCodeAction :: Recorder (WithPriority Log ) -> PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
209
+ fieldSuggestCodeAction recorder ide _ (CodeActionParams _ _ (TextDocumentIdentifier uri) _ CodeActionContext {_diagnostics= diags}) = do
210
+ vfileM <- lift (getVirtualFile $ toNormalizedUri uri)
211
+ case (,) <$> vfileM <*> uriToFilePath' uri of
212
+ Nothing -> pure $ InL []
213
+ Just (vfile, path) -> do
214
+ let fields = mapMaybe FieldSuggest. fieldErrorName diags
215
+ results <- forM fields (getSuggestion vfile path)
216
+ pure $ InL $ map InR $ concat results
217
+ where
218
+ getSuggestion vfile fp (field,Diagnostic { _range= _range@ (Range (Position lineNr col) _) })= do
219
+ let -- compute where we would anticipate the cursor to be.
220
+ -- This is an heuristic and could be incorrect.
221
+ fakeLspCursorPosition = Position lineNr (col + fromIntegral (T. length field))
222
+ lspPrefixInfo = Ghcide. getCompletionPrefix fakeLspCursorPosition vfile
223
+ cabalPrefixInfo = Completions. getCabalPrefixInfo fp lspPrefixInfo
224
+ completions <- liftIO $ computeCompletionsAt recorder cabalPrefixInfo fp (vfile ^. VFS. file_text) (shakeExtras ide)
225
+ let completionTexts = (fmap (^. JL. label) completions)
226
+ pure $ FieldSuggest. fieldErrorAction uri field completionTexts _range
206
227
207
228
-- ----------------------------------------------------------------
208
229
-- Cabal file of Interest rules and global variable
@@ -290,32 +311,32 @@ completion recorder ide _ complParams = do
290
311
contents <- lift $ getVirtualFile $ toNormalizedUri uri
291
312
case (contents, uriToFilePath' uri) of
292
313
(Just cnts, Just path) -> do
293
- let pref = Ghcide. getCompletionPrefix position cnts
294
- let res = result pref path cnts
295
- liftIO $ fmap InL res
314
+ let lspPrefixInfo = Ghcide. getCompletionPrefix position cnts
315
+ cabalPrefixInfo = Completions. getCabalPrefixInfo path lspPrefixInfo
316
+ let compls = computeCompletionsAt recorder cabalPrefixInfo path (cnts ^. VFS. file_text) (shakeExtras ide)
317
+ liftIO $ fmap InL compls
296
318
_ -> pure . InR $ InR Null
297
- where
298
- result :: Ghcide. PosPrefixInfo -> FilePath -> VFS. VirtualFile -> IO [CompletionItem ]
299
- result prefix fp cnts = do
300
- runMaybeT context >>= \ case
301
- Nothing -> pure []
302
- Just ctx -> do
303
- logWith recorder Debug $ LogCompletionContext ctx pos
304
- let completer = Completions. contextToCompleter ctx
305
- let completerData = CompleterTypes. CompleterData
306
- { getLatestGPD = do
307
- mGPD <- runIdeAction " cabal-plugin.modulesCompleter.gpd" (shakeExtras ide) $ useWithStaleFast Types. GetCabalDiagnostics $ toNormalizedFilePath fp
308
- pure $ fmap fst mGPD
309
- , cabalPrefixInfo = prefInfo
310
- , stanzaName =
311
- case fst ctx of
312
- Types. Stanza _ name -> name
313
- _ -> Nothing
314
- }
315
- completions <- completer completerRecorder completerData
316
- pure completions
317
- where
318
- completerRecorder = cmapWithPrio LogCompletions recorder
319
- pos = Ghcide. cursorPos prefix
320
- context = Completions. getContext completerRecorder prefInfo (cnts ^. VFS. file_text)
321
- prefInfo = Completions. getCabalPrefixInfo fp prefix
319
+
320
+ computeCompletionsAt :: Recorder (WithPriority Log ) -> Types. CabalPrefixInfo -> FilePath -> Rope -> ShakeExtras -> IO [CompletionItem ]
321
+ computeCompletionsAt recorder cabalPrefixInfo fp fileRope extras = do
322
+ runMaybeT context >>= \ case
323
+ Nothing -> pure []
324
+ Just ctx -> do
325
+ logWith recorder Debug $ LogCompletionContext ctx pos
326
+ let completer = Completions. contextToCompleter ctx
327
+ let completerData = CompleterTypes. CompleterData
328
+ { getLatestGPD = do
329
+ mGPD <- runIdeAction " computeCompletionsAt.gpd" extras $ useWithStaleFast Types. GetCabalDiagnostics $ toNormalizedFilePath fp
330
+ pure $ fmap fst mGPD
331
+ , cabalPrefixInfo = cabalPrefixInfo
332
+ , stanzaName =
333
+ case fst ctx of
334
+ Types. Stanza _ name -> name
335
+ _ -> Nothing
336
+ }
337
+ completions <- completer completerRecorder completerData
338
+ pure completions
339
+ where
340
+ completerRecorder = cmapWithPrio LogCompletions recorder
341
+ pos = Types. completionCursorPosition cabalPrefixInfo
342
+ context = Completions. getContext completerRecorder cabalPrefixInfo fileRope
0 commit comments