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);