Skip to content

DocumentModel

Defined in: packages/engine/src/engine/DocumentModel.ts:144

Persistent document model with O(log N) line lookups and structural edits.

Design:

  • Each line has an immutable lineId (monotonically increasing counter).
  • LineState objects are stored in a Map<lineId, LineState> for O(1) access.
  • Line ordering is maintained in a SegmentTree (order-statistic Treap) that supports O(log N) insert, delete, and get-at-index operations.
  • A lazy position cache (Map<lineId, number>) provides O(1) position lookups after the first getLinePosition() call and is invalidated on structural edits.

Key invariant: line IDs never change, only their positions in the order tree. This means cached bytecode, dependency graph entries, and VM checkpoints keyed by lineId remain valid across all structural edits.

new DocumentModel(maxLines?): DocumentModel;

Defined in: packages/engine/src/engine/DocumentModel.ts:189

ParameterTypeDefault valueDescription
maxLinesnumberDEFAULT_CONFIG.performance.maxDocumentLinesCeiling on the line count, defaulting to the engine’s configured one. Every line costs a LineState with six arrays in it whatever the line says, so the cost of a document is its line count and nothing else bounds it: two hundred thousand lines of 1 + 1 exhausted the heap here, before a single expression had been looked at.

DocumentModel

get dirtyCount(): number;

Defined in: packages/engine/src/engine/DocumentModel.ts:508

Number of lines currently marked dirty. For diagnostics/tests.

number


get isEmpty(): boolean;

Defined in: packages/engine/src/engine/DocumentModel.ts:657

boolean


get lineCount(): number;

Defined in: packages/engine/src/engine/DocumentModel.ts:653

number

iterator: IterableIterator<LineState>;

Defined in: packages/engine/src/engine/DocumentModel.ts:664

Iterator over LineState in document order.

IterableIterator<LineState>


applyChanges(changes): ApplyChangesResult;

Defined in: packages/engine/src/engine/DocumentModel.ts:270

Apply one or more line-level changes to the document.

Precondition: Changes must be non-overlapping in their line ranges. If two changes target the same or adjacent lines, the reverse-order processing may produce incorrect results because the first-applied change shifts the line numbers that the second change references.

Changes are applied in reverse order (highest startLine first) so that earlier changes in the document don’t shift the indices of later changes during processing.

Returns both the newly inserted line IDs and the removed line IDs. Callers should use removed to clean up the dependency graph and other data structures keyed by lineId.

ParameterType
changesLineChange[]

ApplyChangesResult


clear(): void;

Defined in: packages/engine/src/engine/DocumentModel.ts:673

void


deleteLines(startLine, endLine): number[];

Defined in: packages/engine/src/engine/DocumentModel.ts:343

Delete lines in the given 1-based range [startLine, endLine] inclusive. Convenience wrapper around applyChanges.

ParameterType
startLinenumber
endLinenumber

number[]


editLine(lineNumber, newText): boolean;

Defined in: packages/engine/src/engine/DocumentModel.ts:360

Update the text of a single line in place. If the text hash differs, marks the line dirty and clears its bytecode/result so it gets re-evaluated.

Returns true if the text actually changed (hash mismatch).

ParameterType
lineNumbernumber
newTextstring

boolean


getAllLines(): LineState[];

Defined in: packages/engine/src/engine/DocumentModel.ts:432

Get all LineState entries in order. Useful for batch processing.

LineState[]


getDirtyLines(): LineState[];

Defined in: packages/engine/src/engine/DocumentModel.ts:446

Get all lines that are marked dirty.

LineState[]


getLineAt(position):
| LineState
| undefined;

Defined in: packages/engine/src/engine/DocumentModel.ts:385

Get the LineState at the given 1-based line position. O(1).

ParameterType
positionnumber

| LineState | undefined


getLineById(lineId):
| LineState
| undefined;

Defined in: packages/engine/src/engine/DocumentModel.ts:439

Get a LineState by its persistent line ID. O(1).

ParameterType
lineIdnumber

| LineState | undefined


getLinePosition(lineId): number;

Defined in: packages/engine/src/engine/DocumentModel.ts:399

Get the 1-based position of a line by its persistent ID. Returns -1 if the line ID is not in the document.

Uses a lazy position cache: O(N) on first call after structural edit, O(1) on subsequent calls. The cache is invalidated by any structural edit.

ParameterType
lineIdnumber

number


getVisibleLines(startLine, endLine): LineState[];

Defined in: packages/engine/src/engine/DocumentModel.ts:419

Get all LineState entries within the given viewport range (1-based, inclusive). Uses SegmentTree.getRange() for O(viewport + log N) collection instead of O(viewport × log N) per-line lookups.

ParameterType
startLinenumber
endLinenumber

LineState[]


hasAnyDirtyLineBefore(position): boolean;

Defined in: packages/engine/src/engine/DocumentModel.ts:466

Whether any line before position (1-based, exclusive) is dirty.

Used by ThreeTierEvaluator.setViewport() to decide whether cached checkpoint state might be stale and a full evaluate() (from line 1) is needed instead of the cheap viewport-only path.

O(d log N) where d = current dirty line count via dirtyLineIds, not O(N log N), a document that’s mostly clean (the steady state after initial load) answers this in the cost of resolving a handful of lineIds to positions, not walking every line up to position.

ParameterType
positionnumber

boolean


hasAnyDirtyVariableDefLineBefore(position): boolean;

Defined in: packages/engine/src/engine/DocumentModel.ts:497

Whether any variable-definition line before position (1-based, exclusive) is dirty.

Narrower than hasAnyDirtyLineBefore: VMCheckpointer.snapshot() only ever records state for lines with writes.length > 0 (see VMCheckpoints.ts), so a dirty plain-expression line before the viewport cannot have invalidated any checkpoint, there’s no checkpoint entry for it to invalidate. Only a dirty variable-def line can mean the VM state a checkpoint would restore is stale.

This distinction matters because PageManager.evictPageBytecode() marks evicted non-variable-def lines dirty (so they get Tier 1 if scrolled back into view), and Tier 3’s compile-only path never clears dirty for non-variable-def lines by design. Using the broader hasAnyDirtyLineBefore here meant scrolling far into a large, variable-def-free document would trip setViewport()’s fallback to evaluate() on every single call, evaluate() reprocesses the evicted lines via Tier 3, which recompiles their bytecode without clearing dirty, so the very next maintainAfterEval() re-evicts and re-dirties the same lines, forever re-triggering the fallback on an otherwise unchanged viewport.

ParameterType
positionnumber

boolean


insertLines(atLine, texts): number[];

Defined in: packages/engine/src/engine/DocumentModel.ts:329

Insert new lines at the given 1-based position. Convenience wrapper around applyChanges.

ParameterType
atLinenumber
textsstring[]

number[]


invalidateAll(): void;

Defined in: packages/engine/src/engine/DocumentModel.ts:568

Mark all lines as dirty (e.g., after plugin register/unregister).

void


isBytecodeValid(lineId, compiledAgainstHash): boolean;

Defined in: packages/engine/src/engine/DocumentModel.ts:524

Verify that bytecode compiled by a worker is still valid for this line.

When Phase 5.2h sends compilation to a worker, the worker posts back {lineId, bytecode, reads, writes, compiledAgainstHash}. Between dispatch and response, the user may have edited the line. This method lets the main thread check whether the bytecode is still applicable.

ParameterType
lineIdnumber
compiledAgainstHashnumber

boolean

true if the line still exists and its text hash matches.


markClean(lineId): void;

Defined in: packages/engine/src/engine/DocumentModel.ts:534

Mark a line as clean (re-evaluated successfully).

ParameterType
lineIdnumber

void


markDirty(lineId): void;

Defined in: packages/engine/src/engine/DocumentModel.ts:557

Mark a line as dirty (needs re-evaluation).

ParameterType
lineIdnumber

void


markDirtyByLineNumber(lineNumber): void;

Defined in: packages/engine/src/engine/DocumentModel.ts:546

Mark a line as dirty (needs re-evaluation) by its 1-based position. Convenience for callers that have line numbers instead of line IDs.

ParameterType
lineNumbernumber

void


setDocument(text): void;

Defined in: packages/engine/src/engine/DocumentModel.ts:203

Initialize or replace the entire document from a text blob. Clears all existing state and assigns new persistent line IDs.

ParameterType
textstring

void

DOCUMENT_TOO_LARGE for a document past maxLines, before any of it is stored. Recoverable: nothing has been replaced yet, so the model still holds whatever it held.


toJSON(): object;

Defined in: packages/engine/src/engine/DocumentModel.ts:683

Serialize the document model to a plain object for debugging.

object


updateLineCompiled(
lineId,
expressions,
bytecodes,
reads,
writes,
isVariableDef,
inlineSolveCount?): void;

Defined in: packages/engine/src/engine/DocumentModel.ts:631

Update a line’s compile-only state (Tier 3: background compilation).

Stores expressions, bytecodes, reads, and writes. Does NOT set results and does NOT mark the line clean, it still needs execution (Tier 1 or Tier 2) to produce results. This distinction allows the three-tier evaluation strategy: compile invisible lines in the background without executing them, then execute from cached bytecode when scrolled into view.

ParameterTypeDefault valueDescription
lineIdnumberundefinedPersistent line identifier.
expressionsstring[]undefinedExtracted expression strings (in order).
bytecodesBytecodeProgram[]undefinedCompiled bytecode for each expression (in order).
readsstring[]undefinedAggregated read variables across all expressions.
writesstring[]undefinedAggregated write variables across all expressions.
isVariableDefbooleanundefinedTrue if any expression defines a variable.
inlineSolveCountnumber0Number of inline solves (0 for full-line).

void


updateLineResult(
lineId,
results,
bytecodes,
expressions,
reads,
writes,
isVariableDef,
inlineSolveCount?): void;

Defined in: packages/engine/src/engine/DocumentModel.ts:590

Update a line’s evaluation state after successful execution (Tier 1 / Tier 2).

Sets results, bytecodes, reads, writes, and marks the line clean. Supports multi-expression lines (inline solves) via parallel arrays.

ParameterTypeDefault valueDescription
lineIdnumberundefinedPersistent line identifier.
resultsValue[][]undefinedEvaluation result groups for each expression (in order). Each element is a Value[].
bytecodesBytecodeProgram[]undefinedCompiled bytecode for each expression (in order).
expressionsstring[]undefinedExtracted expression strings (in order).
readsstring[]undefinedAggregated read variables across all expressions.
writesstring[]undefinedAggregated write variables across all expressions.
isVariableDefbooleanundefinedTrue if any expression defines a variable.
inlineSolveCountnumber0Number of inline solves (0 for full-line).

void