diff --git a/ghcide/src/Development/IDE/GHC/ExactPrint.hs b/ghcide/src/Development/IDE/GHC/ExactPrint.hs index 3688f80a82..13ec409ab0 100644 --- a/ghcide/src/Development/IDE/GHC/ExactPrint.hs +++ b/ghcide/src/Development/IDE/GHC/ExactPrint.hs @@ -1,19 +1,19 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DerivingVia #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE TypeFamilies #-} module Development.IDE.GHC.ExactPrint ( Graft(..), graftDecls, graftDeclsWithM, annotate, + annotateDecl, hoistGraft, graftWithM, - graftWithSmallestM, - graftSmallestDecls, + genericGraftWithSmallestM, + genericGraftWithLargestM, graftSmallestDeclsWithM, transform, transformM, @@ -27,6 +27,9 @@ module Development.IDE.GHC.ExactPrint TransformT, Anns, Annotate, + mkBindListT, + setPrecedingLinesT, + everywhereM', ) where @@ -63,7 +66,8 @@ import Parser (parseIdentifier) import Data.Traversable (for) import Data.Foldable (Foldable(fold)) import Data.Bool (bool) -import Data.Monoid (All(All)) +import Data.Monoid (All(All), Any(Any)) +import Data.Functor.Compose (Compose(Compose)) #if __GLASGOW_HASKELL__ == 808 import Control.Arrow #endif @@ -195,7 +199,7 @@ needsParensSpace ExplicitSum{} = (All False, All False) needsParensSpace HsCase{} = (All False, All False) needsParensSpace HsIf{} = (All False, All False) needsParensSpace HsMultiIf{} = (All False, All False) -needsParensSpace HsLet{} = (All False, All False) +needsParensSpace HsLet{} = (All False, All True) needsParensSpace HsDo{} = (All False, All False) needsParensSpace ExplicitList{} = (All False, All False) needsParensSpace RecordCon{} = (All False, All False) @@ -286,30 +290,56 @@ graftWithM dst trans = Graft $ \dflags a -> do ) a -graftWithSmallestM :: - forall ast m a. - (Fail.MonadFail m, Data a, ASTElement ast) => +-- | A generic query intended to be used for calling 'smallestM' and +-- 'largestM'. If the current node is a 'Located', returns whether or not the +-- given 'SrcSpan' is a subspan. For all other nodes, returns 'Nothing', which +-- indicates uncertainty. The search strategy in 'smallestM' et al. will +-- continue searching uncertain nodes. +genericIsSubspan :: + forall ast. + Typeable ast => + -- | The type of nodes we'd like to consider. + Proxy (Located ast) -> SrcSpan -> - (Located ast -> TransformT m (Maybe (Located ast))) -> + GenericQ (Maybe Bool) +genericIsSubspan _ dst = mkQ Nothing $ \case + (L span _ :: Located ast) -> Just $ dst `isSubspanOf` span + +-- | Run the given transformation only on the smallest node in the tree that +-- contains the 'SrcSpan'. +genericGraftWithSmallestM :: + forall m a ast. + (Monad m, Data a, Typeable ast) => + -- | The type of nodes we'd like to consider when finding the smallest. + Proxy (Located ast) -> + SrcSpan -> + (DynFlags -> GenericM (TransformT m)) -> Graft m a -graftWithSmallestM dst trans = Graft $ \dflags a -> do - everywhereM' - ( mkM $ - \case - val@(L src _ :: Located ast) - | dst `isSubspanOf` src -> do - mval <- trans val - case mval of - Just val' -> do - (anns, val'') <- - hoistTransform (either Fail.fail pure) $ - annotate dflags True $ maybeParensAST val' - modifyAnnsT $ mappend anns - pure val'' - Nothing -> pure val - l -> pure l - ) - a +genericGraftWithSmallestM proxy dst trans = Graft $ \dflags -> + smallestM (genericIsSubspan proxy dst) (trans dflags) + +-- | Run the given transformation only on the largest node in the tree that +-- contains the 'SrcSpan'. +genericGraftWithLargestM :: + forall m a ast. + (Monad m, Data a, Typeable ast) => + -- | The type of nodes we'd like to consider when finding the largest. + Proxy (Located ast) -> + SrcSpan -> + (DynFlags -> GenericM (TransformT m)) -> + Graft m a +genericGraftWithLargestM proxy dst trans = Graft $ \dflags -> + largestM (genericIsSubspan proxy dst) (trans dflags) + + +-- | Lift a function that replaces a value with several values into a generic +-- function. The result doesn't perform any searching, so should be driven via +-- 'everywhereM' or friends. +-- +-- The 'Int' argument is the index in the list being bound. +mkBindListT :: forall b m. (Typeable b, Data b, Monad m) => (Int -> b -> m [b]) -> GenericM m +mkBindListT f = mkM $ fmap join . traverse (uncurry f) . zip [0..] + graftDecls :: forall a. @@ -319,32 +349,13 @@ graftDecls :: Graft (Either String) a graftDecls dst decs0 = Graft $ \dflags a -> do decs <- forM decs0 $ \decl -> do - (anns, decl') <- annotateDecl dflags decl - modifyAnnsT $ mappend anns - pure decl' + annotateDecl dflags decl let go [] = DL.empty go (L src e : rest) | src == dst = DL.fromList decs <> DL.fromList rest | otherwise = DL.singleton (L src e) <> go rest modifyDeclsT (pure . DL.toList . go) a -graftSmallestDecls :: - forall a. - (HasDecls a) => - SrcSpan -> - [LHsDecl GhcPs] -> - Graft (Either String) a -graftSmallestDecls dst decs0 = Graft $ \dflags a -> do - decs <- forM decs0 $ \decl -> do - (anns, decl') <- annotateDecl dflags decl - modifyAnnsT $ mappend anns - pure decl' - let go [] = DL.empty - go (L src e : rest) - | dst `isSubspanOf` src = DL.fromList decs <> DL.fromList rest - | otherwise = DL.singleton (L src e) <> go rest - modifyDeclsT (pure . DL.toList . go) a - graftSmallestDeclsWithM :: forall a. (HasDecls a) => @@ -356,11 +367,8 @@ graftSmallestDeclsWithM dst toDecls = Graft $ \dflags a -> do go (e@(L src _) : rest) | dst `isSubspanOf` src = toDecls e >>= \case Just decs0 -> do - decs <- forM decs0 $ \decl -> do - (anns, decl') <- - annotateDecl dflags decl - modifyAnnsT $ mappend anns - pure decl' + decs <- forM decs0 $ \decl -> + annotateDecl dflags decl pure $ DL.fromList decs <> DL.fromList rest Nothing -> (DL.singleton e <>) <$> go rest | otherwise = (DL.singleton e <>) <$> go rest @@ -377,12 +385,9 @@ graftDeclsWithM dst toDecls = Graft $ \dflags a -> do go (e@(L src _) : rest) | src == dst = toDecls e >>= \case Just decs0 -> do - decs <- forM decs0 $ \decl -> do - (anns, decl') <- - hoistTransform (either Fail.fail pure) $ - annotateDecl dflags decl - modifyAnnsT $ mappend anns - pure decl' + decs <- forM decs0 $ \decl -> + hoistTransform (either Fail.fail pure) $ + annotateDecl dflags decl pure $ DL.fromList decs <> DL.fromList rest Nothing -> (DL.singleton e <>) <$> go rest | otherwise = (DL.singleton e <>) <$> go rest @@ -461,7 +466,7 @@ annotate dflags needs_space ast = do pure (anns', expr') -- | Given an 'LHsDecl', compute its exactprint annotations. -annotateDecl :: DynFlags -> LHsDecl GhcPs -> TransformT (Either String) (Anns, LHsDecl GhcPs) +annotateDecl :: DynFlags -> LHsDecl GhcPs -> TransformT (Either String) (LHsDecl GhcPs) -- The 'parseDecl' function fails to parse 'FunBind' 'ValD's which contain -- multiple matches. To work around this, we split the single -- 'FunBind'-of-multiple-'Match'es into multiple 'FunBind's-of-one-'Match', @@ -474,24 +479,23 @@ annotateDecl dflags let set_matches matches = ValD ext fb { fun_matches = mg { mg_alts = L alt_src matches }} - (anns', alts') <- fmap unzip $ for (zip [0..] alts) $ \(ix :: Int, alt) -> do + (anns', alts') <- fmap unzip $ for alts $ \alt -> do uniq <- show <$> uniqueSrcSpanT let rendered = render dflags $ set_matches [alt] lift (mapLeft show $ parseDecl dflags uniq rendered) >>= \case (ann, L _ (ValD _ FunBind { fun_matches = MG { mg_alts = L _ [alt']}})) - -> pure (bool id (setPrecedingLines alt' 1 0) (ix /= 0) ann, alt') + -> pure (setPrecedingLines alt' 1 0 ann, alt') _ -> lift $ Left "annotateDecl: didn't parse a single FunBind match" - let expr' = L src $ set_matches alts' - anns'' = setPrecedingLines expr' 1 0 $ fold anns' - - pure (anns'', expr') + modifyAnnsT $ mappend $ fold anns' + pure $ L src $ set_matches alts' annotateDecl dflags ast = do uniq <- show <$> uniqueSrcSpanT let rendered = render dflags ast (anns, expr') <- lift $ mapLeft show $ parseDecl dflags uniq rendered let anns' = setPrecedingLines expr' 1 0 anns - pure (anns', expr') + modifyAnnsT $ mappend anns' + pure expr' ------------------------------------------------------------------------------ @@ -504,3 +508,77 @@ render dflags = showSDoc dflags . ppr -- | Put parentheses around an expression if required. parenthesize :: LHsExpr GhcPs -> LHsExpr GhcPs parenthesize = parenthesizeHsExpr appPrec + + +------------------------------------------------------------------------------ +-- Custom SYB machinery +------------------------------------------------------------------------------ + +-- | Generic monadic transformations that return side-channel data. +type GenericMQ r m = forall a. Data a => a -> m (r, a) + +------------------------------------------------------------------------------ +-- | Apply the given 'GenericM' at all every node whose children fail the +-- 'GenericQ', but which passes the query itself. +-- +-- The query must be a monotonic function when it returns 'Just'. That is, if +-- @s@ is a subtree of @t@, @q t@ should return @Just True@ if @q s@ does. It +-- is the True-to-false edge of the query that triggers the transformation. +-- +-- Why is the query a @Maybe Bool@? The GHC AST intersperses 'Located' nodes +-- with data nodes, so for any given node we can only definitely return an +-- answer if it's a 'Located'. See 'genericIsSubspan' for how this parameter is +-- used. +smallestM :: forall m. Monad m => GenericQ (Maybe Bool) -> GenericM m -> GenericM m +smallestM q f = fmap snd . go + where + go :: GenericMQ Any m + go x = do + case q x of + Nothing -> gmapMQ go x + Just True -> do + it@(r, x') <- gmapMQ go x + case r of + Any True -> pure it + Any False -> fmap (Any True,) $ f x' + Just False -> pure (mempty, x) + +------------------------------------------------------------------------------ +-- | Apply the given 'GenericM' at every node that passes the 'GenericQ', but +-- don't descend into children if the query matches. Because this traversal is +-- root-first, this policy will find the largest subtrees for which the query +-- holds true. +-- +-- Why is the query a @Maybe Bool@? The GHC AST intersperses 'Located' nodes +-- with data nodes, so for any given node we can only definitely return an +-- answer if it's a 'Located'. See 'genericIsSubspan' for how this parameter is +-- used. +largestM :: forall m. Monad m => GenericQ (Maybe Bool) -> GenericM m -> GenericM m +largestM q f = go + where + go :: GenericM m + go x = do + case q x of + Just True -> f x + Just False -> pure x + Nothing -> gmapM go x + +newtype MonadicQuery r m a = MonadicQuery + { runMonadicQuery :: m (r, a) + } + deriving stock (Functor) + deriving Applicative via Compose m ((,) r) + + +------------------------------------------------------------------------------ +-- | Like 'gmapM', but also returns side-channel data. +gmapMQ :: + forall f r a. (Monoid r, Data a, Applicative f) => + (forall d. Data d => d -> f (r, d)) -> + a -> + f (r, a) +gmapMQ f = runMonadicQuery . gfoldl k pure + where + k :: Data d => MonadicQuery r f (d -> b) -> d -> MonadicQuery r f b + k c x = c <*> MonadicQuery (f x) + diff --git a/plugins/hls-tactics-plugin/src/Wingman/GHC.hs b/plugins/hls-tactics-plugin/src/Wingman/GHC.hs index ccb68d1e5f..d4105f3555 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/GHC.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/GHC.hs @@ -176,6 +176,14 @@ allOccNames = everything (<>) $ mkQ mempty $ \case a -> S.singleton a +------------------------------------------------------------------------------ +-- | Unpack the relevant parts of a 'Match' +pattern AMatch :: HsMatchContext (NameOrRdrName (IdP GhcPs)) -> [Pat GhcPs] -> HsExpr GhcPs -> Match GhcPs (LHsExpr GhcPs) +pattern AMatch ctx pats body <- + Match { m_ctxt = ctx + , m_pats = fmap fromPatCompatPs -> pats + , m_grhss = UnguardedRHSs body + } ------------------------------------------------------------------------------ @@ -183,10 +191,7 @@ allOccNames = everything (<>) $ mkQ mempty $ \case pattern Lambda :: [Pat GhcPs] -> HsExpr GhcPs -> HsExpr GhcPs pattern Lambda pats body <- HsLam _ - (MG {mg_alts = L _ [L _ - (Match { m_pats = fmap fromPatCompatPs -> pats - , m_grhss = UnguardedRHSs body - })]}) + (MG {mg_alts = L _ [L _ (AMatch _ pats body) ]}) where -- If there are no patterns to bind, just stick in the body Lambda [] body = body diff --git a/plugins/hls-tactics-plugin/src/Wingman/Plugin.hs b/plugins/hls-tactics-plugin/src/Wingman/Plugin.hs index d2c2df7e2b..80fdafe9a9 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/Plugin.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/Plugin.hs @@ -7,21 +7,16 @@ module Wingman.Plugin , TacticCommand (..) ) where -import Bag (bagToList, listToBag) import Control.Exception (evaluate) import Control.Monad import Control.Monad.Trans import Control.Monad.Trans.Maybe import Data.Aeson import Data.Bifunctor (first) -import Data.Data (Data) import Data.Foldable (for_) -import Data.Generics.Aliases (mkQ) -import Data.Generics.Schemes (everything) import Data.Maybe -import Data.Monoid +import Data.Proxy (Proxy(..)) import qualified Data.Text as T -import Data.Traversable import Development.IDE.Core.Shake (IdeState (..)) import Development.IDE.GHC.Compat import Development.IDE.GHC.ExactPrint @@ -158,110 +153,49 @@ graftHole -> Graft (Either String) ParsedSource graftHole span rtr | _jIsTopHole (rtr_jdg rtr) - = graftSmallestDeclsWithM span - $ graftDecl span $ \pats -> - splitToDecl (fst $ last $ ctxDefiningFuncs $ rtr_ctx rtr) - $ iterateSplit - $ mkFirstAgda (fmap unXPat pats) - $ unLoc - $ rtr_extract rtr + = genericGraftWithSmallestM (Proxy @(Located [LMatch GhcPs (LHsExpr GhcPs)])) span $ \dflags -> + everywhereM' + $ mkBindListT $ \ix -> + graftDecl dflags span ix $ \name pats -> + splitToDecl (occName name) + $ iterateSplit + $ mkFirstAgda (fmap unXPat pats) + $ unLoc + $ rtr_extract rtr graftHole span rtr = graft span $ rtr_extract rtr ------------------------------------------------------------------------------- --- | Merge in the 'Match'es of a 'FunBind' into a 'HsDecl'. Used to perform --- agda-style case splitting in which we need to separate one 'Match' into --- many, without affecting any matches which might exist but don't need to be --- split. -mergeFunBindMatches - :: ([Pat GhcPs] -> LHsDecl GhcPs) - -> SrcSpan - -> HsBind GhcPs - -> Either String (HsBind GhcPs) -mergeFunBindMatches make_decl span - (fb@FunBind {fun_matches = mg@MG {mg_alts = L alts_src alts}}) = - pure $ fb - { fun_matches = mg - { mg_alts = L alts_src $ do - alt@(L alt_src match) <- alts - case span `isSubspanOf` alt_src of - True -> do - let pats = fmap fromPatCompatPs $ m_pats match - L _ (ValD _ (FunBind {fun_matches = MG - {mg_alts = L _ to_add}})) = make_decl pats - to_add - False -> pure alt - } - } -mergeFunBindMatches _ _ _ = - Left "mergeFunBindMatches: called on something that isnt a funbind" - - -throwError :: String -> TransformT (Either String) a -throwError = lift . Left - - ------------------------------------------------------------------------------ -- | Helper function to route 'mergeFunBindMatches' into the right place in an -- AST --- correctly dealing with inserting into instance declarations. graftDecl - :: SrcSpan - -> ([Pat GhcPs] -> LHsDecl GhcPs) - -> LHsDecl GhcPs - -> TransformT (Either String) (Maybe [LHsDecl GhcPs]) -graftDecl span - make_decl - (L src (ValD ext fb)) - = either throwError (pure . Just . pure . L src . ValD ext) $ - mergeFunBindMatches make_decl span fb --- TODO(sandy): add another case for default methods in class definitions -graftDecl span - make_decl - (L src (InstD ext - cid@ClsInstD{cid_inst = - cidi@ClsInstDecl{cid_sigs = _sigs, cid_binds = binds}})) - = do - binds' <- - for (bagToList binds) $ \b@(L bsrc bind) -> do - case bind of - fb@FunBind{} | span `isSubspanOf` bsrc -> - either throwError (pure . L bsrc) $ - mergeFunBindMatches make_decl span fb - _ -> pure b - - pure $ Just $ pure $ L src $ InstD ext $ cid - { cid_inst = cidi - { cid_binds = listToBag binds' - } - } -graftDecl span _ x = do - traceMX "biggest" $ - unsafeRender $ - locateBiggest @(Match GhcPs (LHsExpr GhcPs)) span x - traceMX "first" $ - unsafeRender $ - locateFirst @(Match GhcPs (LHsExpr GhcPs)) x - throwError "graftDecl: don't know about this AST form" + :: DynFlags + -> SrcSpan + -> Int + -> (RdrName -> [Pat GhcPs] -> LHsDecl GhcPs) + -> LMatch GhcPs (LHsExpr GhcPs) + -> TransformT (Either String) [LMatch GhcPs (LHsExpr GhcPs)] +graftDecl dflags dst ix make_decl (L src (AMatch (FunRhs (L _ name) _ _) pats _)) + | dst `isSubspanOf` src = do + L _ dec <- annotateDecl dflags $ make_decl name pats + case dec of + ValD _ (FunBind { fun_matches = MG { mg_alts = L _ alts@(first_match : _)} + }) -> do + -- For whatever reason, ExactPrint annotates newlines to the ends of + -- case matches and type signatures, but only allows us to insert + -- them at the beginning of those things. Thus, we need want to + -- insert a preceeding newline (done in 'annotateDecl') on all + -- matches, except for the first one --- since it gets its newline + -- from the line above. + when (ix == 0) $ + setPrecedingLinesT first_match 0 0 + pure alts + _ -> lift $ Left "annotateDecl didn't produce a funbind" +graftDecl _ _ _ _ x = pure $ pure x fromMaybeT :: Functor m => a -> MaybeT m a -> m a fromMaybeT def = fmap (fromMaybe def) . runMaybeT - -locateBiggest :: (Data r, Data a) => SrcSpan -> a -> Maybe r -locateBiggest ss x = getFirst $ everything (<>) - ( mkQ mempty $ \case - L span r | ss `isSubspanOf` span -> pure r - _ -> mempty - ) x - - -locateFirst :: (Data r, Data a) => a -> Maybe r -locateFirst x = getFirst $ everything (<>) - ( mkQ mempty $ \case - r -> pure r - ) x - - diff --git a/plugins/hls-tactics-plugin/src/Wingman/Types.hs b/plugins/hls-tactics-plugin/src/Wingman/Types.hs index bc6e0a8290..ab8234deb6 100644 --- a/plugins/hls-tactics-plugin/src/Wingman/Types.hs +++ b/plugins/hls-tactics-plugin/src/Wingman/Types.hs @@ -135,6 +135,9 @@ instance Show Class where instance Show (HsExpr GhcPs) where show = unsafeRender +instance Show (HsDecl GhcPs) where + show = unsafeRender + instance Show (Pat GhcPs) where show = unsafeRender diff --git a/plugins/hls-tactics-plugin/test/CodeAction/DestructSpec.hs b/plugins/hls-tactics-plugin/test/CodeAction/DestructSpec.hs index 2757f3b56a..e5c6636b3a 100644 --- a/plugins/hls-tactics-plugin/test/CodeAction/DestructSpec.hs +++ b/plugins/hls-tactics-plugin/test/CodeAction/DestructSpec.hs @@ -22,8 +22,15 @@ spec = do destructTest "a" 7 25 "SplitPattern.hs" describe "layout" $ do - destructTest "b" 4 3 "LayoutBind.hs" - destructTest "b" 2 15 "LayoutDollarApp.hs" - destructTest "b" 2 18 "LayoutOpApp.hs" - destructTest "b" 2 14 "LayoutLam.hs" + destructTest "b" 4 3 "LayoutBind.hs" + destructTest "b" 2 15 "LayoutDollarApp.hs" + destructTest "b" 2 18 "LayoutOpApp.hs" + destructTest "b" 2 14 "LayoutLam.hs" + destructTest "x" 11 15 "LayoutSplitWhere.hs" + destructTest "x" 3 12 "LayoutSplitClass.hs" + destructTest "b" 3 9 "LayoutSplitGuard.hs" + destructTest "b" 4 13 "LayoutSplitLet.hs" + destructTest "a" 4 7 "LayoutSplitIn.hs" + destructTest "a" 4 31 "LayoutSplitViewPat.hs" + destructTest "a" 7 17 "LayoutSplitPattern.hs" diff --git a/plugins/hls-tactics-plugin/test/golden/AutoThetaFix.hs.expected b/plugins/hls-tactics-plugin/test/golden/AutoThetaFix.hs.expected index 21a5b69691..db63e2bc18 100644 --- a/plugins/hls-tactics-plugin/test/golden/AutoThetaFix.hs.expected +++ b/plugins/hls-tactics-plugin/test/golden/AutoThetaFix.hs.expected @@ -2,6 +2,12 @@ {-# LANGUAGE UndecidableInstances #-} data Fix f a = Fix (f (Fix f a)) -instance (Functor f, Functor (Fix f)) => Functor (Fix f) where + +instance ( Functor f + -- FIXME(sandy): Unfortunately, the recursion tactic fails to fire + -- on this case. By explicitly adding the @Functor (Fix f)@ + -- dictionary, we can get Wingman to generate the right definition. + , Functor (Fix f) + ) => Functor (Fix f) where fmap fab (Fix fffa) = Fix (fmap (fmap fab) fffa) diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenFmapTree.hs.expected b/plugins/hls-tactics-plugin/test/golden/GoldenFmapTree.hs.expected index 64eef825fa..8f20041e20 100644 --- a/plugins/hls-tactics-plugin/test/golden/GoldenFmapTree.hs.expected +++ b/plugins/hls-tactics-plugin/test/golden/GoldenFmapTree.hs.expected @@ -1,4 +1,5 @@ data Tree a = Leaf a | Branch (Tree a) (Tree a) + instance Functor Tree where - fmap fab (Leaf a) = Leaf (fab a) - fmap fab (Branch ta2 ta3) = Branch (fmap fab ta2) (fmap fab ta3) + fmap fab (Leaf a) = Leaf (fab a) + fmap fab (Branch ta2 ta3) = Branch (fmap fab ta2) (fmap fab ta3) diff --git a/plugins/hls-tactics-plugin/test/golden/GoldenIdentityFunctor.hs.expected b/plugins/hls-tactics-plugin/test/golden/GoldenIdentityFunctor.hs.expected index 757bc8347a..5c509d6507 100644 --- a/plugins/hls-tactics-plugin/test/golden/GoldenIdentityFunctor.hs.expected +++ b/plugins/hls-tactics-plugin/test/golden/GoldenIdentityFunctor.hs.expected @@ -1,3 +1,3 @@ data Ident a = Ident a instance Functor Ident where - fmap fab (Ident a) = Ident (fab a) + fmap fab (Ident a) = Ident (fab a) diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitClass.hs b/plugins/hls-tactics-plugin/test/golden/LayoutSplitClass.hs new file mode 100644 index 0000000000..c082169c7b --- /dev/null +++ b/plugins/hls-tactics-plugin/test/golden/LayoutSplitClass.hs @@ -0,0 +1,4 @@ +class Test a where + test :: Bool -> a + test x = _ + diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitClass.hs.expected b/plugins/hls-tactics-plugin/test/golden/LayoutSplitClass.hs.expected new file mode 100644 index 0000000000..a1e34d3db6 --- /dev/null +++ b/plugins/hls-tactics-plugin/test/golden/LayoutSplitClass.hs.expected @@ -0,0 +1,5 @@ +class Test a where + test :: Bool -> a + test False = _ + test True = _ + diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitGuard.hs b/plugins/hls-tactics-plugin/test/golden/LayoutSplitGuard.hs new file mode 100644 index 0000000000..be2d0d30f5 --- /dev/null +++ b/plugins/hls-tactics-plugin/test/golden/LayoutSplitGuard.hs @@ -0,0 +1,3 @@ +test :: Bool -> Bool -> Bool +test a b + | a = _ diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitGuard.hs.expected b/plugins/hls-tactics-plugin/test/golden/LayoutSplitGuard.hs.expected new file mode 100644 index 0000000000..3d68d8ac96 --- /dev/null +++ b/plugins/hls-tactics-plugin/test/golden/LayoutSplitGuard.hs.expected @@ -0,0 +1,5 @@ +test :: Bool -> Bool -> Bool +test a b + | a = (case b of + False -> _ + True -> _) diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitIn.hs b/plugins/hls-tactics-plugin/test/golden/LayoutSplitIn.hs new file mode 100644 index 0000000000..ce6e0341c4 --- /dev/null +++ b/plugins/hls-tactics-plugin/test/golden/LayoutSplitIn.hs @@ -0,0 +1,5 @@ +test :: a +test = + let a = (1,"bbb") + in _ + diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitIn.hs.expected b/plugins/hls-tactics-plugin/test/golden/LayoutSplitIn.hs.expected new file mode 100644 index 0000000000..f6f3ffceab --- /dev/null +++ b/plugins/hls-tactics-plugin/test/golden/LayoutSplitIn.hs.expected @@ -0,0 +1,5 @@ +test :: a +test = + let a = (1,"bbb") + in case a of { (i, l_c) -> _ } + diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitLet.hs b/plugins/hls-tactics-plugin/test/golden/LayoutSplitLet.hs new file mode 100644 index 0000000000..71529d7dd3 --- /dev/null +++ b/plugins/hls-tactics-plugin/test/golden/LayoutSplitLet.hs @@ -0,0 +1,6 @@ +test :: a +test = + let t :: Bool -> a + t b = _ + in _ + diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitLet.hs.expected b/plugins/hls-tactics-plugin/test/golden/LayoutSplitLet.hs.expected new file mode 100644 index 0000000000..a042cb3b13 --- /dev/null +++ b/plugins/hls-tactics-plugin/test/golden/LayoutSplitLet.hs.expected @@ -0,0 +1,7 @@ +test :: a +test = + let t :: Bool -> a + t False = _ + t True = _ + in _ + diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitPattern.hs b/plugins/hls-tactics-plugin/test/golden/LayoutSplitPattern.hs new file mode 100644 index 0000000000..3cabb3c64b --- /dev/null +++ b/plugins/hls-tactics-plugin/test/golden/LayoutSplitPattern.hs @@ -0,0 +1,8 @@ +{-# LANGUAGE PatternSynonyms #-} + +pattern Blah :: a -> Maybe a +pattern Blah a = Just a + +test :: Maybe Bool -> a +test (Blah a) = _ + diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitPattern.hs.expected b/plugins/hls-tactics-plugin/test/golden/LayoutSplitPattern.hs.expected new file mode 100644 index 0000000000..e99d112e6f --- /dev/null +++ b/plugins/hls-tactics-plugin/test/golden/LayoutSplitPattern.hs.expected @@ -0,0 +1,9 @@ +{-# LANGUAGE PatternSynonyms #-} + +pattern Blah :: a -> Maybe a +pattern Blah a = Just a + +test :: Maybe Bool -> a +test (Blah False) = _ +test (Blah True) = _ + diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitViewPat.hs b/plugins/hls-tactics-plugin/test/golden/LayoutSplitViewPat.hs new file mode 100644 index 0000000000..6baed55abd --- /dev/null +++ b/plugins/hls-tactics-plugin/test/golden/LayoutSplitViewPat.hs @@ -0,0 +1,5 @@ +{-# LANGUAGE ViewPatterns #-} + +splitLookup :: [(Int, String)] -> String +splitLookup (lookup 5 -> a) = _ + diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitViewPat.hs.expected b/plugins/hls-tactics-plugin/test/golden/LayoutSplitViewPat.hs.expected new file mode 100644 index 0000000000..81d49a4ff7 --- /dev/null +++ b/plugins/hls-tactics-plugin/test/golden/LayoutSplitViewPat.hs.expected @@ -0,0 +1,6 @@ +{-# LANGUAGE ViewPatterns #-} + +splitLookup :: [(Int, String)] -> String +splitLookup (lookup 5 -> Nothing) = _ +splitLookup (lookup 5 -> (Just l_c)) = _ + diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitWhere.hs b/plugins/hls-tactics-plugin/test/golden/LayoutSplitWhere.hs new file mode 100644 index 0000000000..5035df1b0c --- /dev/null +++ b/plugins/hls-tactics-plugin/test/golden/LayoutSplitWhere.hs @@ -0,0 +1,12 @@ +data A = A | B | C + +some :: A -> IO () +some a = do + foo + bar a + where + foo = putStrLn "Hi" + + bar :: A -> IO () + bar x = _ + diff --git a/plugins/hls-tactics-plugin/test/golden/LayoutSplitWhere.hs.expected b/plugins/hls-tactics-plugin/test/golden/LayoutSplitWhere.hs.expected new file mode 100644 index 0000000000..a6150ce53e --- /dev/null +++ b/plugins/hls-tactics-plugin/test/golden/LayoutSplitWhere.hs.expected @@ -0,0 +1,14 @@ +data A = A | B | C + +some :: A -> IO () +some a = do + foo + bar a + where + foo = putStrLn "Hi" + + bar :: A -> IO () + bar A = _ + bar B = _ + bar C = _ + diff --git a/plugins/hls-tactics-plugin/test/golden/SplitPattern.hs.expected b/plugins/hls-tactics-plugin/test/golden/SplitPattern.hs.expected index 8fe407a304..44d98f1fbd 100644 --- a/plugins/hls-tactics-plugin/test/golden/SplitPattern.hs.expected +++ b/plugins/hls-tactics-plugin/test/golden/SplitPattern.hs.expected @@ -1,12 +1,12 @@ data ADT = One | Two Int | Three | Four Bool ADT | Five case_split :: ADT -> Int -case_split One = _ -case_split (Two i) = _ -case_split Three = _ +case_split One = _ +case_split (Two i) = _ +case_split Three = _ case_split (Four b One) = _ case_split (Four b (Two i)) = _ case_split (Four b Three) = _ case_split (Four b (Four b3 a4)) = _ case_split (Four b Five) = _ -case_split Five = _ +case_split Five = _