Skip to main content

Module undo

Module undo 

Source
Expand description

Bounded, WASM-clean CST-backed undo/redo history (E007 OBJ3, TR-010..014).

UndoStack is the reusable undo/redo model the editor surfaces wire a document into. Each reversible unit is an UndoEntry: a snapshot of the exact prior document bytes (source_text), the lossless CST at that state (CstDocument, cheap to clone — see below), and a caller-supplied cursor/metadata value. Restoring an entry therefore reproduces the prior buffer byte-for-byte with no reflow or normalization (TR-010, SC-005).

§WASM-clean (TR-014, HINT-002)

This module adds no filesystem / native / I/O / network dependency to ronin-core — it holds only CST + text + a generic cursor value, so the crate stays rowan-only and the wasm32-unknown-unknown build gate keeps passing. It also measures no wall-clock time: std::time::Instant is unavailable on wasm32-unknown-unknown, so the timing of a coalesce boundary is decided caller-side (the native ronin-app measures Instant elapsed against the configured window) and handed in as the coalesce flag on record. coalesce_window is retained only as reference metadata; this module never reads a clock. All persistence (atomic save, the recovery sidecar) lives in the native ronin-app; the undo model deliberately knows nothing about it. The stack is reusable by downstream epics (E005/E008, TR-013).

§Cheap snapshots via structural sharing (AD-002, ADR-0001)

CstDocument is Clone, and a rowan green tree is immutable / structurally shared: cloning the document bumps an Arc refcount on the green root, and an edit reuses every untouched subtree verbatim. Snapshotting the CST per undo unit is therefore cheap (no deep copy of the tree), while the retained source_text guarantees the exact-prior-byte restore. This is why the undo unit is a CST + text snapshot rather than a reverse-delta log.

§Cursor / metadata generic (C)

UndoStack and UndoEntry are generic over a cursor/metadata type C so ronin-core stays decoupled from any surface’s cursor representation: the native editor supplies its own CursorState, a future headless surface could supply (). C is restored alongside the document on undo/redo (TR-010).

§Bounds and coalescing (TR-011, TR-024, TR-027)

The undo ring is bounded by both a count cap and a byte-size cap (UndoCap); when either binds, the oldest unit is dropped so memory stays predictable on large files (SC-006/SC-009). A rapid run of edits the caller marks coalesce: true collapses into a single unit (SC-006/SC-010); the first edit after a pause (coalesce: false) commits the prior boundary and starts a new unit (TR-027). A new edit after an undo clears the redo stack (TR-012).

§State model (current / undo ring / redo)

The stack tracks the current committed document state (current) plus a bounded undo ring of prior boundaries and a redo stack. The caller owns the live buffer; the stack owns the history. The contract is:

  • record(entry, coalesce): entry is a snapshot of the document state after the edit just applied. When coalesce is false, the previous current is pushed onto the undo ring as a discrete boundary and entry becomes the new current (a new undo unit). When coalesce is true, entry replaces current in place without pushing a boundary, so a run of coalesced edits collapses to the single boundary captured before the run began. Either way, recording an edit clears redo (TR-012). The very first record on an empty stack just seeds current (there is no prior boundary to keep).
  • undo(): pops the most recent boundary off the undo ring, pushes the current state onto redo, makes the popped boundary the new current, and returns it for the caller to apply to the buffer. Returns None when there is nothing to undo.
  • redo(): pops the most recent state off redo, pushes the current state back onto the undo ring, makes the popped state the new current, and returns it. Returns None when there is nothing to redo.

This keeps restore byte-faithful (the caller writes the returned entry’s source_text verbatim — no reflow) and the history strictly bounded.

Structs§

UndoCap
The history bound for an UndoStack: a unit-count cap and a total snapshot byte-size cap (TR-011, TR-024).
UndoEntry
One reversible unit — a snapshot of a single document state (TR-010).
UndoStack
The bounded undo/redo history for a single document (TR-010..014).

Constants§

DEFAULT_COALESCE_WINDOW
Default coalesce window: edits less than 500 ms apart fold into one unit (TR-027). A pause longer than this boundary starts a new undo unit.
DEFAULT_UNDO_BYTE_CAP
Default maximum total snapshot byte-size retained: 64 MiB (TR-024).
DEFAULT_UNDO_COUNT_CAP
Default maximum number of undo units retained (TR-024).