From ac53d65056fae2037d00e7cdf6f2c454cc1d3bcc Mon Sep 17 00:00:00 2001 From: Nathan Faubion Date: Fri, 17 Sep 2021 14:04:29 -0700 Subject: [PATCH 1/5] Buffer comments, fixes #16 --- src/Tidy.purs | 37 ++- src/Tidy/Doc.purs | 400 +++++++++++++++++------- src/Tidy/Hang.purs | 164 +++++++--- test/snapshots/DeclarationBreaks.output | 4 + test/snapshots/DeclarationBreaks.purs | 2 + test/snapshots/MultiCase.output | 5 + test/snapshots/MultiCase.purs | 5 + 7 files changed, 450 insertions(+), 167 deletions(-) diff --git a/src/Tidy.purs b/src/Tidy.purs index 79d288c..b7cb8d0 100644 --- a/src/Tidy.purs +++ b/src/Tidy.purs @@ -32,7 +32,7 @@ import Dodo as Dodo import Partial.Unsafe (unsafeCrashWith) import PureScript.CST.Errors (RecoveredError(..)) import PureScript.CST.Types (Binder(..), ClassFundep(..), ClassHead, Comment(..), DataCtor(..), DataHead, DataMembers(..), Declaration(..), Delimited, DelimitedNonEmpty, DoStatement(..), Export(..), Expr(..), FixityOp(..), Foreign(..), Guarded(..), GuardedExpr(..), Ident, IfThenElse, Import(..), ImportDecl(..), Instance(..), InstanceBinding(..), InstanceHead, Label, Labeled(..), LetBinding(..), LineFeed, Module(..), ModuleBody(..), ModuleHeader(..), Name(..), OneOrDelimited(..), Operator, PatternGuard(..), Proper, QualifiedName(..), RecordLabeled(..), RecordUpdate(..), Row(..), Separated(..), SourceStyle(..), SourceToken, Token(..), Type(..), TypeVarBinding(..), ValueBindingFields, Where(..), Wrapped(..)) -import Tidy.Doc (FormatDoc, align, alignCurrentColumn, anchor, blockComment, break, flattenMax, flexDoubleBreak, flexGroup, flexSoftBreak, flexSpaceBreak, forceMinSourceBreaks, fromDoc, indent, joinWith, joinWithMap, leadingLineComment, locally, softBreak, softSpace, sourceBreak, space, spaceBreak, text, trailingLineComment) +import Tidy.Doc (FormatDoc, align, alignCurrentColumn, anchor, break, flattenMax, flexDoubleBreak, flexGroup, flexSoftBreak, flexSpaceBreak, forceMinSourceBreaks, fromDoc, indent, joinWith, joinWithMap, leadingBlockComment, leadingLineComment, locally, softBreak, softSpace, sourceBreak, space, spaceBreak, text, trailingBlockComment, trailingLineComment) import Tidy.Doc (FormatDoc, toDoc) as Exports import Tidy.Hang (HangingDoc, HangingOp(..), hang, hangApp, hangBreak, hangOps, hangWithIndent) import Tidy.Hang as Hang @@ -125,13 +125,19 @@ type Format f e a = FormatOptions e a -> f -> FormatDoc a type FormatHanging f e a = FormatOptions e a -> f -> HangingDoc a type FormatSpace a = FormatDoc a -> FormatDoc a -> FormatDoc a -formatComment :: forall l a. (String -> FormatDoc a) -> Comment l -> FormatDoc a -> FormatDoc a -formatComment lineComment com next = case com of +formatComment + :: forall l a + . (String -> FormatDoc a -> FormatDoc a) + -> (String -> FormatDoc a -> FormatDoc a) + -> Comment l + -> FormatDoc a + -> FormatDoc a +formatComment lineComment blockComment com next = case com of Comment str | SCU.take 2 str == "--" -> - lineComment str `break` next + lineComment str next | otherwise -> - blockComment str `space` next + blockComment str next Line _ n -> sourceBreak n next Space _ -> @@ -140,8 +146,8 @@ formatComment lineComment com next = case com of formatWithComments :: forall a. Array (Comment LineFeed) -> Array (Comment Void) -> FormatDoc a -> FormatDoc a formatWithComments leading trailing doc = foldr - (formatComment leadingLineComment) - (doc `space` foldr (formatComment trailingLineComment) mempty trailing) + (formatComment leadingLineComment leadingBlockComment) + (doc `space` foldr (formatComment trailingLineComment trailingBlockComment) mempty trailing) leading formatToken :: forall a r. { unicode :: UnicodeOption | r } -> SourceToken -> FormatDoc a @@ -188,7 +194,7 @@ formatModule conf (Module { header: ModuleHeader header, body: ModuleBody body } ImportWrapSource -> locally (_ { pageWidth = top, ribbonRatio = 1.0 }) imports , forceMinSourceBreaks 2 $ formatTopLevelGroups conf body.decls - , foldr (formatComment leadingLineComment) mempty body.trailingComments + , foldr (formatComment leadingLineComment leadingBlockComment) mempty body.trailingComments ] where imports = @@ -987,7 +993,7 @@ formatDoStatement conf = case _ of formatExpr conf expr DoBind binder tok expr -> flexGroup (formatBinder conf binder) - `space` Hang.toFormatDoc do + `space` Hang.toFormatDoc do indent (anchor (formatToken conf tok)) `hang` formatHangingExpr conf expr DoError e -> conf.formatError e @@ -1066,7 +1072,7 @@ formatParens format conf (Wrapped { open, value, close }) = formatParensBlock :: forall e a b. Format b e a -> Format (Wrapped b) e a formatParensBlock format conf (Wrapped { open, value, close }) = - commentedFlexGroup open softSpace conf do + flexGroup $ formatToken conf open `softSpace` do align 2 (anchor (format conf value)) `softBreak` formatToken conf close @@ -1109,7 +1115,8 @@ formatList :: forall e a b. FormatSpace a -> FormatSpace a -> Int -> FormatGroup formatList openSpace closeSpace alignment grouped format conf { open, head, tail, close } = case grouped of Grouped -> - commentedFlexGroup open openSpace conf listElems + flexGroup $ formatToken conf open + `openSpace` listElems NotGrouped -> formatToken conf open `openSpace` listElems @@ -1154,14 +1161,6 @@ toQualifiedOperatorTree precMap opNs = overLabel :: forall a b c. (a -> b) -> Labeled a c -> Labeled b c overLabel k (Labeled lbl) = Labeled lbl { label = k lbl.label } --- TODO: Properly fix this grouping issue by buffering comments in FormatDoc. --- This would let us group the semantic elements of the doc and not the comments. -commentedFlexGroup :: forall e a. SourceToken -> FormatSpace a -> Format (FormatDoc a) e a -commentedFlexGroup tok spc conf doc = - formatWithComments tok.leadingComments [] $ flexGroup do - formatToken conf (tok { leadingComments = [] }) - `spc` doc - data DeclGroup = DeclGroupValueSignature Ident | DeclGroupValue Ident diff --git a/src/Tidy/Doc.purs b/src/Tidy/Doc.purs index 7042f08..9a6c551 100644 --- a/src/Tidy/Doc.purs +++ b/src/Tidy/Doc.purs @@ -1,14 +1,16 @@ module Tidy.Doc ( FormatDoc(..) + , LeadingComment(..) + , TrailingComment(..) , ForceBreak(..) , text , leadingLineComment , trailingLineComment - , blockComment + , leadingBlockComment + , trailingBlockComment , anchor , flatten , flattenMax - , forceBreak , indent , align , alignCurrentColumn @@ -28,6 +30,8 @@ module Tidy.Doc , fromDoc , toDoc , mapDoc + , breakDoc + , breaks , joinWithMap , joinWith ) where @@ -36,26 +40,86 @@ import Prelude import Control.Alternative (guard) import Data.Array as Array -import Data.Foldable (class Foldable, foldMap, foldl, intercalate) +import Data.Foldable (class Foldable, foldl, intercalate) import Data.Maybe (Maybe(..), fromMaybe) import Data.Monoid (power) import Data.String as String import Data.String.CodeUnits as SCU import Data.Tuple (Tuple(..)) +import Debug (spy) import Dodo (Doc) import Dodo as Dodo import Dodo.Internal (LocalOptions) import Tidy.Util (splitLines) -data ForceBreak - = ForceNone - | ForceSpace - | ForceBreak +data ForceBreak = ForceNone | ForceSpace | ForceBreak derive instance eqForceBreak :: Eq ForceBreak derive instance ordForceBreak :: Ord ForceBreak -data FormatDoc a = FormatEmpty | FormatDoc ForceBreak Int Boolean (Doc a) ForceBreak +newtype LeadingComment a = LeadingComment + { doc :: Doc a + , left :: ForceBreak + , lines :: Int + , multiline :: Boolean + , right :: ForceBreak + } + +newtype TrailingComment a = TrailingComment + { doc :: Doc a + , left :: ForceBreak + , multiline :: Boolean + , right :: ForceBreak + } + +instance Semigroup (LeadingComment a) where + append (LeadingComment c1) (LeadingComment c2) = do + let br = max c1.right c2.left + if c2.lines > 0 || br == ForceBreak then + LeadingComment c1 + { doc = c1.doc <> breaks ForceBreak c2.lines <> c2.doc + , multiline = true + , right = c2.right + } + else + LeadingComment c1 + { doc = c1.doc <> breakDoc br <> c2.doc + , multiline = c1.multiline || c2.multiline + , right = c2.right + } + +instance Monoid (LeadingComment a) where + mempty = LeadingComment + { doc: mempty + , left: ForceNone + , lines: 0 + , multiline: false + , right: ForceNone + } + +instance Semigroup (TrailingComment a) where + append (TrailingComment c1) (TrailingComment c2) = + TrailingComment c1 + { doc = c1.doc <> breakDoc (max c1.right c2.left) <> c2.doc + , multiline = c1.multiline || c2.multiline + , right = c2.right + } + +instance Monoid (TrailingComment a) where + mempty = TrailingComment + { doc: mempty + , left: ForceNone + , multiline: false + , right: ForceNone + } + +newtype FormatDoc a = FormatDoc + { doc :: Doc a + , isEmpty :: Boolean + , leading :: LeadingComment a + , multiline :: Boolean + , trailing :: TrailingComment a + } type FormatDocOperator a = FormatDoc a -> FormatDoc a -> FormatDoc a @@ -63,23 +127,82 @@ instance semigroupFormatDoc :: Semigroup (FormatDoc a) where append = joinDoc (force identity) instance monoidFormatDoc :: Monoid (FormatDoc a) where - mempty = FormatEmpty + mempty = FormatDoc + { doc: mempty + , leading: mempty + , isEmpty: true + , multiline: false + , trailing: mempty + } fromDoc :: forall a. Doc a -> FormatDoc a -fromDoc doc = FormatDoc ForceNone 0 false doc ForceNone +fromDoc doc + | Dodo.isEmpty doc = mempty + | otherwise = FormatDoc + { doc + , leading: mempty + , isEmpty: false + , multiline: false + , trailing: mempty + } text :: forall a. String -> FormatDoc a -text str = FormatDoc ForceNone 0 false (Dodo.text str) ForceNone - -leadingLineComment :: forall a. String -> FormatDoc a -leadingLineComment str = FormatDoc ForceBreak 0 false (Dodo.text str) ForceBreak - -trailingLineComment :: forall a. String -> FormatDoc a -trailingLineComment str = FormatDoc ForceSpace 0 false (Dodo.text str) ForceBreak +text = fromDoc <<< Dodo.text -blockComment :: forall a. String -> FormatDoc a -blockComment = splitLines >>> Array.uncons >>> foldMap \{ head, tail } -> do - let +leadingLineComment :: forall a. String -> FormatDoc a -> FormatDoc a +leadingLineComment str (FormatDoc doc) = FormatDoc doc + { leading = comm <> doc.leading + , isEmpty = false + } + where + comm = LeadingComment + { doc: Dodo.text str + , left: ForceBreak + , lines: 0 + , multiline: false + , right: ForceBreak + } + +trailingLineComment :: forall a. String -> FormatDoc a -> FormatDoc a +trailingLineComment str (FormatDoc doc) = FormatDoc doc + { trailing = doc.trailing <> comm + , isEmpty = false + } + where + comm = TrailingComment + { doc: Dodo.text str + , left: ForceSpace + , multiline: false + , right: ForceBreak + } + +formatBlockComment :: forall a. String -> Tuple Boolean (Doc a) +formatBlockComment = splitLines >>> Array.uncons >>> case _ of + Nothing -> + Tuple false mempty + Just { head, tail } -> + case prefixSpaces of + Nothing -> + Tuple false (Dodo.text head) + Just ind -> + Tuple true $ Dodo.withPosition \pos -> do + let newIndent = if ind < pos.indent then 0 else ind + let spaces = power " " newIndent + let tailDocs = map (\str -> Dodo.text $ fromMaybe str $ String.stripPrefix (String.Pattern spaces) str) tail + Dodo.lines + [ Dodo.text head + , Dodo.locally + ( \prev -> + if newIndent < prev.indent then + prev + { indentSpaces = spaces + , indent = newIndent + } + else prev + ) + (intercalate Dodo.break tailDocs) + ] + where prefixSpaces = tail # Array.mapMaybe @@ -89,87 +212,93 @@ blockComment = splitLines >>> Array.uncons >>> foldMap \{ head, tail } -> do ) # Array.sort # Array.head - case prefixSpaces of - Nothing -> - FormatDoc ForceSpace 0 false (Dodo.text head) ForceSpace - Just ind -> - FormatDoc ForceSpace 0 true commentDoc ForceSpace - where - commentDoc = Dodo.withPosition \pos -> do - let newIndent = if ind < pos.indent then 0 else ind - let spaces = power " " newIndent - let tailDocs = map (\str -> Dodo.text $ fromMaybe str $ String.stripPrefix (String.Pattern spaces) str) tail - Dodo.lines - [ Dodo.text head - , Dodo.locally - ( \prev -> - if newIndent < prev.indent then - prev - { indentSpaces = spaces - , indent = newIndent - } - else prev - ) - (intercalate Dodo.break tailDocs) - ] + +leadingBlockComment :: forall a. String -> FormatDoc a -> FormatDoc a +leadingBlockComment str (FormatDoc doc) = FormatDoc doc + { leading = comm <> doc.leading + , isEmpty = false + } + where + Tuple multi commDoc = + formatBlockComment str + + comm = LeadingComment + { doc: commDoc + , left: ForceSpace + , lines: 0 + , multiline: multi + , right: ForceSpace + } + +trailingBlockComment :: forall a. String -> FormatDoc a -> FormatDoc a +trailingBlockComment str (FormatDoc doc) = FormatDoc doc + { trailing = doc.trailing <> comm + , isEmpty = false + } + where + Tuple multi commDoc = + formatBlockComment str + + comm = TrailingComment + { doc: commDoc + , left: ForceSpace + , multiline: multi + , right: ForceSpace + } anchor :: forall a. FormatDoc a -> FormatDoc a -anchor = case _ of - FormatEmpty -> - FormatEmpty - FormatDoc fl n m doc fr -> - FormatDoc fl 0 (if n > 0 then true else m) doc fr +anchor (FormatDoc doc) = case doc.leading of + LeadingComment comm | comm.lines > 0 -> + FormatDoc doc + { leading = LeadingComment comm { lines = 0 } + , multiline = true + } + _ -> + FormatDoc doc flatten :: forall a. FormatDoc a -> FormatDoc a flatten = flattenMax 0 flattenMax :: forall a. Int -> FormatDoc a -> FormatDoc a -flattenMax n' = case _ of - FormatEmpty -> - FormatEmpty - FormatDoc fl n m doc fr -> - FormatDoc fl (min n' n) m doc fr - -forceBreak :: forall a. FormatDoc a -> FormatDoc a -forceBreak = case _ of - FormatEmpty -> - FormatEmpty - FormatDoc _ n m doc fr -> - FormatDoc ForceBreak n m doc fr +flattenMax n (FormatDoc doc) = case doc.leading of + LeadingComment comm -> + FormatDoc doc + { leading = LeadingComment comm { lines = min comm.lines n } + } flexGroup :: forall a. FormatDoc a -> FormatDoc a -flexGroup = case _ of - FormatEmpty -> - FormatEmpty - a@(FormatDoc fl n m doc fr) - | not m -> FormatDoc fl n false (Dodo.flexGroup doc) fr - | otherwise -> a +flexGroup (FormatDoc doc) + | doc.multiline = FormatDoc doc + | otherwise = FormatDoc doc { doc = Dodo.flexGroup doc.doc } indent :: forall a. FormatDoc a -> FormatDoc a -indent = mapDoc Dodo.indent +indent = mapDocs Dodo.indent align :: forall a. Int -> FormatDoc a -> FormatDoc a -align = mapDoc <<< Dodo.align +align = mapDocs <<< Dodo.align alignCurrentColumn :: forall a. FormatDoc a -> FormatDoc a -alignCurrentColumn = mapDoc Dodo.alignCurrentColumn +alignCurrentColumn = mapDocs Dodo.alignCurrentColumn locally :: forall a. (LocalOptions -> LocalOptions) -> FormatDoc a -> FormatDoc a -locally = mapDoc <<< Dodo.locally +locally k (FormatDoc doc) = FormatDoc doc { doc = Dodo.locally k doc.doc } sourceBreak :: forall a. Int -> FormatDoc a -> FormatDoc a -sourceBreak m = case _ of - FormatEmpty - | m <= 0 -> FormatEmpty - | otherwise -> FormatDoc ForceNone m false mempty ForceNone - FormatDoc fl n multi doc fr -> - FormatDoc fl (m + n) multi doc fr +sourceBreak n (FormatDoc doc) = do + let LeadingComment comm = doc.leading + FormatDoc doc + { isEmpty = false + , leading = LeadingComment comm { lines = comm.lines + n } + } forceMinSourceBreaks :: forall a. Int -> FormatDoc a -> FormatDoc a -forceMinSourceBreaks m = case _ of - FormatEmpty -> FormatEmpty - FormatDoc fl n multi doc fr -> - FormatDoc fl (max m n) multi doc fr +forceMinSourceBreaks n (FormatDoc doc) + | doc.isEmpty = FormatDoc doc + | otherwise = do + let LeadingComment comm = doc.leading + FormatDoc doc + { leading = LeadingComment comm { lines = max comm.lines n } + } space :: forall a. FormatDocOperator a space = joinDoc (force (append Dodo.space)) @@ -242,32 +371,64 @@ softSpace = joinDoc \f m doc -> case f of -- | right associativity. You will always get double breaks when used -- | with left associativity. flexDoubleBreak :: forall a. FormatDocOperator a -flexDoubleBreak = case _, _ of - FormatEmpty, b -> b - a, FormatEmpty -> a - FormatDoc fl1 n1 m1 doc1 _, FormatDoc _ n2 _ doc2 fr2 - | n2 >= 2 || m1 -> - FormatDoc fl1 n1 true (doc1 <> Dodo.break <> Dodo.break <> doc2) fr2 - | otherwise -> - FormatDoc fl1 n1 true (Dodo.flexSelect doc1 mempty Dodo.break <> Dodo.break <> doc2) fr2 +flexDoubleBreak (FormatDoc doc1) (FormatDoc doc2) + | doc1.isEmpty = FormatDoc doc2 + | doc2.isEmpty = FormatDoc doc1 + | otherwise = do + let TrailingComment comm1 = doc1.trailing + let LeadingComment comm2 = doc2.leading + let docLeft = doc1.doc <> breakDoc comm1.left <> comm1.doc + let docRight = comm2.doc <> breakDoc comm2.right <> doc2.doc + if comm2.lines >= 2 || doc1.multiline then + FormatDoc doc1 + { doc = docLeft <> Dodo.break <> Dodo.break <> docRight + , multiline = true + , trailing = doc2.trailing + } + else + FormatDoc doc1 + { doc = Dodo.flexSelect docLeft mempty Dodo.break <> Dodo.break <> docRight + , multiline = true + , trailing = doc2.trailing + } isEmpty :: forall a. FormatDoc a -> Boolean -isEmpty = case _ of - FormatEmpty -> true - _ -> false +isEmpty (FormatDoc doc) = doc.isEmpty + +breakDoc :: forall a. ForceBreak -> Doc a +breakDoc = case _ of + ForceBreak -> Dodo.break + ForceSpace -> Dodo.space + ForceNone -> mempty + +breaks :: forall a. ForceBreak -> Int -> Doc a +breaks fl n + | n >= 2 = Dodo.break <> Dodo.break + | n == 1 = Dodo.break + | otherwise = breakDoc fl joinDoc :: forall a. (ForceBreak -> Boolean -> Doc a -> Tuple Boolean (Doc a)) -> FormatDocOperator a -joinDoc spc = case _, _ of - FormatEmpty, b -> b - a, FormatEmpty -> a - FormatDoc fl1 n1 m1 doc1 fr1, FormatDoc fl2 n2 m2 doc2 fr2 - | n2 == 1 -> - FormatDoc fl1 n1 true (doc1 <> Dodo.break <> doc2) fr2 - | n2 >= 2 -> - FormatDoc fl1 n1 true (doc1 <> Dodo.break <> Dodo.break <> doc2) fr2 - | otherwise -> do - let (Tuple m3 doc3) = spc (max fr1 fl2) m2 doc2 - FormatDoc fl1 n1 (m1 || m3) (doc1 <> doc3) fr2 +joinDoc spaceFn (FormatDoc doc1) (FormatDoc doc2) + | doc1.isEmpty = FormatDoc doc2 + | doc2.isEmpty = FormatDoc doc1 + | otherwise = do + let TrailingComment comm1 = doc1.trailing + let LeadingComment comm2 = doc2.leading + let docLeft = doc1.doc <> breakDoc comm1.left <> comm1.doc + let docRight = comm2.doc <> breakDoc comm2.right <> doc2.doc + if comm2.lines > 0 then + FormatDoc doc1 + { doc = docLeft <> breaks ForceBreak comm2.lines <> docRight + , multiline = true + , trailing= doc2.trailing + } + else do + let Tuple m3 doc3 = spaceFn (max comm1.right comm2.left) (comm2.multiline || doc2.multiline) docRight + FormatDoc doc1 + { doc = docLeft <> doc3 + , multiline = comm1.multiline || doc1.multiline || m3 + , trailing= doc2.trailing + } force :: forall a. (Doc a -> Doc a) -> (ForceBreak -> Boolean -> Doc a -> Tuple Boolean (Doc a)) force k f m doc = case f of @@ -276,17 +437,34 @@ force k f m doc = case f of _ -> Tuple m (k doc) -mapDoc :: forall a b. (Doc a -> Doc b) -> FormatDoc a -> FormatDoc b -mapDoc k = case _ of - FormatEmpty -> - FormatEmpty - FormatDoc fl n m doc fr -> - FormatDoc fl n m (k doc) fr +mapDoc :: forall a. (Doc a -> Doc a) -> FormatDoc a -> FormatDoc a +mapDoc k (FormatDoc doc) + | doc.isEmpty = FormatDoc doc + | otherwise = FormatDoc doc { doc = k doc.doc } + +mapDocs :: forall a. (Doc a -> Doc a) -> FormatDoc a -> FormatDoc a +mapDocs k (FormatDoc doc) + | doc.isEmpty = FormatDoc doc + | otherwise = do + let LeadingComment comm1 = doc.leading + let TrailingComment comm2 = doc.trailing + FormatDoc doc + { doc = k doc.doc + , leading = LeadingComment comm1 { doc = k comm1.doc } + , trailing = TrailingComment comm2 { doc = k comm2.doc } + } toDoc :: forall a. FormatDoc a -> Doc a -toDoc = case _ of - FormatEmpty -> mempty - FormatDoc _ _ _ doc _ -> doc +toDoc (FormatDoc doc) + | doc.isEmpty = mempty + | otherwise = do + let LeadingComment comm1 = doc.leading + let TrailingComment comm2 = doc.trailing + comm1.doc + <> breakDoc comm1.right + <> doc.doc + <> breakDoc comm2.left + <> comm2.doc joinWithMap :: forall f a b diff --git a/src/Tidy/Hang.purs b/src/Tidy/Hang.purs index 855279d..b45e862 100644 --- a/src/Tidy/Hang.purs +++ b/src/Tidy/Hang.purs @@ -21,8 +21,7 @@ import Data.Maybe (maybe) import Data.Tuple (Tuple(..), fst, snd) import Dodo (Doc) import Dodo as Dodo -import Partial.Unsafe (unsafeCrashWith) -import Tidy.Doc (ForceBreak(..), FormatDoc(..), align, break, flatten, flexGroup, forceBreak, indent) +import Tidy.Doc (ForceBreak(..), FormatDoc(..), LeadingComment(..), TrailingComment(..), align, break, breakDoc, flatten, flexGroup, forceMinSourceBreaks, indent) data HangingDoc a = HangBreak (FormatDoc a) @@ -188,7 +187,7 @@ toFormatDoc = fst <<< goInit { init, last } = NonEmptyArray.unsnoc tail next = Array.foldr goInitApp (goLastApp last) init this = fst $ goInit $ case head of - HangApp _ _ _ -> overHangHead forceBreak head + HangApp _ _ _ -> overHangHead (forceMinSourceBreaks 1) head _ -> head docIndent = indMulti head ind (fst next) docGroup = flexSelectJoin this (fst next) docIndent @@ -204,43 +203,134 @@ toFormatDoc = fst <<< goInit docBreak = flexSelectJoin (prevInd this) (prevAlgn docIndent) (prevInd docIndent) Tuple docGroup docBreak - flexSelect = case _, _, _ of - FormatDoc fl1 n1 m1 doc1 fr1, FormatDoc fl2 n2 m2 doc2 fr2, FormatDoc fl3 n3 m3 doc3 fr3 -> do - let - doc2' = breaks (max fr1 fl2) n2 <> doc2 - doc3' = breaks (max fr1 fl3) n3 <> doc3 - FormatDoc fl1 n1 (m1 || (m2 && m3)) - (Dodo.flexSelect doc1 doc2' doc3') - (max fr2 fr3) - _, _, _ -> - unsafeCrashWith "flexSelect/FormatEmpty" - - flexSelectJoin = case _, _, _ of - FormatDoc fl1 n1 m1 doc1 fr1, FormatDoc fl2 n2 m2 doc2 fr2, FormatDoc fl3 n3 m3 doc3 fr3 -> do - let - doc2' = breaks (max fr1 fl2) n2 <> doc2 - doc3' = breaks (max fr1 fl3) n3 <> doc3 - br = if fl1 == ForceBreak || n1 > 0 then Dodo.break else Dodo.spaceBreak - FormatDoc ForceNone 0 (m1 || (m2 && m3)) - (Dodo.flexSelect (br <> doc1) doc2' doc3') - (max fr2 fr3) - _, _, _ -> - unsafeCrashWith "flexSelect/FormatEmpty" - - docJoin = case _ of - FormatEmpty -> FormatEmpty - fdoc@(FormatDoc fl n m doc fr) -> - if fl == ForceBreak || n > 0 then fdoc - else if m then FormatDoc ForceBreak n m doc fr - else FormatDoc ForceNone 0 false (Dodo.spaceBreak <> doc) fr + flexSelect (FormatDoc doc1) (FormatDoc doc2) (FormatDoc doc3) = do + let + TrailingComment comm1r = doc1.trailing + + doc1' = + doc1.doc + <> breakDoc comm1r.left + <> comm1r.doc + + LeadingComment comm2l = doc2.leading + TrailingComment comm2r = doc2.trailing + + doc2' = + breaks (max comm1r.right comm2l.left) comm2l.lines + <> comm2l.doc + <> breakDoc comm2l.right + <> doc2.doc + <> breakDoc comm2r.left + <> comm2r.doc + + LeadingComment comm3l = doc3.leading + TrailingComment comm3r = doc3.trailing + + doc3' = + breaks (max comm1r.right comm3l.left) comm3l.lines + <> comm3l.doc + <> breakDoc comm2l.right + <> doc3.doc + <> breakDoc comm3r.left + <> comm3r.doc + + m1 = doc1.multiline || comm1r.multiline + m2 = comm2l.multiline || doc2.multiline || comm2r.multiline + m3 = comm3l.multiline || doc3.multiline || comm3r.multiline + + FormatDoc + { doc: Dodo.flexSelect doc1' doc2' doc3' + , leading: doc1.leading + , isEmpty: false + , multiline: m1 || (m2 && m3) + , trailing: + TrailingComment + { doc: mempty + , left: ForceNone + , multiline: false + , right: max comm2r.right comm3r.right + } + } + + flexSelectJoin (FormatDoc doc1) (FormatDoc doc2) (FormatDoc doc3) = do + let + LeadingComment comm1l = doc1.leading + TrailingComment comm1r = doc1.trailing + + break + | comm1l.left == ForceBreak || comm1l.lines > 0 = Dodo.break + | otherwise = Dodo.spaceBreak + + doc1' = + break + <> comm1l.doc + <> breakDoc comm1l.right + <> doc1.doc + <> breakDoc comm1r.left + <> comm1r.doc + + LeadingComment comm2l = doc2.leading + TrailingComment comm2r = doc2.trailing + + doc2' = + breaks (max comm1r.right comm2l.left) comm2l.lines + <> comm2l.doc + <> breakDoc comm2l.right + <> doc2.doc + <> breakDoc comm2r.left + <> comm2r.doc + + LeadingComment comm3l = doc3.leading + TrailingComment comm3r = doc3.trailing + + doc3' = + breaks (max comm1r.right comm3l.left) comm3l.lines + <> comm3l.doc + <> breakDoc comm2l.right + <> doc3.doc + <> breakDoc comm3r.left + <> comm3r.doc + + m1 = comm1l.multiline || doc1.multiline || comm1r.multiline + m2 = comm2l.multiline || doc2.multiline || comm2r.multiline + m3 = comm3l.multiline || doc3.multiline || comm3r.multiline + + FormatDoc + { doc: Dodo.flexSelect doc1' doc2' doc3' + , leading: mempty + , isEmpty: false + , multiline: m1 || (m2 && m3) + , trailing: + TrailingComment + { doc: mempty + , left: ForceNone + , multiline: false + , right: max comm2r.right comm3r.right + } + } + + docJoin fdoc@(FormatDoc doc) + | doc.isEmpty = fdoc + | otherwise = do + let LeadingComment comm = doc.leading + if comm.left == ForceBreak || comm.lines > 0 then + fdoc + else if comm.multiline || doc.multiline then + forceMinSourceBreaks 1 fdoc + else + FormatDoc doc + { doc = Dodo.spaceBreak <> comm.doc <> breakDoc comm.right <> doc.doc + , leading = mempty + } realignOp op doc = case op, hangHead doc of - FormatDoc fl1 n1 _ _ fr1, FormatDoc fl2 n2 m2 _ _ - | fl1 /= ForceBreak && n1 == 0 && fr1 /= ForceBreak && fl2 /= ForceBreak && n2 > 0 -> - realignOp (forceBreak op) (overHangHead flatten doc) + FormatDoc doc1@{ leading: LeadingComment comm1, trailing: TrailingComment comm2 }, + FormatDoc { leading: LeadingComment comm3 } + | comm1.left /= ForceBreak && comm1.lines == 0 && comm2.right /= ForceBreak && comm3.left /= ForceBreak && comm3.lines > 0 -> + realignOp (forceMinSourceBreaks 1 op) (overHangHead flatten doc) | HangBreak _ <- doc - , fr1 /= ForceBreak && fl2 /= ForceBreak && n2 == 0 && m2 -> - Tuple op (overHangHead forceBreak doc) + , comm2.right /= ForceBreak && comm3.left /= ForceBreak && comm3.lines == 0 && (comm3.multiline || doc1.multiline) -> + Tuple op (overHangHead (forceMinSourceBreaks 1) doc) _, _ -> Tuple op doc diff --git a/test/snapshots/DeclarationBreaks.output b/test/snapshots/DeclarationBreaks.output index 828741c..efa19d7 100644 --- a/test/snapshots/DeclarationBreaks.output +++ b/test/snapshots/DeclarationBreaks.output @@ -8,8 +8,10 @@ type Ok = String type Ok2 :: Type type Ok2 = String +-- Comment type Ok3 = String type Ok4 = Int +type OK5 = Int class Foo a where foo :: a @@ -62,8 +64,10 @@ type Ok = String type Ok2 :: Type type Ok2 = String +-- Comment type Ok3 = String type Ok4 = Int +type OK5 = Int class Foo a where foo :: a diff --git a/test/snapshots/DeclarationBreaks.purs b/test/snapshots/DeclarationBreaks.purs index f4b1b3a..1304193 100644 --- a/test/snapshots/DeclarationBreaks.purs +++ b/test/snapshots/DeclarationBreaks.purs @@ -5,8 +5,10 @@ import Bar type Ok = String type Ok2 :: Type type Ok2 = String +-- Comment type Ok3 = String type Ok4 = Int +type OK5 = Int class Foo a where foo :: a instance Foo Int where foo = 12 else instance Foo String where foo = "foo" diff --git a/test/snapshots/MultiCase.output b/test/snapshots/MultiCase.output index f4508b8..98fe728 100644 --- a/test/snapshots/MultiCase.output +++ b/test/snapshots/MultiCase.output @@ -11,3 +11,8 @@ test = case _, _ of , c } -> a <> b <> c + +test = case _, _ of + -- Comment + Foo a, Bar b -> + a <> b diff --git a/test/snapshots/MultiCase.purs b/test/snapshots/MultiCase.purs index 9482b3f..0ad0c85 100644 --- a/test/snapshots/MultiCase.purs +++ b/test/snapshots/MultiCase.purs @@ -9,3 +9,8 @@ test = case _, _ of , c } -> a <> b <> c + +test = case _, _ of + -- Comment + Foo a, Bar b -> + a <> b From 14df7ca03b566c24d9338abda63466c46f9ac90f Mon Sep 17 00:00:00 2001 From: Nathan Faubion Date: Fri, 17 Sep 2021 14:11:23 -0700 Subject: [PATCH 2/5] Remove spy --- src/Tidy/Doc.purs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Tidy/Doc.purs b/src/Tidy/Doc.purs index 9a6c551..b700e01 100644 --- a/src/Tidy/Doc.purs +++ b/src/Tidy/Doc.purs @@ -46,7 +46,6 @@ import Data.Monoid (power) import Data.String as String import Data.String.CodeUnits as SCU import Data.Tuple (Tuple(..)) -import Debug (spy) import Dodo (Doc) import Dodo as Dodo import Dodo.Internal (LocalOptions) From 9feda988937ed1def4672b0041f90bd0260fbf77 Mon Sep 17 00:00:00 2001 From: Nathan Faubion Date: Fri, 17 Sep 2021 17:32:46 -0700 Subject: [PATCH 3/5] Fix comment joining --- src/Tidy.purs | 2 +- src/Tidy/Doc.purs | 96 ++++++++++++++-------- src/Tidy/Hang.purs | 35 +++----- test/snapshots/TrailingLineComments.output | 20 +++++ test/snapshots/TrailingLineComments.purs | 9 ++ 5 files changed, 102 insertions(+), 60 deletions(-) create mode 100644 test/snapshots/TrailingLineComments.output create mode 100644 test/snapshots/TrailingLineComments.purs diff --git a/src/Tidy.purs b/src/Tidy.purs index b7cb8d0..537ae71 100644 --- a/src/Tidy.purs +++ b/src/Tidy.purs @@ -147,7 +147,7 @@ formatWithComments :: forall a. Array (Comment LineFeed) -> Array (Comment Void) formatWithComments leading trailing doc = foldr (formatComment leadingLineComment leadingBlockComment) - (doc `space` foldr (formatComment trailingLineComment trailingBlockComment) mempty trailing) + (doc <> foldr (formatComment trailingLineComment trailingBlockComment) mempty trailing) leading formatToken :: forall a r. { unicode :: UnicodeOption | r } -> SourceToken -> FormatDoc a diff --git a/src/Tidy/Doc.purs b/src/Tidy/Doc.purs index b700e01..dc6f939 100644 --- a/src/Tidy/Doc.purs +++ b/src/Tidy/Doc.purs @@ -72,20 +72,36 @@ newtype TrailingComment a = TrailingComment } instance Semigroup (LeadingComment a) where - append (LeadingComment c1) (LeadingComment c2) = do - let br = max c1.right c2.left - if c2.lines > 0 || br == ForceBreak then - LeadingComment c1 - { doc = c1.doc <> breaks ForceBreak c2.lines <> c2.doc - , multiline = true - , right = c2.right - } - else - LeadingComment c1 - { doc = c1.doc <> breakDoc br <> c2.doc - , multiline = c1.multiline || c2.multiline - , right = c2.right - } + append (LeadingComment c1) (LeadingComment c2) + | Dodo.isEmpty c1.doc = + LeadingComment c2 + { left = max c1.left c2.left + , lines = c1.lines + c2.lines + } + | Dodo.isEmpty c2.doc = + LeadingComment c1 + { doc = c1.doc <> breaks ForceNone c2.lines + , multiline = c1.multiline || c2.lines > 0 + , right = + if c2.lines > 0 then + ForceNone + else + max c1.right c2.right + } + | otherwise = do + let br = max c1.right c2.left + if c2.lines > 0 || br == ForceBreak then + LeadingComment c1 + { doc = c1.doc <> breaks ForceBreak c2.lines <> c2.doc + , multiline = true + , right = c2.right + } + else + LeadingComment c1 + { doc = c1.doc <> breakDoc br c2.doc + , multiline = c1.multiline || c2.multiline + , right = c2.right + } instance Monoid (LeadingComment a) where mempty = LeadingComment @@ -97,12 +113,17 @@ instance Monoid (LeadingComment a) where } instance Semigroup (TrailingComment a) where - append (TrailingComment c1) (TrailingComment c2) = - TrailingComment c1 - { doc = c1.doc <> breakDoc (max c1.right c2.left) <> c2.doc - , multiline = c1.multiline || c2.multiline - , right = c2.right - } + append (TrailingComment c1) (TrailingComment c2) + | Dodo.isEmpty c1.doc = + TrailingComment c2 { left = max c1.left c2.left } + | Dodo.isEmpty c2.doc = + TrailingComment c1 { right = max c1.right c2.right } + | otherwise = + TrailingComment c1 + { doc = c1.doc <> breakDoc (max c1.right c2.left) c2.doc + , multiline = c1.multiline || c2.multiline + , right = c2.right + } instance Monoid (TrailingComment a) where mempty = TrailingComment @@ -164,7 +185,7 @@ leadingLineComment str (FormatDoc doc) = FormatDoc doc trailingLineComment :: forall a. String -> FormatDoc a -> FormatDoc a trailingLineComment str (FormatDoc doc) = FormatDoc doc - { trailing = doc.trailing <> comm + { trailing = comm <> doc.trailing , isEmpty = false } where @@ -231,7 +252,7 @@ leadingBlockComment str (FormatDoc doc) = FormatDoc doc trailingBlockComment :: forall a. String -> FormatDoc a -> FormatDoc a trailingBlockComment str (FormatDoc doc) = FormatDoc doc - { trailing = doc.trailing <> comm + { trailing = comm <> doc.trailing , isEmpty = false } where @@ -376,8 +397,8 @@ flexDoubleBreak (FormatDoc doc1) (FormatDoc doc2) | otherwise = do let TrailingComment comm1 = doc1.trailing let LeadingComment comm2 = doc2.leading - let docLeft = doc1.doc <> breakDoc comm1.left <> comm1.doc - let docRight = comm2.doc <> breakDoc comm2.right <> doc2.doc + let docLeft = doc1.doc <> breakDoc comm1.left comm1.doc + let docRight = comm2.doc <> breakDoc comm2.right doc2.doc if comm2.lines >= 2 || doc1.multiline then FormatDoc doc1 { doc = docLeft <> Dodo.break <> Dodo.break <> docRight @@ -394,17 +415,22 @@ flexDoubleBreak (FormatDoc doc1) (FormatDoc doc2) isEmpty :: forall a. FormatDoc a -> Boolean isEmpty (FormatDoc doc) = doc.isEmpty -breakDoc :: forall a. ForceBreak -> Doc a -breakDoc = case _ of - ForceBreak -> Dodo.break - ForceSpace -> Dodo.space - ForceNone -> mempty +breakDoc :: forall a. ForceBreak -> Doc a -> Doc a +breakDoc br doc + | Dodo.isEmpty doc = doc + | otherwise = case br of + ForceBreak -> Dodo.break <> doc + ForceSpace -> Dodo.space <> doc + ForceNone -> doc breaks :: forall a. ForceBreak -> Int -> Doc a breaks fl n | n >= 2 = Dodo.break <> Dodo.break | n == 1 = Dodo.break - | otherwise = breakDoc fl + | otherwise = case fl of + ForceBreak -> Dodo.break + ForceSpace -> Dodo.space + ForceNone -> mempty joinDoc :: forall a. (ForceBreak -> Boolean -> Doc a -> Tuple Boolean (Doc a)) -> FormatDocOperator a joinDoc spaceFn (FormatDoc doc1) (FormatDoc doc2) @@ -413,8 +439,8 @@ joinDoc spaceFn (FormatDoc doc1) (FormatDoc doc2) | otherwise = do let TrailingComment comm1 = doc1.trailing let LeadingComment comm2 = doc2.leading - let docLeft = doc1.doc <> breakDoc comm1.left <> comm1.doc - let docRight = comm2.doc <> breakDoc comm2.right <> doc2.doc + let docLeft = doc1.doc <> breakDoc comm1.left comm1.doc + let docRight = comm2.doc <> breakDoc comm2.right doc2.doc if comm2.lines > 0 then FormatDoc doc1 { doc = docLeft <> breaks ForceBreak comm2.lines <> docRight @@ -460,10 +486,8 @@ toDoc (FormatDoc doc) let LeadingComment comm1 = doc.leading let TrailingComment comm2 = doc.trailing comm1.doc - <> breakDoc comm1.right - <> doc.doc - <> breakDoc comm2.left - <> comm2.doc + <> breakDoc comm1.right doc.doc + <> breakDoc comm2.left comm2.doc joinWithMap :: forall f a b diff --git a/src/Tidy/Hang.purs b/src/Tidy/Hang.purs index b45e862..82837f7 100644 --- a/src/Tidy/Hang.purs +++ b/src/Tidy/Hang.purs @@ -209,8 +209,7 @@ toFormatDoc = fst <<< goInit doc1' = doc1.doc - <> breakDoc comm1r.left - <> comm1r.doc + <> breakDoc comm1r.left comm1r.doc LeadingComment comm2l = doc2.leading TrailingComment comm2r = doc2.trailing @@ -218,10 +217,8 @@ toFormatDoc = fst <<< goInit doc2' = breaks (max comm1r.right comm2l.left) comm2l.lines <> comm2l.doc - <> breakDoc comm2l.right - <> doc2.doc - <> breakDoc comm2r.left - <> comm2r.doc + <> breakDoc comm2l.right doc2.doc + <> breakDoc comm2r.left comm2r.doc LeadingComment comm3l = doc3.leading TrailingComment comm3r = doc3.trailing @@ -229,10 +226,8 @@ toFormatDoc = fst <<< goInit doc3' = breaks (max comm1r.right comm3l.left) comm3l.lines <> comm3l.doc - <> breakDoc comm2l.right - <> doc3.doc - <> breakDoc comm3r.left - <> comm3r.doc + <> breakDoc comm2l.right doc3.doc + <> breakDoc comm3r.left comm3r.doc m1 = doc1.multiline || comm1r.multiline m2 = comm2l.multiline || doc2.multiline || comm2r.multiline @@ -264,10 +259,8 @@ toFormatDoc = fst <<< goInit doc1' = break <> comm1l.doc - <> breakDoc comm1l.right - <> doc1.doc - <> breakDoc comm1r.left - <> comm1r.doc + <> breakDoc comm1l.right doc1.doc + <> breakDoc comm1r.left comm1r.doc LeadingComment comm2l = doc2.leading TrailingComment comm2r = doc2.trailing @@ -275,10 +268,8 @@ toFormatDoc = fst <<< goInit doc2' = breaks (max comm1r.right comm2l.left) comm2l.lines <> comm2l.doc - <> breakDoc comm2l.right - <> doc2.doc - <> breakDoc comm2r.left - <> comm2r.doc + <> breakDoc comm2l.right doc2.doc + <> breakDoc comm2r.left comm2r.doc LeadingComment comm3l = doc3.leading TrailingComment comm3r = doc3.trailing @@ -286,10 +277,8 @@ toFormatDoc = fst <<< goInit doc3' = breaks (max comm1r.right comm3l.left) comm3l.lines <> comm3l.doc - <> breakDoc comm2l.right - <> doc3.doc - <> breakDoc comm3r.left - <> comm3r.doc + <> breakDoc comm2l.right doc3.doc + <> breakDoc comm3r.left comm3r.doc m1 = comm1l.multiline || doc1.multiline || comm1r.multiline m2 = comm2l.multiline || doc2.multiline || comm2r.multiline @@ -319,7 +308,7 @@ toFormatDoc = fst <<< goInit forceMinSourceBreaks 1 fdoc else FormatDoc doc - { doc = Dodo.spaceBreak <> comm.doc <> breakDoc comm.right <> doc.doc + { doc = Dodo.spaceBreak <> comm.doc <> breakDoc comm.right doc.doc , leading = mempty } diff --git a/test/snapshots/TrailingLineComments.output b/test/snapshots/TrailingLineComments.output new file mode 100644 index 0000000..b61009e --- /dev/null +++ b/test/snapshots/TrailingLineComments.output @@ -0,0 +1,20 @@ +module TrailingLineComments where + +test -- ok + = test + +test = test -- ok + +test = test {- a -} {- b -} -- ok + +-- @format --width=10 +module TrailingLineComments where + +test -- ok + = test + +test = + test -- ok + +test = + test {- a -} {- b -} -- ok diff --git a/test/snapshots/TrailingLineComments.purs b/test/snapshots/TrailingLineComments.purs new file mode 100644 index 0000000..5255db3 --- /dev/null +++ b/test/snapshots/TrailingLineComments.purs @@ -0,0 +1,9 @@ +-- @format --width=10 +module TrailingLineComments where + +test -- ok + = test + +test = test -- ok + +test = test {- a -} {- b -} -- ok From 64a081007a65b21c34d85b84f0bb6f40d60ed3ec Mon Sep 17 00:00:00 2001 From: Nathan Faubion Date: Fri, 17 Sep 2021 20:50:43 -0700 Subject: [PATCH 4/5] Fix multiline strings --- src/Tidy.purs | 10 ++++++++-- test/snapshots/MultilineStringLiterals.output | 13 +++++++++++++ test/snapshots/MultilineStringLiterals.purs | 12 ++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/Tidy.purs b/src/Tidy.purs index 537ae71..32dbb84 100644 --- a/src/Tidy.purs +++ b/src/Tidy.purs @@ -161,8 +161,14 @@ formatToken conf tok = formatWithComments tok.leadingComments tok.trailingCommen formatRawString :: forall a. String -> FormatDoc a formatRawString = splitLines >>> Array.uncons >>> foldMap \{ head, tail } -> - text head `break` locally (_ { indent = 0, indentSpaces = "" }) do - joinWithMap break text tail + if Array.null tail then + text head + else + fromDoc $ Dodo.lines + [ Dodo.text head + , Dodo.locally (_ { indent = 0, indentSpaces = "" }) do + Array.intercalate Dodo.break $ Dodo.text <$> tail + ] formatString :: forall a. String -> FormatDoc a formatString = splitStringEscapeLines >>> Array.uncons >>> foldMap \{ head, tail } -> diff --git a/test/snapshots/MultilineStringLiterals.output b/test/snapshots/MultilineStringLiterals.output index f7c913f..96ae694 100644 --- a/test/snapshots/MultilineStringLiterals.output +++ b/test/snapshots/MultilineStringLiterals.output @@ -1,7 +1,20 @@ module MultilineStringLiterals where +test = """ """ + +test = + """ +this is a string +""" + test = """ +this is a string + +this is a string + + + this is a string """ diff --git a/test/snapshots/MultilineStringLiterals.purs b/test/snapshots/MultilineStringLiterals.purs index ed89c7b..0e68d9c 100644 --- a/test/snapshots/MultilineStringLiterals.purs +++ b/test/snapshots/MultilineStringLiterals.purs @@ -1,6 +1,18 @@ module MultilineStringLiterals where +test = """ """ + +test = """ +this is a string +""" + test = """ +this is a string + +this is a string + + + this is a string """ From 8a4eb6f56323ae9726a163159043166c39187725 Mon Sep 17 00:00:00 2001 From: Nathan Faubion Date: Fri, 17 Sep 2021 20:54:41 -0700 Subject: [PATCH 5/5] Format --- src/Tidy.purs | 2 +- src/Tidy/Doc.purs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Tidy.purs b/src/Tidy.purs index 32dbb84..e9b7f22 100644 --- a/src/Tidy.purs +++ b/src/Tidy.purs @@ -999,7 +999,7 @@ formatDoStatement conf = case _ of formatExpr conf expr DoBind binder tok expr -> flexGroup (formatBinder conf binder) - `space` Hang.toFormatDoc do + `space` Hang.toFormatDoc do indent (anchor (formatToken conf tok)) `hang` formatHangingExpr conf expr DoError e -> conf.formatError e diff --git a/src/Tidy/Doc.purs b/src/Tidy/Doc.purs index dc6f939..e92fb26 100644 --- a/src/Tidy/Doc.purs +++ b/src/Tidy/Doc.purs @@ -445,14 +445,14 @@ joinDoc spaceFn (FormatDoc doc1) (FormatDoc doc2) FormatDoc doc1 { doc = docLeft <> breaks ForceBreak comm2.lines <> docRight , multiline = true - , trailing= doc2.trailing + , trailing = doc2.trailing } else do let Tuple m3 doc3 = spaceFn (max comm1.right comm2.left) (comm2.multiline || doc2.multiline) docRight FormatDoc doc1 { doc = docLeft <> doc3 , multiline = comm1.multiline || doc1.multiline || m3 - , trailing= doc2.trailing + , trailing = doc2.trailing } force :: forall a. (Doc a -> Doc a) -> (ForceBreak -> Boolean -> Doc a -> Tuple Boolean (Doc a))