packages feed

ormolu 0.0.5.0 → 0.1.0.0

raw patch · 87 files changed

+878/−668 lines, 87 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Ormolu: [cfgCheckIdempotency] :: Config region -> !Bool
- Ormolu.Config: [cfgCheckIdempotency] :: Config region -> !Bool
- Ormolu.Parser.CommentStream: isPrevHaddock :: Comment -> Bool
- Ormolu.Parser.CommentStream: newtype Comment
- Ormolu.Printer.Combinators: OtherSpan :: RealSrcSpan -> SpanMark
- Ormolu.Printer.Internal: OtherSpan :: RealSrcSpan -> SpanMark
- Ormolu.Printer.Internal: isLineDirty :: R Bool
- Ormolu.Utils: isModule :: Data a => a -> Bool
+ Ormolu: [cfgCheckIdempotence] :: Config region -> !Bool
+ Ormolu.Config: [cfgCheckIdempotence] :: Config region -> !Bool
+ Ormolu.Parser.CommentStream: data Comment
+ Ormolu.Parser.CommentStream: hasAtomsBefore :: Comment -> Bool
+ Ormolu.Parser.CommentStream: unComment :: Comment -> NonEmpty String
+ Ormolu.Printer.Combinators: StatementSpan :: RealSrcSpan -> SpanMark
+ Ormolu.Printer.Combinators: equals :: R ()
+ Ormolu.Printer.Internal: StatementSpan :: RealSrcSpan -> SpanMark
+ Ormolu.Printer.Internal: instance GHC.Classes.Eq Ormolu.Printer.Internal.SpitType
+ Ormolu.Printer.Internal: instance GHC.Show.Show Ormolu.Printer.Internal.SpitType
+ Ormolu.Printer.Internal: interferingTxt :: Text -> R ()
+ Ormolu.Printer.Internal: thisLineSpans :: R [RealSrcSpan]
+ Ormolu.Printer.Meat.Type: p_hsTypePostDoc :: HsType GhcPs -> R ()
+ Ormolu.Printer.Operators: instance GHC.Classes.Eq Ormolu.Printer.Operators.Score
+ Ormolu.Printer.Operators: instance GHC.Classes.Ord Ormolu.Printer.Operators.Score
+ Ormolu.Utils: onTheSameLine :: SrcSpan -> SrcSpan -> Bool
- Ormolu.Parser.CommentStream: Comment :: NonEmpty String -> Comment
+ Ormolu.Parser.CommentStream: Comment :: Bool -> NonEmpty String -> Comment
- Ormolu.Parser.CommentStream: mkCommentStream :: [Located String] -> PState -> (Maybe (RealLocated Comment), [Shebang], [([RealLocated Comment], Pragma)], CommentStream)
+ Ormolu.Parser.CommentStream: mkCommentStream :: String -> [Located String] -> PState -> (Maybe (RealLocated Comment), [Shebang], [([RealLocated Comment], Pragma)], CommentStream)
- Ormolu.Parser.Result: ParseResult :: ParsedSource -> Anns -> Maybe (RealLocated Comment) -> [Shebang] -> [([RealLocated Comment], Pragma)] -> CommentStream -> Bool -> Bool -> Text -> Text -> ParseResult
+ Ormolu.Parser.Result: ParseResult :: HsModule GhcPs -> Anns -> Maybe (RealLocated Comment) -> [Shebang] -> [([RealLocated Comment], Pragma)] -> CommentStream -> Bool -> Bool -> Text -> Text -> ParseResult
- Ormolu.Parser.Result: [prParsedSource] :: ParseResult -> ParsedSource
+ Ormolu.Parser.Result: [prParsedSource] :: ParseResult -> HsModule GhcPs
- Ormolu.Printer.Combinators: located :: Data a => Located a -> (a -> R ()) -> R ()
+ Ormolu.Printer.Combinators: located :: Located a -> (a -> R ()) -> R ()
- Ormolu.Printer.Combinators: located' :: Data a => (a -> R ()) -> Located a -> R ()
+ Ormolu.Printer.Combinators: located' :: (a -> R ()) -> Located a -> R ()
- Ormolu.Printer.Comments: spitFollowingComments :: Data a => RealLocated a -> R ()
+ Ormolu.Printer.Comments: spitFollowingComments :: RealSrcSpan -> R ()
- Ormolu.Printer.Comments: spitPrecedingComments :: Data a => RealLocated a -> R ()
+ Ormolu.Printer.Comments: spitPrecedingComments :: RealSrcSpan -> R ()
- Ormolu.Printer.Meat.Module: p_hsModule :: Maybe (RealLocated Comment) -> [Shebang] -> [([RealLocated Comment], Pragma)] -> Bool -> ParsedSource -> R ()
+ Ormolu.Printer.Meat.Module: p_hsModule :: Maybe (RealLocated Comment) -> [Shebang] -> [([RealLocated Comment], Pragma)] -> Bool -> HsModule GhcPs -> R ()

Files

CHANGELOG.md view
@@ -1,3 +1,46 @@+## Ormolu 0.1.0.0++* Fixed rendering of type signatures concerning several identifiers. [Issue+  566](https://github.com/tweag/ormolu/issues/566).++* Fixed an idempotence issue with inline comments in tuples and parentheses.+  [Issue 450](https://github.com/tweag/ormolu/issues/450).++* Fixed an idempotence issue when certain comments where picked up as+  “continuation” of a series of comments [Issue+  449](https://github.com/tweag/ormolu/issues/449).++* Fixed an idempotence issue related to different indentation levels in a+  comment series. [Issue 512](https://github.com/tweag/ormolu/issues/512).++* Fixed an idempotence issue related to comments which may happen to be+  separated from the elements they are attached to by the equality sign.+  [Issue 340](https://github.com/tweag/ormolu/issues/340).++* Fixed an idempotence issue with type synonym and data declarations where+  the type has a Haddock. [Issue+  578](https://github.com/tweag/ormolu/issues/578).++* Fix the false positive about AST differences in presence of comments with+  multiple blank lines in a row. [Issue+  518](https://github.com/tweag/ormolu/issues/518).++* Fixed rendering of comment around if expressions. [Issue+  458](https://github.com/tweag/ormolu/issues/458).++* Unnamed fields of data constructors are now documented using the `-- ^`+  syntax. [Issue 445](https://github.com/tweag/ormolu/issues/445) and [Issue+  428](https://github.com/tweag/ormolu/issues/428).++* Fixed non-idempotent transformation of partly documented data definition.+  [Issue 590](https://github.com/tweag/ormolu/issues/590).++* Fixed an idempotence issue related to operators. [Issue+  522](https://github.com/tweag/ormolu/issues/522).++* Renamed the `--check-idempotency` flag to `--check-idempotence`.+  Apparently only the latter is correct.+ ## Ormolu 0.0.5.0  * Grouping of statements in `do`-blocks is now preserved. [Issue
CONTRIBUTING.md view
@@ -30,7 +30,7 @@ * The result of printing is parsed back again and the AST is compared to the   AST obtained from the original file. They should match. * The output of printer is checked against the expected output.-* Idempotency property is verified: formatting already formatted code+* Idempotence property is verified: formatting already formatted code   results in exactly the same output.  Examples can be organized in sub-directories, see the existing ones for
DESIGN.md view
@@ -24,7 +24,7 @@  We set for the following goals (mostly taken from [brittany](https://github.com/lspitzner/brittany)):-* Preserve the meaning of the formatted functions;+* Preserve the meaning of the formatted functions when no CPP is used; * Make reasonable use of screen space; * Use linear space and computation time on the size of the input; * Preserve comments;@@ -140,8 +140,10 @@  ### CPP -Formatting a module which uses CPP directives won't be supported. Instead,-we hope for a solution to replace CPP to do conditional compilation.+We allow CPP directives in the input, but we forgo the goal to preserve the+meaning of the formatted functions in that case.+Instead of supporting CPP better, we hope for a solution to replace CPP to+do conditional compilation.  There are the following challenges when formatting a module with CPP: @@ -180,14 +182,15 @@ ```  At the time of this writing, formatting this program with Hindent-produces the same output we would get if the CPP directives were-considered comments:+or Ormolu produces the same output we would get if the CPP directives+were considered comments:  ```-$ hindent --version-hindent 5.2.7+$ ormolu --version+ormolu 0.0.5.0 HEAD fc64eada5c4da7a5b07d2872e253671b48aec115+using ghc-lib-parser 8.10.1.20200412 -$ hindent test.hs+$ ormolu --mode inplace test.hs  $ cat test.hs {-# LANGUAGE CPP #-}@@ -226,9 +229,6 @@ to parse Haskell modules. If CPP is replaced with some language extension or mechanism to do conditional compilation, all tools will benefit from it.--Therefore, CPP won't be supported. If the CPP extension-is enabled, we should signal an error right away.  ### Printing 
README.md view
@@ -91,6 +91,12 @@ $ ormolu --mode inplace Module.hs ``` +Use `find` to format a tree recursively:++```console+$ ormolu --mode inplace $(find . -name '*.hs')+```+ ## Magic comments  Ormolu understands two magic comments:@@ -116,8 +122,9 @@  * CPP support is experimental. CPP is virtually impossible to handle   correctly, so we process them as a sort of unchangeable snippets. This-  works only in simple cases when CPP conditionals are self-contained. Use-  Ormolu with CPP at your own risk.+  works only in simple cases when CPP conditionals surround top-level+  declarations. See the [CPP][design-cpp] section in the design notes for+  a discussion of the dangers. * Input modules should be parsable by Haddock, which is a bit stricter   criterion than just being valid Haskell modules. * Various minor idempotence issues, most of them are related to comments.@@ -154,7 +161,7 @@  Copyright © 2018–present Tweag I/O -[design]: https://github.com/tweag/ormolu/blob/master/DESIGN.md#cpp+[design-cpp]: https://github.com/tweag/ormolu/blob/master/DESIGN.md#cpp [contributing]: https://github.com/tweag/ormolu/blob/master/CONTRIBUTING.md [license]: https://github.com/tweag/ormolu/blob/master/LICENSE.md [haskell-src-exts]: https://hackage.haskell.org/package/haskell-src-exts
app/Main.hs view
@@ -167,17 +167,19 @@         help "Output information useful for debugging"       ]     <*> (switch . mconcat)-      [ long "check-idempotency",+      [ long "check-idempotence",         short 'c',         help "Fail if formatting is not idempotent"       ]     <*> ( RegionIndices             <$> (optional . option auto . mconcat)               [ long "start-line",-                help "Start line of the region to format (lines start from 1)"+                metavar "START",+                help "Start line of the region to format (starts from 1)"               ]             <*> (optional . option auto . mconcat)               [ long "end-line",+                metavar "END",                 help "End line of the region to format (inclusive)"               ]         )
data/examples/declaration/data/gadt/multiline-out.hs view
@@ -8,7 +8,7 @@   Foo ::     forall a b.     (Show a, Eq b) => -- foo-      -- bar+    -- bar     a ->     b ->     Foo 'Int
data/examples/declaration/data/multiline-arg-parens-out.hs view
@@ -5,6 +5,6 @@   = Foo       Bar       (Set Baz) -- and here we go-        -- and that's it+      -- and that's it       Text   deriving (Eq)
+ data/examples/declaration/data/partly-documented-out.hs view
@@ -0,0 +1,5 @@+data Optimisation+  = PETransform+  | -- | partial eval and associated transforms+    GeneralisedNatHack+  deriving (Show, Eq, Generic)
+ data/examples/declaration/data/partly-documented.hs view
@@ -0,0 +1,2 @@+data Optimisation = PETransform | GeneralisedNatHack -- ^ partial eval and associated transforms+  deriving (Show, Eq, Generic)
+ data/examples/declaration/data/unnamed-field-comment-0-out.hs view
@@ -0,0 +1,7 @@+data Foo+  = -- | Bar+    Bar+      Field1+      -- ^ Field 1+      Field2+      -- ^ Field 2
+ data/examples/declaration/data/unnamed-field-comment-0.hs view
@@ -0,0 +1,5 @@+data Foo+  = -- | Bar+    Bar+      Field1 -- ^ Field 1+      Field2 -- ^ Field 2
+ data/examples/declaration/data/unnamed-field-comment-1-out.hs view
@@ -0,0 +1,5 @@+data X+  = B+      !Int+      -- ^ y+      C
+ data/examples/declaration/data/unnamed-field-comment-1.hs view
@@ -0,0 +1,4 @@+data X+  = B+     !Int -- ^ y+     C
+ data/examples/declaration/data/with-comment-out.hs view
@@ -0,0 +1,4 @@+data A+  = B -- C+  | -- D+    E
+ data/examples/declaration/data/with-comment.hs view
@@ -0,0 +1,5 @@+data A =+     B  -- C++   -- D+   | E
+ data/examples/declaration/data/with-weird-haddock-out.hs view
@@ -0,0 +1,4 @@+data PlusLevel' t+  = -- | @n + ℓ@.+    Plus Integer (LevelAtom' t)+  deriving (Show, Data)
+ data/examples/declaration/data/with-weird-haddock.hs view
@@ -0,0 +1,2 @@+data PlusLevel' t = Plus Integer (LevelAtom' t)  -- ^ @n + ℓ@.+  deriving (Show, Data)
data/examples/declaration/signature/type/multi-value-out.hs view
@@ -2,6 +2,11 @@ foo = 1 bar = 2 +a, b, c :: Int+a = 1+b = 2+c = 3+ foo,   bar,   baz ::
data/examples/declaration/signature/type/multi-value.hs view
@@ -2,6 +2,11 @@ foo = 1 bar = 2 +a, b, c :: Int+a = 1+b = 2+c = 3+ foo,   bar,     baz :: Int
+ data/examples/declaration/type-synonyms/with-weird-haddock-out.hs view
@@ -0,0 +1,3 @@+type Elims =+  -- | eliminations ordered left-to-right.+  [Elim]
+ data/examples/declaration/type-synonyms/with-weird-haddock.hs view
@@ -0,0 +1,1 @@+type Elims = [Elim]  -- ^ eliminations ordered left-to-right.
data/examples/declaration/value/function/arrow/proc-do-complex-out.hs view
@@ -40,7 +40,7 @@                   )         else do           let u = x -- Let bindings bind expressions, not commands-            -- Could pattern match directly on x+          -- Could pattern match directly on x           i <- case u of             0 -> (g . h -< u)             n ->
+ data/examples/declaration/value/function/awkward-comment-0-out.hs view
@@ -0,0 +1,6 @@+mergeErrorReply :: ParseError -> Reply s u a -> Reply s u a+mergeErrorReply err1 reply -- XXX where to put it?+  =+  case reply of+    Ok x state err2 -> Ok x state (mergeError err1 err2)+    Error err2 -> Error (mergeError err1 err2)
+ data/examples/declaration/value/function/awkward-comment-0.hs view
@@ -0,0 +1,5 @@+mergeErrorReply :: ParseError -> Reply s u a -> Reply s u a+mergeErrorReply err1 reply -- XXX where to put it?+    = case reply of+        Ok x state err2 -> Ok x state (mergeError err1 err2)+        Error err2      -> Error (mergeError err1 err2)
+ data/examples/declaration/value/function/awkward-comment-1-out.hs view
@@ -0,0 +1,9 @@+doForeign :: Vars -> [Name] -> [Term] -> Idris LExp+doForeign x = x+  where+    splitArg tm | (_, [_, _, l, r]) <- unApply tm -- pair, two implicits+      =+      do+        let l' = toFDesc l+        r' <- irTerm (sMN 0 "__foreignCall") vs env r+        return (l', r')
+ data/examples/declaration/value/function/awkward-comment-1.hs view
@@ -0,0 +1,7 @@+doForeign :: Vars -> [Name] -> [Term] -> Idris LExp+doForeign x = x+  where+    splitArg tm | (_, [_,_,l,r]) <- unApply tm -- pair, two implicits+        = do let l' = toFDesc l+             r' <- irTerm (sMN 0 "__foreignCall") vs env r+             return (l', r')
+ data/examples/declaration/value/function/if-with-comment-out.hs view
@@ -0,0 +1,8 @@+foo =+  if undefined+    then -- then comment+      undefined+    else -- else comment++    do+      undefined
+ data/examples/declaration/value/function/if-with-comment.hs view
@@ -0,0 +1,8 @@+foo =+  if undefined+    -- then comment+    then undefined+    -- else comment+    else+      do+        undefined
data/examples/declaration/value/function/multi-way-if-out.hs view
@@ -3,12 +3,14 @@ foo x = if | x == 5 -> 5  bar x y =-  if  | x > y -> x+  if+      | x > y -> x       | x < y ->         y       | otherwise -> x  baz =-  if  | p -> f+  if+      | p -> f       | otherwise -> g     x
+ data/examples/declaration/value/function/operator-comments-2-out.hs view
@@ -0,0 +1,5 @@+x =+  y+    ++ f g -- commentA+    -- commentB+    -- commentC
+ data/examples/declaration/value/function/operator-comments-2.hs view
@@ -0,0 +1,4 @@+x  =+      y ++ -- commentA+-- commentB+      f g -- commentC
+ data/examples/declaration/value/function/operators-0-out.hs view
@@ -0,0 +1,5 @@+a =+  b & c .~ d+    & e+      %~ f+        g
+ data/examples/declaration/value/function/operators-0.hs view
@@ -0,0 +1,4 @@+a =+  b & c .~ d+    & e %~ f+            g
+ data/examples/declaration/value/function/operators-1-out.hs view
@@ -0,0 +1,4 @@+foo =+  f+    . g+    =<< h . i
+ data/examples/declaration/value/function/operators-1.hs view
@@ -0,0 +1,3 @@+foo = f+    . g+  =<< h . i
+ data/examples/declaration/value/function/operators-2-out.hs view
@@ -0,0 +1,4 @@+foo n+  | x || y && z || n ** x+      || x && n =+    42
+ data/examples/declaration/value/function/operators-2.hs view
@@ -0,0 +1,4 @@+foo n+  | x || y && z || n ** x+      || x && n =+        42
+ data/examples/declaration/value/function/operators-3-out.hs view
@@ -0,0 +1,3 @@+foo =+  op <> n <+> colon <+> prettySe <+> text "="+    <+> prettySe <> text sc
+ data/examples/declaration/value/function/operators-3.hs view
@@ -0,0 +1,3 @@+foo =+  op <> n <+> colon <+> prettySe <+> text "=" <+>+    prettySe <> text sc
+ data/examples/declaration/value/function/operators-4-out.hs view
@@ -0,0 +1,3 @@+foo =+  line <> bindingOf <+> text "=" <+> tPretty <+> colon+    <+> align <> prettyPs
+ data/examples/declaration/value/function/operators-4.hs view
@@ -0,0 +1,3 @@+foo =+  line <> bindingOf <+> text "=" <+> tPretty <+> colon <+>+    align <> prettyPs
+ data/examples/declaration/value/function/operators-5-out.hs view
@@ -0,0 +1,5 @@+foo =+  map bar $+    [ baz+    ]+      ++ quux
+ data/examples/declaration/value/function/operators-5.hs view
@@ -0,0 +1,4 @@+foo =+  map bar $+    [ baz+    ] ++ quux
+ data/examples/declaration/value/function/operators-6-out.hs view
@@ -0,0 +1,9 @@+type PermuteRef =+  "a"+    :> ( "b" :> "c" :> End+           :<|> "c" :> "b" :> End+       )+    :<|> "b"+      :> ( "a" :> "c" :> End+             :<|> "c" :> "a" :> End+         )
+ data/examples/declaration/value/function/operators-6.hs view
@@ -0,0 +1,7 @@+type PermuteRef =+       "a" :> (    "b" :> "c" :> End+              :<|> "c" :> "b" :> End+              )+  :<|> "b" :> (    "a" :> "c" :> End+              :<|> "c" :> "a" :> End+              )
− data/examples/declaration/value/function/operators-out.hs
@@ -1,5 +0,0 @@-a =-  b & c .~ d-    & e-      %~ f-        g
− data/examples/declaration/value/function/operators.hs
@@ -1,4 +0,0 @@-a =-  b & c .~ d-    & e %~ f-            g
− data/examples/declaration/value/function/record-constructors-out.hs
@@ -1,31 +0,0 @@-foo = Foo {a = 3}--bar =-  Bar-    { abc = foo,-      def = Foo {a = 10}-    }--baz = Baz {}--sym = Foo {(+) = 3}--aLongVariableName =-  ALongRecordName-    { short = baz,-      aLongRecordFieldName =-        YetAnotherLongRecordName-          { yetAnotherLongRecordFieldName = "a long string"-          },-      aLongRecordFieldName2 =-        Just-          YetAnotherLongRecordName-            { yetAnotherLongRecordFieldName = "a long string",-              yetAnotherLongRecordFieldName =-                Just-                  "a long string"-            },-      aLongRecordFieldName3 = do-        foo-        bar-    }
− data/examples/declaration/value/function/record-constructors.hs
@@ -1,22 +0,0 @@-foo = Foo { a = 3 }-bar = Bar {-    abc = foo,-    def = Foo {a = 10}-}-baz = Baz{ }-sym = Foo { (+) = 3 }-aLongVariableName =-  ALongRecordName-    { short = baz,-      aLongRecordFieldName = YetAnotherLongRecordName-        { yetAnotherLongRecordFieldName = "a long string"-          },-      aLongRecordFieldName2 = Just YetAnotherLongRecordName-                                 { yetAnotherLongRecordFieldName = "a long string",-                                   yetAnotherLongRecordFieldName = Just-                                                                     "a long string"-                                   },-      aLongRecordFieldName3 = do-                               foo-                               bar-      }
− data/examples/declaration/value/function/record-dot-out.hs
@@ -1,15 +0,0 @@-{-# OPTIONS_GHC -fplugin=RecordDotPreprocessor #-}--data Foo = Foo {bar :: Int}--mfoo = fmap (.bar) $ Nothing--bar = (Foo 1).bar--fooplus f n = f{foo = f.bar + n}--fooplus' f n = f {foo = f.bar + n}--fooplus'' f n = f {foo = f.bar + n}--fooplus''' f n = f {foo = f.bar + n}
− data/examples/declaration/value/function/record-dot.hs
@@ -1,14 +0,0 @@-{-# OPTIONS_GHC -fplugin=RecordDotPreprocessor #-}-data Foo = Foo { bar :: Int }--mfoo = fmap (.bar)   $ Nothing--bar = (  Foo 1).bar--fooplus f n = f{foo = f.bar + n}--fooplus' f n = f { foo = f.bar + n}--fooplus'' f n = f {foo = f.bar + n}--fooplus''' f n = f{ foo = f.bar + n}
− data/examples/declaration/value/function/record-inter-comments-out.hs
@@ -1,13 +0,0 @@-x =-  Just-    -- comment-    A-      { x-      }--x =-  Just-    -- comment-    a-      { x-      }
− data/examples/declaration/value/function/record-inter-comments.hs
@@ -1,11 +0,0 @@-x = Just-  -- comment-  A-   { x-   }--x = Just-  -- comment-  a-   { x-   }
− data/examples/declaration/value/function/record-updaters-out.hs
@@ -1,11 +0,0 @@-foo x = x {a = 3}--bar x =-  x-    { abc = foo,-      def = Foo {a = 10}-    }--baz x = x {}--sym x = x {(+) = 4}
− data/examples/declaration/value/function/record-updaters.hs
@@ -1,7 +0,0 @@-foo x = x { a = 3 }-bar x = x {-    abc = foo,-    def = Foo {a = 10}-}-baz x = x{ }-sym x = x { (+) = 4 }
− data/examples/declaration/value/function/record-wildcards-out.hs
@@ -1,14 +0,0 @@-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE RecordWildCards #-}--foo x y = Foo {x, y}--bar x y z =-  Bar-    { x,-      y,-      z,-      ..-    }--baz = Baz {..}
− data/examples/declaration/value/function/record-wildcards.hs
@@ -1,13 +0,0 @@-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE RecordWildCards #-}--foo x y = Foo { x, y }--bar x y z = Bar {-    x,-    y,-    z,-    ..-}--baz = Baz { .. }
data/examples/other/comment-two-blocks-out.hs view
@@ -2,7 +2,7 @@ newNames =   let (*) = flip (,)    in [ "Control" * "Monad"-        -- Foo+  -- Foo -        -- Bar+  -- Bar       ]
+ data/examples/other/inline-comment-0-out.hs view
@@ -0,0 +1,3 @@+x = ({-a-} b, c)++y = ({-a-} b)
+ data/examples/other/inline-comment-0.hs view
@@ -0,0 +1,3 @@+x = ({-a-}b, c)++y = ({-a-}b)
+ data/examples/other/inline-comment-1-out.hs view
@@ -0,0 +1,12 @@+showPs env ((n, _, Let _ t v) : bs) =+  "  " ++ show n ++ " : "+    ++ showEnv env ({- normalise ctxt env -} t)+    ++ "   =   "+    ++ showEnv env ({- normalise ctxt env -} v)+    ++ "\n"+    ++ showPs env bs+showPs env ((n, _, b) : bs) =+  "  " ++ show n ++ " : "+    ++ showEnv env ({- normalise ctxt env -} (binderTy b))+    ++ "\n"+    ++ showPs env bs
+ data/examples/other/inline-comment-1.hs view
@@ -0,0 +1,9 @@+showPs env ((n, _, Let _ t v):bs)+    = "  " ++ show n ++ " : " +++      showEnv env ({- normalise ctxt env -} t) ++ "   =   " +++      showEnv env ({- normalise ctxt env -} v) +++      "\n" ++ showPs env bs+showPs env ((n, _, b):bs)+    = "  " ++ show n ++ " : " +++      showEnv env ({- normalise ctxt env -} (binderTy b)) +++      "\n" ++ showPs env bs
+ data/examples/other/multiple-blank-line-comment-out.hs view
@@ -0,0 +1,9 @@+module A where++a =+  b+    [ f+    {-,++                                       -}+    ]
+ data/examples/other/multiple-blank-line-comment.hs view
@@ -0,0 +1,10 @@+module A where++a =+  b+    [ f+ {-,+++                                    -}+    ]
ormolu.cabal view
@@ -1,170 +1,183 @@-name:                 ormolu-version:              0.0.5.0-cabal-version:        1.18-tested-with:          GHC==8.6.5, GHC==8.8.3, GHC==8.10.1-license:              BSD3-license-file:         LICENSE.md-maintainer:           Mark Karpov <mark.karpov@tweag.io>-homepage:             https://github.com/tweag/ormolu-bug-reports:          https://github.com/tweag/ormolu/issues-category:             Development, Formatting-synopsis:             A formatter for Haskell source code-build-type:           Simple-description:          A formatter for Haskell source code.-extra-doc-files:      CONTRIBUTING.md-                    , CHANGELOG.md-                    , DESIGN.md-                    , README.md-data-files:           data/examples/declaration/annotation/*.hs-                    , data/examples/declaration/class/*.hs-                    , data/examples/declaration/data/*.hs-                    , data/examples/declaration/data/gadt/*.hs-                    , data/examples/declaration/default/*.hs-                    , data/examples/declaration/deriving/*.hs-                    , data/examples/declaration/foreign/*.hs-                    , data/examples/declaration/instance/*.hs-                    , data/examples/declaration/rewrite-rule/*.hs-                    , data/examples/declaration/role-annotation/*.hs-                    , data/examples/declaration/signature/complete/*.hs-                    , data/examples/declaration/signature/fixity/*.hs-                    , data/examples/declaration/signature/inline/*.hs-                    , data/examples/declaration/signature/minimal/*.hs-                    , data/examples/declaration/signature/pattern/*.hs-                    , data/examples/declaration/signature/set-cost-centre/*.hs-                    , data/examples/declaration/signature/specialize/*.hs-                    , data/examples/declaration/signature/type/*.hs-                    , data/examples/declaration/splice/*.hs-                    , data/examples/declaration/type-families/closed-type-family/*.hs-                    , data/examples/declaration/type-families/data-family/*.hs-                    , data/examples/declaration/type-families/type-family/*.hs-                    , data/examples/declaration/type-synonyms/*.hs-                    , data/examples/declaration/type/*.hs-                    , data/examples/declaration/value/function/*.hs-                    , data/examples/declaration/value/function/arrow/*.hs-                    , data/examples/declaration/value/function/comprehension/*.hs-                    , data/examples/declaration/value/function/do/*.hs-                    , data/examples/declaration/value/function/infix/*.hs-                    , data/examples/declaration/value/function/pattern/*.hs-                    , data/examples/declaration/value/other/*.hs-                    , data/examples/declaration/value/pattern-synonyms/*.hs-                    , data/examples/declaration/warning/*.hs-                    , data/examples/import/*.hs-                    , data/examples/module-header/*.hs-                    , data/examples/other/*.hs+cabal-version:   1.18+name:            ormolu+version:         0.1.0.0+license:         BSD3+license-file:    LICENSE.md+maintainer:      Mark Karpov <mark.karpov@tweag.io>+tested-with:     ghc ==8.6.5 ghc ==8.8.3 ghc ==8.10.1+homepage:        https://github.com/tweag/ormolu+bug-reports:     https://github.com/tweag/ormolu/issues+synopsis:        A formatter for Haskell source code+description:     A formatter for Haskell source code.+category:        Development, Formatting+build-type:      Simple+data-files:+    data/examples/declaration/annotation/*.hs+    data/examples/declaration/class/*.hs+    data/examples/declaration/data/*.hs+    data/examples/declaration/data/gadt/*.hs+    data/examples/declaration/default/*.hs+    data/examples/declaration/deriving/*.hs+    data/examples/declaration/foreign/*.hs+    data/examples/declaration/instance/*.hs+    data/examples/declaration/rewrite-rule/*.hs+    data/examples/declaration/role-annotation/*.hs+    data/examples/declaration/signature/complete/*.hs+    data/examples/declaration/signature/fixity/*.hs+    data/examples/declaration/signature/inline/*.hs+    data/examples/declaration/signature/minimal/*.hs+    data/examples/declaration/signature/pattern/*.hs+    data/examples/declaration/signature/set-cost-centre/*.hs+    data/examples/declaration/signature/specialize/*.hs+    data/examples/declaration/signature/type/*.hs+    data/examples/declaration/splice/*.hs+    data/examples/declaration/type-families/closed-type-family/*.hs+    data/examples/declaration/type-families/data-family/*.hs+    data/examples/declaration/type-families/type-family/*.hs+    data/examples/declaration/type-synonyms/*.hs+    data/examples/declaration/type/*.hs+    data/examples/declaration/value/function/*.hs+    data/examples/declaration/value/function/arrow/*.hs+    data/examples/declaration/value/function/comprehension/*.hs+    data/examples/declaration/value/function/do/*.hs+    data/examples/declaration/value/function/infix/*.hs+    data/examples/declaration/value/function/pattern/*.hs+    data/examples/declaration/value/other/*.hs+    data/examples/declaration/value/pattern-synonyms/*.hs+    data/examples/declaration/warning/*.hs+    data/examples/import/*.hs+    data/examples/module-header/*.hs+    data/examples/other/*.hs +extra-doc-files:+    CONTRIBUTING.md+    CHANGELOG.md+    DESIGN.md+    README.md+ source-repository head-  type:               git-  location:           https://github.com/tweag/ormolu.git+    type:     git+    location: https://github.com/tweag/ormolu.git  flag dev-  description:        Turn on development settings.-  manual:             True-  default:            False+    description: Turn on development settings.+    default:     False+    manual:      True  library-  hs-source-dirs:     src-  build-depends:      base           >= 4.12 && < 5.0-                    , bytestring     >= 0.2 && < 0.11-                    , containers     >= 0.5 && < 0.7-                    , dlist          >= 0.8 && < 0.9-                    , exceptions     >= 0.6 && < 0.11-                    , ghc-lib-parser >= 8.10 && < 8.11-                    , mtl            >= 2.0 && < 3.0-                    , syb            >= 0.7 && < 0.8-                    , text           >= 0.2 && < 1.3-  exposed-modules:    Ormolu-                    , Ormolu.Config-                    , Ormolu.Diff-                    , Ormolu.Exception-                    , Ormolu.Imports-                    , Ormolu.Parser-                    , Ormolu.Parser.Anns-                    , Ormolu.Parser.CommentStream-                    , Ormolu.Parser.Pragma-                    , Ormolu.Parser.Result-                    , Ormolu.Parser.Shebang-                    , Ormolu.Printer-                    , Ormolu.Printer.Combinators-                    , Ormolu.Printer.Comments-                    , Ormolu.Printer.Internal-                    , Ormolu.Printer.Meat.Common-                    , Ormolu.Printer.Meat.Declaration-                    , Ormolu.Printer.Meat.Declaration.Annotation-                    , Ormolu.Printer.Meat.Declaration.Class-                    , Ormolu.Printer.Meat.Declaration.Data-                    , Ormolu.Printer.Meat.Declaration.Default-                    , Ormolu.Printer.Meat.Declaration.Foreign-                    , Ormolu.Printer.Meat.Declaration.Instance-                    , Ormolu.Printer.Meat.Declaration.RoleAnnotation-                    , Ormolu.Printer.Meat.Declaration.Rule-                    , Ormolu.Printer.Meat.Declaration.Signature-                    , Ormolu.Printer.Meat.Declaration.Splice-                    , Ormolu.Printer.Meat.Declaration.Type-                    , Ormolu.Printer.Meat.Declaration.TypeFamily-                    , Ormolu.Printer.Meat.Declaration.Value-                    , Ormolu.Printer.Meat.Declaration.Warning-                    , Ormolu.Printer.Meat.ImportExport-                    , Ormolu.Printer.Meat.Module-                    , Ormolu.Printer.Meat.Pragma-                    , Ormolu.Printer.Meat.Type-                    , Ormolu.Printer.Operators-                    , Ormolu.Printer.SpanStream-                    , Ormolu.Processing.Common-                    , Ormolu.Processing.Cpp-                    , Ormolu.Processing.Postprocess-                    , Ormolu.Processing.Preprocess-                    , Ormolu.Utils-  other-modules:      GHC-                    , GHC.DynFlags-  if flag(dev)-    ghc-options:      -Wall -Werror -Wcompat-                      -Wincomplete-record-updates-                      -Wincomplete-uni-patterns-                      -Wnoncanonical-monad-instances-                      -- https://github.com/haskell/haddock/issues/1116-                      -Wno-missing-home-modules-  else-    ghc-options:      -O2 -Wall-  default-language:   Haskell2010+    exposed-modules:+        Ormolu+        Ormolu.Config+        Ormolu.Diff+        Ormolu.Exception+        Ormolu.Imports+        Ormolu.Parser+        Ormolu.Parser.Anns+        Ormolu.Parser.CommentStream+        Ormolu.Parser.Pragma+        Ormolu.Parser.Result+        Ormolu.Parser.Shebang+        Ormolu.Printer+        Ormolu.Printer.Combinators+        Ormolu.Printer.Comments+        Ormolu.Printer.Internal+        Ormolu.Printer.Meat.Common+        Ormolu.Printer.Meat.Declaration+        Ormolu.Printer.Meat.Declaration.Annotation+        Ormolu.Printer.Meat.Declaration.Class+        Ormolu.Printer.Meat.Declaration.Data+        Ormolu.Printer.Meat.Declaration.Default+        Ormolu.Printer.Meat.Declaration.Foreign+        Ormolu.Printer.Meat.Declaration.Instance+        Ormolu.Printer.Meat.Declaration.RoleAnnotation+        Ormolu.Printer.Meat.Declaration.Rule+        Ormolu.Printer.Meat.Declaration.Signature+        Ormolu.Printer.Meat.Declaration.Splice+        Ormolu.Printer.Meat.Declaration.Type+        Ormolu.Printer.Meat.Declaration.TypeFamily+        Ormolu.Printer.Meat.Declaration.Value+        Ormolu.Printer.Meat.Declaration.Warning+        Ormolu.Printer.Meat.ImportExport+        Ormolu.Printer.Meat.Module+        Ormolu.Printer.Meat.Pragma+        Ormolu.Printer.Meat.Type+        Ormolu.Printer.Operators+        Ormolu.Printer.SpanStream+        Ormolu.Processing.Common+        Ormolu.Processing.Cpp+        Ormolu.Processing.Postprocess+        Ormolu.Processing.Preprocess+        Ormolu.Utils -test-suite tests-  main-is:            Spec.hs-  hs-source-dirs:     tests-  type:               exitcode-stdio-1.0-  build-depends:      base           >= 4.12 && < 5.0-                    , containers     >= 0.5 && < 0.7-                    , filepath       >= 1.2 && < 1.5-                    , hspec          >= 2.0 && < 3.0-                    , ormolu-                    , path           >= 0.6 && < 0.8-                    , path-io        >= 1.4.2 && < 2.0-                    , text           >= 0.2 && < 1.3-  build-tools:        hspec-discover >= 2.0 && < 3.0-  other-modules:-                      Ormolu.Parser.PragmaSpec-                    , Ormolu.PrinterSpec+    hs-source-dirs:   src+    other-modules:+        GHC+        GHC.DynFlags -  if flag(dev)-    ghc-options:      -Wall -Werror-  else-    ghc-options:      -O2 -Wall-  default-language:   Haskell2010+    default-language: Haskell2010+    build-depends:+        base >=4.12 && <5.0,+        bytestring >=0.2 && <0.11,+        containers >=0.5 && <0.7,+        dlist >=0.8 && <0.9,+        exceptions >=0.6 && <0.11,+        ghc-lib-parser >=8.10 && <8.11,+        mtl >=2.0 && <3.0,+        syb >=0.7 && <0.8,+        text >=0.2 && <1.3 +    if flag(dev)+        ghc-options:+            -Wall -Werror -Wcompat -Wincomplete-record-updates+            -Wincomplete-uni-patterns -Wnoncanonical-monad-instances+            -Wno-missing-home-modules++    else+        ghc-options: -O2 -Wall+ executable ormolu-  main-is:            Main.hs-  hs-source-dirs:     app-  build-depends:      base           >= 4.12 && < 5.0-                    , ghc-lib-parser >= 8.10 && < 8.11-                    , gitrev         >= 1.3 && < 1.4-                    , optparse-applicative >= 0.14 && < 0.16-                    , ormolu-                    , text           >= 0.2 && < 1.3-  other-modules:      Paths_ormolu-  if flag(dev)-    ghc-options:      -Wall -Werror -Wcompat-                      -Wincomplete-record-updates-                      -Wincomplete-uni-patterns-                      -Wnoncanonical-monad-instances-  else-    ghc-options:      -O2 -Wall -rtsopts-  default-language:   Haskell2010+    main-is:          Main.hs+    hs-source-dirs:   app+    other-modules:    Paths_ormolu+    default-language: Haskell2010+    build-depends:+        base >=4.12 && <5.0,+        ghc-lib-parser >=8.10 && <8.11,+        gitrev >=1.3 && <1.4,+        optparse-applicative >=0.14 && <0.16,+        ormolu -any,+        text >=0.2 && <1.3++    if flag(dev)+        ghc-options:+            -Wall -Werror -Wcompat -Wincomplete-record-updates+            -Wincomplete-uni-patterns -Wnoncanonical-monad-instances++    else+        ghc-options: -O2 -Wall -rtsopts++test-suite tests+    type:             exitcode-stdio-1.0+    main-is:          Spec.hs+    build-tools:      hspec-discover >=2.0 && <3.0+    hs-source-dirs:   tests+    other-modules:+        Ormolu.Parser.PragmaSpec+        Ormolu.PrinterSpec++    default-language: Haskell2010+    build-depends:+        base >=4.12 && <5.0,+        containers >=0.5 && <0.7,+        filepath >=1.2 && <1.5,+        hspec >=2.0 && <3.0,+        ormolu -any,+        path >=0.6 && <0.8,+        path-io >=1.4.2 && <2.0,+        text >=0.2 && <1.3++    if flag(dev)+        ghc-options: -Wall -Werror++    else+        ghc-options: -O2 -Wall
src/GHC.hs view
@@ -1,6 +1,5 @@ module GHC   ( module X,-    ParsedSource,   ) where @@ -11,5 +10,3 @@ import Module as X import RdrName as X import SrcLoc as X--type ParsedSource = Located (HsModule GhcPs)
src/Ormolu.hs view
@@ -63,7 +63,7 @@   -- to parse the rendered code back, inside of GHC monad wrapper which will   -- lead to error messages presenting the exceptions as GHC bugs.   let !txt = printModule result0-  when (not (cfgUnsafe cfg) || cfgCheckIdempotency cfg) $ do+  when (not (cfgUnsafe cfg) || cfgCheckIdempotence cfg) $ do     let pathRendered = path ++ "<rendered>"     -- Parse the result of pretty-printing again and make sure that AST     -- is the same as AST of original snippet module span positions.@@ -79,7 +79,7 @@         Different ss -> liftIO $ throwIO (OrmoluASTDiffers path ss)     -- Try re-formatting the formatted result to check if we get exactly     -- the same output.-    when (cfgCheckIdempotency cfg) $+    when (cfgCheckIdempotence cfg) $       let txt2 = printModule result1        in case diffText txt txt2 pathRendered of             Nothing -> return ()
src/Ormolu/Config.hs view
@@ -24,7 +24,7 @@     -- | Output information useful for debugging     cfgDebug :: !Bool,     -- | Checks if re-formatting the result is idempotent-    cfgCheckIdempotency :: !Bool,+    cfgCheckIdempotence :: !Bool,     -- | Region selection     cfgRegion :: !region   }@@ -56,7 +56,7 @@     { cfgDynOptions = [],       cfgUnsafe = False,       cfgDebug = False,-      cfgCheckIdempotency = False,+      cfgCheckIdempotence = False,       cfgRegion =         RegionIndices           { regionStartLine = Nothing,
src/Ormolu/Diff.hs view
@@ -15,6 +15,7 @@ import qualified FastString as GHC import GHC import Ormolu.Imports (sortImports)+import Ormolu.Parser.CommentStream import Ormolu.Parser.Result import Ormolu.Utils @@ -38,14 +39,16 @@ diffParseResult   ParseResult     { prCommentStream = cstream0,-      prParsedSource = ps0+      prParsedSource = hs0     }   ParseResult     { prCommentStream = cstream1,-      prParsedSource = ps1+      prParsedSource = hs1     } =     matchIgnoringSrcSpans cstream0 cstream1-      <> matchIgnoringSrcSpans ps0 ps1+      <> matchIgnoringSrcSpans+        hs0 {hsmodImports = sortImports (hsmodImports hs0)}+        hs1 {hsmodImports = sortImports (hsmodImports hs1)}  -- | Compare two values for equality disregarding differences in 'SrcSpan's -- and the ordering of import lists.@@ -67,7 +70,7 @@           gzipWithQ             ( genericQuery                 `extQ` srcSpanEq-                `extQ` hsModuleEq+                `extQ` commentEq                 `extQ` sourceTextEq                 `extQ` hsDocStringEq                 `extQ` importDeclQualifiedStyleEq@@ -78,14 +81,14 @@       | otherwise = Different []     srcSpanEq :: SrcSpan -> GenericQ Diff     srcSpanEq _ _ = Same-    hsModuleEq :: HsModule GhcPs -> GenericQ Diff-    hsModuleEq hs0 hs1' =-      case cast hs1' :: Maybe (HsModule GhcPs) of+    commentEq :: Comment -> GenericQ Diff+    commentEq (Comment _ x) d =+      case cast d :: Maybe Comment of         Nothing -> Different []-        Just hs1 ->-          matchIgnoringSrcSpans-            hs0 {hsmodImports = sortImports (hsmodImports hs0)}-            hs1 {hsmodImports = sortImports (hsmodImports hs1)}+        Just (Comment _ y) ->+          if x == y+            then Same+            else Different []     sourceTextEq :: SourceText -> GenericQ Diff     sourceTextEq _ _ = Same     importDeclQualifiedStyleEq :: ImportDeclQualifiedStyle -> GenericQ Diff
src/Ormolu/Parser.hs view
@@ -88,7 +88,7 @@           case pStateErrors pstate of             Just err -> Left err             Nothing -> error "PFailed does not have an error"-        GHC.POk pstate pmod ->+        GHC.POk pstate (L _ hsModule) ->           case pStateErrors pstate of             -- Some parse errors (pattern/arrow syntax in expr context)             -- do not cause a parse error, but they are replaced with "_"@@ -97,10 +97,10 @@             Just err -> Left err             Nothing ->               let (stackHeader, shebangs, pragmas, comments) =-                    mkCommentStream extraComments pstate+                    mkCommentStream input extraComments pstate                in Right                     ParseResult-                      { prParsedSource = pmod,+                      { prParsedSource = hsModule,                         prAnns = mkAnns pstate,                         prStackHeader = stackHeader,                         prShebangs = shebangs,
src/Ormolu/Parser/CommentStream.hs view
@@ -4,12 +4,16 @@  -- | Functions for working with comment stream. module Ormolu.Parser.CommentStream-  ( CommentStream (..),-    Comment (..),+  ( -- * Comment stream+    CommentStream (..),     mkCommentStream,-    isPrevHaddock,-    isMultilineComment,     showCommentStream,++    -- * Comment+    Comment (..),+    unComment,+    hasAtomsBefore,+    isMultilineComment,   ) where @@ -26,20 +30,20 @@ import Ormolu.Utils (showOutputable) import SrcLoc +----------------------------------------------------------------------------+-- Comment stream+ -- | A stream of 'RealLocated' 'Comment's in ascending order with respect to -- beginning of corresponding spans. newtype CommentStream = CommentStream [RealLocated Comment]   deriving (Eq, Data, Semigroup, Monoid) --- | A wrapper for a single comment. The 'NonEmpty' list inside contains--- lines of multiline comment @{- … -}@ or just single item\/line otherwise.-newtype Comment = Comment (NonEmpty String)-  deriving (Eq, Show, Data)- -- | Create 'CommentStream' from 'GHC.PState'. The pragmas and shebangs are -- removed from the 'CommentStream'. Shebangs are only extracted from the -- comments that come from the first argument. mkCommentStream ::+  -- | Original input+  String ->   -- | Extra comments to include   [Located String] ->   -- | Parser state to use for comment extraction@@ -50,14 +54,14 @@     [([RealLocated Comment], Pragma)],     CommentStream   )-mkCommentStream extraComments pstate =+mkCommentStream input extraComments pstate =   ( mstackHeader,     shebangs,     pragmas,     CommentStream comments   )   where-    (comments, pragmas) = extractPragmas rawComments1+    (comments, pragmas) = extractPragmas input rawComments1     (rawComments1, mstackHeader) = extractStackHeader rawComments0     rawComments0 =       L.sortOn (realSrcSpanStart . getRealSrcSpan) . mapMaybe toRealSpan $@@ -68,15 +72,6 @@             (GHC.annotations_comments pstate)     (shebangs, otherExtraComments) = extractShebangs extraComments --- | Test whether a 'Comment' looks like a Haddock following a definition,--- i.e. something starting with @-- ^@.-isPrevHaddock :: Comment -> Bool-isPrevHaddock (Comment (x :| _)) = "-- ^" `L.isPrefixOf` x---- | Is this comment multiline-style?-isMultilineComment :: Comment -> Bool-isMultilineComment (Comment (x :| _)) = "{-" `L.isPrefixOf` x- -- | Pretty-print a 'CommentStream'. showCommentStream :: CommentStream -> String showCommentStream (CommentStream xs) =@@ -86,49 +81,68 @@     showComment (GHC.L l str) = showOutputable l ++ " " ++ show str  ------------------------------------------------------------------------------- Helpers+-- Comment +-- | A wrapper for a single comment. The 'Bool' indicates whether there were+-- atoms before beginning of the comment in the original input. The+-- 'NonEmpty' list inside contains lines of multiline comment @{- … -}@ or+-- just single item\/line otherwise.+data Comment = Comment Bool (NonEmpty String)+  deriving (Eq, Show, Data)+ -- | Normalize comment string. Sometimes one multi-line comment is turned -- into several lines for subsequent outputting with correct indentation for -- each line.-mkComment :: RealLocated String -> RealLocated Comment-mkComment (L l s) =-  L l . Comment . fmap dropTrailing $-    if "{-" `L.isPrefixOf` s-      then case NE.nonEmpty (lines s) of-        Nothing -> s :| []-        Just (x :| xs) ->-          let getIndent y =-                if all isSpace y-                  then startIndent-                  else length (takeWhile isSpace y)-              n = minimum (startIndent : fmap getIndent xs)-           in x :| (drop n <$> xs)-      else s :| []+mkComment ::+  -- | Lines of original input with their indices+  [(Int, String)] ->+  -- | Raw comment string+  RealLocated String ->+  -- | Remaining lines of original input and the constructed 'Comment'+  ([(Int, String)], RealLocated Comment)+mkComment ls (L l s) = (ls', comment)   where+    comment =+      L l . Comment atomsBefore . removeConseqBlanks . fmap dropTrailing $+        if "{-" `L.isPrefixOf` s+          then case NE.nonEmpty (lines s) of+            Nothing -> s :| []+            Just (x :| xs) ->+              let getIndent y =+                    if all isSpace y+                      then startIndent+                      else length (takeWhile isSpace y)+                  n = minimum (startIndent : fmap getIndent xs)+               in x :| (drop n <$> xs)+          else s :| []+    (atomsBefore, ls') =+      case dropWhile ((< commentLine) . fst) ls of+        [] -> (False, [])+        ((_, i) : ls'') ->+          case take 2 (dropWhile isSpace i) of+            "--" -> (False, ls'')+            "{-" -> (False, ls'')+            _ -> (True, ls'')     dropTrailing = L.dropWhileEnd isSpace     startIndent = srcSpanStartCol l - 1+    commentLine = srcSpanStartLine l --- | Get a 'String' from 'GHC.AnnotationComment'.-unAnnotationComment :: GHC.AnnotationComment -> Maybe String-unAnnotationComment = \case-  GHC.AnnDocCommentNext _ -> Nothing -- @-- |@-  GHC.AnnDocCommentPrev _ -> Nothing -- @-- ^@-  GHC.AnnDocCommentNamed _ -> Nothing -- @-- $@-  GHC.AnnDocSection _ _ -> Nothing -- @-- *@-  GHC.AnnDocOptions s -> Just s-  GHC.AnnLineComment s -> Just s-  GHC.AnnBlockComment s -> Just s+-- | Get a collection of lines from a 'Comment'.+unComment :: Comment -> NonEmpty String+unComment (Comment _ xs) = xs -liftMaybe :: Located (Maybe a) -> Maybe (Located a)-liftMaybe = \case-  L _ Nothing -> Nothing-  L l (Just a) -> Just (L l a)+-- | Check whether the 'Comment' had some non-whitespace atoms in front of+-- it in the original input.+hasAtomsBefore :: Comment -> Bool+hasAtomsBefore (Comment atomsBefore _) = atomsBefore -toRealSpan :: Located a -> Maybe (RealLocated a)-toRealSpan (L (RealSrcSpan l) a) = Just (L l a)-toRealSpan _ = Nothing+-- | Is this comment multiline-style?+isMultilineComment :: Comment -> Bool+isMultilineComment (Comment _ (x :| _)) = "{-" `L.isPrefixOf` x +----------------------------------------------------------------------------+-- Helpers+ -- | Detect and extract stack header if it is present. extractStackHeader ::   [RealLocated String] ->@@ -136,25 +150,60 @@ extractStackHeader = \case   [] -> ([], Nothing)   (x : xs) ->-    let comment = mkComment x+    let comment = snd (mkComment [] x)      in if isStackHeader (unRealSrcSpan comment)           then (xs, Just comment)           else (x : xs, Nothing)   where-    isStackHeader (Comment (x :| _)) =+    isStackHeader (Comment _ (x :| _)) =       "stack" `L.isPrefixOf` dropWhile isSpace (drop 2 x)  -- | Extract pragmas and their associated comments. extractPragmas ::+  String ->   [RealLocated String] ->   ([RealLocated Comment], [([RealLocated Comment], Pragma)])-extractPragmas = go id id+extractPragmas input = go initialLs id id   where-    go csSoFar pragmasSoFar = \case+    initialLs = zip [1 ..] (lines input)+    go ls csSoFar pragmasSoFar = \case       [] -> (csSoFar [], pragmasSoFar [])       (x : xs) ->         case parsePragma (unRealSrcSpan x) of-          Nothing -> go (csSoFar . (mkComment x :)) pragmasSoFar xs+          Nothing ->+            let (ls', x') = mkComment ls x+             in go ls' (csSoFar . (x' :)) pragmasSoFar xs           Just pragma ->             let combined = (csSoFar [], pragma)-             in go id (pragmasSoFar . (combined :)) xs+             in go ls id (pragmasSoFar . (combined :)) xs++-- | Get a 'String' from 'GHC.AnnotationComment'.+unAnnotationComment :: GHC.AnnotationComment -> Maybe String+unAnnotationComment = \case+  GHC.AnnDocCommentNext _ -> Nothing -- @-- |@+  GHC.AnnDocCommentPrev _ -> Nothing -- @-- ^@+  GHC.AnnDocCommentNamed _ -> Nothing -- @-- $@+  GHC.AnnDocSection _ _ -> Nothing -- @-- *@+  GHC.AnnDocOptions s -> Just s+  GHC.AnnLineComment s -> Just s+  GHC.AnnBlockComment s -> Just s++liftMaybe :: Located (Maybe a) -> Maybe (Located a)+liftMaybe = \case+  L _ Nothing -> Nothing+  L l (Just a) -> Just (L l a)++toRealSpan :: Located a -> Maybe (RealLocated a)+toRealSpan (L (RealSrcSpan l) a) = Just (L l a)+toRealSpan _ = Nothing++-- | Remove consecutive blank lines.+removeConseqBlanks :: NonEmpty String -> NonEmpty String+removeConseqBlanks (x :| xs) = x :| go (null x) id xs+  where+    go seenBlank acc = \case+      [] -> acc []+      (y : ys) ->+        if seenBlank && null y+          then go True acc ys+          else go (null y) (acc . (y :)) ys
src/Ormolu/Parser/Result.hs view
@@ -17,7 +17,7 @@ -- | A collection of data that represents a parsed module in Ormolu. data ParseResult = ParseResult   { -- | 'ParsedSource' from GHC-    prParsedSource :: ParsedSource,+    prParsedSource :: HsModule GhcPs,     -- | Ormolu-specfic representation of annotations     prAnns :: Anns,     -- | Stack header
src/Ormolu/Printer/Combinators.hs view
@@ -51,6 +51,7 @@      -- ** Literals     comma,+    equals,      -- ** Stateful markers     SpanMark (..),@@ -62,12 +63,10 @@ where  import Control.Monad-import Data.Data (Data) import Data.List (intersperse) import Data.Text (Text) import Ormolu.Printer.Comments import Ormolu.Printer.Internal-import Ormolu.Utils (isModule) import SrcLoc  ----------------------------------------------------------------------------@@ -79,31 +78,20 @@ -- 'Located' wrapper, it should be “discharged” with a corresponding -- 'located' invocation. located ::-  Data a =>   -- | Thing to enter   Located a ->   -- | How to render inner value   (a -> R ()) ->   R ()-located loc f = do-  let withRealLocated (L l a) g =-        case l of-          UnhelpfulSpan _ -> return ()-          RealSrcSpan l' -> g (L l' a)-  withRealLocated loc spitPrecedingComments-  let setEnclosingSpan =-        case getLoc loc of-          UnhelpfulSpan _ -> id-          RealSrcSpan orf ->-            if isModule (unLoc loc)-              then id-              else withEnclosingSpan orf-  setEnclosingSpan $ switchLayout [getLoc loc] (f (unLoc loc))-  withRealLocated loc spitFollowingComments+located (L (UnhelpfulSpan _) a) f = f a+located (L (RealSrcSpan l) a) f = do+  spitPrecedingComments l+  withEnclosingSpan l $+    switchLayout [RealSrcSpan l] (f a)+  spitFollowingComments l  -- | A version of 'located' with arguments flipped. located' ::-  Data a =>   -- | How to render inner value   (a -> R ()) ->   -- | Thing to enter@@ -288,3 +276,7 @@ -- | Print @,@. comma :: R () comma = txt ","++-- | Print @=@. Do not use @'txt' "="@.+equals :: R ()+equals = interferingTxt "="
src/Ormolu/Printer/Comments.hs view
@@ -13,13 +13,11 @@ where  import Control.Monad-import Data.Coerce (coerce)-import Data.Data (Data) import qualified Data.List.NonEmpty as NE+import Data.Maybe (listToMaybe) import qualified Data.Text as T import Ormolu.Parser.CommentStream import Ormolu.Printer.Internal-import Ormolu.Utils (isModule) import SrcLoc  ----------------------------------------------------------------------------@@ -27,9 +25,8 @@  -- | Output all preceding comments for an element at given location. spitPrecedingComments ::-  Data a =>-  -- | AST element to attach comments to-  RealLocated a ->+  -- | Span of the element to attach comments to+  RealSrcSpan ->   R () spitPrecedingComments ref = do   gotSome <- handleCommentSeries (spitPrecedingComment ref)@@ -37,16 +34,15 @@     lastMark <- getSpanMark     -- Insert a blank line between the preceding comments and the thing     -- after them if there was a blank line in the input.-    when (needsNewlineBefore (getRealSrcSpan ref) lastMark) newline+    when (needsNewlineBefore ref lastMark) newline  -- | Output all comments following an element at given location. spitFollowingComments ::-  Data a =>-  -- | AST element of attach comments to-  RealLocated a ->+  -- | Span of the element to attach comments to+  RealSrcSpan ->   R () spitFollowingComments ref = do-  trimSpanStream (getRealSrcSpan ref)+  trimSpanStream ref   void $ handleCommentSeries (spitFollowingComment ref)  -- | Output all remaining comments in the comment stream.@@ -62,49 +58,41 @@  -- | Output a single preceding comment for an element at given location. spitPrecedingComment ::-  Data a =>-  -- | AST element to attach comments to-  RealLocated a ->-  -- | Location of last comment in the series-  Maybe SpanMark ->+  -- | Span of the element to attach comments to+  RealSrcSpan ->   -- | Are we done?   R Bool-spitPrecedingComment (L ref a) mlastMark = do+spitPrecedingComment ref = do+  mlastMark <- getSpanMark   let p (L l _) = realSrcSpanEnd l <= realSrcSpanStart ref   withPoppedComment p $ \l comment -> do-    dirtyLine <--      case mlastMark of-        -- When the current line is dirty it means that something that can-        -- have comments attached to it is already on the line. To avoid-        -- problems with idempotence we cannot output the first comment-        -- immediately because it'll be attached to the previous element (on-        -- the same line) on the next run, so we play safe here and output-        -- an extra 'newline' in this case.-        Nothing -> isLineDirty -- only for very first preceding comment-        Just _ -> return False-    when (dirtyLine || needsNewlineBefore l mlastMark) newline+    lineSpans <- thisLineSpans+    let thisCommentLine = srcLocLine (realSrcSpanStart l)+        needsNewline =+          case listToMaybe lineSpans of+            Nothing -> False+            Just spn -> srcLocLine (realSrcSpanEnd spn) /= thisCommentLine+    when (needsNewline || needsNewlineBefore l mlastMark) newline     spitCommentNow l comment-    if theSameLinePre l ref && not (isModule a)+    if theSameLinePre l ref       then space       else newline  -- | Output a comment that follows element at given location immediately on -- the same line, if there is any. spitFollowingComment ::-  Data a =>   -- | AST element to attach comments to-  RealLocated a ->-  -- | Location of last comment in the series-  Maybe SpanMark ->+  RealSrcSpan ->   -- | Are we done?   R Bool-spitFollowingComment (L ref a) mlastMark = do+spitFollowingComment ref = do+  mlastMark <- getSpanMark   mnSpn <- nextEltSpan   -- Get first enclosing span that is not equal to reference span, i.e. it's   -- truly something enclosing the AST element.   meSpn <- getEnclosingSpan (/= ref)   withPoppedComment (commentFollowsElt ref mnSpn meSpn mlastMark) $ \l comment ->-    if theSameLinePost l ref && not (isModule a)+    if theSameLinePost l ref       then         if isMultilineComment comment           then space >> spitCommentNow l comment@@ -116,11 +104,10 @@  -- | Output a single remaining comment from the comment stream. spitRemainingComment ::-  -- | Location of last comment in the series-  Maybe SpanMark ->   -- | Are we done?   R Bool-spitRemainingComment mlastMark =+spitRemainingComment = do+  mlastMark <- getSpanMark   withPoppedComment (const True) $ \l comment -> do     when (needsNewlineBefore l mlastMark) newline     spitCommentNow l comment@@ -133,13 +120,13 @@ handleCommentSeries ::   -- | Given location of previous comment, output the next comment   -- returning 'True' if we're done-  (Maybe SpanMark -> R Bool) ->+  R Bool ->   -- | Whether we printed any comments   R Bool handleCommentSeries f = go False   where     go gotSome = do-      done <- getSpanMark >>= f+      done <- f       if done         then return gotSome         else go True@@ -219,9 +206,8 @@     -- 2) The comment logically belongs to the element, four cases:     logicallyFollows =       theSameLinePost l ref -- a) it's on the same line-        || isPrevHaddock comment -- b) it's a Haddock string starting with -- ^-        || continuation -- c) it's a continuation of a comment block-        || lastInEnclosing -- d) it's the last element in the enclosing construct+        || continuation -- b) it's a continuation of a comment block+        || lastInEnclosing -- c) it's the last element in the enclosing construct      -- 3) There is no other AST element between this element and the comment:     noEltBetween =@@ -248,12 +234,16 @@                        >= abs (startColumn ref - startColumn l)                    )     continuation =-      case mlastMark of-        Just (HaddockSpan _ spn) ->-          srcSpanEndLine spn + 1 == srcSpanStartLine l-        Just (CommentSpan spn) ->-          srcSpanEndLine spn + 1 == srcSpanStartLine l-        _ -> False+      -- A comment is a continuation when it doesn't have non-whitespace+      -- lexemes in front of it and goes right after the previous comment.+      not (hasAtomsBefore comment)+        && ( case mlastMark of+               Just (HaddockSpan _ spn) ->+                 srcSpanEndLine spn + 1 == srcSpanStartLine l+               Just (CommentSpan spn) ->+                 srcSpanEndLine spn + 1 == srcSpanStartLine l+               _ -> False+           )     lastInEnclosing =       case meSpn of         -- When there is no enclosing element, return false@@ -275,7 +265,7 @@     . sequence_     . NE.intersperse newline     . fmap (txt . T.pack)-    . coerce+    . unComment     $ comment   setSpanMark (CommentSpan spn) @@ -291,6 +281,6 @@     . sequence_     . NE.toList     . fmap (registerPendingCommentLine position . T.pack)-    . coerce+    . unComment     $ comment   setSpanMark (CommentSpan spn)
src/Ormolu/Printer/Internal.hs view
@@ -14,10 +14,10 @@      -- * Internal functions     txt,+    interferingTxt,     atom,     space,     newline,-    isLineDirty,     useRecordDot,     inci,     sitcc,@@ -39,6 +39,7 @@     popComment,     getEnclosingSpan,     withEnclosingSpan,+    thisLineSpans,      -- * Stateful markers     SpanMark (..),@@ -98,18 +99,19 @@ data SC = SC   { -- | Index of the next column to render     scColumn :: !Int,+    -- | Indentation level that was used for the current line+    scIndent :: !Int,     -- | Rendered source code so far     scBuilder :: Builder,     -- | Span stream     scSpanStream :: SpanStream,+    -- | Spans of atoms that have been printed on the current line so far+    scThisLineSpans :: [RealSrcSpan],     -- | Comment stream     scCommentStream :: CommentStream,     -- | Pending comment lines (in reverse order) to be inserted before next     -- newline, 'Int' is the indentation level-    scPendingComments :: ![(CommentPosition, Int, Text)],-    -- | Whether the current line is “dirty”, that is, already contains-    -- atoms that can have comments attached to them-    scDirtyLine :: !Bool,+    scPendingComments :: ![(CommentPosition, Text)],     -- | Whether to output a space before the next output     scRequestedDelimiter :: !RequestedDelimiter,     -- | An auxiliary marker for keeping track of last output element@@ -175,11 +177,12 @@     sc =       SC         { scColumn = 0,+          scIndent = 0,           scBuilder = mempty,           scSpanStream = sstream,+          scThisLineSpans = [],           scCommentStream = cstream,           scPendingComments = [],-          scDirtyLine = False,           scRequestedDelimiter = VeryBeginning,           scSpanMark = Nothing         }@@ -187,6 +190,23 @@ ---------------------------------------------------------------------------- -- Internal functions +-- | Type of the thing to output. Influences the primary low-level rendering+-- function 'spit'.+data SpitType+  = -- | Simple opaque text that breaks comment series.+    SimpleText+  | -- | Like 'SimpleText', but assume that when this text is inserted it+    -- will separate an 'Atom' and its pending comments, so insert an extra+    -- 'newline' in that case to force the pending comments and continue on+    -- a fresh line.+    InterferingText+  | -- | An atom that typically have span information in the AST and can+    -- have comments attached to it.+    Atom+  | -- | Used for rendering comment lines.+    CommentPart+  deriving (Show, Eq)+ -- | Output a fixed 'Text' fragment. The argument may not contain any line -- breaks. 'txt' is used to output all sorts of “fixed” bits of syntax like -- keywords and pipes @|@ in functional dependencies.@@ -198,8 +218,15 @@   -- | 'Text' to output   Text ->   R ()-txt = spit False False+txt = spit SimpleText +-- |+interferingTxt ::+  -- | 'Text' to output+  Text ->+  R ()+interferingTxt = spit InterferingText+ -- | Output 'Outputable' fragment of AST. This can be used to output numeric -- literals and similar. Everything that doesn't have inner structure but -- does have an 'Outputable' instance.@@ -207,47 +234,59 @@   Outputable a =>   a ->   R ()-atom = spit True False . T.pack . showOutputable+atom = spit Atom . T.pack . showOutputable  -- | Low-level non-public helper to define 'txt' and 'atom'. spit ::-  -- | Should we mark the line as dirty?-  Bool ->-  -- | Used during outputting of pending comments?-  Bool ->+  -- | Type of the thing to spit+  SpitType ->   -- | 'Text' to output   Text ->   R ()-spit dirty printingComments txt' = do+spit stype text = do   requestedDel <- R (gets scRequestedDelimiter)+  pendingComments <- R (gets scPendingComments)+  when (stype == InterferingText && not (null pendingComments)) newline   case requestedDel of     RequestedNewline -> do       R . modify $ \sc ->         sc           { scRequestedDelimiter = RequestedNothing           }-      if printingComments-        then newlineRaw-        else newline+      case stype of+        CommentPart -> newlineRaw+        _ -> newline     _ -> return ()   R $ do     i <- asks rcIndent     c <- gets scColumn-    let spaces =-          if c < i-            then T.replicate (i - c) " "-            else bool mempty " " (requestedDel == RequestedSpace)-        indentedTxt = spaces <> txt'+    closestEnclosing <- listToMaybe <$> asks rcEnclosingSpans+    let indentedTxt = spaces <> text+        spaces = T.replicate spacesN " "+        spacesN =+          if c == 0+            then i+            else bool 0 1 (requestedDel == RequestedSpace)     modify $ \sc ->       sc         { scBuilder = scBuilder sc <> fromText indentedTxt,           scColumn = scColumn sc + T.length indentedTxt,-          scDirtyLine = scDirtyLine sc || dirty,+          scIndent =+            if c == 0+              then i+              else scIndent sc,+          scThisLineSpans =+            let xs = scThisLineSpans sc+             in case stype of+                  Atom -> case closestEnclosing of+                    Nothing -> xs+                    Just x -> x : xs+                  _ -> xs,           scRequestedDelimiter = RequestedNothing,           scSpanMark =             -- If there are pending comments, do not reset last comment             -- location.-            if printingComments || (not . null . scPendingComments) sc+            if (stype == CommentPart) || (not . null . scPendingComments) sc               then scSpanMark sc               else Nothing         }@@ -279,21 +318,22 @@ -- hard to output more than one blank newline in a row. newline :: R () newline = do+  indent <- R (gets scIndent)   cs <- reverse <$> R (gets scPendingComments)   case cs of     [] -> newlineRaw-    ((position, _, _) : _) -> do+    ((position, _) : _) -> do       case position of         OnTheSameLine -> space         OnNextLine -> newlineRaw-      R . forM_ cs $ \(_, indent, txt') ->+      R . forM_ cs $ \(_, text) ->         let modRC rc =               rc                 { rcIndent = indent                 }             R m = do-              unless (T.null txt') $-                spit False True txt'+              unless (T.null text) $+                spit CommentPart text               newlineRaw          in local modRC m       R . modify $ \sc ->@@ -314,7 +354,8 @@             VeryBeginning -> builderSoFar             _ -> builderSoFar <> "\n",           scColumn = 0,-          scDirtyLine = False,+          scIndent = 0,+          scThisLineSpans = [],           scRequestedDelimiter = case scRequestedDelimiter sc of             AfterNewline -> RequestedNewline             RequestedNewline -> RequestedNewline@@ -322,14 +363,9 @@             _ -> AfterNewline         } --- | Check if the current line is “dirty”, that is, there is something on it--- that can have comments attached to it.-isLineDirty :: R Bool-isLineDirty = R (gets scDirtyLine)- -- | Return 'True' if we should print record dot syntax. useRecordDot :: R Bool-useRecordDot = R $ asks rcUseRecDot+useRecordDot = R (asks rcUseRecDot)  -- | Increase indentation level by one indentation step for the inner -- computation. 'inci' should be used when a part of code must be more@@ -346,8 +382,7 @@  -- | Set indentation level for the inner computation equal to current -- column. This makes sure that the entire inner block is uniformly--- \"shifted\" to the right. Only works (and makes sense) when enclosing--- layout is multi-line.+-- \"shifted\" to the right. sitcc :: R () -> R () sitcc (R m) = do   requestedDel <- R (gets scRequestedDelimiter)@@ -355,16 +390,9 @@   c <- R (gets scColumn)   let modRC rc =         rc-          { rcIndent = max i c + bool 0 1 (requestedDel == RequestedSpace)+          { rcIndent = max i (c + bool 0 1 (requestedDel == RequestedSpace))           }-  vlayout (R m) . R $ do-    modify $ \sc ->-      sc-        { scRequestedDelimiter = case requestedDel of-            RequestedSpace -> RequestedNothing-            other -> other-        }-    local modRC m+  R (local modRC m)  -- | Set 'Layout' for internal computation. enterLayout :: Layout -> R () -> R ()@@ -405,11 +433,10 @@   -- | 'Text' to output   Text ->   R ()-registerPendingCommentLine position txt' = R $ do-  i <- asks rcIndent+registerPendingCommentLine position text = R $ do   modify $ \sc ->     sc-      { scPendingComments = (position, i, txt') : scPendingComments sc+      { scPendingComments = (position, text) : scPendingComments sc       }  -- | Drop elements that begin before or at the same place as given@@ -468,6 +495,10 @@         { rcEnclosingSpans = spn : rcEnclosingSpans rc         } +-- | Get spans on this line so far.+thisLineSpans :: R [RealSrcSpan]+thisLineSpans = R (gets scThisLineSpans)+ ---------------------------------------------------------------------------- -- Stateful markers @@ -477,15 +508,15 @@     HaddockSpan HaddockStyle RealSrcSpan   | -- | Non-haddock comment     CommentSpan RealSrcSpan-  | -- | Non-comment span-    OtherSpan RealSrcSpan+  | -- | A statement in a do-block and such span+    StatementSpan RealSrcSpan  -- | Project 'RealSrcSpan' from 'SpanMark'. spanMarkSpan :: SpanMark -> RealSrcSpan spanMarkSpan = \case   HaddockSpan _ s -> s   CommentSpan s -> s-  OtherSpan s -> s+  StatementSpan s -> s  -- | Haddock string style. data HaddockStyle@@ -534,7 +565,7 @@  -- | Return 'True' if we can use braces in this context. canUseBraces :: R Bool-canUseBraces = R $ asks rcCanUseBraces+canUseBraces = R (asks rcCanUseBraces)  ---------------------------------------------------------------------------- -- Constants
src/Ormolu/Printer/Meat/Common.hs view
@@ -154,10 +154,11 @@   LHsDocString ->   R () p_hsDocString hstyle needsNewline (L l str) = do-  goesAfterComment <- getSpanMark >>= \case-    Just (HaddockSpan _ _) -> return True-    Just (CommentSpan _) -> return True-    _ -> return False+  let isCommentSpan = \case+        HaddockSpan _ _ -> True+        CommentSpan _ -> True+        _ -> False+  goesAfterComment <- maybe False isCommentSpan <$> getSpanMark   -- Make sure the Haddock is separated by a newline from other comments.   when goesAfterComment newline   forM_ (zip (splitDocString str) (True : repeat False)) $ \(x, isFirst) -> do
src/Ormolu/Printer/Meat/Declaration/Data.hs view
@@ -66,13 +66,17 @@           let singleConstRec = isSingleConstRec dd_cons           if singleConstRec             then space-            else breakpoint-          txt "="+            else+              if hasHaddocks dd_cons+                then newline+                else breakpoint+          equals           space+          layout <- getLayout           let s =-                vlayout-                  (space >> txt "|" >> space)-                  (newline >> txt "|" >> space)+                if layout == MultiLine || hasHaddocks dd_cons+                  then newline >> txt "|" >> space+                  else space >> txt "|" >> space               sitcc' =                 if singleConstRec                   then id@@ -150,7 +154,7 @@         PrefixCon xs -> do           p_rdrName con_name           unless (null xs) breakpoint-          inci . sitcc $ sep breakpoint (sitcc . located' p_hsType) xs+          inci . sitcc $ sep breakpoint (sitcc . located' p_hsTypePostDoc) xs         RecCon l -> do           p_rdrName con_name           breakpoint@@ -256,3 +260,9 @@     RecCon _ -> True     _ -> False isSingleConstRec _ = False++hasHaddocks :: [LConDecl GhcPs] -> Bool+hasHaddocks = any (f . unLoc)+  where+    f ConDeclH98 {..} = isJust con_doc+    f _ = False
src/Ormolu/Printer/Meat/Declaration/Rule.hs view
@@ -43,7 +43,7 @@     inci $ do       located lhs p_hsExpr       space-      txt "="+      equals       inci $ do         breakpoint         located rhs p_hsExpr
src/Ormolu/Printer/Meat/Declaration/Signature.hs view
@@ -48,11 +48,12 @@   p_rdrName n   if null ns     then p_typeAscription hswc-    else bool id inci indentTail $ do+    else do       comma       breakpoint-      sep (comma >> breakpoint) p_rdrName ns-      p_typeAscription hswc+      bool id inci indentTail $ do+        sep (comma >> breakpoint) p_rdrName ns+        p_typeAscription hswc  p_typeAscription ::   LHsSigWcType GhcPs ->@@ -74,7 +75,11 @@   R () p_patSynSig names hsib = do   txt "pattern"-  let body = p_typeSig False names HsWC {hswc_ext = NoExtField, hswc_body = hsib}+  let body =+        p_typeSig+          False+          names+          HsWC {hswc_ext = NoExtField, hswc_body = hsib}   if length names > 1     then breakpoint >> inci body     else space >> body
src/Ormolu/Printer/Meat/Declaration/Type.hs view
@@ -32,7 +32,9 @@       (p_rdrName name)       (map (located' p_hsTyVarBndr) hsq_explicit)   space-  txt "="-  breakpoint+  equals+  if hasDocStrings (unLoc t)+    then newline+    else breakpoint   inci (located t p_hsType) p_synDecl _ _ (XLHsQTyVars x) _ = noExtCon x
src/Ormolu/Printer/Meat/Declaration/TypeFamily.hs view
@@ -69,7 +69,7 @@         breakpoint         located k p_hsType       TyVarSig NoExtField bndr -> Just $ do-        txt "="+        equals         breakpoint         located bndr p_hsTyVarBndr       XFamilyResultSig x ->@@ -101,7 +101,7 @@         (p_rdrName feqn_tycon)         (located' p_hsType . typeArgToType <$> feqn_pats)     space-    txt "="+    equals     breakpoint     inci (located feqn_rhs p_hsType) p_tyFamInstEqn HsIB {hsib_body = XFamEqn x} = noExtCon x
src/Ormolu/Printer/Meat/Declaration/Value.hs view
@@ -217,35 +217,27 @@       -- about putting certain constructions in hanging positions.       endOfPats = case NE.nonEmpty m_pats of         Nothing -> case style of-          Function name -> (Just . srcSpanEnd . getLoc) name+          Function name -> Just (getLoc name)           _ -> Nothing-        Just pats -> (Just . srcSpanEnd . getLoc . NE.last) pats+        Just pats -> (Just . getLoc . NE.last) pats       isCase = \case         Case -> True         LambdaCase -> True         _ -> False   let hasGuards = withGuards grhssGRHSs-  unless (length grhssGRHSs > 1) $-    case style of-      Function _ | hasGuards -> return ()-      Function _ -> space >> txt "="-      PatternBind -> space >> txt "="-      s | isCase s && hasGuards -> return ()-      _ -> space >> txt "->"-  let grhssSpan =+      grhssSpan =         combineSrcSpans' $           getGRHSSpan . unLoc <$> NE.fromList grhssGRHSs       patGrhssSpan =         maybe           grhssSpan-          (combineSrcSpans grhssSpan . srcLocSpan)+          (combineSrcSpans grhssSpan . srcLocSpan . srcSpanEnd)           endOfPats       placement =         case endOfPats of           Nothing -> blockPlacement placer grhssGRHSs           Just spn ->-            if isOneLineSpan-              (mkSrcSpan spn (srcSpanStart grhssSpan))+            if onTheSameLine spn grhssSpan               then blockPlacement placer grhssGRHSs               else Normal       p_body = do@@ -262,6 +254,13 @@           unless whereIsEmpty breakpoint           inci $ located grhssLocalBinds p_hsLocalBinds   inci' $ do+    unless (length grhssGRHSs > 1) $+      case style of+        Function _ | hasGuards -> return ()+        Function _ -> space >> inci equals+        PatternBind -> space >> inci equals+        s | isCase s && hasGuards -> return ()+        _ -> space >> txt "->"     switchLayout [patGrhssSpan] $       placeHanging placement p_body     inci p_where@@ -287,22 +286,22 @@       space       sitcc (sep (comma >> breakpoint) (sitcc . located' p_stmt) xs)       space-      txt $ case style of-        EqualSign -> "="-        RightArrow -> "->"+      inci $ case style of+        EqualSign -> equals+        RightArrow -> txt "->"       placeHanging placement p_body   where     placement =       case endOfGuards of         Nothing -> placer (unLoc body)         Just spn ->-          if isOneLineSpan (mkSrcSpan spn (srcSpanStart (getLoc body)))+          if onTheSameLine spn (getLoc body)             then placer (unLoc body)             else Normal     endOfGuards =       case NE.nonEmpty guards of         Nothing -> Nothing-        Just gs -> (Just . srcSpanEnd . getLoc . NE.last) gs+        Just gs -> (Just . getLoc . NE.last) gs     p_body = located body render p_grhs' _ _ _ (XGRHS x) = noExtCon x @@ -363,7 +362,7 @@         -- Spacing before comments will be handled by the code         -- that prints comments, so we just have to deal with         -- blank lines between statements here.-        Just (OtherSpan lastSpn) ->+        Just (StatementSpan lastSpn) ->           if srcSpanStartLine currentSpn > srcSpanEndLine lastSpn + 1             then newline             else return ()@@ -374,7 +373,7 @@       getSpanMark >>= \case         Just (HaddockSpan _ _) -> return ()         Just (CommentSpan _) -> return ()-        _ -> setSpanMark (OtherSpan currentSpn)+        _ -> setSpanMark (StatementSpan currentSpn)  p_stmt :: Stmt GhcPs (LHsExpr GhcPs) -> R () p_stmt = p_stmt' exprPlacement p_hsExpr@@ -495,7 +494,7 @@     let p_ipBind (IPBind NoExtField (Left name) expr) = do           atom name           space-          txt "="+          equals           breakpoint           useBraces $ inci $ located expr p_hsExpr         p_ipBind (IPBind NoExtField (Right _) _) =@@ -514,9 +513,12 @@   p_rdrName hsRecFieldLbl   unless hsRecPun $ do     space-    txt "="-    let placement = exprPlacement (unLoc hsRecFieldArg)-    placeHanging placement $ located hsRecFieldArg p_hsExpr+    equals+    let placement =+          if onTheSameLine (getLoc hsRecFieldLbl) (getLoc hsRecFieldArg)+            then exprPlacement (unLoc hsRecFieldArg)+            else Normal+    placeHanging placement (located hsRecFieldArg p_hsExpr)  p_hsTupArg :: HsTupArg GhcPs -> R () p_hsTupArg = \case@@ -661,7 +663,8 @@   HsIf NoExtField _ if' then' else' ->     p_if exprPlacement p_hsExpr if' then' else'   HsMultiIf NoExtField guards -> do-    txt "if "+    txt "if"+    breakpoint     inci . inci . sitcc $ sep newline (located' (p_grhs RightArrow)) guards   HsLet NoExtField localBinds e ->     p_let p_hsExpr localBinds e@@ -819,7 +822,7 @@             breakpoint             located psb_def p_pat           ImplicitBidirectional -> do-            txt "="+            equals             breakpoint             located psb_def p_pat           ExplicitBidirectional mgroup -> do@@ -901,10 +904,12 @@   breakpoint   inci $ do     txt "then"+    space     located then' $ \x ->       placeHanging (placer x) (render x)     breakpoint     txt "else"+    space     located else' $ \x ->       placeHanging (placer x) (render x) @@ -1004,7 +1009,7 @@     p_rdrName (rdrNameFieldOcc x)   unless hsRecPun $ do     space-    txt "="+    equals     breakpoint     inci (located hsRecFieldArg p_pat) 
src/Ormolu/Printer/Meat/Module.hs view
@@ -34,9 +34,9 @@   -- | Whether to use postfix qualified imports   Bool ->   -- | AST to print-  ParsedSource ->+  HsModule GhcPs ->   R ()-p_hsModule mstackHeader shebangs pragmas qualifiedPost (L _ HsModule {..}) = do+p_hsModule mstackHeader shebangs pragmas qualifiedPost HsModule {..} = do   let deprecSpan = maybe [] (\(L s _) -> [s]) hsmodDeprecMessage       exportSpans = maybe [] (\(L s _) -> [s]) hsmodExports   switchLayout (deprecSpan <> exportSpans) $ do
src/Ormolu/Printer/Meat/Type.hs view
@@ -5,6 +5,7 @@ -- | Rendering of types. module Ormolu.Printer.Meat.Type   ( p_hsType,+    p_hsTypePostDoc,     hasDocStrings,     p_hsContext,     p_hsTyVarBndr,@@ -23,14 +24,22 @@ import Ormolu.Utils  p_hsType :: HsType GhcPs -> R ()-p_hsType t = p_hsType' (hasDocStrings t) t+p_hsType t = p_hsType' (hasDocStrings t) PipeStyle t -p_hsType' :: Bool -> HsType GhcPs -> R ()-p_hsType' multilineArgs = \case+p_hsTypePostDoc :: HsType GhcPs -> R ()+p_hsTypePostDoc t = p_hsType' (hasDocStrings t) CaretStyle t++-- | How to render Haddocks associated with a type.+data TypeDocStyle+  = PipeStyle+  | CaretStyle++p_hsType' :: Bool -> TypeDocStyle -> HsType GhcPs -> R ()+p_hsType' multilineArgs docStyle = \case   HsForAllTy NoExtField visibility bndrs t -> do     p_forallBndrs visibility p_hsTyVarBndr bndrs     interArgBreak-    p_hsType' multilineArgs (unLoc t)+    p_hsTypeR (unLoc t)   HsQualTy NoExtField qs t -> do     located qs p_hsContext     space@@ -114,9 +123,15 @@     space     inci (located k p_hsType)   HsSpliceTy NoExtField splice -> p_hsSplice splice-  HsDocTy NoExtField t str -> do-    p_hsDocString Pipe True str-    located t p_hsType+  HsDocTy NoExtField t str ->+    case docStyle of+      PipeStyle -> do+        p_hsDocString Pipe True str+        located t p_hsType+      CaretStyle -> do+        located t p_hsType+        newline+        p_hsDocString Caret False str   HsBangTy NoExtField (HsSrcBang _ u s) t -> do     case u of       SrcUnpack -> txt "{-# UNPACK #-}" >> space@@ -156,14 +171,14 @@   where     isPromoted = \case       HsTyVar _ IsPromoted _ -> True-      HsExplicitListTy {} -> True       HsExplicitTupleTy {} -> True+      HsExplicitListTy {} -> True       _ -> False     interArgBreak =       if multilineArgs         then newline         else breakpoint-    p_hsTypeR = p_hsType' multilineArgs+    p_hsTypeR = p_hsType' multilineArgs docStyle  -- | Return 'True' if at least one argument in 'HsType' has a doc string -- attached to it.
src/Ormolu/Printer/Operators.hs view
@@ -12,8 +12,9 @@  import Data.Function (on) import qualified Data.List as L+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as M import Data.Maybe (fromMaybe, mapMaybe)-import Data.Ord (Down (Down), comparing) import GHC import OccName (mkVarOcc) import Ormolu.Utils (unSrcSpan)@@ -56,7 +57,7 @@ reassociateOpTreeWith ::   forall ty op.   -- | Fixity map for operators-  [(RdrName, Fixity)] ->+  Map RdrName Fixity ->   -- | How to get the name of an operator   (op -> Maybe RdrName) ->   -- | Original 'OpTree'@@ -68,12 +69,12 @@     fixityOf :: op -> Fixity     fixityOf op = fromMaybe defaultFixity $ do       opName <- getOpName op-      lookup opName fixityMap+      M.lookup opName fixityMap     -- Here, left branch is already associated and the root alongside with     -- the right branch is right-associated. This function picks up one item     -- from the right and inserts it correctly to the left.     ---    -- Also, we are using the 'compareFixity' function which returns if the+    -- Also, we are using the 'compareFixity' function which tells if the     -- expression should associate to right.     go :: OpTree ty op -> OpTree ty op     -- base cases@@ -93,6 +94,16 @@         then go $ OpBranch (OpBranch l op (go $ OpBranch r op' l')) op'' r'         else go $ OpBranch (OpBranch (OpBranch l op r) op' l') op'' r' +-- | A score assigned to an operator.+data Score+  = -- | The operator was placed at the beginning of a line+    AtBeginning Int+  | -- | The operator was placed at the end of a line+    AtEnd+  | -- | The operator was placed in between arguments on a single line+    InBetween+  deriving (Eq, Ord)+ -- | Build a map of inferred 'Fixity's from an 'OpTree'. buildFixityMap ::   forall ty op.@@ -101,23 +112,26 @@   -- | Operator tree   OpTree (Located ty) (Located op) ->   -- | Fixity map-  [(RdrName, Fixity)]+  Map RdrName Fixity buildFixityMap getOpName opTree =-  concatMap (\(i, ns) -> map (\(n, _) -> (n, fixity i InfixL)) ns)-    . zip [0 ..]-    . L.groupBy (doubleWithinEps 0.00001 `on` snd)-    . (overrides ++)-    . modeScores+  addOverrides+    . M.fromList+    . concatMap (\(i, ns) -> map (\(n, _) -> (n, fixity i InfixL)) ns)+    . zip [1 ..]+    . L.groupBy ((==) `on` snd)+    . selectScores     $ score opTree   where-    -- Add a special case for ($), since it is pretty unlikely for someone-    -- to override it.-    overrides :: [(RdrName, Double)]-    overrides =-      [ (mkRdrUnqual $ mkVarOcc "$", -1)-      ]-    -- Assign scores to operators based on their location in the source.-    score :: OpTree (Located ty) (Located op) -> [(RdrName, Double)]+    addOverrides :: Map RdrName Fixity -> Map RdrName Fixity+    addOverrides m =+      let mk k v = (mkRdrUnqual (mkVarOcc k), fixity v InfixL)+       in M.fromList+            [ mk "$" 0,+              mk "." 9+            ]+            `M.union` m+    fixity = Fixity NoSourceText+    score :: OpTree (Located ty) (Located op) -> [(RdrName, Score)]     score (OpNode _) = []     score (OpBranch l o r) = fromMaybe (score r) $ do       -- If we fail to get any of these, 'defaultFixity' will be used by@@ -129,46 +143,25 @@       oc <- srcSpanStartCol <$> unSrcSpan (getLoc o) -- operator column       opName <- getOpName (unLoc o)       let s-            | le < ob =-              -- if the operator is in the beginning of a line, assign-              -- a score relative to its column within range [0, 1).-              fromIntegral oc / fromIntegral (maxCol + 1)-            | oe < rb =-              -- if the operator is in the end of the line, assign the-              -- score 1.-              1-            | otherwise =-              2 -- otherwise, assign a high score.+            | le < ob = AtBeginning oc+            | oe < rb = AtEnd+            | otherwise = InBetween       return $ (opName, s) : score r-    -- Pick the most common score per 'RdrName'.-    modeScores :: [(RdrName, Double)] -> [(RdrName, Double)]-    modeScores =+    selectScores :: [(RdrName, Score)] -> [(RdrName, Score)]+    selectScores =       L.sortOn snd         . mapMaybe           ( \case               [] -> Nothing-              xs@((n, _) : _) -> Just (n, mode $ map snd xs)+              xs@((n, _) : _) -> Just (n, selectScore $ map snd xs)           )         . L.groupBy ((==) `on` fst)         . L.sort-    -- Return the most common number, leaning to the smaller-    -- one in case of a tie.-    mode :: [Double] -> Double-    mode =-      head-        . L.minimumBy (comparing (Down . length))-        . L.groupBy (doubleWithinEps 0.0001)-        . L.sort-    -- The start column of the rightmost operator.-    maxCol = go opTree-      where-        go (OpNode (L _ _)) = 0-        go (OpBranch l (L o _) r) =-          maximum-            [ go l,-              maybe 0 srcSpanStartCol (unSrcSpan o),-              go r-            ]+    selectScore :: [Score] -> Score+    selectScore xs =+      case filter (/= InBetween) xs of+        [] -> InBetween+        xs' -> maximum xs'  ---------------------------------------------------------------------------- -- Helpers@@ -182,9 +175,3 @@   OpBranch (OpNode l) lop (normalizeOpTree r) normalizeOpTree (OpBranch (OpBranch l' lop' r') lop r) =   normalizeOpTree (OpBranch l' lop' (OpBranch r' lop r))--fixity :: Int -> FixityDirection -> Fixity-fixity = Fixity NoSourceText--doubleWithinEps :: Double -> Double -> Double -> Bool-doubleWithinEps eps a b = abs (a - b) < eps
src/Ormolu/Utils.hs view
@@ -6,7 +6,6 @@   ( RelativePos (..),     attachRelativePos,     combineSrcSpans',-    isModule,     notImplemented,     showOutputable,     splitDocString,@@ -15,10 +14,10 @@     incSpanLine,     separatedByBlank,     separatedByBlankNE,+    onTheSameLine,   ) where -import Data.Data (Data, showConstr, toConstr) import Data.List (dropWhileEnd) import qualified Data.List.NonEmpty as NE import Data.List.NonEmpty (NonEmpty (..))@@ -52,10 +51,6 @@ combineSrcSpans' :: NonEmpty SrcSpan -> SrcSpan combineSrcSpans' (x :| xs) = foldr combineSrcSpans x xs --- | Return 'True' if given element of AST is module.-isModule :: Data a => a -> Bool-isModule x = showConstr (toConstr x) == "HsModule"- -- | Placeholder for things that are not yet implemented. notImplemented :: String -> a notImplemented msg = error $ "not implemented yet: " ++ msg@@ -139,3 +134,8 @@ -- | Do two declaration groups have a blank between them? separatedByBlankNE :: (a -> SrcSpan) -> NonEmpty a -> NonEmpty a -> Bool separatedByBlankNE loc a b = separatedByBlank loc (NE.last a) (NE.head b)++-- | Return 'True' if one span ends on the same line the second one starts.+onTheSameLine :: SrcSpan -> SrcSpan -> Bool+onTheSameLine a b =+  isOneLineSpan (mkSrcSpan (srcSpanEnd a) (srcSpanStart b))
tests/Ormolu/PrinterSpec.hs view
@@ -62,7 +62,7 @@ -- | A version of 'shouldBe' that is specialized to comparing 'Text' values. -- It also prints multi-line snippets in a more readable form. shouldMatch :: Bool -> Text -> Text -> Expectation-shouldMatch idempotencyTest actual expected =+shouldMatch idempotenceTest actual expected =   when (actual /= expected) . expectationFailure $     unlines       [ ">>>>>>>>>>>>>>>>>>>>>> expected (" ++ pass ++ "):",@@ -72,8 +72,8 @@       ]   where     pass =-      if idempotencyTest-        then "idempotency pass"+      if idempotenceTest+        then "idempotence pass"         else "first pass"  examplesDir :: Path Rel Dir