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