Termisu::Cell
Inherits Struct / Value / Object
Cell represents a single character cell in the terminal buffer.
Cell contains:
- grapheme: The Unicode grapheme cluster (single grapheme for leading cells)
- width: Display column width (0, 1, or 2)
- continuation: True for trailing cell of a wide grapheme
- fg: Foreground color (supports ANSI-8, ANSI-256, and RGB)
- bg: Background color (supports ANSI-8, ANSI-256, and RGB)
- attr: Text attributes (bold, underline, etc.)
Grapheme and Continuation Cells
Wide characters (CJK, emoji) occupy 2 columns. The Cell model represents this:
- Leading cell:
continuation = false,width = 2,graphemecontains the full grapheme - Trailing cell:
continuation = true,width = 0,graphemeis empty
Example:
# Leading cell for "中" (width auto-calculated as 2)
lead = Termisu::Cell.new("中")
lead.grapheme # => "中"
lead.width # => 2
lead.continuation? # => false
# Trailing continuation cell
trail = Termisu::Cell.continuation
trail.grapheme # => ""
trail.width # => 0
trail.continuation? # => true
Compatibility (Public API)
The grapheme property provides backward-compatible access:
cell = Termisu::Cell.new("ABC")
cell.grapheme # => "A" (first grapheme of input-String is stored)
continuation = Termisu::Cell.continuation
continuation.grapheme # => "" (empty for continuation cells)
Constants
Key of the canonical continuation cell, so Buffer's key planes can write continuation cells without materializing a Cell.
Key of the canonical default cell, resolved lazily so default_state? stays a single integer compare.
Style portion of a key (attr + fg + bg, bits 0..83). Cells with equal masked keys render with identical styling (see injectivity notes on #key).
Constructors
Creates a new Cell with the specified grapheme and colors.
Parameters:
- grapheme: Unicode grapheme cluster to display (if multi-grapheme string is passed, only the first grapheme cluster is stored)
- continuation: True if this is a trailing cell of a wide grapheme
- fg: Foreground color (default: white)
- bg: Background color (default: default terminal color)
- attr: Text attributes (default: None)
Note: Width is derived from grapheme content to ensure consistency. Continuation cells always have empty grapheme and width 0.
Occupancy invariants enforced:
- Continuation cells: always empty grapheme, width 0
- Empty non-continuation: normalized to default space cell (width 1)
- Leading cells: width derived via grapheme_width (handles VS16, ZWJ, flags)
- Multi-grapheme strings: only first grapheme is stored; debug log warns of truncation
Class methods
Continuation cells represent the trailing column occupied by a wide character. They have empty grapheme, width 0, and are never rendered directly.
trail = Termisu::Cell.continuation
trail.continuation? # => true
trail.width # => 0
trail.grapheme # => ""
Instance methods
Returns true when this cell is the canonical default blank cell.
Used by Buffer hot paths (clear/dirtiness accounting) to avoid expensive full-buffer work when rows are already blank.
Style setters splice only their own key field instead of re-running compute_key, which would re-derive the grapheme id and take the process-global intern mutex for non-ASCII cells.
Packed identity key: equal keys <=> equal cells (memberwise equality). Buffer hot paths (write dedup, diff scan, default_state?) compare this single UInt128 instead of walking the struct field-by-field.
Bit layout (LSB first):
- 0..15 attr (UInt16 flags value)
- 16..49 fg color key (2-bit mode + 32-bit canonical payload)
- 50..83 bg color key (same encoding)
- 84..115 grapheme id (ASCII byte direct; non-ASCII interned)
- 116..117 width (0..2)
- 118 continuation flag
Injectivity notes: color payload encodes exactly the fields Color#== compares per mode (index for ANSI, r/g/b for RGB), and public Color constructors zero the unused fields, so equal color keys imply Color#==. Grapheme ids are content-interned, so equal ids imply equal strings.