class

Phosphor::Buffer

Inherits Reference < Object

A 2D grid of Cells sized to a Frame.

Buffer is the shared canvas that all widgets write into during a render pass. Widgets call set_string at frame-relative positions; the Renderer diffs two buffers to find changed cells and writes only those to the terminal.

Cells are stored in row-major order: position (col, row) maps to flat index row * frame.width + col. The stored frame lets the renderer translate those positions into absolute terminal coordinates when emitting ANSI cursor moves.

Constants

ANSI_ESCAPE = /\e\[[0-9;]*[a-zA-Z]/

Matches ANSI CSI escape sequences (e.g. \e[31m). Stripped from text in set_string — styling must go through Style.

Constructors

new(frame : Frame, dark_background : Bool = true, color_profile : ColorProfile = ColorProfile::TrueColor)

dark_background resolves any AdaptiveColor written via set_string — defaults to true so existing call sites that don't care about light/dark resolution (most specs) don't need to change. color_profile downsamples colors to what the terminal actually supports — defaults to TrueColor (no downsampling) for the same reason.

Source

Instance methods

cell_at(col : Int32, row : Int32) : Cell

Returns the Cell at local (col, row) — the same buffer-local, zero-based coordinate space #copy_region accepts. Read counterpart to #set_cell.

Source
copy_region(src : Buffer, src_col : Int32, src_row : Int32, dest_col : Int32, dest_row : Int32, width : Int32, height : Int32) : Void

Copies a width×height rectangular region of src, starting at local (src_col, src_row), into self at local (dest_col, dest_row). Coordinates are buffer-local and zero-based — the same space #full_redraw/#diff walk internally — not the absolute, origin-shifted space #set_string accepts.

Transition subclasses (e.g. Transition::Slide) use this to composite two independently rendered Buffers — one per screen — into the shared transition buffer, without needing direct access to either buffer's cell storage. Both buffers must share the same Frame dimensions; the region must fit within both.

Source
diff(other : Buffer) : Array(Diff)

Both buffers must have the same dimensions.

Source
frame
Source
full_redraw

Compares self against other cell by cell and returns only the changed cells.

Wide characters and their Cell::CONTINUATION slot are treated as an atomic pair: if either half differs between the two buffers, both slots appear in the result. Rewriting only one half of a wide character pair corrupts terminal output.

Returns an empty array when both buffers are identical. Positions in the returned Diff values are absolute terminal coordinates (frame origin applied).

Returns every cell in self as a Diff, unconditionally — a genuine full redraw, unlike #diff against a fresh blank Buffer.

Renderer#draw needs this after Renderer#invalidate: diffing against a synthetic blank buffer only catches cells that are non-default in self. Any cell self also leaves at its own default (untouched) — e.g. background rows the new screen doesn't paint — would compare equal to that blank buffer and never appear in the diff, even though the real terminal still shows whatever the previous screen actually drew there. Emitting every cell sidesteps that comparison entirely.

Source
height

Height of the buffer in rows, equal to frame.height.

Source
set_cell(col : Int32, row : Int32, cell : Cell) : Void

Writes cell at local (col, row), the same coordinate space #copy_region accepts. Transition subclasses that composite two buffers cell by cell rather than region by region (e.g. Transition::Fade) use this alongside #cell_at instead of #copy_region.

Source
set_string(x : Int32, y : Int32, text : String, style : Style, max_width : Int32 = width) : Void

Writes text into the buffer starting at column x, row y — in the same frame-relative coordinate space every MockWidget renders in (frame.origin_col + col, frame.origin_row + row; see CLAUDE.md's "All rendering is frame-relative" rule). x/y are translated to this buffer's own local cell array by subtracting @frame.origin_col/ @frame.origin_row — the exact inverse of what #diff/#full_redraw add back when producing absolute terminal positions. This is what makes a non-zero-origin buffer (an Inline session's block, or any nested sub-frame) address the same coordinates its widgets compute, rather than requiring every widget to special-case its own origin.

Walks grapheme clusters via the \X regex, honoring Unicode display widths through Width.of. Wide characters (CJK, emoji) occupy 2 cells; the second slot receives Cell::CONTINUATION. Truncation stops at max_width cells without ever splitting a wide character. Out-of-bounds writes are silently clipped — safety net, not a contract. Raw ANSI escape sequences in text are stripped before processing; styling is applied exclusively through style.

style is merged over whatever style the target cell already carries (via Style#merge), not written in place of it — an attribute style doesn't set (e.g. a plain-text Style.new with no bg) falls back to the existing cell rather than erasing it. This is what lets Renderer#draw's full-frame background fill survive underneath text a Screen/widget draws on top of it. A caller that wants to explicitly erase a cell's background back to the terminal's own default sets Color::Default rather than leaving bg unset — Style#merge treats Color::Default as an explicit value, distinct from nil.

Source
width

Width of the buffer in columns, equal to frame.width.

Source