@@ -181,20 +181,40 @@ function resolveAction(action: string): string {
181181 return action ;
182182}
183183
184+ // ─── Lazy Set cache for O(1) keyword/operator lookups ────────────────────────
185+
186+ const rulesSetCache = new WeakMap < MonarchLanguageRules , Map < string , Set < string > > > ( ) ;
187+
184188/**
185189 * Resolve a `cases` lookup: check if the matched text is in a named array.
190+ *
191+ * Named arrays (keywords, bsonConstructors, shellCommands, operators) are
192+ * lazily converted to Sets on first lookup so subsequent `.has()` calls are O(1).
186193 */
187194function resolveCases ( matchedText : string , cases : Record < string , string > , rules : MonarchLanguageRules ) : string {
195+ let arrayMap = rulesSetCache . get ( rules ) ;
196+ if ( ! arrayMap ) {
197+ arrayMap = new Map ( ) ;
198+ rulesSetCache . set ( rules , arrayMap ) ;
199+ }
200+
188201 for ( const [ key , tokenType ] of Object . entries ( cases ) ) {
189202 if ( key === '@default' ) {
190203 continue ;
191204 }
192205
193- // Look up the named array in the rules object
194206 const arrayName = key . startsWith ( '@' ) ? key . slice ( 1 ) : key ;
195- const array = rules [ arrayName as keyof MonarchLanguageRules ] ;
196207
197- if ( Array . isArray ( array ) && ( array as string [ ] ) . includes ( matchedText ) ) {
208+ let set = arrayMap . get ( arrayName ) ;
209+ if ( ! set ) {
210+ const array = rules [ arrayName as keyof MonarchLanguageRules ] ;
211+ if ( Array . isArray ( array ) ) {
212+ set = new Set ( array as readonly string [ ] ) ;
213+ arrayMap . set ( arrayName , set ) ;
214+ }
215+ }
216+
217+ if ( set ?. has ( matchedText ) ) {
198218 return resolveAction ( tokenType ) ;
199219 }
200220 }
0 commit comments