Buffer system
This module provides the core buffer abstraction for managing terminal screen content and efficient rendering.
Types
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 cell(symbol: char; style: Style = defaultStyle(); hyperlink: string = ""): Cell {. inline, ...raises: [], tags: [], forbids: [].}
- Create a new Cell from a character
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 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 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 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 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 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)