| Safe Haskell | None |
|---|---|
| Language | Haskell98 |
Text.PrettyPrint.Dent
Description
super simple pretty printer:
Abstract: the only option of formatting is to increase indentation by a fixed amount.
Application: this is mainly used for pretty-printing Haskell data literals (records, tuples, lists).
Design goals:
- for parenthesized (by {}[]() ) sub-structure, the opening paren, separators (;,), closing paren should be in the same column
- the location (distance to left margin) of that column should only depend on the semantic nesting level of sub-structures (and not on the width of the layout of elements of sub-structures)
- small sub-structures should be put on one line (if they fit)
Example:
( Leftist
{ tree = Branch
{ left = Branch { left = Leaf, key = 4, right = Leaf }
, key = 3
, right = Leaf
}
, refs = listToFM [ ( Ref 13, [ 0 ] ), ( Ref 17, [ ] ) ]
}
, [ Any, Any ]
)Realisation (concept)
- there is a layout element
nest(that is called for each opening delimiter) that increases the semantic indentation (for its scope) - there is a layout element
skipthat compares semantic indentation s with current indentation (position of write-head on line) c, and if s < c (the head is too far ahead), it will insert a line-break, and advance to position s (by inserting white space)
This is used in combinator
l </> r = (l <> " ") <> nest 2 (skip <> r)
that is applied for each block: 'pretty (1,2,3)' is
vcat [ "(" </> 1, "," </> 2, "," </> 3, ")" ]and 'pretty (Foo {bar = B})' is
"Foo" </> vcat [ "{" </> "bar" <+> "=" <+> "B", "}" ]Note that we might have a named record instead of B,
starting with 'B / ...', so the skip happens
*after* the constructor name. This is applied in the example above.
- We want the full layout process to be lazy (do not build the full document when we only want some prefix).
ghci> L.take 10 $ renderT $ toDoc $ replicate 1000000 () "[ ( )\n, ( " (0.00 secs, 1,169,104 bytes)
There is just one place where the algorithm has to consider
alternative layouts: the group operator will make a layout
without line breaks, if that fits on the current line.
For that decision (does it fit?)
we need to determine the size of the sub-document quickly.
We use a representation of natural numbers, see type Size below,
with lazy addition and comparison.
Synopsis
- data Doc
- displayT :: SimpleDoc -> Text
- renderCompact :: Doc -> SimpleDoc
- renderWide :: Doc -> SimpleDoc
- renderPretty :: p1 -> p2 -> Doc -> SimpleDoc
- renderFlat :: Doc -> SimpleDoc
- textStrict :: Text -> Doc
- (<+>) :: Doc -> Doc -> Doc
- (<$$>) :: Doc -> Doc -> Doc
- (<//>) :: Doc -> Doc -> Doc
- empty :: Doc
- vcat :: [Doc] -> Doc
- hcat :: [Doc] -> Doc
- cat :: [Doc] -> Doc
- hsep :: [Doc] -> Doc
- vsep :: [Doc] -> Doc
- sep :: [Doc] -> Doc
- line :: Doc
- softline :: Doc
- linebreak :: Doc
- softbreak :: Doc
- fillSep :: [Doc] -> Doc
- fill :: p -> a -> a
- fillBreak :: p -> a -> a
- group :: Doc -> Doc
- align :: a -> a
- nest :: Integral a => a -> Doc -> Doc
- indent :: Integer -> Doc -> Doc
- parens :: Doc -> Doc
- brackets :: Doc -> Doc
- braces :: Doc -> Doc
- dquotes :: Doc -> Doc
- encloseSep :: Semigroup a => a -> a -> a -> a
- skip :: Doc
- comma :: Doc
- colon :: Doc
- semi :: Doc
- equals :: Doc
- char :: IsString a => Char -> a
- int :: Int -> Doc
- integer :: Integer -> Doc
- float :: Float -> Doc
- double :: Double -> Doc
Documentation
renderCompact :: Doc -> SimpleDoc Source #
renderWide :: Doc -> SimpleDoc Source #
renderPretty :: p1 -> p2 -> Doc -> SimpleDoc Source #
renderFlat :: Doc -> SimpleDoc Source #
textStrict :: Text -> Doc Source #
concatenate vertically. in flat layout, concatenate horizontally (no separating space)
concatenate vertically. in flat layout, concatenate horizontally (with separating space)
encloseSep :: Semigroup a => a -> a -> a -> a Source #