pretty_print
A module for printing documents using the least number of lines possible while trying to stay under a certain number of columns.
Works well for constraining source code to a particular width.
Examples
// The same Document may pretty print as
[ 1, 2, 3, 4 ]
// or
[ 1
, 2
, 3
, 4
]
// depending on the desired maximum columns.
// Similarly
one().two().three().four()
// may break to
one()
.two()
.three()
.four()
The core concepts here are:
- Text nodes will never break.
- Break nodes will break if they must.
- Documents can be placed into groups.
- Groups may be inside of groups.
- Outer groups will always break before inner groups.
- Groups can be told to indent their children when broken.
This is essentially some convenince wrappers around this algorithm: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.34.2200
Types
The core datastructure of a document being built. You cannot build a document directly but you can use the functions in this module to build one.
pub opaque type Document
Functions
pub fn append(l: Document, r: Document) -> Document
Append one Document to Another. Does not indicate a break, simply joining two documents together. append(text(), text()) will never get broken up.
Examples
let doc =
text("left")
|> append(text("-"))
|> append(text("right"))
render(doc, 100)
|> should.equal("left-right")
render(doc, 5)
|> should.equal("left-right")
pub fn append_t(l: Document, r: String) -> Document
Convenience function for appending a String to a document.
Examples
let doc =
text("left")
|> append_t("-")
|> append_t("right")
render(doc, 100)
|> should.equal("left-right")
render(doc, 5)
|> should.equal("left-right")
pub fn break(s: String) -> Document
A possible break in the document. The same string will be used whether the document breaks or not. The break will always occur after the String.
Examples
let doc =
text("left")
|> append(break(","))
|> append_t("right")
doc
|> render(100)
|> should.equal("left,right")
doc
|> render(3)
|> should.equal("left,
right")
pub fn break_after(no_break: String, break: String) -> Document
A possible break in the Document that will occur after the String. The first String is for when the Document doesn't break, and the second is for when the Document does break.
Examples
let doc =
text("one")
|> append(break_after(" | ", " |"))
|> append_t("two")
|> append(break_after(" | ", " |"))
|> append(text("three"))
doc
|> render(100)
|> should.equal("one | two | three")
doc
|> render(8)
|> should.equal("one |
two |
three")
pub fn break_before(no_break: String, break: String) -> Document
A possible break in the Document that will occur after the String. The first String is for when the Document doesn't break, and the second is for when the Document does break.
Examples
let doc =
text("one")
|> append(break_before(" | ", "| "))
|> append_t("two")
|> append(break_before(" | ", "| "))
|> append(text("three"))
|> group()
doc
|> render(100)
|> should.equal("one | two | three")
doc
|> render(8)
|> should.equal("one
| two
| three")
pub fn empty() -> Document
An empty Document. Renders nothing. Useful when you need to conditionally render something.
Examples
empty()
|> render(0)
|> should.equal("")
pub fn group(d: Document) -> Document
Groups represent a document hierarchy. When a document must be broken
outer groups always break before inner groups. Groups that do not contain
any break()
s will never be broken.
Examples
text("left")
|> append_t("-right")
|> group()
|> render(1)
|> should.equal("left-right")
text("left")
|> append(break(""))
|> append_t("-right")
|> group()
|> render(1)
|> should.equal("left\n-right")
pub fn indent(document d: Document, depth i: Int) -> Document
Indicate how deeply to indent the children of a group when it must be broken.
Examples
text("left")
|> append(break(""))
|> append_t("-middle")
|> append(break(""))
|> append_t("-right")
|> group()
|> indent(2)
|> render(1)
|> should.equal("left\n -middle\n -right")
pub fn line() -> Document
Forces a new line. A convenience method for text("\n")
.
Examples
text("top")
|> append(line())
|> append_t("bottom")
|> render(100)
|> should.equal("top
bottom")
pub fn lines(number n: Int) -> Document
Forces a number of new lines.
Examples
text("top")
|> append(lines(3))
|> append_t("bottom")
|> render(100)
|> should.equal("top
bottom")
pub fn render(document doc: Document, columns w: Int) -> String
Render a document to String.
Takes a Document and a number of column to try to fit it into.
See the other functions in this module for many examples.
pub fn sequence( left left: Document, items items: List(Document), sep sep: Document, right right: Document, depth depth: Int, ) -> Document
Helper for grouping and nesting a sequence of items.
If you dont need an opening, closing, or separator you can set them
to empty()
.
Examples
let left = text("( ")
let items =
["one", "two", "three", "four"]
|> list.map(text)
let sep = break_before(", ", ", ")
let right = break_before(" )", ")")
let seq = sequence(left, items, sep, right, 2)
seq
|> render(100)
|> should.equal("( one, two, three, four )")
seq
|> render(10)
|> should.equal("( one
, two
, three
, four
)")
pub fn sequence_trailing( left left: Document, items items: List(Document), sep sep: Document, right right: Document, depth depth: Int, ) -> Document
Same as sequence() but has a trailing separator.
Examples
let left = append(text("("), break_after(" ", ""))
let items =
["one", "two", "three", "four"]
|> list.map(text)
let sep = break_after(", ", ",")
let right = text(")")
let seq = sequence_trailing(left, items, sep, right, 2)
seq
|> render(100)
|> should.equal("( one, two, three, four, )")
seq
|> render(10)
|> should.equal("(
one,
two,
three,
four,
)")
pub fn text(s: String) -> Document
An exact string to render. Never gets broken up.
Examples
text("hello")
|> render(80)
|> should.equal("hello")