1
1
{-# LANGUAGE CPP #-}
2
2
{-# LANGUAGE DataKinds #-}
3
3
{-# LANGUAGE DuplicateRecordFields #-}
4
+ {-# LANGUAGE LambdaCase #-}
4
5
{-# LANGUAGE MultiWayIf #-}
5
6
{-# LANGUAGE OverloadedStrings #-}
6
7
{-# LANGUAGE ViewPatterns #-}
@@ -25,14 +26,17 @@ import qualified Data.Map as M
25
26
import Data.Maybe (mapMaybe )
26
27
import qualified Data.Text as T
27
28
import Development.IDE hiding (line )
28
- import Development.IDE.Core.Compile (sourceParser ,
29
- sourceTypecheck )
29
+ import Development.IDE.Core.FileStore (getVersionedTextDoc )
30
30
import Development.IDE.Core.PluginUtils
31
31
import Development.IDE.GHC.Compat
32
+ import Development.IDE.GHC.Compat.Error (msgEnvelopeErrorL )
32
33
import Development.IDE.Plugin.Completions (ghcideCompletionsPluginPriority )
33
34
import Development.IDE.Plugin.Completions.Logic (getCompletionPrefixFromRope )
34
35
import Development.IDE.Plugin.Completions.Types (PosPrefixInfo (.. ))
35
36
import qualified Development.IDE.Spans.Pragmas as Pragmas
37
+ import GHC.Types.Error (GhcHint (SuggestExtension ),
38
+ LanguageExtensionHint (.. ),
39
+ diagnosticHints )
36
40
import Ide.Plugin.Error
37
41
import Ide.Types
38
42
import qualified Language.LSP.Protocol.Lens as L
@@ -74,19 +78,25 @@ suggestPragmaProvider = mkCodeActionProvider suggest
74
78
suggestDisableWarningProvider :: PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
75
79
suggestDisableWarningProvider = mkCodeActionProvider $ const suggestDisableWarning
76
80
77
- mkCodeActionProvider :: (Maybe DynFlags -> Diagnostic -> [PragmaEdit ]) -> PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
81
+ mkCodeActionProvider :: (Maybe DynFlags -> FileDiagnostic -> [PragmaEdit ]) -> PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
78
82
mkCodeActionProvider mkSuggest state _plId
79
- (LSP. CodeActionParams _ _ LSP. TextDocumentIdentifier { _uri = uri } _ (LSP. CodeActionContext diags _monly _)) = do
80
- normalizedFilePath <- getNormalizedFilePathE uri
83
+ (LSP. CodeActionParams _ _ docId@ LSP. TextDocumentIdentifier { _uri = uri } caRange _) = do
84
+ verTxtDocId <- liftIO $ runAction " classplugin.codeAction.getVersionedTextDoc" state $ getVersionedTextDoc docId
85
+ normalizedFilePath <- getNormalizedFilePathE (verTxtDocId ^. L. uri)
81
86
-- ghc session to get some dynflags even if module isn't parsed
82
87
(hscEnv -> hsc_dflags -> sessionDynFlags, _) <-
83
88
runActionE " Pragmas.GhcSession" state $ useWithStaleE GhcSession normalizedFilePath
84
89
fileContents <- liftIO $ runAction " Pragmas.GetFileContents" state $ getFileContents normalizedFilePath
85
90
parsedModule <- liftIO $ runAction " Pragmas.GetParsedModule" state $ getParsedModule normalizedFilePath
91
+
92
+
86
93
let parsedModuleDynFlags = ms_hspp_opts . pm_mod_summary <$> parsedModule
87
94
nextPragmaInfo = Pragmas. getNextPragmaInfo sessionDynFlags fileContents
88
- pedits = nubOrdOn snd $ concatMap (mkSuggest parsedModuleDynFlags) diags
89
- pure $ LSP. InL $ pragmaEditToAction uri nextPragmaInfo <$> pedits
95
+ activeDiagnosticsInRange (shakeExtras state) normalizedFilePath caRange >>= \ case
96
+ Nothing -> pure $ LSP. InL []
97
+ Just fileDiags -> do
98
+ let actions = concatMap (mkSuggest parsedModuleDynFlags) fileDiags
99
+ pure $ LSP. InL $ pragmaEditToAction uri nextPragmaInfo <$> nubOrdOn snd actions
90
100
91
101
92
102
@@ -115,15 +125,15 @@ pragmaEditToAction uri Pragmas.NextPragmaInfo{ nextPragmaLine, lineSplitTextEdit
115
125
Nothing
116
126
Nothing
117
127
118
- suggest :: Maybe DynFlags -> Diagnostic -> [PragmaEdit ]
128
+ suggest :: Maybe DynFlags -> FileDiagnostic -> [PragmaEdit ]
119
129
suggest dflags diag =
120
130
suggestAddPragma dflags diag
121
131
122
132
-- ---------------------------------------------------------------------
123
133
124
- suggestDisableWarning :: Diagnostic -> [PragmaEdit ]
134
+ suggestDisableWarning :: FileDiagnostic -> [PragmaEdit ]
125
135
suggestDisableWarning diagnostic
126
- | Just (Just (JSON. Array attachedReasons)) <- diagnostic ^? attachedReason
136
+ | Just (Just (JSON. Array attachedReasons)) <- diagnostic ^? fdLspDiagnosticL . attachedReason
127
137
=
128
138
[ (" Disable \" " <> w <> " \" warnings" , OptGHC w)
129
139
| JSON. String attachedReason <- Foldable. toList attachedReasons
@@ -143,13 +153,10 @@ warningBlacklist =
143
153
-- ---------------------------------------------------------------------
144
154
145
155
-- | Offer to add a missing Language Pragma to the top of a file.
146
- -- Pragmas are defined by a curated list of known pragmas, see 'possiblePragmas'.
147
- suggestAddPragma :: Maybe DynFlags -> Diagnostic -> [PragmaEdit ]
148
- suggestAddPragma mDynflags Diagnostic {_message, _source}
149
- | _source == Just sourceTypecheck || _source == Just sourceParser = genPragma _message
156
+ -- Pragmas are defined by a cuNewrated list of known pragmas, see 'possiblePragmas'.
157
+ suggestAddPragma :: Maybe DynFlags -> FileDiagnostic -> [PragmaEdit ]
158
+ suggestAddPragma mDynflags fd= [(" Add \" " <> r <> " \" " , LangExt r) | r <- map (T. pack . show ) $ suggestsExtension fd, r `notElem` disabled]
150
159
where
151
- genPragma target =
152
- [(" Add \" " <> r <> " \" " , LangExt r) | r <- findPragma target, r `notElem` disabled]
153
160
disabled
154
161
| Just dynFlags <- mDynflags =
155
162
-- GHC does not export 'OnOff', so we have to view it as string
@@ -158,25 +165,22 @@ suggestAddPragma mDynflags Diagnostic {_message, _source}
158
165
-- When the module failed to parse, we don't have access to its
159
166
-- dynFlags. In that case, simply don't disable any pragmas.
160
167
[]
161
- suggestAddPragma _ _ = []
162
168
163
- -- | Find all Pragmas are an infix of the search term.
164
- findPragma :: T. Text -> [T. Text ]
165
- findPragma str = concatMap check possiblePragmas
166
- where
167
- check p = [p | T. isInfixOf p str]
168
-
169
- -- We exclude the Strict extension as it causes many false positives, see
170
- -- the discussion at https://github.com/haskell/ghcide/pull/638
171
- --
172
- -- We don't include the No- variants, as GHC never suggests disabling an
173
- -- extension in an error message.
174
- possiblePragmas :: [T. Text ]
175
- possiblePragmas =
176
- [ name
177
- | FlagSpec {flagSpecName = T. pack -> name} <- xFlags
178
- , " Strict" /= name
179
- ]
169
+ suggestsExtension :: FileDiagnostic -> [Extension ]
170
+ suggestsExtension message = case message ^? fdStructuredMessageL . _SomeStructuredMessage . msgEnvelopeErrorL of
171
+ Just s -> concat $ mapMaybe (\ case
172
+ SuggestExtension s -> Just $ ghcHintSuggestsExtension s
173
+ _ -> Nothing ) (diagnosticHints s)
174
+ _ -> []
175
+
176
+ ghcHintSuggestsExtension :: LanguageExtensionHint -> [Extension ]
177
+ ghcHintSuggestsExtension (SuggestSingleExtension _ ext) = [ext]
178
+ ghcHintSuggestsExtension (SuggestAnyExtension _ (ext: _)) = [ext] -- ghc suggests any of those, we pick first
179
+ ghcHintSuggestsExtension (SuggestAnyExtension _ [] ) = []
180
+ ghcHintSuggestsExtension (SuggestExtensions _ ext) = ext
181
+ ghcHintSuggestsExtension (SuggestExtensionInOrderTo _ ext) = [ext]
182
+
183
+
180
184
181
185
-- | All language pragmas, including the No- variants
182
186
allPragmas :: [T. Text ]
0 commit comments