From 171d77d3b04ca38ea28852dc5bfa482705588f31 Mon Sep 17 00:00:00 2001 From: Potato Hatsue <1793913507@qq.com> Date: Tue, 23 Feb 2021 17:58:17 +0800 Subject: [PATCH 1/4] Add code action for importing class methods --- .../src/Development/IDE/Plugin/CodeAction.hs | 39 +++++++- .../IDE/Plugin/CodeAction/ExactPrint.hs | 83 ++++++++++++----- ghcide/test/exe/Main.hs | 88 ++++++++++++++++++- 3 files changed, 184 insertions(+), 26 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index 8d93479b66..b8fea834e6 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -144,6 +144,7 @@ suggestAction packageExports ideOptions parsedModule text df annSource tcM har d , rewrite df annSource $ \_ ps -> suggestExtendImport packageExports ps diag , rewrite df annSource $ \df ps -> suggestImportDisambiguation df text ps diag + , rewrite df annSource $ \_ ps -> suggestNewOrExtendImportForClassMethod packageExports ps diag , suggestFillTypeWildcard diag , suggestFixConstructorImport text diag , suggestModuleTypo diag @@ -760,8 +761,6 @@ suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_ = mod_srcspan >>= uncurry (suggestions hsmodImports binding) | otherwise = [] where - unImportStyle (ImportTopLevel x) = (Nothing, T.unpack x) - unImportStyle (ImportViaParent x y) = (Just $ T.unpack y, T.unpack x) suggestions decls binding mod srcspan | range <- case [ x | (x,"") <- readSrcSpan (T.unpack srcspan)] of [s] -> let x = realSrcSpanToRange s @@ -1149,6 +1148,39 @@ removeRedundantConstraints mContents Diagnostic{..} ------------------------------------------------------------------------------------------------- +suggestNewOrExtendImportForClassMethod :: ExportsMap -> ParsedSource -> Diagnostic -> [(T.Text, [Rewrite])] +suggestNewOrExtendImportForClassMethod packageExportsMap ps Diagnostic {_message} + | Just [methodName, className] <- + matchRegexUnifySpaces + _message + "‘([^’]*)’ is not a \\(visible\\) method of class ‘([^’]*)’", + idents <- + maybe [] (Set.toList . Set.filter (\x -> parent x == Just className)) $ + Map.lookup methodName $ getExportsMap packageExportsMap = + mconcat $ suggest <$> idents + | otherwise = [] + where + suggest identInfo@IdentInfo {moduleNameText} + | importStyle <- NE.toList $ importStyles identInfo, + mImportDecl <- findImportDeclByModuleName (hsmodImports $ unLoc ps) (T.unpack moduleNameText) = + case mImportDecl of + -- extend + Just decl -> + [ ( "Add " <> renderImportStyle style <> " to the import list of " <> moduleNameText, + [uncurry extendImport (unImportStyle style) decl] + ) + | style <- importStyle + ] + -- new + _ -> + [ ( "Import " <> moduleNameText <> " with " <> rendered, + maybeToList $ newImport (T.unpack moduleNameText) (Just $ T.unpack rendered) Nothing False ps + ) + | style <- importStyle, + let rendered = renderImportStyle style + ] + <> maybeToList (("Import " <> moduleNameText,) <$> fmap pure (newImport (T.unpack moduleNameText) Nothing Nothing False ps)) + suggestNewImport :: ExportsMap -> ParsedModule -> Diagnostic -> [(T.Text, [TextEdit])] suggestNewImport packageExportsMap ParsedModule {pm_parsed_source = L _ HsModule {..}} Diagnostic{_message} | msg <- unifySpaces _message @@ -1442,3 +1474,6 @@ renderImportStyle :: ImportStyle -> T.Text renderImportStyle (ImportTopLevel x) = x renderImportStyle (ImportViaParent x p) = p <> "(" <> x <> ")" +unImportStyle :: ImportStyle -> (Maybe String, String) +unImportStyle (ImportTopLevel x) = (Nothing, T.unpack x) +unImportStyle (ImportViaParent x y) = (Just $ T.unpack y, T.unpack x) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index 201b5030d3..9f0e7c6d1e 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -15,6 +15,7 @@ module Development.IDE.Plugin.CodeAction.ExactPrint hideImplicitPreludeSymbol, hideSymbol, liftParseAST, + newImport, ) where @@ -31,7 +32,7 @@ import Development.IDE.GHC.Compat hiding (parseExpr) import Development.IDE.GHC.ExactPrint ( Annotate, ASTElement(parseAST) ) import FieldLabel (flLabel) -import GhcPlugins (sigPrec, mkRealSrcLoc) +import GhcPlugins (sigPrec, mkRealSrcLoc, realSrcSpanStart) import Language.Haskell.GHC.ExactPrint import Language.Haskell.GHC.ExactPrint.Types (DeltaPos (DP), KeywordId (G), mkAnnKey) import Language.LSP.Types @@ -411,25 +412,61 @@ deleteFromImport (T.pack -> symbol) (L l idecl) llies@(L lieLoc lies) _ =do (filter ((/= symbol) . T.pack . unpackFS . flLabel . unLoc) flds) killLie v = Just v --- | Insert a import declaration hiding a symbole from Prelude -hideImplicitPreludeSymbol - :: String -> ParsedSource -> Maybe Rewrite -hideImplicitPreludeSymbol symbol (L _ HsModule{..}) = do - let predLine old = mkRealSrcLoc (srcLocFile old) (srcLocLine old - 1) (srcLocCol old) - existingImpSpan = (fmap (id,) . realSpan . getLoc) =<< lastMaybe hsmodImports - existingDeclSpan = (fmap (predLine, ) . realSpan . getLoc) =<< headMaybe hsmodDecls - (f, s) <- existingImpSpan <|> existingDeclSpan - let beg = f $ realSrcSpanEnd s - indentation = srcSpanStartCol s - ran = RealSrcSpan $ mkRealSrcSpan beg beg - pure $ Rewrite ran $ \df -> do - 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)) - [(G AnnImport, DP (1, indentation - 1))] - pure idecl +-- | Insert a import declaration with at most one symbol +-- +-- newImport "A" (Just "Bar(Cons)") Nothing False --> import A (Bar(Cons)) +-- newImport "A" (Just "foo") Nothing True --> import A hiding (foo) +-- newImport "A" Nothing (Just "Q") False --> import qualified A as Q +-- +-- Wrong combinations will result in parse error +-- Returns Nothing if there is no imports and declarations +newImport :: + -- | module name + String -> + -- | the symbol + Maybe String -> + -- | whether to be qualified + Maybe String -> + -- | the symbol is to be imported or hided + Bool -> + ParsedSource -> + Maybe Rewrite +newImport modName mSymbol mQual hiding (L _ HsModule {..}) = do + -- TODO (berberman): if the previous line is module name and there is no other imports, + -- 'AnnWhere' will be crowded out to the next line, which is a bug + let predLine old = + mkRealSrcLoc + (srcLocFile old) + (srcLocLine old - 1) + (srcLocCol old) + existingImpSpan = (fmap (realSrcSpanEnd,) . realSpan . getLoc) =<< lastMaybe hsmodImports + existingDeclSpan = (fmap (predLine . realSrcSpanStart,) . realSpan . getLoc) =<< headMaybe hsmodDecls + (f, s) <- existingImpSpan <|> existingDeclSpan + let beg = f s + indentation = srcSpanStartCol s + ran = RealSrcSpan $ mkRealSrcSpan beg beg + pure $ + Rewrite ran $ \df -> do + let symImp + | Just symbol <- mSymbol, + symOcc <- mkVarOcc symbol = + "(" <> showSDoc df (parenSymOcc symOcc $ ppr symOcc) <> ")" + | otherwise = "" + impStmt = + "import " + <> maybe "" (const "qualified ") mQual + <> modName + <> (if hiding then " hiding " else " ") + <> symImp + <> maybe "" (" as " <>) mQual + -- Re-labeling is needed to reflect annotations correctly + L _ idecl0 <- liftParseAST @(ImportDecl GhcPs) df impStmt + let idecl = L ran idecl0 + addSimpleAnnT + idecl + (DP (1, indentation - 1)) + [(G AnnImport, DP (1, indentation - 1))] + pure idecl + +hideImplicitPreludeSymbol :: String -> ParsedSource -> Maybe Rewrite +hideImplicitPreludeSymbol symbol = newImport "Prelude" (Just symbol) Nothing True diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 0cfb5c7e56..a2762c947a 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -702,6 +702,7 @@ codeActionTests = testGroup "code actions" , typeWildCardActionTests , removeImportTests , extendImportTests + , suggesImportClassMethodTests , suggestImportTests , suggestHideShadowTests , suggestImportDisambiguationTests @@ -1416,7 +1417,92 @@ extendImportTestsRegEx = testGroup "regex parsing" template message expected = do liftIO $ matchRegExMultipleImports message @=? expected - +suggesImportClassMethodTests :: TestTree +suggesImportClassMethodTests = + testGroup + "suggest import class methods" + [ testGroup + "new" + [ testSession "via parent" $ + template + [ "module A where", + "" + ] + (Range (Position 5 2) (Position 5 8)) + "Import Data.Semigroup with Semigroup(stimes)" + [ "module A where", + "", + "import Data.Semigroup (Semigroup(stimes))" + ], + testSession "top level" $ + template + [ "module A where", + "" + ] + (Range (Position 5 2) (Position 5 8)) + "Import Data.Semigroup with stimes" + [ "module A where", + "", + "import Data.Semigroup (stimes)" + ], + testSession "all" $ + template + [ "module A where", + "" + ] + (Range (Position 5 2) (Position 5 8)) + "Import Data.Semigroup" + [ "module A where", + "", + "import Data.Semigroup" + ] + ], + testGroup + "extend" + [ testSession "via parent" $ + template + [ "module A where", + "", + "import Data.Semigroup ()" + ] + (Range (Position 6 2) (Position 6 8)) + "Add Semigroup(stimes) to the import list of Data.Semigroup" + [ "module A where", + "", + "import Data.Semigroup (Semigroup (stimes))" + ], + testSession "top level" $ + template + [ "module A where", + "", + "import Data.Semigroup ()" + ] + (Range (Position 6 2) (Position 6 8)) + "Add stimes to the import list of Data.Semigroup" + [ "module A where", + "", + "import Data.Semigroup (stimes)" + ] + ] + ] + where + decls = + [ "data X = X", + "instance Semigroup X where", + " (<>) _ _ = X", + " stimes _ _ = X" + ] + template beforeContent range executeTitle expectedContent = do + doc <- createDoc "A.hs" "haskell" $ T.unlines (beforeContent <> decls) + _ <- waitForDiagnostics + waitForProgressDone + actions <- getCodeActions doc range + let actions' = [x | InR x <- actions] + titles = [_title | CodeAction {_title} <- actions'] + liftIO $ executeTitle `elem` titles @? T.unpack executeTitle <> " does not in " <> show titles + executeCodeAction $ fromJust $ find (\CodeAction {_title} -> _title == executeTitle) actions' + content <- documentContents doc + liftIO $ T.unlines (expectedContent <> decls) @=? content suggestImportTests :: TestTree suggestImportTests = testGroup "suggest import actions" From cbc287cbce62f2a745d91661cf3d02fa50e5eaf1 Mon Sep 17 00:00:00 2001 From: Potato Hatsue <1793913507@qq.com> Date: Tue, 23 Feb 2021 18:04:05 +0800 Subject: [PATCH 2/4] Format ExactPrint --- .../IDE/Plugin/CodeAction/ExactPrint.hs | 80 ++++++++++--------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index 9f0e7c6d1e..d459305ddb 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -1,7 +1,7 @@ {-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} module Development.IDE.Plugin.CodeAction.ExactPrint ( Rewrite (..), @@ -19,31 +19,39 @@ module Development.IDE.Plugin.CodeAction.ExactPrint ) 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.Map.Strict as Map -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 FieldLabel (flLabel) -import GhcPlugins (sigPrec, mkRealSrcLoc, realSrcSpanStart) -import Language.Haskell.GHC.ExactPrint -import Language.Haskell.GHC.ExactPrint.Types (DeltaPos (DP), KeywordId (G), mkAnnKey) -import Language.LSP.Types -import OccName -import Outputable (ppr, showSDocUnsafe, showSDoc) -import Retrie.GHC (rdrNameOcc, unpackFS, mkRealSrcSpan, realSrcSpanEnd) -import Development.IDE.Spans.Common -import Development.IDE.GHC.Error -import Data.Generics (listify) -import GHC.Exts (IsList (fromList)) -import Control.Monad.Extra (whenJust) +import Control.Applicative +import Control.Monad +import Control.Monad.Extra (whenJust) +import Control.Monad.Trans +import Data.Char (isAlphaNum) +import Data.Data (Data) +import Data.Functor +import Data.Generics (listify) +import qualified Data.Map.Strict as Map +import Data.Maybe (fromJust, isNothing, + mapMaybe) +import qualified Data.Text as T +import Development.IDE.GHC.Compat hiding (parseExpr) +import Development.IDE.GHC.Error +import Development.IDE.GHC.ExactPrint (ASTElement (parseAST), + Annotate) +import Development.IDE.Spans.Common +import FieldLabel (flLabel) +import GHC.Exts (IsList (fromList)) +import GhcPlugins (mkRealSrcLoc, + realSrcSpanStart, + sigPrec) +import Language.Haskell.GHC.ExactPrint +import Language.Haskell.GHC.ExactPrint.Types (DeltaPos (DP), + KeywordId (G), mkAnnKey) +import Language.LSP.Types +import OccName +import Outputable (ppr, showSDoc, + showSDocUnsafe) +import Retrie.GHC (mkRealSrcSpan, + rdrNameOcc, + realSrcSpanEnd, + unpackFS) ------------------------------------------------------------------------------ @@ -116,7 +124,7 @@ fixParens openDP closeDP ctxt@(L _ elems) = do dropHsParTy :: LHsType pass -> LHsType pass dropHsParTy (L _ (HsParTy _ ty)) = ty - dropHsParTy other = other + dropHsParTy other = other -- | Append a constraint at the end of a type context. -- If no context is present, a new one will be created. @@ -162,7 +170,7 @@ appendConstraint constraintT = go liftParseAST :: ASTElement ast => DynFlags -> String -> TransformT (Either String) (Located ast) liftParseAST df s = case parseAST df "" s of Right (anns, x) -> modifyAnnsT (anns <>) $> x - Left _ -> lift $ Left $ "No parse: " <> s + Left _ -> lift $ Left $ "No parse: " <> s lookupAnn :: (Data a, Monad m) => KeywordId -> Located a -> TransformT m (Maybe DeltaPos) lookupAnn comment la = do @@ -173,16 +181,16 @@ dp00 :: DeltaPos dp00 = DP (0, 0) headMaybe :: [a] -> Maybe a -headMaybe [] = Nothing +headMaybe [] = Nothing headMaybe (a : _) = Just a lastMaybe :: [a] -> Maybe a -lastMaybe [] = Nothing +lastMaybe [] = Nothing lastMaybe other = Just $ last other liftMaybe :: String -> Maybe a -> TransformT (Either String) a liftMaybe _ (Just x) = return x -liftMaybe s _ = lift $ Left s +liftMaybe s _ = lift $ Left s -- | Copy anns attached to a into b with modification, then delete anns of a transferAnn :: (Data a, Data b) => Located a -> Located b -> (Annotation -> Annotation) -> TransformT (Either String) () @@ -199,7 +207,7 @@ extendImport mparent identifier lDecl@(L l _) = Rewrite l $ \df -> do case mparent of Just parent -> extendImportViaParent df parent identifier lDecl - _ -> extendImportTopLevel df identifier lDecl + _ -> extendImportTopLevel df identifier lDecl -- | Add an identifier to import list -- @@ -312,7 +320,7 @@ unIEWrappedName (occName -> occ) = showSDocUnsafe $ parenSymOcc occ (ppr occ) hasParen :: String -> Bool hasParen ('(' : _) = True -hasParen _ = False +hasParen _ = False unqalDP :: Bool -> [(KeywordId, DeltaPos)] unqalDP paren = @@ -427,7 +435,7 @@ newImport :: Maybe String -> -- | whether to be qualified Maybe String -> - -- | the symbol is to be imported or hided + -- | the symbol is to be imported or hidden Bool -> ParsedSource -> Maybe Rewrite From c1cfca4b8e31e432c13d0c8f941482352aab3fc3 Mon Sep 17 00:00:00 2001 From: Potato Hatsue <1793913507@qq.com> Date: Wed, 24 Feb 2021 10:50:42 +0800 Subject: [PATCH 3/4] Format CodeAction --- .../src/Development/IDE/Plugin/CodeAction.hs | 159 ++++++------ .../IDE/Plugin/CodeAction/ExactPrint.hs | 227 ++++++++++-------- 2 files changed, 208 insertions(+), 178 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction.hs b/ghcide/src/Development/IDE/Plugin/CodeAction.hs index b8fea834e6..bfe3221fd3 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction.hs @@ -1,10 +1,10 @@ -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 +{-# LANGUAGE CPP #-} {-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE CPP #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE GADTs #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE RankNTypes #-} #include "ghc-api-version.h" -- | Go to the definition of a variable. @@ -15,59 +15,68 @@ module Development.IDE.Plugin.CodeAction , matchRegExMultipleImports ) where -import Control.Monad (join, guard) -import Control.Monad.IO.Class -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.Error -import Development.IDE.GHC.ExactPrint -import Development.IDE.Plugin.CodeAction.ExactPrint -import Development.IDE.Plugin.CodeAction.PositionIndexed -import Development.IDE.Plugin.TypeLenses (suggestSignature) -import Development.IDE.Types.Exports -import Development.IDE.Types.HscEnvEq -import Development.IDE.Types.Location -import Development.IDE.Types.Options -import qualified Data.HashMap.Strict as Map -import qualified Language.LSP.Server as LSP -import Language.LSP.VFS -import Language.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 Data.Function -import Control.Arrow ((>>>), second) -import Data.Functor -import Control.Applicative ((<|>)) -import Safe (atMay) -import Bag (isEmptyBag) -import qualified Data.HashSet as Set -import Control.Concurrent.Extra (readVar) -import Development.IDE.GHC.Util (printRdrName, prettyPrint) -import Ide.PluginUtils (subRange) -import Ide.Types -import qualified Data.DList as DL -import Development.IDE.Spans.Common -import OccName -import qualified GHC.LanguageExtensions as Lang -import Control.Lens (alaf) -import Data.Monoid (Ap(..)) -import TcRnTypes (TcGblEnv(..), ImportAvails(..)) -import HscTypes (ImportedModsVal(..), importedByUser) -import RdrName (GlobalRdrElt(..), lookupGlobalRdrEnv) -import SrcLoc (realSrcSpanStart) -import Module (moduleEnvElts) -import qualified Data.Map as M -import qualified Data.Set as S +import Bag (isEmptyBag) +import Control.Applicative ((<|>)) +import Control.Arrow (second, + (>>>)) +import Control.Concurrent.Extra (readVar) +import Control.Lens (alaf) +import Control.Monad (guard, join) +import Control.Monad.IO.Class +import Data.Char +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.List.Extra +import Data.List.NonEmpty (NonEmpty ((:|))) +import qualified Data.List.NonEmpty as NE +import qualified Data.Map as M +import Data.Maybe +import Data.Monoid (Ap (..)) +import qualified Data.Rope.UTF16 as Rope +import qualified Data.Set as S +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 (prettyPrint, + printRdrName) +import Development.IDE.Plugin.CodeAction.ExactPrint +import Development.IDE.Plugin.CodeAction.PositionIndexed +import Development.IDE.Plugin.TypeLenses (suggestSignature) +import Development.IDE.Spans.Common +import Development.IDE.Types.Exports +import Development.IDE.Types.HscEnvEq +import Development.IDE.Types.Location +import Development.IDE.Types.Options +import qualified GHC.LanguageExtensions as Lang +import HscTypes (ImportedModsVal (..), + importedByUser) +import Ide.PluginUtils (subRange) +import Ide.Types +import qualified Language.LSP.Server as LSP +import Language.LSP.Types +import Language.LSP.VFS +import Module (moduleEnvElts) +import OccName +import Outputable (Outputable, + ppr, + showSDoc, + showSDocUnsafe) +import RdrName (GlobalRdrElt (..), + lookupGlobalRdrEnv) +import Safe (atMay) +import SrcLoc (realSrcSpanStart) +import TcRnTypes (ImportAvails (..), + TcGblEnv (..)) +import Text.Regex.TDFA (mrAfter, + (=~), (=~~)) descriptor :: PluginId -> PluginDescriptor IdeState descriptor plId = @@ -224,7 +233,7 @@ suggestHideShadow pm@(L _ HsModule {hsmodImports}) mTcM mHar Diagnostic {_messag findImportDeclByModuleName :: [LImportDecl GhcPs] -> String -> Maybe (LImportDecl GhcPs) findImportDeclByModuleName decls modName = flip find decls $ \case (L _ ImportDecl {..}) -> modName == moduleNameString (unLoc ideclName) - _ -> error "impossible" + _ -> error "impossible" isTheSameLine :: SrcSpan -> SrcSpan -> Bool isTheSameLine s1 s2 @@ -365,7 +374,7 @@ suggestRemoveRedundantExport ParsedModule{pm_parsed_source = L _ HsModule{..}} D matchExportItem msg = regexSingleMatch msg "The export item ‘([^’]+)’" matchDupExport msg = regexSingleMatch msg "Duplicate ‘([^’]+)’ in export list" getRanges exports txt = case smallerRangesForBindingExport exports (T.unpack txt) of - [] -> (txt, [_range]) + [] -> (txt, [_range]) ranges -> (txt, ranges) suggestRemoveRedundantExport _ _ = Nothing @@ -534,9 +543,9 @@ suggestExportUnusedTopBinding srcOpt ParsedModule{pm_parsed_source = L _ HsModul in loc >= Just l && loc <= Just r printExport :: ExportsAs -> T.Text -> T.Text - printExport ExportName x = parenthesizeIfNeeds False x + printExport ExportName x = parenthesizeIfNeeds False x printExport ExportPattern x = "pattern " <> x - printExport ExportAll x = parenthesizeIfNeeds True x <> "(..)" + printExport ExportAll x = parenthesizeIfNeeds True x <> "(..)" isTopLevel :: Range -> Bool isTopLevel l = (_character . _start) l == 0 @@ -733,7 +742,7 @@ processHoleSuggestions mm = (holeSuggestions, refSuggestions) return holeFit mapHead f (a:aa) = f a : aa - mapHead _ [] = [] + mapHead _ [] = [] -- > getIndentedGroups [" H1", " l1", " l2", " H2", " l3"] = [[" H1,", " l1", " l2"], [" H2", " l3"]] getIndentedGroups :: [T.Text] -> [[T.Text]] @@ -802,13 +811,13 @@ data ModuleTarget deriving (Show) targetImports :: ModuleTarget -> [LImportDecl GhcPs] -targetImports (ExistingImp ne) = NE.toList ne +targetImports (ExistingImp ne) = NE.toList ne targetImports (ImplicitPrelude xs) = xs oneAndOthers :: [a] -> [(a, [a])] oneAndOthers = go where - go [] = [] + go [] = [] go (x : xs) = (x, xs) : map (second (x :)) (go xs) isPreludeImplicit :: DynFlags -> Bool @@ -870,7 +879,7 @@ suggestImportDisambiguation df (Just txt) ps@(L _ HsModule {hsmodImports}) diag@ (targetImports modTarget) || case modTarget of ImplicitPrelude{} -> True - _ -> False + _ -> False ] ] | otherwise = [] @@ -1174,12 +1183,12 @@ suggestNewOrExtendImportForClassMethod packageExportsMap ps Diagnostic {_message -- new _ -> [ ( "Import " <> moduleNameText <> " with " <> rendered, - maybeToList $ newImport (T.unpack moduleNameText) (Just $ T.unpack rendered) Nothing False ps + maybeToList $ newUnqualImport (T.unpack moduleNameText) (T.unpack rendered) False ps ) | style <- importStyle, let rendered = renderImportStyle style ] - <> maybeToList (("Import " <> moduleNameText,) <$> fmap pure (newImport (T.unpack moduleNameText) Nothing Nothing False ps)) + <> maybeToList (("Import " <> moduleNameText,) <$> fmap pure (newImportAll (T.unpack moduleNameText) ps)) suggestNewImport :: ExportsMap -> ParsedModule -> Diagnostic -> [(T.Text, [TextEdit])] suggestNewImport packageExportsMap ParsedModule {pm_parsed_source = L _ HsModule {..}} Diagnostic{_message} @@ -1189,10 +1198,10 @@ suggestNewImport packageExportsMap ParsedModule {pm_parsed_source = L _ HsModule , Just insertLine <- case hsmodImports of [] -> case srcSpanStart $ getLoc (head hsmodDecls) of RealSrcLoc s -> Just $ srcLocLine s - 1 - _ -> Nothing + _ -> Nothing _ -> case srcSpanEnd $ getLoc (last hsmodImports) of RealSrcLoc s -> Just $ srcLocLine s - _ -> Nothing + _ -> Nothing , insertPos <- Position insertLine 0 , extendImportSuggestions <- matchRegexUnifySpaces msg "Perhaps you want to add ‘[^’]*’ to the import list in the import of ‘([^’]*)’" @@ -1235,9 +1244,9 @@ data NotInScope deriving Show notInScope :: NotInScope -> T.Text -notInScope (NotInScopeDataConstructor t) = t +notInScope (NotInScopeDataConstructor t) = t notInScope (NotInScopeTypeConstructorOrClass t) = t -notInScope (NotInScopeThing t) = t +notInScope (NotInScopeThing t) = t extractNotInScopeName :: T.Text -> Maybe NotInScope extractNotInScopeName x @@ -1384,7 +1393,7 @@ allMatchRegexUnifySpaces message = 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 + Nothing -> Nothing -- | Returns Just (all matches) for the first capture, or Nothing. allMatchRegex :: T.Text -> T.Text -> Maybe [[T.Text]] @@ -1400,7 +1409,7 @@ unifySpaces = T.unwords . T.words regexSingleMatch :: T.Text -> T.Text -> Maybe T.Text regexSingleMatch msg regex = case matchRegexUnifySpaces msg regex of Just (h:_) -> Just h - _ -> Nothing + _ -> Nothing -- | Parses tuples like (‘Data.Map’, (app/ModuleB.hs:2:1-18)) and -- | return (Data.Map, app/ModuleB.hs:2:1-18) @@ -1428,7 +1437,7 @@ matchRegExMultipleImports message = do let pat = T.pack "Perhaps you want to add ‘([^’]*)’ to one of these import lists: *(‘.*\\))$" (binding, imports) <- case matchRegexUnifySpaces message pat of Just [x, xs] -> Just (x, xs) - _ -> Nothing + _ -> Nothing imps <- regExImports imports return (binding, imps) @@ -1471,9 +1480,9 @@ importStyles IdentInfo {parent, rendered, isDatacon} = ImportTopLevel rendered :| [] renderImportStyle :: ImportStyle -> T.Text -renderImportStyle (ImportTopLevel x) = x +renderImportStyle (ImportTopLevel x) = x renderImportStyle (ImportViaParent x p) = p <> "(" <> x <> ")" unImportStyle :: ImportStyle -> (Maybe String, String) -unImportStyle (ImportTopLevel x) = (Nothing, T.unpack x) +unImportStyle (ImportTopLevel x) = (Nothing, T.unpack x) unImportStyle (ImportViaParent x y) = (Just $ T.unpack y, T.unpack x) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index d459305ddb..23a2436ff7 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -16,6 +16,8 @@ module Development.IDE.Plugin.CodeAction.ExactPrint hideSymbol, liftParseAST, newImport, + newUnqualImport, + newImportAll, ) where @@ -76,22 +78,23 @@ rewriteToEdit :: Either String [TextEdit] rewriteToEdit dflags anns (Rewrite dst f) = do (ast, (anns, _), _) <- runTransformT anns $ do - ast <- f dflags - ast <$ setEntryDPT ast (DP (0,0)) - let editMap = [ TextEdit (fromJust $ srcSpanToRange dst) $ - T.pack $ exactPrint ast anns - ] + ast <- f dflags + ast <$ setEntryDPT ast (DP (0, 0)) + let editMap = + [ TextEdit (fromJust $ srcSpanToRange dst) $ + T.pack $ exactPrint ast anns + ] pure editMap -- | Convert a 'Rewrite' into a 'WorkspaceEdit' rewriteToWEdit :: DynFlags -> Uri -> Anns -> Rewrite -> Either String WorkspaceEdit rewriteToWEdit dflags uri anns r = do - edits <- rewriteToEdit dflags anns r - return $ - WorkspaceEdit - { _changes = Just (fromList [(uri, List edits)]) - , _documentChanges = Nothing - } + edits <- rewriteToEdit dflags anns r + return $ + WorkspaceEdit + { _changes = Just (fromList [(uri, List edits)]), + _documentChanges = Nothing + } ------------------------------------------------------------------------------ @@ -158,13 +161,13 @@ appendConstraint constraintT = go lTop <- uniqueSrcSpanT let context = L lContext [constraint] addSimpleAnnT context (DP (0, 0)) $ - (G AnnDarrow, DP (0, 1)) - : concat - [ [ (G AnnOpenP, dp00), - (G AnnCloseP, dp00) - ] - | hsTypeNeedsParens sigPrec $ unLoc constraint + (G AnnDarrow, DP (0, 1)) : + concat + [ [ (G AnnOpenP, dp00), + (G AnnCloseP, dp00) ] + | hsTypeNeedsParens sigPrec $ unLoc constraint + ] return $ L lTop $ HsQualTy noExtField context (L l other) liftParseAST :: ASTElement ast => DynFlags -> String -> TransformT (Either String) (Located ast) @@ -225,22 +228,24 @@ extendImportTopLevel df idnetifier (L l it@ImportDecl {..}) rdr <- liftParseAST df idnetifier let alreadyImported = - showNameWithoutUniques (occName (unLoc rdr)) `elem` - map (showNameWithoutUniques @OccName) (listify (const True) lies) + showNameWithoutUniques (occName (unLoc rdr)) + `elem` map (showNameWithoutUniques @OccName) (listify (const True) lies) when alreadyImported $ - lift (Left $ idnetifier <> " already imported") + lift (Left $ idnetifier <> " already imported") let lie = L src $ IEName rdr x = L top $ IEVar noExtField lie - if x `elem` lies then lift (Left $ idnetifier <> " already imported") else do + if x `elem` lies + then lift (Left $ idnetifier <> " already imported") + else do when hasSibling $ - addTrailingCommaT (last lies) + addTrailingCommaT (last lies) addSimpleAnnT x (DP (0, if hasSibling then 1 else 0)) [] addSimpleAnnT rdr dp00 $ unqalDP $ hasParen idnetifier -- 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 + transferAnn (L l' lies) (L l' [x]) id return $ L l it {ideclHiding = Just (hide, L l' $ lies ++ [x])} extendImportTopLevel _ _ _ = lift $ Left "Unable to extend the import list" @@ -281,8 +286,8 @@ extendImportViaParent df parent child (L l it@ImportDecl {..}) childRdr <- liftParseAST df child let alreadyImported = - showNameWithoutUniques(occName (unLoc childRdr)) `elem` - map (showNameWithoutUniques @OccName) (listify (const True) lies') + showNameWithoutUniques (occName (unLoc childRdr)) + `elem` map (showNameWithoutUniques @OccName) (listify (const True) lies') when alreadyImported $ lift (Left $ child <> " already included in " <> parent <> " imports") @@ -331,94 +336,103 @@ unqalDP paren = (G AnnVal, dp00) ------------------------------------------------------------------------------ + -- | Hide a symbol from import declaration hideSymbol :: - String -> LImportDecl GhcPs -> Rewrite + 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 + 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" + error "cannot happen" extendHiding :: - String -> - LImportDecl GhcPs -> - Maybe (Located [LIE GhcPs]) -> - DynFlags -> - TransformT (Either String) (LImportDecl GhcPs) + 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 - 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 + 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 + 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 -> - LImportDecl GhcPs -> - Located [LIE GhcPs] -> - DynFlags -> - TransformT (Either String) (LImportDecl GhcPs) -deleteFromImport (T.pack -> symbol) (L l idecl) llies@(L lieLoc lies) _ =do - let edited = L lieLoc deletedLies - lidecl' = L l $ idecl + String -> + LImportDecl GhcPs -> + Located [LIE GhcPs] -> + DynFlags -> + TransformT (Either String) (LImportDecl GhcPs) +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) } - -- avoid import A (foo,) - whenJust (lastMaybe deletedLies) removeTrailingCommaT - 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 lidecl' - where - deletedLies = - mapMaybe killLie lies - killLie :: LIE GhcPs -> Maybe (LIE GhcPs) - killLie v@(L _ (IEVar _ (L _ (unqualIEWrapName -> nam)))) - | nam == symbol = Nothing - | otherwise = Just v - killLie v@(L _ (IEThingAbs _ (L _ (unqualIEWrapName -> nam)))) - | nam == symbol = Nothing - | otherwise = Just v - - 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) . unqualIEWrapName . unLoc) cons) - (filter ((/= symbol) . T.pack . unpackFS . flLabel . unLoc) flds) - killLie v = Just v + -- avoid import A (foo,) + whenJust (lastMaybe deletedLies) removeTrailingCommaT + 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 lidecl' + where + deletedLies = + mapMaybe killLie lies + killLie :: LIE GhcPs -> Maybe (LIE GhcPs) + killLie v@(L _ (IEVar _ (L _ (unqualIEWrapName -> nam)))) + | nam == symbol = Nothing + | otherwise = Just v + killLie v@(L _ (IEThingAbs _ (L _ (unqualIEWrapName -> nam)))) + | nam == symbol = Nothing + | otherwise = Just v + 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) . unqualIEWrapName . unLoc) cons) + (filter ((/= symbol) . T.pack . unpackFS . flLabel . unLoc) flds) + killLie v = Just v -- | Insert a import declaration with at most one symbol -- @@ -476,5 +490,12 @@ newImport modName mSymbol mQual hiding (L _ HsModule {..}) = do [(G AnnImport, DP (1, indentation - 1))] pure idecl +newUnqualImport :: String -> String -> Bool -> ParsedSource -> Maybe Rewrite +newUnqualImport modName symbol = newImport modName (Just symbol) Nothing + +newImportAll :: String -> ParsedSource -> Maybe Rewrite +newImportAll modName = newImport modName Nothing Nothing False + +-- | Insert "import Prelude hiding (symbol)" hideImplicitPreludeSymbol :: String -> ParsedSource -> Maybe Rewrite -hideImplicitPreludeSymbol symbol = newImport "Prelude" (Just symbol) Nothing True +hideImplicitPreludeSymbol symbol = newUnqualImport "Prelude" symbol True From e2521c7301fa32288597a9247538476718edd7af Mon Sep 17 00:00:00 2001 From: Potato Hatsue <1793913507@qq.com> Date: Wed, 24 Feb 2021 10:57:20 +0800 Subject: [PATCH 4/4] Reformat ExactPrint --- .../IDE/Plugin/CodeAction/ExactPrint.hs | 293 +++++++++--------- 1 file changed, 146 insertions(+), 147 deletions(-) diff --git a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs index 23a2436ff7..7f591a47d4 100644 --- a/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs +++ b/ghcide/src/Development/IDE/Plugin/CodeAction/ExactPrint.hs @@ -3,23 +3,22 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} -module Development.IDE.Plugin.CodeAction.ExactPrint - ( Rewrite (..), - rewriteToEdit, - rewriteToWEdit, - transferAnn, - - -- * Utilities - appendConstraint, - extendImport, - hideImplicitPreludeSymbol, - hideSymbol, - liftParseAST, - newImport, - newUnqualImport, - newImportAll, - ) -where +module Development.IDE.Plugin.CodeAction.ExactPrint ( + Rewrite (..), + rewriteToEdit, + rewriteToWEdit, + transferAnn, + + -- * Utilities + appendConstraint, + extendImport, + hideImplicitPreludeSymbol, + hideSymbol, + liftParseAST, + newImport, + newUnqualImport, + newImportAll, +) where import Control.Applicative import Control.Monad @@ -92,8 +91,8 @@ rewriteToWEdit dflags uri anns r = do edits <- rewriteToEdit dflags anns r return $ WorkspaceEdit - { _changes = Just (fromList [(uri, List edits)]), - _documentChanges = Nothing + { _changes = Just (fromList [(uri, List edits)]) + , _documentChanges = Nothing } ------------------------------------------------------------------------------ @@ -122,12 +121,12 @@ fixParens openDP closeDP ctxt@(L _ elems) = do ) (mkAnnKey ctxt) return $ map dropHsParTy elems - where - parens = Map.fromList [(G AnnOpenP, dp00), (G AnnCloseP, dp00)] + where + parens = Map.fromList [(G AnnOpenP, dp00), (G AnnCloseP, dp00)] - dropHsParTy :: LHsType pass -> LHsType pass - dropHsParTy (L _ (HsParTy _ ty)) = ty - dropHsParTy other = other + dropHsParTy :: LHsType pass -> LHsType pass + dropHsParTy (L _ (HsParTy _ ty)) = ty + dropHsParTy other = other -- | Append a constraint at the end of a type context. -- If no context is present, a new one will be created. @@ -138,37 +137,37 @@ appendConstraint :: LHsType GhcPs -> Rewrite appendConstraint constraintT = go - where - go (L l it@HsQualTy {hst_ctxt = L l' ctxt}) = Rewrite l $ \df -> do - constraint <- liftParseAST df constraintT - setEntryDPT constraint (DP (0, 1)) - - -- Paren annotations are usually attached to the first and last constraints, - -- rather than to the constraint list itself, so to preserve them we need to reposition them - closeParenDP <- lookupAnn (G AnnCloseP) `mapM` lastMaybe ctxt - openParenDP <- lookupAnn (G AnnOpenP) `mapM` headMaybe ctxt - ctxt' <- fixParens (join openParenDP) (join closeParenDP) (L l' ctxt) - - addTrailingCommaT (last ctxt') - - return $ L l $ it {hst_ctxt = L l' $ ctxt' ++ [constraint]} - go (L _ HsForAllTy {hst_body}) = go hst_body - go (L _ (HsParTy _ ty)) = go ty - go (L l other) = Rewrite l $ \df -> do - -- there isn't a context, so we must create one - constraint <- liftParseAST df constraintT - lContext <- uniqueSrcSpanT - lTop <- uniqueSrcSpanT - let context = L lContext [constraint] - addSimpleAnnT context (DP (0, 0)) $ - (G AnnDarrow, DP (0, 1)) : - concat - [ [ (G AnnOpenP, dp00), - (G AnnCloseP, dp00) - ] - | hsTypeNeedsParens sigPrec $ unLoc constraint + where + go (L l it@HsQualTy{hst_ctxt = L l' ctxt}) = Rewrite l $ \df -> do + constraint <- liftParseAST df constraintT + setEntryDPT constraint (DP (0, 1)) + + -- Paren annotations are usually attached to the first and last constraints, + -- rather than to the constraint list itself, so to preserve them we need to reposition them + closeParenDP <- lookupAnn (G AnnCloseP) `mapM` lastMaybe ctxt + openParenDP <- lookupAnn (G AnnOpenP) `mapM` headMaybe ctxt + ctxt' <- fixParens (join openParenDP) (join closeParenDP) (L l' ctxt) + + addTrailingCommaT (last ctxt') + + return $ L l $ it{hst_ctxt = L l' $ ctxt' ++ [constraint]} + go (L _ HsForAllTy{hst_body}) = go hst_body + go (L _ (HsParTy _ ty)) = go ty + go (L l other) = Rewrite l $ \df -> do + -- there isn't a context, so we must create one + constraint <- liftParseAST df constraintT + lContext <- uniqueSrcSpanT + lTop <- uniqueSrcSpanT + let context = L lContext [constraint] + addSimpleAnnT context (DP (0, 0)) $ + (G AnnDarrow, DP (0, 1)) : + concat + [ [ (G AnnOpenP, dp00) + , (G AnnCloseP, dp00) ] - return $ L lTop $ HsQualTy noExtField context (L l other) + | hsTypeNeedsParens sigPrec $ unLoc constraint + ] + return $ L lTop $ HsQualTy noExtField context (L l other) liftParseAST :: ASTElement ast => DynFlags -> String -> TransformT (Either String) (Located ast) liftParseAST df s = case parseAST df "" s of @@ -220,9 +219,9 @@ extendImport mparent identifier lDecl@(L l _) = -- import A (foo) --> Error -- import A (bar) --> import A (bar, foo) extendImportTopLevel :: DynFlags -> String -> LImportDecl GhcPs -> TransformT (Either String) (LImportDecl GhcPs) -extendImportTopLevel df idnetifier (L l it@ImportDecl {..}) - | Just (hide, L l' lies) <- ideclHiding, - hasSibling <- not $ null lies = do +extendImportTopLevel df idnetifier (L l it@ImportDecl{..}) + | Just (hide, L l' lies) <- ideclHiding + , hasSibling <- not $ null lies = do src <- uniqueSrcSpanT top <- uniqueSrcSpanT rdr <- liftParseAST df idnetifier @@ -246,7 +245,7 @@ extendImportTopLevel df idnetifier (L l it@ImportDecl {..}) -- 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 it {ideclHiding = Just (hide, L l' $ lies ++ [x])} + return $ L l it{ideclHiding = Just (hide, L l' $ lies ++ [x])} extendImportTopLevel _ _ _ = lift $ Left "Unable to extend the import list" -- | Add an identifier with its parent to import list @@ -260,64 +259,64 @@ extendImportTopLevel _ _ _ = lift $ Left "Unable to extend the import list" -- import A (Foo, Bar) --> import A (Foo, Bar(Cons)) -- import A (Foo, Bar()) --> import A (Foo, Bar(Cons)) extendImportViaParent :: DynFlags -> String -> String -> LImportDecl GhcPs -> TransformT (Either String) (LImportDecl GhcPs) -extendImportViaParent df parent child (L l it@ImportDecl {..}) +extendImportViaParent df parent child (L l it@ImportDecl{..}) | Just (hide, L l' lies) <- ideclHiding = go hide l' [] lies - where - go :: Bool -> SrcSpan -> [LIE GhcPs] -> [LIE GhcPs] -> TransformT (Either String) (LImportDecl GhcPs) - go _hide _l' _pre ((L _ll' (IEThingAll _ (L _ ie))) : _xs) - | parent == unIEWrappedName ie = lift . Left $ child <> " already included in " <> parent <> " imports" - go hide l' pre (lAbs@(L ll' (IEThingAbs _ absIE@(L _ ie))) : xs) - -- ThingAbs ie => ThingWith ie child - | parent == unIEWrappedName ie = do - srcChild <- uniqueSrcSpanT - childRdr <- liftParseAST df child - let childLIE = L srcChild $ IEName childRdr - x :: LIE GhcPs = L ll' $ IEThingWith noExtField absIE NoIEWildcard [childLIE] [] - -- take anns from ThingAbs, and attatch parens to it - transferAnn lAbs x $ \old -> old {annsDP = annsDP old ++ [(G AnnOpenP, DP (0, 1)), (G AnnCloseP, dp00)]} - addSimpleAnnT childRdr dp00 [(G AnnVal, dp00)] - return $ L l it {ideclHiding = Just (hide, L l' $ reverse pre ++ [x] ++ xs)} - go hide l' pre ((L l'' (IEThingWith _ twIE@(L _ ie) _ lies' _)) : xs) - -- ThingWith ie lies' => ThingWith ie (lies' ++ [child]) - | parent == unIEWrappedName ie, - hasSibling <- not $ null lies' = - do - srcChild <- uniqueSrcSpanT - childRdr <- liftParseAST df child - - let alreadyImported = - showNameWithoutUniques (occName (unLoc childRdr)) - `elem` map (showNameWithoutUniques @OccName) (listify (const True) lies') - when alreadyImported $ - lift (Left $ child <> " already included in " <> parent <> " imports") - - when hasSibling $ - addTrailingCommaT (last lies') - let childLIE = L srcChild $ IEName childRdr - addSimpleAnnT childRdr (DP (0, if hasSibling then 1 else 0)) $ unqalDP $ hasParen child - return $ L l it {ideclHiding = Just (hide, L l' $ reverse pre ++ [L l'' (IEThingWith noExtField twIE NoIEWildcard (lies' ++ [childLIE]) [])] ++ xs)} - go hide l' pre (x : xs) = go hide l' (x : pre) xs - go hide l' pre [] - | hasSibling <- not $ null pre = do - -- [] => ThingWith parent [child] - l'' <- uniqueSrcSpanT - srcParent <- uniqueSrcSpanT + where + go :: Bool -> SrcSpan -> [LIE GhcPs] -> [LIE GhcPs] -> TransformT (Either String) (LImportDecl GhcPs) + go _hide _l' _pre ((L _ll' (IEThingAll _ (L _ ie))) : _xs) + | parent == unIEWrappedName ie = lift . Left $ child <> " already included in " <> parent <> " imports" + go hide l' pre (lAbs@(L ll' (IEThingAbs _ absIE@(L _ ie))) : xs) + -- ThingAbs ie => ThingWith ie child + | parent == unIEWrappedName ie = do + srcChild <- uniqueSrcSpanT + childRdr <- liftParseAST df child + let childLIE = L srcChild $ IEName childRdr + x :: LIE GhcPs = L ll' $ IEThingWith noExtField absIE NoIEWildcard [childLIE] [] + -- take anns from ThingAbs, and attatch parens to it + transferAnn lAbs x $ \old -> old{annsDP = annsDP old ++ [(G AnnOpenP, DP (0, 1)), (G AnnCloseP, dp00)]} + addSimpleAnnT childRdr dp00 [(G AnnVal, dp00)] + return $ L l it{ideclHiding = Just (hide, L l' $ reverse pre ++ [x] ++ xs)} + go hide l' pre ((L l'' (IEThingWith _ twIE@(L _ ie) _ lies' _)) : xs) + -- ThingWith ie lies' => ThingWith ie (lies' ++ [child]) + | parent == unIEWrappedName ie + , hasSibling <- not $ null lies' = + do srcChild <- uniqueSrcSpanT - parentRdr <- liftParseAST df parent childRdr <- liftParseAST df child + + let alreadyImported = + showNameWithoutUniques (occName (unLoc childRdr)) + `elem` map (showNameWithoutUniques @OccName) (listify (const True) lies') + when alreadyImported $ + lift (Left $ child <> " already included in " <> parent <> " imports") + when hasSibling $ - addTrailingCommaT (head pre) - let parentLIE = L srcParent $ IEName parentRdr - childLIE = L srcChild $ IEName childRdr - x :: LIE GhcPs = L l'' $ IEThingWith noExtField parentLIE NoIEWildcard [childLIE] [] - addSimpleAnnT parentRdr (DP (0, if hasSibling then 1 else 0)) $ unqalDP $ hasParen parent - addSimpleAnnT childRdr (DP (0, 0)) $ unqalDP $ hasParen child - addSimpleAnnT x (DP (0, 0)) [(G AnnOpenP, DP (0, 1)), (G AnnCloseP, DP (0, 0))] - -- Parens are attachted to `pre`, so if `pre` was empty previously, - -- we need change the ann key from `[]` to `:` to keep parens and other anns. - unless hasSibling $ - transferAnn (L l' $ reverse pre) (L l' [x]) id - return $ L l it {ideclHiding = Just (hide, L l' $ reverse pre ++ [x])} + addTrailingCommaT (last lies') + let childLIE = L srcChild $ IEName childRdr + addSimpleAnnT childRdr (DP (0, if hasSibling then 1 else 0)) $ unqalDP $ hasParen child + return $ L l it{ideclHiding = Just (hide, L l' $ reverse pre ++ [L l'' (IEThingWith noExtField twIE NoIEWildcard (lies' ++ [childLIE]) [])] ++ xs)} + go hide l' pre (x : xs) = go hide l' (x : pre) xs + go hide l' pre [] + | hasSibling <- not $ null pre = do + -- [] => ThingWith parent [child] + l'' <- uniqueSrcSpanT + srcParent <- uniqueSrcSpanT + srcChild <- uniqueSrcSpanT + parentRdr <- liftParseAST df parent + childRdr <- liftParseAST df child + when hasSibling $ + addTrailingCommaT (head pre) + let parentLIE = L srcParent $ IEName parentRdr + childLIE = L srcChild $ IEName childRdr + x :: LIE GhcPs = L l'' $ IEThingWith noExtField parentLIE NoIEWildcard [childLIE] [] + addSimpleAnnT parentRdr (DP (0, if hasSibling then 1 else 0)) $ unqalDP $ hasParen parent + addSimpleAnnT childRdr (DP (0, 0)) $ unqalDP $ hasParen child + addSimpleAnnT x (DP (0, 0)) [(G AnnOpenP, DP (0, 1)), (G AnnCloseP, DP (0, 0))] + -- Parens are attachted to `pre`, so if `pre` was empty previously, + -- we need change the ann key from `[]` to `:` to keep parens and other anns. + unless hasSibling $ + transferAnn (L l' $ reverse pre) (L l' [x]) id + return $ L l it{ideclHiding = Just (hide, L l' $ reverse pre ++ [x])} extendImportViaParent _ _ _ _ = lift $ Left "Unable to extend the import list via parent" unIEWrappedName :: IEWrappedName (IdP GhcPs) -> String @@ -340,7 +339,7 @@ unqalDP paren = -- | Hide a symbol from import declaration hideSymbol :: String -> LImportDecl GhcPs -> Rewrite -hideSymbol symbol lidecl@(L loc ImportDecl {..}) = +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) @@ -369,9 +368,9 @@ extendHiding symbol (L l idecls) mlies df = do addSimpleAnnT singleHide dp00 - [ (G AnnHiding, DP (0, 1)), - (G AnnOpenP, DP (0, 1)), - (G AnnCloseP, DP (0, 0)) + [ (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 @@ -383,9 +382,9 @@ extendHiding symbol (L l idecls) mlies df = do 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 + return $ L l idecls{ideclHiding = Just (True, L l' $ x : lies)} + where + isOperator = not . all isAlphaNum . occNameString . rdrNameOcc deleteFromImport :: String -> @@ -407,35 +406,35 @@ deleteFromImport (T.pack -> symbol) (L l idecl) llies@(L lieLoc lies) _ = do addSimpleAnnT edited dp00 - [ (G AnnOpenP, DP (0, 1)), - (G AnnCloseP, DP (0, 0)) + [ (G AnnOpenP, DP (0, 1)) + , (G AnnCloseP, DP (0, 0)) ] pure lidecl' - where - deletedLies = - mapMaybe killLie lies - killLie :: LIE GhcPs -> Maybe (LIE GhcPs) - killLie v@(L _ (IEVar _ (L _ (unqualIEWrapName -> nam)))) - | nam == symbol = Nothing - | otherwise = Just v - killLie v@(L _ (IEThingAbs _ (L _ (unqualIEWrapName -> nam)))) - | nam == symbol = Nothing - | otherwise = Just v - 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) . unqualIEWrapName . unLoc) cons) - (filter ((/= symbol) . T.pack . unpackFS . flLabel . unLoc) flds) - killLie v = Just v + where + deletedLies = + mapMaybe killLie lies + killLie :: LIE GhcPs -> Maybe (LIE GhcPs) + killLie v@(L _ (IEVar _ (L _ (unqualIEWrapName -> nam)))) + | nam == symbol = Nothing + | otherwise = Just v + killLie v@(L _ (IEThingAbs _ (L _ (unqualIEWrapName -> nam)))) + | nam == symbol = Nothing + | otherwise = Just v + 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) . unqualIEWrapName . unLoc) cons) + (filter ((/= symbol) . T.pack . unpackFS . flLabel . unLoc) flds) + killLie v = Just v -- | Insert a import declaration with at most one symbol --- + -- newImport "A" (Just "Bar(Cons)") Nothing False --> import A (Bar(Cons)) -- newImport "A" (Just "foo") Nothing True --> import A hiding (foo) -- newImport "A" Nothing (Just "Q") False --> import qualified A as Q @@ -453,7 +452,7 @@ newImport :: Bool -> ParsedSource -> Maybe Rewrite -newImport modName mSymbol mQual hiding (L _ HsModule {..}) = do +newImport modName mSymbol mQual hiding (L _ HsModule{..}) = do -- TODO (berberman): if the previous line is module name and there is no other imports, -- 'AnnWhere' will be crowded out to the next line, which is a bug let predLine old = @@ -470,8 +469,8 @@ newImport modName mSymbol mQual hiding (L _ HsModule {..}) = do pure $ Rewrite ran $ \df -> do let symImp - | Just symbol <- mSymbol, - symOcc <- mkVarOcc symbol = + | Just symbol <- mSymbol + , symOcc <- mkVarOcc symbol = "(" <> showSDoc df (parenSymOcc symOcc $ ppr symOcc) <> ")" | otherwise = "" impStmt =