@@ -22,14 +22,15 @@ import Control.Arrow (second,
22
22
(&&&) ,
23
23
(>>>) )
24
24
import Control.Concurrent.STM.Stats (atomically )
25
+ import Control.Lens hiding (List ,
26
+ uncons , use )
25
27
import Control.Monad.Extra
26
28
import Control.Monad.IO.Class
27
29
import Control.Monad.Trans.Except (ExceptT (ExceptT ))
28
30
import Control.Monad.Trans.Maybe
29
31
import Data.Char
30
32
import qualified Data.DList as DL
31
33
import Data.Function
32
- import Data.Functor
33
34
import qualified Data.HashMap.Strict as Map
34
35
import qualified Data.HashSet as Set
35
36
import Data.List.Extra
@@ -49,6 +50,9 @@ import Development.IDE.Core.Service
49
50
import Development.IDE.Core.Shake hiding (Log )
50
51
import Development.IDE.GHC.Compat hiding
51
52
(ImplicitPrelude )
53
+ import Development.IDE.GHC.Compat.Error (TcRnMessage (.. ),
54
+ _TcRnMessage ,
55
+ msgEnvelopeErrorL )
52
56
#if !MIN_VERSION_ghc(9,11,0)
53
57
import Development.IDE.GHC.Compat.Util
54
58
#endif
@@ -78,6 +82,8 @@ import GHC (DeltaPos (..
78
82
import GHC.Iface.Ext.Types (ContextInfo (.. ),
79
83
IdentifierDetails (.. ))
80
84
import qualified GHC.LanguageExtensions as Lang
85
+ import GHC.Tc.Errors.Types (UnusedImportName (.. ),
86
+ UnusedImportReason (.. ))
81
87
import Ide.Logger hiding
82
88
(group )
83
89
import Ide.PluginUtils (extendToFullLines ,
@@ -138,12 +144,12 @@ codeAction state _ (CodeActionParams _ _ (TextDocumentIdentifier uri) range _) =
138
144
contents <- liftIO $ runAction " hls-refactor-plugin.codeAction.getUriContents" state $ getUriContents $ toNormalizedUri uri
139
145
liftIO $ do
140
146
let mbFile = toNormalizedFilePath' <$> uriToFilePath uri
141
- allDiags <- atomically $ fmap fdLspDiagnostic . filter (\ d -> mbFile == Just (fdFilePath d)) <$> getDiagnostics state
147
+ allDiags <- atomically $ filter (\ d -> mbFile == Just (fdFilePath d)) <$> getDiagnostics state
142
148
(join -> parsedModule) <- runAction " GhcideCodeActions.getParsedModule" state $ getParsedModule `traverse` mbFile
143
149
let
144
150
textContents = fmap Rope. toText contents
145
151
actions = caRemoveRedundantImports parsedModule textContents allDiags range uri
146
- <> caRemoveInvalidExports parsedModule textContents allDiags range uri
152
+ <> caRemoveInvalidExports parsedModule textContents (fdLspDiagnostic <$> allDiags) range uri
147
153
pure $ InL actions
148
154
149
155
-------------------------------------------------------------------------------------------------
@@ -447,33 +453,28 @@ isUnusedImportedId
447
453
maybe True (not . any (\ (_, IdentifierDetails {.. }) -> identInfo == S. singleton Use )) refs
448
454
| otherwise = False
449
455
450
- suggestRemoveRedundantImport :: ParsedModule -> Maybe T. Text -> Diagnostic -> [(T. Text , [TextEdit ])]
451
- suggestRemoveRedundantImport ParsedModule {pm_parsed_source = L _ HsModule {hsmodImports}} contents Diagnostic {_range = _range, .. }
452
- -- The qualified import of ‘many’ from module ‘Control.Applicative’ is redundant
453
- | Just [_, bindings] <- matchRegexUnifySpaces _message " The( qualified)? import of ‘([^’]*)’ from module [^ ]* is redundant "
454
- , Just ( L _ impDecl) <- find ( \ ( L (locA -> l) _) -> _start _range `isInsideSrcSpan` l && _end _range `isInsideSrcSpan` l ) hsmodImports
455
- , Just c <- contents
456
- , ranges <- map (rangesForBindingImport impDecl . T. unpack) ( T. splitOn " , " bindings >>= trySplitIntoOriginalAndRecordField)
456
+ suggestRemoveRedundantImport :: ParsedModule -> Maybe T. Text -> FileDiagnostic -> [(T. Text , [TextEdit ])]
457
+ suggestRemoveRedundantImport _ contents
458
+ FileDiagnostic {fdStructuredMessage,fdLspDiagnostic = Diagnostic {_range = _range}}
459
+ | Just ( TcRnUnusedImport impDecl ( UnusedImportSome names)) <- fdStructuredMessage ^? _SomeStructuredMessage . msgEnvelopeErrorL . _TcRnMessage
460
+ , Just c <- contents
461
+ , let bindings = names >>= bindingsInImp
462
+ , ranges <- map (rangesForBindingImport impDecl . T. unpack) bindings
457
463
, ranges' <- extendAllToIncludeCommaIfPossible False (indexedByPosition $ T. unpack c) (concat ranges)
458
464
, not (null ranges')
459
- = [( " Remove " <> bindings <> " from import" , [ TextEdit r " " | r <- ranges' ] )]
460
-
461
- -- File.hs:16:1: warning:
462
- -- The import of `Data.List' is redundant
463
- -- except perhaps to import instances from `Data.List'
464
- -- To import instances alone, use: import Data.List()
465
- | _message =~ (" The( qualified)? import of [^ ]* is redundant" :: String )
466
- = [(" Remove import" , [TextEdit (extendToWholeLineIfPossible contents _range) " " ])]
465
+ = [( " Remove " <> T. intercalate " , " (pprBinding <$> names) <> " from import" , [ TextEdit r " " | r <- ranges' ] )]
466
+ | Just (TcRnUnusedImport _ UnusedImportNone ) <- fdStructuredMessage ^? _SomeStructuredMessage. msgEnvelopeErrorL . _TcRnMessage =
467
+ [(" Remove import" , [TextEdit (extendToWholeLineIfPossible contents _range) " " ])]
467
468
| otherwise = []
468
469
where
469
- -- In case of an unused record field import, the binding from the message will not match any import directly
470
- -- In this case, we try if we can additionally extract a record field name
471
- -- Example: The import of ‘B(b2)’ from module ‘ModuleB’ is redundant
472
- trySplitIntoOriginalAndRecordField :: T. Text -> [ T. Text ]
473
- trySplitIntoOriginalAndRecordField binding =
474
- case matchRegexUnifySpaces binding " ([^ ]+) \\ (([^)]+) \\ ) " of
475
- Just [_, fields] -> [binding, fields]
476
- _ -> [binding]
470
+ bindingsInImp :: UnusedImportName -> [ T. Text ]
471
+ bindingsInImp ( UnusedImportNameRecField NoParent name) = [printOutputable name]
472
+ bindingsInImp b @ ( UnusedImportNameRecField ( ParentIs _) field) = [pprBinding b,printOutputable field]
473
+ bindingsInImp ( UnusedImportNameRegular name) = [printOutputable name ]
474
+ pprBinding :: UnusedImportName -> T. Text
475
+ pprBinding ( UnusedImportNameRecField NoParent name) = printOutputable $ occName name
476
+ pprBinding ( UnusedImportNameRecField ( ParentIs parent) field) = printOutputable parent <> " ( " <> printOutputable field <> " ) "
477
+ pprBinding ( UnusedImportNameRegular name) = printOutputable name
477
478
478
479
diagInRange :: Diagnostic -> Range -> Bool
479
480
diagInRange Diagnostic {_range = dr} r = dr `subRange` extendedRange
@@ -488,19 +489,19 @@ diagInRange Diagnostic {_range = dr} r = dr `subRange` extendedRange
488
489
-- is likely to be removed and less likely the warning will be disabled.
489
490
-- Therefore actions to remove a single or all redundant imports should be
490
491
-- preferred, so that the client can prioritize them higher.
491
- caRemoveRedundantImports :: Maybe ParsedModule -> Maybe T. Text -> [Diagnostic ] -> Range -> Uri -> [Command |? CodeAction ]
492
+ caRemoveRedundantImports :: Maybe ParsedModule -> Maybe T. Text -> [FileDiagnostic ] -> Range -> Uri -> [Command |? CodeAction ]
492
493
caRemoveRedundantImports m contents allDiags contextRange uri
493
494
| Just pm <- m,
494
495
r <- join $ map (\ d -> repeat d `zip` suggestRemoveRedundantImport pm contents d) allDiags,
495
496
allEdits <- [ e | (_, (_, edits)) <- r, e <- edits],
496
497
caRemoveAll <- removeAll allEdits,
497
- ctxEdits <- [ x | x@ (d, _) <- r, d `diagInRange` contextRange],
498
+ ctxEdits <- [ x | x@ (d, _) <- r, fdLspDiagnostic d `diagInRange` contextRange],
498
499
not $ null ctxEdits,
499
500
caRemoveCtx <- map (\ (d, (title, tedit)) -> removeSingle title tedit d) ctxEdits
500
501
= caRemoveCtx ++ [caRemoveAll]
501
502
| otherwise = []
502
503
where
503
- removeSingle title tedit diagnostic = mkCA title (Just CodeActionKind_QuickFix ) Nothing [diagnostic] WorkspaceEdit {.. } where
504
+ removeSingle title tedit diagnostic = mkCA title (Just CodeActionKind_QuickFix ) Nothing [fdLspDiagnostic diagnostic] WorkspaceEdit {.. } where
504
505
_changes = Just $ M. singleton uri tedit
505
506
_documentChanges = Nothing
506
507
_changeAnnotations = Nothing
@@ -1946,7 +1947,7 @@ textInRange (Range (Position (fromIntegral -> startRow) (fromIntegral -> startCo
1946
1947
linesBeginningWithStartLine = drop startRow (T. splitOn " \n " text)
1947
1948
1948
1949
-- | Returns the ranges for a binding in an import declaration
1949
- rangesForBindingImport :: ImportDecl GhcPs -> String -> [Range ]
1950
+ rangesForBindingImport :: ImportDecl GhcRn -> String -> [Range ]
1950
1951
rangesForBindingImport ImportDecl {
1951
1952
ideclImportList = Just (Exactly , L _ lies)
1952
1953
} b =
@@ -1988,7 +1989,7 @@ smallerRangesForBindingExport lies b =
1988
1989
[ locA l' | L l' x <- inners, T. unpack (printOutputable x) == b']
1989
1990
ranges' _ = []
1990
1991
1991
- rangesForBinding' :: String -> LIE GhcPs -> [SrcSpan ]
1992
+ rangesForBinding' :: String -> LIE GhcRn -> [SrcSpan ]
1992
1993
#if MIN_VERSION_ghc(9,9,0)
1993
1994
rangesForBinding' b (L (locA -> l) (IEVar _ nm _))
1994
1995
#else
0 commit comments