From 1b3c6abd036544a77cfa25372371188585b1b6f2 Mon Sep 17 00:00:00 2001 From: Bharath Chandra Date: Tue, 13 Oct 2026 20:07:55 +0530 Subject: [PATCH 1/6] Add code action to remove redundant qualified in imports --- ChangeLog.md | 1 + .../src/Development/IDE/Plugin/CodeAction.hs | 44 +++++++++++++++++++ .../Development/IDE/Plugin/CodeAction/Args.hs | 11 +++-- plugins/hls-refactor-plugin/test/Main.hs | 35 +++++++++++++++ 4 files changed, 88 insertions(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 160d68fc4c..ca8f35171a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -17,6 +17,7 @@ - Improved limited cross-module renaming - Improvements to `hls-refactor-plugin` - Code action to delete all unused bindings, including `let` bindings + - Code action to remove a redundant `qualified` from an import - Improvements to `hls-eval-plugin` - Capture `stdout` and `stderr` from evaluated expressions - Improvements to `hls-splice-plugin` diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 716af176be..96873b8b67 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -177,6 +177,7 @@ iePluginDescriptor recorder plId = let old = mkGhcideCAsPlugin [ wrap suggestModuleTypo + , wrap suggestRemoveDuplicateQualified , wrap suggestFixConstructorImport , wrap suggestExtendImport , wrap suggestImportDisambiguation @@ -987,6 +988,49 @@ suggestModuleTypo Diagnostic{_range=_range,..} [modul, "(from", _] -> Just modul _ -> Nothing +suggestRemoveDuplicateQualified :: [FileDiagnostic] -> T.Text -> Diagnostic -> [(T.Text, [TextEdit])] +suggestRemoveDuplicateQualified diags contents Diagnostic{_range=_range, _message} + | "Multiple occurrences of 'qualified'" `T.isInfixOf` _message + , Just edit <- redundantQualifiedEdit + = [("Remove redundant 'qualified'", [edit])] + | otherwise = [] + where + qualifiedPostEnabled = not $ any mentionsImportQualifiedPost diags + mentionsImportQualifiedPost fd = case fdLspDiagnostic fd of + Diagnostic{_message = m} -> "ImportQualifiedPost" `T.isInfixOf` m + redundantQualifiedEdit = do + let Position startLine _ = _start _range + line <- listToMaybe $ drop (fromIntegral startLine) $ T.lines contents + (preQual, postQual) <- case qualifiedOccurrences line of + a : b : _ -> Just (a, b) + _ -> Nothing + let (colStart, colEnd) + | qualifiedPostEnabled = preQual + | otherwise = postQual + baseRange = Range (Position startLine (fromIntegral colStart)) (Position startLine (fromIntegral colEnd)) + trailing = T.takeWhile isSpace (T.drop colEnd line) + leading = T.takeWhileEnd isSpace (T.take colStart line) + editRange + | qualifiedPostEnabled = baseRange { _end = Position startLine (fromIntegral (colEnd + T.length trailing)) } + | otherwise = baseRange { _start = Position startLine (fromIntegral (colStart - T.length leading)) } + pure $ TextEdit editRange "" + +qualifiedOccurrences :: T.Text -> [(Int, Int)] +qualifiedOccurrences = go 0 + where + kw = "qualified" + kwLen = T.length kw + go off t = case T.breakOn kw t of + (_, rest) | T.null rest -> [] + (pre, rest) -> + let start = off + T.length pre + after = T.drop kwLen rest + boundedBefore = T.null pre || not (isIdentChar (T.last pre)) + boundedAfter = T.null after || not (isIdentChar (T.head after)) + found = if boundedBefore && boundedAfter then ((start, start + kwLen) :) else id + in found (go (start + kwLen) after) + isIdentChar c = isAlphaNum c || c == '_' + suggestExtendImport :: ExportsMap -> ParsedSource -> Diagnostic -> [(T.Text, CodeActionKind, CodeActionPreferred, Rewrite)] suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_range,..} | Just [binding, mod, srcspan] <- diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs index 4bff75ecff..1e696b996b 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs @@ -79,12 +79,12 @@ runGhcideCodeAction state (CodeActionParams _ _ (TextDocumentIdentifier uri) _ra caaHar <- onceIO $ runRule GetHieAst caaBindings <- onceIO $ runRule GetBindings caaGblSigs <- onceIO $ runRule GetGlobalBindingTypeSigs - diags <- activeDiagnosticsInRange (shakeExtras state) nfp _range + caaDiagnostics <- activeDiagnosticsInRange (shakeExtras state) nfp _range results <- liftIO $ sequence [ runReaderT (runExceptT codeAction) CodeActionArgs {..} - | caaDiagnostic <- diags + | caaDiagnostic <- caaDiagnostics ] let (_errs, successes) = partitionEithers results pure $ concat successes @@ -150,7 +150,9 @@ data CodeActionArgs = CodeActionArgs caaHar :: IO (Maybe HieAstResult), caaBindings :: IO (Maybe Bindings), caaGblSigs :: IO (Maybe GlobalBindingTypeSigsResult), - caaDiagnostic :: FileDiagnostic + caaDiagnostic :: FileDiagnostic, + -- | All diagnostics that overlap the requested code action range. + caaDiagnostics :: [FileDiagnostic] } -- | There's no concurrency in each provider, @@ -233,6 +235,9 @@ instance ToCodeAction r => ToCodeAction (Diagnostic -> r) where instance ToCodeAction r => ToCodeAction (FileDiagnostic -> r) where toCodeAction f = ExceptT . ReaderT $ \caa@CodeActionArgs {caaDiagnostic = x} -> flip runReaderT caa . runExceptT . toCodeAction $ f x +instance ToCodeAction r => ToCodeAction ([FileDiagnostic] -> r) where + toCodeAction f = ExceptT . ReaderT $ \caa@CodeActionArgs {caaDiagnostics = x} -> flip runReaderT caa . runExceptT . toCodeAction $ f x + instance ToCodeAction r => ToCodeAction (Maybe ParsedModule -> r) where toCodeAction = toCodeAction1 caaParsedModule diff --git a/plugins/hls-refactor-plugin/test/Main.hs b/plugins/hls-refactor-plugin/test/Main.hs index fa104a634c..3c746e89e8 100644 --- a/plugins/hls-refactor-plugin/test/Main.hs +++ b/plugins/hls-refactor-plugin/test/Main.hs @@ -308,6 +308,7 @@ codeActionTests = testGroup "code actions" , suggestHideShadowTests , fixConstructorImportTests , fixModuleImportTypoTests + , removeDuplicateQualifiedTests , importRenameActionTests , fillTypedHoleTests , addSigActionTests @@ -1701,6 +1702,40 @@ fixModuleImportTypoTests = testGroup "fix module import typo" liftIO $ contentAfterAction @?= "import Data.Eq" ] +removeDuplicateQualifiedTests :: TestTree +removeDuplicateQualifiedTests = testGroup "multiple occurrences of qualified in import" + [ testSession "removes prepositive qualified when ImportQualifiedPost is enabled" $ do + doc <- createDoc "A.hs" "haskell" $ T.unlines + [ "{-# LANGUAGE ImportQualifiedPost #-}" + , "module A where" + , "import qualified Data.List qualified" + ] + _ <- waitForDiagnostics + action <- pickActionWithTitle "Remove redundant 'qualified'" =<< getCodeActions doc (R 2 0 2 36) + executeCodeAction action + contentAfterAction <- documentContents doc + liftIO $ contentAfterAction @?= T.unlines + [ "{-# LANGUAGE ImportQualifiedPost #-}" + , "module A where" + , "import Data.List qualified" + ] + , testSession "removes postpositive qualified without ImportQualifiedPost" $ do + doc <- createDoc "A.hs" "haskell" $ T.unlines + [ "{-# LANGUAGE NoImportQualifiedPost #-}" + , "module A where" + , "import qualified Data.List qualified" + ] + _ <- waitForDiagnostics + action <- pickActionWithTitle "Remove redundant 'qualified'" =<< getCodeActions doc (R 2 0 2 36) + executeCodeAction action + contentAfterAction <- documentContents doc + liftIO $ contentAfterAction @?= T.unlines + [ "{-# LANGUAGE NoImportQualifiedPost #-}" + , "module A where" + , "import qualified Data.List" + ] + ] + suggestImportClassMethodTests :: TestTree suggestImportClassMethodTests = testGroup From 7c1761696d37e0337a8c09a95b3b177216adebd3 Mon Sep 17 00:00:00 2001 From: Bharath Chandra Date: Sat, 24 Oct 2026 13:55:23 +0530 Subject: [PATCH 2/6] use DynFlags via qualifiedImportStyle for duplicate qualified removal --- .../src/Development/IDE/Plugin/CodeAction.hs | 21 ++++++++++--------- .../Development/IDE/Plugin/CodeAction/Args.hs | 11 +++------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 96873b8b67..69a41ff635 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -988,16 +988,18 @@ suggestModuleTypo Diagnostic{_range=_range,..} [modul, "(from", _] -> Just modul _ -> Nothing -suggestRemoveDuplicateQualified :: [FileDiagnostic] -> T.Text -> Diagnostic -> [(T.Text, [TextEdit])] -suggestRemoveDuplicateQualified diags contents Diagnostic{_range=_range, _message} +suggestRemoveDuplicateQualified :: Maybe DynFlags -> T.Text -> Diagnostic -> [(T.Text, [TextEdit])] +suggestRemoveDuplicateQualified mbDf contents Diagnostic{_range=_range, _message} | "Multiple occurrences of 'qualified'" `T.isInfixOf` _message , Just edit <- redundantQualifiedEdit = [("Remove redundant 'qualified'", [edit])] | otherwise = [] where - qualifiedPostEnabled = not $ any mentionsImportQualifiedPost diags - mentionsImportQualifiedPost fd = case fdLspDiagnostic fd of - Diagnostic{_message = m} -> "ImportQualifiedPost" `T.isInfixOf` m + usePost = case mbDf of + Just df -> case qualifiedImportStyle df of + QualifiedImportPostfix -> True + QualifiedImportPrefix -> False + Nothing -> False redundantQualifiedEdit = do let Position startLine _ = _start _range line <- listToMaybe $ drop (fromIntegral startLine) $ T.lines contents @@ -1005,16 +1007,15 @@ suggestRemoveDuplicateQualified diags contents Diagnostic{_range=_range, _messag a : b : _ -> Just (a, b) _ -> Nothing let (colStart, colEnd) - | qualifiedPostEnabled = preQual - | otherwise = postQual + | usePost = preQual + | otherwise = postQual baseRange = Range (Position startLine (fromIntegral colStart)) (Position startLine (fromIntegral colEnd)) trailing = T.takeWhile isSpace (T.drop colEnd line) leading = T.takeWhileEnd isSpace (T.take colStart line) editRange - | qualifiedPostEnabled = baseRange { _end = Position startLine (fromIntegral (colEnd + T.length trailing)) } - | otherwise = baseRange { _start = Position startLine (fromIntegral (colStart - T.length leading)) } + | usePost = baseRange { _end = Position startLine (fromIntegral (colEnd + T.length trailing)) } + | otherwise = baseRange { _start = Position startLine (fromIntegral (colStart - T.length leading)) } pure $ TextEdit editRange "" - qualifiedOccurrences :: T.Text -> [(Int, Int)] qualifiedOccurrences = go 0 where diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs index 1e696b996b..4bff75ecff 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs @@ -79,12 +79,12 @@ runGhcideCodeAction state (CodeActionParams _ _ (TextDocumentIdentifier uri) _ra caaHar <- onceIO $ runRule GetHieAst caaBindings <- onceIO $ runRule GetBindings caaGblSigs <- onceIO $ runRule GetGlobalBindingTypeSigs - caaDiagnostics <- activeDiagnosticsInRange (shakeExtras state) nfp _range + diags <- activeDiagnosticsInRange (shakeExtras state) nfp _range results <- liftIO $ sequence [ runReaderT (runExceptT codeAction) CodeActionArgs {..} - | caaDiagnostic <- caaDiagnostics + | caaDiagnostic <- diags ] let (_errs, successes) = partitionEithers results pure $ concat successes @@ -150,9 +150,7 @@ data CodeActionArgs = CodeActionArgs caaHar :: IO (Maybe HieAstResult), caaBindings :: IO (Maybe Bindings), caaGblSigs :: IO (Maybe GlobalBindingTypeSigsResult), - caaDiagnostic :: FileDiagnostic, - -- | All diagnostics that overlap the requested code action range. - caaDiagnostics :: [FileDiagnostic] + caaDiagnostic :: FileDiagnostic } -- | There's no concurrency in each provider, @@ -235,9 +233,6 @@ instance ToCodeAction r => ToCodeAction (Diagnostic -> r) where instance ToCodeAction r => ToCodeAction (FileDiagnostic -> r) where toCodeAction f = ExceptT . ReaderT $ \caa@CodeActionArgs {caaDiagnostic = x} -> flip runReaderT caa . runExceptT . toCodeAction $ f x -instance ToCodeAction r => ToCodeAction ([FileDiagnostic] -> r) where - toCodeAction f = ExceptT . ReaderT $ \caa@CodeActionArgs {caaDiagnostics = x} -> flip runReaderT caa . runExceptT . toCodeAction $ f x - instance ToCodeAction r => ToCodeAction (Maybe ParsedModule -> r) where toCodeAction = toCodeAction1 caaParsedModule From b7d34336f90a8affac354c1cabf4e54a0bd8828d Mon Sep 17 00:00:00 2001 From: Bharath Chandra Date: Sat, 24 Oct 2026 16:40:15 +0530 Subject: [PATCH 3/6] try both parsed and session dynflags --- .../src/Development/IDE/Plugin/CodeAction.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 69a41ff635..ed2757a277 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -988,13 +988,14 @@ suggestModuleTypo Diagnostic{_range=_range,..} [modul, "(from", _] -> Just modul _ -> Nothing -suggestRemoveDuplicateQualified :: Maybe DynFlags -> T.Text -> Diagnostic -> [(T.Text, [TextEdit])] -suggestRemoveDuplicateQualified mbDf contents Diagnostic{_range=_range, _message} +suggestRemoveDuplicateQualified :: Maybe DynFlags -> Maybe DynFlags -> T.Text -> Diagnostic -> [(T.Text, [TextEdit])] +suggestRemoveDuplicateQualified mbDfFromModule mbDfFromSession contents Diagnostic{_range=_range, _message} | "Multiple occurrences of 'qualified'" `T.isInfixOf` _message , Just edit <- redundantQualifiedEdit = [("Remove redundant 'qualified'", [edit])] | otherwise = [] where + mbDf = mbDfFromModule <|> mbDfFromSession usePost = case mbDf of Just df -> case qualifiedImportStyle df of QualifiedImportPostfix -> True From a3d1ba7478ec2af03e26bd4021db30a02d18ea91 Mon Sep 17 00:00:00 2001 From: Bharath Chandra Date: Sat, 24 Oct 2026 21:43:59 +0530 Subject: [PATCH 4/6] fix duplicate qualified action to use single DynFlags --- .../src/Development/IDE/Plugin/CodeAction.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index ed2757a277..69a41ff635 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -988,14 +988,13 @@ suggestModuleTypo Diagnostic{_range=_range,..} [modul, "(from", _] -> Just modul _ -> Nothing -suggestRemoveDuplicateQualified :: Maybe DynFlags -> Maybe DynFlags -> T.Text -> Diagnostic -> [(T.Text, [TextEdit])] -suggestRemoveDuplicateQualified mbDfFromModule mbDfFromSession contents Diagnostic{_range=_range, _message} +suggestRemoveDuplicateQualified :: Maybe DynFlags -> T.Text -> Diagnostic -> [(T.Text, [TextEdit])] +suggestRemoveDuplicateQualified mbDf contents Diagnostic{_range=_range, _message} | "Multiple occurrences of 'qualified'" `T.isInfixOf` _message , Just edit <- redundantQualifiedEdit = [("Remove redundant 'qualified'", [edit])] | otherwise = [] where - mbDf = mbDfFromModule <|> mbDfFromSession usePost = case mbDf of Just df -> case qualifiedImportStyle df of QualifiedImportPostfix -> True From 0339074f436da1ff24299591fe19e3fc25216717 Mon Sep 17 00:00:00 2001 From: Bharath Chandra Date: Sat, 24 Oct 2026 21:52:29 +0530 Subject: [PATCH 5/6] adjust duplicate qualified code action signature --- .../src/Development/IDE/Plugin/CodeAction.hs | 9 +++++---- .../src/Development/IDE/Plugin/CodeAction/Args.hs | 6 ++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index 69a41ff635..bfaa7d0a54 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -988,10 +988,11 @@ suggestModuleTypo Diagnostic{_range=_range,..} [modul, "(from", _] -> Just modul _ -> Nothing -suggestRemoveDuplicateQualified :: Maybe DynFlags -> T.Text -> Diagnostic -> [(T.Text, [TextEdit])] -suggestRemoveDuplicateQualified mbDf contents Diagnostic{_range=_range, _message} +suggestRemoveDuplicateQualified :: Maybe DynFlags -> Maybe T.Text -> Diagnostic -> [(T.Text, [TextEdit])] +suggestRemoveDuplicateQualified mbDf contents' Diagnostic{_range=_range, _message} | "Multiple occurrences of 'qualified'" `T.isInfixOf` _message - , Just edit <- redundantQualifiedEdit + , Just contents <- contents' + , Just edit <- redundantQualifiedEdit contents = [("Remove redundant 'qualified'", [edit])] | otherwise = [] where @@ -1000,7 +1001,7 @@ suggestRemoveDuplicateQualified mbDf contents Diagnostic{_range=_range, _message QualifiedImportPostfix -> True QualifiedImportPrefix -> False Nothing -> False - redundantQualifiedEdit = do + redundantQualifiedEdit contents = do let Position startLine _ = _start _range line <- listToMaybe $ drop (fromIntegral startLine) $ T.lines contents (preQual, postQual) <- case qualifiedOccurrences line of diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs index 4bff75ecff..241a760294 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs @@ -251,6 +251,12 @@ instance ToCodeAction r => ToCodeAction (Maybe DynFlags -> r) where instance ToCodeAction r => ToCodeAction (DynFlags -> r) where toCodeAction = toCodeAction2 caaDf +instance ToCodeAction r => ToCodeAction (Maybe HscEnvEq -> r) where + toCodeAction = toCodeAction1 caaGhcSession + +instance ToCodeAction r => ToCodeAction (HscEnvEq -> r) where + toCodeAction = toCodeAction2 caaGhcSession + instance ToCodeAction r => ToCodeAction (Maybe ParsedSource -> r) where toCodeAction = toCodeAction1 caaAnnSource From 5b94fb0ff44c73da44e74e9deac2fb8fbddfc873 Mon Sep 17 00:00:00 2001 From: Bharath Chandra Date: Sat, 24 Oct 2026 22:28:17 +0530 Subject: [PATCH 6/6] fix duplicate qualified action to use mod summary flags when parse fails --- .../src/Development/IDE/Plugin/CodeAction.hs | 24 +++++++++++++++---- .../Development/IDE/Plugin/CodeAction/Args.hs | 18 ++++++++------ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs index bfaa7d0a54..ed7d0dacf5 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction.hs @@ -997,10 +997,10 @@ suggestRemoveDuplicateQualified mbDf contents' Diagnostic{_range=_range, _messag | otherwise = [] where usePost = case mbDf of - Just df -> case qualifiedImportStyle df of - QualifiedImportPostfix -> True - QualifiedImportPrefix -> False - Nothing -> False + Just df -> xopt Lang.ImportQualifiedPost df + -- The module could not be parsed and no DynFlags were available, so + -- fall back to the LANGUAGE/OPTIONS_GHC pragmas in the file itself. + Nothing -> maybe False importQualifiedPostFromPragmas contents' redundantQualifiedEdit contents = do let Position startLine _ = _start _range line <- listToMaybe $ drop (fromIntegral startLine) $ T.lines contents @@ -1017,6 +1017,22 @@ suggestRemoveDuplicateQualified mbDf contents' Diagnostic{_range=_range, _messag | usePost = baseRange { _end = Position startLine (fromIntegral (colEnd + T.length trailing)) } | otherwise = baseRange { _start = Position startLine (fromIntegral (colStart - T.length leading)) } pure $ TextEdit editRange "" +importQualifiedPostFromPragmas :: T.Text -> Bool +importQualifiedPostFromPragmas contents = + case enabledStates of + [] -> False + states -> last states + where + enabledStates = + [ token /= "NoImportQualifiedPost" + | line <- T.lines contents + , "{-# LANGUAGE" `T.isInfixOf` line || "{-# OPTIONS_GHC" `T.isInfixOf` line + , token <- map T.unpack $ extensionsIn line + , token == "ImportQualifiedPost" || token == "NoImportQualifiedPost" + ] + extensionsIn = + T.split (\c -> not (isAlphaNum c || c == '_' || c == '-')) . T.replace "-X" "" + qualifiedOccurrences :: T.Text -> [(Int, Int)] qualifiedOccurrences = go 0 where diff --git a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs index 241a760294..3cff3b0946 100644 --- a/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs +++ b/plugins/hls-refactor-plugin/src/Development/IDE/Plugin/CodeAction/Args.hs @@ -68,12 +68,21 @@ runGhcideCodeAction state (CodeActionParams _ _ (TextDocumentIdentifier uri) _ra _ -> pure mempty caaIdeOptions <- onceIO $ runAction "GhcideCodeActions.getIdeOptions" state getIdeOptions caaParsedModule <- onceIO $ runRule GetParsedModuleWithComments + caaModSummary <- onceIO $ runRule GetModSummary caaContents <- onceIO $ runRule GetFileContents <&> \case Just (_, mbContents) -> fmap Rope.toText mbContents Nothing -> Nothing - caaDf <- onceIO $ fmap (ms_hspp_opts . pm_mod_summary) <$> caaParsedModule + caaDf <- onceIO $ do + mbParsedModule <- caaParsedModule + case mbParsedModule of + Just pm -> pure $ Just (ms_hspp_opts (pm_mod_summary pm)) + -- The module may fail to parse (e.g. malformed imports). In + -- that case use the flags from the module summary, which are + -- computed without parsing the module. + Nothing -> + fmap (ms_hspp_opts . msrModSummary) <$> caaModSummary caaAnnSource <- onceIO $ runRule GetAnnotatedParsedSource caaTmr <- onceIO $ runRule TypeCheck caaHar <- onceIO $ runRule GetHieAst @@ -143,6 +152,7 @@ data CodeActionArgs = CodeActionArgs caaGhcSession :: IO (Maybe HscEnvEq), caaIdeOptions :: IO IdeOptions, caaParsedModule :: IO (Maybe ParsedModule), + caaModSummary :: IO (Maybe ModSummaryResult), caaContents :: IO (Maybe T.Text), caaDf :: IO (Maybe DynFlags), caaAnnSource :: IO (Maybe ParsedSource), @@ -251,12 +261,6 @@ instance ToCodeAction r => ToCodeAction (Maybe DynFlags -> r) where instance ToCodeAction r => ToCodeAction (DynFlags -> r) where toCodeAction = toCodeAction2 caaDf -instance ToCodeAction r => ToCodeAction (Maybe HscEnvEq -> r) where - toCodeAction = toCodeAction1 caaGhcSession - -instance ToCodeAction r => ToCodeAction (HscEnvEq -> r) where - toCodeAction = toCodeAction2 caaGhcSession - instance ToCodeAction r => ToCodeAction (Maybe ParsedSource -> r) where toCodeAction = toCodeAction1 caaAnnSource