stringfmt
Safe HaskellSafe-Inferred
LanguageHaskell2010

Data.Fmt.Tree

Description

Pretty-printer API over Doc document trees.

Smart constructors, combinators, layout algorithms, and rendering.

Synopsis

Smart constructors

fail_ :: Tree m ann Source #

Layout failure.

emptyDoc :: Tree m ann Source #

Empty document.

leaf :: Int -> m -> Tree m ann Source #

Literal content with display width.

hardline :: Tree m ann Source #

Hard line break. Cannot be flattened.

line :: IsString m => Tree m ann Source #

Line break, or space when flattened.

line' :: Tree m ann Source #

Line break, or empty when flattened.

flatAlt :: Tree m ann -> Tree m ann -> Tree m ann Source #

flatAlt default flat: use default normally, flat when flattened by group.

nest :: Int -> Tree m ann -> Tree m ann Source #

Increase nesting by i.

union :: Tree m ann -> Tree m ann -> Tree m ann Source #

Layout alternatives. Invariant: first argument is the flattened form of the second.

annotate :: ann -> Tree m ann -> Tree m ann Source #

Attach an annotation.

column :: (Int -> Tree m ann) -> Tree m ann Source #

React to the current column position.

nesting :: (Int -> Tree m ann) -> Tree m ann Source #

React to the current nesting level.

Combinators

flatten :: Tree m ann -> Tree m ann Source #

Replace FlatAlt with its flat branch, Line with Fail.

Implemented as a fold — children are flattened first, so Column/Nesting functions automatically produce flattened subtrees.

group :: Tree m ann -> Tree m ann Source #

Try to lay out the document on a single line. Falls back to the original if flattening fails.

group x = union (flatten x) x

align :: Tree m ann -> Tree m ann Source #

Lay out relative to the current column rather than the current nesting level.

align d = column (\k -> nesting (\i -> nest (k - i) d))

Line breaks

softline :: IsString m => Tree m ann Source #

A line break that behaves like a space when flattened by group.

softline = group line

softline' :: Tree m ann Source #

A line break that vanishes when flattened by group.

softline' = group line'

Separators

(<+>) :: IsString m => Tree m ann -> Tree m ann -> Tree m ann infixr 6 Source #

Concatenate with a space in between.

concatWith :: (Tree m ann -> Tree m ann -> Tree m ann) -> [Tree m ann] -> Tree m ann Source #

Concatenate documents using a binary operator.

hsep :: IsString m => [Tree m ann] -> Tree m ann Source #

Concatenate with spaces.

vsep :: IsString m => [Tree m ann] -> Tree m ann Source #

Concatenate with line separators.

fillSep :: IsString m => [Tree m ann] -> Tree m ann Source #

Concatenate with softline separators.

sep :: IsString m => [Tree m ann] -> Tree m ann Source #

vsep that tries to fit on one line (group).

Concatenation

hcat :: [Tree m ann] -> Tree m ann Source #

Concatenate without separators.

vcat :: [Tree m ann] -> Tree m ann Source #

Concatenate with line' separators (line or empty).

fillCat :: [Tree m ann] -> Tree m ann Source #

Concatenate with softline' separators.

cat :: [Tree m ann] -> Tree m ann Source #

vcat that tries to fit on one line (group).

Indentation

hang :: Int -> Tree m ann -> Tree m ann Source #

hang i doc = align (nest i doc)

indent :: IsString m => Int -> Tree m ann -> Tree m ann Source #

indent i doc inserts i spaces then aligns.

Enclosure

surround :: Tree m ann -> Tree m ann -> Tree m ann -> Tree m ann Source #

surround mid left right = left <> mid <> right

encloseSep :: IsString m => Tree m ann -> Tree m ann -> Tree m ann -> [Tree m ann] -> Tree m ann Source #

Enclose a list with separators.

encloseSep lbrace rbrace comma [a, b, c] = lbrace <> a <> comma <> b <> comma <> c <> rbrace

When the content fits, renders on one line. Otherwise, each element gets its own line, aligned.

list :: IsString m => [Tree m ann] -> Tree m ann Source #

list = encloseSep "[" "]" ", "

tupled :: IsString m => [Tree m ann] -> Tree m ann Source #

tupled = encloseSep "(" ")" ", "

punctuate :: Tree m ann -> [Tree m ann] -> [Tree m ann] Source #

Append a separator to all but the last element.

Filling

width :: Tree m ann -> (Int -> Tree m ann) -> Tree m ann Source #

width doc f renders doc then passes its rendered width to f.

fill :: IsString m => Int -> Tree m ann -> Tree m ann Source #

fill n doc pads doc to width n with spaces.

fillBreak :: IsString m => Int -> Tree m ann -> Tree m ann Source #

fillBreak n doc pads or breaks after doc if it exceeds n.

Annotations

reAnnotate :: (ann -> ann') -> Tree m ann -> Tree m ann' Source #

Map over annotations.

unAnnotate :: Tree m ann -> Tree m ann' Source #

Remove all annotations.

alterAnnotations :: (ann -> [ann']) -> Tree m ann -> Tree m ann' Source #

Alter annotations, potentially adding or removing layers.

Optimized group

data FlattenResult a Source #

Result of checking whether flattening changes a document.

Constructors

Flattened a

Flattening produces a different document

AlreadyFlat

Document is already flat (no FlatAlt/Line)

NeverFlat

Document can never be flattened (bare Line)

Instances

Instances details
Functor FlattenResult Source # 
Instance details

Defined in Data.Fmt.Tree

Methods

fmap :: (a -> b) -> FlattenResult a -> FlattenResult b #

(<$) :: a -> FlattenResult b -> FlattenResult a #

Show a => Show (FlattenResult a) Source # 
Instance details

Defined in Data.Fmt.Tree

Eq a => Eq (FlattenResult a) Source # 
Instance details

Defined in Data.Fmt.Tree

changesUponFlattening :: Tree m ann -> FlattenResult (Tree m ann) Source #

Check whether flattening changes a document, and if so, produce the flattened version.

This is the key optimization for group': by checking first, we avoid creating unnecessary Union nodes.

Uses direct recursion via unwrap (matching prettyprinter's approach). A zygomorphism formulation is possible but less readable.

group' :: Tree m ann -> Tree m ann Source #

Optimized group: avoids creating Union when the document is already flat or can never be flattened.

pretty opts (group' doc) = pretty opts (group doc)

Fusion

fuse :: Semigroup m => Tree m ann -> Tree m ann Source #

Merge adjacent Leaf nodes and collapse nested Nest.

This is a semantic no-op — the rendered output is identical — but reduces tree size for more efficient layout.

Operates as a fold (catamorphism). Does not recurse into Column/Nesting functions.

pretty opts (fuse doc) = pretty opts doc

Trailing whitespace

removeTrailingWhitespace :: String -> String Source #

Remove trailing whitespace from rendered output.

Drops spaces at the end of each line. Applied as a post-processing step on the rendered string.

Tokens

data Token m ann Source #

A single rendered token. The output of the layout algorithm.

Constructors

TLeaf !Int !m

Content with display width

TLine !Int

Newline followed by n indentation spaces

TAnnPush ann

Begin annotation scope

TAnnPop

End annotation scope

Instances

Instances details
(Show m, Show ann) => Show (Token m ann) Source # 
Instance details

Defined in Data.Fmt.Tree

Methods

showsPrec :: Int -> Token m ann -> ShowS #

show :: Token m ann -> String #

showList :: [Token m ann] -> ShowS #

(Eq m, Eq ann) => Eq (Token m ann) Source # 
Instance details

Defined in Data.Fmt.Tree

Methods

(==) :: Token m ann -> Token m ann -> Bool #

(/=) :: Token m ann -> Token m ann -> Bool #

Layout

data PageWidth Source #

Page width configuration.

Constructors

AvailablePerLine !Int !Double
AvailablePerLine maxColumns ribbonFraction
Unbounded 

Instances

Instances details
Show PageWidth Source # 
Instance details

Defined in Data.Fmt.Tree

Eq PageWidth Source # 
Instance details

Defined in Data.Fmt.Tree

newtype LayoutOptions Source #

Layout options.

Constructors

LayoutOptions 

Instances

Instances details
Show LayoutOptions Source # 
Instance details

Defined in Data.Fmt.Tree

Eq LayoutOptions Source # 
Instance details

Defined in Data.Fmt.Tree

defaultLayoutOptions :: LayoutOptions Source #

80 columns, ribbon fraction 1.0.

layoutPretty :: LayoutOptions -> Tree m ann -> [Token m ann] Source #

Wadler/Leijen layout with one-line lookahead.

At Union nodes, tries the flattened (first) branch. If the remainder of the line fits within the page width, uses it. Otherwise falls back to the second (default) branch.

layoutSmart :: LayoutOptions -> Tree m ann -> [Token m ann] Source #

Smart layout with multi-line lookahead.

Like layoutPretty, but the fitting predicate checks beyond the first line break — it continues checking until it finds a line that starts at the same or shallower indentation level. This prevents surprises where content looks like it fits but subsequent lines overflow.

Use this for deeply nested structures where layoutPretty makes suboptimal choices.

layoutCompact :: Tree m ann -> [Token m ann] Source #

Compact layout: no width-sensitivity, always breaks.

Every FlatAlt uses its default, every Union uses the narrow branch. Column and Nesting receive 0.

Streaming layout

layoutStream :: LayoutOptions -> Tree m ann -> Nu (Cons (Token m ann)) Source #

Wadler/Leijen layout producing a Nu stream.

Unlike layoutPretty which materializes a [Token] list, layoutStream produces a Nu (Cons (Token m ann)) — a seed + step function that generates tokens lazily on demand. Construction is O(1); tokens are computed as they are consumed.

renderStream :: (Monoid m, IsString m) => Nu (Cons (Token m ann)) -> m Source #

Render a Nu token stream to the output monoid.

prettyStream :: (Monoid m, IsString m) => LayoutOptions -> Tree m ann -> m Source #

Streaming layout + render.

prettyStream opts = renderStream . layoutStream opts

Rendering

render :: (Monoid m, IsString m) => [Token m ann] -> m Source #

Render a token stream to the output monoid, discarding annotations.

pretty :: (Monoid m, IsString m) => LayoutOptions -> Tree m ann -> m Source #

Lay out and render a document.

pretty opts = render . layoutPretty opts