celina/core/buffer

Search:
Group by:

Buffer system

This module provides the core buffer abstraction for managing terminal screen content and efficient rendering.

Types

Buffer = object
  area*: Rect
2D buffer representing terminal screen content
Cell = object
  symbol*: string
  style*: Style
  hyperlink*: string
Represents a single character cell in the terminal
DirtyRegion = object
  isDirty*: bool
  minX*, minY*: int
  maxX*, maxY*: int
Tracks the rectangular region of changed cells

Procs

proc `$`(buffer: Buffer): string {....raises: [], tags: [], forbids: [].}
String representation of Buffer
proc `$`(cell: Cell): string {....raises: [], tags: [], forbids: [].}
String representation of Cell
proc `==`(a, b: Buffer): bool {....raises: [], tags: [], forbids: [].}
Compare two buffers for equality
proc `==`(a, b: Cell): bool {.inline, ...raises: [], tags: [], forbids: [].}
Compare two cells for equality
proc `[]`(buffer: Buffer; pos: Position): Cell {.inline, ...raises: [], tags: [],
    forbids: [].}
Get cell at position
proc `[]`(buffer: Buffer; x, y: int): Cell {....raises: [], tags: [], forbids: [].}
Get cell at coordinates (relative to buffer area)
proc `[]=`(buffer: var Buffer; pos: Position; cell: Cell) {.inline, ...raises: [],
    tags: [], forbids: [].}
Set cell at position
proc `[]=`(buffer: var Buffer; x, y: int; cell: Cell) {....raises: [], tags: [],
    forbids: [].}
Set cell at coordinates
proc cell(symbol: char; style: Style = defaultStyle(); hyperlink: string = ""): Cell {.
    inline, ...raises: [], tags: [], forbids: [].}
Create a new Cell from a character
proc cell(symbol: Rune; style: Style = defaultStyle(); hyperlink: string = ""): Cell {.
    inline, ...raises: [], tags: [], forbids: [].}
Create a new Cell from a Rune
proc cell(symbol: string = " "; style: Style = defaultStyle();
          hyperlink: string = ""): Cell {.inline, ...raises: [], tags: [],
    forbids: [].}
Create a new Cell
proc clear(buffer: var Buffer; cell: Cell = cell()) {....raises: [], tags: [],
    forbids: [].}
Clear the entire buffer with the given cell
proc clearDirty(buffer: var Buffer) {.inline, ...raises: [], tags: [], forbids: [].}
Clear the dirty region after rendering Should be called after buffer has been successfully rendered
proc clone(buffer: Buffer): Buffer {....raises: [], tags: [], forbids: [].}
Create a deep copy of the buffer
proc diff(old, new: Buffer): seq[tuple[pos: Position, cell: Cell]] {....raises: [],
    tags: [], forbids: [].}

Calculate differences between two buffers with dirty region optimization Returns a sequence of changes needed to transform old into new

This optimized implementation uses the dirty region tracking to avoid scanning unchanged portions of the buffer, providing significant performance improvements for typical use cases (e.g., single character edits, cursor movement).

Performance characteristics:

  • No changes: O(1) - immediate return
  • Small changes (1-100 cells): O(dirty region size) - highly optimized
  • Large changes (>2000 cells): O(width × height) - fallback to full scan
proc fill(buffer: var Buffer; area: Rect; fillCell: Cell) {....raises: [],
    tags: [], forbids: [].}
Fill a rectangular area with the given cell
proc getCell(buffer: Buffer; x, y: int): Cell {.inline, ...raises: [], tags: [],
    forbids: [].}
Get cell at coordinates (alias for operator)
proc getDirtyRegionSize(buffer: Buffer): int {....raises: [], tags: [], forbids: [].}
Get the number of cells in the dirty region Returns 0 if no changes have been made
proc isDirty(buffer: Buffer): bool {....raises: [], tags: [], forbids: [].}
Check if changed region for optimized diff
proc isEmpty(cell: Cell): bool {.inline, ...raises: [], tags: [], forbids: [].}
Check if cell is truly empty (no content at all)
proc isValidPos(buffer: Buffer; pos: Position): bool {.inline, ...raises: [],
    tags: [], forbids: [].}
Check if position is within buffer bounds
proc isValidPos(buffer: Buffer; x, y: int): bool {.inline, ...raises: [], tags: [],
    forbids: [].}
Check if coordinates are within buffer bounds
proc markDirty(buffer: var Buffer; x, y: int) {.inline, ...raises: [], tags: [],
    forbids: [].}
Mark a specific cell as dirty for optimized diff calculation This expands the dirty region to include the specified cell
proc markDirtyRect(buffer: var Buffer; rect: Rect) {.inline, ...raises: [],
    tags: [], forbids: [].}
Mark a rectangular area as dirty More efficient than marking individual cells when multiple cells change
proc merge(dest: var Buffer; src: Buffer; destPos: Position = pos(0, 0)) {.
    inline, ...raises: [], tags: [], forbids: [].}
Merge entire source buffer into destination buffer
proc merge(dest: var Buffer; src: Buffer; srcArea: Rect; destPos: Position) {.
    ...raises: [], tags: [], forbids: [].}
Merge part of source buffer into destination buffer
proc newBuffer(area: Rect): Buffer {.inline, ...raises: [], tags: [], forbids: [].}
Create a new Buffer with the specified area
proc newBuffer(width, height: int): Buffer {.inline, ...raises: [], tags: [],
    forbids: [].}
Create a new Buffer with specified dimensions at origin
proc resize(buffer: var Buffer; newArea: Rect) {....raises: [], tags: [],
    forbids: [].}
Resize the buffer to a new area
proc runesWidth(runes: seq[Rune]): int {....raises: [], tags: [], forbids: [].}
Calculate total display width of a sequence of runes
proc runeWidth(r: Rune): int {....raises: [], tags: [], forbids: [].}
Get the display width of a rune using Unicode standard width detection
proc setCell(buffer: var Buffer; pos: Position; rune: Rune; width: int;
             style: Style = defaultStyle(); hyperlink: string = "") {.inline,
    ...raises: [], tags: [], forbids: [].}
Position + Rune overload for setCell.
proc setCell(buffer: var Buffer; pos: Position; symbol: string; width: int;
             style: Style = defaultStyle(); hyperlink: string = "") {.inline,
    ...raises: [], tags: [], forbids: [].}
Position overload for setCell.
proc setCell(buffer: var Buffer; x, y: int; rune: Rune; width: int;
             style: Style = defaultStyle(); hyperlink: string = "") {.inline,
    ...raises: [], tags: [], forbids: [].}
Rune overload — converts to string once, then delegates.
proc setCell(buffer: var Buffer; x, y: int; symbol: string; width: int;
             style: Style = defaultStyle(); hyperlink: string = "") {.inline,
    ...raises: [], tags: [], forbids: [].}

Set a single character cell at (x, y) with a known display width. Unlike setString, this skips UTF-8 parsing and width calculation. For wide characters (width=2), the next cell is automatically marked empty. If there is not enough room for the shadow cell, the character is not written. Caller is responsible for providing correct width.

Note: If overwriting a shadow cell of an existing wide character, the original wide character at (x-1) will be left in an inconsistent state. This is the same behavior as setString.

proc setRunes(buffer: var Buffer; pos: Position; runes: seq[Rune];
              style: Style = defaultStyle(); hyperlink: string = "") {.inline,
    ...raises: [], tags: [], forbids: [].}
Set a sequence of runes starting at the given position
proc setRunes(buffer: var Buffer; x, y: int; runes: seq[Rune];
              style: Style = defaultStyle(); hyperlink: string = "") {.
    ...raises: [], tags: [], forbids: [].}
Set a sequence of runes starting at the given coordinates If hyperlink is provided, the text becomes a clickable link (OSC 8)
proc setString(buffer: var Buffer; area: Rect; text: string;
               style: Style = defaultStyle(); hAlign: HAlign = hLeft;
               vAlign: VAlign = vTop; hyperlink: string = "") {....raises: [],
    tags: [], forbids: [].}
Set a string within the given area with alignment Text is clipped to the area boundaries. Handles Unicode characters and wide characters properly for alignment calculation
proc setString(buffer: var Buffer; pos: Position; runes: seq[Rune];
               style: Style = defaultStyle(); hyperlink: string = "") {.inline,
    ...raises: [], tags: [], forbids: [].}
Alias for setRunes for convenience
proc setString(buffer: var Buffer; pos: Position; text: string;
               style: Style = defaultStyle(); hyperlink: string = "") {.inline,
    ...raises: [], tags: [], forbids: [].}
Set a string starting at the given position
proc setString(buffer: var Buffer; x, y: int; runes: seq[Rune];
               style: Style = defaultStyle(); hyperlink: string = "") {.inline,
    ...raises: [], tags: [], forbids: [].}
Alias for setRunes for convenience
proc setString(buffer: var Buffer; x, y: int; text: string;
               style: Style = defaultStyle(); hyperlink: string = "") {.
    ...raises: [], tags: [], forbids: [].}
Set a string starting at the given coordinates Handles Unicode characters and wide characters properly If hyperlink is provided, the text becomes a clickable link (OSC 8)
proc toStrings(buffer: Buffer): seq[string] {....raises: [], tags: [], forbids: [].}
Convert buffer to sequence of strings (one per row)
proc width(cell: Cell): int {....raises: [], tags: [], forbids: [].}
Get the display width of the cell's symbol