Argparse.PrettyPrinter

This module lets you define how text should be formatted before printing it to the terminal.

Wrapping

toString does not wrap: defaultOptions.maxColumns is Math.maxSafeInteger, so a Document comes out however long it is and the terminal makes of it what it will. Give it a column budget with toStringWithOptions and the text is broken to fit. Argparse.Program does this for you — it passes the width of the terminal it is talking to, falling back to 80 columns when there is no terminal to ask — so a program built on it wraps its help and error output without doing anything.

The dots below mark the unused columns of the budget.

words breaks between words:

PP.words "a description long enough to wrap"
    |> PP.toStringWithOptions
        { PP.defaultOptions | maxColumns = 20 }

a description long··
enough to wrap······

text has no word boundaries to break at, so it breaks wherever the budget runs out:

a description long e
nough to wrap·······

The lines a document wraps onto are rows of their own, so they open under the same indent as the line they came from:

PP.words "a description long enough to wrap"
    |> PP.indent
    |> PP.toStringWithOptions
        { PP.defaultOptions | maxColumns = 20 }

    a description···
    long enough to··
    wrap············

Inside a block, each part is laid out against the columns its neighbours to the left have already taken — a part does not start at column 0 just because it is a document of its own:

PP.block
    [ PP.text "--flag "
    , PP.words "a description long enough to wrap"
    ]
    |> PP.indent
    |> PP.toStringWithOptions
        { PP.defaultOptions | maxColumns = 30 }

    --flag a description long·
    enough to wrap············

Only the first line of a part is squeezed that way. Once it wraps it is starting rows of its own, which get the whole width.

type Document

A Document represents formatted text.

empty : Document

The empty Document takes up no space. It's analogous to the empty String.

text : String -> Document

Turn a String into a Document. Newlines are not respected, if you want to spread text over multiple lines, use verticalBlock.

Word boundaries are also not respected. If that's important to you, you might want to use words. Under a column budget that means a long text is cut wherever the budget runs out, mid-word if that is where it lands:

PP.text "a description long enough to wrap"
    |> PP.toStringWithOptions
        { PP.defaultOptions | maxColumns = 20 }

a description long e
nough to wrap
words : String -> Document

Similar to text, but word boundaries are respected if the String has to be broken up over multiple lines. Whitespace between words are reduced to a single space.

PP.words "a description long enough to wrap"
    |> PP.toStringWithOptions
        { PP.defaultOptions | maxColumns = 20 }

a description long
enough to wrap

A word wider than the whole budget still gets a line of its own — it is placed rather than cut.

indent : Document -> Document

The contents of the Document is indented one level.

The indent is not just a prefix on the first line: the lines the contents wrap onto are rows of their own, and open under the same indent.

PP.words "a description long enough to wrap"
    |> PP.indent
    |> PP.toStringWithOptions
        { PP.defaultOptions | maxColumns = 20 }

    a description
    long enough to
    wrap

The indent counts against the budget, so the text above has 16 columns to work with, not 20.

block : Array Document -> Document

This joins multiple Documents into one. If possible, everything will be placed on a single line.

Under a column budget, each part is laid out against the columns the parts to its left have already taken, so it breaks where the row actually runs out rather than where it would have if it had started the line:

PP.block
    [ PP.text "--flag "
    , PP.words "a description long enough to wrap"
    ]
    |> PP.indent
    |> PP.toStringWithOptions
        { PP.defaultOptions | maxColumns = 30 }

    --flag a description long
    enough to wrap

Only a part's first line is squeezed that way — it is the one continuing somebody else's row. The lines it wraps onto are its own, and get the full width and the surrounding indent.

verticalBlock : Array Document -> Document

Like block, but each Document is placed on a seperate line.

type Color
= Black
| Red
| Green
| Yellow
| Blue
| Magenta
| Cyan
| White

Supported text colors

color : Color -> Document -> Document

Colorize the text in the given Document.

intenseColor : Color -> Document -> Document

Like color, but intensifies the given color.

stripColor : Document -> Document

This removes all colorized text in a Document.

toString : Document -> String

Convert a Document into a String that's ready to be written to the terminal.

type alias ToStringOptions =
{ maxColumns : Int
, indentationSize : Int
, newlineSeparator : String
}

Different settings when converting a Document into String.

  • maxColumns defines the maximum number of characters in a line. No rendered line is wider than this, indent included; see words for how the text is broken to fit, and the module header for worked examples.
  • indentationSize defines the number of spaces per indentation level.
  • newlineSeparator defines the String used for representing newlines.
defaultOptions : ToStringOptions

A default set of options for converting Document into String.

maxColumns is Math.maxSafeInteger here, which is to say these options do not wrap at all. Pass a real width to toStringWithOptions to get wrapping — or let Argparse.Program do it, which renders with the width of the terminal it is talking to and 80 columns when there is no terminal.

toStringWithOptions : ToStringOptions -> Document -> String

Like toString, but allows you to override the default options.