Pretty's API
Table of Contents
This document describes the API of the library.
1 Usage patterns
The first step is to construct a document. Larger documents are the result of combining smaller documents. The smallest documents are called "primitives".
When a document is ready, it can be converted to a layout for a particular page width. A layout can be written to an output stream, converted to a string, or traversed arbitrarily ("visited").
2 Preliminaries
Pretty is contained in a single header file:
#include <pretty.hpp>
All of Pretty's public interface is in the pretty
namespace. For convenience, the rest of this document assumes
using namespace pretty;
3 Documents
Documents are constructed via combinators. Once constructed, a document is immutable (it can't be changed).
Documents are cheap to copy and move.
3.1 Document primitives
These documents are the basis for more complex documents.
3.1.1 nil
Doc{}
Doc{Doc::Nil{}}
nil()
nil()
is the empty document.
Example:
Doc d = nil();
3.1.2 char
Doc{Doc::Char{}, 'a'}
char_('a')
char_(c)
is the document consisting of the single character c
.
Example:
Doc d = char_('x');
3.1.3 line
Doc{Doc::Line{}}
line()
line()
is the document consisting of a new-line.
Example:
Doc d = line();
3.1.4 text
Doc{Doc::Text{}, "hello"}
text("hello")
text(s)
is the document consisting of the string s
.
Generally speaking, s
should not contain whitespace since it will not be handled by the pretty-printing engine.
Example:
Doc d = text("hello");
3.1.5 concat
Doc{Doc::Concat{}, d1, d2}
concat(d1, d2)
d1 + d2
d1 + d2
is the document consisting of the document d1
followed by the document d2
.
Example:
Doc d = text("AAA") + text("BBB");
3.1.6 nest
Doc{Doc::Nest{}, i, d}
nest(i, d)
nest(i, d)
is the document d
except every line after the first new-line is indented by i
characters.
Example:
Doc d1 = text("AAA") + line() + text("BBB") + line() + text("CCC"); Doc d2 = nest(2, d1);
When formatted (with sufficient width), d2
produces the output:
AAA BBB CCC
3.1.7 group
Doc{Doc::Group{}, d}
group(d)
All the document primitives so far can only be formatted in a single way. This primitive differs in an important way.
Given a document d
, group(d)
is a document that can be formatted in one of two ways: d
is either flattened so that all new-lines become single spaces, or it's unchanged. The choice is made during layout: the flattened document is chosen if it fits in the available space.
Example:
Doc d = group(text("AAA") + line() + text("BBB") + line() + text("CCC"));
Given a page width of 20, d
produces the output:
AAA BBB CCC
However, given a page width of 8, the output is:
AAA BBB CCC
3.2 Building documents
3.2.1 cut
cut()
is equivalent to group(line())
.
Example:
text("AAA") + cut() + text("BBB") + cut() + text("CCC") + cut() + text("DDD")
formatted to a width of 8 is
AAA BBB CCC DDD
3.2.2 space
space()
is char(' ')
.
3.2.3 beside
beside(d1, d2)
is d1 + space() + d2
.
3.2.4 above
above(d1, d2)
is d1 + line() + d2
.
3.2.5 bracket
bracket(left, d, right)
nests d
between the strings left
and right
.
Example:
bracket("{", above(text("AAA"), text("BBB")), "}")
formatted to a width of 10 is
{ AAA BBB }
3.2.6 fold
template <typename F, std::same_as<Doc>... Ds> Doc fold(F, Ds const &...); template <std::input_iterator I, typename F> Doc fold(I begin, I end, F);
A right-fold on a sequence of documents. Folding over an empty sequence produces the nil()
document.
3.2.7 spread
template <std::same_as<Doc>... Ds> Doc spread(Ds const &...); template <std::input_iterator I> Doc spread(I begin, I end);
spread(d1, d2, ...)
is fold(beside, d1, d2, ...)
.
3.2.8 stack
template <std::same_as<Doc>... Ds> Doc stack(Ds const &...); template <std::input_iterator I> Doc stack(I begin, I end);
stack(d1, d2, ...)
is fold(above, d1, d2, ...)
.
3.2.9 fill
template <std::same_as<Doc>... Ds> Doc fill(Ds const &...); template <std::input_iterator I> Doc fill(I begin, I end);
Quoting Wadler, fill
[…] collapes a list of documents into a document. It puts a space between two documents when this leads to a reasonable layout, and a newline otherwise.
3.2.10 fill_words
Doc fill_words(std::string const &);
fill_words
is a document with as many words as possible fit on each line.
A word is considered to be a sequence of one or more non-whitespace ASCII characters.
4 Layouts
After a document is constructed, it can be formatted to fit to a particular width.
Layout Doc::fit_to(std::size_t width) const;
A document can be fit to a layout any number of times. Layouts are immutable and cheap to copy and move.
4.1 Layout output
A layout can be converted to a string:
std::string Layout::to_string() const;
or written to an output stream:
std::ostream& operator<<(std::ostream&, Layout const&);
4.2 Layout traversal
For advanced users, layouts can also be traversed using the visitor pattern.
class Visitor { public: Visitor() = default; Visitor(Visitor const &) = delete; Visitor(Visitor &&) = delete; Visitor &operator=(Visitor const &) = delete; Visitor &operator=(Visitor &&) = delete; virtual ~Visitor() = default; virtual void nil() = 0; virtual void char_(char) = 0; virtual void text(std::string const &) = 0; virtual void line(std::size_t) = 0; };
void Layout::visit(Visitor&) const;
For convenience, a function can be invoked for each line of output in the layout:
template <typename F> void each_line(Layout const&, F);