text

package
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Sep 4, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package text is repo-owned CLI text machinery: sanitization, terminal cell width measurement, truncation, glyph-safe pluralization/conjugation. Not a general-purpose text library — every rule here exists for evident-output's own rendering paths.

Sanitization (this file): neutralizes untrusted text for terminal-safe display.

Width: terminal cell width measurement for display text.

Index

Constants

View Source
const (
	SGRReset  = "\x1b[0m"
	SGRBold   = "\x1b[1m"
	SGRDim    = "\x1b[2m"
	SGRRed    = "\x1b[31m"
	SGRGreen  = "\x1b[32m"
	SGRYellow = "\x1b[33m"
	SGRCyan   = "\x1b[36m"
	SGRBlue   = "\x1b[34m"
)

SGR styles for terminal projection (library-owned sequences only).

View Source
const DefaultVisibleNames = 3

DefaultVisibleNames is how many names TruncateNames keeps before summarizing.

View Source
const SpinnerPeriod = 80 * time.Millisecond

SpinnerPeriod is the wall-clock duration between spinner frame advances.

Variables

View Source
var (
	GlyphDone         = glyphSpec{"✓", "[ok]"}
	GlyphFailedState  = glyphSpec{"✗", "[x]"}
	GlyphBlockedState = glyphSpec{"⊘", "[blocked]"}
	GlyphWarningState = glyphSpec{"!", "[!]"}
	GlyphCancelled    = glyphSpec{"■", "[cancel]"}
	GlyphNotStarted   = glyphSpec{"-", "[-]"}
	GlyphPending      = glyphSpec{"○", "[.]"}
	GlyphRunning      = glyphSpec{"◐", "[~]"}
	GlyphHumanInput   = glyphSpec{"?", "[?]"}
	// GlyphNextAction marks a follow-up command/label line. evo-rec.md's
	// tightened vocabulary table gives it its own row so the meaning does not
	// depend on the cyan color alone (rule: text/glyph carries meaning).
	GlyphNextAction = glyphSpec{"→", ">"}
	// GlyphEvidence is the tree connector for Detail/Cause rows under a
	// Problem — dim per "Color and style demotions" (subordinate evidence).
	GlyphEvidence = glyphSpec{"└─", "-"}
	// GlyphOverflow marks a truncated/omitted-count line ("… +N more").
	GlyphOverflow = glyphSpec{"…", "..."}
	// GlyphUnclassified covers states with no distinct row in the vocabulary
	// table (e.g. Empty); it must stay visually distinct from Pending's "○".
	GlyphUnclassified = glyphSpec{"·", "."}
)

State glyph table (evo-rec.md "Tightened glyph vocabulary"). A live interactive redraw drives Running from the spinner alphabet instead (writeLiveTaskLine overrides TaskGlyph's Running result with the current animated frame); every other consumer — a plain/non-interactive durable line, a residual dump, a snapshot — has no animation loop behind it, so TaskGlyph gives Running its own static face (GlyphRunning) rather than a spinner frame frozen mid-spin (beginner-8: "no spinner glyph in plain").

Functions

func Block

func Block(s string) string

Block neutralizes control characters while preserving newlines for multi-line presentation fields (Problem.Detail, capture tails). CRLF/CR normalize to LF. ESC/CSI and other C0 controls (except TAB and LF) are still stripped.

func Cells

func Cells(s string) int

Cells returns terminal cell width for s using a conservative East-Asian / emoji heuristic suitable for CLI layout (TXT-001…006).

func ConjugatePast

func ConjugatePast(verb string) string

ConjugatePast returns the past-tense spelling of an imperative mutation verb (delete -> deleted, push -> pushed, write -> wrote) so callers write one imperative verb and evo picks the tense for [changed] rows; [planned] rows keep the imperative verb as written. Hyphenated compound verbs (delete-remote, fetch-prune) fall back to conjugating their first segment and dropping the rest, unless compoundPastTense names a specific spelling — see that table's comment for why the general rule doesn't always apply.

func Dim

func Dim(s string, color bool) string

Dim applies the dim SGR style.

func PadLeft

func PadLeft(s string, n int) string

PadLeft left-pads s with spaces to width n.

func PadRight

func PadRight(s string, n int) string

PadRight right-pads s with spaces to width n.

func Pluralize

func Pluralize(quantity int64, singular string) string

Pluralize returns the plural spelling of singular when quantity != 1 (an irregular table for the common exceptions, else the regular English +s/+es/+ies rule), and singular unchanged when quantity == 1 — the object-pluralization counterpart to ConjugatePast's verb table, so a mutation call site stops writing its own singular/plural noun() switch.

A glob/path/symbol object ("stale origin/*", "*.tmp") renders unchanged at any quantity — see isPluralizableWord — instead of blindly gaining a trailing "s" ("stale origin/*s") that the +s/+es/+ies rule was never designed to produce.

func RuneCells

func RuneCells(r rune) int

RuneCells returns the display width of a single rune.

func SpinnerFrames

func SpinnerFrames(profile GlyphProfile) []string

SpinnerFrames returns the animation alphabet for profile.

func SpinnerGlyph

func SpinnerGlyph(now time.Time, profile GlyphProfile) string

SpinnerGlyph picks a frame from the clock so a fixed clock freezes it in tests.

func StripANSI

func StripANSI(s string) string

StripANSI removes CSI, OSC, and other common terminal control sequences.

func Style

func Style(s, code string, color bool) string

Style wraps s in code (an SGR escape) when color is true and both s and code are non-empty, closing with SGRReset.

func StyleGlyph

func StyleGlyph(glyph, code string, color bool) string

StyleGlyph applies Style to a rendered glyph face.

func Text

func Text(s string) string

Text neutralizes control characters and invalid UTF-8 for single-line fields. Newlines become spaces. ESC/CSI/OSC and C0 controls other than TAB are stripped or replaced. Prefer Block for multi-line evidence (diffs, capture tails).

func Truncate

func Truncate(s string, maxCells int) string

Truncate trims s to at most maxCells, never splitting a multi-cell rune, appending "…" when truncated. Combining sequences stay attached to base.

func TruncateNames

func TruncateNames(names []string, visible int, profile ...GlyphProfile) string

TruncateNames joins names for a skip/kept-style summary. Empty names yields "". visible <= 0 uses DefaultVisibleNames. When more names remain than visible, appends the overflow glyph for profile (evo-rec.md's tightened vocabulary: "… +N more", ASCII "... +N more") instead of a bare ", +N" that carries no glyph at all. profile is variadic so the simplest call — TruncateNames(names, visible) — stays correct: an omitted profile renders the Unicode overflow glyph; a caller that has already resolved a GlyphProfile passes it explicitly.

func TruncateUTF8

func TruncateUTF8(s string, max int, suffix string) string

TruncateUTF8 trims s to at most max bytes without splitting a multi-byte rune, then appends suffix when truncation occurs. max is a byte budget for s before suffix.

func TruncateVisible

func TruncateVisible(s string, maxCells int) string

TruncateVisible trims styled terminal text to maxCells while retaining complete control sequences and closing any active presentation state.

func VisibleCells

func VisibleCells(s string) int

VisibleCells returns cell width after stripping ANSI CSI/OSC sequences so styled and unstyled visible widths match (TXT-013) and OSC 8 links count as zero cells (TXT-014).

Types

type GlyphProfile

type GlyphProfile int

GlyphProfile selects which glyph vocabulary state markers render in (evo-rec.md "Tightened glyph vocabulary", rule GLYPH-001: glyph selection via capability profile, cell-width measurement not rune counts).

The zero value, GlyphsAuto, keeps today's Unicode vocabulary off a TTY (a non-interactive stream can't show a human mojibake, so there is nothing to guard against) and on any TTY whose locale already advertises UTF-8. It downgrades to the ASCII vocabulary only on an interactive terminal without UTF-8 locale support — the one case where the status column would otherwise render as mojibake.

const (
	// GlyphsAuto detects the vocabulary from locale and TTY interactivity.
	GlyphsAuto GlyphProfile = iota
	// GlyphsUnicode forces the Unicode vocabulary regardless of locale.
	GlyphsUnicode
	// GlyphsASCII forces the ASCII vocabulary regardless of locale.
	GlyphsASCII
)

Jump to

Keyboard shortcuts

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