Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Operation System

Every mutation to a NullCad document — drawing, moving, deleting, vertex edits, layer changes, comparisons, and more — goes through a single, strongly-typed operation system. This is what powers undo/redo, the audit trail, and (indirectly) the Lua plugin and MCP server integrations, since both reuse the same operation builders rather than mutating the document directly.

Key pieces

  • OperationType — a strongly-typed enum covering every mutation: create/delete/restore, move/rotate/scale, vertex edits, split/extend/trim, explode, layer changes, comparisons, and more.
  • OperationHistory — an append-only log. Undo doesn’t remove the original operation — it appends a compensating (inverse) operation. Redo re-applies. This means the full history is always preserved as an audit trail, and a new operation clears the redo stack (no branching history).
  • DocumentState — combines a Document, an OperationHistory, and a Vec<Snapshot>. apply_operation() applies the operation, records it, and auto-snapshots periodically (every 50 operations by default). undo()/redo() create and apply the compensating operations.
  • Phase tracking — every operation is tagged with the active survey phase at the time it was applied.

Example

#![allow(unused)]
fn main() {
let op = OperationType::MoveGeometry {
    geometry_ids: vec![id],
    delta: Vector2::new(10.0, 20.0),
};
doc_state.apply_operation(user_id, op)?;
doc_state.undo(user_id)?;   // applies the compensating op
doc_state.redo(user_id)?;
}

Soft deletes

Deleted geometry is tombstoned rather than physically removed, preserving history — a delete is just another operation with a well-defined inverse (restore).

Persistence

Operation history is saved with the document (see The .nullcad Archive), so the full edit history travels with the project file.

Collaboration-ready by design

The operations system was designed to support future collaborative editing with minimal changes: UUID geometry IDs, tombstone soft deletes, operation metadata (user_id/timestamp/phase/description), an append-only log, and a HashMap-based document are all CRDT-friendly properties. The remaining path to multi-user editing is real authenticated user IDs, an operation-sync transport, conflict resolution, and per-user undo — without needing to break the existing model.

Adding a new operation

  1. Add a variant to OperationType in src/operations/types.rs.
  2. Implement inverse(), summary(), and affected_geometry_ids() for it.
  3. Add execution logic in apply_operation_type() in src/operations/executor.rs.
  4. Apply it via DocumentState::apply_operation().
  5. Test the operation, its inverse, and its summary.