@@ -19,21 +19,23 @@ import Data.Hashable
1919import Data.HashMap.Strict (HashMap )
2020import qualified Data.HashMap.Strict as HashMap
2121import qualified Data.List.NonEmpty as NE
22+ import Data.Maybe (mapMaybe )
23+ import qualified Data.Text as T
2224import qualified Data.Text.Encoding as Encoding
25+ import Data.Text.Utf16.Rope.Mixed (Rope )
2326import Data.Typeable
2427import Development.IDE as D
2528import Development.IDE.Core.Shake (restartShakeSession )
2629import qualified Development.IDE.Core.Shake as Shake
2730import Development.IDE.Graph (alwaysRerun )
2831import qualified Development.IDE.Plugin.Completions.Logic as Ghcide
29- import qualified Development.IDE.Plugin.Completions.Types as Ghcide
3032import GHC.Generics
3133import qualified Ide.Plugin.Cabal.Completion.Completer.Types as CompleterTypes
3234import qualified Ide.Plugin.Cabal.Completion.Completions as Completions
3335import qualified Ide.Plugin.Cabal.Completion.Types as Types
3436import qualified Ide.Plugin.Cabal.Diagnostics as Diagnostics
35- import qualified Ide.Plugin.Cabal.LicenseSuggest as LicenseSuggest
3637import qualified Ide.Plugin.Cabal.FieldSuggest as FieldSuggest
38+ import qualified Ide.Plugin.Cabal.LicenseSuggest as LicenseSuggest
3739import qualified Ide.Plugin.Cabal.Parse as Parse
3840import Ide.Types
3941import qualified Language.LSP.Protocol.Lens as JL
@@ -84,7 +86,7 @@ descriptor recorder plId =
8486 mconcat
8587 [ mkPluginHandler LSP. SMethod_TextDocumentCodeAction licenseSuggestCodeAction
8688 , mkPluginHandler LSP. SMethod_TextDocumentCompletion $ completion recorder
87- , mkPluginHandler LSP. SMethod_TextDocumentCodeAction fieldSuggestCodeAction
89+ , mkPluginHandler LSP. SMethod_TextDocumentCodeAction $ fieldSuggestCodeAction recorder
8890 ]
8991 , pluginNotificationHandlers =
9092 mconcat
@@ -199,9 +201,27 @@ licenseSuggestCodeAction :: PluginMethodHandler IdeState 'LSP.Method_TextDocumen
199201licenseSuggestCodeAction _ _ (CodeActionParams _ _ (TextDocumentIdentifier uri) _range CodeActionContext {_diagnostics= diags}) =
200202 pure $ InL $ diags >>= (fmap InR . LicenseSuggest. licenseErrorAction uri)
201203
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 (,) <$> 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
205225
206226-- ----------------------------------------------------------------
207227-- Cabal file of Interest rules and global variable
@@ -287,32 +307,32 @@ completion recorder ide _ complParams = do
287307 contents <- lift $ getVirtualFile $ toNormalizedUri uri
288308 case (contents, uriToFilePath' uri) of
289309 (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
293314 _ -> 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