Documentation
¶
Overview ¶
Package tabwrap provides tab-aware, grapheme-cluster-aware display width operations for terminal/fixed-width output.
It wraps clipperhouse/displaywidth to add tab-stop handling, line wrapping, truncation, and padding — the common building blocks for CLI table renderers and TUI applications.
Width is measured in terminal display columns, by grapheme cluster rather than rune. Tabs expand to tab stops, newlines reset the column, and the width of a multi-line string is the width of its widest line. The handling of East Asian ambiguous width and ECMA-48 control sequences follows the active Condition options.
Key differences from mattn/go-runewidth:
- Grapheme-cluster-aware (emoji, combining characters) via displaywidth.
- Built-in tab-stop expansion in every operation.
Key additions over clipperhouse/displaywidth:
- Tab-aware StringWidth, ExpandTab, Wrap, Truncate, FillLeft, FillRight.
Index ¶
- func ExpandTab(s string) string
- func ExpandTabFunc(s string, fn func(nSpaces int) string) string
- func FillLeft(s string, width int) string
- func FillRight(s string, width int) string
- func StringWidth(s string) int
- func Truncate(s string, maxWidth int, tail string) string
- func Wrap(s string, width int) string
- type Condition
- func (c *Condition) ExpandTab(s string) string
- func (c *Condition) ExpandTabFunc(s string, fn func(nSpaces int) string) string
- func (c *Condition) FillLeft(s string, width int) string
- func (c *Condition) FillRight(s string, width int) string
- func (c *Condition) StringWidth(s string) int
- func (c *Condition) Truncate(s string, maxWidth int, tail string) string
- func (c *Condition) Wrap(s string, width int) string
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ExpandTabFunc ¶ added in v0.1.3
ExpandTabFunc replaces every tab using a custom callback with default settings.
ExpandTabFunc panics if fn is nil and s contains a tab, because fn is only called when a tab is encountered.
func StringWidth ¶
StringWidth returns the display width of s using default settings. See Condition.StringWidth for the width model.
Types ¶
type Condition ¶
type Condition struct {
// TabWidth is the number of columns per tab stop. Zero or negative defaults to 4.
TabWidth int
// EastAsianWidth treats ambiguous East Asian characters as width 2 when true.
EastAsianWidth bool
// ControlSequences treats 7-bit ANSI escape sequences (CSI, OSC, etc.)
// as zero-width when true. This allows correct width measurement of
// strings containing terminal color codes and other SGR sequences.
ControlSequences bool
// ControlSequences8Bit treats 8-bit C1 ECMA-48 escape sequences as zero-width
// when true. It can be enabled independently of ControlSequences; enabling
// both covers both the 7-bit and 8-bit forms. Truncate follows displaywidth
// and ignores this option.
ControlSequences8Bit bool
// TrimTrailingSpace removes trailing spaces and tabs from each output line
// produced by Wrap when true. This applies after wrapping, while preserving
// trailing zero-width graphemes on the line (for example, ANSI control
// sequences when ControlSequences or ControlSequences8Bit are enabled).
TrimTrailingSpace bool
}
Condition configures display width behaviour.
func NewCondition ¶
func NewCondition() *Condition
NewCondition returns a Condition with default settings (TabWidth = 4).
func (*Condition) ExpandTab ¶
ExpandTab replaces every tab with spaces according to tab stops. Columns reset at each newline.
func (*Condition) ExpandTabFunc ¶ added in v0.1.2
ExpandTabFunc replaces every tab by calling fn with the number of spaces the tab would normally expand to (based on the current column and tab width). The column advances by nSpaces regardless of what fn returns, so the caller is responsible for returning a string whose display width equals nSpaces if alignment matters. Columns reset at each newline.
ExpandTabFunc panics if fn is nil and s contains a tab, because fn is only called when a tab is encountered.
func (*Condition) FillLeft ¶
FillLeft pads s on the left with spaces to reach width display columns. For multi-line strings, padding is added only at the start of the full string, so only the first line changes. If another line is already at least width columns wide, s is returned unchanged. Width is measured using the same rules as Condition.StringWidth. When left padding is needed, tabs in the first line are expanded first so the added spaces do not shift later tab stops there.
func (*Condition) FillRight ¶
FillRight pads s on the right with spaces to reach width display columns. For multi-line strings, padding is computed from the widest line but is added only at the end of the full string, so only the last line changes. Width is measured using the same rules as Condition.StringWidth. If s is already at least width columns wide it is returned unchanged.
func (*Condition) StringWidth ¶
StringWidth returns the display width of s in terminal columns.
Width is measured by grapheme cluster, not rune. Tabs expand to tab stops, newlines reset the column, and for multi-line strings the result is the width of the widest line. EastAsianWidth, ControlSequences, and ControlSequences8Bit affect how individual graphemes are counted.
func (*Condition) Truncate ¶
Truncate truncates s to fit within positive maxWidth display columns, appending tail if truncation occurs. Tabs are expanded before measuring. If tail itself is too wide to fit, it is truncated first so the result still fits maxWidth. When maxWidth <= 0, tail is returned as-is.
ControlSequences8Bit follows displaywidth and is ignored here, even when it is enabled for StringWidth and Wrap. This can make 8-bit C1 sequences count as zero-width for measurement but not for truncation; go-tabwrap keeps that behavior to avoid mis-parsing UTF-8 byte sequences as standalone C1 controls.
func (*Condition) Wrap ¶
Wrap wraps s to fit within width display columns.
Tabs are indivisible tokens: if a tab does not fit on the current line the entire tab moves to the next line. Tabs in the output are expanded to spaces so the result is render-ready.
Existing newlines are preserved. When width <= 0 the string is returned with tabs expanded but no wrapping applied.
When control-sequence handling is enabled, Wrap carries across line breaks only those SGR (Select Graphic Rendition) sequences that are recognized as zero-width under the active options: 7-bit sequences when ControlSequences is true, and 8-bit sequences when ControlSequences8Bit is true. For those sequences, a reset is emitted before each newline and the active SGR sequences are replayed after it so each output line remains independently styled.