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). LineStateobjects are stored in aMap<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 firstgetLinePosition()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.
Constructors
Section titled “Constructors”Constructor
Section titled “Constructor”new DocumentModel(maxLines?): DocumentModel;Defined in: packages/engine/src/engine/DocumentModel.ts:189
Parameters
Section titled “Parameters”| Parameter | Type | Default value | Description |
|---|---|---|---|
maxLines | number | DEFAULT_CONFIG.performance.maxDocumentLines | Ceiling 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. |
Returns
Section titled “Returns”DocumentModel
Accessors
Section titled “Accessors”dirtyCount
Section titled “dirtyCount”Get Signature
Section titled “Get Signature”get dirtyCount(): number;Defined in: packages/engine/src/engine/DocumentModel.ts:508
Number of lines currently marked dirty. For diagnostics/tests.
Returns
Section titled “Returns”number
isEmpty
Section titled “isEmpty”Get Signature
Section titled “Get Signature”get isEmpty(): boolean;Defined in: packages/engine/src/engine/DocumentModel.ts:657
Returns
Section titled “Returns”boolean
lineCount
Section titled “lineCount”Get Signature
Section titled “Get Signature”get lineCount(): number;Defined in: packages/engine/src/engine/DocumentModel.ts:653
Returns
Section titled “Returns”number
Methods
Section titled “Methods”[iterator]()
Section titled “[iterator]()”iterator: IterableIterator<LineState>;Defined in: packages/engine/src/engine/DocumentModel.ts:664
Iterator over LineState in document order.
Returns
Section titled “Returns”IterableIterator<LineState>
applyChanges()
Section titled “applyChanges()”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.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
changes | LineChange[] |
Returns
Section titled “Returns”clear()
Section titled “clear()”clear(): void;Defined in: packages/engine/src/engine/DocumentModel.ts:673
Returns
Section titled “Returns”void
deleteLines()
Section titled “deleteLines()”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.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
startLine | number |
endLine | number |
Returns
Section titled “Returns”number[]
editLine()
Section titled “editLine()”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).
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
lineNumber | number |
newText | string |
Returns
Section titled “Returns”boolean
getAllLines()
Section titled “getAllLines()”getAllLines(): LineState[];Defined in: packages/engine/src/engine/DocumentModel.ts:432
Get all LineState entries in order. Useful for batch processing.
Returns
Section titled “Returns”getDirtyLines()
Section titled “getDirtyLines()”getDirtyLines(): LineState[];Defined in: packages/engine/src/engine/DocumentModel.ts:446
Get all lines that are marked dirty.
Returns
Section titled “Returns”getLineAt()
Section titled “getLineAt()”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).
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
position | number |
Returns
Section titled “Returns”| LineState
| undefined
getLineById()
Section titled “getLineById()”getLineById(lineId): | LineState | undefined;Defined in: packages/engine/src/engine/DocumentModel.ts:439
Get a LineState by its persistent line ID. O(1).
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
lineId | number |
Returns
Section titled “Returns”| LineState
| undefined
getLinePosition()
Section titled “getLinePosition()”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.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
lineId | number |
Returns
Section titled “Returns”number
getVisibleLines()
Section titled “getVisibleLines()”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.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
startLine | number |
endLine | number |
Returns
Section titled “Returns”hasAnyDirtyLineBefore()
Section titled “hasAnyDirtyLineBefore()”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.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
position | number |
Returns
Section titled “Returns”boolean
hasAnyDirtyVariableDefLineBefore()
Section titled “hasAnyDirtyVariableDefLineBefore()”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.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
position | number |
Returns
Section titled “Returns”boolean
insertLines()
Section titled “insertLines()”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.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
atLine | number |
texts | string[] |
Returns
Section titled “Returns”number[]
invalidateAll()
Section titled “invalidateAll()”invalidateAll(): void;Defined in: packages/engine/src/engine/DocumentModel.ts:568
Mark all lines as dirty (e.g., after plugin register/unregister).
Returns
Section titled “Returns”void
isBytecodeValid()
Section titled “isBytecodeValid()”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.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
lineId | number |
compiledAgainstHash | number |
Returns
Section titled “Returns”boolean
true if the line still exists and its text hash matches.
markClean()
Section titled “markClean()”markClean(lineId): void;Defined in: packages/engine/src/engine/DocumentModel.ts:534
Mark a line as clean (re-evaluated successfully).
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
lineId | number |
Returns
Section titled “Returns”void
markDirty()
Section titled “markDirty()”markDirty(lineId): void;Defined in: packages/engine/src/engine/DocumentModel.ts:557
Mark a line as dirty (needs re-evaluation).
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
lineId | number |
Returns
Section titled “Returns”void
markDirtyByLineNumber()
Section titled “markDirtyByLineNumber()”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.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
lineNumber | number |
Returns
Section titled “Returns”void
setDocument()
Section titled “setDocument()”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.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
text | string |
Returns
Section titled “Returns”void
Throws
Section titled “Throws”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()
Section titled “toJSON()”toJSON(): object;Defined in: packages/engine/src/engine/DocumentModel.ts:683
Serialize the document model to a plain object for debugging.
Returns
Section titled “Returns”object
updateLineCompiled()
Section titled “updateLineCompiled()”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.
Parameters
Section titled “Parameters”| Parameter | Type | Default value | Description |
|---|---|---|---|
lineId | number | undefined | Persistent line identifier. |
expressions | string[] | undefined | Extracted expression strings (in order). |
bytecodes | BytecodeProgram[] | undefined | Compiled bytecode for each expression (in order). |
reads | string[] | undefined | Aggregated read variables across all expressions. |
writes | string[] | undefined | Aggregated write variables across all expressions. |
isVariableDef | boolean | undefined | True if any expression defines a variable. |
inlineSolveCount | number | 0 | Number of inline solves (0 for full-line). |
Returns
Section titled “Returns”void
updateLineResult()
Section titled “updateLineResult()”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.
Parameters
Section titled “Parameters”| Parameter | Type | Default value | Description |
|---|---|---|---|
lineId | number | undefined | Persistent line identifier. |
results | Value[][] | undefined | Evaluation result groups for each expression (in order). Each element is a Value[]. |
bytecodes | BytecodeProgram[] | undefined | Compiled bytecode for each expression (in order). |
expressions | string[] | undefined | Extracted expression strings (in order). |
reads | string[] | undefined | Aggregated read variables across all expressions. |
writes | string[] | undefined | Aggregated write variables across all expressions. |
isVariableDef | boolean | undefined | True if any expression defines a variable. |
inlineSolveCount | number | 0 | Number of inline solves (0 for full-line). |
Returns
Section titled “Returns”void