or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdmain-package.mdrenderer-package.mdtw-package.mdtwcache-package.mdtwwarp-package.mdtwwidth-package.md
tile.json

twwidth-package.mddocs/

twwidth Package

The twwidth package provides display width calculation for strings, with automatic handling of ANSI escape sequences and East Asian character widths. It includes a thread-safe LRU cache for high-performance width calculations and text truncation with ANSI preservation.

Package Information

  • Package Name: github.com/olekukonko/tablewriter/pkg/twwidth
  • Package Type: Go module
  • Language: Go
  • Installation: go get github.com/olekukonko/tablewriter/pkg/twwidth

Core Imports

import "github.com/olekukonko/tablewriter/pkg/twwidth"

Basic Usage

import "github.com/olekukonko/tablewriter/pkg/twwidth"

// Calculate display width of plain text
width := twwidth.Width("Hello")  // Returns: 5

// Width calculation automatically strips ANSI codes
colorized := "\x1b[31mHello\x1b[0m"
width = twwidth.Width(colorized)  // Returns: 5 (color codes ignored)

// Truncate string to fit within display width
truncated := twwidth.Truncate("Hello World", 8, "...")  // Returns: "Hello..."

// Enable East Asian width handling (treats ambiguous characters as wide)
twwidth.SetEastAsian(true)

// Get cache statistics
size, capacity, hitRate := twwidth.GetCacheStats()

Key Features

  • ANSI Escape Sequence Handling: Automatically filters out ANSI color codes and control sequences when calculating display width
  • East Asian Width Support: Configurable handling of East Asian ambiguous-width characters
  • Thread-Safe Caching: LRU cache with configurable capacity for high-performance repeated calculations
  • Smart Truncation: Truncate strings to display width while preserving ANSI codes and adding reset sequences
  • Display Width vs Byte Length: Correctly handles multi-byte Unicode characters and double-width East Asian characters

Capabilities

Width Calculation

Calculate the visual display width of a string, automatically stripping ANSI escape sequences.

func Width(str string) int

Uses the global cache and East Asian width settings. Thread-safe.

Parameters:

  • str - Input string (may contain ANSI escape sequences)

Returns:

  • Visual display width in columns (ANSI sequences excluded)

Example:

// Plain text
width := twwidth.Width("Hello")  // Returns: 5

// Text with ANSI color codes
colored := "\x1b[31mRed Text\x1b[0m"
width = twwidth.Width(colored)  // Returns: 8 (color codes stripped)

// East Asian characters (with default settings)
width = twwidth.Width("你好")  // Returns: 4 (2 wide characters)

Calculate width with specific options, bypassing global settings and cache.

func WidthWithOptions(str string, opts Options) int

Parameters:

  • str - Input string
  • opts - Width calculation options (including EastAsianWidth setting)

Returns:

  • Visual display width in columns

Example:

// Calculate width with East Asian handling enabled
opts := twwidth.Options{EastAsianWidth: true}
width := twwidth.WidthWithOptions("Hello 你好", opts)

// Calculate width with East Asian handling disabled
opts = twwidth.Options{EastAsianWidth: false}
width = twwidth.WidthWithOptions("Hello 你好", opts)

Calculate width without using the cache (uses current global settings).

func WidthNoCache(str string) int

Parameters:

  • str - Input string

Returns:

  • Visual display width in columns

Example:

// Bypass cache for one-off calculation
width := twwidth.WidthNoCache("Temporary text")

Deprecated: Calculate width using a runewidth.Condition (backward compatibility).

func Display(cond *runewidth.Condition, str string) int

Use WidthWithOptions instead with the new Options struct.

String Truncation

Truncate a string to fit within a specified visual display width.

func Truncate(s string, maxWidth int, suffix ...string) string

Preserves ANSI escape sequences and adds reset sequences (\x1b[0m) when needed to prevent formatting bleed. Respects global East Asian width settings. Thread-safe.

Parameters:

  • s - Input string (may contain ANSI codes)
  • maxWidth - Maximum visual display width
  • suffix - Optional suffix to append (e.g., "..." for ellipsis). Multiple suffixes are joined

Returns:

  • Truncated string fitting within maxWidth, including any suffix

Behavior:

  • If maxWidth < 0: Returns empty string
  • If maxWidth == 0 and suffix provided: Returns suffix if it fits
  • If string fits within maxWidth: Returns original string (with ANSI reset if needed)
  • If truncation needed: Truncates content, adds ANSI reset if needed, appends suffix

Example:

// Basic truncation
result := twwidth.Truncate("Hello World", 8)  // Returns: "Hello Wo"

// Truncation with suffix
result = twwidth.Truncate("Hello World", 8, "...")  // Returns: "Hello..."

// Truncation with ANSI codes preserved
colored := "\x1b[31mRed Text Here\x1b[0m"
result = twwidth.Truncate(colored, 8, "...")
// Returns: "\x1b[31mRed Text\x1b[0m..." (preserves color, adds reset)

// Custom suffix
result = twwidth.Truncate("Long text here", 10, " [more]")
// Returns: "Long [more]"

// Empty result when maxWidth is negative
result = twwidth.Truncate("Hello", -1)  // Returns: ""

// String shorter than maxWidth
result = twwidth.Truncate("Hello", 10)  // Returns: "Hello" (or "Hello\x1b[0m" if ANSI present)

Global Configuration

Configure global East Asian width handling and options.

func SetOptions(opts Options)

Sets global width calculation options. Changes to EastAsianWidth purge the cache. Thread-safe.

Parameters:

  • opts - Global options to apply

Example:

// Enable East Asian width handling globally
opts := twwidth.Options{EastAsianWidth: true}
twwidth.SetOptions(opts)

// Disable East Asian width handling
opts = twwidth.Options{EastAsianWidth: false}
twwidth.SetOptions(opts)

Enable or disable East Asian width handling globally.

func SetEastAsian(enable bool)

Thread-safe. Purges cache when setting changes.

Parameters:

  • enable - true to enable East Asian width (treat ambiguous characters as wide), false to disable

Example:

// Enable East Asian width handling
twwidth.SetEastAsian(true)

// Characters like "│" are now treated as wide
width := twwidth.Width("│")  // Returns: 2 (with EastAsian enabled)

// Disable East Asian width handling
twwidth.SetEastAsian(false)
width = twwidth.Width("│")  // Returns: 1 (with EastAsian disabled)

Get current East Asian width setting.

func IsEastAsian() bool

Thread-safe.

Returns:

  • true if East Asian width handling is enabled, false otherwise

Example:

if twwidth.IsEastAsian() {
    fmt.Println("East Asian width handling is enabled")
} else {
    fmt.Println("East Asian width handling is disabled")
}

Deprecated: Set runewidth condition for backward compatibility.

func SetCondition(cond *runewidth.Condition)

Use SetOptions with the new Options struct instead.

Cache Management

Manage the internal LRU cache for width calculations.

func SetCacheCapacity(capacity int)

Changes the cache size dynamically. Thread-safe.

Parameters:

  • capacity - New cache capacity. If capacity <= 0, caching is disabled entirely

Example:

// Increase cache size
twwidth.SetCacheCapacity(16384)

// Disable caching
twwidth.SetCacheCapacity(0)

// Re-enable with smaller cache
twwidth.SetCacheCapacity(4096)

Get current cache statistics.

func GetCacheStats() (size, capacity int, hitRate float64)

Thread-safe.

Returns:

  • size - Current number of entries in cache
  • capacity - Maximum cache capacity
  • hitRate - Cache hit rate (0.0 to 1.0), where 1.0 means 100% cache hits

Example:

size, capacity, hitRate := twwidth.GetCacheStats()
fmt.Printf("Cache: %d/%d entries, %.2f%% hit rate\n",
    size, capacity, hitRate*100)

// Example output: "Cache: 1234/8192 entries, 87.50% hit rate"

ANSI Escape Sequence Filtering

Get a compiled regular expression for matching ANSI escape sequences.

func Filter() *regexp.Regexp

Returns a regex that matches both CSI (Control Sequence Introducer) and OSC (Operating System Command) ANSI sequences.

Returns:

  • Compiled regular expression for ANSI escape sequence matching

Example:

ansi := twwidth.Filter()

// Strip ANSI codes from a string
text := "\x1b[31mRed\x1b[0m \x1b[32mGreen\x1b[0m"
clean := ansi.ReplaceAllLiteralString(text, "")
fmt.Println(clean)  // Output: "Red Green"

// Check if string contains ANSI codes
hasANSI := ansi.MatchString(text)
fmt.Println(hasANSI)  // Output: true

Types

// Options configures width calculation on a per-call basis
type Options struct {
    // EastAsianWidth enables East Asian ambiguous character width handling.
    // When true, ambiguous-width characters (like box drawing characters)
    // are treated as wide (width=2) instead of narrow (width=1).
    EastAsianWidth bool
}

Understanding Display Width

ANSI Escape Sequences

ANSI escape sequences are special character sequences used for terminal formatting (colors, cursor movement, etc.). They are not visible when rendered but occupy bytes in the string. The twwidth package automatically detects and excludes these sequences when calculating display width.

ANSI Sequence Types Handled:

  • CSI (Control Sequence Introducer): \x1b[ followed by parameters and a command byte (e.g., \x1b[31m for red text)
  • OSC (Operating System Command): \x1b] followed by content and terminated by \x1b\ or \x07 (e.g., \x1b]0;Title\x07 for window title)

Example:

// String with ANSI color codes
colored := "\x1b[31mHello\x1b[0m World"

// Byte length includes ANSI codes
fmt.Println(len(colored))  // Output: 21 bytes

// Display width excludes ANSI codes
width := twwidth.Width(colored)
fmt.Println(width)  // Output: 11 (just "Hello World")

East Asian Width

The Unicode standard defines some characters as "East Asian Ambiguous Width" (like box-drawing characters , , etc.). In East Asian locales, these are typically displayed as wide (2 columns), while in other locales they are narrow (1 column).

When to Enable:

  • Enable EastAsianWidth when rendering for East Asian terminals or when working with East Asian text
  • Disable for Western/Latin text or when you want consistent narrow handling

Example:

// Box drawing character with different settings
boxChar := "│"

// With East Asian width disabled (default)
twwidth.SetEastAsian(false)
fmt.Println(twwidth.Width(boxChar))  // Output: 1

// With East Asian width enabled
twwidth.SetEastAsian(true)
fmt.Println(twwidth.Width(boxChar))  // Output: 2

Display Width vs Byte Length

Display width differs from byte length because:

  1. Multi-byte characters: UTF-8 characters can use 1-4 bytes
  2. Wide characters: Some characters (like CJK) occupy 2 display columns
  3. Zero-width characters: ANSI escape sequences, combining marks, etc.

Example:

// ASCII: 1 byte = 1 display column
ascii := "A"
fmt.Println(len(ascii))          // 1 byte
fmt.Println(twwidth.Width(ascii)) // 1 column

// CJK: 3 bytes = 2 display columns
cjk := "你"
fmt.Println(len(cjk))          // 3 bytes (UTF-8)
fmt.Println(twwidth.Width(cjk)) // 2 columns (wide character)

// ANSI codes: N bytes = 0 display columns
ansi := "\x1b[31m"
fmt.Println(len(ansi))          // 5 bytes
fmt.Println(twwidth.Width(ansi)) // 0 columns (control sequence)

Caching Mechanism

The package uses a thread-safe LRU (Least Recently Used) cache to optimize repeated width calculations. The cache is particularly beneficial when:

  • Rendering tables with repeated cell content
  • Processing large datasets with duplicate strings
  • Calculating widths for the same strings multiple times

Cache Behavior:

  • Default capacity: 8192 entries
  • Cache keys: Strings are cached with their East Asian width setting ("0:" prefix for false, "1:" prefix for true)
  • Cache invalidation: Changing EastAsianWidth setting purges the cache
  • Thread-safety: All cache operations are protected by mutex locks
  • Eviction: Least recently used entries are evicted when capacity is reached

Functions Using Cache:

  • Width() - Uses cache with global settings
  • Truncate() - Uses cache internally for width calculations

Functions Bypassing Cache:

  • WidthWithOptions() - Direct calculation without cache
  • WidthNoCache() - Direct calculation with global settings

Example:

// Initial calculation (cache miss)
width1 := twwidth.Width("Hello")

// Subsequent calculation (cache hit)
width2 := twwidth.Width("Hello")

// Check cache statistics
size, capacity, hitRate := twwidth.GetCacheStats()
fmt.Printf("Hit rate: %.2f%%\n", hitRate*100)

// Disable caching for memory-constrained environments
twwidth.SetCacheCapacity(0)

// Re-enable with custom capacity
twwidth.SetCacheCapacity(4096)

Thread Safety

All exported functions in the twwidth package are thread-safe:

  • Global state access: Protected by mutex locks
  • Cache operations: Thread-safe LRU implementation
  • Concurrent calls: Multiple goroutines can safely call package functions simultaneously

Example:

import (
    "sync"
    "github.com/olekukonko/tablewriter/pkg/twwidth"
)

// Safe to use from multiple goroutines
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(text string) {
        defer wg.Done()
        width := twwidth.Width(text)
        fmt.Println(width)
    }(fmt.Sprintf("Text %d", i))
}
wg.Wait()

// Safe to change settings concurrently
go twwidth.SetEastAsian(true)
go twwidth.SetCacheCapacity(16384)

Common Use Cases

Table Rendering

Calculate column widths for aligned table output:

headers := []string{"Name", "Age", "City"}
rows := [][]string{
    {"Alice", "30", "New York"},
    {"Bob", "25", "Los Angeles"},
    {"Charlie", "35", "Chicago"},
}

// Calculate maximum width for each column
colWidths := make([]int, len(headers))
for i, header := range headers {
    colWidths[i] = twwidth.Width(header)
}
for _, row := range rows {
    for i, cell := range row {
        width := twwidth.Width(cell)
        if width > colWidths[i] {
            colWidths[i] = width
        }
    }
}

// Use colWidths for formatting

Terminal Output with Colors

Handle colored text while maintaining alignment:

// Colored strings with different visual widths
items := []string{
    "\x1b[31mError:\x1b[0m Connection failed",
    "\x1b[33mWarning:\x1b[0m Low memory",
    "\x1b[32mSuccess:\x1b[0m Complete",
}

// Truncate to consistent display width
maxWidth := 30
for _, item := range items {
    truncated := twwidth.Truncate(item, maxWidth, "...")
    fmt.Println(truncated)
}

Unicode Text Processing

Handle mixed ASCII and Unicode text:

// Mixed content
texts := []string{
    "Hello World",           // ASCII
    "你好世界",                // Chinese (wide characters)
    "Hello 世界",            // Mixed
    "\x1b[31m你好\x1b[0m",   // Colored Chinese
}

// Calculate widths correctly for all cases
for _, text := range texts {
    width := twwidth.Width(text)
    fmt.Printf("Text: %s, Width: %d\n", text, width)
}

Text Truncation with Ellipsis

Truncate long text to fit in limited space:

// Truncate file paths
longPath := "/very/long/path/to/some/file/with/a/long/name.txt"
shortPath := twwidth.Truncate(longPath, 30, "...")

// Truncate descriptions
desc := "This is a very long description that needs to be shortened"
shortDesc := twwidth.Truncate(desc, 40, "…")

// Truncate with custom suffix
code := "function veryLongFunctionName(param1, param2, param3)"
shortCode := twwidth.Truncate(code, 35, "...)")