@@ -17,7 +17,6 @@ module Ormolu.Printer.Internal
1717 atom,
1818 space,
1919 newline,
20− isLineDirty,
2120 useRecordDot,
2221 inci,
2322 sitcc,
@@ -39,6 +38,7 @@ module Ormolu.Printer.Internal
3938 popComment,
4039 getEnclosingSpan,
4140 withEnclosingSpan,
41+ thisLineSpans,
4242
4343 -- * Stateful markers
4444 SpanMark (..),
@@ -102,14 +102,13 @@ data SC = SC
102102 scBuilder :: Builder,
103103 -- | Span stream
104104 scSpanStream :: SpanStream,
105+ -- | Spans of atoms that have been printed on the current line so far
106+ scThisLineSpans :: [RealSrcSpan],
105107 -- | Comment stream
106108 scCommentStream :: CommentStream,
107109 -- | Pending comment lines (in reverse order) to be inserted before next
108110 -- newline, 'Int' is the indentation level
109111 scPendingComments :: ![(CommentPosition, Int, Text)],
110− -- | Whether the current line is “dirty”, that is, already contains
111− -- atoms that can have comments attached to them
112− scDirtyLine :: !Bool,
113112 -- | Whether to output a space before the next output
114113 scRequestedDelimiter :: !RequestedDelimiter,
115114 -- | An auxiliary marker for keeping track of last output element
@@ -177,9 +176,9 @@ runR (R m) sstream cstream anns recDot =
177176 { scColumn = 0,
178177 scBuilder = mempty,
179178 scSpanStream = sstream,
179+ scThisLineSpans = [],
180180 scCommentStream = cstream,
181181 scPendingComments = [],
182− scDirtyLine = False,
183182 scRequestedDelimiter = VeryBeginning,
184183 scSpanMark = Nothing
185184 }
@@ -211,14 +210,14 @@ atom = spit True False . T.pack . showOutputable
211210
212211-- | Low-level non-public helper to define 'txt' and 'atom'.
213212spit ::
214− -- | Should we mark the line as dirty?
213+ -- | Whether to register the outermost enclosing span
215214 Bool ->
216215 -- | Used during outputting of pending comments?
217216 Bool ->
218217 -- | 'Text' to output
219218 Text ->
220219 R ()
221−spit dirty printingComments txt' = do
220+spit registerSpan printingComments text = do
222221 requestedDel <- R (gets scRequestedDelimiter)
223222 case requestedDel of
224223 RequestedNewline -> do
@@ -233,16 +232,23 @@ spit dirty printingComments txt' = do
233232 R $ do
234233 i <- asks rcIndent
235234 c <- gets scColumn
235+ outermostEnclosing <- listToMaybe <$> asks rcEnclosingSpans
236236 let spaces =
237237 if c < i
238238 then T.replicate (i - c) " "
239239 else bool mempty " " (requestedDel == RequestedSpace)
240− indentedTxt = spaces <> txt'
240+ indentedTxt = spaces <> text
241241 modify $ \sc ->
242242 sc
243243 { scBuilder = scBuilder sc <> fromText indentedTxt,
244244 scColumn = scColumn sc + T.length indentedTxt,
245− scDirtyLine = scDirtyLine sc || dirty,
245+ scThisLineSpans =
246+ let xs = scThisLineSpans sc
247+ in if registerSpan
248+ then case outermostEnclosing of
249+ Nothing -> xs
250+ Just x -> x : xs
251+ else xs,
246252 scRequestedDelimiter = RequestedNothing,
247253 scSpanMark =
248254 -- If there are pending comments, do not reset last comment
@@ -286,14 +292,14 @@ newline = do
286292 case position of
287293 OnTheSameLine -> space
288294 OnNextLine -> newlineRaw
289− R . forM_ cs $ \(_, indent, txt') ->
295+ R . forM_ cs $ \(_, indent, text) ->
290296 let modRC rc =
291297 rc
292298 { rcIndent = indent
293299 }
294300 R m = do
295− unless (T.null txt') $
296− spit False True txt'
301+ unless (T.null text) $
302+ spit False True text
297303 newlineRaw
298304 in local modRC m
299305 R . modify $ \sc ->
@@ -314,7 +320,7 @@ newlineRaw = R . modify $ \sc ->
314320 VeryBeginning -> builderSoFar
315321 _ -> builderSoFar <> "\n",
316322 scColumn = 0,
317− scDirtyLine = False,
323+ scThisLineSpans = [],
318324 scRequestedDelimiter = case scRequestedDelimiter sc of
319325 AfterNewline -> RequestedNewline
320326 RequestedNewline -> RequestedNewline
@@ -322,11 +328,6 @@ newlineRaw = R . modify $ \sc ->
322328 _ -> AfterNewline
323329 }
324330
325−-- | Check if the current line is “dirty”, that is, there is something on it
326−-- that can have comments attached to it.
327−isLineDirty :: R Bool
328−isLineDirty = R (gets scDirtyLine)
329−
330331-- | Return 'True' if we should print record dot syntax.
331332useRecordDot :: R Bool
332333useRecordDot = R $ asks rcUseRecDot
@@ -405,11 +406,11 @@ registerPendingCommentLine ::
405406 -- | 'Text' to output
406407 Text ->
407408 R ()
408−registerPendingCommentLine position txt' = R $ do
409+registerPendingCommentLine position text = R $ do
409410 i <- asks rcIndent
410411 modify $ \sc ->
411412 sc
412− { scPendingComments = (position, i, txt') : scPendingComments sc
413+ { scPendingComments = (position, i, text) : scPendingComments sc
413414 }
414415
415416-- | Drop elements that begin before or at the same place as given
@@ -468,6 +469,10 @@ withEnclosingSpan spn (R m) = R (local modRC m)
468469 { rcEnclosingSpans = spn : rcEnclosingSpans rc
469470 }
470471
472+-- | Get spans on this line so far.
473+thisLineSpans :: R [RealSrcSpan]
474+thisLineSpans = R (gets scThisLineSpans)
475+
471476----------------------------------------------------------------------------
472477-- Stateful markers
473478
@@ -477,15 +482,15 @@ data SpanMark
477482 HaddockSpan HaddockStyle RealSrcSpan
478483 | -- | Non-haddock comment
479484 CommentSpan RealSrcSpan
480− | -- | Non-comment span
481− OtherSpan RealSrcSpan
485+ | -- | A statement in a do-block and such span
486+ StatementSpan RealSrcSpan
482487
483488-- | Project 'RealSrcSpan' from 'SpanMark'.
484489spanMarkSpan :: SpanMark -> RealSrcSpan
485490spanMarkSpan = \case
486491 HaddockSpan _ s -> s
487492 CommentSpan s -> s
488− OtherSpan s -> s
493+ StatementSpan s -> s
489494
490495-- | Haddock string style.
491496data HaddockStyle