on master help sign in
Preserve grouping of do statements
145f729Mark Karpov
CHANGELOG.md+5
@@ -1,3 +1,8 @@
1+## Unreleased
2+
3+* Grouping of statements in `do`-blocks is now respected. [Issue
4+ 74](https://github.com/tweag/ormolu/issues/74).
5+
1## Ormolu 0.0.4.0
6## Ormolu 0.0.4.0
2
7
3* When given several files to format, Ormolu does not stop on the first
8* When given several files to format, Ormolu does not stop on the first
data/examples/declaration/value/function/do/blocks-out.hs+1
@@ -15,4 +15,5 @@ readInClause = do
15 <|> do
15 <|> do
16 optional g_Semi
16 optional g_Semi
17 void allspacing
17 void allspacing
18+
18 return things
19 return things
data/examples/declaration/value/function/do/comment-alignment-out.hsnew file+9
@@ -0,0 +1,9 @@
1+main = do
2+ case blah of
3+ Nothing -> return ()
4+ Just xs -> do
5+ forM_ xs foo
6+ bar
7+ -- and here it goes
8+ unless bobla $
9+ quux
data/examples/declaration/value/function/do/comment-alignment.hsnew file+9
@@ -0,0 +1,9 @@
1+main = do
2+ case blah of
3+ Nothing -> return ()
4+ Just xs -> do
5+ forM_ xs foo
6+ bar
7+ -- and here it goes
8+ unless bobla $
9+ quux
data/examples/declaration/value/function/do/comment-spacing-out.hsnew file+7
@@ -0,0 +1,7 @@
1+foo = do
2+ bar
3+ -- foo
4+ baz
5+
6+ -- foo
7+ quux
data/examples/declaration/value/function/do/comment-spacing.hsnew file+7
@@ -0,0 +1,7 @@
1+foo = do
2+ bar
3+ -- foo
4+ baz
5+
6+ -- foo
7+ quux
data/examples/declaration/value/function/do/recursive-do-rec-out.hs+5
@@ -2,13 +2,18 @@
2
2
3foo = do
3foo = do
4 rec a <- b + 5
4 rec a <- b + 5
5+
5 let d = c
6 let d = c
7+
6 b <- a * 5
8 b <- a * 5
9+
7 something
10 something
11+
8 c <- a + b
12 c <- a + b
9 print c
13 print c
10 rec something $ do
14 rec something $ do
11 x <- a
15 x <- a
12 print x
16 print x
17+
13 y <- c
18 y <- c
14 print y
19 print y
expected-failures/Agda.txt+2−2
@@ -11,7 +11,7 @@ Formatting is not idempotent:
11Please, consider reporting the bug.
11Please, consider reporting the bug.
12
12
13Parsing of formatted code failed:
13Parsing of formatted code failed:
14− src/full/Agda/Syntax/Translation/ConcreteToAbstract.hs<rendered>:509:7-13
14+ src/full/Agda/Syntax/Translation/ConcreteToAbstract.hs<rendered>:525:7-13
15 parse error on input `C.QName'
15 parse error on input `C.QName'
16Please, consider reporting the bug.
16Please, consider reporting the bug.
17
17
@@ -22,7 +22,7 @@ Formatting is not idempotent:
22Please, consider reporting the bug.
22Please, consider reporting the bug.
23
23
24Formatting is not idempotent:
24Formatting is not idempotent:
25− src/full/Agda/TypeChecking/MetaVars.hs<rendered>:1419:16
25+ src/full/Agda/TypeChecking/MetaVars.hs<rendered>:1448:16
26 before: "ubstCand = -- | a po"
26 before: "ubstCand = -- | a po"
27 after: "ubstCand =\n -- | a "
27 after: "ubstCand =\n -- | a "
28Please, consider reporting the bug.
28Please, consider reporting the bug.
expected-failures/brick.txt+1−1
@@ -1,5 +1,5 @@
1Formatting is not idempotent:
1Formatting is not idempotent:
2− src/Brick/Widgets/List.hs<rendered>:571:3
2+ src/Brick/Widgets/List.hs<rendered>:573:3
3 before: "t at i\n in -- If t"
3 before: "t at i\n in -- If t"
4 after: "t at i\n -"
4 after: "t at i\n -"
5Please, consider reporting the bug.
5Please, consider reporting the bug.
expected-failures/idris.txt+3−3
@@ -35,7 +35,7 @@ Formatting is not idempotent:
35Please, consider reporting the bug.
35Please, consider reporting the bug.
36
36
37Formatting is not idempotent:
37Formatting is not idempotent:
38− src/Idris/Elab/Term.hs<rendered>:423:47
38+ src/Idris/Elab/Term.hs<rendered>:432:47
39 before: "K\" _ _)) = -- for ch"
39 before: "K\" _ _)) = -- for ch"
40 after: "K\" _ _)) =\n -- "
40 after: "K\" _ _)) =\n -- "
41Please, consider reporting the bug.
41Please, consider reporting the bug.
@@ -65,13 +65,13 @@ The GHC parser (in Haddock mode) failed:
65 parse error on input `@'
65 parse error on input `@'
66
66
67Formatting is not idempotent:
67Formatting is not idempotent:
68− src/Idris/PartialEval.hs<rendered>:280:42
68+ src/Idris/PartialEval.hs<rendered>:281:42
69 before: ": ns) as = -- Droppe"
69 before: ": ns) as = -- Droppe"
70 after: ": ns) as =\n -- "
70 after: ": ns) as =\n -- "
71Please, consider reporting the bug.
71Please, consider reporting the bug.
72
72
73Formatting is not idempotent:
73Formatting is not idempotent:
74− src/Idris/ProofSearch.hs<rendered>:483:20
74+ src/Idris/ProofSearch.hs<rendered>:487:20
75 before: "OK ty hs = -- if any"
75 before: "OK ty hs = -- if any"
76 after: "OK ty hs =\n -- "
76 after: "OK ty hs =\n -- "
77Please, consider reporting the bug.
77Please, consider reporting the bug.
expected-failures/intero.txt+1−1
@@ -1,5 +1,5 @@
1Formatting is not idempotent:
1Formatting is not idempotent:
2− src/InteractiveUI.hs<rendered>:2595:36
2+ src/InteractiveUI.hs<rendered>:2635:36
3 before: "Decl d2) = -- A bit "
3 before: "Decl d2) = -- A bit "
4 after: "Decl d2) =\n -- A bi"
4 after: "Decl d2) =\n -- A bi"
5Please, consider reporting the bug.
5Please, consider reporting the bug.
expected-failures/leksah.txt+4−4
@@ -1,11 +1,11 @@
1Formatting is not idempotent:
1Formatting is not idempotent:
2− src/IDE/Pane/Modules.hs<rendered>:1182:7
2+ src/IDE/Pane/Modules.hs<rendered>:1188:7
3 before: "cr\n -- show"
3 before: "cr\n -- show"
4 after: "cr\n in -- show"
4 after: "cr\n in -- show"
5Please, consider reporting the bug.
5Please, consider reporting the bug.
6
6
7Formatting is not idempotent:
7Formatting is not idempotent:
8− src/IDE/SymbolNavigation.hs<rendered>:229:8
8+ src/IDE/SymbolNavigation.hs<rendered>:233:8
9− before: ")\n return [\n {-i"
9+ before: "\n\n return [\n {-i"
10− after: ")\n return\n [ {-i"
10+ after: "\n\n return\n [ {-i"
11Please, consider reporting the bug.
11Please, consider reporting the bug.
expected-failures/pandoc.txt+1−1
@@ -9,7 +9,7 @@ Formatting is not idempotent:
9Please, consider reporting the bug.
9Please, consider reporting the bug.
10
10
11Formatting is not idempotent:
11Formatting is not idempotent:
12− src/Text/Pandoc/Writers/Docx.hs<rendered>:960:25
12+ src/Text/Pandoc/Writers/Docx.hs<rendered>:997:25
13 before: " -- w:p\n "
13 before: " -- w:p\n "
14 after: " -- w:p\n "
14 after: " -- w:p\n "
15Please, consider reporting the bug.
15Please, consider reporting the bug.
src/Ormolu/Printer/Combinators.hs+5−3
@@ -52,10 +52,12 @@ module Ormolu.Printer.Combinators
52 -- ** Literals
52 -- ** Literals
53 comma,
53 comma,
54
54
55− -- ** Comments
55+ -- ** Stateful markers
56+ SpanMark (..),
57+ spanMarkSpan,
56 HaddockStyle (..),
58 HaddockStyle (..),
57− setLastCommentSpan,
59+ setSpanMark,
58− getLastCommentSpan,
60+ getSpanMark,
59 )
61 )
60where
62where
61
63
src/Ormolu/Printer/Comments.hs+31−27
@@ -1,3 +1,4 @@
1+{-# LANGUAGE LambdaCase #-}
1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
2
3
3-- | Helpers for formatting of comments. This is low-level code, use
4-- | Helpers for formatting of comments. This is low-level code, use
@@ -35,10 +36,10 @@ spitPrecedingComments ::
35spitPrecedingComments ref = do
36spitPrecedingComments ref = do
36 gotSome <- handleCommentSeries (spitPrecedingComment ref)
37 gotSome <- handleCommentSeries (spitPrecedingComment ref)
37 when gotSome $ do
38 when gotSome $ do
39+ lastMark <- getSpanMark
38 -- Insert a blank line between the preceding comments and the thing
40 -- Insert a blank line between the preceding comments and the thing
39 -- after them if there was a blank line in the input.
41 -- after them if there was a blank line in the input.
40− lastSpn <- fmap snd <$> getLastCommentSpan
41− when (needsNewlineBefore (getRealSrcSpan ref) lastSpn) newline
42+ when (needsNewlineBefore (getRealSrcSpan ref) lastMark) newline
42
43
43-- | Output all comments following an element at given location.
44-- | Output all comments following an element at given location.
44spitFollowingComments ::
45spitFollowingComments ::
@@ -73,14 +74,14 @@ spitPrecedingComment ::
73 -- | AST element to attach comments to
74 -- | AST element to attach comments to
74 RealLocated a ->
75 RealLocated a ->
75 -- | Location of last comment in the series
76 -- | Location of last comment in the series
76− Maybe RealSrcSpan ->
77+ Maybe SpanMark ->
77 -- | Are we done?
78 -- | Are we done?
78 R Bool
79 R Bool
79−spitPrecedingComment (L ref a) mlastSpn = do
80+spitPrecedingComment (L ref a) mlastMark = do
80 let p (L l _) = realSrcSpanEnd l <= realSrcSpanStart ref
81 let p (L l _) = realSrcSpanEnd l <= realSrcSpanStart ref
81 withPoppedComment p $ \l comment -> do
82 withPoppedComment p $ \l comment -> do
82 dirtyLine <-
83 dirtyLine <-
83− case mlastSpn of
84+ case mlastMark of
84 -- When the current line is dirty it means that something that can
85 -- When the current line is dirty it means that something that can
85 -- have comments attached to it is already on the line. To avoid
86 -- have comments attached to it is already on the line. To avoid
86 -- problems with idempotence we cannot output the first comment
87 -- problems with idempotence we cannot output the first comment
@@ -89,7 +90,7 @@ spitPrecedingComment (L ref a) mlastSpn = do
89 -- an extra 'newline' in this case.
90 -- an extra 'newline' in this case.
90 Nothing -> isLineDirty -- only for very first preceding comment
91 Nothing -> isLineDirty -- only for very first preceding comment
91 Just _ -> return False
92 Just _ -> return False
92− when (dirtyLine || needsNewlineBefore l mlastSpn) newline
93+ when (dirtyLine || needsNewlineBefore l mlastMark) newline
93 spitCommentNow l comment
94 spitCommentNow l comment
94 if theSameLinePre l ref && not (isModule a)
95 if theSameLinePre l ref && not (isModule a)
95 then space
96 then space
@@ -102,34 +103,34 @@ spitFollowingComment ::
102 -- | AST element to attach comments to
103 -- | AST element to attach comments to
103 RealLocated a ->
104 RealLocated a ->
104 -- | Location of last comment in the series
105 -- | Location of last comment in the series
105− Maybe RealSrcSpan ->
106+ Maybe SpanMark ->
106 -- | Are we done?
107 -- | Are we done?
107 R Bool
108 R Bool
108−spitFollowingComment (L ref a) mlastSpn = do
109+spitFollowingComment (L ref a) mlastMark = do
109 mnSpn <- nextEltSpan
110 mnSpn <- nextEltSpan
110 -- Get first enclosing span that is not equal to reference span, i.e. it's
111 -- Get first enclosing span that is not equal to reference span, i.e. it's
111 -- truly something enclosing the AST element.
112 -- truly something enclosing the AST element.
112 meSpn <- getEnclosingSpan (/= ref)
113 meSpn <- getEnclosingSpan (/= ref)
113− withPoppedComment (commentFollowsElt ref mnSpn meSpn mlastSpn) $ \l comment ->
114+ withPoppedComment (commentFollowsElt ref mnSpn meSpn mlastMark) $ \l comment ->
114 if theSameLinePost l ref && not (isModule a)
115 if theSameLinePost l ref && not (isModule a)
115 then
116 then
116 if isMultilineComment comment
117 if isMultilineComment comment
117 then space >> spitCommentNow l comment
118 then space >> spitCommentNow l comment
118 else spitCommentPending OnTheSameLine l comment
119 else spitCommentPending OnTheSameLine l comment
119 else do
120 else do
120− when (needsNewlineBefore l mlastSpn) $
121+ when (needsNewlineBefore l mlastMark) $
121 registerPendingCommentLine OnNextLine ""
122 registerPendingCommentLine OnNextLine ""
122 spitCommentPending OnNextLine l comment
123 spitCommentPending OnNextLine l comment
123
124
124-- | Output a single remaining comment from the comment stream.
125-- | Output a single remaining comment from the comment stream.
125spitRemainingComment ::
126spitRemainingComment ::
126 -- | Location of last comment in the series
127 -- | Location of last comment in the series
127− Maybe RealSrcSpan ->
128+ Maybe SpanMark ->
128 -- | Are we done?
129 -- | Are we done?
129 R Bool
130 R Bool
130−spitRemainingComment mlastSpn =
131+spitRemainingComment mlastMark =
131 withPoppedComment (const True) $ \l comment -> do
132 withPoppedComment (const True) $ \l comment -> do
132− when (needsNewlineBefore l mlastSpn) newline
133+ when (needsNewlineBefore l mlastMark) newline
133 spitCommentNow l comment
134 spitCommentNow l comment
134 newline
135 newline
135
136
@@ -140,13 +141,13 @@ spitRemainingComment mlastSpn =
140handleCommentSeries ::
141handleCommentSeries ::
141 -- | Given location of previous comment, output the next comment
142 -- | Given location of previous comment, output the next comment
142 -- returning 'True' if we're done
143 -- returning 'True' if we're done
143− (Maybe RealSrcSpan -> R Bool) ->
144+ (Maybe SpanMark -> R Bool) ->
144 -- | Whether we printed any comments
145 -- | Whether we printed any comments
145 R Bool
146 R Bool
146handleCommentSeries f = go False
147handleCommentSeries f = go False
147 where
148 where
148 go gotSome = do
149 go gotSome = do
149− done <- getLastCommentSpan >>= f . fmap snd
150+ done <- getSpanMark >>= f
150 if done
151 if done
151 then return gotSome
152 then return gotSome
152 else go True
153 else go True
@@ -172,13 +173,13 @@ needsNewlineBefore ::
172 -- | Current comment span
173 -- | Current comment span
173 RealSrcSpan ->
174 RealSrcSpan ->
174 -- | Last printed comment span
175 -- | Last printed comment span
175− Maybe RealSrcSpan ->
176+ Maybe SpanMark ->
176 Bool
177 Bool
177−needsNewlineBefore l mlastSpn =
178+needsNewlineBefore l mlastMark =
178− case mlastSpn of
179+ case spanMarkSpan <$> mlastMark of
179 Nothing -> False
180 Nothing -> False
180− Just lastSpn ->
181+ Just lastMark ->
181− srcSpanStartLine l > srcSpanEndLine lastSpn + 1
182+ srcSpanStartLine l > srcSpanEndLine lastMark + 1
182
183
183-- | Is the preceding comment and AST element are on the same line?
184-- | Is the preceding comment and AST element are on the same line?
184theSameLinePre ::
185theSameLinePre ::
@@ -209,11 +210,11 @@ commentFollowsElt ::
209 -- | Location of enclosing AST element
210 -- | Location of enclosing AST element
210 Maybe RealSrcSpan ->
211 Maybe RealSrcSpan ->
211 -- | Location of last comment in the series
212 -- | Location of last comment in the series
212− Maybe RealSrcSpan ->
213+ Maybe SpanMark ->
213 -- | Comment to test
214 -- | Comment to test
214 RealLocated Comment ->
215 RealLocated Comment ->
215 Bool
216 Bool
216−commentFollowsElt ref mnSpn meSpn mlastSpn (L l comment) =
217+commentFollowsElt ref mnSpn meSpn mlastMark (L l comment) =
217 -- A comment follows a AST element if all 4 conditions are satisfied:
218 -- A comment follows a AST element if all 4 conditions are satisfied:
218 goesAfter
219 goesAfter
219 && logicallyFollows
220 && logicallyFollows
@@ -255,9 +256,12 @@ commentFollowsElt ref mnSpn meSpn mlastSpn (L l comment) =
255 >= abs (startColumn ref - startColumn l)
256 >= abs (startColumn ref - startColumn l)
256 )
257 )
257 continuation =
258 continuation =
258− case mlastSpn of
259+ case mlastMark of
260+ Just (HaddockSpan _ spn) ->
261+ srcSpanEndLine spn + 1 == srcSpanStartLine l
262+ Just (CommentSpan spn) ->
263+ srcSpanEndLine spn + 1 == srcSpanStartLine l
259− Nothing -> False
264+ _ -> False
260− Just spn -> srcSpanEndLine spn + 1 == srcSpanStartLine l
261 lastInEnclosing =
265 lastInEnclosing =
262 case meSpn of
266 case meSpn of
263 -- When there is no enclosing element, return false
267 -- When there is no enclosing element, return false
@@ -281,7 +285,7 @@ spitCommentNow spn comment = do
281 . fmap (txt . T.pack)
285 . fmap (txt . T.pack)
282 . coerce
286 . coerce
283 $ comment
287 $ comment
284− setLastCommentSpan Nothing spn
288+ setSpanMark (CommentSpan spn)
285
289
286-- | Output a 'Comment' at the end of correct line or after it depending on
290-- | Output a 'Comment' at the end of correct line or after it depending on
287-- 'CommentPosition'. Used for comments that may potentially follow on the
291-- 'CommentPosition'. Used for comments that may potentially follow on the
@@ -297,4 +301,4 @@ spitCommentPending position spn comment = do
297 . fmap (registerPendingCommentLine position . T.pack)
301 . fmap (registerPendingCommentLine position . T.pack)
298 . coerce
302 . coerce
299 $ comment
303 $ comment
300− setLastCommentSpan Nothing spn
304+ setSpanMark (CommentSpan spn)
src/Ormolu/Printer/Internal.hs+38−16
@@ -1,4 +1,5 @@
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE LambdaCase #-}
2{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE RankNTypes #-}
4{-# LANGUAGE RankNTypes #-}
4
5
@@ -38,9 +39,13 @@ module Ormolu.Printer.Internal
38 popComment,
39 popComment,
39 getEnclosingSpan,
40 getEnclosingSpan,
40 withEnclosingSpan,
41 withEnclosingSpan,
42+
43+ -- * Stateful markers
44+ SpanMark (..),
45+ spanMarkSpan,
41 HaddockStyle (..),
46 HaddockStyle (..),
42− setLastCommentSpan,
47+ setSpanMark,
43− getLastCommentSpan,
48+ getSpanMark,
44
49
45 -- * Annotations
50 -- * Annotations
46 getAnns,
51 getAnns,
@@ -107,8 +112,8 @@ data SC = SC
107 scDirtyLine :: !Bool,
112 scDirtyLine :: !Bool,
108 -- | Whether to output a space before the next output
113 -- | Whether to output a space before the next output
109 scRequestedDelimiter :: !RequestedDelimiter,
114 scRequestedDelimiter :: !RequestedDelimiter,
110− -- | Span of last output comment
115+ -- | An auxiliary marker for keeping track of last output element
111− scLastCommentSpan :: !(Maybe (Maybe HaddockStyle, RealSrcSpan))
116+ scSpanMark :: !(Maybe SpanMark)
112 }
117 }
113
118
114-- | Make sure next output is delimited by one of the following.
119-- | Make sure next output is delimited by one of the following.
@@ -176,7 +181,7 @@ runR (R m) sstream cstream anns recDot =
176 scPendingComments = [],
181 scPendingComments = [],
177 scDirtyLine = False,
182 scDirtyLine = False,
178 scRequestedDelimiter = VeryBeginning,
183 scRequestedDelimiter = VeryBeginning,
179− scLastCommentSpan = Nothing
184+ scSpanMark = Nothing
180 }
185 }
181
186
182----------------------------------------------------------------------------
187----------------------------------------------------------------------------
@@ -239,11 +244,11 @@ spit dirty printingComments txt' = do
239 scColumn = scColumn sc + T.length indentedTxt,
244 scColumn = scColumn sc + T.length indentedTxt,
240 scDirtyLine = scDirtyLine sc || dirty,
245 scDirtyLine = scDirtyLine sc || dirty,
241 scRequestedDelimiter = RequestedNothing,
246 scRequestedDelimiter = RequestedNothing,
242− scLastCommentSpan =
247+ scSpanMark =
243 -- If there are pending comments, do not reset last comment
248 -- If there are pending comments, do not reset last comment
244 -- location.
249 -- location.
245 if printingComments || (not . null . scPendingComments) sc
250 if printingComments || (not . null . scPendingComments) sc
246− then scLastCommentSpan sc
251+ then scSpanMark sc
247 else Nothing
252 else Nothing
248 }
253 }
249
254
@@ -463,6 +468,25 @@ withEnclosingSpan spn (R m) = R (local modRC m)
463 { rcEnclosingSpans = spn : rcEnclosingSpans rc
468 { rcEnclosingSpans = spn : rcEnclosingSpans rc
464 }
469 }
465
470
471+----------------------------------------------------------------------------
472+-- Stateful markers
473+
474+-- | An auxiliary marker for keeping track of last output element.
475+data SpanMark
476+ = -- | Haddock comment
477+ HaddockSpan HaddockStyle RealSrcSpan
478+ | -- | Non-haddock comment
479+ CommentSpan RealSrcSpan
480+ | -- | Non-comment span
481+ OtherSpan RealSrcSpan
482+
483+-- | Project 'RealSrcSpan' from 'SpanMark'.
484+spanMarkSpan :: SpanMark -> RealSrcSpan
485+spanMarkSpan = \case
486+ HaddockSpan _ s -> s
487+ CommentSpan s -> s
488+ OtherSpan s -> s
489+
466-- | Haddock string style.
490-- | Haddock string style.
467data HaddockStyle
491data HaddockStyle
468 = -- | @-- |@
492 = -- | @-- |@
@@ -475,20 +499,18 @@ data HaddockStyle
475 Named String
499 Named String
476
500
477-- | Set span of last output comment.
501-- | Set span of last output comment.
478−setLastCommentSpan ::
502+setSpanMark ::
479− -- | 'HaddockStyle' or 'Nothing' if it's a non-Haddock comment
503+ -- | Span mark to set
480− Maybe HaddockStyle ->
504+ SpanMark ->
481− -- | Location of last printed comment
482− RealSrcSpan ->
483 R ()
505 R ()
484−setLastCommentSpan mhStyle spn = R . modify $ \sc ->
506+setSpanMark spnMark = R . modify $ \sc ->
485 sc
507 sc
486− { scLastCommentSpan = Just (mhStyle, spn)
508+ { scSpanMark = Just spnMark
487 }
509 }
488
510
489-- | Get span of last output comment.
511-- | Get span of last output comment.
490−getLastCommentSpan :: R (Maybe (Maybe HaddockStyle, RealSrcSpan))
512+getSpanMark :: R (Maybe SpanMark)
491−getLastCommentSpan = R (gets scLastCommentSpan)
513+getSpanMark = R (gets scSpanMark)
492
514
493----------------------------------------------------------------------------
515----------------------------------------------------------------------------
494-- Annotations
516-- Annotations
src/Ormolu/Printer/Meat/Common.hs+6−4
@@ -17,7 +17,6 @@ where
17
17
18import Control.Monad
18import Control.Monad
19import Data.List (isPrefixOf)
19import Data.List (isPrefixOf)
20−import Data.Maybe (isJust)
21import qualified Data.Text as T
20import qualified Data.Text as T
22import GHC hiding (GhcPs, IE)
21import GHC hiding (GhcPs, IE)
23import Name (nameStableString)
22import Name (nameStableString)
@@ -155,7 +154,10 @@ p_hsDocString ::
155 LHsDocString ->
154 LHsDocString ->
156 R ()
155 R ()
157p_hsDocString hstyle needsNewline (L l str) = do
156p_hsDocString hstyle needsNewline (L l str) = do
158− goesAfterComment <- isJust <$> getLastCommentSpan
157+ goesAfterComment <- getSpanMark >>= \case
158+ Just (HaddockSpan _ _) -> return True
159+ Just (CommentSpan _) -> return True
160+ _ -> return False
159 -- Make sure the Haddock is separated by a newline from other comments.
161 -- Make sure the Haddock is separated by a newline from other comments.
160 when goesAfterComment newline
162 when goesAfterComment newline
161 forM_ (zip (splitDocString str) (True : repeat False)) $ \(x, isFirst) -> do
163 forM_ (zip (splitDocString str) (True : repeat False)) $ \(x, isFirst) -> do
@@ -174,8 +176,8 @@ p_hsDocString hstyle needsNewline (L l str) = do
174 -- It's often the case that the comment itself doesn't have a span
176 -- It's often the case that the comment itself doesn't have a span
175 -- attached to it and instead its location can be obtained from
177 -- attached to it and instead its location can be obtained from
176 -- nearest enclosing span.
178 -- nearest enclosing span.
177− getEnclosingSpan (const True) >>= mapM_ (setLastCommentSpan (Just hstyle))
179+ getEnclosingSpan (const True) >>= mapM_ (setSpanMark . HaddockSpan hstyle)
178− RealSrcSpan spn -> setLastCommentSpan (Just hstyle) spn
180+ RealSrcSpan spn -> setSpanMark (HaddockSpan hstyle spn)
179
181
180-- | Print anchor of named doc section.
182-- | Print anchor of named doc section.
181p_hsDocName :: String -> R ()
183p_hsDocName :: String -> R ()
src/Ormolu/Printer/Meat/Declaration/Value.hs+25−3
@@ -345,7 +345,7 @@ p_hsCmd = \case
345 txt "do"
345 txt "do"
346 newline
346 newline
347 inci . located es $
347 inci . located es $
348− sitcc . sep newline (located' (sitcc . p_stmt' cmdPlacement p_hsCmd))
348+ sitcc . sep newline (sitcc . withSpacing (p_stmt' cmdPlacement p_hsCmd))
349 HsCmdWrap {} -> notImplemented "HsCmdWrap"
349 HsCmdWrap {} -> notImplemented "HsCmdWrap"
350 XCmd x -> noExtCon x
350 XCmd x -> noExtCon x
351
351
@@ -354,6 +354,28 @@ p_hsCmdTop = \case
354 HsCmdTop NoExtField cmd -> located cmd p_hsCmd
354 HsCmdTop NoExtField cmd -> located cmd p_hsCmd
355 XCmdTop x -> noExtCon x
355 XCmdTop x -> noExtCon x
356
356
357+withSpacing :: Data a => (a -> R ()) -> Located a -> R ()
358+withSpacing f l = located l $ \x -> do
359+ case getLoc l of
360+ UnhelpfulSpan _ -> f x
361+ RealSrcSpan currentSpn -> do
362+ getSpanMark >>= \case
363+ -- Spacing before comments will be handled by the code
364+ -- that prints comments, so we just have to deal with
365+ -- blank lines between statements here.
366+ Just (OtherSpan lastSpn) ->
367+ if srcSpanStartLine currentSpn > srcSpanEndLine lastSpn + 1
368+ then newline
369+ else return ()
370+ _ -> return ()
371+ f x
372+ -- In some cases the (f x) expression may insert a new mark. We want
373+ -- to be careful not to override comment marks.
374+ getSpanMark >>= \case
375+ Just (HaddockSpan _ _) -> return ()
376+ Just (CommentSpan _) -> return ()
377+ _ -> setSpanMark (OtherSpan currentSpn)
378+
357p_stmt :: Stmt GhcPs (LHsExpr GhcPs) -> R ()
379p_stmt :: Stmt GhcPs (LHsExpr GhcPs) -> R ()
358p_stmt = p_stmt' exprPlacement p_hsExpr
380p_stmt = p_stmt' exprPlacement p_hsExpr
359
381
@@ -425,7 +447,7 @@ p_stmt' placer render = \case
425 RecStmt {..} -> do
447 RecStmt {..} -> do
426 txt "rec"
448 txt "rec"
427 space
449 space
428− sitcc $ sepSemi (located' (p_stmt' placer render)) recS_stmts
450+ sitcc $ sepSemi (withSpacing (p_stmt' placer render)) recS_stmts
429 XStmtLR c -> noExtCon c
451 XStmtLR c -> noExtCon c
430
452
431gatherStmt :: ExprLStmt GhcPs -> [[ExprLStmt GhcPs]]
453gatherStmt :: ExprLStmt GhcPs -> [[ExprLStmt GhcPs]]
@@ -647,7 +669,7 @@ p_hsExpr' s = \case
647 ub <- layoutToBraces <$> getLayout
669 ub <- layoutToBraces <$> getLayout
648 inci $
670 inci $
649 sepSemi
671 sepSemi
650− (located' (ub . p_stmt' exprPlacement (p_hsExpr' S)))
672+ (ub . withSpacing (p_stmt' exprPlacement (p_hsExpr' S)))
651 (unLoc es)
673 (unLoc es)
652 compBody = brackets N $ located es $ \xs -> do
674 compBody = brackets N $ located es $ \xs -> do
653 let p_parBody =
675 let p_parBody =