@@ -18,6 +18,8 @@ import Data.Hashable
1818import Data.HashMap.Strict (HashMap )
1919import qualified Data.HashMap.Strict as HashMap
2020import qualified Data.List.NonEmpty as NE
21+ import Data.Maybe (mapMaybe )
22+ import qualified Data.Text as T
2123import qualified Data.Text.Encoding as Encoding
2224import Data.Typeable
2325import Development.IDE as D
@@ -89,7 +91,7 @@ descriptor recorder plId =
8991 mconcat
9092 [ mkPluginHandler LSP. SMethod_TextDocumentCodeAction licenseSuggestCodeAction
9193 , mkPluginHandler LSP. SMethod_TextDocumentCompletion $ completion recorder
92- , mkPluginHandler LSP. SMethod_TextDocumentCodeAction fieldSuggestCodeAction
94+ , mkPluginHandler LSP. SMethod_TextDocumentCodeAction $ fieldSuggestCodeAction recorder
9395 ]
9496 , pluginNotificationHandlers =
9597 mconcat
@@ -230,9 +232,34 @@ licenseSuggestCodeAction :: PluginMethodHandler IdeState 'LSP.Method_TextDocumen
230232licenseSuggestCodeAction _ _ (CodeActionParams _ _ (TextDocumentIdentifier uri) _range CodeActionContext {_diagnostics= diags}) =
231233 pure $ InL $ diags >>= (fmap InR . LicenseSuggest. licenseErrorAction uri)
232234
233- fieldSuggestCodeAction :: PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
234- fieldSuggestCodeAction _ _ (CodeActionParams _ _ (TextDocumentIdentifier uri) _range CodeActionContext {_diagnostics= diags}) =
235- pure $ InL $ diags >>= (fmap InR . FieldSuggest. fieldErrorAction uri)
235+ -- | CodeActions for correcting field names with typos in them.
236+ --
237+ -- Provides CodeActions that fix typos in field names, in both stanzas and top-level field names.
238+ -- The suggestions are computed based on the completion context, where we "move" a fake cursor
239+ -- to the end of the field name and trigger cabal file completions. The completions are then
240+ -- suggested to the user.
241+ fieldSuggestCodeAction :: Recorder (WithPriority Log ) -> PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
242+ fieldSuggestCodeAction recorder ide _ (CodeActionParams _ _ (TextDocumentIdentifier uri) _ CodeActionContext {_diagnostics= diags}) = do
243+ vfileM <- lift (getVirtualFile $ toNormalizedUri uri)
244+ case (,) <$> vfileM <*> uriToFilePath' uri of
245+ Nothing -> pure $ InL []
246+ Just (vfile, path) -> do
247+ mFields <- liftIO $ runIdeAction " cabal-plugin.fields" (shakeExtras ide) $ useWithStaleFast ParseCabalFields $ toNormalizedFilePath path
248+ case mFields of
249+ Nothing ->
250+ pure $ InL []
251+ Just (fields, _) -> do
252+ let errorFields = mapMaybe FieldSuggest. fieldErrorName diags
253+ results <- forM errorFields (getSuggestion fields vfile path)
254+ pure $ InL $ map InR $ concat results
255+ where
256+ getSuggestion fields vfile fp (field,Diagnostic { _range= _range@ (Range (Position lineNr col) _) })= do
257+ let -- Compute where we would anticipate the cursor to be.
258+ fakeLspCursorPosition = Position lineNr (col + fromIntegral (T. length field))
259+ lspPrefixInfo = Ghcide. getCompletionPrefix fakeLspCursorPosition vfile
260+ completions <- liftIO $ produceCompletions recorder lspPrefixInfo fp fields (shakeExtras ide)
261+ let completionTexts = (fmap (^. JL. label) completions)
262+ pure $ FieldSuggest. fieldErrorAction uri field completionTexts _range
236263
237264-- ----------------------------------------------------------------
238265-- Cabal file of Interest rules and global variable
@@ -326,32 +353,31 @@ completion recorder ide _ complParams = do
326353 pure . InR $ InR Null
327354 Just (fields, _) -> do
328355 let pref = Ghcide. getCompletionPrefix position cnts
329- let res = produceCompletions pref path fields
356+ let res = produceCompletions recorder pref path fields (shakeExtras ide)
330357 liftIO $ fmap InL res
331358 Nothing -> pure . InR $ InR Null
332- where
333- completerRecorder = cmapWithPrio LogCompletions recorder
334359
335- produceCompletions :: Ghcide. PosPrefixInfo -> FilePath -> [Syntax. Field Syntax. Position ] -> IO [CompletionItem ]
336- produceCompletions prefix fp fields = do
337- runMaybeT (context fields) >>= \ case
338- Nothing -> pure []
339- Just ctx -> do
340- logWith recorder Debug $ LogCompletionContext ctx pos
341- let completer = Completions. contextToCompleter ctx
342- let completerData = CompleterTypes. CompleterData
343- { getLatestGPD = do
344- mGPD <- runIdeAction " cabal-plugin.modulesCompleter.gpd" (shakeExtras ide) $ useWithStaleFast ParseCabalFile $ toNormalizedFilePath fp
345- pure $ fmap fst mGPD
346- , cabalPrefixInfo = prefInfo
347- , stanzaName =
348- case fst ctx of
349- Types. Stanza _ name -> name
350- _ -> Nothing
351- }
352- completions <- completer completerRecorder completerData
353- pure completions
354- where
355- pos = Ghcide. cursorPos prefix
356- context fields = Completions. getContext completerRecorder prefInfo fields
357- prefInfo = Completions. getCabalPrefixInfo fp prefix
360+ produceCompletions :: Recorder (WithPriority Log ) -> Ghcide. PosPrefixInfo -> FilePath -> [Syntax. Field Syntax. Position ] -> ShakeExtras -> IO [CompletionItem ]
361+ produceCompletions recorder prefix fp fields extras = do
362+ runMaybeT (context fields) >>= \ case
363+ Nothing -> pure []
364+ Just ctx -> do
365+ logWith recorder Debug $ LogCompletionContext ctx pos
366+ let completer = Completions. contextToCompleter ctx
367+ let completerData = CompleterTypes. CompleterData
368+ { getLatestGPD = do
369+ mGPD <- runIdeAction " cabal-plugin.modulesCompleter.gpd" extras $ useWithStaleFast ParseCabalFile $ toNormalizedFilePath fp
370+ pure $ fmap fst mGPD
371+ , cabalPrefixInfo = prefInfo
372+ , stanzaName =
373+ case fst ctx of
374+ Types. Stanza _ name -> name
375+ _ -> Nothing
376+ }
377+ completions <- completer completerRecorder completerData
378+ pure completions
379+ where
380+ pos = Ghcide. cursorPos prefix
381+ context fields = Completions. getContext completerRecorder prefInfo fields
382+ prefInfo = Completions. getCabalPrefixInfo fp prefix
383+ completerRecorder = cmapWithPrio LogCompletions recorder
0 commit comments