@@ -16,6 +16,7 @@ import Development.IDE.GHC.Compat
16
16
import Development.IDE.Core.Rules
17
17
import Development.IDE.Core.RuleTypes
18
18
import Development.IDE.Core.Shake
19
+ import Development.IDE.GHC.Error
19
20
import Development.IDE.LSP.Server
20
21
import Development.IDE.Types.Location
21
22
import qualified Data.HashMap.Strict as Map
@@ -85,8 +86,22 @@ executeAddSignatureCommand _lsp _ideState ExecuteCommandParams{..}
85
86
| otherwise
86
87
= return (Null , Nothing )
87
88
88
- suggestAction :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
89
- suggestAction contents diag@ Diagnostic {_range= _range@ Range {.. },.. }
89
+ suggestAction :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
90
+ suggestAction text diag = concat
91
+ [ suggestAddExtension diag
92
+ , suggestExtendImport text diag
93
+ , suggestFillHole diag
94
+ , suggestFillTypeWildcard diag
95
+ , suggestFixConstructorImport text diag
96
+ , suggestModuleTypo diag
97
+ , suggestRemoveRedundantImport text diag
98
+ , suggestReplaceIdentifier text diag
99
+ , suggestSignature True diag
100
+ ]
101
+
102
+
103
+ suggestRemoveRedundantImport :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
104
+ suggestRemoveRedundantImport contents Diagnostic {_range= _range@ Range {.. },.. }
90
105
-- The qualified import of ‘many’ from module ‘Control.Applicative’ is redundant
91
106
| Just [_, bindings] <- matchRegex _message " The( qualified)? import of ‘([^’]*)’ from module [^ ]* is redundant"
92
107
, Just c <- contents
@@ -100,7 +115,10 @@ suggestAction contents diag@Diagnostic{_range=_range@Range{..},..}
100
115
-- To import instances alone, use: import Data.List()
101
116
| _message =~ (" The( qualified)? import of [^ ]* is redundant" :: String )
102
117
= [(" Remove import" , [TextEdit (extendToWholeLineIfPossible contents _range) " " ])]
118
+ | otherwise = []
103
119
120
+ suggestReplaceIdentifier :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
121
+ suggestReplaceIdentifier contents Diagnostic {_range= _range@ Range {.. },.. }
104
122
-- File.hs:52:41: error:
105
123
-- * Variable not in scope:
106
124
-- suggestAcion :: Maybe T.Text -> Range -> Range
@@ -114,15 +132,21 @@ suggestAction contents diag@Diagnostic{_range=_range@Range{..},..}
114
132
-- Module ‘Data.Text’ does not export ‘isPrfixOf’.
115
133
| renameSuggestions@ (_: _) <- extractRenamableTerms _message
116
134
= [ (" Replace with ‘" <> name <> " ’" , [mkRenameEdit contents _range name]) | name <- renameSuggestions ]
135
+ | otherwise = []
117
136
137
+ suggestFillTypeWildcard :: Diagnostic -> [(T. Text , [TextEdit ])]
138
+ suggestFillTypeWildcard Diagnostic {_range= _range@ Range {.. },.. }
118
139
-- Foo.hs:3:8: error:
119
140
-- * Found type wildcard `_' standing for `p -> p1 -> p'
120
141
121
142
| " Found type wildcard" `T.isInfixOf` _message
122
143
, " standing for " `T.isInfixOf` _message
123
144
, typeSignature <- extractWildCardTypeSignature _message
124
145
= [(" Use type signature: ‘" <> typeSignature <> " ’" , [TextEdit _range typeSignature])]
146
+ | otherwise = []
125
147
148
+ suggestAddExtension :: Diagnostic -> [(T. Text , [TextEdit ])]
149
+ suggestAddExtension Diagnostic {_range= _range@ Range {.. },.. }
126
150
-- File.hs:22:8: error:
127
151
-- Illegal lambda-case (use -XLambdaCase)
128
152
-- File.hs:22:6: error:
@@ -145,7 +169,10 @@ suggestAction contents diag@Diagnostic{_range=_range@Range{..},..}
145
169
-- In the instance declaration for `Unit (m a)'
146
170
| exts@ (_: _) <- filter (`Set.member` ghcExtensions) $ T. split (not . isAlpha) $ T. replace " -X" " " _message
147
171
= [(" Add " <> x <> " extension" , [TextEdit (Range (Position 0 0 ) (Position 0 0 )) $ " {-# LANGUAGE " <> x <> " #-}\n " ]) | x <- exts]
172
+ | otherwise = []
148
173
174
+ suggestModuleTypo :: Diagnostic -> [(T. Text , [TextEdit ])]
175
+ suggestModuleTypo Diagnostic {_range= _range@ Range {.. },.. }
149
176
-- src/Development/IDE/Core/Compile.hs:58:1: error:
150
177
-- Could not find module ‘Data.Cha’
151
178
-- Perhaps you meant Data.Char (from base-4.12.0.0)
@@ -154,7 +181,10 @@ suggestAction contents diag@Diagnostic{_range=_range@Range{..},..}
154
181
findSuggestedModules = map (head . T. words ) . drop 2 . T. lines
155
182
proposeModule mod = (" replace with " <> mod , [TextEdit _range mod ])
156
183
in map proposeModule $ nubOrd $ findSuggestedModules _message
184
+ | otherwise = []
157
185
186
+ suggestFillHole :: Diagnostic -> [(T. Text , [TextEdit ])]
187
+ suggestFillHole Diagnostic {_range= _range@ Range {.. },.. }
158
188
-- ...Development/IDE/LSP/CodeAction.hs:103:9: warning:
159
189
-- * Found hole: _ :: Int -> String
160
190
-- * In the expression: _
@@ -187,9 +217,36 @@ suggestAction contents diag@Diagnostic{_range=_range@Range{..},..}
187
217
extractFitNames = map (T. strip . head . T. splitOn " :: " )
188
218
in map proposeHoleFit $ nubOrd $ findSuggestedHoleFits _message
189
219
190
- | tlb @ [_] <- suggestSignature True diag = tlb
220
+ | otherwise = []
191
221
192
- suggestAction _ _ = []
222
+ suggestExtendImport :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
223
+ suggestExtendImport contents Diagnostic {_range= _range,.. }
224
+ | Just [binding, mod , srcspan] <-
225
+ matchRegex _message
226
+ " Perhaps you want to add ‘([^’]*)’ to the import list in the import of ‘([^’]*)’ *\\ ((.*)\\ ).$"
227
+ , Just c <- contents
228
+ = let range = case [ x | (x," " ) <- readSrcSpan (T. unpack srcspan)] of
229
+ [s] -> let x = srcSpanToRange s
230
+ in x{_end = (_end x){_character = succ (_character (_end x))}}
231
+ _ -> error " bug in srcspan parser"
232
+ importLine = textInRange range c
233
+ in [(" Add " <> binding <> " to the import list of " <> mod
234
+ , [TextEdit range (addBindingToImportList binding importLine)])]
235
+ | otherwise = []
236
+
237
+ suggestFixConstructorImport :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
238
+ suggestFixConstructorImport _ Diagnostic {_range= _range,.. }
239
+ -- ‘Success’ is a data constructor of ‘Result’
240
+ -- To import it use
241
+ -- import Data.Aeson.Types( Result( Success ) )
242
+ -- or
243
+ -- import Data.Aeson.Types( Result(..) ) (lsp-ui)
244
+ | Just [constructor, typ] <-
245
+ matchRegex _message
246
+ " ‘([^’]*)’ is a data constructor of ‘([^’]*)’ To import it use"
247
+ = let fixedImport = typ <> " (" <> constructor <> " )"
248
+ in [(" Fix import of " <> fixedImport, [TextEdit _range fixedImport])]
249
+ | otherwise = []
193
250
194
251
suggestSignature :: Bool -> Diagnostic -> [(T. Text , [TextEdit ])]
195
252
suggestSignature isQuickFix Diagnostic {_range= _range@ Range {.. },.. }
@@ -282,6 +339,7 @@ splitTextAtPosition (Position row col) x
282
339
= (T. intercalate " \n " $ preRow ++ [preCol], T. intercalate " \n " $ postCol : postRow)
283
340
| otherwise = (x, T. empty)
284
341
342
+ -- | Returns [start .. end[
285
343
textInRange :: Range -> T. Text -> T. Text
286
344
textInRange (Range (Position startRow startCol) (Position endRow endCol)) text =
287
345
case compare startRow endRow of
@@ -338,11 +396,27 @@ dropBindingsFromImportLine bindings_ importLine =
338
396
joinCloseParens (x : rest) = x : joinCloseParens rest
339
397
joinCloseParens [] = []
340
398
399
+ -- | Extends an import list with a new binding.
400
+ -- Assumes an import statement of the form:
401
+ -- import (qualified) A (..) ..
402
+ -- Places the new binding first, preserving whitespace.
403
+ -- Copes with multi-line import lists
404
+ addBindingToImportList :: T. Text -> T. Text -> T. Text
405
+ addBindingToImportList binding importLine = case T. breakOn " (" importLine of
406
+ (pre, T. uncons -> Just (_, rest)) ->
407
+ case T. uncons (T. dropWhile isSpace rest) of
408
+ Just (' )' , _) -> T. concat [pre, " (" , binding, rest]
409
+ _ -> T. concat [pre, " (" , binding, " , " , rest]
410
+ _ ->
411
+ error
412
+ $ " importLine does not have the expected structure: "
413
+ <> T. unpack importLine
414
+
341
415
-- | Returns Just (the submatches) for the first capture, or Nothing.
342
416
matchRegex :: T. Text -> T. Text -> Maybe [T. Text ]
343
- matchRegex message regex = case message =~~ regex of
344
- Just (_ :: T. Text , _ :: T. Text , _ :: T. Text , bindings ) -> Just bindings
345
- Nothing -> Nothing
417
+ matchRegex message regex = case T. unwords ( T. words message) =~~ regex of
418
+ Just (_ :: T. Text , _ :: T. Text , _ :: T. Text , bindings ) -> Just bindings
419
+ Nothing -> Nothing
346
420
347
421
setHandlersCodeAction :: PartialHandlers
348
422
setHandlersCodeAction = PartialHandlers $ \ WithMessage {.. } x -> return x{
0 commit comments