A linear time pretty printing library, ported from brownplt/pretty-fast-pretty-printer
moon add marianoguerra/pretty-fast-pretty-printerimport {
"marianoguerra/pretty-fast-pretty-printer" @pp,
}///|
test {
let cond = @pp.txt("x > 0")
let body = @pp.txt("print(x)")
let doc = @pp.vert([
@pp.horz([@pp.txt("while ("), cond, @pp.txt(") {")]),
@pp.horz([@pp.txt(" "), body]),
@pp.txt("}"),
])
inspect(
doc.display_string(width=80),
content=(
#|while (x > 0) {
#| print(x)
#|}
),
)
}///|
test {
inspect(
@pp.vert([@pp.txt("Hello,"), @pp.txt("world!")]).display_string(),
content=(
#|Hello,
#|world!
),
)
}///|
test {
let doc = @pp.horz([
@pp.txt("BEGIN "),
@pp.vert([@pp.txt("first line"), @pp.txt("second line")]),
])
inspect(
doc.display_string(),
content=(
#|BEGIN first line
#| second line
),
)
}///|
test {
let doc = @pp.if_flat(
@pp.txt("[1, 2, 3]"),
@pp.vert([@pp.txt("[1,"), @pp.txt(" 2,"), @pp.txt(" 3]")]),
)
inspect(doc.display_string(width=20), content="[1, 2, 3]")
inspect(
doc.display_string(width=5),
content=(
#|[1,
#| 2,
#| 3]
),
)
}///|
test {
let flat = @pp.txt("[1, 2]")
let broken = @pp.vert([@pp.txt("[1,"), @pp.txt(" 2]")])
// The group fits in seven columns, so it stays flat and the `))` overflows.
inspect(
@pp.horz([@pp.if_flat(flat, broken), @pp.txt("))")]).display_string(width=7),
content="[1, 2]))",
)
// Told that two more columns follow, it breaks instead.
inspect(
@pp.horz([@pp.if_flat(flat, broken, reserve=2), @pp.txt("))")]).display_string(
width=7,
),
content=(
#|[1,
#| 2]))
),
)
}///|
test {
let given = @pp.vert([@pp.txt("first line"), @pp.txt("second line")])
inspect(
@pp.nest(2, given).display_string(),
content=(
#|first line
#| second line
),
)
inspect(
@pp.horz([@pp.txt(" "), given]).display_string(),
content=(
#| first line
#| second line
),
)
}///|
test {
let doc = @pp.horz([
@pp.txt("(let ((x "),
@pp.vert([@pp.txt("first"), @pp.txt("second")]),
])
assert_eq(doc.display(), ["(let ((x first", " second"])
assert_eq(doc.display(max_indent=4), ["(let ((x first", " second"])
}///|
test {
// A CJK character is one UTF-16 unit but occupies two columns.
assert_eq(@pp.txt_as("世界", 4).flat_width(), Some(4))
// An ANSI colour escape is five UTF-16 units and occupies none, so a
// syntax-highlighted document lays out exactly like the plain one.
let red = @pp.txt_as("\u{1b}[31m", 0)
let reset = @pp.txt_as("\u{1b}[0m", 0)
assert_eq(@pp.horz([red, @pp.txt("hello"), reset]).flat_width(), Some(5))
}///|
test {
inspect(
@pp.horz([@pp.txt("x = "), @pp.txt_lines("a\nb")]).display_string(),
content=(
#|x = a
#| b
),
)
inspect(
@pp.horz([@pp.txt("x = "), @pp.txt_raw("a\nb")]).display_string(),
content=(
#|x = a
#|b
),
)
}///|
test {
let c = @pp.txt("a == b")
let t = @pp.txt("a << 2")
let e = @pp.txt("a + b")
let doc = @pp.pretty([
Str("if ("),
Val(c),
Str(") {\n "),
Val(t),
Str("\n} else {\n "),
Val(e),
Str("\n}"),
])
inspect(
doc.display_string(),
content=(
#|if (a == b) {
#| a << 2
#|} else {
#| a + b
#|}
),
)
}///|
test {
let words = ["This", "is", "a", "sentence", "with", "eight", "words"].map(
@pp.txt,
)
inspect(
@pp.wrap(words).display_string(width=20),
content=(
#|This is a sentence
#|with eight words
),
)
}///|
test {
let doc = @pp.sep_by(
["alpha", "beta", "gamma"].map(@pp.txt),
sep=", ",
vert_sep=",",
)
inspect(doc.display_string(width=40), content="alpha, beta, gamma")
inspect(
doc.display_string(width=10),
content=(
#|alpha,
#|beta,
#|gamma
),
)
}///|
test {
let doc = @pp.begin_like_sexpr(@pp.txt("begin"), [
@pp.standard_sexpr(@pp.txt("+"), [@pp.txt("1"), @pp.txt("2")]),
@pp.standard_sexpr(@pp.txt("display"), [
@pp.txt("a-fairly-long-variable-name"),
]),
])
inspect(
doc.display_string(width=30),
content=(
#|(begin
#| (+ 1 2)
#| (display
#| a-fairly-long-variable-name))
),
)
}moon test # unit tests, including the ported upstream suite
moon run cmd/main # a small s-expression printer, shown at three widthstest {
let doc = @pp.vert([@pp.txt("one"), @pp.horz([@pp.txt(" "), @pp.txt("two")])])
assert_eq(doc.display(), ["one", " two"])
}test {
inspect(
@pp.vert([@pp.txt("one"), @pp.txt("two")]).display_string(),
content=(
#|one
#|two
),
)
}test {
assert_eq(@pp.horz([@pp.txt("ab"), @pp.txt("cd")]).flat_width(), Some(4))
assert_eq(@pp.vert([@pp.txt("ab"), @pp.txt("cd")]).flat_width(), None)
}test {
let buf = StringBuilder()
buf.write_string("> ")
@pp.vert([@pp.txt("one"), @pp.txt("two")]).write_to(buf)
inspect(
buf.to_string(),
content=(
#|> one
#|two
),
)
}test {
let doc = @pp.horz([
@pp.txt("(let ((x "),
@pp.vert([@pp.txt("first"), @pp.txt("second")]),
])
assert_eq(doc.display(), ["(let ((x first", " second"])
assert_eq(doc.display(max_indent=4), ["(let ((x first", " second"])
}test {
let group = @pp.if_flat(
@pp.txt("[1, 2, 3]"),
@pp.vert([@pp.txt("[1,"), @pp.txt(" 2,"), @pp.txt(" 3]")]),
)
let doc = @pp.horz([@pp.txt(" "), @pp.vert([@pp.txt("xs ="), group])])
// At width 16 the group starts in column 8 and does not fit.
assert_eq(doc.display(width=16), [
" xs =", " [1,", " 2,", " 3]",
])
// Capped at column 2 it does.
assert_eq(doc.display(width=16, max_indent=2), [" xs =", " [1, 2, 3]"])
}(keyword
bodies
...
bodies)test {
let doc = @pp.begin_like_sexpr(@pp.txt("begin"), ["1", "2", "3"].map(@pp.txt))
inspect(
doc.display_string(),
content=(
#|(begin
#| 1
#| 2
#| 3)
),
)
}test {
let doc = @pp.concat([
@pp.txt("BEGIN "),
@pp.vert([@pp.txt("first line"), @pp.txt("second line")]),
])
inspect(
doc.display_string(),
content=(
#|BEGIN first line
#|second line
),
)
}test {
let docs = ["a", "b", "c"].map(@pp.txt)
inspect(
docs.fold(init=@pp.empty, @pp.concat2).display_string(),
content="abc",
)
}test {
let plain = @pp.horz([
@pp.if_flat(@pp.txt("a"), @pp.vert([@pp.txt("a"), @pp.txt("A")])),
@pp.txt("b"),
])
inspect(plain.display_string(width=20), content="ab")
let forced = @pp.horz([
@pp.if_flat(
@pp.full_line(@pp.txt("a")),
@pp.vert([@pp.txt("a"), @pp.txt("A")]),
),
@pp.txt("b"),
])
inspect(
forced.display_string(width=20),
content=(
#|a
#|Ab
),
)
}test {
let doc = @pp.horz([
@pp.txt("BEGIN "),
@pp.vert([@pp.txt("first line"), @pp.txt("second line")]),
])
inspect(
doc.display_string(),
content=(
#|BEGIN first line
#| second line
),
)
}test {
let docs = ["a", "b", "c"].map(@pp.txt)
inspect(docs.fold(init=@pp.empty, @pp.horz2).display_string(), content="abc")
}test {
let flat = @pp.txt("[1, 2]")
let broken = @pp.vert([@pp.txt("[1,"), @pp.txt(" 2]")])
// The group fits in seven columns, so it stays flat -- and then the `))`
// that follows overflows.
let doc = @pp.horz([@pp.if_flat(flat, broken), @pp.txt("))")])
inspect(doc.display_string(width=7), content="[1, 2]))")
// Told that two more columns follow, it breaks, and the result fits.
let doc = @pp.horz([@pp.if_flat(flat, broken, reserve=2), @pp.txt("))")])
inspect(
doc.display_string(width=7),
content=(
#|[1,
#| 2]))
),
)
}test {
let doc = @pp.if_flat(
@pp.txt("[1, 2, 3]"),
@pp.vert([@pp.txt("[1,"), @pp.txt(" 2,"), @pp.txt(" 3]")]),
)
inspect(doc.display_string(width=20), content="[1, 2, 3]")
inspect(
doc.display_string(width=5),
content=(
#|[1,
#| 2,
#| 3]
),
)
}(keyword defn body)(keyword defn
body)test {
let doc = @pp.lambda_like_sexpr(
@pp.txt("lambda"),
@pp.txt("(number)"),
@pp.txt("(* number number)"),
)
inspect(doc.display_string(), content="(lambda (number) (* number number))")
inspect(
doc.display_string(width=25),
content=(
#|(lambda (number)
#| (* number number))
),
)
}test {
let doc = @pp.vert([@pp.txt("first line"), @pp.txt("second line")])
inspect(
@pp.nest(2, doc).display_string(),
content=(
#|first line
#| second line
),
)
}test {
let doc = @pp.vert([@pp.txt("first line"), @pp.txt("second line")])
inspect(
@pp.horz([@pp.txt(" "), doc]).display_string(),
content=(
#| first line
#| second line
),
)
}test {
inspect(@pp.parens(@pp.txt("a b")).display_string(), content="(a b)")
}test {
let c = @pp.txt("a == b")
let t = @pp.txt("a << 2")
let e = @pp.txt("a + b")
let doc = @pp.pretty([
Str("if ("),
Val(c),
Str(") {\n "),
Val(t),
Str("\n} else {\n "),
Val(e),
Str("\n}"),
])
inspect(
doc.display_string(),
content=(
#|if (a == b) {
#| a << 2
#|} else {
#| a + b
#|}
),
)
}items[0] sep items[1] sep ... items[n]items[0] vert_sep
items[1] vert_sep
...
items[n]test {
let items = ["alpha", "beta", "gamma"].map(@pp.txt)
let doc = @pp.sep_by(items, sep=", ", vert_sep=",")
inspect(doc.display_string(width=40), content="alpha, beta, gamma")
inspect(
doc.display_string(width=10),
content=(
#|alpha,
#|beta,
#|gamma
),
)
}(func args ... args)(func
args
...
args)test {
let doc = @pp.standard_sexpr(@pp.txt("function"), [
@pp.txt("very-long-argument"),
])
inspect(doc.display_string(), content="(function very-long-argument)")
inspect(
doc.display_string(width=20),
content=(
#|(function
#| very-long-argument)
),
)
}test {
inspect(@pp.txt("Hello, world").display_string(), content="Hello, world")
}test {
// A CJK character is one UTF-16 unit wide but occupies two columns.
let cjk = @pp.txt_as("世界", 4)
assert_eq(cjk.flat_width(), Some(4))
// An ANSI colour escape is five UTF-16 units and occupies none.
let red = @pp.txt_as("\u{1b}[31m", 0)
let reset = @pp.txt_as("\u{1b}[0m", 0)
assert_eq(@pp.horz([red, @pp.txt("hello"), reset]).flat_width(), Some(5))
}test {
inspect(
@pp.horz([@pp.txt("> "), @pp.txt_lines("one\ntwo")]).display_string(),
content=(
#|> one
#| two
),
)
}test {
assert_eq(@pp.horz([@pp.txt("x = "), @pp.txt_lines("a\nb")]).display(), [
"x = a", " b",
])
assert_eq(@pp.horz([@pp.txt("x = "), @pp.txt_raw("a\nb")]).display(), [
"x = a", "b",
])
}test {
inspect(
@pp.vert([@pp.txt("Hello,"), @pp.txt("world!")]).display_string(),
content=(
#|Hello,
#|world!
),
)
}test {
inspect(
@pp.vert2(@pp.txt("a"), @pp.txt("b")).display_string(),
content=(
#|a
#|b
),
)
}test {
let words = ["This", "is", "a", "sentence", "with", "eight", "words"].map(
@pp.txt,
)
inspect(
@pp.wrap(words).display_string(width=20),
content=(
#|This is a sentence
#|with eight words
),
)
}Install
Download zipA linear time pretty printing library, ported from brownplt/pretty-fast-pretty-printer