on master help sign in
Implement pretty-printing of imports
commitb6d49bb625f2ef2f492c56e81f7deb7fe7996dad authormrkkrp <mark.karpov@tweag.io> dateThu 28 Feb 2019, 23:25 (8 years ago) parent6f169f2
26 files changed+357−51
data/examples/import/comments-per-import-out.hs+4 data/examples/import/comments-per-import.hs+3 data/examples/import/explicit-imports-out.hs+12 data/examples/import/explicit-imports-with-comments-out.hs+5 data/examples/import/explicit-imports-with-comments.hs+5 data/examples/import/explicit-imports.hs+2 data/examples/import/explicit-prelude-out.hs+3 data/examples/import/explicit-prelude.hs+3 data/examples/import/misc-out.hs+11 data/examples/import/misc.hs+13 data/examples/import/nested-explicit-imports-out.hs+10 data/examples/import/nested-explicit-imports.hs+2 data/examples/import/simple-out.hs+6 data/examples/import/simple.hs+6 data/examples/import/sorted-out.hs+3 data/examples/import/sorted.hs+3 ormolu.cabal+1 src/Ormolu/Diff.hs+18−7 src/Ormolu/Exception.hs+7−4 src/Ormolu/Imports.hs+90 src/Ormolu/Parser.hs+67−3 src/Ormolu/Printer.hs+56−19 src/Ormolu/Printer/Combinators.hs+11−9 src/Ormolu/Printer/Comments.hs+7−3 src/Ormolu/Printer/Internal.hs+8−5 tests/Ormolu/PrinterSpec.hs+1−1
data/examples/import/comments-per-import-out.hsnew file+4
@@ -0,0 +1,4 @@
1+import Bar
2+import Baz
3+import Foo
4+-- (3)
data/examples/import/comments-per-import.hsnew file+3
@@ -0,0 +1,3 @@
1+import Foo -- (1)
2+import Bar -- (2)
3+import Baz -- (3)
data/examples/import/explicit-imports-out.hsnew file+12
@@ -0,0 +1,12 @@
1+import qualified MegaModule as M
2+ ( (<<<)
3+ , (>>>)
4+ , Either
5+ , Maybe (Just, Nothing)
6+ , MaybeT (..)
7+ , Monad ((>>), (>>=), return)
8+ , MonadBaseControl
9+ , join
10+ , liftIO
11+ , void
12+ )
data/examples/import/explicit-imports-with-comments-out.hsnew file+5
@@ -0,0 +1,5 @@
1+import qualified MegaModule as M
2+ ( (<<<)
3+ , (>>>)
4+ , Either
5+ )
data/examples/import/explicit-imports-with-comments.hsnew file+5
@@ -0,0 +1,5 @@
1+import qualified MegaModule as M
2+ ( (>>>) -- (1)
3+ , (<<<) -- (2)
4+ , Either -- (3)
5+ )
data/examples/import/explicit-imports.hsnew file+2
@@ -0,0 +1,2 @@
1+import qualified MegaModule as M
2+ ((>>>), MonadBaseControl, void, MaybeT(..), join, Maybe(Nothing, Just), liftIO, Either, (<<<), Monad(return, (>>=), (>>)))
data/examples/import/explicit-prelude-out.hsnew file+3
@@ -0,0 +1,3 @@
1+import Aaa
2+import Zzz
3+import Prelude
data/examples/import/explicit-prelude.hsnew file+3
@@ -0,0 +1,3 @@
1+import Aaa
2+import Prelude
3+import Zzz
data/examples/import/misc-out.hsnew file+11
@@ -0,0 +1,11 @@
1+import A hiding
2+ ( foobarbazqux
3+ , foobarbazqux
4+ , foobarbazqux
5+ , foobarbazqux
6+ , foobarbazqux
7+ , foobarbazqux
8+ , foobarbazqux
9+ )
10+import {-# SOURCE #-} safe qualified Module as M hiding (a, b, c, d, e, f)
11+import Name hiding ()
data/examples/import/misc.hsnew file+13
@@ -0,0 +1,13 @@
1+import A hiding
2+ ( foobarbazqux
3+ , foobarbazqux
4+ , foobarbazqux
5+ , foobarbazqux
6+ , foobarbazqux
7+ , foobarbazqux
8+ , foobarbazqux
9+ )
10+
11+import Name hiding ()
12+
13+import {-# SOURCE #-} safe qualified Module as M hiding (a, b, c, d, e, f)
data/examples/import/nested-explicit-imports-out.hsnew file+10
@@ -0,0 +1,10 @@
1+import qualified MegaModule as M
2+ ( (<<<)
3+ , (>>>)
4+ , Either
5+ , Monad
6+ ( (>>)
7+ , (>>=)
8+ , return
9+ )
10+ )
data/examples/import/nested-explicit-imports.hsnew file+2
@@ -0,0 +1,2 @@
1+import qualified MegaModule as M ((>>>), Either, (<<<), Monad(
2+ return, (>>=), (>>)))
data/examples/import/simple-out.hsnew file+6
@@ -0,0 +1,6 @@
1+import Data.Text
2+import Data.Text
3+import qualified Data.Text as T
4+import qualified Data.Text (a, b, c)
5+import Data.Text (a, b, c)
6+import Data.Text hiding (a, b, c)
data/examples/import/simple.hsnew file+6
@@ -0,0 +1,6 @@
1+import Data.Text
2+import Data.Text
3+import qualified Data.Text as T
4+import qualified Data.Text (a, c, b)
5+import Data.Text (a, b, c)
6+import Data.Text hiding (c, b, a)
data/examples/import/sorted-out.hsnew file+3
@@ -0,0 +1,3 @@
1+import A
2+import B
3+import C
data/examples/import/sorted.hsnew file+3
@@ -0,0 +1,3 @@
1+import B
2+import A
3+import C
ormolu.cabal+1
@@ -42,6 +42,7 @@ library
4242 , Ormolu.Config
4343 , Ormolu.Diff
4444 , Ormolu.Exception
45+ , Ormolu.Imports
4546 , Ormolu.Parser
4647 , Ormolu.Printer
4748 , Ormolu.Printer.Combinators
src/Ormolu/Diff.hs+18−7
@@ -11,12 +11,13 @@ where
1111
1212import Data.Generics
1313import Data.Maybe (mapMaybe)
14−import GHC
14+import GHC hiding (GhcPs)
1515import Language.Haskell.GHC.ExactPrint.Types
1616import Ormolu.Comments
17+import Ormolu.Imports (sortImports)
1718import qualified Data.Map.Strict as M
1819
19−-- | Return 'False' of two annotated ASTs are the same modulo span
20+-- | Return 'False' if two annotated ASTs are the same modulo span
2021-- positions.
2122
2223diff
@@ -28,17 +29,27 @@ diff (anns0, ps0) (anns1, ps1) =
2829 || not (matchIgnoringSrcSpans ps0 ps1)
2930
3031-- | Compare two 'ParsedSource' values disregarding differences in
31−-- 'SrcSpan's.
32+-- 'SrcSpan's and the ordering of import lists.
3233
3334matchIgnoringSrcSpans :: Data a => a -> a -> Bool
34−matchIgnoringSrcSpans x0 y0 = geq' x0 y0
35+matchIgnoringSrcSpans = genericQuery
3536 where
36− geq' :: GenericQ (GenericQ Bool)
37− geq' x y = (toConstr x == toConstr y)
38− && and (gzipWithQ (geq' `extQ` srcSpanEq) x y)
37+ genericQuery :: GenericQ (GenericQ Bool)
38+ genericQuery x y = (toConstr x == toConstr y)
39+ && and (gzipWithQ (genericQuery `extQ` srcSpanEq `extQ` hsModuleEq) x y)
40+
3941 srcSpanEq :: SrcSpan -> GenericQ Bool
4042 srcSpanEq _ _ = True
4143
44+ hsModuleEq :: HsModule GhcPs -> GenericQ Bool
45+ hsModuleEq hs0 hs1' =
46+ case cast hs1' :: Maybe (HsModule GhcPs) of
47+ Nothing -> False
48+ Just hs1 ->
49+ matchIgnoringSrcSpans
50+ hs0 { hsmodImports = sortImports (hsmodImports hs0) }
51+ hs1 { hsmodImports = sortImports (hsmodImports hs1) }
52+
4253-- | Simplified collection of 'Comment's.
4354
4455data Comments = Comments [Comment] [Comment] [Comment]
src/Ormolu/Exception.hs+7−4
@@ -33,11 +33,14 @@ instance Exception OrmoluException where
3333 displayException = \case
3434 OrmoluCppEnabled -> "CPP is not supported"
3535 OrmoluParsingFailed s e ->
36− showParsingErr "parsing of source code failed:" s e
36+ showParsingErr "Parsing of source code failed:" s e
3737 OrmoluOutputParsingFailed s e ->
38− showParsingErr "parsing of formatted code failed (report the bug):" s e
39− OrmoluASTDiffers _ _ ->
40− "AST of input and AST of formatted code differ, report the bug"
38+ showParsingErr "Parsing of formatted code failed:" s e ++
39+ "Please, consider reporting the bug."
40+ OrmoluASTDiffers _ _ -> unlines
41+ [ "AST of input and AST of formatted code differ."
42+ , "Please, consider reporting the bug."
43+ ]
4144
4245-- | Inside this wrapper 'OrmoluException' will be caught and displayed
4346-- nicely using 'displayException'.
src/Ormolu/Imports.hsnew file+90
@@ -0,0 +1,90 @@
1+{-# LANGUAGE LambdaCase #-}
2+{-# LANGUAGE RankNTypes #-}
3+
4+-- | Manipulations on import lists.
5+
6+module Ormolu.Imports
7+ ( sortImports
8+ )
9+where
10+
11+import Data.Bifunctor
12+import Data.Function (on)
13+import Data.List (sortBy)
14+import GHC hiding (GhcPs, IE)
15+import HsImpExp (IE (..))
16+import Language.Haskell.GHC.ExactPrint.Types
17+
18+-- | Sort imports by module name. This also sorts explicit import lists for
19+-- each declaration.
20+
21+sortImports :: [LImportDecl GhcPs] -> [LImportDecl GhcPs]
22+sortImports = sortBy compareIdecl . fmap (fmap sortImportLists)
23+ where
24+ sortImportLists :: ImportDecl GhcPs -> ImportDecl GhcPs
25+ sortImportLists decl =
26+ decl { ideclHiding = second (fmap sortLies) <$> ideclHiding decl
27+ }
28+
29+-- | Compare two @'LImportDecl' 'GhcPs'@ things.
30+
31+compareIdecl :: LImportDecl GhcPs -> LImportDecl GhcPs -> Ordering
32+compareIdecl (L _ m0) (L _ m1) =
33+ if isPrelude n0
34+ then GT
35+ else if isPrelude n1
36+ then LT
37+ else n0 `compare` n1
38+ where
39+ n0 = unL (ideclName m0)
40+ n1 = unL (ideclName m1)
41+ isPrelude = (== "Prelude") . moduleNameString
42+
43+-- | Sort located import or export.
44+
45+sortLies :: [LIE GhcPs] -> [LIE GhcPs]
46+sortLies = sortBy (compareIE `on` unL) . fmap (fmap sortThings)
47+
48+-- | Sort imports\/exports inside of 'IEThingWith'.
49+
50+sortThings :: IE GhcPs -> IE GhcPs
51+sortThings = \case
52+ IEThingWith x w xs fl ->
53+ IEThingWith x w (sortBy (compareIewn `on` unL) xs) fl
54+ other -> other
55+
56+-- | Compare two located imports or exports.
57+
58+compareIE :: IE GhcPs -> IE GhcPs -> Ordering
59+compareIE = compareIewn `on` getIewn
60+
61+-- | Project @'IEWrappedName' 'RdrName'@ from @'IE' 'GhcPs'@.
62+
63+getIewn :: IE GhcPs -> IEWrappedName RdrName
64+getIewn = \case
65+ IEVar x -> unL x
66+ IEThingAbs x -> unL x
67+ IEThingAll x -> unL x
68+ IEThingWith x _ _ _ -> unL x
69+ IEModuleContents _ -> error "Ormolu.Imports.projectName: IEModuleContents"
70+ IEGroup _ _ -> error "Ormolu.Imports.projectName: IEGroup"
71+ IEDoc _ -> error "Ormolu.Imports.projectName: IEGroup"
72+ IEDocNamed _ -> error "Ormolu.Imports.projectName: IEGroup"
73+
74+-- | Compare two @'IEWrapppedName' 'RdrName'@ things.
75+
76+compareIewn :: IEWrappedName RdrName -> IEWrappedName RdrName -> Ordering
77+compareIewn (IEName x) (IEName y) = unL x `compare` unL y
78+compareIewn (IEName _) (IEPattern _) = LT
79+compareIewn (IEName _) (IEType _) = LT
80+compareIewn (IEPattern _) (IEName _) = GT
81+compareIewn (IEPattern x) (IEPattern y) = unL x `compare` unL y
82+compareIewn (IEPattern _) (IEType _) = LT
83+compareIewn (IEType _) (IEName _) = GT
84+compareIewn (IEType _) (IEPattern _) = GT
85+compareIewn (IEType x) (IEType y) = unL x `compare` unL y
86+
87+-- | Exact inner value from 'Located'.
88+
89+unL :: Located e -> e
90+unL (L _ e) = e
src/Ormolu/Parser.hs+67−3
@@ -1,3 +1,5 @@
1+{-# LANGUAGE RecordWildCards #-}
2+
13-- | Parser for Haskell source code.
24
35module Ormolu.Parser
@@ -8,14 +10,16 @@ where
810import Control.Exception
911import Control.Monad
1012import Control.Monad.IO.Class
13+import Data.Generics
14+import GHC hiding (GhcPs, IE, parseModule)
1115import GHC.LanguageExtensions.Type (Extension (Cpp))
1216import Language.Haskell.GHC.ExactPrint.Parsers hiding (parseModule)
1317import Language.Haskell.GHC.ExactPrint.Types
1418import Ormolu.Config
1519import Ormolu.Exception
1620import qualified CmdLineParser as GHC
21+import qualified Data.Map.Strict as M
1722import qualified DynFlags as GHC
18−import qualified GHC hiding (parseModule)
1923
2024-- | Parse a complete module from string.
2125
@@ -24,7 +28,7 @@ parseModule
2428 => [DynOption] -- ^ Dynamic options that affect parsing
2529 -> FilePath -- ^ File name (only for source location annotations)
2630 -> String -- ^ Input for parser
27− -> m ([GHC.Warn], Either (GHC.SrcSpan, String) (Anns, GHC.ParsedSource))
31+ -> m ([GHC.Warn], Either (SrcSpan, String) (Anns, ParsedSource))
2832parseModule dynOpts path input = liftIO $ do
2933 (ws, dynFlags) <- ghcWrapper $ do
3034 dynFlags0 <- initDynFlagsPure path input
@@ -36,4 +40,64 @@ parseModule dynOpts path input = liftIO $ do
3640 -- want.
3741 when (GHC.xopt Cpp dynFlags) $
3842 throwIO OrmoluCppEnabled
39− return (ws, parseModuleFromStringInternal dynFlags path input)
43+ let r = case parseModuleFromStringInternal dynFlags path input of
44+ Left e -> Left e
45+ Right (anns, psrc) ->
46+ Right (dropImportComments anns psrc, psrc)
47+ return (ws, r)
48+
49+-- | Drop comments associated with elements of import lists. The reason we
50+-- do this is that those comments are not associated as user would expect.
51+--
52+-- For example:
53+--
54+-- > import Foo -- (1)
55+-- > import Bar -- (2)
56+-- > import Baz -- (3)
57+--
58+-- Here, @(1)@ is considered preceding comment of @Baz@ import, in fact, it
59+-- has nothing to do with @Foo@. Similarly, @(2)@ is associted with @Baz@,
60+-- not @Bar@. Finally @(3)@ is actually preceeding comment for the thing
61+-- that follows the import list, or if nothing follows, it's a trailing
62+-- comment of the whole module.
63+--
64+-- Since we do sorting of imports when we print them, we would need to do
65+-- sorting of corresponding annotations as well (because we do checking of
66+-- annotations and AST in "Ormolu.Diff" as part of the self-check), and this
67+-- is hard. Even if we manage to do that the result will be totally
68+-- confusing, e.g. for the example above:
69+--
70+-- > -- (1)
71+-- > import Bar
72+-- > -- (2)
73+-- > import Baz
74+-- > import Foo
75+-- > -- (3)
76+--
77+-- The solution is to drop the tricky comments right after parsing, so we
78+-- don't need to deal with them at all, including the need to update
79+-- annotations for the correctness checking we do in "Ormolu.Diff".
80+--
81+-- The solution is not perfect, but practical.
82+
83+dropImportComments
84+ :: Anns
85+ -> ParsedSource
86+ -> Anns
87+dropImportComments anns (L _ HsModule {..}) =
88+ foldr M.delete anns (hsmodImports >>= keyAnnsFor)
89+
90+-- | Extract all 'AnnKey's from given 'Data'.
91+
92+keyAnnsFor :: GenericQ [AnnKey]
93+keyAnnsFor a = everything mappend (const id `ext2Q` queryLocated) a []
94+ where
95+ queryLocated
96+ :: (Data e0, Data e1)
97+ => GenLocated e0 e1
98+ -> [AnnKey]
99+ -> [AnnKey]
100+ queryLocated (L mspn x) =
101+ case cast mspn :: Maybe SrcSpan of
102+ Nothing -> id
103+ Just spn -> (mkAnnKey (L spn x) :)
src/Ormolu/Printer.hs+56−19
@@ -9,14 +9,15 @@ module Ormolu.Printer
99 )
1010where
1111
12+import Control.Monad
1213import Data.Char (isAlphaNum)
1314import Data.Text (Text)
14−import FieldLabel (FieldLbl (..))
1515import GHC hiding (GhcPs, IE)
1616import HsImpExp (IE (..))
1717import Language.Haskell.GHC.ExactPrint.Types
1818import Module (Module (..))
1919import OccName (OccName (..))
20+import Ormolu.Imports (sortImports)
2021import Ormolu.Printer.Combinators
2122import Outputable (Outputable (..), showSDocUnsafe)
2223import RdrName (RdrName (..), rdrNameOcc)
@@ -41,18 +42,21 @@ p_HsModule loc@(L moduleSpan hsModule) = do
4142 case hsmodExports hsModule of
4243 Nothing -> moduleSpan
4344 Just (L exportsSpan _) -> combineSrcSpans moduleSpan exportsSpan
44− locatedVia spn loc $ \HsModule {..} ->
45+ locatedVia (Just spn) loc $ \HsModule {..} -> do
4546 case hsmodName of
4647 Nothing -> pure ()
47− Just hsmodName' -> line . velt' $
48− [ located hsmodName' p_hsmodName ] ++
49− (case hsmodExports of
50− Nothing -> []
51− Just hsmodExports' ->
52− [ inci (locatedVia spn hsmodExports' p_hsmodExports)
53− ])
54− ++ [ txt "where"
55− ]
48+ Just hsmodName' -> do
49+ line . velt' $
50+ [ located hsmodName' p_hsmodName ] ++
51+ (case hsmodExports of
52+ Nothing -> []
53+ Just hsmodExports' ->
54+ [ inci (locatedVia Nothing hsmodExports' p_hsmodExports)
55+ ])
56+ ++ [ txt "where"
57+ ]
58+ unless (null hsmodImports) newline
59+ forM_ (sortImports hsmodImports) (located' p_hsmodImports)
5660
5761p_hsmodName :: ModuleName -> R ()
5862p_hsmodName mname = do
@@ -70,18 +74,51 @@ p_lie = \case
7074 IEThingAll l1 -> do
7175 located l1 p_ieWrappedName
7276 txt " (..)"
73− IEThingWith l1 w xs fls -> do
74− located l1 p_ieWrappedName
75− space
76− p_ieWildcard w
77− parens . velt $ withSep comma (located' p_ieWrappedName) xs
78− parens . velt $ withSep comma (located' p_FieldLbl) fls
77+ IEThingWith l1 w xs _ -> velt'
78+ [ located l1 p_ieWrappedName
79+ , inci $ do
80+ p_ieWildcard w
81+ parens . velt $ withSep comma (located' p_ieWrappedName) xs
82+ ]
83+ -- XXX I have no idea what field labels are in this context.
84+ -- parens . velt $ withSep comma (located' p_FieldLbl) fls
7985 IEModuleContents l1 -> located l1 p_hsmodName
8086 -- XXX I have no idea what these things are for.
8187 IEGroup _ _ -> return ()
8288 IEDoc _ -> return ()
8389 IEDocNamed _ -> return ()
8490
91+p_hsmodImports :: ImportDecl GhcPs -> R ()
92+p_hsmodImports ImportDecl {..} = line . velt' $
93+ [ do txt "import "
94+ when ideclSource $
95+ txt "{-# SOURCE #-} "
96+ when ideclSafe $
97+ txt "safe "
98+ when ideclQualified $
99+ txt "qualified "
100+ case ideclPkgQual of
101+ Nothing -> return ()
102+ Just slit -> do
103+ atom slit
104+ space
105+ located ideclName atom
106+ case ideclAs of
107+ Nothing -> return ()
108+ Just l -> do
109+ txt " as "
110+ located l atom
111+ case ideclHiding of
112+ Nothing -> return ()
113+ Just (hiding, _) ->
114+ when hiding $
115+ txt " hiding"
116+ ] ++ (case ideclHiding of
117+ Nothing -> []
118+ Just (_, l) ->
119+ [ inci . locatedVia Nothing l $ parens . velt . withSep comma (located' p_lie)
120+ ])
121+
85122p_ieWrappedName :: IEWrappedName RdrName -> R ()
86123p_ieWrappedName = \case
87124 IEName l2 -> located l2 p_rdrName
@@ -99,8 +136,8 @@ p_rdrName x = opParens (rdrNameOcc x) $ case x of
99136 Orig (Module _ mname) occName -> p_qualName mname occName
100137 Exact name -> atom name
101138
102−p_FieldLbl :: FieldLbl RdrName -> R ()
103−p_FieldLbl (FieldLabel x _ _) = atom x
139+-- p_FieldLbl :: FieldLbl RdrName -> R ()
140+-- p_FieldLbl (FieldLabel x _ _) = atom x
104141
105142p_qualName :: ModuleName -> OccName -> R ()
106143p_qualName mname occName = do
src/Ormolu/Printer/Combinators.hs+11−9
@@ -67,23 +67,27 @@ located
6767 => Located a -- ^ Thing to enter
6868 -> (a -> R ()) -- ^ How to render inner value
6969 -> R ()
70−located loc@(L l _) = locatedVia l loc
70+located loc@(L l _) = locatedVia (Just l) loc
7171
7272-- | A special version of 'located' that allows to control layout using
73−-- externally provided span.
73+-- externally provided span. 'Nothing' means that layout won't be changed.
7474
7575locatedVia
7676 :: Data a
77− => SrcSpan -- ^ Span that controls layout selection
77+ => Maybe SrcSpan -- ^ Span that controls layout selection
7878 -> Located a -- ^ Thing to enter
7979 -> (a -> R ()) -- ^ How to renedr inner value
8080 -> R ()
81−locatedVia l' loc@(L l a) f = do
81+locatedVia ml loc@(L l a) f = do
8282 mann <- lookupAnn loc
83+ layout <- currentLayout
8384 let m = enterLayout
84− (if isOneLineSpan l'
85− then SingleLine
86− else MultiLine)
85+ (case ml of
86+ Nothing -> layout
87+ Just l' ->
88+ if isOneLineSpan l'
89+ then SingleLine
90+ else MultiLine)
8791 (f a)
8892 case mann of
8993 Nothing -> m
@@ -110,8 +114,6 @@ locatedVia l' loc@(L l a) f = do
110114 -- comments that go before things. Exact location can only be
111115 -- deduced by analyzing the associated span.
112116
113− -- traceShowM (before,after, decoratedElt)
114−
115117 spitComments before
116118 m
117119 spitComments after
src/Ormolu/Printer/Comments.hs+7−3
@@ -95,9 +95,13 @@ partitionDPsModule
9595 :: SrcSpan -- ^ Span of element the comments are attached to
9696 -> [(KeywordId, DeltaPos)] -- ^ Annotations
9797 -> ([(Comment, Decoration)], [(Comment, Decoration)])
98−partitionDPsModule refSpan
99− = bimap (takeComments Before) (takeComments After)
100− . break ((== G AnnModule) . fst)
98+partitionDPsModule refSpan xs
99+ = bimap (takeComments Before) (takeComments After) $
100+ -- NOTE If there is no annotation corresponding to module keyword, then
101+ -- the module doesn't have a header and all comments shoud go after.
102+ if G AnnModule `elem` fmap fst xs
103+ then break ((== G AnnModule) . fst) xs
104+ else ([], xs)
101105 where
102106 takeComments pos = mapMaybe $ \(keywordId, dpos) -> do
103107 c <- annComment keywordId
src/Ormolu/Printer/Internal.hs+8−5
@@ -19,6 +19,7 @@ module Ormolu.Printer.Internal
1919 , Layout (..)
2020 , enterLayout
2121 , vlayout
22+ , currentLayout
2223 , lookupAnn
2324 )
2425where
@@ -189,18 +190,20 @@ vlayout
189190 -> R () -- ^ Multi line
190191 -> R ()
191192vlayout sline mline = do
192− l <- R (asks rcLayout)
193+ l <- currentLayout
193194 case l of
194195 SingleLine -> sline
195196 MultiLine -> mline
196197
198+-- | Return current layout.
199+
200+currentLayout :: R Layout
201+currentLayout = R (asks rcLayout)
202+
197203-- | Lookup an annotation.
198204
199205lookupAnn :: Data a => Located a -> R (Maybe Annotation)
200−lookupAnn (L s d) = do
201− anns <- R (asks rcAnns)
202− let k = AnnKey s (annGetConstr d)
203− return (M.lookup k anns)
206+lookupAnn l = M.lookup (mkAnnKey l) <$> R (asks rcAnns)
204207
205208----------------------------------------------------------------------------
206209-- Debug helpers
tests/Ormolu/PrinterSpec.hs+1−1
@@ -68,7 +68,7 @@ shouldMatch actual expected =
6868 when (actual /= expected) . expectationFailure $ unlines
6969 [ "expected:"
7070 , T.unpack expected
71− , "bot got:"
71+ , "but got:"
7272 , T.unpack actual
7373 ]
7474