From 00bd1248e195b1976429b8621c7ab3ac8f72786e Mon Sep 17 00:00:00 2001 From: Horizontal42 <75383519+Horizontal42@users.noreply.github.com> Date: Wed, 8 Jul 2026 14:12:38 +0300 Subject: [PATCH] use a Set for the cross-sheet resolving stack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit resolving was an array checked with .includes() on every xref lookup during resolution — an O(n) membership test where only membership ever mattered, not order. Set makes it O(1). --- src/workspace.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/workspace.ts b/src/workspace.ts index 43b7645..fc688b7 100644 --- a/src/workspace.ts +++ b/src/workspace.ts @@ -25,18 +25,18 @@ function escapeRegExp(s: string): string { export class Workspace { private cache = new Map(); - private resolving: string[] = []; + private resolving: Set = new Set(); private parsedRefsCache = new Map(); constructor(private engine: SumEngine, private sheets: () => SheetSource[]) {} /** Evaluate a sheet as the user's active/open document — refs resolve live. */ evaluateSheet(sheetId: string, text: string): LineResult[] { - this.resolving.push(sheetId); + this.resolving.add(sheetId); try { return this.engine.evaluateDocument(text, (sheet, key) => this.resolveXRef(sheet, key)); } finally { - this.resolving.pop(); + this.resolving.delete(sheetId); } } @@ -106,7 +106,7 @@ export class Workspace { private resolveXRef(sheetTitle: string, key: string): XRefResolution { const target = this.findSheetByTitle(sheetTitle); if (!target) return { ok: false, reason: `sheet "${sheetTitle}" not found` }; - if (this.resolving.includes(target.id)) { + if (this.resolving.has(target.id)) { return { ok: false, reason: "circular reference" }; } const exports = this.exportsFor(target); @@ -128,12 +128,12 @@ export class Workspace { private exportsFor(sheet: SheetSource): SheetExports { const cached = this.cache.get(sheet.id); if (cached) return cached; - this.resolving.push(sheet.id); + this.resolving.add(sheet.id); let results: LineResult[]; try { results = this.engine.evaluateDocument(sheet.text, (s, k) => this.resolveXRef(s, k)); } finally { - this.resolving.pop(); + this.resolving.delete(sheet.id); } const vars = new Map(); for (const r of results) {