pretty

    Wadler-style pretty printer for MoonBit: documents, groups and layout that fits a line width

    pretty-printer
    wadler
    layout
    document
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    7 hours ago
    Downloads
    72

    Dependencies

    #moonrockz/pretty

    A Wadler-style pretty printer for MoonBit. Build a document from text, line breaks and groups; render lays it out to fit a line width.

    let items = [@pretty.text("a"), @pretty.text("b"), @pretty.text("c")]
    let doc = @pretty.group(
    @pretty.text("[") +
    @pretty.nest(2, @pretty.line() + @pretty.join(items, @pretty.text(",") + @pretty.line())) +
    @pretty.line() +
    @pretty.text("]"),
    )
    @pretty.render(doc) // "[ a, b, c ]"
    @pretty.render(doc, width=6) // "[\n a,\n b,\n c\n]"

    FunctionFlatBroken
    text(s)ss
    verbatim(s)ss, no indentation after its line feeds
    line()a spacea line break
    softline()nothinga line break
    hardline()always a line break
    nest(n, d)dd, indented by n more
    align(d)dd, indented to its start column
    tab(n, d)dd, indented to the next multiple of n
    group(d)flat if it fitselse broken
    if_break(b, f)fb

    • A group is flat when it fits together with the rest of its line, up to the next possible break (the fit rule of Wadler's "A prettier printer").
    • Evaluation is strict (Lindig, "Strictly Pretty") and uses explicit stacks, so deep documents render on wasm, wasm-gc, js and native.
    • The engine writes no trailing whitespace and no indentation on empty lines. Width counts code points.

    Doc

    type Doc

    A document: pieces of text and places where render may break a line. Build documents with the functions of this package and join them with +. The representation is private, so it can change without a breaking release.
    impl Add for Doc

    align

    fn align(d : Doc) -> Doc

    d with the indentation of its line breaks set to the column where d starts.

    concat

    fn concat(docs : Array[Doc]) -> Doc

    The documents one after the other.

    empty

    fn empty() -> Doc

    The empty document. empty() + d and d + empty() render as d.

    group

    fn group(d : Doc) -> Doc

    d on one line when it fits (with the rest of the line up to the next possible break), otherwise d with its line breaks.

    test {
    let d = @pretty.group(@pretty.text("a") + @pretty.line() + @pretty.text("b"))
    inspect(@pretty.render(d, width=3), content="a b")
    inspect(@pretty.render(d, width=2), content="a\nb")
    }

    hardline

    fn hardline() -> Doc

    Always a line break and the indentation. Every group that holds it breaks.

    if_break

    fn if_break(broken : Doc, flat : Doc) -> Doc

    broken when the enclosing group breaks, flat when it is flat. Only flat decides whether the group can be flat.

    join

    fn join(docs : Array[Doc], sep : Doc) -> Doc

    The documents with sep between them.

    line

    fn line() -> Doc

    A space in flat mode; a line break and the indentation in break mode.

    nest

    fn nest(n : Int, d : Doc) -> Doc

    d with the indentation of its line breaks increased by n.

    render

    fn render(doc : Doc, width? : Int) -> String

    Lay out doc in lines of at most width columns where its groups allow. A group is flat when it fits, together with the rest of its line up to the next possible break. Text wider than width stays whole. Uses explicit stacks, so deep documents render on every target.

    test {
    let items = [@pretty.text("a"), @pretty.text("b")]
    let d = @pretty.group(
    @pretty.text("[") +
    @pretty.nest(
    2,
    @pretty.line() + @pretty.join(items, @pretty.text(",") + @pretty.line()),
    ) +
    @pretty.line() +
    @pretty.text("]"),
    )
    inspect(@pretty.render(d), content="[ a, b ]")
    inspect(@pretty.render(d, width=4), content="[\n a,\n b\n]")
    }

    softline

    fn softline() -> Doc

    Nothing in flat mode; a line break and the indentation in break mode.

    tab

    fn tab(n : Int, d : Doc) -> Doc

    d with the indentation of its line breaks set to the next multiple of n that is greater than the current indentation (a tab stop). With n <= 0 the indentation does not change. Use it after align to indent to a fixed grid: indentation 6 becomes 8, indentation 8 becomes 12.

    test {
    let body = @pretty.text("x") +
    @pretty.tab(4, @pretty.hardline() + @pretty.text("y"))
    inspect(
    @pretty.render(@pretty.text("ab") + @pretty.align(body)),
    content="abx\n y",
    )
    inspect(
    @pretty.render(@pretty.text("abcd") + @pretty.align(body)),
    content="abcdx\n y",
    )
    }

    text

    fn text(s : String) -> Doc

    The text s. A line feed in s is a hardline(), so the lines after it get the current indentation. Width counts code points.

    test {
    inspect(
    @pretty.render(@pretty.nest(2, @pretty.text("a\nb"))),
    content="a\n b",
    )
    }

    verbatim

    fn verbatim(s : String) -> Doc

    The text s as it is: a line feed in s starts a new line with no indentation. Use it for text whose bytes must not change, such as a doc comment. A group that holds a line feed this way always breaks.

    Source Files

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io