struct Termisu::Cell

Overview

Cell represents a single character cell in the terminal buffer.

Cell contains:

Grapheme and Continuation Cells

Wide characters (CJK, emoji) occupy 2 columns. The Cell model represents this:

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)

Defined in:

termisu/cell.cr

Constant Summary

CONTINUATION_KEY = Cell.new(continuation: true).key

Key of the canonical continuation cell, so Buffer's key planes can write continuation cells without materializing a Cell.

DEFAULT_KEY = Cell.new.key

Key of the canonical default cell, resolved lazily so default_state? stays a single integer compare.

KEY_STYLE_MASK = (1_u128 << 84) - 1

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

Class Method Summary

Instance Method Summary

Constructor Detail

def self.new(grapheme : String = " ", continuation : Bool = false, fg : Color = Color.white, bg : Color = Color.default, attr : Attribute = Attribute::None) #

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

[View source]

Class Method Detail

def self.continuation #

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      # => ""

[View source]
def self.default #

default empty cell (space with default colors, width 1, not continuation).


[View source]

Instance Method Detail

def attr : Attribute #

[View source]
def attr=(attr : Attribute) #

[View source]
def bg : Color #

[View source]
def bg=(bg : Color) #

[View source]
def continuation? : Bool #

[View source]
def default_state? : Bool #

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.


[View source]
def fg : Color #

[View source]
def fg=(fg : Color) #

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.


[View source]
def grapheme : String #

[View source]
def grapheme=(grapheme : String) #

[View source]
def key : UInt128 #

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.


[View source]
def width : UInt8 #

[View source]