termtable

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 22, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

README

termtable

A library for compsing and rendering text tables on a terminal featuring a DOM-like model. termtable handles Unicode width (emoji, CJK, combining marks), ANSI escape sequences, word-wrapping, trimming, and column/row spans with configurable Unicode box borders. All controllable programatically and through CSS declarations.

Install

go get github.com/carabiner-dev/termtable

Quick start

package main

import (
	"fmt"

	"github.com/carabiner-dev/termtable"
)

func main() {
	t := termtable.NewTable(termtable.WithTargetWidth(50))

	banner := t.AddHeader()
	banner.AddCell(
		termtable.WithContent("Evaluation Results"),
		termtable.WithColSpan(3),
		termtable.WithAlign(termtable.AlignCenter),
	)

	head := t.AddHeader()
	head.AddCell(termtable.WithContent("Check"))
	head.AddCell(termtable.WithContent("Status"))
	head.AddCell(termtable.WithContent("Message"))

	r1 := t.AddRow()
	r1.AddCell(termtable.WithContent("OSPS-BR-05"))
	r1.AddCell(termtable.WithContent("PASS"),
		termtable.WithAlign(termtable.AlignCenter))
	r1.AddCell(termtable.WithContent("all criteria met"))

	r2 := t.AddRow()
	r2.AddCell(termtable.WithContent("OSPS-DO-02"))
	r2.AddCell(termtable.WithContent("FAIL"),
		termtable.WithAlign(termtable.AlignCenter))
	r2.AddCell(termtable.WithContent("review dependencies"))

	fmt.Print(t.String())
}

Output:

┌────────────────────────────────────────────────┐
│               Evaluation Results               │
├────────────────┬────────────┬──────────────────┤
│ Check          │ Status     │ Message          │
├────────────────┼────────────┼──────────────────┤
│ OSPS-BR-05     │    PASS    │ all criteria met │
├────────────────┼────────────┼──────────────────┤
│ OSPS-DO-02     │    FAIL    │ review           │
│                │            │ dependencies     │
└────────────────┴────────────┴──────────────────┘

Examples

Feature-by-feature runnable snippets live in examples/. Start with examples/basic.md and browse from there.

Docs

Start with the guide for a walkthrough of the mental model, a step-by-step tutorial, and pointers to the rest. Per-subsystem reference:

  • Border styles — the six built-in glyph sets and how to select them imperatively or via CSS.
  • Column configuration — sizing (width / min / max / weight), alignment cascade, Column.Style CSS.
  • Styling — table → column → row → cell cascade, CSS property reference, colour grammar, NoColor handling.
  • Wrapping and overflow — single-line vs multi-line modes, white-space / text-overflow / line-clamp, column and table cascade for line-mode control.
  • Emoji width — why tables can misalign on some terminals, the conservative vs grapheme modes, auto-detection whitelist, and the TERMTABLE_EMOJI_WIDTH env var.
  • Warnings — authoring vs render events, Table.Warnings, Table.LastRenderError.

Contributing

PRs welcome! If you're adding a feature, pair it with:

  • A focused test next to the existing ones for the subsystem you're touching.
  • A short entry under examples/ if the feature is user-visible.
  • A note in the matching page under docs/ for anything affecting the API or CSS surface.

Before opening a PR, please run:

go test -race ./...
golangci-lint run

Both should be clean. Stable snapshots of the rendered tables are locked via testdata/golden/ — regenerate with TERMTABLE_UPDATE_GOLDEN=1 go test ./... if your change intentionally alters the baseline.

License

termtable is Copyright by Carabiner Systems, Inc and released under the Apache-2.0 license. See LICENSE for details, as with all our open source projects feel free to open pull requests and issues, we love feedback!

Documentation

Overview

Package termtable provides a DOM-like model for composing and rendering text tables on a terminal. It handles Unicode width (including emoji and CJK), ANSI escape sequences, word-wrapping, trimming, and column/row spanning with single-line Unicode box-drawing borders.

A table is composed of headers, body rows, and footers. Each row contains cells. Cells can span multiple columns (ColSpan) and multiple rows within the same section (RowSpan). Headers, body, and footers form separate sections in the grid; rowspans do not cross section boundaries.

Elements can be addressed two ways:

  • Logical: row.Cell(i) returns the i-th declared cell in that row.
  • Grid: table.CellAt(r, c) returns the cell covering the absolute grid coordinate (r, c); multiple (r, c) pairs map to the same cell when it spans.

Any element may be tagged with a unique ID via its With*ID option and looked up with table.GetElementByID.

Rendering proceeds in three passes: measurement, layout, and paint. The table is fully buffered; there is no streaming output.

Known limitations (Phase 1–3):

  • Right-to-left / bidirectional text is not supported.
  • Styling (colors, bold, alternate border styles) is reserved for a later phase; option hooks exist but have no effect yet.
  • Terminal width resolves in this order: WithTargetWidth, WithTargetWidthPercent (as a fraction of the detected terminal width, COLUMNS, or 80), the COLUMNS environment variable, then the default rule — fill 90% of the attached screen with 80 as the floor. The resolved value is clamped to the attached terminal's width when one is detected, so output never exceeds the physical screen; writing to a pipe leaves the value uncapped.
Example

Example demonstrates the minimal usage pattern: build a table, add headers and rows, print the result.

package main

import (
	"fmt"

	"github.com/fatih/color"

	"github.com/carabiner-dev/termtable"
)

func main() {
	// Suppress ANSI codes so the expected output below is stable
	// regardless of where `go test` is run.
	color.NoColor = true

	t := termtable.NewTable(termtable.WithTargetWidth(30))

	h := t.AddHeader()
	h.AddCell(termtable.WithContent("Name"))
	h.AddCell(termtable.WithContent("Count"))

	r1 := t.AddRow()
	r1.AddCell(termtable.WithContent("alpha"))
	r1.AddCell(termtable.WithContent("1"))

	r2 := t.AddRow()
	r2.AddCell(termtable.WithContent("beta"))
	r2.AddCell(termtable.WithContent("2"))

	fmt.Print(t.String())
}
Output:
┌──────────────┬─────────────┐
│ Name         │ Count       │
├──────────────┼─────────────┤
│ alpha        │ 1           │
├──────────────┼─────────────┤
│ beta         │ 2           │
└──────────────┴─────────────┘
Example (BorderStyle)

Example_borderStyle shows selecting an alternate border glyph set through the table's CSS-style configuration.

package main

import (
	"fmt"

	"github.com/fatih/color"

	"github.com/carabiner-dev/termtable"
)

func main() {
	color.NoColor = true

	t := termtable.NewTable(
		termtable.WithTargetWidth(30),
		termtable.WithTableStyle("border-style: rounded"),
	)

	h := t.AddHeader()
	h.AddCell(termtable.WithContent("Col A"))
	h.AddCell(termtable.WithContent("Col B"))

	r := t.AddRow()
	r.AddCell(termtable.WithContent("one"))
	r.AddCell(termtable.WithContent("two"))

	fmt.Print(t.String())
}
Output:
╭──────────────┬─────────────╮
│ Col A        │ Col B       │
├──────────────┼─────────────┤
│ one          │ two         │
╰──────────────┴─────────────╯
Example (Columns)

Example_columns shows configuring column widths and alignment with the CSS-style Column.Style helper.

package main

import (
	"fmt"

	"github.com/fatih/color"

	"github.com/carabiner-dev/termtable"
)

func main() {
	color.NoColor = true

	t := termtable.NewTable(termtable.WithTargetWidth(40))
	t.Column(0).Style("min-width: 10")
	t.Column(1).Style("width: 6; text-align: center")
	t.Column(2).Style("flex: 2")

	h := t.AddHeader()
	h.AddCell(termtable.WithContent("Check"))
	h.AddCell(termtable.WithContent("Sts"))
	h.AddCell(termtable.WithContent("Message"))

	r := t.AddRow()
	r.AddCell(termtable.WithContent("lookup"))
	r.AddCell(termtable.WithContent("OK"))
	r.AddCell(termtable.WithContent("all good"))

	fmt.Print(t.String())
}
Output:
┌───────────────┬────────┬─────────────┐
│ Check         │  Sts   │ Message     │
├───────────────┼────────┼─────────────┤
│ lookup        │   OK   │ all good    │
└───────────────┴────────┴─────────────┘

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrSpanConflict is returned when a cell's span would overlap a grid
	// slot already occupied by another cell, and the table is not
	// configured with WithSpanOverwrite(true). Returned errors wrap this
	// sentinel with positional context.
	ErrSpanConflict = errors.New("cell span conflicts with an occupied grid slot")

	// ErrReaderAlreadyConsumed is a defensive guard returned if a cell's
	// reader has already been consumed when a render pass attempts to
	// resolve it again. In normal operation the cell buffers the reader's
	// content on first use so this error should not surface.
	ErrReaderAlreadyConsumed = errors.New("cell reader already consumed")

	// ErrTargetTooNarrow is returned during layout when the target
	// width cannot give every column at least one glyph of content
	// space after paying for borders and padding. When minimums
	// merely exceed the budget (a common case on narrow terminals),
	// the layout silently shrinks columns and clips content — that
	// is not an error.
	ErrTargetTooNarrow = errors.New("target width too narrow for one glyph per column")

	// ErrCrossSectionSpan is the sentinel wrapped by CrossSectionSpanEvent
	// for callers that want to use errors.Is on a warning's backing
	// error. No API returns this error directly — rowspan clamping
	// is reported as a warning, not a returned error.
	ErrCrossSectionSpan = errors.New("row span crosses section boundary")
)

Functions

func DisplayWidth

func DisplayWidth(s string) int

DisplayWidth returns the number of terminal columns s would occupy when rendered, ignoring ANSI escape sequences. Grapheme clusters are counted by their East Asian Width: most characters are 1 column, CJK and emoji are 2, combining marks are 0.

This reports the Unicode-standard width. Table rendering may pick a wider "conservative" value to survive terminals without emoji ligature support — see EmojiWidthMode.

func Layout

func Layout(t *Table, m *measureResult) *layoutResult

Layout performs Pass 2 of rendering: it takes per-column widths from Measure, combines them with user-supplied column configuration (width / min / max / weight), solves a width assignment that fits within the table's target width, then wraps every cell's content to those widths and computes per-row heights.

The solver algorithm:

  1. Effective bounds: effMin = max(contentMin, userMin); effMax = userMax (or unbounded). Columns with SetWidth are pinned so effMin = effMax = userWidth (clamped up to contentMin on conflict, producing a best-effort overflow rather than silently cropping content).
  2. Feasibility: when sum(effMin) exceeds the budget, shrink every column proportionally so each still gets at least one glyph of width; cells with unbreakable content wider than their slot clip it with an ellipsis via the wrap pass. ErrTargetTooNarrow only fires when the budget cannot even give every column a single glyph (available < nCols).
  3. Initialize colAssigned to effMin. Water-fill the remaining budget by column weights (default 1.0), capped at effMax per column. Rounding leftovers distribute one-per-column left-first until consumed.
  4. Multi-span constraint pass: for each multi-span cell, borrow width from outside-span slack until the cell's min fits across its columns. Donors must remain >= effMin; receivers must remain <= effMax.
  5. Wrap every cell to its content width and compute row heights, bumping the tail row of rowspan cells when their wrapped output exceeds the natural sum of covered rows.

Empty tables (zero columns) return an empty result with no error.

func Measure

func Measure(t *Table) *measureResult

Measure performs Pass 1 of rendering: it walks every cell in the table, consumes any WithReader content (caching the bytes), and accumulates per-column minimum and desired display widths.

Single-span cells contribute directly to the column they occupy. Multi-span cells contribute a joint constraint that the layout solver balances across the cell's column range.

Reader failures are recorded in readerErrs but do not abort measurement; affected cells are treated as empty.

func MinUnbreakableWidth

func MinUnbreakableWidth(s string) int

MinUnbreakableWidth returns the display width of the widest run of consecutive non-whitespace grapheme clusters in s (ANSI ignored). It is the smallest column width at which s can render without hard- breaking a word. Widths follow Unicode standard semantics; see DisplayWidth.

func NaturalLines

func NaturalLines(s string) [][]GraphemeRun

NaturalLines splits s on '\n' (hard breaks) and returns each line as a sequence of grapheme runs with preserved ANSI escape prefixes. A trailing '\r' on each split line is removed so CRLF inputs behave the same as LF inputs.

An input of "" produces a single empty line. An input of "\n" produces two empty lines.

func StripANSI

func StripANSI(s string) string

StripANSI returns s with all ANSI escape sequences removed. Visible text bytes are preserved byte-for-byte.

func Wrap

func Wrap(lines [][]GraphemeRun, width int, wrap, trim bool, maxHeight int, trimPos TrimPosition) []string

Wrap transforms a slice of natural lines (e.g. as returned by NaturalLines) into a slice of rendered lines each at most width terminal columns wide. When wrap is true, content breaks at whitespace where possible and hard-breaks on runs longer than width. When wrap is false, each natural line yields one output line, optionally truncated with "…" if trim is true and the line exceeds width.

ANSI escape state is preserved across line boundaries: the cumulative escape bytes that were active at the start of each output line are re-emitted at its head, and every line that carries any escape bytes is terminated with a reset ("\x1b[0m"). This is deliberately redundant — a reduced-fidelity policy that keeps colored content readable without full SGR state tracking.

maxHeight > 0 caps the total number of output lines. Exceeding content is dropped; when trim is also true the last kept line is truncated to end in "…". maxHeight == 0 disables the cap.

Types

type Alignment

type Alignment uint8

Alignment controls horizontal placement of wrapped cell content within its allotted width.

const (
	// AlignLeft is the default: content hugs the left edge, right padded
	// to fill the column.
	AlignLeft Alignment = iota
	// AlignCenter distributes padding on both sides; any odd remainder
	// goes to the right side.
	AlignCenter
	// AlignRight hugs the right edge, left padded to fill the column.
	AlignRight
)

func (Alignment) String

func (a Alignment) String() string

type BorderEdge added in v1.1.0

type BorderEdge uint8

BorderEdge is the per-edge directive used by row and cell styles. The zero value (BorderEdgeAuto) means "inherit from a parent or fall back to the table default".

const (
	// BorderEdgeAuto is the unset value — the edge inherits from the
	// enclosing row, then the table default.
	BorderEdgeAuto BorderEdge = iota
	// BorderEdgeNone suppresses the edge entirely. If every adjacent
	// cell at a boundary resolves to None, the line (or column seam)
	// is omitted from output altogether.
	BorderEdgeNone
	// BorderEdgeHidden keeps the boundary's spacing but paints it as
	// whitespace. The line is still emitted; the glyph is a space.
	BorderEdgeHidden
	// BorderEdgeSolid draws the boundary using the table's BorderSet
	// glyphs (single/double/heavy/…). "Solid" here is the CSS name
	// for "draw it"; the actual stroke style is the BorderSet choice.
	BorderEdgeSolid
)

type BorderSet

type BorderSet struct {
	// Horizontal is the glyph repeated along horizontal border runs.
	Horizontal rune
	// Vertical is the glyph repeated along vertical border runs.
	Vertical rune
	// Joins maps arm bitmasks to the glyph drawn at corners and
	// junctions.
	Joins [16]rune
}

BorderSet is the glyph set used to draw table borders. The Joins array is indexed by a four-bit arm mask with bit layout (least-significant first): N=1, E=2, S=4, W=8. Only the 11 valid entries (runs, corners, T-joins, crosses) are consulted during rendering; the remaining entries are left zero.

termtable ships several ready-made sets (SingleLine, DoubleLine, HeavyLine, RoundedLine, ASCIILine, NoBorder). Callers may also construct a BorderSet manually — see the ASCIILine source for the full shape.

func ASCIILine

func ASCIILine() BorderSet

ASCIILine returns an ASCII-only BorderSet (- | +) suitable for environments that cannot render Unicode box-drawing characters — logs, legacy terminals, email clients. All T-joins, corners, and crosses render as '+'.

func DefaultSingleLine deprecated

func DefaultSingleLine() BorderSet

DefaultSingleLine is an alias for SingleLine retained for compatibility with Phase 1 code. New callers should use SingleLine directly.

Deprecated: use SingleLine.

func DoubleLine

func DoubleLine() BorderSet

DoubleLine returns the Unicode double-line BorderSet: ═ ║ ╔ ╗ ╚ ╝ ╠ ╣ ╦ ╩ ╬ . Useful for emphasis or to visually distinguish outer borders from any future inner styling.

func HeavyLine

func HeavyLine() BorderSet

HeavyLine returns the Unicode heavy (bold) box-drawing BorderSet: ━ ┃ ┏ ┓ ┗ ┛ ┣ ┫ ┳ ┻ ╋ .

func NoBorder

func NoBorder() BorderSet

NoBorder returns a BorderSet whose glyphs are all U+0020 (space). The resulting table has no visible dividers but preserves the horizontal and vertical spacing, producing an invisibly-gridded layout. Combine with WithTablePadding(Padding{}) to collapse padding as well.

func RoundedLine

func RoundedLine() BorderSet

RoundedLine returns a BorderSet using single-line runs and joins but rounded outer corners (╭ ╮ ╰ ╯). Unicode has no rounded equivalents for T-joins or the full cross, so those remain the standard single-line glyphs.

func SingleLine

func SingleLine() BorderSet

SingleLine returns the Unicode single-line box-drawing BorderSet: ─ │ ┌ ┐ └ ┘ ├ ┤ ┬ ┴ ┼ . This is the default used by NewTable when no WithBorder option is supplied.

type Cell

type Cell struct {
	// contains filtered or unexported fields
}

Cell is the fundamental content-bearing element. Cells belong to at most one row and occupy a rectangle in the table's grid defined by their anchor (GridRow, GridCol) and span (ColSpan, RowSpan).

func NewCell

func NewCell(opts ...CellOption) *Cell

NewCell constructs a detached cell with the given options. A detached cell has no grid position until it is passed to a row via WithCell or Row.AttachCell.

func (*Cell) Align

func (c *Cell) Align() Alignment

Align returns the cell's horizontal alignment. Reads the cell's style; returns AlignLeft when no alignment has been set at the cell level (column, row, and table cascade happens at render time).

func (*Cell) ColSpan

func (c *Cell) ColSpan() int

ColSpan returns the number of columns the cell occupies.

func (*Cell) Content

func (c *Cell) Content() string

Content returns the cell's authored string content. If the cell was configured with WithReader and the reader has not yet been consumed, the empty string is returned.

func (*Cell) GridCol

func (c *Cell) GridCol() int

GridCol returns the zero-based column of the cell's anchor.

func (*Cell) GridRow

func (c *Cell) GridRow() int

GridRow returns the absolute grid row of the cell's anchor across the whole table (headers first, then body, then footers). For detached cells (constructed with NewCell but never attached to a row), returns the section-local row since no table context exists.

func (*Cell) ID

func (c *Cell) ID() string

ID returns the cell's user-assigned ID, or the empty string.

func (*Cell) RowSpan

func (c *Cell) RowSpan() int

RowSpan returns the number of rows the cell occupies within its section.

type CellOption

type CellOption func(*Cell)

CellOption configures a *Cell during NewCell, AddCell, or AttachCell.

func WithAlign

func WithAlign(a Alignment) CellOption

WithAlign sets the cell's horizontal alignment. Default is AlignLeft. Stored on the cell's Style so it participates in the table → column → row → cell cascade; cells that never call WithAlign inherit from their row, then column, then table, defaulting to AlignLeft.

func WithBackgroundColor

func WithBackgroundColor(value string) CellOption

WithBackgroundColor sets the cell's background color. Accepts the same value grammar as WithTextColor.

func WithBold

func WithBold() CellOption

WithBold enables the bold text attribute on the cell.

func WithCellBorder added in v1.1.0

func WithCellBorder(e BorderEdge) CellOption

WithCellBorder sets the border directive on all four edges of the cell. Equivalent to the CSS shorthand "border: <e>" in a cell style. See BorderEdge for the semantics.

func WithCellBorderBottom added in v1.1.0

func WithCellBorderBottom(e BorderEdge) CellOption

WithCellBorderBottom sets the border directive for the cell's bottom edge.

func WithCellBorderLeft added in v1.1.0

func WithCellBorderLeft(e BorderEdge) CellOption

WithCellBorderLeft sets the border directive for the cell's left edge.

func WithCellBorderRight added in v1.1.0

func WithCellBorderRight(e BorderEdge) CellOption

WithCellBorderRight sets the border directive for the cell's right edge.

func WithCellBorderTop added in v1.1.0

func WithCellBorderTop(e BorderEdge) CellOption

WithCellBorderTop sets the border directive for the cell's top edge.

func WithCellID

func WithCellID(id string) CellOption

WithCellID assigns a unique ID to the cell.

func WithCellStyle

func WithCellStyle(css string) CellOption

WithCellStyle sets style properties on the cell, cascaded over the row's and table's style. See WithTableStyle for the CSS grammar. Convenience options WithTextColor, WithBackgroundColor, WithBold, WithItalic, WithUnderline, and WithStrikethrough set individual properties and may be combined with WithCellStyle.

func WithColSpan

func WithColSpan(n int) CellOption

WithColSpan sets the number of columns the cell occupies. Values of n <= 0 clamp to 1 (the default) so the option never produces an invalid span.

func WithContent

func WithContent(s string) CellOption

WithContent sets the cell's textual content. Honors "\n" as a hard line break; combines with automatic wrapping when the cell is wider than its assigned column width. If a reader source was previously set on the cell via WithReader, it is discarded (a ContentSourceReplacedEvent warning is emitted when the cell is attached to a row).

func WithItalic

func WithItalic() CellOption

WithItalic enables the italic text attribute on the cell.

func WithMaxLines

func WithMaxLines(n int) CellOption

WithMaxLines caps the cell's wrapped content to at most n lines. A value of 0 means unbounded (the default). Equivalent to CSS line-clamp: N. When the limit fires and trim is enabled, the final kept line ends in an ellipsis.

func WithMultiLine

func WithMultiLine() CellOption

WithMultiLine is a shorthand for WithWrap(true). Useful when a row or column has forced single-line mode and a particular cell needs to opt back into wrapping.

func WithReader

func WithReader(r io.Reader) CellOption

WithReader sets the cell's content source to an io.Reader consumed lazily on the first render pass. If a string source was previously set on the cell via WithContent, it is discarded (a ContentSourceReplacedEvent warning is emitted when the cell is attached to a row).

func WithRowSpan

func WithRowSpan(n int) CellOption

WithRowSpan sets the number of rows the cell occupies within its section. Values of n <= 0 clamp to 1 (the default). Rowspans that would extend past the last row of the section are clamped by the renderer and a CrossSectionSpanEvent is emitted.

func WithSingleLine

func WithSingleLine() CellOption

WithSingleLine is a shorthand for WithWrap(false). Long content renders on one line; if trim is enabled (the default) it is truncated with an ellipsis.

func WithStrikethrough

func WithStrikethrough() CellOption

WithStrikethrough enables the line-through text attribute on the cell. (Not every terminal renders this; supported by most modern emulators.)

func WithTextColor

func WithTextColor(value string) CellOption

WithTextColor sets the cell's foreground color. Accepts named colors, a hex string, or rgb(r,g,b). Unrecognized values are ignored.

func WithTrim

func WithTrim(enable bool) CellOption

WithTrim toggles ellipsis-based trimming when a cell's content must be cut (either because single-line content overflows the column, or because a line-clamp limit was exceeded). Default is true. Equivalent to CSS text-overflow: ellipsis (trim=true) or clip (trim=false).

func WithTrimPosition

func WithTrimPosition(pos TrimPosition) CellOption

WithTrimPosition controls where the ellipsis (or clip) lands when a cell's content must be truncated to fit. Default is TrimEnd — the content's prefix is kept and the marker sits at the right edge. TrimStart keeps the suffix (marker on the left), TrimMiddle keeps both ends. Equivalent to termtable's CSS extension text-overflow-position: end | start | middle.

This only affects horizontal single-line truncation (wrap=false, or the last line of a line-clamped multi-line cell when it doesn't fit its column width). Vertical dropping under line-clamp always happens from the end.

func WithUnderline

func WithUnderline() CellOption

WithUnderline enables the underline text attribute on the cell.

func WithVAlign

func WithVAlign(v VerticalAlignment) CellOption

WithVAlign sets the cell's vertical alignment within its row (which may be taller than the cell's own wrapped content when a neighbour wrapped to more lines). Default is VAlignTop. Like WithAlign, the value cascades via Style — cells without an explicit vertical alignment inherit row, column, and table defaults in that order.

func WithWrap

func WithWrap(enable bool) CellOption

WithWrap toggles automatic word-wrapping on whitespace. Default is true (multi-line). Equivalent to setting CSS white-space: normal (wrap=true) or nowrap (wrap=false). Participates in the Style cascade — a row or column setting the same property forces every inheriting cell.

type Column

type Column struct {
	// contains filtered or unexported fields
}

Column is a virtual element representing a grid column. Columns are created automatically as cells populate new column positions; they may also be retrieved explicitly via Table.Column. Configuration is imperative through Set* methods:

t.Column(1).SetMax(8).SetAlign(termtable.AlignCenter)

Methods return the receiver so calls may be chained. Unset fields are inherited from content measurements or from the layout solver's defaults (weight = 1, alignment = AlignLeft).

func (*Column) Align

func (c *Column) Align() Alignment

Align returns the column's alignment override. Check HasAlign to distinguish an unset value from an explicit AlignLeft.

func (*Column) HasAlign

func (c *Column) HasAlign() bool

HasAlign reports whether the column has an alignment override in force, either from Column.SetAlign or from Column.Style with text-align.

func (*Column) ID

func (c *Column) ID() string

ID returns the column's user-assigned ID, or the empty string.

func (*Column) Index

func (c *Column) Index() int

Index returns the zero-based column index.

func (*Column) Max

func (c *Column) Max() int

Max returns the user-set maximum width, or 0 if unset.

func (*Column) Min

func (c *Column) Min() int

Min returns the user-set minimum width, or 0 if unset.

func (*Column) SetAlign

func (c *Column) SetAlign(a Alignment) *Column

SetAlign sets the default horizontal alignment for cells in this column. Cells with their own WithAlign override this; cells without an explicit alignment, and rows without a row-level text-align, inherit from the column.

func (*Column) SetID

func (c *Column) SetID(id string) *Column

SetID assigns the column an ID that can be resolved via Table.GetElementByID. Passing an empty string unsets a previously assigned ID. If id collides with an element already using it, a DuplicateIDEvent is recorded on Table.Warnings, the old ID mapping is preserved, and the column's ID is cleared.

func (*Column) SetMax

func (c *Column) SetMax(n int) *Column

SetMax caps the column's content width at n. The solver honors the cap even when content would naturally prefer more space; content wraps to fit. A value of n <= 0 clears the cap. Also clears any percent form previously set via SetMaxPercent.

func (*Column) SetMaxPercent

func (c *Column) SetMaxPercent(p int) *Column

SetMaxPercent caps the column's content width at p percent of the table's target width. Mutually exclusive with SetMax — last setter wins.

func (*Column) SetMin

func (c *Column) SetMin(n int) *Column

SetMin sets a lower bound on the column's content width. The effective minimum is max(contentMinimum, userMinimum), so this never shrinks the column below what the content genuinely requires. A value of n <= 0 clears the override. Also clears any percent form previously set via SetMinPercent.

func (*Column) SetMinPercent

func (c *Column) SetMinPercent(p int) *Column

SetMinPercent sets the column's min-width floor as p percent of the table's target width. Mutually exclusive with SetMin — last setter wins.

func (*Column) SetVAlign

func (c *Column) SetVAlign(v VerticalAlignment) *Column

SetVAlign sets the default vertical alignment for cells in this column. Cells with their own WithVAlign override this; otherwise the column's value participates in the table → column → row → cell cascade.

func (*Column) SetWeight

func (c *Column) SetWeight(w float64) *Column

SetWeight sets the column's share of leftover width after minimums and explicit widths are satisfied. Columns with larger weights receive proportionally more of the remainder. Default is 1.0 (equal share). A weight of 0 prevents the column from absorbing any leftover — useful for pinning a column close to its minimum while others grow.

func (*Column) SetWidth

func (c *Column) SetWidth(n int) *Column

SetWidth pins the column to exactly n display columns of content (not counting padding or borders). Overrides SetMin and SetMax for layout purposes. A value of n <= 0 clears the explicit width so the solver returns to applying min/max/weight instead. Also clears any percent form previously set via SetWidthPercent.

func (*Column) SetWidthPercent

func (c *Column) SetWidthPercent(p int) *Column

SetWidthPercent pins the column to p percent of the table's target width. Mutually exclusive with SetWidth — last setter wins. p must be in (0, 100]; out-of-range values clear the percent form.

func (*Column) Style

func (c *Column) Style(css string) *Column

Style parses a CSS-like declaration block and applies it to the column. Sizing properties route to the imperative Set* methods (so Column.Style and Column.SetWidth are interchangeable); style properties populate a column-level Style that cascades to every cell in the column.

Supported sizing properties:

width: N           pins the column to exactly N content columns
min-width: N       lower bound on content width
max-width: N       upper bound on content width
flex: N            weight for distributing leftover budget
text-align: L|C|R  default alignment for cells in the column

Style properties are the same set accepted by WithTableStyle (color, background, font-weight, font-style, text-decoration). border-color at column level is ignored — border glyphs are table-wide and configured via WithTableStyle.

Unrecognized properties and unparseable values are silently ignored.

func (*Column) Weight

func (c *Column) Weight() float64

Weight returns the column's distribution weight. Defaults to 1.0 for columns that have not called SetWeight.

func (*Column) Width

func (c *Column) Width() int

Width returns the pinned width set via SetWidth, or 0 if unset.

type ContentSourceReplacedEvent

type ContentSourceReplacedEvent struct {
	CellID      string
	FinalSource string
}

ContentSourceReplacedEvent is recorded when a cell is configured with both WithContent and WithReader. The later option wins; the earlier source is discarded. FinalSource is "content" or "reader" depending on which option was applied last.

func (ContentSourceReplacedEvent) String

type CrossSectionSpanEvent

type CrossSectionSpanEvent struct {
	CellID        string
	DeclaredSpan  int
	EffectiveSpan int
	Section       string
}

CrossSectionSpanEvent is recorded when a rowSpan declared on a cell reaches beyond the last row of its section (headers, body, or footers). Rendering clamps the effective rowspan to the section boundary; authored rowSpan on the Cell is preserved as-is.

func (CrossSectionSpanEvent) String

func (e CrossSectionSpanEvent) String() string

type DuplicateIDEvent

type DuplicateIDEvent struct {
	ID   string
	Kind string // "cell", "row", "header", "footer", "column", "table"
}

DuplicateIDEvent is recorded when an element tries to register an ID already claimed by a different element. The first element keeps its registration; the second element's ID is cleared so Table.GetElementByID cannot return ambiguous results.

func (DuplicateIDEvent) String

func (e DuplicateIDEvent) String() string

type Element

type Element interface {
	// contains filtered or unexported methods
}

Element is the sealed interface implemented by every addressable object in a table: *Table, *Header, *Footer, *Row, *Cell, *Column. Callers resolve to a concrete type with a type switch.

type EmojiWidthMode

type EmojiWidthMode uint8

EmojiWidthMode controls how termtable counts display columns for grapheme clusters whose rendering varies across terminals — most commonly ZWJ emoji families like 👨‍👩‍👧, regional-indicator flag pairs like 🇯🇵, and skin-tone modifier sequences like 👋🏽.

The Unicode standard considers each of these a single grapheme cluster and defines a "collapsed" display width (typically 2). In practice the rendering width depends on whether the user's font has a glyph for the joined form. When the font doesn't, terminals fall back to rendering each constituent codepoint as its own glyph — which can double, triple, or sextuple the visual width and misalign the whole table.

const (
	// EmojiWidthAuto (the zero value) resolves to EmojiWidthGrapheme
	// when termtable detects a terminal known to render composite
	// emoji correctly, and EmojiWidthConservative everywhere else.
	// The TERMTABLE_EMOJI_WIDTH environment variable overrides the
	// detection.
	EmojiWidthAuto EmojiWidthMode = iota

	// EmojiWidthConservative counts every visible codepoint in a
	// grapheme cluster at its standalone width, skipping zero-width
	// composers (ZWJ, variation selectors, combining marks). The
	// result is an upper bound on the rendered width — tables stay
	// aligned even on terminals that don't implement emoji
	// ligatures.
	EmojiWidthConservative

	// EmojiWidthGrapheme uses the Unicode-standard cluster width
	// reported by uniseg. Produces tight layouts on modern
	// terminals; may misalign rows on ones that fall back to
	// rendering composite emoji piecewise.
	EmojiWidthGrapheme
)

func (EmojiWidthMode) String

func (m EmojiWidthMode) String() string
type Footer struct {
	// contains filtered or unexported fields
}

Footer is a footer row. Footers are rendered below body rows.

func (*Footer) AddCell

func (f *Footer) AddCell(opts ...CellOption) *Cell

AddCell constructs a cell from the given options and attaches it to the footer row. See Row.AddCell for the panic / error contract.

func (*Footer) AddCellWithError

func (f *Footer) AddCellWithError(opts ...CellOption) (*Cell, error)

AddCellWithError is the error-returning counterpart to AddCell.

func (*Footer) AttachCell

func (f *Footer) AttachCell(c *Cell) *Cell

AttachCell attaches a previously constructed cell to the footer row.

func (*Footer) AttachCellWithError

func (f *Footer) AttachCellWithError(c *Cell) (*Cell, error)

AttachCellWithError is the error-returning counterpart to AttachCell.

func (*Footer) Cell

func (f *Footer) Cell(i int) *Cell

Cell returns the i-th cell declared in the footer row.

func (*Footer) Cells

func (f *Footer) Cells() []*Cell

Cells returns a snapshot of the footer row's cells.

func (*Footer) ID

func (f *Footer) ID() string

ID returns the footer's user-assigned ID.

type GraphemeRun

type GraphemeRun struct {
	Text      string
	Width     int
	EscPrefix string
}

GraphemeRun carries a single grapheme cluster's text, its display width, and any ANSI escape bytes that immediately preceded it in the source (so a wrap-aware renderer can re-emit them). Cluster text never contains ANSI escapes and never contains '\n' — NaturalLines splits hard breaks before segmentation.

type Header struct {
	// contains filtered or unexported fields
}

Header is a header row. Headers are rendered above body rows and may carry styling or layout differences once styling support lands.

func (*Header) AddCell

func (h *Header) AddCell(opts ...CellOption) *Cell

AddCell constructs a cell from the given options and attaches it to the header row. See Row.AddCell for the panic / error contract.

func (*Header) AddCellWithError

func (h *Header) AddCellWithError(opts ...CellOption) (*Cell, error)

AddCellWithError is the error-returning counterpart to AddCell.

func (*Header) AttachCell

func (h *Header) AttachCell(c *Cell) *Cell

AttachCell attaches a previously constructed cell to the header row.

func (*Header) AttachCellWithError

func (h *Header) AttachCellWithError(c *Cell) (*Cell, error)

AttachCellWithError is the error-returning counterpart to AttachCell.

func (*Header) Cell

func (h *Header) Cell(i int) *Cell

Cell returns the i-th cell declared in the header row.

func (*Header) Cells

func (h *Header) Cells() []*Cell

Cells returns a snapshot of the header row's cells.

func (*Header) ID

func (h *Header) ID() string

ID returns the header's user-assigned ID.

type OverwriteEvent

type OverwriteEvent struct {
	// DroppedID is set when an existing cell was entirely covered by the
	// new cell and removed from its row.
	DroppedID string

	// TruncatedID is set when an existing cell's span was reduced to
	// avoid the new cell. NewColSpan / NewRowSpan describe the resulting
	// span.
	TruncatedID string
	NewColSpan  int
	NewRowSpan  int

	// At is the grid anchor of the overwriting cell.
	At [2]int
}

OverwriteEvent is recorded when WithSpanOverwrite(true) causes a later cell's span to drop or truncate an earlier cell.

func (OverwriteEvent) String

func (e OverwriteEvent) String() string

type Padding

type Padding struct {
	Left, Right, Top, Bottom int
}

Padding controls the empty space reserved inside a cell around its content. Values are measured in terminal columns (left/right) and rows (top/bottom).

func DefaultPadding

func DefaultPadding() Padding

DefaultPadding is the padding applied table-wide when WithTablePadding is not supplied: one column of left/right breathing room, no vertical padding.

type ReaderErrorEvent

type ReaderErrorEvent struct {
	CellID string
	Err    error
}

ReaderErrorEvent is recorded when Measure's lazy reader consumption fails. The affected cell renders as empty; the error is preserved for inspection via Table.Warnings.

func (ReaderErrorEvent) String

func (e ReaderErrorEvent) String() string

type Row

type Row struct {
	// contains filtered or unexported fields
}

Row is a row in the table body.

func (*Row) AddCell

func (r *Row) AddCell(opts ...CellOption) *Cell

AddCell constructs a cell from the given options and attaches it to the row. Under the default table configuration (WithSpanOverwrite(true)) this call cannot fail — span conflicts absorb existing cells and are recorded as OverwriteEvent warnings on Table.Warnings. Under WithSpanOverwrite(false), a conflict panics; use AddCellWithError for explicit error handling instead.

func (*Row) AddCellWithError

func (r *Row) AddCellWithError(opts ...CellOption) (*Cell, error)

AddCellWithError is the error-returning counterpart to AddCell. Useful when the table runs in strict mode (WithSpanOverwrite(false)) and callers want to recover from span conflicts without a panic.

func (*Row) AttachCell

func (r *Row) AttachCell(c *Cell) *Cell

AttachCell attaches a previously constructed cell to the row. Cells already belonging to another row are migrated — their previous attachment is cleaned up before the new one is applied. The panic / WithError contract mirrors AddCell.

func (*Row) AttachCellWithError

func (r *Row) AttachCellWithError(c *Cell) (*Cell, error)

AttachCellWithError is the error-returning counterpart to AttachCell.

func (*Row) Cell

func (r *Row) Cell(i int) *Cell

Cell returns the i-th cell declared in the row (logical coordinate). Returns nil if i is out of range.

func (*Row) Cells

func (r *Row) Cells() []*Cell

Cells returns a snapshot of the cells declared in the row, in declaration order. Spans are not expanded.

func (*Row) ID

func (r *Row) ID() string

ID returns the row's user-assigned ID, or the empty string.

type RowOption

type RowOption func(*rowBody)

RowOption configures a row being added via AddRow, AddHeader, or AddFooter. Internally it operates on the shared rowBody; users never interact with rowBody directly.

func WithCell

func WithCell(c *Cell) RowOption

WithCell queues a previously constructed cell for adoption into the row. Multiple WithCell options may be supplied; they are attached in the order given after the row itself has been inserted.

func WithRowBorder added in v1.1.0

func WithRowBorder(e BorderEdge) RowOption

WithRowBorder sets the border directive for both the top and the bottom edges of a row. Equivalent to the CSS shorthand "border: <e>" on the row's style. See BorderEdge for the semantics.

func WithRowBorderBottom added in v1.1.0

func WithRowBorderBottom(e BorderEdge) RowOption

WithRowBorderBottom sets the border directive for the row's bottom edge. Use this to underline the header row in a borderless table.

func WithRowBorderTop added in v1.1.0

func WithRowBorderTop(e BorderEdge) RowOption

WithRowBorderTop sets the border directive for the row's top edge.

func WithRowID

func WithRowID(id string) RowOption

WithRowID assigns a unique ID to the row being added.

func WithRowStyle

func WithRowStyle(css string) RowOption

WithRowStyle sets a style that applies to every cell in this row unless the cell overrides the corresponding properties. See WithTableStyle for the supported CSS property grammar.

type SpanOverflowEvent

type SpanOverflowEvent struct {
	CellID   string
	Required int
	Got      int
}

SpanOverflowEvent is recorded when a column-span cell cannot fit within the column budget its span covers, even after layout borrow/repay. Rendering continues but the cell overflows its allotted width.

func (SpanOverflowEvent) String

func (e SpanOverflowEvent) String() string

type Style

type Style struct {
	// contains filtered or unexported fields
}

Style collects the visual formatting attributes that can be applied to a Table, row, or Cell. Each field is optional; unset fields are inherited from an enclosing element via style merge. Display-width math is unaffected by styling — attributes are emitted as ANSI escape sequences that contribute zero terminal columns.

When cell content already contains its own ANSI escape sequences and a background is also set via this Style, the content's inner reset sequence will drop the outer background. Use one or the other to avoid artifacts.

type Table

type Table struct {
	// contains filtered or unexported fields
}

Table is the root element. It owns the column list, the header / body / footer row collections, the ID registry, and the occupancy grids used for span tracking.

func NewTable

func NewTable(opts ...TableOption) *Table

NewTable constructs an empty table configured with the given options.

func (*Table) AddFooter

func (t *Table) AddFooter(opts ...RowOption) *Footer

AddFooter appends a new footer row and returns it. See AddHeader for the panic contract.

func (*Table) AddHeader

func (t *Table) AddHeader(opts ...RowOption) *Header

AddHeader appends a new header row and returns it. Under the default table configuration this call cannot fail. If strict mode is enabled via WithSpanOverwrite(false) and a pre-built cell supplied via WithCell produces a span conflict, AddHeader panics — use AttachCellWithError on the returned row for explicit error handling instead.

func (*Table) AddRow

func (t *Table) AddRow(opts ...RowOption) *Row

AddRow appends a new body row and returns it. See AddHeader for the panic contract.

func (*Table) CellAt

func (t *Table) CellAt(r, c int) *Cell

CellAt returns the cell covering absolute grid coordinate (r, c). r is interpreted as: rows [0, len(Headers)) index into headers; rows [len(Headers), len(Headers)+len(Rows)) index into the body; the remainder index into footers. Returns nil if (r, c) is out of bounds or the slot is unoccupied.

func (*Table) Column

func (t *Table) Column(i int) *Column

Column returns the virtual Column element at index i, creating it (and any earlier missing columns) on demand.

func (*Table) Columns

func (t *Table) Columns() []*Column

Columns returns a snapshot of the table's columns in index order.

func (*Table) Footers

func (t *Table) Footers() []*Footer

Footers returns a snapshot of the table's footer rows.

func (*Table) GetElementByID

func (t *Table) GetElementByID(id string) Element

GetElementByID looks up any named element in the table: the table itself, a column, a header/body/footer row, or a cell. Returns nil if no element with that ID is registered.

func (*Table) Headers

func (t *Table) Headers() []*Header

Headers returns a snapshot of the table's header rows.

func (*Table) ID

func (t *Table) ID() string

ID returns the table's user-assigned ID, or the empty string.

func (*Table) InBounds

func (t *Table) InBounds(r, c int) bool

InBounds reports whether (r, c) is a valid grid coordinate within the table's current dimensions.

func (*Table) LastRenderError

func (t *Table) LastRenderError() error

LastRenderError returns the error from the most recent call to String, or nil if the last call succeeded. The value is overwritten on every String call (so calling String a second time after a successful render will clear a previous error). WriteTo callers do not need this accessor — they receive the error directly.

func (*Table) NumColumns

func (t *Table) NumColumns() int

NumColumns returns the number of columns currently present in the table. Columns grow as cells populate new positions.

func (*Table) NumRows

func (t *Table) NumRows() int

NumRows returns the total number of rows across all sections (headers + body + footers).

func (*Table) ResolvedTargetWidth

func (t *Table) ResolvedTargetWidth() int

ResolvedTargetWidth returns the target width the table will use for layout. The resolver follows CSS-style semantics:

  • width (WithTargetWidth / WithTargetWidthPercent / "width: …" in CSS / COLUMNS env) pins the table to an absolute or relative size.
  • min-width (WithMinWidth / "min-width: …") is the floor.
  • max-width (WithMaxWidth / "max-width: …") is the ceiling.
  • When no explicit width is set, the default is min-width:80 and max-width:90 % — the table lives at at least 80 columns and grows to fill up to 90 % of the attached screen as content demands.

Whatever value the cascade produces is clamped to the attached terminal's width when one is detected (stdout or stderr, via golang.org/x/term), so output never exceeds the physical screen.

ResolvedTargetWidth is called without a measurement, so its "natural" baseline is the table's max-width. Layout uses the same resolver internally but with the content-derived natural width, giving the table exactly enough columns to hold its content (clamped to the bounds).

func (*Table) Rows

func (t *Table) Rows() []*Row

Rows returns a snapshot of the table's body rows.

func (*Table) String

func (t *Table) String() string

String renders the table to a string. Layout errors (e.g., a target width too narrow to fit content minimums) do not prevent best-effort output — the renderer falls back to the minimum column widths and produces a possibly-overflowing table. Inspect Table.Warnings() to see non-fatal events collected during rendering; use WriteTo for access to the underlying error.

func (*Table) Warnings

func (t *Table) Warnings() []Warning

Warnings returns the concatenation of authoring-time events (span overwrites, ID reassignments) and the events produced by the most recent render pass (span overflow, cross-section spans, reader errors). Calling String or WriteTo multiple times does not duplicate render-time events — each render overwrites them.

func (*Table) WriteTo

func (t *Table) WriteTo(w io.Writer) (int64, error)

WriteTo renders the table to w. Returns the number of bytes written and either a write error from w or a layout error (e.g., ErrTargetTooNarrow) — write errors take precedence when both occur.

When the attached stdout or stderr is a terminal, every output line is clipped to that terminal's width (with an ellipsis marking the cut). Writes to pipes, files, or other non-interactive sinks pass through unclipped.

type TableOption

type TableOption func(*Table)

TableOption configures a *Table.

func WithBorder

func WithBorder(b BorderSet) TableOption

WithBorder replaces the table's border glyph set. Defaults to DefaultSingleLine.

func WithEmojiWidth

func WithEmojiWidth(mode EmojiWidthMode) TableOption

WithEmojiWidth pins the emoji-width counting mode for the table. The default (EmojiWidthAuto) picks EmojiWidthConservative unless termtable detects a terminal known to render composite emoji correctly, in which case it picks EmojiWidthGrapheme. The TERMTABLE_EMOJI_WIDTH environment variable overrides the detection; an explicit non-auto value here overrides both.

See EmojiWidthMode for the semantics.

func WithMaxWidth

func WithMaxWidth(n int) TableOption

WithMaxWidth sets the CSS-style max-width ceiling for the table. When no explicit WithTargetWidth is supplied, the layout size still never exceeds n columns. Non-positive n is ignored. Mutually exclusive with WithMaxWidthPercent — last setter wins.

func WithMaxWidthPercent

func WithMaxWidthPercent(p int) TableOption

WithMaxWidthPercent sets the max-width ceiling as a percentage of the attached screen width. Mutually exclusive with WithMaxWidth.

func WithMinWidth

func WithMinWidth(n int) TableOption

WithMinWidth sets the CSS-style min-width floor for the table. When no explicit WithTargetWidth is supplied, the layout size still never drops below n columns. Non-positive n is ignored. Mutually exclusive with WithMinWidthPercent — last setter wins.

func WithMinWidthPercent

func WithMinWidthPercent(p int) TableOption

WithMinWidthPercent sets the min-width floor as a percentage of the attached screen width. Mutually exclusive with WithMinWidth.

func WithSpanOverwrite

func WithSpanOverwrite(enable bool) TableOption

WithSpanOverwrite controls span-conflict behavior. The default (true) lets later cells overwrite earlier spans: fully-covered cells are dropped and partially overlapped cells are truncated, with an OverwriteEvent recorded on Table.Warnings for each. This means AddCell and AttachCell never fail for layout reasons under default settings — conflicts are absorbed.

Passing false switches to strict mode: a colliding span is an author error. AddCell / AttachCell panic with a wrapped ErrSpanConflict, and the new cell is not placed. Callers who want explicit error handling instead of panics can use the AddCellWithError / AttachCellWithError methods on the row.

func WithTableID

func WithTableID(id string) TableOption

WithTableID assigns a unique ID to the table itself, retrievable via Table.GetElementByID.

func WithTablePadding

func WithTablePadding(p Padding) TableOption

WithTablePadding overrides the table-wide cell padding. Padding is uniform across every cell — configuring it per-cell would let columns misalign. Default is DefaultPadding() (one column of horizontal padding, no vertical).

func WithTableStyle

func WithTableStyle(css string) TableOption

WithTableStyle sets table-wide style defaults via a CSS-like declaration block, e.g.

WithTableStyle("color: white; background: blue; border-style: double; border-color: cyan")

Supported properties:

  • color, background (background-color), border-color: color values as named colors ("red", "bright-cyan"), hex ("#rrggbb"), or rgb(r,g,b).
  • font-weight: bold | normal
  • font-style: italic | normal
  • text-decoration: underline | line-through | none
  • border-style: single | double | heavy | rounded | ascii — selects the BorderSet used for the table, equivalent to calling WithBorder with the corresponding constructor (SingleLine, DoubleLine, HeavyLine, RoundedLine, ASCIILine).
  • border-style: hidden — uses space glyphs for every boundary, so borders preserve spacing but render invisibly. Equivalent to WithBorder(NoBorder()).
  • border-style: none — sets the table's default edge directive to "no border". Unlike "hidden", boundaries with no cell opting in to a visible border are dropped entirely from output. Combine with per-row or per-cell "border-bottom: solid" etc. to selectively re-enable a single edge.
  • border / border-top / border-right / border-bottom / border-left: per-edge directive accepting "none", "hidden", or "solid". Works on table (default for all rows/cells), row, and cell styles.
  • width: N | N% — target layout width, equivalent to WithTargetWidth / WithTargetWidthPercent. The last width declaration on the table wins.

Unknown properties and unrecognized values are silently ignored.

func WithTargetWidth

func WithTargetWidth(w int) TableOption

WithTargetWidth pins the layout target width to w terminal columns. When unset, the table reads the COLUMNS environment variable, then falls back to the default rule — fill 90% of the attached screen with 80 as the floor. In every case the resolved value is clamped to the attached terminal's width so output never overflows the screen; pipes and other non-interactive sinks leave the value uncapped.

Mutually exclusive with WithTargetWidthPercent — last setter wins.

func WithTargetWidthPercent

func WithTargetWidthPercent(p int) TableOption

WithTargetWidthPercent pins the layout target width to p percent of the terminal width. The percentage base is the attached terminal when one is detected, else the COLUMNS environment variable, else the 80-column default. Non-positive p is ignored.

The resulting width is always clamped to the attached terminal so output never overflows the screen — percentages above 100 therefore behave the same as 100 on a TTY, but can produce genuinely wider tables when writing to a pipe.

Mutually exclusive with WithTargetWidth — last setter wins.

type TrimPosition

type TrimPosition uint8

TrimPosition controls where an ellipsis (or clip) lands when a cell's content needs to be truncated horizontally. It is only consulted when content is actually being truncated — cells that fit leave their content unchanged regardless of this setting.

const (
	// TrimEnd (the default) keeps the content's prefix and places
	// the truncation marker at the right — e.g. "www.exampl…".
	TrimEnd TrimPosition = iota
	// TrimStart keeps the content's suffix and places the marker
	// at the left — e.g. "…/page.html".
	TrimStart
	// TrimMiddle keeps both ends and places the marker between —
	// e.g. "www.exam…/page.html".
	TrimMiddle
)

func (TrimPosition) String

func (p TrimPosition) String() string

type VerticalAlignment

type VerticalAlignment uint8

VerticalAlignment controls where a cell's wrapped content sits within a row (or rowspan block) that is taller than the content itself — a common situation when one cell wraps to multiple lines and its neighbours have only one.

const (
	// VAlignTop is the default: content hugs the top of the cell,
	// any extra vertical space sits below.
	VAlignTop VerticalAlignment = iota
	// VAlignMiddle distributes extra space evenly above and below
	// the content; any odd remainder goes to the bottom.
	VAlignMiddle
	// VAlignBottom pushes content to the bottom; any extra vertical
	// space sits above.
	VAlignBottom
)

func (VerticalAlignment) String

func (v VerticalAlignment) String() string

type Warning

type Warning interface {
	String() string
	// contains filtered or unexported methods
}

Warning is implemented by non-fatal events surfaced during table construction or rendering. Retrieve them via Table.Warnings.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL