From b35aa37d7e14ba3084d83431df4ce6b45e752902 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Wed, 27 Jan 2021 03:20:55 +0900 Subject: [PATCH 01/43] wip --- .../src/Development/IDE/Plugin/CodeAction.hs | 193 +++++++++++++++--- .../IDE/Plugin/CodeAction/ExactPrint.hs | 9 + 2 files changed, 168 insertions(+), 34 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 1dbddf9e6a..09c3e0a484 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -3,6 +3,7 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE CPP #-} +{-# LANGUAGE LambdaCase #-} #include "ghc-api-version.h" -- | Go to the definition of a variable. @@ -13,14 +14,31 @@ module Development.IDE.Plugin.CodeAction , matchRegExMultipleImports ) where -import Control.Monad (join, guard) -import Development.IDE.GHC.Compat -import Development.IDE.Core.Rules +import Bag (isEmptyBag) +import Control.Applicative ((<|>)) +import Control.Arrow (second, (>>>)) +import Control.Concurrent.Extra (readVar) +import Control.Monad (guard, join) +import Data.Char +import Data.Function +import Data.Functor +import qualified Data.HashMap.Strict as Map +import qualified Data.HashSet as Set +import Data.List.Extra +import Data.List.NonEmpty (NonEmpty ((:|))) +import qualified Data.List.NonEmpty as NE +import Data.Maybe +import Data.Ord (Down (Down)) +import qualified Data.Rope.UTF16 as Rope +import qualified Data.Text as T import Development.IDE.Core.RuleTypes +import Development.IDE.Core.Rules import Development.IDE.Core.Service import Development.IDE.Core.Shake +import Development.IDE.GHC.Compat import Development.IDE.GHC.Error import Development.IDE.GHC.ExactPrint +import Development.IDE.GHC.Util (printRdrName) import Development.IDE.Plugin.CodeAction.ExactPrint import Development.IDE.Plugin.CodeAction.PositionIndexed import Development.IDE.Plugin.CodeAction.RuleTypes @@ -29,31 +47,18 @@ import Development.IDE.Plugin.TypeLenses (suggestSignature) import Development.IDE.Types.Exports import Development.IDE.Types.Location import Development.IDE.Types.Options -import qualified Data.HashMap.Strict as Map +import Ide.PluginUtils (subRange) +import Ide.Types import qualified Language.Haskell.LSP.Core as LSP -import Language.Haskell.LSP.VFS import Language.Haskell.LSP.Types -import qualified Data.Rope.UTF16 as Rope -import Data.Char -import Data.Maybe -import Data.List.Extra -import Data.List.NonEmpty (NonEmpty((:|))) -import qualified Data.List.NonEmpty as NE -import qualified Data.Text as T -import Text.Regex.TDFA (mrAfter, (=~), (=~~)) +import Language.Haskell.LSP.VFS import Outputable (Outputable, ppr, showSDoc, showSDocUnsafe) -import Data.Function -import Control.Arrow ((>>>)) -import Data.Functor -import Control.Applicative ((<|>)) +import Retrie.GHC (fsLit, mkRealSrcLoc, mkRealSrcSpan) import Safe (atMay) -import Bag (isEmptyBag) -import qualified Data.HashSet as Set -import Control.Concurrent.Extra (readVar) -import Development.IDE.GHC.Util (printRdrName) -import Ide.PluginUtils (subRange) -import Ide.Types - +import Text.Regex.TDFA (mrAfter, (=~), (=~~)) +import Retrie (unpackFS) +import Retrie.GHC (mkVarOcc) +import Language.Haskell.GHC.ExactPrint.Transform (uniqueSrcSpanT) descriptor :: PluginId -> PluginDescriptor IdeState descriptor plId = (defaultPluginDescriptor plId) @@ -96,9 +101,9 @@ codeAction lsp state _ (TextDocumentIdentifier uri) _range CodeActionContext{_di | x <- xs , Just ps <- [annotatedPS] , Just dynflags <- [df] - , (title, graft) <- suggestExactAction exportsMap dynflags ps x - , let edit = either error id $ - rewriteToEdit dynflags uri (annsA ps) graft + , (title, grafts) <- suggestExactAction exportsMap dynflags ps x + , let edit = foldMap (either error id . + rewriteToEdit dynflags uri (annsA ps)) grafts ] actions'' = caRemoveRedundantImports parsedModule text diag xs uri <> actions @@ -115,12 +120,13 @@ suggestExactAction :: DynFlags -> Annotated ParsedSource -> Diagnostic -> - [(T.Text, Rewrite)] + [(T.Text, [Rewrite])] suggestExactAction exportsMap df ps x = concat [ suggestConstraint df (astA ps) x , suggestImplicitParameter (astA ps) x , suggestExtendImport exportsMap (astA ps) x + , suggestImportDisambiguation exportsMap (astA ps) x ] suggestAction @@ -664,7 +670,7 @@ getIndentedGroupsBy pred inp = case dropWhile (not.pred) inp of indentation :: T.Text -> Int indentation = T.length . T.takeWhile isSpace -suggestExtendImport :: ExportsMap -> ParsedSource -> Diagnostic -> [(T.Text, Rewrite)] +suggestExtendImport :: ExportsMap -> ParsedSource -> Diagnostic -> [(T.Text, [Rewrite])] suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_range,..} | Just [binding, mod, srcspan] <- matchRegexUnifySpaces _message @@ -685,7 +691,7 @@ suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_ Just decl <- findImportDeclByRange decls range, Just ident <- lookupExportMap binding mod = [ ( "Add " <> renderImportStyle importStyle <> " to the import list of " <> mod - , uncurry extendImport (unImportStyle importStyle) decl + , [uncurry extendImport (unImportStyle importStyle) decl] ) | importStyle <- NE.toList $ importStyles ident ] @@ -703,6 +709,114 @@ suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_ , parent = Nothing , isDatacon = False} +data HidingMode = HideOthers [LImportDecl GhcPs] | ToQualified ModuleName + deriving (Show) + +oneAndOthers :: [a] -> [(a, [a])] +oneAndOthers = go + where + go [] = [] + go (x : xs) = (x, xs) : map (second (x :)) (go xs) + +-- | Suggests disambiguation for ambiguous symbols. +suggestImportDisambiguation :: + ExportsMap -> + ParsedSource -> + Diagnostic -> + [(T.Text, [Rewrite])] +suggestImportDisambiguation exportsMap (L _ HsModule {hsmodImports}) diag@Diagnostic {..} + | Just [ambiguous] <- + matchRegexUnifySpaces + _message + "^Ambiguous occurrence ‘([^’]+)’" + , Just modules <- + map last + <$> allMatchRegexUnifySpaces _message "imported from ‘([^’]+)’" = + suggestions ambiguous modules + | otherwise = [] + where + locDic = + Map.fromList $ + map + ( \i@(L _ idecl) -> + ( T.pack $ moduleNameString $ unLoc $ ideclName idecl + , i + ) + ) + hsmodImports + suggestions symbol mods + | Just targets <- mapM (`Map.lookup` locDic) mods = + [ ( renderUniquify mode modNameText symbol + , disambiguateSymbol diag symbol lidecl mode + ) + | (lidecl@(L _ ImportDecl {..}), restImports) <- oneAndOthers targets + , let modName = unLoc ideclName + modNameText = T.pack $ moduleNameString modName + , mode <- + [ ToQualified qual + | L _ qual <- maybeToList ideclAs + ] + ++ [HideOthers restImports] + ++ [ToQualified modName] + ] + | otherwise = [] + renderUniquify HideOthers {} modName symbol = + "Use the import from " <> modName <> " for " <> symbol <> ", hiding other imports" + renderUniquify (ToQualified qual) _ symbol = + "Replace with qualified: " + <> T.pack (moduleNameString qual) + <> "." + <> symbol + lookupExportMap binding mod + | Just match <- Map.lookup binding (getExportsMap exportsMap) + , [(ident, _)] <- filter (\(_, m) -> mod == m) (Set.toList match) = + Just ident + -- fallback to using GHC suggestion even though it is not always correct + | otherwise = + Just + IdentInfo + { name = binding + , rendered = binding + , parent = Nothing + , isDatacon = False + } + +disambiguateSymbol :: + Diagnostic -> + T.Text -> + GenLocated SrcSpan (ImportDecl GhcPs) -> + HidingMode -> + [Rewrite] +disambiguateSymbol Diagnostic {..} symbol (L loc _) = \case + (HideOthers hiddens0) -> + [ hideSymbol symbol idecl + | idecl <- sortOn (Down . getLoc) hiddens0 + ] + (ToQualified qualMod) -> + [ Rewrite (rangeToSrcSpan theFile _range) $ \_ -> do + spn <- uniqueSrcSpanT + pure + ( L spn $ + HsVar NoExt $ + L spn $ + Qual qualMod (mkVarOcc $ T.unpack symbol) :: + LHsExpr GhcPs + ) + ] + where + theFile + | RealSrcSpan real <- loc = unpackFS $ srcSpanFile real + | otherwise = "" + +rangeToSrcSpan :: String -> Range -> SrcSpan +rangeToSrcSpan file range = RealSrcSpan $ rangeToRealSrcSpan file range + +rangeToRealSrcSpan :: String -> Range -> RealSrcSpan +rangeToRealSrcSpan file (Range (Position startLn startCh) (Position endLn endCh)) = + mkRealSrcSpan + (mkRealSrcLoc (fsLit file) (startLn + 1) (startCh + 1)) + (mkRealSrcLoc (fsLit file) (endLn + 1) (endCh + 1)) + findImportDeclByRange :: [LImportDecl GhcPs] -> Range -> Maybe (LImportDecl GhcPs) findImportDeclByRange xs range = find (\(L l _)-> srcSpanToRange l == Just range) xs @@ -720,13 +834,13 @@ suggestFixConstructorImport _ Diagnostic{_range=_range,..} in [("Fix import of " <> fixedImport, [TextEdit _range fixedImport])] | otherwise = [] -- | Suggests a constraint for a declaration for which a constraint is missing. -suggestConstraint :: DynFlags -> ParsedSource -> Diagnostic -> [(T.Text, Rewrite)] +suggestConstraint :: DynFlags -> ParsedSource -> Diagnostic -> [(T.Text, [Rewrite])] suggestConstraint df parsedModule diag@Diagnostic {..} | Just missingConstraint <- findMissingConstraint _message = let codeAction = if _message =~ ("the type signature for:" :: String) then suggestFunctionConstraint df parsedModule else suggestInstanceConstraint df parsedModule - in codeAction diag missingConstraint + in map (second pure) $ codeAction diag missingConstraint | otherwise = [] where findMissingConstraint :: T.Text -> Maybe T.Text @@ -782,14 +896,14 @@ suggestInstanceConstraint df (L _ HsModule {hsmodDecls}) Diagnostic {..} missing suggestImplicitParameter :: ParsedSource -> Diagnostic -> - [(T.Text, Rewrite)] + [(T.Text, [Rewrite])] suggestImplicitParameter (L _ HsModule {hsmodDecls}) Diagnostic {_message, _range} | Just [implicitT] <- matchRegexUnifySpaces _message "Unbound implicit parameter \\(([^:]+::.+)\\) arising", Just (L _ (ValD _ FunBind {fun_id = L _ funId})) <- findDeclContainingLoc (_start _range) hsmodDecls, Just (TypeSig _ _ HsWC {hswc_body = HsIB {hsib_body}}) <- findSigOfDecl (== funId) hsmodDecls = [( "Add " <> implicitT <> " to the context of " <> T.pack (printRdrName funId) - , appendConstraint (T.unpack implicitT) hsib_body)] + , [appendConstraint (T.unpack implicitT) hsib_body])] | otherwise = [] findTypeSignatureName :: T.Text -> Maybe T.Text @@ -1107,11 +1221,22 @@ rangesForBinding' _ _ = [] matchRegexUnifySpaces :: T.Text -> T.Text -> Maybe [T.Text] matchRegexUnifySpaces message = matchRegex (unifySpaces message) +-- | 'allMatchRegex' combined with 'unifySpaces' +allMatchRegexUnifySpaces :: T.Text -> T.Text -> Maybe [[T.Text]] +allMatchRegexUnifySpaces message = + allMatchRegex (unifySpaces message) + -- | Returns Just (the submatches) for the first capture, or Nothing. matchRegex :: T.Text -> T.Text -> Maybe [T.Text] matchRegex message regex = case message =~~ regex of Just (_ :: T.Text, _ :: T.Text, _ :: T.Text, bindings) -> Just bindings Nothing -> Nothing + +-- | Returns Just (all matches) for the first capture, or Nothing. +allMatchRegex :: T.Text -> T.Text -> Maybe [[T.Text]] +allMatchRegex message regex = message =~~ regex + + unifySpaces :: T.Text -> T.Text unifySpaces = T.unwords . T.words diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index a35c793c16..98b1182a57 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -10,6 +10,8 @@ module Development.IDE.Plugin.CodeAction.ExactPrint -- * Utilities appendConstraint, extendImport, + hideSymbol, + liftParseAST, ) where @@ -298,3 +300,10 @@ unqalDP paren = else pure ) (G AnnVal, dp00) + +------------------------------------------------------------------------------ +-- | Hide a symbol from import declaration +hideSymbol + :: String -> LImportDecl GhcPs -> Rewrite +hideSymbol symbol (L loc ImportDecl{..}) = Rewrite loc $ \df -> + pure (undefined :: LImportDecl GhcPs) From cbcb0f0c94b44c4e7303af0f6e5b206cd0ff87ca Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Wed, 27 Jan 2021 04:04:11 +0900 Subject: [PATCH 02/43] Draft completed --- .../src/Development/IDE/Plugin/CodeAction.hs | 4 +- .../IDE/Plugin/CodeAction/ExactPrint.hs | 75 +++++++++++++++++-- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 09c3e0a484..20e1b00bae 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -787,7 +787,7 @@ disambiguateSymbol :: GenLocated SrcSpan (ImportDecl GhcPs) -> HidingMode -> [Rewrite] -disambiguateSymbol Diagnostic {..} symbol (L loc _) = \case +disambiguateSymbol Diagnostic {..} (T.unpack -> symbol) (L loc _) = \case (HideOthers hiddens0) -> [ hideSymbol symbol idecl | idecl <- sortOn (Down . getLoc) hiddens0 @@ -799,7 +799,7 @@ disambiguateSymbol Diagnostic {..} symbol (L loc _) = \case ( L spn $ HsVar NoExt $ L spn $ - Qual qualMod (mkVarOcc $ T.unpack symbol) :: + Qual qualMod (mkVarOcc symbol) :: LHsExpr GhcPs ) ] diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index 98b1182a57..1abcc8f560 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE EmptyCase #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} @@ -22,7 +23,7 @@ import Data.Data (Data) import Data.Functor import qualified Data.HashMap.Strict as HMap import qualified Data.Map.Strict as Map -import Data.Maybe (fromJust) +import Data.Maybe (fromJust, mapMaybe) import qualified Data.Text as T import Development.IDE.GHC.Compat hiding (parseExpr) import Development.IDE.GHC.ExactPrint @@ -33,6 +34,8 @@ import Language.Haskell.GHC.ExactPrint.Types (DeltaPos (DP), KeywordId (G), mkAn import Language.Haskell.LSP.Types import OccName import Outputable (ppr, showSDocUnsafe) +import Retrie.GHC (unpackFS) +import FieldLabel (flLabel) ------------------------------------------------------------------------------ @@ -303,7 +306,69 @@ unqalDP paren = ------------------------------------------------------------------------------ -- | Hide a symbol from import declaration -hideSymbol - :: String -> LImportDecl GhcPs -> Rewrite -hideSymbol symbol (L loc ImportDecl{..}) = Rewrite loc $ \df -> - pure (undefined :: LImportDecl GhcPs) +hideSymbol :: + String -> LImportDecl GhcPs -> Rewrite +hideSymbol symbol lidecl@(L loc ImportDecl {..}) = + case ideclHiding of + Nothing -> Rewrite loc $ extendHiding symbol lidecl Nothing + Just (True, hides) -> Rewrite loc $ extendHiding symbol lidecl (Just hides) + Just (False, imports) -> Rewrite loc $ deleteFromImport symbol lidecl imports +hideSymbol _ (L _ (XImportDecl _)) = + error "cannot happen" + +extendHiding :: + String -> + LImportDecl GhcPs -> + Maybe (Located [LIE GhcPs]) -> + DynFlags -> + TransformT (Either String) (LImportDecl GhcPs) +extendHiding symbol (L l idecls) mlies df = do + L l' lies <- case mlies of + Nothing -> flip L [] <$> uniqueSrcSpanT + Just pr -> pure pr + let hasSibling = not $ null lies + src <- uniqueSrcSpanT + top <- uniqueSrcSpanT + rdr <- liftParseAST df symbol + let lie = L src $ IEName rdr + x = L top $ IEVar noExtField lie + when hasSibling $ + addTrailingCommaT (last lies) + addSimpleAnnT x (DP (0, if hasSibling then 1 else 0)) [] + addSimpleAnnT rdr dp00 $ unqalDP $ hasParen symbol + -- Parens are attachted to `lies`, so if `lies` was empty previously, + -- we need change the ann key from `[]` to `:` to keep parens and other anns. + unless hasSibling $ + transferAnn (L l' lies) (L l' [x]) id + return $ L l idecls {ideclHiding = Just (True, L l' $ lies ++ [x])} + +deleteFromImport :: + String -> + LImportDecl GhcPs -> + Located [LIE GhcPs] -> + DynFlags -> + TransformT (Either String) (LImportDecl GhcPs) +deleteFromImport symbol (L l idecls) (L lieLoc lies) _ = + pure $ L l $ idecls + { ideclHiding = Just (False, L lieLoc deletedLies) + } + where + deletedLies = + mapMaybe killLie lies + killLie :: LIE GhcPs -> Maybe (LIE GhcPs) + killLie v@(L _ (IEVar _ (L _ (unIEWrappedName -> nam)))) + | nam == symbol = Nothing + | otherwise = Just v + killLie v@(L _ (IEThingAbs _ (L _ (unIEWrappedName -> nam)))) + | nam == symbol = Nothing + | otherwise = Just v + + killLie (L lieL (IEThingWith xt ty@(L _ (unIEWrappedName -> nam)) wild cons flds)) + | nam == symbol = Nothing + | otherwise = Just $ + L lieL $ IEThingWith xt ty wild + (filter ((/= symbol) . unIEWrappedName . unLoc) cons) + (filter ((/= symbol) . unpackFS . flLabel . unLoc) flds) + killLie v = Just v + + From a0682573823fcbdcd51adb6f56174b831ed8c674 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Wed, 27 Jan 2021 04:05:32 +0900 Subject: [PATCH 03/43] Removes Unuseds --- ghcide/src/Development/IDE/Plugin/CodeAction.hs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 20e1b00bae..27b72478c5 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -767,19 +767,6 @@ suggestImportDisambiguation exportsMap (L _ HsModule {hsmodImports}) diag@Diagno <> T.pack (moduleNameString qual) <> "." <> symbol - lookupExportMap binding mod - | Just match <- Map.lookup binding (getExportsMap exportsMap) - , [(ident, _)] <- filter (\(_, m) -> mod == m) (Set.toList match) = - Just ident - -- fallback to using GHC suggestion even though it is not always correct - | otherwise = - Just - IdentInfo - { name = binding - , rendered = binding - , parent = Nothing - , isDatacon = False - } disambiguateSymbol :: Diagnostic -> From e506ddf6050d713fe652096c67e104f1f72356e7 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Wed, 27 Jan 2021 04:14:57 +0900 Subject: [PATCH 04/43] Redundant extension --- ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index 1abcc8f560..cfacb96099 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE EmptyCase #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} From a995c5e5af33a034e50bd788cc415a054f801c5a Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Wed, 27 Jan 2021 04:21:03 +0900 Subject: [PATCH 05/43] linting --- ghcide/src/Development/IDE/Plugin/CodeAction.hs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 27b72478c5..50ce5ad7ff 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -49,16 +49,16 @@ import Development.IDE.Types.Location import Development.IDE.Types.Options import Ide.PluginUtils (subRange) import Ide.Types +import Language.Haskell.GHC.ExactPrint.Transform (uniqueSrcSpanT) import qualified Language.Haskell.LSP.Core as LSP import Language.Haskell.LSP.Types import Language.Haskell.LSP.VFS import Outputable (Outputable, ppr, showSDoc, showSDocUnsafe) -import Retrie.GHC (fsLit, mkRealSrcLoc, mkRealSrcSpan) +import Retrie (unpackFS) +import Retrie.GHC (fsLit, mkRealSrcLoc, mkRealSrcSpan, mkVarOcc) import Safe (atMay) import Text.Regex.TDFA (mrAfter, (=~), (=~~)) -import Retrie (unpackFS) -import Retrie.GHC (mkVarOcc) -import Language.Haskell.GHC.ExactPrint.Transform (uniqueSrcSpanT) + descriptor :: PluginId -> PluginDescriptor IdeState descriptor plId = (defaultPluginDescriptor plId) @@ -126,7 +126,7 @@ suggestExactAction exportsMap df ps x = [ suggestConstraint df (astA ps) x , suggestImplicitParameter (astA ps) x , suggestExtendImport exportsMap (astA ps) x - , suggestImportDisambiguation exportsMap (astA ps) x + , suggestImportDisambiguation (astA ps) x ] suggestAction @@ -720,11 +720,10 @@ oneAndOthers = go -- | Suggests disambiguation for ambiguous symbols. suggestImportDisambiguation :: - ExportsMap -> ParsedSource -> Diagnostic -> [(T.Text, [Rewrite])] -suggestImportDisambiguation exportsMap (L _ HsModule {hsmodImports}) diag@Diagnostic {..} +suggestImportDisambiguation (L _ HsModule {hsmodImports}) diag@Diagnostic {..} | Just [ambiguous] <- matchRegexUnifySpaces _message From 169db8f62740891fb903d612ea139cee8720152d Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Wed, 27 Jan 2021 04:25:37 +0900 Subject: [PATCH 06/43] Makes HLint happy --- ghcide/src/Development/IDE/Plugin/CodeAction.hs | 11 +++++------ .../Development/IDE/Plugin/CodeAction/ExactPrint.hs | 5 ++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 50ce5ad7ff..29cc24a478 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -3,7 +3,6 @@ {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE CPP #-} -{-# LANGUAGE LambdaCase #-} #include "ghc-api-version.h" -- | Go to the definition of a variable. @@ -316,8 +315,8 @@ suggestDeleteUnusedBinding let findSig (L (RealSrcSpan l) (SigD _ sig)) = findRelatedSigSpan indexedContent name l sig findSig _ = [] in - [extendForSpaces indexedContent $ toRange l] - ++ concatMap findSig hsmodDecls + extendForSpaces indexedContent (toRange l) : + concatMap findSig hsmodDecls _ -> concatMap (findRelatedSpanForMatch indexedContent name) matches findRelatedSpans _ _ _ = [] @@ -392,7 +391,7 @@ suggestDeleteUnusedBinding then let findSig (L (RealSrcSpan l) sig) = findRelatedSigSpan indexedContent name l sig findSig _ = [] - in [extendForSpaces indexedContent $ toRange l] ++ concatMap findSig lsigs + in extendForSpaces indexedContent (toRange l) : concatMap findSig lsigs else concatMap (findRelatedSpanForMatch indexedContent name) matches findRelatedSpanForHsBind _ _ _ _ = [] @@ -727,7 +726,7 @@ suggestImportDisambiguation (L _ HsModule {hsmodImports}) diag@Diagnostic {..} | Just [ambiguous] <- matchRegexUnifySpaces _message - "^Ambiguous occurrence ‘([^’]+)’" + "Ambiguous occurrence ‘([^’]+)’" , Just modules <- map last <$> allMatchRegexUnifySpaces _message "imported from ‘([^’]+)’" = @@ -773,7 +772,7 @@ disambiguateSymbol :: GenLocated SrcSpan (ImportDecl GhcPs) -> HidingMode -> [Rewrite] -disambiguateSymbol Diagnostic {..} (T.unpack -> symbol) (L loc _) = \case +disambiguateSymbol Diagnostic {..} (T.unpack -> symbol) (L loc _) h = case h of (HideOthers hiddens0) -> [ hideSymbol symbol idecl | idecl <- sortOn (Down . getLoc) hiddens0 diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index cfacb96099..b186b19a6f 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -150,9 +150,8 @@ appendConstraint constraintT = go lTop <- uniqueSrcSpanT let context = L lContext [constraint] addSimpleAnnT context (DP (0, 1)) $ - [ (G AnnDarrow, DP (0, 1)) - ] - ++ concat + (G AnnDarrow, DP (0, 1)) + : concat [ [ (G AnnOpenP, dp00), (G AnnCloseP, dp00) ] From 4e92ee9f70e06ad9a87da1bc8f361869a7013eb0 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Wed, 27 Jan 2021 04:41:20 +0900 Subject: [PATCH 07/43] tweak for transfer annotation logic (not working) --- ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index b186b19a6f..1b7c71ad0d 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -334,10 +334,8 @@ extendHiding symbol (L l idecls) mlies df = do addTrailingCommaT (last lies) addSimpleAnnT x (DP (0, if hasSibling then 1 else 0)) [] addSimpleAnnT rdr dp00 $ unqalDP $ hasParen symbol - -- Parens are attachted to `lies`, so if `lies` was empty previously, - -- we need change the ann key from `[]` to `:` to keep parens and other anns. - unless hasSibling $ - transferAnn (L l' lies) (L l' [x]) id + unless hasSibling $ forM_ mlies $ \lies0 -> + transferAnn lies0 (L l' [x]) id return $ L l idecls {ideclHiding = Just (True, L l' $ lies ++ [x])} deleteFromImport :: From 72477dda6998fac96329173fab00543990484ef1 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Wed, 27 Jan 2021 22:40:56 +0900 Subject: [PATCH 08/43] Initial implementation done --- .../src/Development/IDE/Plugin/CodeAction.hs | 158 ++++++++++++------ .../IDE/Plugin/CodeAction/ExactPrint.hs | 65 +++++-- 2 files changed, 159 insertions(+), 64 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 29cc24a478..bf94a4b944 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -17,17 +17,20 @@ import Bag (isEmptyBag) import Control.Applicative ((<|>)) import Control.Arrow (second, (>>>)) import Control.Concurrent.Extra (readVar) +import Control.Lens (foldMapBy, (^.)) import Control.Monad (guard, join) import Data.Char +import Data.Coerce (coerce) +import qualified Data.DList as DL import Data.Function import Data.Functor import qualified Data.HashMap.Strict as Map import qualified Data.HashSet as Set +import Data.Hashable (Hashable) import Data.List.Extra import Data.List.NonEmpty (NonEmpty ((:|))) import qualified Data.List.NonEmpty as NE import Data.Maybe -import Data.Ord (Down (Down)) import qualified Data.Rope.UTF16 as Rope import qualified Data.Text as T import Development.IDE.Core.RuleTypes @@ -46,18 +49,18 @@ import Development.IDE.Plugin.TypeLenses (suggestSignature) import Development.IDE.Types.Exports import Development.IDE.Types.Location import Development.IDE.Types.Options +import qualified GHC.LanguageExtensions as Lang import Ide.PluginUtils (subRange) import Ide.Types -import Language.Haskell.GHC.ExactPrint.Transform (uniqueSrcSpanT) import qualified Language.Haskell.LSP.Core as LSP import Language.Haskell.LSP.Types import Language.Haskell.LSP.VFS import Outputable (Outputable, ppr, showSDoc, showSDocUnsafe) -import Retrie (unpackFS) -import Retrie.GHC (fsLit, mkRealSrcLoc, mkRealSrcSpan, mkVarOcc) +import Retrie.GHC (mkVarOcc) import Safe (atMay) import Text.Regex.TDFA (mrAfter, (=~), (=~~)) - +import Language.Haskell.LSP.Types.Lens (start, character, end, line) +import OccName (parenSymOcc) descriptor :: PluginId -> PluginDescriptor IdeState descriptor plId = (defaultPluginDescriptor plId) @@ -101,8 +104,26 @@ codeAction lsp state _ (TextDocumentIdentifier uri) _range CodeActionContext{_di , Just ps <- [annotatedPS] , Just dynflags <- [df] , (title, grafts) <- suggestExactAction exportsMap dynflags ps x - , let edit = foldMap (either error id . + , let edit = foldMapBy unionWSEdit mempty (either error id . rewriteToEdit dynflags uri (annsA ps)) grafts + ] ++ + [mkCA title [x] edit + | x <- xs + , ps <- maybeToList annotatedPS + , dynflags <- maybeToList df + , (title, edRewrs) <- + suggestImportDisambiguation dynflags (astA ps) x + , let edit = + foldMapBy unionWSEdit mempty + (either + (\te -> WorkspaceEdit + { _changes = Just $ Map.singleton uri $ List [te] + , _documentChanges = Nothing } + ) + (either error id . + rewriteToEdit dynflags uri (annsA ps)) + ) edRewrs + ] actions'' = caRemoveRedundantImports parsedModule text diag xs uri <> actions @@ -110,6 +131,19 @@ codeAction lsp state _ (TextDocumentIdentifier uri) _range CodeActionContext{_di <> caRemoveInvalidExports parsedModule text diag xs uri pure $ Right $ List actions'' +-- | Semigroup instance just overrides duplicated keys in the first argument +unionWSEdit :: WorkspaceEdit -> WorkspaceEdit -> WorkspaceEdit +unionWSEdit (WorkspaceEdit a b) (WorkspaceEdit c d) = + -- FIXME: Want to use monoidal-containers, but it supports aeson <1.5 only... + WorkspaceEdit (runCatHashMap <$> fmap CatHashMap a <> fmap CatHashMap c) + (b <> d) + +newtype CatHashMap k v = CatHashMap { runCatHashMap :: Map.HashMap k v } +instance (Eq k, Hashable k, Semigroup v) => Semigroup (CatHashMap k v) where + (<>) = coerce $ Map.unionWith @k @v (<>) +instance (Eq k, Hashable k, Semigroup v) => Monoid (CatHashMap k v) where + mempty = CatHashMap mempty + mkCA :: T.Text -> [Diagnostic] -> WorkspaceEdit -> CAResult mkCA title diags edit = CACodeAction $ CodeAction title (Just CodeActionQuickFix) (Just $ List diags) (Just edit) Nothing @@ -125,7 +159,6 @@ suggestExactAction exportsMap df ps x = [ suggestConstraint df (astA ps) x , suggestImplicitParameter (astA ps) x , suggestExtendImport exportsMap (astA ps) x - , suggestImportDisambiguation (astA ps) x ] suggestAction @@ -708,7 +741,13 @@ suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_ , parent = Nothing , isDatacon = False} -data HidingMode = HideOthers [LImportDecl GhcPs] | ToQualified ModuleName +data HidingMode = HideOthers [ModuleTarget] + | ToQualified ModuleName + deriving (Show) + +data ModuleTarget + = ExistingImp (NonEmpty (LImportDecl GhcPs)) + | ImplicitPrelude [LImportDecl GhcPs] deriving (Show) oneAndOthers :: [a] -> [(a, [a])] @@ -717,12 +756,16 @@ oneAndOthers = go go [] = [] go (x : xs) = (x, xs) : map (second (x :)) (go xs) +isPreludeImplicit :: DynFlags -> Bool +isPreludeImplicit = xopt Lang.ImplicitPrelude + -- | Suggests disambiguation for ambiguous symbols. suggestImportDisambiguation :: + DynFlags -> ParsedSource -> Diagnostic -> - [(T.Text, [Rewrite])] -suggestImportDisambiguation (L _ HsModule {hsmodImports}) diag@Diagnostic {..} + [(T.Text, [Either TextEdit Rewrite])] +suggestImportDisambiguation df ps@(L _ HsModule {hsmodImports}) diag@Diagnostic {..} | Just [ambiguous] <- matchRegexUnifySpaces _message @@ -734,28 +777,38 @@ suggestImportDisambiguation (L _ HsModule {hsmodImports}) diag@Diagnostic {..} | otherwise = [] where locDic = - Map.fromList $ + fmap (NE.fromList . DL.toList) $ + Map.fromListWith (<>) $ map ( \i@(L _ idecl) -> ( T.pack $ moduleNameString $ unLoc $ ideclName idecl - , i + , DL.singleton i ) ) hsmodImports + toModuleTarget "Prelude" + | isPreludeImplicit df + = Just $ ImplicitPrelude $ + maybe [] NE.toList (Map.lookup "Prelude" locDic) + toModuleTarget mName = ExistingImp <$> Map.lookup mName locDic + suggestions symbol mods - | Just targets <- mapM (`Map.lookup` locDic) mods = + | Just targets <- mapM toModuleTarget mods = + sortOn fst [ ( renderUniquify mode modNameText symbol - , disambiguateSymbol diag symbol lidecl mode + , disambiguateSymbol df ps diag symbol mode ) - | (lidecl@(L _ ImportDecl {..}), restImports) <- oneAndOthers targets - , let modName = unLoc ideclName + | (modTarget, restImports) <- oneAndOthers targets + , let modName = targetModuleName modTarget modNameText = T.pack $ moduleNameString modName , mode <- + HideOthers restImports : [ ToQualified qual - | L _ qual <- maybeToList ideclAs + | ExistingImp imps <- [modTarget] + , L _ qual <- nubOrd $ mapMaybe (ideclAs . unLoc) + $ NE.toList imps ] - ++ [HideOthers restImports] - ++ [ToQualified modName] + ++ [ToQualified modName] ] | otherwise = [] renderUniquify HideOthers {} modName symbol = @@ -766,41 +819,52 @@ suggestImportDisambiguation (L _ HsModule {hsmodImports}) diag@Diagnostic {..} <> "." <> symbol +targetModuleName :: ModuleTarget -> ModuleName +targetModuleName ImplicitPrelude{} = mkModuleName "Prelude" +targetModuleName (ExistingImp (L _ ImportDecl{..} :| _)) = + unLoc ideclName +targetModuleName (ExistingImp _) = + error "Cannot happen!" + disambiguateSymbol :: + DynFlags -> + ParsedSource -> Diagnostic -> T.Text -> - GenLocated SrcSpan (ImportDecl GhcPs) -> HidingMode -> - [Rewrite] -disambiguateSymbol Diagnostic {..} (T.unpack -> symbol) (L loc _) h = case h of + [Either TextEdit Rewrite] +disambiguateSymbol df pm Diagnostic {..} (T.unpack -> symbol) = \case (HideOthers hiddens0) -> - [ hideSymbol symbol idecl - | idecl <- sortOn (Down . getLoc) hiddens0 + [ Right $ hideSymbol symbol idecl + | ExistingImp idecls <- hiddens0 + , idecl <- NE.toList idecls ] + ++ mconcat + [ if null imps + then [Left $ hidePreludeSymbol df symbol pm] + else Right . hideSymbol symbol <$> imps + | ImplicitPrelude imps <- hiddens0 + ] (ToQualified qualMod) -> - [ Rewrite (rangeToSrcSpan theFile _range) $ \_ -> do - spn <- uniqueSrcSpanT - pure - ( L spn $ - HsVar NoExt $ - L spn $ - Qual qualMod (mkVarOcc symbol) :: - LHsExpr GhcPs - ) - ] - where - theFile - | RealSrcSpan real <- loc = unpackFS $ srcSpanFile real - | otherwise = "" - -rangeToSrcSpan :: String -> Range -> SrcSpan -rangeToSrcSpan file range = RealSrcSpan $ rangeToRealSrcSpan file range - -rangeToRealSrcSpan :: String -> Range -> RealSrcSpan -rangeToRealSrcSpan file (Range (Position startLn startCh) (Position endLn endCh)) = - mkRealSrcSpan - (mkRealSrcLoc (fsLit file) (startLn + 1) (startCh + 1)) - (mkRealSrcLoc (fsLit file) (endLn + 1) (endCh + 1)) + let occSym = mkVarOcc symbol + rdr = Qual qualMod occSym + in [Left $ TextEdit _range + $ T.pack $ showSDoc df + $ parenSymOcc occSym + $ ppr rdr] + +-- Couldn't make out how to add New imports by ghc-exactprint; +-- using direct TextEdit instead. +hidePreludeSymbol :: DynFlags -> String -> ParsedSource -> TextEdit +hidePreludeSymbol df symbol (L _ HsModule{..}) = + let ran = fromJust $ srcSpanToRange $ getLoc $ last hsmodImports + col = ran ^. start.character + beg = Position (1 + (ran ^. end.line)) 0 + symOcc = mkVarOcc symbol + symImp = T.pack $ showSDoc df $ parenSymOcc symOcc $ ppr symOcc + in TextEdit + (Range beg beg) + $ T.replicate col " " <> "import Prelude hiding (" <> symImp <> ")\n" findImportDeclByRange :: [LImportDecl GhcPs] -> Range -> Maybe (LImportDecl GhcPs) findImportDeclByRange xs range = find (\(L l _)-> srcSpanToRange l == Just range) xs diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index 1b7c71ad0d..f22ec0bbcf 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -1,4 +1,5 @@ {-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE CPP #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} @@ -6,6 +7,7 @@ module Development.IDE.Plugin.CodeAction.ExactPrint ( Rewrite (..), rewriteToEdit, + transferAnn, -- * Utilities appendConstraint, @@ -18,24 +20,28 @@ where import Control.Applicative import Control.Monad import Control.Monad.Trans +import Data.Char (isAlphaNum) import Data.Data (Data) import Data.Functor import qualified Data.HashMap.Strict as HMap import qualified Data.Map.Strict as Map -import Data.Maybe (fromJust, mapMaybe) +import Data.Maybe (fromJust, isNothing, mapMaybe) import qualified Data.Text as T import Development.IDE.GHC.Compat hiding (parseExpr) import Development.IDE.GHC.ExactPrint import Development.IDE.Types.Location +import FieldLabel (flLabel) import GhcPlugins (realSrcSpanEnd, realSrcSpanStart, sigPrec) import Language.Haskell.GHC.ExactPrint import Language.Haskell.GHC.ExactPrint.Types (DeltaPos (DP), KeywordId (G), mkAnnKey) import Language.Haskell.LSP.Types import OccName import Outputable (ppr, showSDocUnsafe) -import Retrie.GHC (unpackFS) -import FieldLabel (flLabel) - +import Retrie.GHC (rdrNameOcc, unpackFS) +import HeaderInfo (mkPrelImports) +import Retrie (mkRealSrcSpan) +import Retrie.GHC (SourceText(NoSourceText)) +import RdrName (mkRdrUnqual) ------------------------------------------------------------------------------ -- | Construct a 'Rewrite', replacing the node at the given 'SrcSpan' with the @@ -330,13 +336,28 @@ extendHiding symbol (L l idecls) mlies df = do rdr <- liftParseAST df symbol let lie = L src $ IEName rdr x = L top $ IEVar noExtField lie - when hasSibling $ - addTrailingCommaT (last lies) - addSimpleAnnT x (DP (0, if hasSibling then 1 else 0)) [] - addSimpleAnnT rdr dp00 $ unqalDP $ hasParen symbol - unless hasSibling $ forM_ mlies $ \lies0 -> - transferAnn lies0 (L l' [x]) id - return $ L l idecls {ideclHiding = Just (True, L l' $ lies ++ [x])} + singleHide = L l' [x] + when (isNothing mlies) $ do + addSimpleAnnT + singleHide + dp00 + [ (G AnnHiding, DP (0, 1)) + , (G AnnOpenP, DP (0, 1)) + , (G AnnCloseP, DP (0, 0)) + ] + addSimpleAnnT x (DP (0, 0)) [] + addSimpleAnnT rdr dp00 $ unqalDP $ isOperator $ unLoc rdr + if hasSibling + then when hasSibling $ do + addTrailingCommaT x + addSimpleAnnT (head lies) (DP (0, 1)) [] + unless (null $ tail lies) $ + addTrailingCommaT (head lies) -- Why we need this? + else forM_ mlies $ \lies0 -> do + transferAnn lies0 singleHide id + return $ L l idecls {ideclHiding = Just (True, L l' $ x : lies)} + where + isOperator = not . all isAlphaNum . occNameString . rdrNameOcc deleteFromImport :: String -> @@ -344,7 +365,14 @@ deleteFromImport :: Located [LIE GhcPs] -> DynFlags -> TransformT (Either String) (LImportDecl GhcPs) -deleteFromImport symbol (L l idecls) (L lieLoc lies) _ = +deleteFromImport symbol (L l idecls) llies@(L lieLoc lies) _ =do + let edited = L lieLoc deletedLies + when (not (null lies) && null deletedLies) $ do + transferAnn llies edited id + addSimpleAnnT edited dp00 + [(G AnnOpenP, DP (0, 1)) + ,(G AnnCloseP, DP (0,0)) + ] pure $ L l $ idecls { ideclHiding = Just (False, L lieLoc deletedLies) } @@ -352,19 +380,22 @@ deleteFromImport symbol (L l idecls) (L lieLoc lies) _ = deletedLies = mapMaybe killLie lies killLie :: LIE GhcPs -> Maybe (LIE GhcPs) - killLie v@(L _ (IEVar _ (L _ (unIEWrappedName -> nam)))) + killLie v@(L _ (IEVar _ (L _ (rawIEWrapName -> nam)))) | nam == symbol = Nothing | otherwise = Just v - killLie v@(L _ (IEThingAbs _ (L _ (unIEWrappedName -> nam)))) + killLie v@(L _ (IEThingAbs _ (L _ (rawIEWrapName -> nam)))) | nam == symbol = Nothing | otherwise = Just v - killLie (L lieL (IEThingWith xt ty@(L _ (unIEWrappedName -> nam)) wild cons flds)) + killLie (L lieL (IEThingWith xt ty@(L _ (rawIEWrapName -> nam)) wild cons flds)) | nam == symbol = Nothing | otherwise = Just $ L lieL $ IEThingWith xt ty wild - (filter ((/= symbol) . unIEWrappedName . unLoc) cons) + (filter ((/= symbol) . rawIEWrapName . unLoc) cons) (filter ((/= symbol) . unpackFS . flLabel . unLoc) flds) killLie v = Just v - +rawIEWrapName :: IEWrappedName RdrName -> String +rawIEWrapName (IEName (L _ nam)) = occNameString $ rdrNameOcc nam +rawIEWrapName (IEPattern (L _ nam)) = occNameString $ rdrNameOcc nam +rawIEWrapName (IEType (L _ nam)) = occNameString $ rdrNameOcc nam From 65724faa9cc157689bc3289d86004eca05a68c5e Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Wed, 27 Jan 2021 22:46:34 +0900 Subject: [PATCH 09/43] Import list reorder --- ghcide/src/Development/IDE/Plugin/CodeAction.hs | 5 +++-- ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs | 5 +---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index bf94a4b944..a039dd6681 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -54,13 +54,14 @@ import Ide.PluginUtils (subRange) import Ide.Types import qualified Language.Haskell.LSP.Core as LSP import Language.Haskell.LSP.Types +import Language.Haskell.LSP.Types.Lens (character, end, line, start) import Language.Haskell.LSP.VFS +import OccName (parenSymOcc) import Outputable (Outputable, ppr, showSDoc, showSDocUnsafe) import Retrie.GHC (mkVarOcc) import Safe (atMay) import Text.Regex.TDFA (mrAfter, (=~), (=~~)) -import Language.Haskell.LSP.Types.Lens (start, character, end, line) -import OccName (parenSymOcc) + descriptor :: PluginId -> PluginDescriptor IdeState descriptor plId = (defaultPluginDescriptor plId) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index f22ec0bbcf..dd96104f20 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -38,10 +38,7 @@ import Language.Haskell.LSP.Types import OccName import Outputable (ppr, showSDocUnsafe) import Retrie.GHC (rdrNameOcc, unpackFS) -import HeaderInfo (mkPrelImports) -import Retrie (mkRealSrcSpan) -import Retrie.GHC (SourceText(NoSourceText)) -import RdrName (mkRdrUnqual) + ------------------------------------------------------------------------------ -- | Construct a 'Rewrite', replacing the node at the given 'SrcSpan' with the From 4ed8abc2509fef004d31fc198c548248ac74a0a7 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Wed, 27 Jan 2021 22:49:28 +0900 Subject: [PATCH 10/43] Remove redundant fmt --- ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index dd96104f20..db4ee0cd0c 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -1,5 +1,4 @@ {-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE CPP #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} From 30f2f165d42776c93a9a4b1799a9879207b49c31 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Fri, 29 Jan 2021 10:39:32 +0900 Subject: [PATCH 11/43] lint --- ghcide/src/Development/IDE/Plugin/CodeAction.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 00dd120c97..4a122c3ed1 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -122,7 +122,7 @@ codeAction lsp state _ (TextDocumentIdentifier uri) _range CodeActionContext{_di , _documentChanges = Nothing } ) (-- either (Left . traceShow) Right $ - either (const mempty) id . + fromRight mempty. rewriteToEdit dynflags uri (annsA ps)) ) edRewrs From 9e656c5201c1abfec1878d885f3b269880b24db6 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Fri, 29 Jan 2021 10:45:41 +0900 Subject: [PATCH 12/43] Missing import --- ghcide/src/Development/IDE/Plugin/CodeAction.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 4a122c3ed1..b894812137 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -61,6 +61,7 @@ import Outputable (Outputable, ppr, showSDoc, showSDocUnsafe) import Retrie.GHC (mkVarOcc) import Safe (atMay) import Text.Regex.TDFA (mrAfter, (=~), (=~~)) +import Data.Either (fromRight) descriptor :: PluginId -> PluginDescriptor IdeState descriptor plId = From d19b4fbf845d8dab919aebbe5641fc6bbcfa5a59 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 00:03:07 +0900 Subject: [PATCH 13/43] Excludes false-positive qualified imports --- .../src/Development/IDE/Plugin/CodeAction.hs | 42 ++++++++++++++++++- .../IDE/Plugin/CodeAction/ExactPrint.hs | 1 + 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index b894812137..3f2add02e2 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -62,6 +62,8 @@ import Retrie.GHC (mkVarOcc) import Safe (atMay) import Text.Regex.TDFA (mrAfter, (=~), (=~~)) import Data.Either (fromRight) +import Retrie (unpackFS) +import FieldLabel (flLabel) descriptor :: PluginId -> PluginDescriptor IdeState descriptor plId = @@ -753,6 +755,10 @@ data ModuleTarget | ImplicitPrelude [LImportDecl GhcPs] deriving (Show) +targetImports :: ModuleTarget -> [LImportDecl GhcPs] +targetImports (ExistingImp ne) = NE.toList ne +targetImports (ImplicitPrelude xs) = xs + oneAndOthers :: [a] -> [(a, [a])] oneAndOthers = go where @@ -811,17 +817,49 @@ suggestImportDisambiguation df ps@(L _ HsModule {hsmodImports}) diag@Diagnostic , L _ qual <- nubOrd $ mapMaybe (ideclAs . unLoc) $ NE.toList imps ] - ++ [ToQualified modName] + ++ [ToQualified modName + | any (occursUnqualified symbol . unLoc) + (targetImports modTarget) + || case modTarget of + ImplicitPrelude{} -> True + _ -> False + ] ] | otherwise = [] renderUniquify HideOthers {} modName symbol = - "Use the import from " <> modName <> " for " <> symbol <> ", hiding other imports" + "Use " <> modName <> " for " <> symbol <> ", hiding other imports" renderUniquify (ToQualified qual) _ symbol = "Replace with qualified: " <> T.pack (moduleNameString qual) <> "." <> symbol +occursUnqualified :: T.Text -> ImportDecl GhcPs -> Bool +occursUnqualified symbol ImportDecl{..} + | isNothing ideclAs = Just False /= + -- I don't find this particularly comprehensible, + -- but HLint suggested me to do so... + (ideclHiding <&> \(isHiding, L _ ents) -> + let occurs = any ((symbol `symbolOccursIn`) . unLoc) ents + in isHiding && not occurs || not isHiding && occurs + ) +occursUnqualified _ _ = False + +symbolOccursIn :: T.Text -> IE GhcPs -> Bool +symbolOccursIn symb = \case + IEVar _ (L _ n) -> rawIEWrapName n == T.unpack symb + IEThingAbs _ (L _ n) -> rawIEWrapName n == T.unpack symb + IEThingAll _ (L _ n) -> rawIEWrapName n == T.unpack symb + IEThingWith _ (L _ n) _ ents flds -> + rawIEWrapName n == T.unpack symb + || any ((== T.unpack symb) . rawIEWrapName . unLoc) ents + || any ((== T.unpack symb) . unpackFS . flLabel . unLoc) flds + IEModuleContents{} -> False + IEGroup{} -> False + IEDoc{} -> False + IEDocNamed{} -> False + XIE{} -> False + targetModuleName :: ModuleTarget -> ModuleName targetModuleName ImplicitPrelude{} = mkModuleName "Prelude" targetModuleName (ExistingImp (L _ ImportDecl{..} :| _)) = diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index db4ee0cd0c..854979e952 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -7,6 +7,7 @@ module Development.IDE.Plugin.CodeAction.ExactPrint ( Rewrite (..), rewriteToEdit, transferAnn, + rawIEWrapName, -- * Utilities appendConstraint, From eb3cd113326ed98887b32d0e9e93083c3c336e48 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 00:04:18 +0900 Subject: [PATCH 14/43] A first test (not enough though) --- ghcide/test/data/hiding/AVec.hs | 17 +++++++++++++ ghcide/test/data/hiding/BVec.hs | 17 +++++++++++++ ghcide/test/data/hiding/CVec.hs | 17 +++++++++++++ ghcide/test/data/hiding/DVec.hs | 17 +++++++++++++ ghcide/test/data/hiding/EVec.hs | 17 +++++++++++++ ghcide/test/data/hiding/HideFunction.hs | 11 ++++++++ .../HideFunction.hs.expected.A.fromList | 11 ++++++++ ghcide/test/data/hiding/hie.yaml | 10 ++++++++ ghcide/test/exe/Main.hs | 25 +++++++++++++++++++ 9 files changed, 142 insertions(+) create mode 100644 ghcide/test/data/hiding/AVec.hs create mode 100644 ghcide/test/data/hiding/BVec.hs create mode 100644 ghcide/test/data/hiding/CVec.hs create mode 100644 ghcide/test/data/hiding/DVec.hs create mode 100644 ghcide/test/data/hiding/EVec.hs create mode 100644 ghcide/test/data/hiding/HideFunction.hs create mode 100644 ghcide/test/data/hiding/HideFunction.hs.expected.A.fromList create mode 100644 ghcide/test/data/hiding/hie.yaml diff --git a/ghcide/test/data/hiding/AVec.hs b/ghcide/test/data/hiding/AVec.hs new file mode 100644 index 0000000000..62dee4ca3a --- /dev/null +++ b/ghcide/test/data/hiding/AVec.hs @@ -0,0 +1,17 @@ +module AVec (Vec, (++), cons, fromList, snoc) where + +import Prelude hiding ((++)) + +data Vec a + +(++) :: Vec a -> Vec a -> Vec a +(++) = undefined + +fromList :: [a] -> Vec a +fromList = undefined + +cons :: a -> Vec a -> Vec a +cons = undefined + +snoc :: Vec a -> a -> Vec a +snoc = undefined diff --git a/ghcide/test/data/hiding/BVec.hs b/ghcide/test/data/hiding/BVec.hs new file mode 100644 index 0000000000..3f2a1ab259 --- /dev/null +++ b/ghcide/test/data/hiding/BVec.hs @@ -0,0 +1,17 @@ +module BVec (Vec, (++), cons, fromList, snoc) where + +import Prelude hiding ((++)) + +data Vec a + +(++) :: Vec a -> Vec a -> Vec a +(++) = undefined + +fromList :: [a] -> Vec a +fromList = undefined + +cons :: a -> Vec a -> Vec a +cons = undefined + +snoc :: Vec a -> a -> Vec a +snoc = undefined diff --git a/ghcide/test/data/hiding/CVec.hs b/ghcide/test/data/hiding/CVec.hs new file mode 100644 index 0000000000..b943c786bd --- /dev/null +++ b/ghcide/test/data/hiding/CVec.hs @@ -0,0 +1,17 @@ +module CVec (Vec, (++), cons, fromList, snoc) where + +import Prelude hiding ((++)) + +data Vec a + +(++) :: Vec a -> Vec a -> Vec a +(++) = undefined + +fromList :: [a] -> Vec a +fromList = undefined + +cons :: a -> Vec a -> Vec a +cons = undefined + +snoc :: Vec a -> a -> Vec a +snoc = undefined diff --git a/ghcide/test/data/hiding/DVec.hs b/ghcide/test/data/hiding/DVec.hs new file mode 100644 index 0000000000..b293b18176 --- /dev/null +++ b/ghcide/test/data/hiding/DVec.hs @@ -0,0 +1,17 @@ +module DVec (Vec, (++), cons, fromList, snoc) where + +import Prelude hiding ((++)) + +data Vec a + +(++) :: Vec a -> Vec a -> Vec a +(++) = undefined + +fromList :: [a] -> Vec a +fromList = undefined + +cons :: a -> Vec a -> Vec a +cons = undefined + +snoc :: Vec a -> a -> Vec a +snoc = undefined diff --git a/ghcide/test/data/hiding/EVec.hs b/ghcide/test/data/hiding/EVec.hs new file mode 100644 index 0000000000..456305f915 --- /dev/null +++ b/ghcide/test/data/hiding/EVec.hs @@ -0,0 +1,17 @@ +module EVec (Vec, (++), cons, fromList, snoc) where + +import Prelude hiding ((++)) + +data Vec a + +(++) :: Vec a -> Vec a -> Vec a +(++) = undefined + +fromList :: [a] -> Vec a +fromList = undefined + +cons :: a -> Vec a -> Vec a +cons = undefined + +snoc :: Vec a -> a -> Vec a +snoc = undefined diff --git a/ghcide/test/data/hiding/HideFunction.hs b/ghcide/test/data/hiding/HideFunction.hs new file mode 100644 index 0000000000..ade8f63ac5 --- /dev/null +++ b/ghcide/test/data/hiding/HideFunction.hs @@ -0,0 +1,11 @@ +module HideFunction where + +import AVec (fromList) +import BVec (fromList, (++)) +import CVec hiding (cons) +import DVec hiding (cons, snoc) +import EVec as E + +theFun = fromList + +theOp = (++) diff --git a/ghcide/test/data/hiding/HideFunction.hs.expected.A.fromList b/ghcide/test/data/hiding/HideFunction.hs.expected.A.fromList new file mode 100644 index 0000000000..b91d83f98b --- /dev/null +++ b/ghcide/test/data/hiding/HideFunction.hs.expected.A.fromList @@ -0,0 +1,11 @@ +module HideFunction where + +import AVec (fromList) +import BVec ( (++)) +import CVec hiding (fromList, cons) +import DVec hiding (fromList, cons, snoc) +import EVec as E hiding (fromList) + +theFun = fromList + +theOp = (++) diff --git a/ghcide/test/data/hiding/hie.yaml b/ghcide/test/data/hiding/hie.yaml new file mode 100644 index 0000000000..075686555e --- /dev/null +++ b/ghcide/test/data/hiding/hie.yaml @@ -0,0 +1,10 @@ +cradle: + direct: + arguments: + - -Wall + - HideFunction.hs + - AVec.hs + - BVec.hs + - CVec.hs + - DVec.hs + - EVec.hs diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index d124a5e77f..b9dfd09728 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -27,6 +27,7 @@ import Development.IDE.Core.PositionMapping (fromCurrent, toCurrent, PositionRes import Development.IDE.Core.Shake (Q(..)) import Development.IDE.GHC.Util import qualified Data.Text as T +import qualified Data.Text.IO as T import Data.Typeable import Development.IDE.Plugin.TypeLenses (typeLensCommandId) import Development.IDE.Spans.Common @@ -678,6 +679,7 @@ codeActionTests = testGroup "code actions" , removeImportTests , extendImportTests , suggestImportTests + , suggestImportDisambiguationTests , disableWarningTests , fixConstructorImportTests , importRenameActionTests @@ -1460,6 +1462,29 @@ suggestImportTests = testGroup "suggest import actions" else liftIO $ [_title | CACodeAction CodeAction{_title} <- actions, _title == newImp ] @?= [] +suggestImportDisambiguationTests :: TestTree +suggestImportDisambiguationTests = testGroup "suggest import disambiguation actions" + [ testGroup "Hiding strategy works" + [ testCase "Symbol" $ runInDir hidingDir $ do + doc <- openDoc ("HideFunction" <.> "hs") "haskell" + expected <- liftIO $ + T.readFile (hidingDir "HideFunction" <.> "hs" <.> "expected.A.fromList") + void (skipManyTill anyMessage message + :: Session WorkDoneProgressEndNotification) + void waitForDiagnostics + liftIO $ sleep 0.5 + contents <- documentContents doc + let range = Range (Position 0 0) (Position (length $ T.lines contents) 0) + actions <- getCodeActions doc range + action <- liftIO $ pickActionWithTitle "Use AVec for fromList, hiding other imports" actions + executeCodeAction action + contentAfterAction <- documentContents doc + liftIO $ expected @=? contentAfterAction + ] + ] + where + hidingDir = "test/data/hiding" + disableWarningTests :: TestTree disableWarningTests = testGroup "disable warnings" $ From 0239aa0e718bb1f279ca38681bcea2a20d58987b Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 00:09:26 +0900 Subject: [PATCH 15/43] fmt.sh --- ghcide/test/exe/Main.hs | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index b9dfd09728..4fec418940 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -27,11 +27,20 @@ import Development.IDE.Core.PositionMapping (fromCurrent, toCurrent, PositionRes import Development.IDE.Core.Shake (Q(..)) import Development.IDE.GHC.Util import qualified Data.Text as T -import qualified Data.Text.IO as T import Data.Typeable import Development.IDE.Plugin.TypeLenses (typeLensCommandId) import Development.IDE.Spans.Common import Development.IDE.Test + ( canonicalizeUri, + diagnostic, + expectCurrentDiagnostics, + expectDiagnostics, + expectDiagnosticsWithTags, + expectNoMoreDiagnostics, + flushMessages, + standardizeQuotes, + waitForAction, + Cursor ) import Development.IDE.Test.Runfiles import qualified Development.IDE.Types.Diagnostics as Diagnostics import Development.IDE.Types.Location @@ -1465,10 +1474,21 @@ suggestImportTests = testGroup "suggest import actions" suggestImportDisambiguationTests :: TestTree suggestImportDisambiguationTests = testGroup "suggest import disambiguation actions" [ testGroup "Hiding strategy works" - [ testCase "Symbol" $ runInDir hidingDir $ do + [ testGroup "fromList" + [ testCase "AVec" $ withHideFunction $ \doc actions -> do + expected <- liftIO $ + readFileUtf8 (hidingDir "HideFunction" <.> "hs" <.> "expected.A.fromList") + action <- liftIO $ pickActionWithTitle "Use AVec for fromList, hiding other imports" actions + executeCodeAction action + contentAfterAction <- documentContents doc + liftIO $ expected @=? contentAfterAction + ] + ] + ] + where + hidingDir = "test/data/hiding" + withHideFunction k = runInDir hidingDir $ do doc <- openDoc ("HideFunction" <.> "hs") "haskell" - expected <- liftIO $ - T.readFile (hidingDir "HideFunction" <.> "hs" <.> "expected.A.fromList") void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification) void waitForDiagnostics @@ -1476,14 +1496,7 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti contents <- documentContents doc let range = Range (Position 0 0) (Position (length $ T.lines contents) 0) actions <- getCodeActions doc range - action <- liftIO $ pickActionWithTitle "Use AVec for fromList, hiding other imports" actions - executeCodeAction action - contentAfterAction <- documentContents doc - liftIO $ expected @=? contentAfterAction - ] - ] - where - hidingDir = "test/data/hiding" + k doc actions disableWarningTests :: TestTree disableWarningTests = @@ -2911,7 +2924,7 @@ findDefinitionAndHoverTests = let Position{_line = l + 1, _character = c + 1} in case map (read . T.unpack) lineCol of - [l,c] -> liftIO $ (adjust $ _start expectedRange) @=? Position l c + [l,c] -> liftIO $ adjust (_start expectedRange) @=? Position l c _ -> liftIO $ assertFailure $ "expected: " <> show ("[...]" <> sourceFileName <> "::**[...]", Just expectedRange) <> "\n but got: " <> show (msg, rangeInHover) From a6ddab25b4876f94eacac6b04211a9e3989d352a Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 00:33:02 +0900 Subject: [PATCH 16/43] Some more test cases --- .../hiding/HideFunction.hs.expected.append.E | 12 ++++++ ...st => HideFunction.hs.expected.fromList.A} | 0 ghcide/test/exe/Main.hs | 41 +++++++++++++++---- 3 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 ghcide/test/data/hiding/HideFunction.hs.expected.append.E rename ghcide/test/data/hiding/{HideFunction.hs.expected.A.fromList => HideFunction.hs.expected.fromList.A} (100%) diff --git a/ghcide/test/data/hiding/HideFunction.hs.expected.append.E b/ghcide/test/data/hiding/HideFunction.hs.expected.append.E new file mode 100644 index 0000000000..94d333b24a --- /dev/null +++ b/ghcide/test/data/hiding/HideFunction.hs.expected.append.E @@ -0,0 +1,12 @@ +module HideFunction where + +import AVec (fromList) +import BVec (fromList,) +import CVec hiding ((++), cons) +import DVec hiding ((++), cons, snoc) +import EVec as E +import Prelude hiding ((++)) + +theFun = fromList + +theOp = (++) diff --git a/ghcide/test/data/hiding/HideFunction.hs.expected.A.fromList b/ghcide/test/data/hiding/HideFunction.hs.expected.fromList.A similarity index 100% rename from ghcide/test/data/hiding/HideFunction.hs.expected.A.fromList rename to ghcide/test/data/hiding/HideFunction.hs.expected.fromList.A diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 4fec418940..a9eac281d5 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -1475,18 +1475,45 @@ suggestImportDisambiguationTests :: TestTree suggestImportDisambiguationTests = testGroup "suggest import disambiguation actions" [ testGroup "Hiding strategy works" [ testGroup "fromList" - [ testCase "AVec" $ withHideFunction $ \doc actions -> do - expected <- liftIO $ - readFileUtf8 (hidingDir "HideFunction" <.> "hs" <.> "expected.A.fromList") - action <- liftIO $ pickActionWithTitle "Use AVec for fromList, hiding other imports" actions - executeCodeAction action - contentAfterAction <- documentContents doc - liftIO $ expected @=? contentAfterAction + [ testCase "AVec" $ + compareHideFunctionTo + "Use AVec for fromList, hiding other imports" + "HideFunction.hs.expected.fromList.A" + ] + , testGroup "(++)" + [testCase "EVec" $ + compareHideFunctionTo + "Use EVec for ++, hiding other imports" + "HideFunction.hs.expected.append.E" ] ] + , testGroup "Qualify strategy" + [ testCase "won't suggest full name for qualified module" $ + withHideFunction $ \_ actions -> do + liftIO $ + assertBool "EVec.fromList must not be suggested" $ + "Replace with qualified: EVec.fromList" `notElem` + [ actionTitle + | CACodeAction CodeAction { _title = actionTitle } <- actions + ] + liftIO $ + assertBool "EVec.++ must not be suggested" $ + "Replace with qualified: EVec.++" `notElem` + [ actionTitle + | CACodeAction CodeAction { _title = actionTitle } <- actions + ] + ] ] where hidingDir = "test/data/hiding" + compareHideFunctionTo cmd expected = + withHideFunction $ \doc actions -> do + expected <- liftIO $ + readFileUtf8 (hidingDir expected) + action <- liftIO $ pickActionWithTitle cmd actions + executeCodeAction action + contentAfterAction <- documentContents doc + liftIO $ expected @=? contentAfterAction withHideFunction k = runInDir hidingDir $ do doc <- openDoc ("HideFunction" <.> "hs") "haskell" void (skipManyTill anyMessage message From 4816c8455000d2f5d74bdc513fdb2f87b58af922 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 01:05:26 +0900 Subject: [PATCH 17/43] More test cases --- .../IDE/Plugin/CodeAction/ExactPrint.hs | 10 ++++++---- .../hiding/HideFunction.hs.expected.fromList.B | 11 +++++++++++ ...unction.hs.expected.qualified.append.Prelude | 11 +++++++++++ ...ideFunction.hs.expected.qualified.fromList.E | 11 +++++++++++ ghcide/test/exe/Main.hs | 17 +++++++++++++++++ 5 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 ghcide/test/data/hiding/HideFunction.hs.expected.fromList.B create mode 100644 ghcide/test/data/hiding/HideFunction.hs.expected.qualified.append.Prelude create mode 100644 ghcide/test/data/hiding/HideFunction.hs.expected.qualified.fromList.E diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index 854979e952..fbf9d43c97 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -362,17 +362,18 @@ deleteFromImport :: Located [LIE GhcPs] -> DynFlags -> TransformT (Either String) (LImportDecl GhcPs) -deleteFromImport symbol (L l idecls) llies@(L lieLoc lies) _ =do +deleteFromImport symbol (L l idecl) llies@(L lieLoc lies) _ =do let edited = L lieLoc deletedLies + lidecl' = L l $ idecl + { ideclHiding = Just (False, edited) + } when (not (null lies) && null deletedLies) $ do transferAnn llies edited id addSimpleAnnT edited dp00 [(G AnnOpenP, DP (0, 1)) ,(G AnnCloseP, DP (0,0)) ] - pure $ L l $ idecls - { ideclHiding = Just (False, L lieLoc deletedLies) - } + pure lidecl' where deletedLies = mapMaybe killLie lies @@ -392,6 +393,7 @@ deleteFromImport symbol (L l idecls) llies@(L lieLoc lies) _ =do (filter ((/= symbol) . unpackFS . flLabel . unLoc) flds) killLie v = Just v +-- This must not belong here? rawIEWrapName :: IEWrappedName RdrName -> String rawIEWrapName (IEName (L _ nam)) = occNameString $ rdrNameOcc nam rawIEWrapName (IEPattern (L _ nam)) = occNameString $ rdrNameOcc nam diff --git a/ghcide/test/data/hiding/HideFunction.hs.expected.fromList.B b/ghcide/test/data/hiding/HideFunction.hs.expected.fromList.B new file mode 100644 index 0000000000..e131d86c1c --- /dev/null +++ b/ghcide/test/data/hiding/HideFunction.hs.expected.fromList.B @@ -0,0 +1,11 @@ +module HideFunction where + +import AVec () +import BVec (fromList, (++)) +import CVec hiding (fromList, cons) +import DVec hiding (fromList, cons, snoc) +import EVec as E hiding (fromList) + +theFun = fromList + +theOp = (++) diff --git a/ghcide/test/data/hiding/HideFunction.hs.expected.qualified.append.Prelude b/ghcide/test/data/hiding/HideFunction.hs.expected.qualified.append.Prelude new file mode 100644 index 0000000000..505125f55a --- /dev/null +++ b/ghcide/test/data/hiding/HideFunction.hs.expected.qualified.append.Prelude @@ -0,0 +1,11 @@ +module HideFunction where + +import AVec (fromList) +import BVec (fromList, (++)) +import CVec hiding (cons) +import DVec hiding (cons, snoc) +import EVec as E + +theFun = fromList + +theOp = (Prelude.++) diff --git a/ghcide/test/data/hiding/HideFunction.hs.expected.qualified.fromList.E b/ghcide/test/data/hiding/HideFunction.hs.expected.qualified.fromList.E new file mode 100644 index 0000000000..e81909ce0f --- /dev/null +++ b/ghcide/test/data/hiding/HideFunction.hs.expected.qualified.fromList.E @@ -0,0 +1,11 @@ +module HideFunction where + +import AVec (fromList) +import BVec (fromList, (++)) +import CVec hiding (cons) +import DVec hiding (cons, snoc) +import EVec as E + +theFun = E.fromList + +theOp = (++) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index a9eac281d5..45fe975d1b 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -1479,6 +1479,11 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti compareHideFunctionTo "Use AVec for fromList, hiding other imports" "HideFunction.hs.expected.fromList.A" + , expectFailBecause "Known bug - Modifying topmost import decl adds additional newline at the top" + $ testCase "BVec" $ + compareHideFunctionTo + "Use BVec for fromList, hiding other imports" + "HideFunction.hs.expected.fromList.B" ] , testGroup "(++)" [testCase "EVec" $ @@ -1502,6 +1507,18 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti [ actionTitle | CACodeAction CodeAction { _title = actionTitle } <- actions ] + , testGroup "fromList" + [ testCase "EVec" $ + compareHideFunctionTo + "Replace with qualified: E.fromList" + "HideFunction.hs.expected.qualified.fromList.E" + ] + , testGroup "(++)" + [ testCase "Prelude" $ + compareHideFunctionTo + "Replace with qualified: Prelude.++" + "HideFunction.hs.expected.qualified.append.Prelude" + ] ] ] where From 3b6c048c393d5504f4c4898a01d4ab8e2b170668 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 01:27:35 +0900 Subject: [PATCH 18/43] Ah! CRLF have bitten me! --- ghcide/test/exe/Main.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 45fe975d1b..71f18b1cac 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -1530,7 +1530,7 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti action <- liftIO $ pickActionWithTitle cmd actions executeCodeAction action contentAfterAction <- documentContents doc - liftIO $ expected @=? contentAfterAction + liftIO $ T.replace "\r\n" "\n" expected @=? contentAfterAction withHideFunction k = runInDir hidingDir $ do doc <- openDoc ("HideFunction" <.> "hs") "haskell" void (skipManyTill anyMessage message From dff4d035b4c59351d427c0e8436a40d330c81139 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 17:31:57 +0900 Subject: [PATCH 19/43] Tentative workaround for #1274 --- ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs | 7 ++++++- ghcide/test/exe/Main.hs | 3 +-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index fbf9d43c97..d22c01893a 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -29,6 +29,7 @@ import Data.Maybe (fromJust, isNothing, mapMaybe) import qualified Data.Text as T import Development.IDE.GHC.Compat hiding (parseExpr) import Development.IDE.GHC.ExactPrint + ( Annotate, ASTElement(parseAST) ) import Development.IDE.Types.Location import FieldLabel (flLabel) import GhcPlugins (realSrcSpanEnd, realSrcSpanStart, sigPrec) @@ -68,12 +69,16 @@ rewriteToEdit dflags uri anns (Rewrite dst f) = do [ ( uri, List [ TextEdit (fromJust $ srcSpanToRange dst) $ - T.pack $ tail $ exactPrint ast anns + stripPrecedingNewline $ T.pack $ tail $ exactPrint ast anns ] ) ] pure $ WorkspaceEdit (Just editMap) Nothing +stripPrecedingNewline + :: T.Text -> T.Text +stripPrecedingNewline = T.dropWhile (`elem` ("\r\n" :: [Char])) + srcSpanToRange :: SrcSpan -> Maybe Range srcSpanToRange (UnhelpfulSpan _) = Nothing srcSpanToRange (RealSrcSpan real) = Just $ realSrcSpanToRange real diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 71f18b1cac..c7dd2ba9da 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -1479,8 +1479,7 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti compareHideFunctionTo "Use AVec for fromList, hiding other imports" "HideFunction.hs.expected.fromList.A" - , expectFailBecause "Known bug - Modifying topmost import decl adds additional newline at the top" - $ testCase "BVec" $ + , testCase "BVec" $ compareHideFunctionTo "Use BVec for fromList, hiding other imports" "HideFunction.hs.expected.fromList.B" From 0696e5a8e5f0e506ff8eec6cbb7381bf425cbf80 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 17:53:41 +0900 Subject: [PATCH 20/43] Wait much to ensure rewrite suggestion to be collected --- ghcide/test/exe/Main.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index c7dd2ba9da..284a1eb0f5 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -1535,7 +1535,7 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification) void waitForDiagnostics - liftIO $ sleep 0.5 + liftIO $ sleep 1.0 contents <- documentContents doc let range = Range (Position 0 0) (Position (length $ T.lines contents) 0) actions <- getCodeActions doc range From c2907ddea4fc752b9a3bef589333b8478a6a1a2d Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 17:54:01 +0900 Subject: [PATCH 21/43] Tests for type suggestion --- ghcide/test/data/hiding/AVec.hs | 5 ++++- ghcide/test/data/hiding/BVec.hs | 5 ++++- ghcide/test/data/hiding/CVec.hs | 5 ++++- ghcide/test/data/hiding/DVec.hs | 5 ++++- ghcide/test/data/hiding/EVec.hs | 5 ++++- ghcide/test/data/hiding/HideType.hs | 9 ++++++++ .../test/data/hiding/HideType.hs.expected.A | 9 ++++++++ .../test/data/hiding/HideType.hs.expected.E | 9 ++++++++ ghcide/test/exe/Main.hs | 22 +++++++++++++++---- 9 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 ghcide/test/data/hiding/HideType.hs create mode 100644 ghcide/test/data/hiding/HideType.hs.expected.A create mode 100644 ghcide/test/data/hiding/HideType.hs.expected.E diff --git a/ghcide/test/data/hiding/AVec.hs b/ghcide/test/data/hiding/AVec.hs index 62dee4ca3a..4c1ea30874 100644 --- a/ghcide/test/data/hiding/AVec.hs +++ b/ghcide/test/data/hiding/AVec.hs @@ -1,4 +1,5 @@ -module AVec (Vec, (++), cons, fromList, snoc) where +{-# LANGUAGE TypeOperators #-} +module AVec (Vec, type (@@@), (++), cons, fromList, snoc) where import Prelude hiding ((++)) @@ -7,6 +8,8 @@ data Vec a (++) :: Vec a -> Vec a -> Vec a (++) = undefined +data (@@@) a b + fromList :: [a] -> Vec a fromList = undefined diff --git a/ghcide/test/data/hiding/BVec.hs b/ghcide/test/data/hiding/BVec.hs index 3f2a1ab259..e086bb6ff4 100644 --- a/ghcide/test/data/hiding/BVec.hs +++ b/ghcide/test/data/hiding/BVec.hs @@ -1,4 +1,5 @@ -module BVec (Vec, (++), cons, fromList, snoc) where +{-# LANGUAGE TypeOperators #-} +module BVec (Vec, type (@@@), (++), cons, fromList, snoc) where import Prelude hiding ((++)) @@ -7,6 +8,8 @@ data Vec a (++) :: Vec a -> Vec a -> Vec a (++) = undefined +data (@@@) a b + fromList :: [a] -> Vec a fromList = undefined diff --git a/ghcide/test/data/hiding/CVec.hs b/ghcide/test/data/hiding/CVec.hs index b943c786bd..4a5fd3e7e9 100644 --- a/ghcide/test/data/hiding/CVec.hs +++ b/ghcide/test/data/hiding/CVec.hs @@ -1,4 +1,5 @@ -module CVec (Vec, (++), cons, fromList, snoc) where +{-# LANGUAGE TypeOperators #-} +module CVec (Vec, type (@@@), (++), cons, fromList, snoc) where import Prelude hiding ((++)) @@ -7,6 +8,8 @@ data Vec a (++) :: Vec a -> Vec a -> Vec a (++) = undefined +data (@@@) a b + fromList :: [a] -> Vec a fromList = undefined diff --git a/ghcide/test/data/hiding/DVec.hs b/ghcide/test/data/hiding/DVec.hs index b293b18176..a580ca907d 100644 --- a/ghcide/test/data/hiding/DVec.hs +++ b/ghcide/test/data/hiding/DVec.hs @@ -1,4 +1,5 @@ -module DVec (Vec, (++), cons, fromList, snoc) where +{-# LANGUAGE TypeOperators #-} +module DVec (Vec, (++), type (@@@), cons, fromList, snoc) where import Prelude hiding ((++)) @@ -7,6 +8,8 @@ data Vec a (++) :: Vec a -> Vec a -> Vec a (++) = undefined +data (@@@) a b + fromList :: [a] -> Vec a fromList = undefined diff --git a/ghcide/test/data/hiding/EVec.hs b/ghcide/test/data/hiding/EVec.hs index 456305f915..f5e0b2c269 100644 --- a/ghcide/test/data/hiding/EVec.hs +++ b/ghcide/test/data/hiding/EVec.hs @@ -1,4 +1,5 @@ -module EVec (Vec, (++), cons, fromList, snoc) where +{-# LANGUAGE TypeOperators #-} +module EVec (Vec, (++), type (@@@), cons, fromList, snoc) where import Prelude hiding ((++)) @@ -7,6 +8,8 @@ data Vec a (++) :: Vec a -> Vec a -> Vec a (++) = undefined +data (@@@) a b + fromList :: [a] -> Vec a fromList = undefined diff --git a/ghcide/test/data/hiding/HideType.hs b/ghcide/test/data/hiding/HideType.hs new file mode 100644 index 0000000000..926cedf15d --- /dev/null +++ b/ghcide/test/data/hiding/HideType.hs @@ -0,0 +1,9 @@ +module HideType where + +import AVec (Vec, fromList) +import BVec (fromList, (++)) +import CVec hiding (cons) +import DVec hiding (cons, snoc) +import EVec as E + +type TheType = Vec diff --git a/ghcide/test/data/hiding/HideType.hs.expected.A b/ghcide/test/data/hiding/HideType.hs.expected.A new file mode 100644 index 0000000000..a59de871b4 --- /dev/null +++ b/ghcide/test/data/hiding/HideType.hs.expected.A @@ -0,0 +1,9 @@ +module HideType where + +import AVec (Vec, fromList) +import BVec (fromList, (++)) +import CVec hiding (Vec, cons) +import DVec hiding (Vec, cons, snoc) +import EVec as E hiding (Vec) + +type TheType = Vec diff --git a/ghcide/test/data/hiding/HideType.hs.expected.E b/ghcide/test/data/hiding/HideType.hs.expected.E new file mode 100644 index 0000000000..51fa6610b5 --- /dev/null +++ b/ghcide/test/data/hiding/HideType.hs.expected.E @@ -0,0 +1,9 @@ +module HideType where + +import AVec ( fromList) +import BVec (fromList, (++)) +import CVec hiding (Vec, cons) +import DVec hiding (Vec, cons, snoc) +import EVec as E + +type TheType = Vec diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 284a1eb0f5..6ad2ee5735 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -1490,6 +1490,18 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti "Use EVec for ++, hiding other imports" "HideFunction.hs.expected.append.E" ] + , testGroup "Vec (type)" + [ testCase "AVec" $ + compareTwo + "HideType.hs" + "Use AVec for Vec, hiding other imports" + "HideType.hs.expected.A" + , testCase "EVec" $ + compareTwo + "HideType.hs" + "Use EVec for Vec, hiding other imports" + "HideType.hs.expected.E" + ] ] , testGroup "Qualify strategy" [ testCase "won't suggest full name for qualified module" $ @@ -1522,16 +1534,17 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti ] where hidingDir = "test/data/hiding" - compareHideFunctionTo cmd expected = - withHideFunction $ \doc actions -> do + compareTwo original cmd expected = + withTarget original $ \doc actions -> do expected <- liftIO $ readFileUtf8 (hidingDir expected) action <- liftIO $ pickActionWithTitle cmd actions executeCodeAction action contentAfterAction <- documentContents doc liftIO $ T.replace "\r\n" "\n" expected @=? contentAfterAction - withHideFunction k = runInDir hidingDir $ do - doc <- openDoc ("HideFunction" <.> "hs") "haskell" + compareHideFunctionTo = compareTwo "HideFunction.hs" + withTarget file k = runInDir hidingDir $ do + doc <- openDoc file "haskell" void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification) void waitForDiagnostics @@ -1540,6 +1553,7 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti let range = Range (Position 0 0) (Position (length $ T.lines contents) 0) actions <- getCodeActions doc range k doc actions + withHideFunction = withTarget ("HideFunction" <.> "hs") disableWarningTests :: TestTree disableWarningTests = From 41d6c75bfd3e1e87d339fb3f8820ac9a1515defc Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 18:56:18 +0900 Subject: [PATCH 22/43] Slightly more wait --- ghcide/test/exe/Main.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 6ad2ee5735..3022c6506e 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -1548,7 +1548,7 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification) void waitForDiagnostics - liftIO $ sleep 1.0 + liftIO $ sleep 1.5 contents <- documentContents doc let range = Range (Position 0 0) (Position (length $ T.lines contents) 0) actions <- getCodeActions doc range From 223808be1f0d015d346a5a5fc38b3dbcb2ea636e Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 20:20:39 +0900 Subject: [PATCH 23/43] A little smarter waiting strartegy for actions --- ghcide/test/exe/Main.hs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 3022c6506e..21adda5296 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -76,6 +76,7 @@ import Control.Monad.Extra (whenJust) import qualified Language.Haskell.LSP.Types.Lens as L import Control.Lens ((^.)) import Data.Functor +import Numeric.Natural (Natural) main :: IO () main = do @@ -1548,13 +1549,29 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification) void waitForDiagnostics - liftIO $ sleep 1.5 contents <- documentContents doc let range = Range (Position 0 0) (Position (length $ T.lines contents) 0) - actions <- getCodeActions doc range + actions <- waitForAtLeatOneAction 0.5 4 doc range k doc actions withHideFunction = withTarget ("HideFunction" <.> "hs") +waitForAtLeatOneAction :: + -- | Waiting interval + Double -> + -- | Maximum # of retry (0 for no retry at ll) + Natural -> + TextDocumentIdentifier -> + Range -> + Session [CAResult] +waitForAtLeatOneAction wait count doc range = go count [] + where + go !remain !acc = do + liftIO $ sleep wait + actions <- getCodeActions doc range + if not (null actions) || remain <= 0 + then pure $ acc ++ actions + else go (remain - 1) (acc ++ actions) + disableWarningTests :: TestTree disableWarningTests = testGroup "disable warnings" $ From 0721ef9be0cbb72f407bece59019b6ec2dd3ffe5 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 21:57:59 +0900 Subject: [PATCH 24/43] Import list slim up --- .../src/Development/IDE/Plugin/CodeAction.hs | 71 +++++++++---------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 3f2add02e2..f189d970fe 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -13,34 +13,14 @@ module Development.IDE.Plugin.CodeAction , matchRegExMultipleImports ) where -import Bag (isEmptyBag) -import Control.Applicative ((<|>)) -import Control.Arrow (second, (>>>)) -import Control.Concurrent.Extra (readVar) -import Control.Lens (foldMapBy, (^.)) -import Control.Monad (guard, join) -import Data.Char -import Data.Coerce (coerce) -import qualified Data.DList as DL -import Data.Function -import Data.Functor -import qualified Data.HashMap.Strict as Map -import qualified Data.HashSet as Set -import Data.Hashable (Hashable) -import Data.List.Extra -import Data.List.NonEmpty (NonEmpty ((:|))) -import qualified Data.List.NonEmpty as NE -import Data.Maybe -import qualified Data.Rope.UTF16 as Rope -import qualified Data.Text as T -import Development.IDE.Core.RuleTypes +import Control.Monad (join, guard) +import Development.IDE.GHC.Compat import Development.IDE.Core.Rules +import Development.IDE.Core.RuleTypes import Development.IDE.Core.Service import Development.IDE.Core.Shake -import Development.IDE.GHC.Compat import Development.IDE.GHC.Error import Development.IDE.GHC.ExactPrint -import Development.IDE.GHC.Util (printRdrName) import Development.IDE.Plugin.CodeAction.ExactPrint import Development.IDE.Plugin.CodeAction.PositionIndexed import Development.IDE.Plugin.CodeAction.RuleTypes @@ -49,21 +29,38 @@ import Development.IDE.Plugin.TypeLenses (suggestSignature) import Development.IDE.Types.Exports import Development.IDE.Types.Location import Development.IDE.Types.Options -import qualified GHC.LanguageExtensions as Lang -import Ide.PluginUtils (subRange) -import Ide.Types +import qualified Data.HashMap.Strict as Map import qualified Language.Haskell.LSP.Core as LSP -import Language.Haskell.LSP.Types -import Language.Haskell.LSP.Types.Lens (character, end, line, start) import Language.Haskell.LSP.VFS -import OccName (parenSymOcc) +import Language.Haskell.LSP.Types +import qualified Data.Rope.UTF16 as Rope +import Data.Char +import Data.Maybe +import Data.List.Extra +import Data.List.NonEmpty (NonEmpty((:|))) +import qualified Data.List.NonEmpty as NE +import qualified Data.Text as T +import Text.Regex.TDFA (mrAfter, (=~), (=~~)) import Outputable (Outputable, ppr, showSDoc, showSDocUnsafe) -import Retrie.GHC (mkVarOcc) +import Data.Function +import Data.Functor +import Control.Applicative ((<|>)) import Safe (atMay) -import Text.Regex.TDFA (mrAfter, (=~), (=~~)) +import Bag (isEmptyBag) +import qualified Data.HashSet as Set +import Control.Concurrent.Extra (readVar) +import Development.IDE.GHC.Util (printRdrName) +import Ide.PluginUtils (subRange) +import Ide.Types +import Data.Hashable (Hashable) +import qualified Data.DList as DL +import Development.IDE.Spans.Common +import OccName +import Data.Coerce import Data.Either (fromRight) -import Retrie (unpackFS) -import FieldLabel (flLabel) +import Control.Arrow +import qualified GHC.LanguageExtensions as Lang +import Control.Lens (foldMapBy) descriptor :: PluginId -> PluginDescriptor IdeState descriptor plId = @@ -852,8 +849,8 @@ symbolOccursIn symb = \case IEThingAll _ (L _ n) -> rawIEWrapName n == T.unpack symb IEThingWith _ (L _ n) _ ents flds -> rawIEWrapName n == T.unpack symb - || any ((== T.unpack symb) . rawIEWrapName . unLoc) ents - || any ((== T.unpack symb) . unpackFS . flLabel . unLoc) flds + || any ((== symb) . showNameWithoutUniques . unLoc) ents + || any ((== symb) . showNameWithoutUniques . unLoc) flds IEModuleContents{} -> False IEGroup{} -> False IEDoc{} -> False @@ -899,8 +896,8 @@ disambiguateSymbol df pm Diagnostic {..} (T.unpack -> symbol) = \case hidePreludeSymbol :: DynFlags -> String -> ParsedSource -> TextEdit hidePreludeSymbol df symbol (L _ HsModule{..}) = let ran = fromJust $ srcSpanToRange $ getLoc $ last hsmodImports - col = ran ^. start.character - beg = Position (1 + (ran ^. end.line)) 0 + col = _character $ _start ran + beg = Position (1 + _line (_end ran)) 0 symOcc = mkVarOcc symbol symImp = T.pack $ showSDoc df $ parenSymOcc symOcc $ ppr symOcc in TextEdit From 748744658564d7f2afdff9889dade14a9b95ecc6 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 21:59:05 +0900 Subject: [PATCH 25/43] Adjusted to the master --- ghcide/src/Development/IDE/Plugin/CodeAction.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index f189d970fe..12475a1535 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -43,6 +43,7 @@ import qualified Data.Text as T import Text.Regex.TDFA (mrAfter, (=~), (=~~)) import Outputable (Outputable, ppr, showSDoc, showSDocUnsafe) import Data.Function +import Control.Arrow ((>>>), second) import Data.Functor import Control.Applicative ((<|>)) import Safe (atMay) @@ -58,7 +59,6 @@ import Development.IDE.Spans.Common import OccName import Data.Coerce import Data.Either (fromRight) -import Control.Arrow import qualified GHC.LanguageExtensions as Lang import Control.Lens (foldMapBy) From b5b7f03f784922a1aa6af0c5d0712db4fd6ee105 Mon Sep 17 00:00:00 2001 From: Hiromi Ishii Date: Sat, 30 Jan 2021 22:08:56 +0900 Subject: [PATCH 26/43] Update ghcide/src/Development/IDE/Plugin/CodeAction.hs Co-authored-by: Pepe Iborra --- ghcide/src/Development/IDE/Plugin/CodeAction.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 12475a1535..45dc2a7d65 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -136,10 +136,11 @@ codeAction lsp state _ (TextDocumentIdentifier uri) _range CodeActionContext{_di -- | Semigroup instance just overrides duplicated keys in the first argument unionWSEdit :: WorkspaceEdit -> WorkspaceEdit -> WorkspaceEdit unionWSEdit (WorkspaceEdit a b) (WorkspaceEdit c d) = - -- FIXME: Want to use monoidal-containers, but it supports aeson <1.5 only... WorkspaceEdit (runCatHashMap <$> fmap CatHashMap a <> fmap CatHashMap c) (b <> d) +-- | A monoidal hashmap + -- FIXME: Want to use monoidal-containers, but it supports aeson <1.5 only... newtype CatHashMap k v = CatHashMap { runCatHashMap :: Map.HashMap k v } instance (Eq k, Hashable k, Semigroup v) => Semigroup (CatHashMap k v) where (<>) = coerce $ Map.unionWith @k @v (<>) From d4075dc9a529e7ae7d74e847d4dc283499cccae1 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 22:37:39 +0900 Subject: [PATCH 27/43] Rewrote using `expectDiagnostics` --- ghcide/test/exe/Main.hs | 47 ++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 55c61b56f4..1b025316cb 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -78,7 +78,6 @@ import Control.Monad.Extra (whenJust) import qualified Language.Haskell.LSP.Types.Lens as L import Control.Lens ((^.)) import Data.Functor -import Numeric.Natural (Natural) import Data.Tuple.Extra main :: IO () @@ -1481,36 +1480,36 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti [ testGroup "Hiding strategy works" [ testGroup "fromList" [ testCase "AVec" $ - compareHideFunctionTo + compareHideFunctionTo [(8,9),(10,8)] "Use AVec for fromList, hiding other imports" "HideFunction.hs.expected.fromList.A" , testCase "BVec" $ - compareHideFunctionTo + compareHideFunctionTo [(8,9),(10,8)] "Use BVec for fromList, hiding other imports" "HideFunction.hs.expected.fromList.B" ] , testGroup "(++)" [testCase "EVec" $ - compareHideFunctionTo + compareHideFunctionTo [(8,9),(10,8)] "Use EVec for ++, hiding other imports" "HideFunction.hs.expected.append.E" ] , testGroup "Vec (type)" [ testCase "AVec" $ compareTwo - "HideType.hs" + "HideType.hs" [(8,15)] "Use AVec for Vec, hiding other imports" "HideType.hs.expected.A" , testCase "EVec" $ compareTwo - "HideType.hs" + "HideType.hs" [(8,15)] "Use EVec for Vec, hiding other imports" "HideType.hs.expected.E" ] ] , testGroup "Qualify strategy" [ testCase "won't suggest full name for qualified module" $ - withHideFunction $ \_ actions -> do + withHideFunction [(8,9),(10,8)] $ \_ actions -> do liftIO $ assertBool "EVec.fromList must not be suggested" $ "Replace with qualified: EVec.fromList" `notElem` @@ -1525,13 +1524,13 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti ] , testGroup "fromList" [ testCase "EVec" $ - compareHideFunctionTo + compareHideFunctionTo [(8,9),(10,8)] "Replace with qualified: E.fromList" "HideFunction.hs.expected.qualified.fromList.E" ] , testGroup "(++)" [ testCase "Prelude" $ - compareHideFunctionTo + compareHideFunctionTo [(8,9),(10,8)] "Replace with qualified: Prelude.++" "HideFunction.hs.expected.qualified.append.Prelude" ] @@ -1539,8 +1538,8 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti ] where hidingDir = "test/data/hiding" - compareTwo original cmd expected = - withTarget original $ \doc actions -> do + compareTwo original locs cmd expected = + withTarget original locs $ \doc actions -> do expected <- liftIO $ readFileUtf8 (hidingDir expected) action <- liftIO $ pickActionWithTitle cmd actions @@ -1548,34 +1547,20 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti contentAfterAction <- documentContents doc liftIO $ T.replace "\r\n" "\n" expected @=? contentAfterAction compareHideFunctionTo = compareTwo "HideFunction.hs" - withTarget file k = runInDir hidingDir $ do + auxFiles = ["AVec.hs", "BVec.hs", "CVec.hs", "DVec.hs", "EVec.hs"] + withTarget file locs k = withTempDir $ \dir -> runInDir dir $ do + liftIO $ mapM_ (\fp -> copyFile (hidingDir fp) $ dir fp) + $ file : auxFiles doc <- openDoc file "haskell" void (skipManyTill anyMessage message :: Session WorkDoneProgressEndNotification) - void waitForDiagnostics + void $ expectDiagnostics [(file, [(DsError, loc, "Ambiguous occurrence") | loc <- locs])] contents <- documentContents doc let range = Range (Position 0 0) (Position (length $ T.lines contents) 0) - actions <- waitForAtLeatOneAction 0.5 4 doc range + actions <- getCodeActions doc range k doc actions withHideFunction = withTarget ("HideFunction" <.> "hs") -waitForAtLeatOneAction :: - -- | Waiting interval - Double -> - -- | Maximum # of retry (0 for no retry at ll) - Natural -> - TextDocumentIdentifier -> - Range -> - Session [CAResult] -waitForAtLeatOneAction wait count doc range = go count [] - where - go !remain !acc = do - liftIO $ sleep wait - actions <- getCodeActions doc range - if not (null actions) || remain <= 0 - then pure $ acc ++ actions - else go (remain - 1) (acc ++ actions) - disableWarningTests :: TestTree disableWarningTests = testGroup "disable warnings" $ From 130afec61e71b94bc25c48e150aed3cbba4c024c Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 22:39:43 +0900 Subject: [PATCH 28/43] Case for Prelude.++ --- .../hiding/HideFunction.hs.expected.append.Prelude | 11 +++++++++++ ghcide/test/exe/Main.hs | 6 +++++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 ghcide/test/data/hiding/HideFunction.hs.expected.append.Prelude diff --git a/ghcide/test/data/hiding/HideFunction.hs.expected.append.Prelude b/ghcide/test/data/hiding/HideFunction.hs.expected.append.Prelude new file mode 100644 index 0000000000..0b202451f0 --- /dev/null +++ b/ghcide/test/data/hiding/HideFunction.hs.expected.append.Prelude @@ -0,0 +1,11 @@ +module HideFunction where + +import AVec (fromList) +import BVec (fromList,) +import CVec hiding ((++), cons) +import DVec hiding ((++), cons, snoc) +import EVec as E hiding ((++)) + +theFun = fromList + +theOp = (++) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 1b025316cb..f43020bd82 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -1489,10 +1489,14 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti "HideFunction.hs.expected.fromList.B" ] , testGroup "(++)" - [testCase "EVec" $ + [ testCase "EVec" $ compareHideFunctionTo [(8,9),(10,8)] "Use EVec for ++, hiding other imports" "HideFunction.hs.expected.append.E" + , testCase "Prelude" $ + compareHideFunctionTo [(8,9),(10,8)] + "Use EVec for ++, hiding other imports" + "HideFunction.hs.expected.append.Prelude" ] , testGroup "Vec (type)" [ testCase "AVec" $ From a3486ac6b069cf8a03f11197ed35ff14a318dc2f Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 22:49:26 +0900 Subject: [PATCH 29/43] Corrects test name --- ghcide/test/exe/Main.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index f43020bd82..44bb9b6dd7 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -1495,7 +1495,7 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti "HideFunction.hs.expected.append.E" , testCase "Prelude" $ compareHideFunctionTo [(8,9),(10,8)] - "Use EVec for ++, hiding other imports" + "Use Prelude for ++, hiding other imports" "HideFunction.hs.expected.append.Prelude" ] , testGroup "Vec (type)" From c252aca111a34cb33849b480b659c44744e26012 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sat, 30 Jan 2021 22:50:00 +0900 Subject: [PATCH 30/43] Renames `rawIEWrapName` to `unqualIEWrapName`, and moved to the appropriate module --- .../src/Development/IDE/Plugin/CodeAction.hs | 14 +++++++------ .../IDE/Plugin/CodeAction/ExactPrint.hs | 20 +++++++------------ ghcide/src/Development/IDE/Spans/Common.hs | 8 ++++++++ 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 45dc2a7d65..436ed5457e 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -61,6 +61,8 @@ import Data.Coerce import Data.Either (fromRight) import qualified GHC.LanguageExtensions as Lang import Control.Lens (foldMapBy) +import FieldLabel (flLabel) +import FastString (unpackFS) descriptor :: PluginId -> PluginDescriptor IdeState descriptor plId = @@ -845,13 +847,13 @@ occursUnqualified _ _ = False symbolOccursIn :: T.Text -> IE GhcPs -> Bool symbolOccursIn symb = \case - IEVar _ (L _ n) -> rawIEWrapName n == T.unpack symb - IEThingAbs _ (L _ n) -> rawIEWrapName n == T.unpack symb - IEThingAll _ (L _ n) -> rawIEWrapName n == T.unpack symb + IEVar _ (L _ n) -> unqualIEWrapName n == symb + IEThingAbs _ (L _ n) -> unqualIEWrapName n == symb + IEThingAll _ (L _ n) -> unqualIEWrapName n == symb IEThingWith _ (L _ n) _ ents flds -> - rawIEWrapName n == T.unpack symb - || any ((== symb) . showNameWithoutUniques . unLoc) ents - || any ((== symb) . showNameWithoutUniques . unLoc) flds + unqualIEWrapName n == symb + || any ((== symb) . unqualIEWrapName . unLoc) ents + || any ((== symb) . T.pack . unpackFS . flLabel . unLoc) flds IEModuleContents{} -> False IEGroup{} -> False IEDoc{} -> False diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index d22c01893a..81ccd5085f 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -7,7 +7,6 @@ module Development.IDE.Plugin.CodeAction.ExactPrint ( Rewrite (..), rewriteToEdit, transferAnn, - rawIEWrapName, -- * Utilities appendConstraint, @@ -39,6 +38,7 @@ import Language.Haskell.LSP.Types import OccName import Outputable (ppr, showSDocUnsafe) import Retrie.GHC (rdrNameOcc, unpackFS) +import Development.IDE.Spans.Common ------------------------------------------------------------------------------ @@ -367,7 +367,7 @@ deleteFromImport :: Located [LIE GhcPs] -> DynFlags -> TransformT (Either String) (LImportDecl GhcPs) -deleteFromImport symbol (L l idecl) llies@(L lieLoc lies) _ =do +deleteFromImport (T.pack -> symbol) (L l idecl) llies@(L lieLoc lies) _ =do let edited = L lieLoc deletedLies lidecl' = L l $ idecl { ideclHiding = Just (False, edited) @@ -383,23 +383,17 @@ deleteFromImport symbol (L l idecl) llies@(L lieLoc lies) _ =do deletedLies = mapMaybe killLie lies killLie :: LIE GhcPs -> Maybe (LIE GhcPs) - killLie v@(L _ (IEVar _ (L _ (rawIEWrapName -> nam)))) + killLie v@(L _ (IEVar _ (L _ (unqualIEWrapName -> nam)))) | nam == symbol = Nothing | otherwise = Just v - killLie v@(L _ (IEThingAbs _ (L _ (rawIEWrapName -> nam)))) + killLie v@(L _ (IEThingAbs _ (L _ (unqualIEWrapName -> nam)))) | nam == symbol = Nothing | otherwise = Just v - killLie (L lieL (IEThingWith xt ty@(L _ (rawIEWrapName -> nam)) wild cons flds)) + killLie (L lieL (IEThingWith xt ty@(L _ (unqualIEWrapName -> nam)) wild cons flds)) | nam == symbol = Nothing | otherwise = Just $ L lieL $ IEThingWith xt ty wild - (filter ((/= symbol) . rawIEWrapName . unLoc) cons) - (filter ((/= symbol) . unpackFS . flLabel . unLoc) flds) + (filter ((/= symbol) . unqualIEWrapName . unLoc) cons) + (filter ((/= symbol) . T.pack . unpackFS . flLabel . unLoc) flds) killLie v = Just v - --- This must not belong here? -rawIEWrapName :: IEWrappedName RdrName -> String -rawIEWrapName (IEName (L _ nam)) = occNameString $ rdrNameOcc nam -rawIEWrapName (IEPattern (L _ nam)) = occNameString $ rdrNameOcc nam -rawIEWrapName (IEType (L _ nam)) = occNameString $ rdrNameOcc nam diff --git a/ghcide/src/Development/IDE/Spans/Common.hs b/ghcide/src/Development/IDE/Spans/Common.hs index e7ad090e5e..b690f84ed7 100644 --- a/ghcide/src/Development/IDE/Spans/Common.hs +++ b/ghcide/src/Development/IDE/Spans/Common.hs @@ -6,6 +6,7 @@ module Development.IDE.Spans.Common ( showGhc , showNameWithoutUniques +, unqualIEWrapName , safeTyThingId , safeTyThingType , SpanDoc(..) @@ -35,6 +36,7 @@ import qualified Documentation.Haddock.Parser as H import qualified Documentation.Haddock.Types as H import Development.IDE.GHC.Orphans () import Development.IDE.GHC.Util +import RdrName (rdrNameOcc) type DocMap = NameEnv SpanDoc type KindMap = NameEnv TyThing @@ -52,6 +54,12 @@ showNameWithoutUniques = T.pack . prettyprint prettyprint x = renderWithStyle dyn (ppr x) style style = mkUserStyle dyn neverQualify AllTheWay +-- | Shows IEWrappedName, without any modifier, qualifier or unique identifier. +unqualIEWrapName :: IEWrappedName RdrName -> T.Text +unqualIEWrapName (IEName (L _ nam)) = showNameWithoutUniques $ rdrNameOcc nam +unqualIEWrapName (IEPattern (L _ nam)) = showNameWithoutUniques $ rdrNameOcc nam +unqualIEWrapName (IEType (L _ nam)) = showNameWithoutUniques $ rdrNameOcc nam + -- From haskell-ide-engine/src/Haskell/Ide/Engine/Support/HieExtras.hs safeTyThingType :: TyThing -> Maybe Type safeTyThingType thing From f6594fdf95be64df62561e19192dc7d080b22009 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sun, 31 Jan 2021 00:26:54 +0900 Subject: [PATCH 31/43] Rewrote qualifying strategy with `Rewrite` --- ghcide/src/Development/IDE/Plugin/CodeAction.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 436ed5457e..a26fb51c4f 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -889,10 +889,10 @@ disambiguateSymbol df pm Diagnostic {..} (T.unpack -> symbol) = \case (ToQualified qualMod) -> let occSym = mkVarOcc symbol rdr = Qual qualMod occSym - in [Left $ TextEdit _range - $ T.pack $ showSDoc df - $ parenSymOcc occSym - $ ppr rdr] + in [Right $ Rewrite (rangeToSrcSpan nfp _range) $ \df -> do + liftParseAST @(HsExpr GhcPs) df $ prettyPrint $ HsVar @GhcPs noExtField + $ L (UnhelpfulSpan "") rdr + ] -- Couldn't make out how to add New imports by ghc-exactprint; -- using direct TextEdit instead. From e6946941511e5c19f4bfb02f21ceb58bb23a2e90 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sun, 31 Jan 2021 02:37:16 +0900 Subject: [PATCH 32/43] Use exactprint also for hideImplicitPreludeSymbol --- ghcide/src/Development/IDE/GHC/Error.hs | 18 +++++++ .../src/Development/IDE/Plugin/CodeAction.hs | 29 ++++------ .../IDE/Plugin/CodeAction/ExactPrint.hs | 54 +++++++++++-------- .../test/data/hiding/HidePreludeIndented.hs | 4 ++ .../hiding/HidePreludeIndented.hs.expected | 5 ++ ghcide/test/exe/Main.hs | 5 ++ 6 files changed, 72 insertions(+), 43 deletions(-) create mode 100644 ghcide/test/data/hiding/HidePreludeIndented.hs create mode 100644 ghcide/test/data/hiding/HidePreludeIndented.hs.expected diff --git a/ghcide/src/Development/IDE/GHC/Error.hs b/ghcide/src/Development/IDE/GHC/Error.hs index bce5cc733f..9e06ea9a5c 100644 --- a/ghcide/src/Development/IDE/GHC/Error.hs +++ b/ghcide/src/Development/IDE/GHC/Error.hs @@ -17,6 +17,9 @@ module Development.IDE.GHC.Error , realSrcLocToPosition , realSrcSpanToLocation , srcSpanToFilename + , rangeToSrcSpan + , rangeToRealSrcSpan + , positionToRealSrcLoc , zeroSpan , realSpan , isInsideSrcSpan @@ -39,6 +42,7 @@ import Panic import ErrUtils import SrcLoc import qualified Outputable as Out +import Data.String (fromString) @@ -102,6 +106,20 @@ srcSpanToLocation src = do -- important that the URI's we produce have been properly normalized, otherwise they point at weird places in VS Code pure $ Location (fromNormalizedUri $ filePathToUri' $ toNormalizedFilePath' fs) rng +rangeToSrcSpan :: NormalizedFilePath -> Range -> SrcSpan +rangeToSrcSpan = fmap RealSrcSpan . rangeToRealSrcSpan + +rangeToRealSrcSpan + :: NormalizedFilePath -> Range -> RealSrcSpan +rangeToRealSrcSpan nfp = + mkRealSrcSpan + <$> positionToRealSrcLoc nfp . _start + <*> positionToRealSrcLoc nfp . _end + +positionToRealSrcLoc :: NormalizedFilePath -> Position -> RealSrcLoc +positionToRealSrcLoc nfp (Position l c)= + mkRealSrcLoc (fromString $ fromNormalizedFilePath nfp) (l + 1) (c + 1) + isInsideSrcSpan :: Position -> SrcSpan -> Bool p `isInsideSrcSpan` r = case srcSpanToRange r of Just (Range sp ep) -> sp <= p && p <= ep diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index a26fb51c4f..1bff2409bb 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -50,7 +50,7 @@ import Safe (atMay) import Bag (isEmptyBag) import qualified Data.HashSet as Set import Control.Concurrent.Extra (readVar) -import Development.IDE.GHC.Util (printRdrName) +import Development.IDE.GHC.Util (printRdrName, prettyPrint) import Ide.PluginUtils (subRange) import Ide.Types import Data.Hashable (Hashable) @@ -114,8 +114,9 @@ codeAction lsp state _ (TextDocumentIdentifier uri) _range CodeActionContext{_di | x <- xs , ps <- maybeToList annotatedPS , dynflags <- maybeToList df + , nfp <- maybeToList mbFile , (title, edRewrs) <- - suggestImportDisambiguation dynflags (astA ps) x + suggestImportDisambiguation nfp dynflags (astA ps) x , let edit = foldMapBy unionWSEdit mempty (either @@ -770,11 +771,12 @@ isPreludeImplicit = xopt Lang.ImplicitPrelude -- | Suggests disambiguation for ambiguous symbols. suggestImportDisambiguation :: + NormalizedFilePath -> DynFlags -> ParsedSource -> Diagnostic -> [(T.Text, [Either TextEdit Rewrite])] -suggestImportDisambiguation df ps@(L _ HsModule {hsmodImports}) diag@Diagnostic {..} +suggestImportDisambiguation nfp df ps@(L _ HsModule {hsmodImports}) diag@Diagnostic {..} | Just [ambiguous] <- matchRegexUnifySpaces _message @@ -805,7 +807,7 @@ suggestImportDisambiguation df ps@(L _ HsModule {hsmodImports}) diag@Diagnostic | Just targets <- mapM toModuleTarget mods = sortOn fst [ ( renderUniquify mode modNameText symbol - , disambiguateSymbol df ps diag symbol mode + , disambiguateSymbol nfp ps diag symbol mode ) | (modTarget, restImports) <- oneAndOthers targets , let modName = targetModuleName modTarget @@ -868,13 +870,13 @@ targetModuleName (ExistingImp _) = error "Cannot happen!" disambiguateSymbol :: - DynFlags -> + NormalizedFilePath -> ParsedSource -> Diagnostic -> T.Text -> HidingMode -> [Either TextEdit Rewrite] -disambiguateSymbol df pm Diagnostic {..} (T.unpack -> symbol) = \case +disambiguateSymbol nfp pm Diagnostic {..} (T.unpack -> symbol) = \case (HideOthers hiddens0) -> [ Right $ hideSymbol symbol idecl | ExistingImp idecls <- hiddens0 @@ -882,7 +884,7 @@ disambiguateSymbol df pm Diagnostic {..} (T.unpack -> symbol) = \case ] ++ mconcat [ if null imps - then [Left $ hidePreludeSymbol df symbol pm] + then [Right $ hideImplicitPreludeSymbol symbol pm] else Right . hideSymbol symbol <$> imps | ImplicitPrelude imps <- hiddens0 ] @@ -894,19 +896,6 @@ disambiguateSymbol df pm Diagnostic {..} (T.unpack -> symbol) = \case $ L (UnhelpfulSpan "") rdr ] --- Couldn't make out how to add New imports by ghc-exactprint; --- using direct TextEdit instead. -hidePreludeSymbol :: DynFlags -> String -> ParsedSource -> TextEdit -hidePreludeSymbol df symbol (L _ HsModule{..}) = - let ran = fromJust $ srcSpanToRange $ getLoc $ last hsmodImports - col = _character $ _start ran - beg = Position (1 + _line (_end ran)) 0 - symOcc = mkVarOcc symbol - symImp = T.pack $ showSDoc df $ parenSymOcc symOcc $ ppr symOcc - in TextEdit - (Range beg beg) - $ T.replicate col " " <> "import Prelude hiding (" <> symImp <> ")\n" - findImportDeclByRange :: [LImportDecl GhcPs] -> Range -> Maybe (LImportDecl GhcPs) findImportDeclByRange xs range = find (\(L l _)-> srcSpanToRange l == Just range) xs diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index 81ccd5085f..dae612c7ae 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -11,6 +11,7 @@ module Development.IDE.Plugin.CodeAction.ExactPrint -- * Utilities appendConstraint, extendImport, + hideImplicitPreludeSymbol, hideSymbol, liftParseAST, ) @@ -31,14 +32,15 @@ import Development.IDE.GHC.ExactPrint ( Annotate, ASTElement(parseAST) ) import Development.IDE.Types.Location import FieldLabel (flLabel) -import GhcPlugins (realSrcSpanEnd, realSrcSpanStart, sigPrec) +import GhcPlugins (sigPrec) import Language.Haskell.GHC.ExactPrint import Language.Haskell.GHC.ExactPrint.Types (DeltaPos (DP), KeywordId (G), mkAnnKey) import Language.Haskell.LSP.Types import OccName -import Outputable (ppr, showSDocUnsafe) -import Retrie.GHC (rdrNameOcc, unpackFS) +import Outputable (ppr, showSDocUnsafe, showSDoc) +import Retrie.GHC (rdrNameOcc, unpackFS, mkRealSrcSpan, realSrcSpanEnd) import Development.IDE.Spans.Common +import Development.IDE.GHC.Error ------------------------------------------------------------------------------ @@ -63,36 +65,20 @@ rewriteToEdit :: Rewrite -> Either String WorkspaceEdit rewriteToEdit dflags uri anns (Rewrite dst f) = do - (ast, (anns, _), _) <- runTransformT anns $ f dflags + (ast, (anns, _), _) <- runTransformT anns $ do + ast <- f dflags + ast <$ setEntryDPT ast (DP (0,0)) let editMap = HMap.fromList [ ( uri, List [ TextEdit (fromJust $ srcSpanToRange dst) $ - stripPrecedingNewline $ T.pack $ tail $ exactPrint ast anns + T.pack $ exactPrint ast anns ] ) ] pure $ WorkspaceEdit (Just editMap) Nothing -stripPrecedingNewline - :: T.Text -> T.Text -stripPrecedingNewline = T.dropWhile (`elem` ("\r\n" :: [Char])) - -srcSpanToRange :: SrcSpan -> Maybe Range -srcSpanToRange (UnhelpfulSpan _) = Nothing -srcSpanToRange (RealSrcSpan real) = Just $ realSrcSpanToRange real - -realSrcSpanToRange :: RealSrcSpan -> Range -realSrcSpanToRange real = - Range - (realSrcLocToPosition $ realSrcSpanStart real) - (realSrcLocToPosition $ realSrcSpanEnd real) - -realSrcLocToPosition :: RealSrcLoc -> Position -realSrcLocToPosition real = - Position (srcLocLine real - 1) (srcLocCol real - 1) - ------------------------------------------------------------------------------ -- | Fix the parentheses around a type context @@ -397,3 +383,25 @@ deleteFromImport (T.pack -> symbol) (L l idecl) llies@(L lieLoc lies) _ =do (filter ((/= symbol) . unqualIEWrapName . unLoc) cons) (filter ((/= symbol) . T.pack . unpackFS . flLabel . unLoc) flds) killLie v = Just v + +hideImplicitPreludeSymbol + :: String -> ParsedSource -> Rewrite +hideImplicitPreludeSymbol symbol (L _ HsModule{..}) = + let -- We assume there is at least one import, to collide with Prelude; + -- otherwise (custom) Prelude must be buggy, (or preprocessor adds some collision?) + existingImp = last hsmodImports + exisImpSpan = fromJust $ realSpan $ getLoc existingImp + indentation = srcSpanStartCol exisImpSpan + beg = realSrcSpanEnd exisImpSpan + ran = RealSrcSpan $ mkRealSrcSpan beg beg + in Rewrite ran $ \df -> do + -- Re-labeling is needed to reflect annotations correctly + let symOcc = mkVarOcc symbol + symImp = T.pack $ showSDoc df $ parenSymOcc symOcc $ ppr symOcc + impStmt = "import Prelude hiding (" <> symImp <> ")" + + L _ idecl0 <- liftParseAST @(ImportDecl GhcPs) df $ T.unpack impStmt + let idecl = L ran idecl0 + addSimpleAnnT idecl (DP (1,indentation - 1)) + [(G AnnImport, DP (1, indentation - 1))] + pure idecl diff --git a/ghcide/test/data/hiding/HidePreludeIndented.hs b/ghcide/test/data/hiding/HidePreludeIndented.hs new file mode 100644 index 0000000000..122b64a390 --- /dev/null +++ b/ghcide/test/data/hiding/HidePreludeIndented.hs @@ -0,0 +1,4 @@ +module HidePreludeIndented where + + import AVec + op = (++) diff --git a/ghcide/test/data/hiding/HidePreludeIndented.hs.expected b/ghcide/test/data/hiding/HidePreludeIndented.hs.expected new file mode 100644 index 0000000000..4218338bee --- /dev/null +++ b/ghcide/test/data/hiding/HidePreludeIndented.hs.expected @@ -0,0 +1,5 @@ +module HidePreludeIndented where + + import AVec + import Prelude hiding ((++)) + op = (++) diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 44bb9b6dd7..6d6b9538db 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -1497,6 +1497,11 @@ suggestImportDisambiguationTests = testGroup "suggest import disambiguation acti compareHideFunctionTo [(8,9),(10,8)] "Use Prelude for ++, hiding other imports" "HideFunction.hs.expected.append.Prelude" + , testCase "AVec, indented" $ + compareTwo "HidePreludeIndented.hs" [(3,8)] + "Use AVec for ++, hiding other imports" + "HidePreludeIndented.hs.expected" + ] , testGroup "Vec (type)" [ testCase "AVec" $ From 0550b6f00894c64a3de83031663ba388597a7d35 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sun, 31 Jan 2021 02:43:36 +0900 Subject: [PATCH 33/43] Unify exact actions and `suggestImportDisambiguation` --- .../src/Development/IDE/Plugin/CodeAction.hs | 42 ++++++------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 1bff2409bb..a21a40a004 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -58,7 +58,6 @@ import qualified Data.DList as DL import Development.IDE.Spans.Common import OccName import Data.Coerce -import Data.Either (fromRight) import qualified GHC.LanguageExtensions as Lang import Control.Lens (foldMapBy) import FieldLabel (flLabel) @@ -102,33 +101,14 @@ codeAction lsp state _ (TextDocumentIdentifier uri) _range CodeActionContext{_di , let edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing ] actions' = - [mkCA title [x] edit - | x <- xs - , Just ps <- [annotatedPS] - , Just dynflags <- [df] - , (title, grafts) <- suggestExactAction exportsMap dynflags ps x - , let edit = foldMapBy unionWSEdit mempty (either error id . - rewriteToEdit dynflags uri (annsA ps)) grafts - ] ++ [mkCA title [x] edit | x <- xs , ps <- maybeToList annotatedPS , dynflags <- maybeToList df , nfp <- maybeToList mbFile - , (title, edRewrs) <- - suggestImportDisambiguation nfp dynflags (astA ps) x - , let edit = - foldMapBy unionWSEdit mempty - (either - (\te -> WorkspaceEdit - { _changes = Just $ Map.singleton uri $ List [te] - , _documentChanges = Nothing } - ) - (-- either (Left . traceShow) Right $ - fromRight mempty. - rewriteToEdit dynflags uri (annsA ps)) - ) edRewrs - + , (title, grafts) <- suggestExactAction exportsMap nfp dynflags ps x + , let edit = foldMapBy unionWSEdit mempty (either error id . + rewriteToEdit dynflags uri (annsA ps)) grafts ] actions'' = caRemoveRedundantImports parsedModule text diag xs uri <> actions @@ -156,15 +136,17 @@ mkCA title diags edit = suggestExactAction :: ExportsMap -> + NormalizedFilePath -> DynFlags -> Annotated ParsedSource -> Diagnostic -> [(T.Text, [Rewrite])] -suggestExactAction exportsMap df ps x = +suggestExactAction exportsMap nfp df ps x = concat [ suggestConstraint df (astA ps) x , suggestImplicitParameter (astA ps) x , suggestExtendImport exportsMap (astA ps) x + , suggestImportDisambiguation nfp df (astA ps) x ] suggestAction @@ -775,7 +757,7 @@ suggestImportDisambiguation :: DynFlags -> ParsedSource -> Diagnostic -> - [(T.Text, [Either TextEdit Rewrite])] + [(T.Text, [Rewrite])] suggestImportDisambiguation nfp df ps@(L _ HsModule {hsmodImports}) diag@Diagnostic {..} | Just [ambiguous] <- matchRegexUnifySpaces @@ -875,23 +857,23 @@ disambiguateSymbol :: Diagnostic -> T.Text -> HidingMode -> - [Either TextEdit Rewrite] + [Rewrite] disambiguateSymbol nfp pm Diagnostic {..} (T.unpack -> symbol) = \case (HideOthers hiddens0) -> - [ Right $ hideSymbol symbol idecl + [ hideSymbol symbol idecl | ExistingImp idecls <- hiddens0 , idecl <- NE.toList idecls ] ++ mconcat [ if null imps - then [Right $ hideImplicitPreludeSymbol symbol pm] - else Right . hideSymbol symbol <$> imps + then [hideImplicitPreludeSymbol symbol pm] + else hideSymbol symbol <$> imps | ImplicitPrelude imps <- hiddens0 ] (ToQualified qualMod) -> let occSym = mkVarOcc symbol rdr = Qual qualMod occSym - in [Right $ Rewrite (rangeToSrcSpan nfp _range) $ \df -> do + in [Rewrite (rangeToSrcSpan nfp _range) $ \df -> do liftParseAST @(HsExpr GhcPs) df $ prettyPrint $ HsVar @GhcPs noExtField $ L (UnhelpfulSpan "") rdr ] From bf99f31a17d1b93a7b0969913efbb582ef884a73 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sun, 31 Jan 2021 02:48:54 +0900 Subject: [PATCH 34/43] Moves a comment to the right place --- ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index dae612c7ae..00b5fa436c 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -395,11 +395,11 @@ hideImplicitPreludeSymbol symbol (L _ HsModule{..}) = beg = realSrcSpanEnd exisImpSpan ran = RealSrcSpan $ mkRealSrcSpan beg beg in Rewrite ran $ \df -> do - -- Re-labeling is needed to reflect annotations correctly let symOcc = mkVarOcc symbol symImp = T.pack $ showSDoc df $ parenSymOcc symOcc $ ppr symOcc impStmt = "import Prelude hiding (" <> symImp <> ")" + -- Re-labeling is needed to reflect annotations correctly L _ idecl0 <- liftParseAST @(ImportDecl GhcPs) df $ T.unpack impStmt let idecl = L ran idecl0 addSimpleAnnT idecl (DP (1,indentation - 1)) From d96d6126b9df7e0f6030f0df2229810d21b23765 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sun, 31 Jan 2021 02:52:41 +0900 Subject: [PATCH 35/43] Won't panic on errornous input, let HLS keep going --- ghcide/src/Development/IDE/Plugin/CodeAction.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index a21a40a004..ee28614485 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -107,7 +107,7 @@ codeAction lsp state _ (TextDocumentIdentifier uri) _range CodeActionContext{_di , dynflags <- maybeToList df , nfp <- maybeToList mbFile , (title, grafts) <- suggestExactAction exportsMap nfp dynflags ps x - , let edit = foldMapBy unionWSEdit mempty (either error id . + , let edit = foldMapBy unionWSEdit mempty (either mempty id . rewriteToEdit dynflags uri (annsA ps)) grafts ] actions'' = caRemoveRedundantImports parsedModule text diag xs uri From 10cc8151ab02e795678b2d53a9ee35cd9b71cd48 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sun, 31 Jan 2021 03:16:40 +0900 Subject: [PATCH 36/43] No, each errornous rewrite must not be dropped seprately, but discarded as a whole --- ghcide/src/Development/IDE/Plugin/CodeAction.hs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index ee28614485..ac9e3c44b4 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -45,7 +45,7 @@ import Outputable (Outputable, ppr, showSDoc, showSDocUnsafe) import Data.Function import Control.Arrow ((>>>), second) import Data.Functor -import Control.Applicative ((<|>)) +import Control.Applicative ((<|>), Applicative (liftA2)) import Safe (atMay) import Bag (isEmptyBag) import qualified Data.HashSet as Set @@ -62,6 +62,7 @@ import qualified GHC.LanguageExtensions as Lang import Control.Lens (foldMapBy) import FieldLabel (flLabel) import FastString (unpackFS) +import Data.Monoid (Ap(Ap, getAp)) descriptor :: PluginId -> PluginDescriptor IdeState descriptor plId = @@ -107,8 +108,9 @@ codeAction lsp state _ (TextDocumentIdentifier uri) _range CodeActionContext{_di , dynflags <- maybeToList df , nfp <- maybeToList mbFile , (title, grafts) <- suggestExactAction exportsMap nfp dynflags ps x - , let edit = foldMapBy unionWSEdit mempty (either mempty id . - rewriteToEdit dynflags uri (annsA ps)) grafts + , Right edit + <- [getAp $ foldMapBy (liftA2 unionWSEdit) mempty + (Ap . rewriteToEdit dynflags uri (annsA ps)) grafts] ] actions'' = caRemoveRedundantImports parsedModule text diag xs uri <> actions From 82345d194bb91a13eba6bae96cabdf16be2aa906 Mon Sep 17 00:00:00 2001 From: Hiromi Ishii Date: Sun, 31 Jan 2021 13:47:08 +0900 Subject: [PATCH 37/43] Update ghcide/src/Development/IDE/Spans/Common.hs Co-authored-by: Potato Hatsue --- ghcide/src/Development/IDE/Spans/Common.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ghcide/src/Development/IDE/Spans/Common.hs b/ghcide/src/Development/IDE/Spans/Common.hs index b690f84ed7..479c908c2a 100644 --- a/ghcide/src/Development/IDE/Spans/Common.hs +++ b/ghcide/src/Development/IDE/Spans/Common.hs @@ -56,9 +56,7 @@ showNameWithoutUniques = T.pack . prettyprint -- | Shows IEWrappedName, without any modifier, qualifier or unique identifier. unqualIEWrapName :: IEWrappedName RdrName -> T.Text -unqualIEWrapName (IEName (L _ nam)) = showNameWithoutUniques $ rdrNameOcc nam -unqualIEWrapName (IEPattern (L _ nam)) = showNameWithoutUniques $ rdrNameOcc nam -unqualIEWrapName (IEType (L _ nam)) = showNameWithoutUniques $ rdrNameOcc nam +unqualIEWrapName = showNameWithoutUniques . rdrNameOcc . ieWrappedName -- From haskell-ide-engine/src/Haskell/Ide/Engine/Support/HieExtras.hs safeTyThingType :: TyThing -> Maybe Type From 191df1b52549585545ae42c927660e73e049e991 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sun, 31 Jan 2021 14:30:52 +0900 Subject: [PATCH 38/43] ieNames --- ghcide/src/Development/IDE/Plugin/CodeAction.hs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index ac9e3c44b4..7435c6f833 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -832,19 +832,7 @@ occursUnqualified symbol ImportDecl{..} occursUnqualified _ _ = False symbolOccursIn :: T.Text -> IE GhcPs -> Bool -symbolOccursIn symb = \case - IEVar _ (L _ n) -> unqualIEWrapName n == symb - IEThingAbs _ (L _ n) -> unqualIEWrapName n == symb - IEThingAll _ (L _ n) -> unqualIEWrapName n == symb - IEThingWith _ (L _ n) _ ents flds -> - unqualIEWrapName n == symb - || any ((== symb) . unqualIEWrapName . unLoc) ents - || any ((== symb) . T.pack . unpackFS . flLabel . unLoc) flds - IEModuleContents{} -> False - IEGroup{} -> False - IEDoc{} -> False - IEDocNamed{} -> False - XIE{} -> False +symbolOccursIn symb = any ((== symb). showNameWithoutUniques) . ieNames targetModuleName :: ModuleTarget -> ModuleName targetModuleName ImplicitPrelude{} = mkModuleName "Prelude" From 3b2b4e8978ec3326b669a0f5cd70c490bb1e3d3a Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sun, 31 Jan 2021 14:38:17 +0900 Subject: [PATCH 39/43] Makes Splice plugin compiles --- .../src/Ide/Plugin/Splice.hs | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs b/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs index 44e6d3d3bc..373be9f919 100644 --- a/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs +++ b/plugins/hls-splice-plugin/src/Ide/Plugin/Splice.hs @@ -47,12 +47,11 @@ import Ide.Plugin.Splice.Types import Ide.PluginUtils (mkLspCommand, responseError) import Development.IDE.GHC.ExactPrint import Ide.Types -import Language.Haskell.GHC.ExactPrint (TransformT, setPrecedingLines, uniqueSrcSpanT) +import Language.Haskell.GHC.ExactPrint (setPrecedingLines, uniqueSrcSpanT) import Language.Haskell.LSP.Core import Language.Haskell.LSP.Messages import Language.Haskell.LSP.Types import qualified Language.Haskell.LSP.Types.Lens as J -import Retrie.ExactPrint (Annotated) import RnSplice import TcRnMonad import Data.Foldable (Foldable(foldl')) @@ -392,10 +391,7 @@ codeAction _ state plId docId ran _ = ParsedModule {..} <- MaybeT . runAction "splice.codeAction.GitHieAst" state $ use GetParsedModule fp - let spn = - rangeToRealSrcSpan ran $ - fromString $ - fromNormalizedFilePath fp + let spn = rangeToRealSrcSpan fp ran mouterSplice = something' (detectSplice spn) pm_parsed_source mcmds <- forM mouterSplice $ \(spliceSpan, spliceContext) -> @@ -459,15 +455,3 @@ something' f = go case f x of Stop -> Nothing resl -> foldl' (flip (<|>)) (fromSearchResult resl) (gmapQ go x) - -posToRealSrcLoc :: Position -> FastString -> RealSrcLoc -posToRealSrcLoc pos fs = mkRealSrcLoc fs (line + 1) (col + 1) - where - line = _line pos - col = _character pos - -rangeToRealSrcSpan :: Range -> FastString -> RealSrcSpan -rangeToRealSrcSpan ran fs = - mkRealSrcSpan - (posToRealSrcLoc (_start ran) fs) - (posToRealSrcLoc (_end ran) fs) From d75b51eb32e09e68d832276627b5fea5c55426ff Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sun, 31 Jan 2021 16:04:53 +0900 Subject: [PATCH 40/43] Stop using nfp --- .../src/Development/IDE/Plugin/CodeAction.hs | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 6cb7136d06..676e85db05 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -94,7 +94,7 @@ codeAction lsp state _ (TextDocumentIdentifier uri) _range CodeActionContext{_di df = ms_hspp_opts . pm_mod_summary <$> parsedModule actions = [ mkCA title [x] edit - | x <- xs, (title, tedit) <- suggestAction exportsMap ideOptions mbFile parsedModule text df annotatedPS x + | x <- xs, (title, tedit) <- suggestAction exportsMap ideOptions parsedModule text df annotatedPS x , let edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing ] actions' = caRemoveRedundantImports parsedModule text diag xs uri @@ -120,26 +120,20 @@ rewrite _ _ _ = [] suggestAction :: ExportsMap -> IdeOptions - -> Maybe NormalizedFilePath -> Maybe ParsedModule -> Maybe T.Text -> Maybe DynFlags -> Maybe (Annotated ParsedSource) -> Diagnostic -> [(T.Text, [TextEdit])] -suggestAction packageExports ideOptions mbFile parsedModule text df annSource diag = +suggestAction packageExports ideOptions parsedModule text df annSource diag = concat -- Order these suggestions by priority [ suggestSignature True diag , rewrite df annSource $ \_ ps -> suggestExtendImport packageExports ps diag - ] - ++ concat - [ rewrite df annSource $ \df ps -> - suggestImportDisambiguation nfp df ps diag - | nfp <- maybeToList mbFile - ] ++ - concat [ - suggestFillTypeWildcard diag + , rewrite df annSource $ \df ps -> + suggestImportDisambiguation df ps diag + , suggestFillTypeWildcard diag , suggestFixConstructorImport text diag , suggestModuleTypo diag , suggestReplaceIdentifier text diag @@ -735,12 +729,11 @@ isPreludeImplicit = xopt Lang.ImplicitPrelude -- | Suggests disambiguation for ambiguous symbols. suggestImportDisambiguation :: - NormalizedFilePath -> DynFlags -> ParsedSource -> Diagnostic -> [(T.Text, [Rewrite])] -suggestImportDisambiguation nfp df ps@(L _ HsModule {hsmodImports}) diag@Diagnostic {..} +suggestImportDisambiguation df ps@(L _ HsModule {hsmodImports}) diag@Diagnostic {..} | Just [ambiguous] <- matchRegexUnifySpaces _message @@ -771,7 +764,7 @@ suggestImportDisambiguation nfp df ps@(L _ HsModule {hsmodImports}) diag@Diagnos | Just targets <- mapM toModuleTarget mods = sortOn fst [ ( renderUniquify mode modNameText symbol - , disambiguateSymbol nfp ps diag symbol mode + , disambiguateSymbol ps diag symbol mode ) | (modTarget, restImports) <- oneAndOthers targets , let modName = targetModuleName modTarget @@ -822,13 +815,12 @@ targetModuleName (ExistingImp _) = error "Cannot happen!" disambiguateSymbol :: - NormalizedFilePath -> ParsedSource -> Diagnostic -> T.Text -> HidingMode -> [Rewrite] -disambiguateSymbol nfp pm Diagnostic {..} (T.unpack -> symbol) = \case +disambiguateSymbol pm Diagnostic {..} (T.unpack -> symbol) = \case (HideOthers hiddens0) -> [ hideSymbol symbol idecl | ExistingImp idecls <- hiddens0 @@ -843,7 +835,7 @@ disambiguateSymbol nfp pm Diagnostic {..} (T.unpack -> symbol) = \case (ToQualified qualMod) -> let occSym = mkVarOcc symbol rdr = Qual qualMod occSym - in [Rewrite (rangeToSrcSpan nfp _range) $ \df -> do + in [Rewrite (rangeToSrcSpan "" _range) $ \df -> do liftParseAST @(HsExpr GhcPs) df $ prettyPrint $ HsVar @GhcPs noExtField $ L (UnhelpfulSpan "") rdr ] From 13b9f2cc81580e6f70c6f089704af70ea510d17a Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sun, 31 Jan 2021 16:29:37 +0900 Subject: [PATCH 41/43] Since there is global `setEntryDPT dp00`, we don't add offset here --- ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index dd706c28b7..f1bcf1ed56 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -134,7 +134,7 @@ appendConstraint constraintT = go lContext <- uniqueSrcSpanT lTop <- uniqueSrcSpanT let context = L lContext [constraint] - addSimpleAnnT context (DP (0, 1)) $ + addSimpleAnnT context (DP (0, 0)) $ (G AnnDarrow, DP (0, 1)) : concat [ [ (G AnnOpenP, dp00), From 4aae21a71b22d88a847d66ffa6a432cfcac10f0c Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sun, 31 Jan 2021 22:18:52 +0900 Subject: [PATCH 42/43] Removes redundant (why warned here?) --- ghcide/src/Development/IDE/GHC/Orphans.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/ghcide/src/Development/IDE/GHC/Orphans.hs b/ghcide/src/Development/IDE/GHC/Orphans.hs index be3c830794..e9a5e91538 100644 --- a/ghcide/src/Development/IDE/GHC/Orphans.hs +++ b/ghcide/src/Development/IDE/GHC/Orphans.hs @@ -22,7 +22,6 @@ import qualified StringBuffer as SB import Data.Text (Text) import Data.String (IsString(fromString)) import Retrie.ExactPrint (Annotated) -import Data.List (foldl') -- Orphan instances for types from the GHC API. From a45667b52b00d353db0a2f625e5500a5c2f00d26 Mon Sep 17 00:00:00 2001 From: Hiromi ISHII Date: Sun, 31 Jan 2021 22:19:46 +0900 Subject: [PATCH 43/43] Made `hideImplicitPreludeSymbol` total --- ghcide/src/Development/IDE/Plugin/CodeAction.hs | 10 ++++++---- .../IDE/Plugin/CodeAction/ExactPrint.hs | 15 +++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 676e85db05..e966522373 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -828,16 +828,18 @@ disambiguateSymbol pm Diagnostic {..} (T.unpack -> symbol) = \case ] ++ mconcat [ if null imps - then [hideImplicitPreludeSymbol symbol pm] + then maybeToList $ hideImplicitPreludeSymbol symbol pm else hideSymbol symbol <$> imps | ImplicitPrelude imps <- hiddens0 ] (ToQualified qualMod) -> let occSym = mkVarOcc symbol rdr = Qual qualMod occSym - in [Rewrite (rangeToSrcSpan "" _range) $ \df -> do - liftParseAST @(HsExpr GhcPs) df $ prettyPrint $ HsVar @GhcPs noExtField - $ L (UnhelpfulSpan "") rdr + in [ Rewrite (rangeToSrcSpan "" _range) $ \df -> do + liftParseAST @(HsExpr GhcPs) df $ + prettyPrint $ + HsVar @GhcPs noExtField $ + L (UnhelpfulSpan "") rdr ] findImportDeclByRange :: [LImportDecl GhcPs] -> Range -> Maybe (LImportDecl GhcPs) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index f1bcf1ed56..49114c70d0 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -39,6 +39,7 @@ import Outputable (ppr, showSDocUnsafe, showSDoc) import Retrie.GHC (rdrNameOcc, unpackFS, mkRealSrcSpan, realSrcSpanEnd) import Development.IDE.Spans.Common import Development.IDE.GHC.Error +import Safe (lastMay) ------------------------------------------------------------------------------ @@ -376,16 +377,14 @@ deleteFromImport (T.pack -> symbol) (L l idecl) llies@(L lieLoc lies) _ =do killLie v = Just v hideImplicitPreludeSymbol - :: String -> ParsedSource -> Rewrite -hideImplicitPreludeSymbol symbol (L _ HsModule{..}) = - let -- We assume there is at least one import, to collide with Prelude; - -- otherwise (custom) Prelude must be buggy, (or preprocessor adds some collision?) - existingImp = last hsmodImports - exisImpSpan = fromJust $ realSpan $ getLoc existingImp - indentation = srcSpanStartCol exisImpSpan + :: String -> ParsedSource -> Maybe Rewrite +hideImplicitPreludeSymbol symbol (L _ HsModule{..}) = do + existingImp <- lastMay hsmodImports + exisImpSpan <- realSpan $ getLoc existingImp + let indentation = srcSpanStartCol exisImpSpan beg = realSrcSpanEnd exisImpSpan ran = RealSrcSpan $ mkRealSrcSpan beg beg - in Rewrite ran $ \df -> do + pure $ Rewrite ran $ \df -> do let symOcc = mkVarOcc symbol symImp = T.pack $ showSDoc df $ parenSymOcc symOcc $ ppr symOcc impStmt = "import Prelude hiding (" <> symImp <> ")"