Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell2010 |
Re-exports of combinators for use when building Render
instances.
Synopsis
- data Doc ann
- class Pretty a where
- dquote :: Doc ann
- squote :: Doc ann
- comma :: Doc ann
- punctuate :: Doc ann -> [Doc ann] -> [Doc ann]
- enclose :: Doc ann -> Doc ann -> Doc ann -> Doc ann
- lbracket :: Doc ann
- rbracket :: Doc ann
- (<+>) :: Doc ann -> Doc ann -> Doc ann
- lbrace :: Doc ann
- rbrace :: Doc ann
- lparen :: Doc ann
- rparen :: Doc ann
- emptyDoc :: Doc ann
- sep :: [Doc ann] -> Doc ann
- hsep :: [Doc ann] -> Doc ann
- vsep :: [Doc ann] -> Doc ann
- fillCat :: [Doc ann] -> Doc ann
- fillSep :: [Doc ann] -> Doc ann
- flatAlt :: Doc ann -> Doc ann -> Doc ann
- hcat :: [Doc ann] -> Doc ann
- vcat :: [Doc ann] -> Doc ann
- annotate :: ann -> Doc ann -> Doc ann
- unAnnotate :: Doc ann -> Doc xxx
- line :: Doc ann
- line' :: Doc ann
- softline :: Doc ann
- softline' :: Doc ann
- hardline :: Doc ann
- group :: Doc ann -> Doc ann
- hang :: Int -> Doc ann -> Doc ann
- indent :: Int -> Doc ann -> Doc ann
- nest :: Int -> Doc ann -> Doc ann
- concatWith :: Foldable t => (Doc ann -> Doc ann -> Doc ann) -> t (Doc ann) -> Doc ann
Pretty Printing
from Data.Text.Prettyprint.Doc
Re-exported from Data.Text.Prettyprint.Doc in prettyprinter and Data.Text.Prettyprint.Doc.Render.Terminal in prettyprinter-ansi-terminal:
The abstract data type
represents pretty documents that have
been annotated with data of type Doc
annann
.
More specifically, a value of type
represents a non-empty set of
possible layouts of a document. The layout functions select one of these
possibilities, taking into account things like the width of the output
document.Doc
The annotation is an arbitrary piece of data associated with (part of) a document. Annotations may be used by the rendering backends in order to display output differently, such as
- color information (e.g. when rendering to the terminal)
- mouseover text (e.g. when rendering to rich HTML)
- whether to show something or not (to allow simple or detailed versions)
The simplest way to display a Doc
is via the Show
class.
>>>
putStrLn (show (vsep ["hello", "world"]))
hello world
Instances
Instances
Pretty Void | Finding a good example for printing something that does not exist is hard, so here is an example of printing a list full of nothing.
|
Defined in Prettyprinter.Internal | |
Pretty Int16 | |
Defined in Prettyprinter.Internal | |
Pretty Int32 | |
Defined in Prettyprinter.Internal | |
Pretty Int64 | |
Defined in Prettyprinter.Internal | |
Pretty Int8 | |
Defined in Prettyprinter.Internal | |
Pretty Word16 | |
Defined in Prettyprinter.Internal | |
Pretty Word32 | |
Defined in Prettyprinter.Internal | |
Pretty Word64 | |
Defined in Prettyprinter.Internal | |
Pretty Word8 | |
Defined in Prettyprinter.Internal | |
Pretty JsonKey | |
Defined in Core.Encoding.Json | |
Pretty JsonValue | |
Defined in Core.Encoding.Json | |
Pretty LongName Source # | |
Defined in Core.Program.Arguments | |
Pretty Rope | |
Defined in Core.Text.Rope | |
Pretty Text | Automatically converts all newlines to
Note that
Manually use |
Defined in Prettyprinter.Internal | |
Pretty Text | (lazy |
Defined in Prettyprinter.Internal | |
Pretty Integer |
|
Defined in Prettyprinter.Internal | |
Pretty Natural | |
Defined in Prettyprinter.Internal | |
Pretty () |
The argument is not used:
|
Defined in Prettyprinter.Internal | |
Pretty Bool |
|
Defined in Prettyprinter.Internal | |
Pretty Char | Instead of
|
Defined in Prettyprinter.Internal | |
Pretty Double |
|
Defined in Prettyprinter.Internal | |
Pretty Float |
|
Defined in Prettyprinter.Internal | |
Pretty Int |
|
Defined in Prettyprinter.Internal | |
Pretty Word | |
Defined in Prettyprinter.Internal | |
Pretty a => Pretty (Identity a) |
|
Defined in Prettyprinter.Internal | |
Pretty a => Pretty (NonEmpty a) | |
Defined in Prettyprinter.Internal | |
Pretty a => Pretty (Maybe a) | Ignore
|
Defined in Prettyprinter.Internal | |
Pretty a => Pretty [a] |
|
Defined in Prettyprinter.Internal | |
(Pretty a1, Pretty a2) => Pretty (a1, a2) |
|
Defined in Prettyprinter.Internal | |
Pretty a => Pretty (Const a b) | |
Defined in Prettyprinter.Internal | |
(Pretty a1, Pretty a2, Pretty a3) => Pretty (a1, a2, a3) |
|
Defined in Prettyprinter.Internal |
(
appends punctuate
p xs)p
to all but the last document in xs
.
>>>
let docs = punctuate comma (Util.words "lorem ipsum dolor sit amet")
>>>
putDocW 80 (hsep docs)
lorem, ipsum, dolor, sit, amet
The separators are put at the end of the entries, which we can see if we position the result vertically:
>>>
putDocW 20 (vsep docs)
lorem, ipsum, dolor, sit, amet
If you want put the commas in front of their elements instead of at the end,
you should use tupled
or, in general, encloseSep
.
(
tries laying out the documents sep
xs)xs
separated with space
s,
and if this does not fit the page, separates them with newlines. This is what
differentiates it from vsep
, which always lays out its contents beneath
each other.
>>>
let doc = "prefix" <+> sep ["text", "to", "lay", "out"]
>>>
putDocW 80 doc
prefix text to lay out
With a narrower layout, the entries are separated by newlines:
>>>
putDocW 20 doc
prefix text to lay out
sep
=group
.vsep
hsep :: [Doc ann] -> Doc ann #
(
concatenates all documents hsep
xs)xs
horizontally with
,
i.e. it puts a space between all entries.<+>
>>>
let docs = Util.words "lorem ipsum dolor sit amet"
>>>
hsep docs
lorem ipsum dolor sit amet
does not introduce line breaks on its own, even when the page is too
narrow:hsep
>>>
putDocW 5 (hsep docs)
lorem ipsum dolor sit amet
For automatic line breaks, consider using fillSep
instead.
vsep :: [Doc ann] -> Doc ann #
(
concatenates all documents vsep
xs)xs
above each other. If a
group
undoes the line breaks inserted by vsep
, the documents are
separated with a space
instead.
Using vsep
alone yields
>>>
"prefix" <+> vsep ["text", "to", "lay", "out"]
prefix text to lay out
group
ing a vsep
separates the documents with a space
if it fits the
page (and does nothing otherwise). See the
convenience function for
this use case.sep
The align
function can be used to align the documents under their first
element:
>>>
"prefix" <+> align (vsep ["text", "to", "lay", "out"])
prefix text to lay out
Since group
ing a vsep
is rather common, sep
is a built-in for doing
that.
fillCat :: [Doc ann] -> Doc ann #
(
concatenates documents fillCat
xs)xs
horizontally with
as
long as it fits the page, then inserts a <>
and continues doing that
for all documents in line'
xs
. This is similar to how an ordinary word processor
lays out the text if you just keep typing after you hit the maximum line
length.
(
means that if line'
group
ed, the documents are separated with nothing
instead of newlines. See fillSep
if you want a space
instead.)
Observe the difference between fillSep
and fillCat
. fillSep
concatenates the entries space
d when group
ed:
>>>
let docs = take 20 (cycle (["lorem", "ipsum", "dolor", "sit", "amet"]))
>>>
putDocW 40 ("Grouped:" <+> group (fillSep docs))
Grouped: lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet
On the other hand, fillCat
concatenates the entries directly when
group
ed:
>>>
putDocW 40 ("Grouped:" <+> group (fillCat docs))
Grouped: loremipsumdolorsitametlorem ipsumdolorsitametloremipsumdolorsitamet loremipsumdolorsitamet
fillSep :: [Doc ann] -> Doc ann #
(
concatenates the documents fillSep
xs)xs
horizontally with
as long as it fits the page, then inserts a <+>
and continues doing that
for all documents in line
xs
. (
means that if line
group
ed, the documents
are separated with a space
instead of newlines. Use fillCat
if you do not
want a space
.)
Let's print some words to fill the line:
>>>
let docs = take 20 (cycle ["lorem", "ipsum", "dolor", "sit", "amet"])
>>>
putDocW 80 ("Docs:" <+> fillSep docs)
Docs: lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet
The same document, printed at a width of only 40, yields
>>>
putDocW 40 ("Docs:" <+> fillSep docs)
Docs: lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet
By default, (
renders as flatAlt
x y)x
. However when group
ed,
y
will be preferred, with x
as the fallback for the case when y
doesn't fit.
>>>
let doc = flatAlt "a" "b"
>>>
putDoc doc
a>>>
putDoc (group doc)
b>>>
putDocW 0 (group doc)
a
flatAlt
is particularly useful for defining conditional separators such as
softline =group
(flatAlt
hardline
" ")
>>>
let hello = "Hello" <> softline <> "world!"
>>>
putDocW 12 hello
Hello world!>>>
putDocW 11 hello
Hello world!
Example: Haskell's do-notation
We can use this to render Haskell's do-notation nicely:
>>>
let open = flatAlt "" "{ "
>>>
let close = flatAlt "" " }"
>>>
let separator = flatAlt "" "; "
>>>
let prettyDo xs = group ("do" <+> align (encloseSep open close separator xs))
>>>
let statements = ["name:_ <- getArgs", "let greet = \"Hello, \" <> name", "putStrLn greet"]
This is put into a single line with {;}
style if it fits:
>>>
putDocW 80 (prettyDo statements)
do { name:_ <- getArgs; let greet = "Hello, " <> name; putStrLn greet }
When there is not enough space the statements are broken up into lines nicely:
>>>
putDocW 10 (prettyDo statements)
do name:_ <- getArgs let greet = "Hello, " <> name putStrLn greet
Notes
Users should be careful to choose x
to be less wide than y
.
Otherwise, if y
turns out not to fit the page, we fall back on an even
wider layout:
>>>
let ugly = group (flatAlt "even wider" "too wide")
>>>
putDocW 7 ugly
even wider
Also note that group
will flatten y
:
>>>
putDoc (group (flatAlt "x" ("y" <> line <> "y")))
y y
This also means that an "unflattenable" y
which contains a hard linebreak
will never be rendered:
>>>
putDoc (group (flatAlt "x" ("y" <> hardline <> "y")))
x
vcat :: [Doc ann] -> Doc ann #
(
vertically concatenates the documents vcat
xs)xs
. If it is
group
ed, the line breaks are removed.
In other words
is like vcat
, with newlines removed instead of
replaced by vsep
space
s.
>>>
let docs = Util.words "lorem ipsum dolor"
>>>
vcat docs
lorem ipsum dolor>>>
group (vcat docs)
loremipsumdolor
Since group
ing a vcat
is rather common, cat
is a built-in shortcut for
it.
annotate :: ann -> Doc ann -> Doc ann #
Add an annotation to a
. This annotation can then be used by the
renderer to e.g. add color to certain parts of the output. For a full
tutorial example on how to use it, see the
Prettyprinter.Render.Tutorials.StackMachineTutorial or
Prettyprinter.Render.Tutorials.TreeRenderingTutorial modules.Doc
This function is only relevant for custom formats with their own annotations, and not relevant for basic prettyprinting. The predefined renderers, e.g. Prettyprinter.Render.Text, should be enough for the most common needs.
unAnnotate :: Doc ann -> Doc xxx #
Remove all annotations.
Although unAnnotate
is idempotent with respect to rendering,
unAnnotate
.unAnnotate
=unAnnotate
it should not be used without caution, for each invocation traverses the
entire contained document. If possible, it is preferrable to unannotate after
producing the layout by using unAnnotateS
.
softline
behaves like
if the resulting output fits the page,
otherwise like space
.line
Here, we have enough space to put everything in one line:
>>>
let doc = "lorem ipsum" <> softline <> "dolor sit amet"
>>>
putDocW 80 doc
lorem ipsum dolor sit amet
If we narrow the page to width 10, the layouter produces a line break:
>>>
putDocW 10 doc
lorem ipsum dolor sit amet
softline
=group
line
is like softline'
, but behaves like softline
if the
resulting output does not fit on the page (instead of mempty
). In other
words, space
is to line
how line'
is to softline
.softline'
With enough space, we get direct concatenation:
>>>
let doc = "ThisWord" <> softline' <> "IsWayTooLong"
>>>
putDocW 80 doc
ThisWordIsWayTooLong
If we narrow the page to width 10, the layouter produces a line break:
>>>
putDocW 10 doc
ThisWord IsWayTooLong
softline'
=group
line'
A
is always laid out as a line break, even when hardline
group
ed or
when there is plenty of space. Note that it might still be simply discarded
if it is part of a flatAlt
inside a group
.
>>>
let doc = "lorem ipsum" <> hardline <> "dolor sit amet"
>>>
putDocW 1000 doc
lorem ipsum dolor sit amet
>>>
group doc
lorem ipsum dolor sit amet
(
tries laying out group
x)x
into a single line by removing the
contained line breaks; if this does not fit the page, or when a hardline
within x
prevents it from being flattened, x
is laid out without any
changes.
The group
function is key to layouts that adapt to available space nicely.
See vcat
, line
, or flatAlt
for examples that are related, or make good
use of it.
(
lays out the document hang
i x)x
with a nesting level set to the
current column plus i
. Negative values are allowed, and decrease the
nesting level accordingly.
>>>
let doc = reflow "Indenting these words with hang"
>>>
putDocW 24 ("prefix" <+> hang 4 doc)
prefix Indenting these words with hang
This differs from nest
, which is based on the current nesting level plus
i
. When you're not sure, try the more efficient nest
first. In our
example, this would yield
>>>
let doc = reflow "Indenting these words with nest"
>>>
putDocW 24 ("prefix" <+> nest 4 doc)
prefix Indenting these words with nest
hang
i doc =align
(nest
i doc)
(
lays out the document nest
i x)x
with the current nesting level
(indentation of the following lines) increased by i
. Negative values are
allowed, and decrease the nesting level accordingly.
>>>
vsep [nest 4 (vsep ["lorem", "ipsum", "dolor"]), "sit", "amet"]
lorem ipsum dolor sit amet
See also
concatWith :: Foldable t => (Doc ann -> Doc ann -> Doc ann) -> t (Doc ann) -> Doc ann #
Concatenate all documents element-wise with a binary function.
concatWith
_ [] =mempty
concatWith
(**) [x,y,z] = x ** y ** z
Multiple convenience definitions based on concatWith
are already predefined,
for example:
hsep
=concatWith
(<+>
)fillSep
=concatWith
(\x y -> x<>
softline
<>
y)
This is also useful to define customized joiners:
>>>
concatWith (surround dot) ["Prettyprinter", "Render", "Text"]
Prettyprinter.Render.Text