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:

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