Behaviour by format¶
The renderer's Format is fixed at construction and every method consults it.
This page states, per format, exactly what each method does — including the
cases where a method does nothing at all, and the options that are silently
ignored.
For the symbol list see formats & options; for the errors these calls can return see errors & failure modes.
Which methods produce output in which format¶
| Method | text |
json |
yaml |
csv |
tsv |
markdown |
|---|---|---|---|---|---|---|
Write |
calls textFunc |
marshals data |
marshals data |
tabulates | tabulates | tabulates |
Table |
aligned table | marshals rows | marshals rows | delimited | delimited | GFM table |
Render |
styled ANSI | no-op | styled ANSI | styled ANSI | styled ANSI | styled ANSI |
Emit / EmitError |
no-op | envelope | no-op | no-op | no-op | no-op |
Status / Progress / Spin |
writes | writes | writes | writes | writes | writes |
Two rows in that table surprise people, so they are stated plainly:
Emitis JSON-only. There is no YAML envelope. In every other formatEmitandEmitErrorreturnnilhaving written nothing, which is what lets you callEmitErrorunconditionally on the error path and still fall through to your normal text error handling.Renderis a no-op only in JSON. Inyaml,csv,tsvandmarkdownit writes glamour-styled ANSI escape sequences into the stream, which will corrupt output a machine is parsing. Guard it yourself if your tool can be asked for those formats.
The progress family ignores the format entirely — it is driven by
interactivity, not by Format. Point it at
os.Stderr so it cannot contaminate a machine-readable stdout.
An unrecognised Format value¶
Format is a string type, so a caller can construct one that is not in
Formats(). Both dispatchers fall through to text: Write calls textFunc
and Table renders an aligned text table. Nothing errors.
Values arriving from a flag or a config file should go through
ParseFormat, whose second return value is the only signal that the input was
not understood.
Which table options apply to which format¶
TableOption values are accepted by every format but honoured by only some:
| Option | text |
markdown |
csv / tsv |
json / yaml |
|---|---|---|---|---|
WithColumns |
yes | yes | yes | ignored |
WithSortBy |
yes | yes | yes | ignored |
WithSortDescending |
yes | yes | yes | ignored |
WithNoHeader |
yes | yes | ignored | ignored |
WithNoTruncation |
yes | n/a — never truncates | n/a | ignored |
WithMaxWidth |
yes | ignored | n/a | ignored |
json and yaml do not go through the column machinery at all — they marshal
the row slice as given. Three consequences:
- Field names and casing come from the
json:/yaml:struct tags, not from thetable:tags. A column set withWithColumnsdoes not narrow the output. - Fields excluded with
table:"-"still appear. - A sort column that is misspelled or not
Sortablereturns an error in every other format, but is silently accepted here, becauseWithSortByis never consulted.
WithNoHeader is honoured by the text and Markdown renderers only. CSV and TSV
always write a header row.
Text table layout¶
- Columns are separated by three spaces. Every cell is left-aligned; there is no alignment option.
- A column's width is
Column.Widthwhen non-zero, otherwise the display width of the widest of its header and cells. - Trailing spaces are trimmed from each line, so the final column is never padded.
- Widths are measured with
charmbracelet/x/ansi, which counts display columns rather than bytes, so CJK, emoji, combining characters and embedded ANSI do not skew alignment.
Text table truncation¶
Unless WithNoTruncation is set, the total table width is compared against the
terminal width and columns are shrunk to fit.
- The width used is
WithMaxWidthif given, otherwiseterm.GetSize(os.Stdout.Fd()), otherwise 80. It is measured against the process's stdout, not the renderer's writer — a renderer pointed at a buffer or a file still lays out at 80 columns. - Columns are shrunk right to left, and each may be reduced to a minimum of one column. So the rightmost columns lose their width first, and a narrow terminal can leave a header cut to a single character:
- A cell shortened below its content gains a
...tail, unless the remaining width is 3 or less, in which case it is cut without one. - Truncation is rune- and ANSI-aware: it never splits a multi-byte rune or an escape sequence in half.
Column.Width is a fixed width, not a minimum or a maximum — a header longer
than the width is truncated to it too.
CSV and TSV¶
Both are written by encoding/csv; TSV is the same writer with the comma
replaced by a tab. That means RFC 4180 quoting applies to both: a field
containing the delimiter, a double quote, a carriage return or a newline is
wrapped in double quotes.
If you need unquoted tab-separated fields, strip the delimiter from your values before rendering — there is no option to disable quoting.
Neither format truncates, and neither honours WithNoHeader.
Markdown¶
Output is a GitHub-flavoured pipe table with a header separator row, padded so the source lines up in a text editor.
Cell content is escaped before widths are computed, so what is measured is what is written:
- a literal
|becomes\|, so it cannot open a new column; \r\n,\rand\neach become<br>, so a multi-line value cannot open a new row.
Markdown tables are never truncated to terminal width — WithMaxWidth and
WithNoTruncation have no effect. A wide table produces a wide line, which is
usually what you want when the output is going into a file or a merge request
description.
JSON¶
Write, Table and Emit all use an indented encoder (two spaces) with a
trailing newline.
Successive Emit calls append successive documents; they are not collected
into an array, and the result is not a single valid JSON document:
Emit once per command invocation, with everything the caller needs inside
Data.
YAML¶
Write and Table encode via gopkg.in/yaml.v3 with its default settings: no
--- document marker, and keys lower-cased from field names unless a yaml:
tag says otherwise.
There is no YAML equivalent of the JSON envelope. If a script needs status and
command alongside the payload in YAML, build a struct carrying those fields
and pass it to Write.
A value YAML cannot represent — a channel, a function — makes the encoder panic rather than return an error. Marshal-safe types only.