Skip to content
This repository was archived by the owner on Jan 2, 2021. It is now read-only.

Commit cf143ea

Browse files
Add code action for remove all redundant imports (#867)
* Add code action for remove all redundant imports * Call suggestRemoveRedundantImport only once * Adjust tests for code action removing all redundant imports * Update src/Development/IDE/Plugin/CodeAction.hs Co-authored-by: Pepe Iborra <[email protected]> * Refactor removeAll * Update the test of remove all redundant imports Co-authored-by: Pepe Iborra <[email protected]>
1 parent f58edfb commit cf143ea

File tree

2 files changed

+73
-12
lines changed

2 files changed

+73
-12
lines changed

src/Development/IDE/Plugin/CodeAction.hs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ codeAction lsp state (TextDocumentIdentifier uri) _range CodeActionContext{_diag
9090
contents <- LSP.getVirtualFileFunc lsp $ toNormalizedUri uri
9191
let text = Rope.toText . (_text :: VirtualFile -> Rope.Rope) <$> contents
9292
mbFile = toNormalizedFilePath' <$> uriToFilePath uri
93-
(ideOptions, parsedModule, join -> env) <- runAction "CodeAction" state $
93+
diag <- fmap (\(_, _, d) -> d) . filter (\(p, _, _) -> mbFile == Just p) <$> getDiagnostics state
94+
(ideOptions, join -> parsedModule, join -> env) <- runAction "CodeAction" state $
9495
(,,) <$> getIdeOptions
9596
<*> getParsedModule `traverse` mbFile
9697
<*> use GhcSession `traverse` mbFile
@@ -99,11 +100,11 @@ codeAction lsp state (TextDocumentIdentifier uri) _range CodeActionContext{_diag
99100
localExports <- readVar (exportsMap $ shakeExtras state)
100101
let exportsMap = localExports <> fromMaybe mempty pkgExports
101102
let dflags = hsc_dflags . hscEnv <$> env
102-
pure $ Right
103+
pure . Right $
103104
[ CACodeAction $ CodeAction title (Just CodeActionQuickFix) (Just $ List [x]) (Just edit) Nothing
104-
| x <- xs, (title, tedit) <- suggestAction dflags exportsMap ideOptions ( join parsedModule ) text x
105+
| x <- xs, (title, tedit) <- suggestAction dflags exportsMap ideOptions parsedModule text x
105106
, let edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing
106-
]
107+
] <> caRemoveRedundantImports parsedModule text diag xs uri
107108

108109
-- | Generate code lenses.
109110
codeLens
@@ -173,7 +174,6 @@ suggestAction dflags packageExports ideOptions parsedModule text diag = concat
173174
] ++ concat
174175
[ suggestConstraint pm text diag
175176
++ suggestNewDefinition ideOptions pm text diag
176-
++ suggestRemoveRedundantImport pm text diag
177177
++ suggestNewImport packageExports pm diag
178178
++ suggestDeleteUnusedBinding pm text diag
179179
++ suggestExportUnusedTopBinding text pm diag
@@ -201,6 +201,35 @@ suggestRemoveRedundantImport ParsedModule{pm_parsed_source = L _ HsModule{hsmod
201201
= [("Remove import", [TextEdit (extendToWholeLineIfPossible contents _range) ""])]
202202
| otherwise = []
203203

204+
caRemoveRedundantImports :: Maybe ParsedModule -> Maybe T.Text -> [Diagnostic] -> [Diagnostic] -> Uri -> [CAResult]
205+
caRemoveRedundantImports m contents digs ctxDigs uri
206+
| Just pm <- m,
207+
r <- join $ map (\d -> repeat d `zip` suggestRemoveRedundantImport pm contents d) digs,
208+
not $ null r,
209+
allEdits <- [ e | (_, (_, edits)) <- r, e <- edits],
210+
caRemoveAll <- removeAll allEdits,
211+
ctxEdits <- [ x | x@(d, _) <- r, d `elem` ctxDigs],
212+
caRemoveCtx <- join $ map (\(d, (title, tedit)) -> removeSingle title tedit d) ctxEdits
213+
= caRemoveCtx ++ caRemoveAll
214+
| otherwise = []
215+
where
216+
removeSingle title tedit diagnostic = [CACodeAction CodeAction{..}] where
217+
_changes = Just $ Map.singleton uri $ List tedit
218+
_title = title
219+
_kind = Just CodeActionQuickFix
220+
_diagnostics = Just $ List [diagnostic]
221+
_documentChanges = Nothing
222+
_edit = Just WorkspaceEdit{..}
223+
_command = Nothing
224+
removeAll tedit = [CACodeAction CodeAction {..}] where
225+
_changes = Just $ Map.singleton uri $ List tedit
226+
_title = "Remove all redundant imports"
227+
_kind = Just CodeActionQuickFix
228+
_diagnostics = Nothing
229+
_documentChanges = Nothing
230+
_edit = Just WorkspaceEdit{..}
231+
_command = Nothing
232+
204233
suggestDeleteUnusedBinding :: ParsedModule -> Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])]
205234
suggestDeleteUnusedBinding
206235
ParsedModule{pm_parsed_source = L _ HsModule{hsmodDecls}}

test/exe/Main.hs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,7 @@ removeImportTests = testGroup "remove import actions"
756756
]
757757
docB <- createDoc "ModuleB.hs" "haskell" contentB
758758
_ <- waitForDiagnostics
759-
[CACodeAction action@CodeAction { _title = actionTitle }]
759+
[CACodeAction action@CodeAction { _title = actionTitle }, _]
760760
<- getCodeActions docB (Range (Position 2 0) (Position 2 5))
761761
liftIO $ "Remove import" @=? actionTitle
762762
executeCodeAction action
@@ -782,7 +782,7 @@ removeImportTests = testGroup "remove import actions"
782782
]
783783
docB <- createDoc "ModuleB.hs" "haskell" contentB
784784
_ <- waitForDiagnostics
785-
[CACodeAction action@CodeAction { _title = actionTitle }]
785+
[CACodeAction action@CodeAction { _title = actionTitle }, _]
786786
<- getCodeActions docB (Range (Position 2 0) (Position 2 5))
787787
liftIO $ "Remove import" @=? actionTitle
788788
executeCodeAction action
@@ -811,7 +811,7 @@ removeImportTests = testGroup "remove import actions"
811811
]
812812
docB <- createDoc "ModuleB.hs" "haskell" contentB
813813
_ <- waitForDiagnostics
814-
[CACodeAction action@CodeAction { _title = actionTitle }]
814+
[CACodeAction action@CodeAction { _title = actionTitle }, _]
815815
<- getCodeActions docB (Range (Position 2 0) (Position 2 5))
816816
liftIO $ "Remove stuffA, stuffC from import" @=? actionTitle
817817
executeCodeAction action
@@ -840,7 +840,7 @@ removeImportTests = testGroup "remove import actions"
840840
]
841841
docB <- createDoc "ModuleB.hs" "haskell" contentB
842842
_ <- waitForDiagnostics
843-
[CACodeAction action@CodeAction { _title = actionTitle }]
843+
[CACodeAction action@CodeAction { _title = actionTitle }, _]
844844
<- getCodeActions docB (Range (Position 2 0) (Position 2 5))
845845
liftIO $ "Remove !!, <?> from import" @=? actionTitle
846846
executeCodeAction action
@@ -868,7 +868,7 @@ removeImportTests = testGroup "remove import actions"
868868
]
869869
docB <- createDoc "ModuleB.hs" "haskell" contentB
870870
_ <- waitForDiagnostics
871-
[CACodeAction action@CodeAction { _title = actionTitle }]
871+
[CACodeAction action@CodeAction { _title = actionTitle }, _]
872872
<- getCodeActions docB (Range (Position 2 0) (Position 2 5))
873873
liftIO $ "Remove A from import" @=? actionTitle
874874
executeCodeAction action
@@ -895,7 +895,7 @@ removeImportTests = testGroup "remove import actions"
895895
]
896896
docB <- createDoc "ModuleB.hs" "haskell" contentB
897897
_ <- waitForDiagnostics
898-
[CACodeAction action@CodeAction { _title = actionTitle }]
898+
[CACodeAction action@CodeAction { _title = actionTitle }, _]
899899
<- getCodeActions docB (Range (Position 2 0) (Position 2 5))
900900
liftIO $ "Remove A, E, F from import" @=? actionTitle
901901
executeCodeAction action
@@ -919,7 +919,7 @@ removeImportTests = testGroup "remove import actions"
919919
]
920920
docB <- createDoc "ModuleB.hs" "haskell" contentB
921921
_ <- waitForDiagnostics
922-
[CACodeAction action@CodeAction { _title = actionTitle }]
922+
[CACodeAction action@CodeAction { _title = actionTitle }, _]
923923
<- getCodeActions docB (Range (Position 2 0) (Position 2 5))
924924
liftIO $ "Remove import" @=? actionTitle
925925
executeCodeAction action
@@ -929,6 +929,38 @@ removeImportTests = testGroup "remove import actions"
929929
, "module ModuleB where"
930930
]
931931
liftIO $ expectedContentAfterAction @=? contentAfterAction
932+
, testSession "remove all" $ do
933+
let content = T.unlines
934+
[ "{-# OPTIONS_GHC -Wunused-imports #-}"
935+
, "module ModuleA where"
936+
, "import Data.Function (fix, (&))"
937+
, "import qualified Data.Functor.Const"
938+
, "import Data.Functor.Identity"
939+
, "import Data.Functor.Sum (Sum (InL, InR))"
940+
, "import qualified Data.Kind as K (Constraint, Type)"
941+
, "x = InL (Identity 123)"
942+
, "y = fix id"
943+
, "type T = K.Type"
944+
]
945+
doc <- createDoc "ModuleC.hs" "haskell" content
946+
_ <- waitForDiagnostics
947+
[_, _, _, _, CACodeAction action@CodeAction { _title = actionTitle }]
948+
<- getCodeActions doc (Range (Position 2 0) (Position 2 5))
949+
liftIO $ "Remove all redundant imports" @=? actionTitle
950+
executeCodeAction action
951+
contentAfterAction <- documentContents doc
952+
let expectedContentAfterAction = T.unlines
953+
[ "{-# OPTIONS_GHC -Wunused-imports #-}"
954+
, "module ModuleA where"
955+
, "import Data.Function (fix)"
956+
, "import Data.Functor.Identity"
957+
, "import Data.Functor.Sum (Sum (InL))"
958+
, "import qualified Data.Kind as K (Type)"
959+
, "x = InL (Identity 123)"
960+
, "y = fix id"
961+
, "type T = K.Type"
962+
]
963+
liftIO $ expectedContentAfterAction @=? contentAfterAction
932964
]
933965

934966
extendImportTests :: TestTree

0 commit comments

Comments
 (0)