@@ -17,73 +17,170 @@ function debugLog(...args: unknown[]) {
1717}
1818
1919// =============================================================================
20- // Parser caching for performance
20+ // Parser Manager (LSP-style background initialization)
2121// =============================================================================
2222
23+ interface ManagedLanguage {
24+ language : unknown
25+ initPromise ?: Promise < unknown >
26+ isInitializing : boolean
27+ lastUsedAt : number
28+ }
29+
2330// eslint-disable-next-line @typescript-eslint/no-explicit-any
2431let parserClass : any = null
25- let parserInitialized = false
26- const languageCache = new Map < string , unknown > ( )
32+ let parserInitPromise : Promise < void > | null = null
33+ const languageCache = new Map < string , ManagedLanguage > ( )
2734
28- async function getParser ( ) {
29- if ( ! parserClass ) {
30- debugLog ( "importing web-tree-sitter (first time)..." )
31- parserClass = ( await import ( "web-tree-sitter" ) ) . default
35+ const LANGUAGE_NAME_MAP : Record < string , string > = {
36+ golang : "go" ,
37+ csharp : "c_sharp" ,
38+ cpp : "cpp" ,
39+ }
40+
41+ const COMMON_LANGUAGES = [
42+ "python" ,
43+ "typescript" ,
44+ "javascript" ,
45+ "tsx" ,
46+ "go" ,
47+ "rust" ,
48+ "java" ,
49+ ]
50+
51+ async function initParserClass ( ) : Promise < void > {
52+ if ( parserClass ) return
53+
54+ if ( parserInitPromise ) {
55+ await parserInitPromise
56+ return
3257 }
3358
34- if ( ! parserInitialized ) {
35- debugLog ( "initializing Parser (first time)..." )
59+ parserInitPromise = ( async ( ) => {
60+ debugLog ( "importing web-tree-sitter..." )
61+ parserClass = ( await import ( "web-tree-sitter" ) ) . default
3662 const treeSitterWasmPath = require . resolve ( "web-tree-sitter/tree-sitter.wasm" )
3763 debugLog ( "wasm path:" , treeSitterWasmPath )
3864 await parserClass . init ( {
3965 locateFile : ( ) => treeSitterWasmPath ,
4066 } )
41- parserInitialized = true
42- debugLog ( "Parser initialized" )
43- }
67+ debugLog ( "Parser class initialized" )
68+ } ) ( )
4469
70+ await parserInitPromise
71+ }
72+
73+ async function getParser ( ) {
74+ await initParserClass ( )
4575 return new parserClass ( )
4676}
4777
48- async function getLanguage ( langName : string ) {
49- if ( languageCache . has ( langName ) ) {
50- debugLog ( "using cached language:" , langName )
51- return languageCache . get ( langName )
52- }
53-
54- debugLog ( "loading language wasm:" , langName )
78+ async function loadLanguageWasm ( langName : string ) : Promise < unknown | null > {
79+ const mappedLang = LANGUAGE_NAME_MAP [ langName ] || langName
5580
56- let wasmPath : string
5781 try {
5882 const wasmModule = await import ( `tree-sitter-wasms/out/tree-sitter-${ langName } .wasm` )
59- wasmPath = wasmModule . default
83+ return wasmModule . default
6084 } catch {
61- const languageMap : Record < string , string > = {
62- golang : "go" ,
63- csharp : "c_sharp" ,
64- cpp : "cpp" ,
85+ if ( mappedLang !== langName ) {
86+ try {
87+ const wasmModule = await import ( `tree-sitter-wasms/out/tree-sitter-${ mappedLang } .wasm` )
88+ return wasmModule . default
89+ } catch {
90+ return null
91+ }
6592 }
66- const mappedLang = languageMap [ langName ] || langName
67- try {
68- const wasmModule = await import ( `tree-sitter-wasms/out/tree-sitter-${ mappedLang } .wasm` )
69- wasmPath = wasmModule . default
70- } catch ( err ) {
71- debugLog ( "failed to load language wasm:" , langName , err )
72- return null
93+ return null
94+ }
95+ }
96+
97+ async function getLanguage ( langName : string ) : Promise < unknown | null > {
98+ const cached = languageCache . get ( langName )
99+
100+ if ( cached ) {
101+ if ( cached . initPromise ) {
102+ await cached . initPromise
73103 }
104+ cached . lastUsedAt = Date . now ( )
105+ debugLog ( "using cached language:" , langName )
106+ return cached . language
74107 }
75108
76- if ( ! parserClass ) {
77- await getParser ( ) // ensure parserClass is initialized
109+ debugLog ( "loading language wasm:" , langName )
110+
111+ const initPromise = ( async ( ) => {
112+ await initParserClass ( )
113+ const wasmPath = await loadLanguageWasm ( langName )
114+ if ( ! wasmPath ) {
115+ debugLog ( "failed to load language wasm:" , langName )
116+ return null
117+ }
118+ return await parserClass ! . Language . load ( wasmPath )
119+ } ) ( )
120+
121+ languageCache . set ( langName , {
122+ language : null as unknown ,
123+ initPromise,
124+ isInitializing : true ,
125+ lastUsedAt : Date . now ( ) ,
126+ } )
127+
128+ const language = await initPromise
129+ const managed = languageCache . get ( langName )
130+ if ( managed ) {
131+ managed . language = language
132+ managed . initPromise = undefined
133+ managed . isInitializing = false
78134 }
79135
80- const language = await parserClass ! . Language . load ( wasmPath )
81- languageCache . set ( langName , language )
82136 debugLog ( "language loaded and cached:" , langName )
83-
84137 return language
85138}
86139
140+ function warmupLanguage ( langName : string ) : void {
141+ if ( languageCache . has ( langName ) ) return
142+
143+ debugLog ( "warming up language (background):" , langName )
144+
145+ const initPromise = ( async ( ) => {
146+ await initParserClass ( )
147+ const wasmPath = await loadLanguageWasm ( langName )
148+ if ( ! wasmPath ) return null
149+ return await parserClass ! . Language . load ( wasmPath )
150+ } ) ( )
151+
152+ languageCache . set ( langName , {
153+ language : null as unknown ,
154+ initPromise,
155+ isInitializing : true ,
156+ lastUsedAt : Date . now ( ) ,
157+ } )
158+
159+ initPromise . then ( ( language ) => {
160+ const managed = languageCache . get ( langName )
161+ if ( managed ) {
162+ managed . language = language
163+ managed . initPromise = undefined
164+ managed . isInitializing = false
165+ debugLog ( "warmup complete:" , langName )
166+ }
167+ } ) . catch ( ( err ) => {
168+ debugLog ( "warmup failed:" , langName , err )
169+ languageCache . delete ( langName )
170+ } )
171+ }
172+
173+ export function warmupCommonLanguages ( ) : void {
174+ debugLog ( "starting background warmup for common languages..." )
175+ initParserClass ( ) . then ( ( ) => {
176+ for ( const lang of COMMON_LANGUAGES ) {
177+ warmupLanguage ( lang )
178+ }
179+ } ) . catch ( ( err ) => {
180+ debugLog ( "warmup initialization failed:" , err )
181+ } )
182+ }
183+
87184// =============================================================================
88185// Public API
89186// =============================================================================
0 commit comments