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 #-}
@@ -27,12 +28,17 @@ import qualified Data.Text as T
27
28
import Development.IDE hiding (line )
28
29
import Development.IDE.Core.Compile (sourceParser ,
29
30
sourceTypecheck )
31
+ import Development.IDE.Core.FileStore (getVersionedTextDoc )
30
32
import Development.IDE.Core.PluginUtils
31
33
import Development.IDE.GHC.Compat
34
+ import Development.IDE.GHC.Compat.Error (msgEnvelopeErrorL )
32
35
import Development.IDE.Plugin.Completions (ghcideCompletionsPluginPriority )
33
36
import Development.IDE.Plugin.Completions.Logic (getCompletionPrefixFromRope )
34
37
import Development.IDE.Plugin.Completions.Types (PosPrefixInfo (.. ))
35
38
import qualified Development.IDE.Spans.Pragmas as Pragmas
39
+ import GHC.Types.Error (GhcHint (SuggestExtension ),
40
+ LanguageExtensionHint (.. ),
41
+ diagnosticHints )
36
42
import Ide.Plugin.Error
37
43
import Ide.Types
38
44
import qualified Language.LSP.Protocol.Lens as L
@@ -69,13 +75,33 @@ data Pragma = LangExt T.Text | OptGHC T.Text
69
75
deriving (Show , Eq , Ord )
70
76
71
77
suggestPragmaProvider :: PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
72
- suggestPragmaProvider = mkCodeActionProvider suggest
78
+ suggestPragmaProvider = if ghcVersion /= GHC96 then
79
+ mkCodeActionProvider suggestAddPragma
80
+ else mkCodeActionProvider96 suggestAddPragma96
73
81
74
82
suggestDisableWarningProvider :: PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
75
83
suggestDisableWarningProvider = mkCodeActionProvider $ const suggestDisableWarning
76
84
77
- mkCodeActionProvider :: (Maybe DynFlags -> Diagnostic -> [PragmaEdit ]) -> PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
85
+ mkCodeActionProvider :: (Maybe DynFlags -> FileDiagnostic -> [PragmaEdit ]) -> PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
78
86
mkCodeActionProvider mkSuggest state _plId
87
+ (LSP. CodeActionParams _ _ docId@ LSP. TextDocumentIdentifier { _uri = uri } caRange _) = do
88
+ verTxtDocId <- liftIO $ runAction " classplugin.codeAction.getVersionedTextDoc" state $ getVersionedTextDoc docId
89
+ normalizedFilePath <- getNormalizedFilePathE (verTxtDocId ^. L. uri)
90
+ -- ghc session to get some dynflags even if module isn't parsed
91
+ (hscEnv -> hsc_dflags -> sessionDynFlags, _) <-
92
+ runActionE " Pragmas.GhcSession" state $ useWithStaleE GhcSession normalizedFilePath
93
+ fileContents <- liftIO $ runAction " Pragmas.GetFileContents" state $ getFileContents normalizedFilePath
94
+ parsedModule <- liftIO $ runAction " Pragmas.GetParsedModule" state $ getParsedModule normalizedFilePath
95
+ let parsedModuleDynFlags = ms_hspp_opts . pm_mod_summary <$> parsedModule
96
+ nextPragmaInfo = Pragmas. getNextPragmaInfo sessionDynFlags fileContents
97
+ activeDiagnosticsInRange (shakeExtras state) normalizedFilePath caRange >>= \ case
98
+ Nothing -> pure $ LSP. InL []
99
+ Just fileDiags -> do
100
+ let actions = concatMap (mkSuggest parsedModuleDynFlags) fileDiags
101
+ pure $ LSP. InL $ pragmaEditToAction uri nextPragmaInfo <$> nubOrdOn snd actions
102
+
103
+ mkCodeActionProvider96 :: (Maybe DynFlags -> Diagnostic -> [PragmaEdit ]) -> PluginMethodHandler IdeState 'LSP.Method_TextDocumentCodeAction
104
+ mkCodeActionProvider96 mkSuggest state _plId
79
105
(LSP. CodeActionParams _ _ LSP. TextDocumentIdentifier { _uri = uri } _ (LSP. CodeActionContext diags _monly _)) = do
80
106
normalizedFilePath <- getNormalizedFilePathE uri
81
107
-- ghc session to get some dynflags even if module isn't parsed
@@ -89,7 +115,6 @@ mkCodeActionProvider mkSuggest state _plId
89
115
pure $ LSP. InL $ pragmaEditToAction uri nextPragmaInfo <$> pedits
90
116
91
117
92
-
93
118
-- | Add a Pragma to the given URI at the top of the file.
94
119
-- It is assumed that the pragma name is a valid pragma,
95
120
-- thus, not validated.
@@ -108,22 +133,17 @@ pragmaEditToAction uri Pragmas.NextPragmaInfo{ nextPragmaLine, lineSplitTextEdit
108
133
, let LSP. TextEdit { _range, _newText } = insertTextEdit ->
109
134
[LSP. TextEdit _range (render p <> _newText), deleteTextEdit]
110
135
| otherwise -> [LSP. TextEdit pragmaInsertRange (render p)]
111
-
112
136
edit =
113
137
LSP. WorkspaceEdit
114
138
(Just $ M. singleton uri textEdits)
115
139
Nothing
116
140
Nothing
117
141
118
- suggest :: Maybe DynFlags -> Diagnostic -> [PragmaEdit ]
119
- suggest dflags diag =
120
- suggestAddPragma dflags diag
121
-
122
142
-- ---------------------------------------------------------------------
123
143
124
- suggestDisableWarning :: Diagnostic -> [PragmaEdit ]
144
+ suggestDisableWarning :: FileDiagnostic -> [PragmaEdit ]
125
145
suggestDisableWarning diagnostic
126
- | Just (Just (JSON. Array attachedReasons)) <- diagnostic ^? attachedReason
146
+ | Just (Just (JSON. Array attachedReasons)) <- diagnostic ^? fdLspDiagnosticL . attachedReason
127
147
=
128
148
[ (" Disable \" " <> w <> " \" warnings" , OptGHC w)
129
149
| JSON. String attachedReason <- Foldable. toList attachedReasons
@@ -142,10 +162,24 @@ warningBlacklist =
142
162
143
163
-- ---------------------------------------------------------------------
144
164
165
+ -- | Offer to add a missing Language Pragma to the top of a file.
166
+ suggestAddPragma :: Maybe DynFlags -> FileDiagnostic -> [PragmaEdit ]
167
+ suggestAddPragma mDynflags fd= [(" Add \" " <> r <> " \" " , LangExt r) | r <- map (T. pack . show ) $ suggestsExtension fd, r `notElem` disabled]
168
+ where
169
+ disabled
170
+ | Just dynFlags <- mDynflags =
171
+ -- GHC does not export 'OnOff', so we have to view it as string
172
+ mapMaybe (T. stripPrefix " Off " . printOutputable) (extensions dynFlags)
173
+ | otherwise =
174
+ -- When the module failed to parse, we don't have access to its
175
+ -- dynFlags. In that case, simply don't disable any pragmas.
176
+ []
177
+
145
178
-- | Offer to add a missing Language Pragma to the top of a file.
146
179
-- Pragmas are defined by a curated list of known pragmas, see 'possiblePragmas'.
147
- suggestAddPragma :: Maybe DynFlags -> Diagnostic -> [PragmaEdit ]
148
- suggestAddPragma mDynflags Diagnostic {_message, _source}
180
+ -- Kept for compatibility with ghc9.6 - it is missing some structured diagnostics
181
+ suggestAddPragma96 :: Maybe DynFlags -> Diagnostic -> [PragmaEdit ]
182
+ suggestAddPragma96 mDynflags Diagnostic {_message, _source}
149
183
| _source == Just sourceTypecheck || _source == Just sourceParser = genPragma _message
150
184
where
151
185
genPragma target =
@@ -158,7 +192,7 @@ suggestAddPragma mDynflags Diagnostic {_message, _source}
158
192
-- When the module failed to parse, we don't have access to its
159
193
-- dynFlags. In that case, simply don't disable any pragmas.
160
194
[]
161
- suggestAddPragma _ _ = []
195
+ suggestAddPragma96 _ _ = []
162
196
163
197
-- | Find all Pragmas are an infix of the search term.
164
198
findPragma :: T. Text -> [T. Text ]
@@ -178,6 +212,20 @@ findPragma str = concatMap check possiblePragmas
178
212
, " Strict" /= name
179
213
]
180
214
215
+ suggestsExtension :: FileDiagnostic -> [Extension ]
216
+ suggestsExtension message = case message ^? fdStructuredMessageL . _SomeStructuredMessage . msgEnvelopeErrorL of
217
+ Just s -> concat $ map (\ case
218
+ SuggestExtension s -> ghcHintSuggestsExtension s
219
+ _ -> [] ) (diagnosticHints s)
220
+ _ -> []
221
+
222
+ ghcHintSuggestsExtension :: LanguageExtensionHint -> [Extension ]
223
+ ghcHintSuggestsExtension (SuggestSingleExtension _ ext) = [ext]
224
+ ghcHintSuggestsExtension (SuggestAnyExtension _ (ext: _)) = [ext] -- ghc suggests any of those, we pick first
225
+ ghcHintSuggestsExtension (SuggestAnyExtension _ [] ) = []
226
+ ghcHintSuggestsExtension (SuggestExtensions _ ext) = ext
227
+ ghcHintSuggestsExtension (SuggestExtensionInOrderTo _ ext) = [ext]
228
+
181
229
-- | All language pragmas, including the No- variants
182
230
allPragmas :: [T. Text ]
183
231
allPragmas =
0 commit comments