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