|
1 | | -import Parser from 'tree-sitter'; |
| 1 | +import path from 'path'; |
2 | 2 | import type { LanguageMapper } from './types'; |
3 | 3 |
|
4 | 4 | export { type LanguageMapper, type ExtractedSymbol, type ExtractedEdge, type ExtractedImport } from './types'; |
5 | 5 |
|
| 6 | +// web-tree-sitter types (loaded lazily) |
| 7 | +type WTSLanguage = any; |
| 8 | + |
6 | 9 | interface LanguageEntry { |
7 | | - /** tree-sitter Language object */ |
8 | | - grammar: any; |
| 10 | + /** WASM file name, e.g. 'tree-sitter-typescript.wasm' */ |
| 11 | + wasmFile: string; |
| 12 | + /** Loaded Language instance (null until loadLanguage is called) */ |
| 13 | + language: WTSLanguage | null; |
9 | 14 | /** Mapper that extracts symbols, edges, imports from the AST */ |
10 | 15 | mapper: LanguageMapper; |
11 | 16 | } |
12 | 17 |
|
13 | 18 | /** Map from language name (matching file-lang.ts names) to entry. */ |
14 | 19 | const languages = new Map<string, LanguageEntry>(); |
15 | 20 |
|
16 | | -/** Shared parser instance — setLanguage() is cheap, no need for one per language. */ |
17 | | -let _parser: Parser | undefined; |
| 21 | +/** WASM directory containing grammar .wasm files */ |
| 22 | +const WASM_DIR = path.join( |
| 23 | + path.dirname(require.resolve('@vscode/tree-sitter-wasm/package.json')), |
| 24 | + 'wasm', |
| 25 | +); |
| 26 | + |
| 27 | +let _initPromise: Promise<void> | null = null; |
| 28 | + |
| 29 | +/** web-tree-sitter module (lazy loaded) */ |
| 30 | +let _wts: any = null; |
| 31 | + |
| 32 | +/** Initialize the WASM parser runtime. Must be called before parsing. */ |
| 33 | +export async function initParser(): Promise<void> { |
| 34 | + if (_wts) return; |
| 35 | + if (_initPromise) return _initPromise; |
| 36 | + |
| 37 | + _initPromise = (async () => { |
| 38 | + _wts = require('web-tree-sitter'); |
| 39 | + await _wts.Parser.init(); |
| 40 | + })(); |
18 | 41 |
|
19 | | -function getParser(): Parser { |
20 | | - if (!_parser) _parser = new Parser(); |
21 | | - return _parser; |
| 42 | + return _initPromise; |
22 | 43 | } |
23 | 44 |
|
24 | | -/** Register a language. Called at module load time by each language module. */ |
25 | | -export function registerLanguage(name: string, grammar: any, mapper: LanguageMapper): void { |
26 | | - languages.set(name, { grammar, mapper }); |
| 45 | +/** Register a language (sync — only stores metadata). */ |
| 46 | +export function registerLanguage(name: string, wasmFile: string, mapper: LanguageMapper): void { |
| 47 | + languages.set(name, { wasmFile, language: null, mapper }); |
27 | 48 | } |
28 | 49 |
|
29 | | -/** Get a registered language entry. Returns undefined for unsupported languages. */ |
30 | | -export function getLanguageEntry(languageName: string): LanguageEntry | undefined { |
31 | | - return languages.get(languageName); |
| 50 | +/** Load a language WASM if not already loaded. */ |
| 51 | +async function loadLanguage(entry: LanguageEntry): Promise<WTSLanguage> { |
| 52 | + if (entry.language) return entry.language; |
| 53 | + await initParser(); |
| 54 | + const wasmPath = path.join(WASM_DIR, entry.wasmFile); |
| 55 | + entry.language = await _wts.Language.load(wasmPath); |
| 56 | + return entry.language; |
32 | 57 | } |
33 | 58 |
|
34 | | -/** Check if a language is supported. */ |
| 59 | +/** Check if a language is registered. */ |
35 | 60 | export function isLanguageSupported(languageName: string): boolean { |
36 | 61 | return languages.has(languageName); |
37 | 62 | } |
38 | 63 |
|
39 | 64 | /** Parse source code with the appropriate language grammar. Returns root node or null. */ |
40 | | -export function parseSource(code: string, languageName: string): any | null { |
| 65 | +export async function parseSource(code: string, languageName: string): Promise<any | null> { |
41 | 66 | const entry = languages.get(languageName); |
42 | 67 | if (!entry) return null; |
43 | 68 |
|
44 | | - const parser = getParser(); |
45 | | - parser.setLanguage(entry.grammar); |
| 69 | + await initParser(); |
| 70 | + const lang = await loadLanguage(entry); |
| 71 | + const parser = new _wts.Parser(); |
| 72 | + parser.setLanguage(lang); |
46 | 73 | const tree = parser.parse(code); |
47 | | - return tree.rootNode; |
| 74 | + return tree?.rootNode ?? null; |
48 | 75 | } |
49 | 76 |
|
50 | 77 | /** Get the mapper for a language. Returns undefined for unsupported languages. */ |
|
0 commit comments