tabwrap

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Mar 8, 2026 License: MIT Imports: 2 Imported by: 0

README

go-tabwrap

Tab-aware, grapheme-cluster-aware display width utilities for Go.

Provides StringWidth, ExpandTab, Wrap, Truncate, FillLeft, and FillRight — the common building blocks for CLI table renderers and TUI applications.

Features

  • Grapheme-cluster-aware — emoji sequences and combining characters are measured correctly (via [displaywidth]).
  • Tab-stop expansion — every operation handles \t as an elastic tab stop, not a single character.
  • Line wrappingWrap breaks text to fit a column width. Tabs are indivisible: if a tab does not fit, it moves to the next line.
  • ANSI escape sequence aware — optional ControlSequences mode treats SGR and other 7-bit escape sequences as zero-width, allowing correct measurement of styled terminal output. Wrap carries SGR state across line breaks, so each output line is independently styled.
  • East Asian Width — optional treatment of ambiguous characters as double-width.

Install

go get github.com/apstndb/go-tabwrap

Usage

package main

import (
	"fmt"

	"github.com/apstndb/go-tabwrap"
)

func main() {
	// Package-level functions use default settings (TabWidth = 4).
	fmt.Println(tabwrap.StringWidth("hello"))        // 5
	fmt.Println(tabwrap.StringWidth("a\tb"))          // 5 (tab expands to 3 spaces)
	fmt.Println(tabwrap.StringWidth("日本語"))        // 6

	fmt.Println(tabwrap.Truncate("hello world", 8, "...")) // "hello..."
	fmt.Println(tabwrap.FillLeft("42", 5))                  // "   42"
	fmt.Println(tabwrap.FillRight("hi", 5))                 // "hi   "

	// Use Condition for custom tab width or East Asian Width.
	c := &tabwrap.Condition{TabWidth: 8}
	fmt.Println(c.StringWidth("\t"))            // 8
	fmt.Println(c.Wrap("hello world", 5))       // "hello\n world"
	fmt.Println(c.ExpandTab("a\tb"))            // "a       b"

	// ANSI escape sequences: measure visible width only.
	ansi := &tabwrap.Condition{TabWidth: 4, ControlSequences: true}
	styled := "\x1b[31mhello\x1b[0m"
	fmt.Println(ansi.StringWidth(styled))       // 5 (escape sequences ignored)

	// Wrap carries SGR state across line breaks.
	wrapped := ansi.Wrap("\x1b[31mhelloworld\x1b[0m", 5)
	// Result: "\x1b[31mhello\x1b[0m\n\x1b[31mworld\x1b[0m"
	// Each line is independently styled.
	fmt.Println(wrapped)
}

API

Package-level (default: TabWidth = 4)
Function Description
StringWidth(s) int Display width of s (tab & newline aware)
Truncate(s, maxWidth, tail) string Truncate s, append tail if truncated
FillLeft(s, width) string Left-pad with spaces
FillRight(s, width) string Right-pad with spaces
Condition

Condition provides all the above functions as methods, with configurable fields:

Field Default Description
TabWidth 4 Columns per tab stop
EastAsianWidth false Treat ambiguous EA chars as width 2
ControlSequences false Treat 7-bit ANSI escapes as zero-width

Additional methods:

Method Description
ExpandTab(s) string Replace tabs with spaces
Wrap(s, width) string Wrap to width columns (tabs expanded)

Acknowledgements

This package stands on the shoulders of:

  • mattn/go-runewidth — the long-standing standard for terminal string width in Go. go-tabwrap provides a similar API shape while adding tab-awareness and grapheme-cluster support.
  • clipperhouse/displaywidth — the underlying grapheme-cluster-aware width engine that powers go-tabwrap. go-tabwrap adds tab-stop handling, wrapping, truncation, and padding on top.

License

MIT

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.

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func FillLeft

func FillLeft(s string, width int) string

FillLeft pads s on the left using default settings.

func FillRight

func FillRight(s string, width int) string

FillRight pads s on the right using default settings.

func StringWidth

func StringWidth(s string) int

StringWidth returns the display width of s using default settings.

func Truncate

func Truncate(s string, maxWidth int, tail string) string

Truncate truncates s using default settings.

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
}

Condition configures display width behaviour.

func NewCondition

func NewCondition() *Condition

NewCondition returns a Condition with default settings (TabWidth = 4).

func (*Condition) ExpandTab

func (c *Condition) ExpandTab(s string) string

ExpandTab replaces every tab with spaces according to tab stops. Columns reset at each newline.

func (*Condition) FillLeft

func (c *Condition) FillLeft(s string, width int) string

FillLeft pads s on the left with spaces to reach width display columns. If s is already at least width columns wide it is returned unchanged.

func (*Condition) FillRight

func (c *Condition) FillRight(s string, width int) string

FillRight pads s on the right with spaces to reach width display columns. If s is already at least width columns wide it is returned unchanged.

func (*Condition) StringWidth

func (c *Condition) StringWidth(s string) int

StringWidth returns the display width of s, handling tabs and newlines. For multi-line strings it returns the width of the widest line.

func (*Condition) Truncate

func (c *Condition) Truncate(s string, maxWidth int, tail string) string

Truncate truncates s to fit within maxWidth display columns, appending tail if truncation occurs. Tabs are expanded before measuring.

func (*Condition) Wrap

func (c *Condition) Wrap(s string, width int) string

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 ControlSequences is true, SGR (Select Graphic Rendition) state is carried across line breaks: a reset is emitted before each newline and the active SGR sequences are replayed after it. This ensures each output line is independently styled.

Jump to

Keyboard shortcuts

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