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
  content*: seq[seq[Cell]]
  dirty*: DirtyRegion
2D buffer representing terminal screen content
Cell = object
  symbol*: string
  style*: Style
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()): Cell {.inline,
    ...raises: [], tags: [], forbids: [].}
Create a new Cell from a character
proc cell(symbol: Rune; style: Style = defaultStyle()): Cell {.inline,
    ...raises: [], tags: [], forbids: [].}
Create a new Cell from a Rune
proc cell(symbol: string = " "; style: Style = defaultStyle()): 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 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 getDirtyRegionSize(buffer: Buffer): int {.inline, ...raises: [], tags: [],
    forbids: [].}
Get the number of cells in the dirty region Returns 0 if no changes have been made
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 setRunes(buffer: var Buffer; pos: Position; runes: seq[Rune];
              style: Style = defaultStyle()) {.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()) {....raises: [], tags: [], forbids: [].}
Set a sequence of runes starting at the given coordinates
proc setString(buffer: var Buffer; pos: Position; runes: seq[Rune];
               style: Style = defaultStyle()) {.inline, ...raises: [], tags: [],
    forbids: [].}
Alias for setRunes for convenience
proc setString(buffer: var Buffer; pos: Position; text: string;
               style: Style = defaultStyle()) {.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()) {.inline, ...raises: [], tags: [],
    forbids: [].}
Alias for setRunes for convenience
proc setString(buffer: var Buffer; x, y: int; text: string;
               style: Style = defaultStyle()) {....raises: [], tags: [],
    forbids: [].}
Set a string starting at the given coordinates Handles Unicode characters and wide characters properly
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