feat: Move parsing to worker threads for smooth progress animation

Offloads tree-sitter parsing to a dedicated worker thread, keeping the main thread unblocked so shimmer progress animations render smoothly during indexing. Refactors shimmer progress renderer into separate worker for consistent 50ms animation updates. Falls back to in-process parsing when worker compilation unavailable (e.g., tests).
This commit is contained in:
Colby McHenry committed 2026-04-04 11:36:20 -05:00
1 parent ed35d65f4c
commit 64d844c938
7 files changed
+354 -248

No files matched your search

+51
View File
@@ -0,0 +1,51 @@
/**
* Parse Worker
*
* Runs tree-sitter parsing in a separate thread so the main thread
* stays unblocked and the UI animation renders smoothly.
*/
import { parentPort } from 'worker_threads';
import { extractFromSource } from './tree-sitter';
import { detectLanguage, loadGrammarsForLanguages, resetParser } from './grammars';
import type { Language, ExtractionResult } from '../types';
const PARSER_RESET_INTERVAL = 5000;
const parseCounts = new Map<Language, number>();
parentPort!.on('message', async (msg: { type: string; id?: number; filePath?: string; content?: string; languages?: Language[] }) => {
if (msg.type === 'load-grammars') {
await loadGrammarsForLanguages(msg.languages!);
parentPort!.postMessage({ type: 'grammars-loaded' });
} else if (msg.type === 'parse') {
const { id, filePath, content } = msg;
try {
const language = detectLanguage(filePath!);
const result: ExtractionResult = extractFromSource(filePath!, content!, language);
// Periodic parser reset to reclaim WASM heap memory
const count = (parseCounts.get(language) ?? 0) + 1;
parseCounts.set(language, count);
if (count % PARSER_RESET_INTERVAL === 0) {
resetParser(language);
}
parentPort!.postMessage({ type: 'parse-result', id, result });
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
parentPort!.postMessage({
type: 'parse-result',
id,
result: {
nodes: [],
edges: [],
unresolvedReferences: [],
errors: [{ message: `Parse worker error: ${message}`, filePath: filePath!, severity: 'error', code: 'parse_error' }],
durationMs: 0,
} satisfies ExtractionResult,
});
}
} else if (msg.type === 'shutdown') {
parentPort!.postMessage({ type: 'shutdown-ack' });
}
});