@@ -30,6 +30,8 @@ import Data.Char
30
30
import Data.Maybe
31
31
import Data.List.Extra
32
32
import qualified Data.Text as T
33
+ import Text.Regex.TDFA ((=~) , (=~~) )
34
+ import Text.Regex.TDFA.Text ()
33
35
34
36
-- | Generate code actions.
35
37
codeAction
@@ -85,14 +87,18 @@ executeAddSignatureCommand _lsp _ideState ExecuteCommandParams{..}
85
87
86
88
suggestAction :: Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
87
89
suggestAction contents diag@ Diagnostic {_range= _range@ Range {.. },.. }
90
+ -- The qualified import of ‘many’ from module ‘Control.Applicative’ is redundant
91
+ | Just [_, bindings] <- matchRegex _message " The( qualified)? import of ‘([^’]*)’ from module [^ ]* is redundant"
92
+ , Just c <- contents
93
+ , importLine <- textInRange _range c
94
+ = [( " Remove " <> bindings <> " from import"
95
+ , [TextEdit _range (dropBindingsFromImportLine (T. splitOn " ," bindings) importLine)])]
88
96
89
97
-- File.hs:16:1: warning:
90
98
-- The import of `Data.List' is redundant
91
99
-- except perhaps to import instances from `Data.List'
92
100
-- To import instances alone, use: import Data.List()
93
- | " The import of " `T.isInfixOf` _message
94
- || " The qualified import of " `T.isInfixOf` _message
95
- , " is redundant" `T.isInfixOf` _message
101
+ | _message =~ (" The( qualified)? import of [^ ]* is redundant" :: String )
96
102
= [(" Remove import" , [TextEdit (extendToWholeLineIfPossible contents _range) " " ])]
97
103
98
104
-- File.hs:52:41: error:
@@ -293,6 +299,51 @@ textInRange (Range (Position startRow startCol) (Position endRow endCol)) text =
293
299
where
294
300
linesBeginningWithStartLine = drop startRow (T. splitOn " \n " text)
295
301
302
+ -- | Drop all occurrences of a binding in an import line.
303
+ -- Preserves well-formedness but not whitespace between bindings.
304
+ --
305
+ -- >>> dropBindingsFromImportLine ["bA", "bC"] "import A(bA, bB,bC ,bA)"
306
+ -- "import A(bB)"
307
+ --
308
+ -- >>> dropBindingsFromImportLine ["+"] "import "P" qualified A as B ((+))"
309
+ -- "import "P" qualified A() as B hiding (bB)"
310
+ dropBindingsFromImportLine :: [T. Text ] -> T. Text -> T. Text
311
+ dropBindingsFromImportLine bindings_ importLine =
312
+ importPre <> " (" <> importRest'
313
+ where
314
+ bindings = map (wrapOperatorInParens . removeQualified) bindings_
315
+
316
+ (importPre, importRest) = T. breakOn " (" importLine
317
+
318
+ wrapOperatorInParens x = if isAlpha (T. head x) then x else " (" <> x <> " )"
319
+
320
+ removeQualified x = case T. breakOn " ." x of
321
+ (_qualifier, T. uncons -> Just (_, unqualified)) -> unqualified
322
+ _ -> x
323
+
324
+ importRest' = case T. uncons importRest of
325
+ Just (_, x) ->
326
+ T. intercalate " ,"
327
+ $ joinCloseParens
328
+ $ mapMaybe (filtering . T. strip)
329
+ $ T. splitOn " ," x
330
+ Nothing -> importRest
331
+
332
+ filtering x = case () of
333
+ () | x `elem` bindings -> Nothing
334
+ () | x `elem` map (<> " )" ) bindings -> Just " )"
335
+ _ -> Just x
336
+
337
+ joinCloseParens (x : " )" : rest) = (x <> " )" ) : joinCloseParens rest
338
+ joinCloseParens (x : rest) = x : joinCloseParens rest
339
+ joinCloseParens [] = []
340
+
341
+ -- | Returns Just (the submatches) for the first capture, or Nothing.
342
+ 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
346
+
296
347
setHandlersCodeAction :: PartialHandlers
297
348
setHandlersCodeAction = PartialHandlers $ \ WithMessage {.. } x -> return x{
298
349
LSP. codeActionHandler = withResponse RspCodeAction codeAction
0 commit comments