Skip to content

blackmatriXblack/cmdtextc

Repository files navigation

📘 CmdTextC Buffer & Display Subsystems – Comprehensive Technical Documentation

1. Executive Summary

The Buffer & Display Subsystems form the foundational data management and presentation layers of the CmdTextC terminal-based text editor. Implemented in pure C99, these modules provide a robust, high-performance architecture for in-memory text storage, dynamic row manipulation, syntax highlighting buffer allocation, coordinate translation, snapshot-based undo/redo, and ANSI terminal rendering. The buffer module handles raw character storage, tab expansion, render string generation, and state persistence. The display module manages viewport scrolling, cursor positioning, syntax-aware color emission, status/messaging bars, and direct terminal I/O via optimized buffer construction. Together, they deliver a deterministic, zero-external-dependency editing core optimized for fast interactive sessions and predictable memory behavior.


2. System Architecture & Design Philosophy

Design Principle Implementation Strategy
Dual-Buffer Row Model Each EditorRow maintains chars (raw input) and render (tab-expanded display). Separation ensures editing precision while guaranteeing accurate visual alignment.
Highlight Array Decoupling Syntax highlighting is stored in a parallel unsigned char *hl array matching render length. Rendering loop applies ANSI codes dynamically without mutating text data.
Snapshot-Based History Undo/Redo captures deep copies of the entire buffer state. Trade-off: higher memory footprint for O(1) rollback complexity and complete state fidelity.
Terminal-Optimized Rendering Constructs a contiguous ANSI-encoded string buffer (screenbuf[64KB]) in memory before a single termWrite() syscall. Minimizes I/O latency and terminal flicker.
Viewport-Centric Scrolling Calculates rowoff/coloff dynamically against terminal dimensions. Guarantees cursor visibility while preserving edit position integrity.
Strict Lifecycle Management Explicit calloc/malloc/free patterns. Zero global mutable state. All resources tied to EditorBuffer or EditorDisplay structs.

3. Core Data Structures

3.1 EditorRow

typedef struct {
    int    size;             // Raw character count in chars[]
    char   *chars;           // Raw input buffer (includes '\t', raw newlines excluded)
    char   *render;          // Display buffer (tabs expanded to spaces)
    int    rsize;            // Rendered character count
    unsigned char *hl;       // Parallel highlight array (1 byte per render char)
    int    hl_open_comment;  // Cross-row multiline comment state (0/1)
    int    idx;              // Row index in buffer
    TabStop *tabs;           // Dynamic array tracking tab stop positions & lengths
    int    tab_count;        // Active tab stops
    int    tab_cap;          // Allocated tab stop capacity
} EditorRow;
  • Dual-Storage Rationale: chars preserves exact user input for serialization. render normalizes whitespace for terminal alignment and syntax token mapping.
  • Highlight Mapping: hl[i] stores an HlColor enum value corresponding to render[i]. Enables per-character ANSI coloring without string duplication.

3.2 EditorBuffer

typedef struct {
    EditorRow *rows;         // Dynamic array of row structs
    int count;               // Current active rows
    int capacity;            // Allocated row capacity
    int dirty;               // Modification flag (triggers save prompt)
    char *filename;          // Associated file path
    int    num_undo;         // Active undo states in stack
    UndoState *undo_stack;   // Fixed-size circular stack (256)
    int    tab_size;         // Tab expansion width (default: 4)
} EditorBuffer;
  • Dynamic Growth: Initial capacity 128. Doubles via realloc when count == capacity.
  • Undo Stack: Pre-allocated 256 slots. Oldest state discarded when full.

3.3 UndoState

typedef struct {
    int last_cx, last_cy, last_action;
    EditorRow *rows;         // Deep-copied row snapshot
    int row_count;           // Number of rows in snapshot
    char *msg;               // Contextual message prefix
    char msg_prefix;
    long long saved_msgs;    // Reserved for message history
} UndoState;
  • Deep Copy Semantics: Captures chars, render, hl, and tabs for every row at time of action.
  • Restoration Logic: Frees current buffer state, reallocates from snapshot, restores cursor position.

3.4 EditorDisplay

typedef struct {
    int rowoff, coloff;      // Viewport scroll offsets (top row, left column)
    int cx, cy, rx;          // Cursor position: raw index, row index, render index
    int mode;                // Editor mode (0=NORMAL, 1=INSERT, 2=COMMAND, etc.)
    char statusmsg[256];     // Transient status message
    time_t statusmsg_time;   // Message timestamp for auto-expiry
    int search_hit;          // Current search match index
    int search_dir;          // Search direction (1=forward, -1=backward)
    int show_line_numbers;   // Toggle line number rendering
    int wrap_lines;          // Line wrapping flag (reserved)
} EditorDisplay;
  • Viewport Logic: cy - rowoff determines visible row range. rx - coloff determines visible column range.
  • Message Timeout: MSG_TIMEOUT = 5 seconds. Automatically clears after expiry.

4. Buffer Management Subsystem (buffer.c / buffer.h)

4.1 Row & Character CRUD

Function Complexity Behavior
bufferInsertRow O(N) Inserts row at index. Shifts subsequent rows via memmove. Expands capacity if needed. Marks dirty=1.
bufferDeleteRow O(N) Frees row resources. Shifts remaining rows. Updates idx fields.
bufferRowInsertChar O(L) Inserts character at position. Reallocates chars (+2 bytes). Shifts suffix.
bufferRowDeleteChar O(L) Removes character at position. Reallocates chars (-1 byte). Shifts suffix.
bufferRowAppendString O(S) Appends string to row end. Reallocates. Copies via memcpy.

4.2 Tab Expansion & Render Generation

bufferUpdateRow() performs the critical transformation from raw input to display-ready format:

  1. Counts \t characters to calculate required buffer size.
  2. Allocates render string: size + tabs_count * (tab_size - 1) + 1.
  3. Iterates chars:
    • On \t: Calculates spaces to next tab stop (tab_size - (idx % tab_size)). Records TabStop entry. Fills render with spaces.
    • On other chars: Direct copy to render.
  4. Allocates hl array of size rsize, initialized to HL_NORMAL.
  5. Updates rsize, tab_count, and tab stop array.

4.3 Coordinate Translation

Function Purpose Complexity
bufferRowCxToRx Converts raw char index (cx) to visual/render index (rx) O(cx)
bufferRowRxToCx Converts visual index (rx) to raw char index (cx) O(cx)
  • Handles tab width mapping. If tabs array is null, defaults to tab_size=4 fallback.
  • Essential for accurate cursor placement, scrolling boundaries, and syntax highlight mapping.

4.4 Buffer Serialization

bufferRowsToString():

  • Calculates total length including newline separators.
  • Allocates contiguous buffer.
  • Copies chars from each row, inserts \n between rows.
  • Returns heap-allocated string with length via pointer output. Used for file saving.

5. Display & Rendering Engine (display.c / display.h)

5.1 Viewport & Scrolling Logic

/* Scroll vertical boundaries */
if (d->cy < d->rowoff) d->rowoff = d->cy;
if (d->cy >= d->rowoff + screen_body_rows) d->rowoff = d->cy - screen_body_rows + 1;
if (d->rowoff < 0) d->rowoff = 0;
  • screen_body_rows = screenrows - 2 (reserves 1 row for status bar, 1 for message bar).
  • Guarantees cursor (cy) remains visible within viewport without manual scroll commands.

5.2 Screen Buffer Construction Pipeline

The displayRefresh() function builds a single ANSI-encoded string in screenbuf[65536]:

  1. Header: \x1b[?25l (hide cursor) + \x1b[H (move to home).
  2. Line Numbers (Optional): Calculates width based on buf->count. Pads with spaces. Colors via HL_COMMENT.
  3. Row Rendering Loop:
    • Computes visible slice: coloff to coloff + text_cols.
    • Iterates render chars. Checks hl[i]:
      • If HL_NORMAL: Emits default color.
      • If colored: Emits \x1b[<code>m via syntaxToColor().
      • Control chars (\0-\x1A): Displayed as @+char in inverse video (\x1b[7m).
    • Terminates line with \x1b[K (clear to end of line).
  4. Status Bar: Reverse video (\x1b[7m). Displays filename, dirty flag [+], mode, line count, cursor coords, filetype. Pads to screen width.
  5. Message Bar: Displays statusmsg if time() - statusmsg_time < MSG_TIMEOUT.
  6. Cursor Positioning: Calculates absolute terminal coords. Emits \x1b[y;xH + \x1b[?25h (show cursor).
  7. Output: Single termWrite(screenbuf, sb_len) syscall.

5.3 Status & Message Management

  • displaySetStatusMessage(): Formats string via vsnprintf, records timestamp. Auto-expires after 5 seconds during refresh.
  • Mode indicator maps integers to strings: 0=NORMAL, 1=INSERT, 2=COMMAND, 3=SEARCH, 4=VISUAL, 5=REPLACE.

6. Undo/Redo State Management

6.1 State Capture (bufferPushUndo)

  1. Discards states beyond current num_undo pointer (trims redo history on new action).
  2. If stack full (256), frees oldest state, shifts array left.
  3. Calls undoStateCapture():
    • Deep copies EditorRow array: allocates new chars, render, hl, tabs for each row.
    • Stores cursor position (last_cx, last_cy) and action type.
  4. Increments num_undo.

6.2 State Restoration (bufferUndo / bufferRedo)

  1. Decrements/increments num_undo pointer.
  2. Frees all current rows, chars, render, hl, tabs in active buffer.
  3. Reallocates buffer arrays from snapshot.
  4. Deep copies snapshot data back into active buffer.
  5. Restores cx, cy to captured positions.
  6. Marks buffer dirty implicitly via structural replacement.

Complexity: O(R * L) per undo/redo, where R = row count, L = avg line length. High memory cost, instantaneous execution.


7. API Reference

7.1 Buffer API

Function Signature Description
bufferCreate EditorBuffer *bufferCreate(void); Allocates & initializes buffer. Inserts empty row.
bufferFree void bufferFree(EditorBuffer *buf); Frees all rows, undo stack, and buffer struct.
bufferInsertRow int bufferInsertRow(EditorBuffer *buf, int at, const char *s, size_t len); Inserts row at index. Returns new index or -1.
bufferDeleteRow void bufferDeleteRow(EditorBuffer *buf, int at); Deletes row at index. Updates indices.
bufferRowInsertChar int bufferRowInsertChar(EditorBuffer *buf, EditorRow *row, int at, int c); Inserts character at raw index.
bufferRowDeleteChar void bufferRowDeleteChar(EditorBuffer *buf, EditorRow *row, int at); Deletes character at raw index.
bufferUpdateRow void bufferUpdateRow(EditorBuffer *buf, EditorRow *row); Rebuilds render, hl, and tabs. Call after text mutation.
bufferRowsToString char *bufferRowsToString(EditorBuffer *buf, int *buflen); Serializes buffer to heap-allocated string.
bufferPushUndo void bufferPushUndo(EditorBuffer *buf, int action_type, int cx, int cy); Captures current state for undo.
bufferUndo void bufferUndo(EditorBuffer *buf, int *cx, int *cy); Restores previous state. Updates cursor.
bufferRedo void bufferRedo(EditorBuffer *buf, int *cx, int *cy); Re-applies undone state. Updates cursor.

7.2 Display API

Function Signature Description
displayInit void displayInit(EditorDisplay *d); Zero-initializes display state.
displayRefresh void displayRefresh(EditorDisplay *d, EditorBuffer *buf, struct EditorSyntax *syn); Renders full screen to terminal.
displaySetStatusMessage void displaySetStatusMessage(EditorDisplay *d, const char *fmt, ...); Sets formatted status message with 5s timeout.

8. Rendering Pipeline & Terminal I/O

[Input Event] → bufferMutation() → bufferUpdateRow() → syntaxHighlight()
      ↓
[Refresh Trigger] → displayRefresh()
      ↓
[Viewport Calc] → rowoff/coloff boundaries → screenrows/screencols
      ↓
[Screen Buffer Build] → sbAppend() + ANSI codes + syntaxToColor()
      ↓
[Status/Message] → Reverse video + timeout check + padding
      ↓
[Cursor Placement] → \x1b[y;xH + \x1b[?25h
      ↓
[I/O Flush] → termWrite(screenbuf, sb_len)
      ↓
[Terminal Emulator] → Renders frame instantly

ANSI Code Mapping:

  • \x1b[?25l / \x1b[?25h: Hide/Show cursor
  • \x1b[H: Cursor home
  • \x1b[K: Clear to end of line
  • \x1b[7m / \x1b[27m: Enter/Exit inverse video (status bar, control chars)
  • \x1b[<N>m: Set foreground color (31-37, 90-97)
  • \x1b[y;xH: Absolute cursor positioning

9. Performance, Memory & Complexity Analysis

Metric Value / Behavior Notes
Render Time O(R * C) per refresh, where R = visible rows, C = visible cols Highly optimized via contiguous buffer build
Memory Overhead ~3 * L bytes per row (chars + render + hl) + tab stops Linear scaling. Predictable heap usage
Undo Memory O(R_total * L_avg * UNDO_STACK_SIZE) Peak ~256 full buffer copies. Suitable for <5MB files
I/O Efficiency 1 write() syscall per frame Eliminates per-char/line terminal latency
Tab Expansion O(L) per row update Cached in tabs[] array. Fast coordinate conversion
Thread Safety Not thread-safe Designed for single-threaded main loop

10. Integration Guidelines

10.1 Initialization Sequence

EditorBuffer *buf = bufferCreate();
EditorDisplay d;
displayInit(&d);
SyntaxRegistry *syn = syntaxInit();
// Load file, run syntaxSelectHighlight(syn, buf)

10.2 Edit Loop

while (running) {
    displayRefresh(&d, buf, syn->current);
    int key = termReadKey();
    inputProcessKeypress(key, buf, &d, syn, &running);
}

10.3 Best Practices

  • Always call bufferUpdateRow() after modifying row->chars or row->size.
  • Push undo states before mutation, not after.
  • Use bufferRowCxToRx for cursor positioning logic; use bufferRowRxToCx for hit-testing.
  • Clear d.statusmsg_time = 0 to force immediate message display.

11. Known Limitations & Engineering Roadmap

Limitation Impact Recommended Mitigation
Full snapshot undo High RAM usage for large files Implement incremental delta logging or copy-on-write row pointers
Fixed 64KB screen buffer Limits rendering on ultra-wide terminals Switch to dynamic realloc or chunked I/O writes
CxToRx/RxToCx tab lookup bug row->tabs[0] always accessed instead of matching stop Iterate tabs[] array or use binary search for exact match
No line wrapping Long lines require horizontal scroll Implement soft-wrap rendering layer with virtual line mapping
Hardcoded tab size No per-file or user-configurable tabs Load tab_size from config or file shebang/metadata
Blocking refresh UI freezes during heavy syntax parse Decouple highlight computation to background thread or async queue

🚀 v2.0 Roadmap:

  • Incremental undo with operation logs (InsertChar, DeleteLine, etc.)
  • Virtual scrolling & infinite canvas support
  • Dynamic screen buffer allocation
  • Configurable tab size & soft-wrap engine
  • Async syntax highlighting pipeline
  • Terminal capability detection (TrueColor, mouse tracking, bracketed paste)

12. Appendix: Constants & Enumerations

12.1 Highlight Color Enum (HlColor)

typedef enum {
    HL_NORMAL = 0,      // Default text
    HL_COMMENT,         // Single-line comment
    HL_MLCOMMENT,       // Multi-line comment
    HL_KEYWORD1,        // Control flow, loops
    HL_KEYWORD2,        // Secondary keywords
    HL_STRING,          // String literals
    HL_NUMBER,          // Numeric literals
    HL_MATCH,           // Bracket/quote match
    HL_SEARCH_MATCH,    // Search highlight
    HL_ERROR,           // Control chars, syntax errors
    HL_PREPROC,         // Preprocessor directives (#)
    HL_TYPE,            // Data types, built-ins
    HL_FUNCTION,        // Function calls/declarations
    HL_CONSTANT,        // Constants, enums
    HL_OPERATOR,        // (){}[]
} HlColor;

12.2 Editor Modes

Value Mode Description
0 NORMAL Navigation, command execution
1 INSERT Text insertion
2 COMMAND : prompt for save/quit/search
3 SEARCH / or ? prompt for find
4 VISUAL Selection mode
5 REPLACE Overwrite mode

12.3 Key Internal Constants

Constant Value Purpose
INITIAL_ROWS 128 Starting row capacity
UNDO_STACK_SIZE 256 Max undo history depth
TAB_SIZE 4 Default tab expansion width
MSG_TIMEOUT 5 Status message expiry (seconds)
screenbuf size 65536 Max ANSI render buffer (64KB)

📄 Document Version: 1.0
📅 Last Updated: May 2026
🔧 Target Platform: POSIX / Windows Terminal Emulators
📦 Source Reference: buffer.h, buffer.c, display.h, display.c
🏷️ Tags: #TextEditorCore #ANSIRendering #BufferManagement #UndoRedo #TerminalUI #C99 #SyntaxHighlighting #ViewportScrolling #ZeroDependency #PerformanceOptimized

About

Cmdtextc was made by pure C with supercoder fireguest zero.projects

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors