@@ -53,6 +53,77 @@ define(function (require, exports, module) {
5353 "F12"
5454 ] ;
5555
56+ // Optimized data structures for fast snippet lookups
57+ let snippetsByLanguage = new Map ( ) ;
58+ let snippetsByAbbreviation = new Map ( ) ;
59+ let allSnippetsOptimized = [ ] ;
60+
61+ /**
62+ * Preprocesses a snippet to add optimized lookup properties
63+ * @param {Object } snippet - The original snippet object
64+ * @returns {Object } - The snippet with added optimization properties
65+ */
66+ function preprocessSnippet ( snippet ) {
67+ const optimizedSnippet = { ...snippet } ;
68+
69+ // pre-compute lowercase abbreviation for faster matching
70+ optimizedSnippet . abbreviationLower = snippet . abbreviation . toLowerCase ( ) ;
71+
72+ // parse and create a Set of supported extensions for O(1) lookup
73+ if ( snippet . fileExtension . toLowerCase ( ) === "all" ) {
74+ optimizedSnippet . supportedLangSet = new Set ( [ "all" ] ) ;
75+ optimizedSnippet . supportsAllLanguages = true ;
76+ } else {
77+ const extensions = snippet . fileExtension
78+ . toLowerCase ( )
79+ . split ( "," )
80+ . map ( ext => ext . trim ( ) )
81+ . filter ( ext => ext ) ;
82+ optimizedSnippet . supportedLangSet = new Set ( extensions ) ;
83+ optimizedSnippet . supportsAllLanguages = false ;
84+ }
85+
86+ return optimizedSnippet ;
87+ }
88+
89+ /**
90+ * Rebuilds optimized data structures from the current snippet list
91+ * we call this function whenever snippets are loaded, added, modified, or deleted
92+ * i.e. whenever the snippetList is updated
93+ */
94+ function rebuildOptimizedStructures ( ) {
95+ // clear existing structures
96+ snippetsByLanguage . clear ( ) ;
97+ snippetsByAbbreviation . clear ( ) ;
98+ allSnippetsOptimized . length = 0 ;
99+
100+ // Process each snippet
101+ Global . SnippetHintsList . forEach ( snippet => {
102+ const optimizedSnippet = preprocessSnippet ( snippet ) ;
103+ allSnippetsOptimized . push ( optimizedSnippet ) ;
104+
105+ // Index by abbreviation (lowercase) for exact matches
106+ snippetsByAbbreviation . set ( optimizedSnippet . abbreviationLower , optimizedSnippet ) ;
107+
108+ // Index by supported languages/extensions
109+ if ( optimizedSnippet . supportsAllLanguages ) {
110+ // Add to a special "all" key for universal snippets
111+ if ( ! snippetsByLanguage . has ( "all" ) ) {
112+ snippetsByLanguage . set ( "all" , new Set ( ) ) ;
113+ }
114+ snippetsByLanguage . get ( "all" ) . add ( optimizedSnippet ) ;
115+ } else {
116+ // Add to each supported extension
117+ optimizedSnippet . supportedLangSet . forEach ( ext => {
118+ if ( ! snippetsByLanguage . has ( ext ) ) {
119+ snippetsByLanguage . set ( ext , new Set ( ) ) ;
120+ }
121+ snippetsByLanguage . get ( ext ) . add ( optimizedSnippet ) ;
122+ } ) ;
123+ }
124+ } ) ;
125+ }
126+
56127 /**
57128 * map the language IDs to their file extensions for snippet matching
58129 * this is needed because we expect the user to enter file extensions and not the file type inside the input field
@@ -237,12 +308,37 @@ define(function (require, exports, module) {
237308 * Checks if a snippet is supported in the given language context
238309 * Falls back to file extension matching if language mapping isn't available
239310 *
240- * @param {Object } snippet - The snippet object
311+ * @param {Object } snippet - The snippet object (optimized or regular)
241312 * @param {string|null } languageContext - The current language context
242313 * @param {Editor } editor - The editor instance for fallback
243314 * @returns {boolean } - True if the snippet is supported
244315 */
245316 function isSnippetSupportedInLanguageContext ( snippet , languageContext , editor ) {
317+ // first we need to try the optimizedSnippet
318+ if ( snippet . supportsAllLanguages !== undefined ) {
319+ if ( snippet . supportsAllLanguages ) {
320+ return true ;
321+ }
322+
323+ if ( languageContext ) {
324+ const effectiveExtension = mapLanguageToExtension ( languageContext ) ;
325+ // if we have a proper mapping (starts with .), use language context matching
326+ if ( effectiveExtension . startsWith ( "." ) ) {
327+ return snippet . supportedLangSet . has ( effectiveExtension ) ;
328+ }
329+ }
330+
331+ // this is just a fallback if language context matching failed
332+ // file extension matching
333+ if ( editor ) {
334+ const fileExtension = getCurrentFileExtension ( editor ) ;
335+ return isSnippetSupportedInFile ( snippet , fileExtension ) ;
336+ }
337+
338+ return false ;
339+ }
340+
341+ // for non-optimized snippets
246342 if ( snippet . fileExtension . toLowerCase ( ) === "all" ) {
247343 return true ;
248344 }
@@ -303,12 +399,12 @@ define(function (require, exports, module) {
303399 const queryLower = query . toLowerCase ( ) ;
304400 const languageContext = getCurrentLanguageContext ( editor ) ;
305401
306- return Global . SnippetHintsList . some ( ( snippet ) => {
307- if ( snippet . abbreviation . toLowerCase ( ) === queryLower ) {
308- return isSnippetSupportedInLanguageContext ( snippet , languageContext , editor ) ;
309- }
310- return false ;
311- } ) ;
402+ const snippet = snippetsByAbbreviation . get ( queryLower ) ;
403+ if ( snippet ) {
404+ return isSnippetSupportedInLanguageContext ( snippet , languageContext , editor ) ;
405+ }
406+
407+ return false ;
312408 }
313409
314410 /**
@@ -321,21 +417,41 @@ define(function (require, exports, module) {
321417 const queryLower = query . toLowerCase ( ) ;
322418 const languageContext = getCurrentLanguageContext ( editor ) ;
323419
324- const matchingSnippets = Global . SnippetHintsList . filter ( ( snippet ) => {
325- if ( snippet . abbreviation . toLowerCase ( ) . startsWith ( queryLower ) ) {
326- return isSnippetSupportedInLanguageContext ( snippet , languageContext , editor ) ;
420+ // Get the candidate snippets for the current language/extension
421+ let candidateSnippets = new Set ( ) ;
422+
423+ // Add universal snippets (support "all" languages)
424+ const universalSnippets = snippetsByLanguage . get ( "all" ) ;
425+ if ( universalSnippets ) {
426+ universalSnippets . forEach ( snippet => candidateSnippets . add ( snippet ) ) ;
427+ }
428+
429+ // Add language-specific snippets
430+ if ( languageContext ) {
431+ const effectiveExtension = mapLanguageToExtension ( languageContext ) ;
432+ if ( effectiveExtension . startsWith ( "." ) ) {
433+ const languageSnippets = snippetsByLanguage . get ( effectiveExtension ) ;
434+ if ( languageSnippets ) {
435+ languageSnippets . forEach ( snippet => candidateSnippets . add ( snippet ) ) ;
436+ }
327437 }
328- return false ;
438+ }
439+
440+ // Fallback: if we can't determine language, check all snippets
441+ if ( candidateSnippets . size === 0 ) {
442+ candidateSnippets = new Set ( allSnippetsOptimized ) ;
443+ }
444+
445+ // Filter candidates by prefix match using pre-computed lowercase abbreviations
446+ const matchingSnippets = Array . from ( candidateSnippets ) . filter ( ( snippet ) => {
447+ return snippet . abbreviationLower . startsWith ( queryLower ) ;
329448 } ) ;
330449
331450 // sort snippets so that the exact matches will appear over the partial matches
332451 return matchingSnippets . sort ( ( a , b ) => {
333- const aLower = a . abbreviation . toLowerCase ( ) ;
334- const bLower = b . abbreviation . toLowerCase ( ) ;
335-
336452 // check if either is an exact match
337- const aExact = aLower === queryLower ;
338- const bExact = bLower === queryLower ;
453+ const aExact = a . abbreviationLower === queryLower ;
454+ const bExact = b . abbreviationLower === queryLower ;
339455
340456 // because exact matches appear first
341457 if ( aExact && ! bExact ) {
@@ -345,7 +461,7 @@ define(function (require, exports, module) {
345461 return 1 ;
346462 }
347463
348- return aLower . localeCompare ( bLower ) ;
464+ return a . abbreviationLower . localeCompare ( b . abbreviationLower ) ;
349465 } ) ;
350466 }
351467
@@ -834,6 +950,7 @@ define(function (require, exports, module) {
834950 exports . getCurrentLanguageContext = getCurrentLanguageContext ;
835951 exports . getCurrentFileExtension = getCurrentFileExtension ;
836952 exports . mapLanguageToExtension = mapLanguageToExtension ;
953+ exports . rebuildOptimizedStructures = rebuildOptimizedStructures ;
837954 exports . isSnippetSupportedInLanguageContext = isSnippetSupportedInLanguageContext ;
838955 exports . isSnippetSupportedInFile = isSnippetSupportedInFile ;
839956 exports . hasExactMatchingSnippet = hasExactMatchingSnippet ;
0 commit comments