From b3c07c82443d1f5c06c7f4f54eeb6ef08b941b67 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2026 09:14:43 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20cache=20referencedTitles=20?= =?UTF-8?q?regex=20execution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces `parsedRefsCache` in `Workspace` to prevent repeatedly executing the heavy `XREF_SCAN_RE` regex across the same unchanged sheet text during cache invalidations. Co-authored-by: Horizontal42 <75383519+Horizontal42@users.noreply.github.com> --- .jules/bolt.md | 3 +++ src/workspace.ts | 13 +++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..1e7e508 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2025-02-18 - Repeated Regex Execution Caching + **Learning:** In recursive or looping traversal like graph cycle detection (e.g. invalidation of multiple sheets), running heavy Regex (`matchAll`) repeatedly on the exact same text is extremely slow and compounds exponentially. + **Action:** Introduce text-based short-circuiting caches (e.g., matching the original `text` property against the cached payload string) where text extraction logic like Regex or parsing runs purely on deterministic inputs. diff --git a/src/workspace.ts b/src/workspace.ts index c387051..43b7645 100644 --- a/src/workspace.ts +++ b/src/workspace.ts @@ -26,6 +26,7 @@ function escapeRegExp(s: string): string { export class Workspace { private cache = new Map(); private resolving: string[] = []; + private parsedRefsCache = new Map(); constructor(private engine: SumEngine, private sheets: () => SheetSource[]) {} @@ -47,7 +48,7 @@ export class Workspace { changed = false; for (const s of this.sheets()) { if (dirty.has(s.id)) continue; - for (const title of this.referencedTitles(s.text)) { + for (const title of this.referencedTitles(s.text, s.id)) { const target = this.findSheetByTitle(title); if (target && dirty.has(target.id)) { dirty.add(s.id); @@ -62,6 +63,7 @@ export class Workspace { invalidateAll(): void { this.cache.clear(); + this.parsedRefsCache.clear(); } /** @@ -83,9 +85,16 @@ export class Workspace { return out; } - private referencedTitles(text: string): string[] { + private referencedTitles(text: string, id?: string): string[] { + if (id) { + const cached = this.parsedRefsCache.get(id); + if (cached && cached.text === text) return cached.refs; + } const out: string[] = []; for (const m of text.matchAll(XREF_SCAN_RE)) out.push((m[1] ?? m[2]).trim()); + if (id) { + this.parsedRefsCache.set(id, { text, refs: out }); + } return out; }