@@ -26,15 +26,22 @@ pub struct TechniqueLanguageServer {
2626 documents : HashMap < Uri , String > ,
2727 /// Workspace folders provided during initialization
2828 folders : Option < Vec < WorkspaceFolder > > ,
29+ /// Cache of symbols from all Technique files in the workspace
30+ cache : HashMap < PathBuf , Vec < SymbolInformation > > ,
2931}
3032
3133impl TechniqueLanguageServer {
3234 pub fn new ( params : InitializeParams ) -> Self {
3335 let folders = params. workspace_folders ;
34- Self {
36+ let mut server = Self {
3537 documents : HashMap :: new ( ) ,
3638 folders,
37- }
39+ cache : HashMap :: new ( ) ,
40+ } ;
41+
42+ // Populate symbol cache
43+ server. rebuild_symbol_cache ( ) ;
44+ server
3845 }
3946
4047 /// Main server loop that handles incoming LSP messages
@@ -227,6 +234,8 @@ impl TechniqueLanguageServer {
227234 self . documents
228235 . insert ( uri. clone ( ) , content. clone ( ) ) ;
229236
237+ self . update_symbol_cache ( & uri, & content) ;
238+
230239 self . parse_and_report ( uri, content, sender) ?;
231240 Ok ( ( ) )
232241 }
@@ -255,6 +264,8 @@ impl TechniqueLanguageServer {
255264 self . documents
256265 . insert ( uri. clone ( ) , content. clone ( ) ) ;
257266
267+ self . update_symbol_cache ( & uri, & content) ;
268+
258269 self . parse_and_report ( uri, content, sender) ?;
259270 }
260271 Ok ( ( ) )
@@ -300,6 +311,10 @@ impl TechniqueLanguageServer {
300311 self . documents
301312 . remove ( & uri) ;
302313
314+ // We intentionally do NOT remove the file from symbol cache here just
315+ // because the user closed the file; we've already gone to all the
316+ // trouble of of calculating the symbols, so we keep that work.
317+
303318 // Clear diagnostics for closed document
304319 self . publish_diagnostics ( uri, vec ! [ ] , sender) ?;
305320 Ok ( ( ) )
@@ -393,7 +408,7 @@ impl TechniqueLanguageServer {
393408 }
394409 } ;
395410
396- let symbols = self . extract_symbols_from_document ( & uri , content, & document) ;
411+ let symbols = self . extract_symbols_from_document ( path , content, & document) ;
397412 Ok ( DocumentSymbolResponse :: Flat ( symbols) )
398413 }
399414
@@ -423,7 +438,7 @@ impl TechniqueLanguageServer {
423438
424439 // Try to parse each document
425440 if let Ok ( document) = parsing:: parse_with_recovery ( & path, content) {
426- let symbols = self . extract_symbols_from_document ( uri , content, & document) ;
441+ let symbols = self . extract_symbols_from_document ( & path , content, & document) ;
427442
428443 // Filter symbols by query
429444 for symbol in symbols {
@@ -439,35 +454,23 @@ impl TechniqueLanguageServer {
439454 }
440455 }
441456
442- // Then search all Technique files in the workspace that aren't
443- // already open
444- let paths = self . find_technique_files ( ) ;
445- for path in paths {
446- // Skip files we've already processed
447- if searched. contains ( & path) {
457+ // Then search cached symbols from workspace files that aren't open,
458+ // skip files we've already processed (beacuse they are open!)
459+ for ( path, symbols) in & self . cache {
460+ //
461+ if searched. contains ( path) {
448462 continue ;
449463 }
450464
451- // Read and parse the file
452- if let Ok ( content) = fs:: read_to_string ( & path) {
453- if let Ok ( document) = parsing:: parse_with_recovery ( & path, & content) {
454- // Create a URI for the file
455- let uri: Uri = format ! ( "file://{}" , path. display( ) )
456- . parse ( )
457- . unwrap ( ) ;
458- let symbols = self . extract_symbols_from_document ( & uri, & content, & document) ;
459-
460- // Filter symbols by query
461- for symbol in symbols {
462- if query. is_empty ( )
463- || symbol
464- . name
465- . to_lowercase ( )
466- . contains ( & query)
467- {
468- result. push ( symbol) ;
469- }
470- }
465+ // Filter symbols by query
466+ for symbol in symbols {
467+ if query. is_empty ( )
468+ || symbol
469+ . name
470+ . to_lowercase ( )
471+ . contains ( & query)
472+ {
473+ result. push ( symbol. clone ( ) ) ;
471474 }
472475 }
473476 }
@@ -477,7 +480,7 @@ impl TechniqueLanguageServer {
477480
478481 fn extract_symbols_from_document (
479482 & self ,
480- uri : & Uri ,
483+ path : & Path ,
481484 content : & str ,
482485 document : & Document ,
483486 ) -> Vec < SymbolInformation > {
@@ -591,6 +594,45 @@ impl TechniqueLanguageServer {
591594 Ok ( ( ) )
592595 }
593596
597+ /// Rebuild the symbol cache by parsing all Technique files in the workspace
598+ fn rebuild_symbol_cache ( & mut self ) {
599+ debug ! ( "Rebuilding symbol cache" ) ;
600+ self . cache
601+ . clear ( ) ;
602+
603+ let paths = self . find_technique_files ( ) ;
604+
605+ for path in paths {
606+ debug ! ( "Caching symbols from: {:?}" , path) ;
607+ if let Ok ( content) = fs:: read_to_string ( & path) {
608+ if let Ok ( document) = parsing:: parse_with_recovery ( & path, & content) {
609+ let symbols = self . extract_symbols_from_document ( & path, & content, & document) ;
610+ debug ! ( "Found {} symbols in {:?}" , symbols. len( ) , path) ;
611+ self . cache
612+ . insert ( path, symbols) ;
613+ } else {
614+ debug ! ( "Failed to parse {:?}" , path) ;
615+ }
616+ } else {
617+ debug ! ( "Failed to read {:?}" , path) ;
618+ }
619+ }
620+ }
621+
622+ /// Update the symbol cache for a specific file
623+ fn update_symbol_cache ( & mut self , uri : & Uri , content : & str ) {
624+ let path = PathBuf :: from (
625+ uri. path ( )
626+ . as_str ( ) ,
627+ ) ;
628+
629+ if let Ok ( document) = parsing:: parse_with_recovery ( & path, content) {
630+ let symbols = self . extract_symbols_from_document ( & path, content, & document) ;
631+ self . cache
632+ . insert ( path, symbols) ;
633+ }
634+ }
635+
594636 /// Find all Technique files in the workspace so they can be scanned for
595637 /// procedure names and other symbols.
596638 fn find_technique_files ( & self ) -> Vec < PathBuf > {
@@ -611,6 +653,8 @@ impl TechniqueLanguageServer {
611653
612654 let path = Path :: new ( filename) ;
613655 if path. exists ( ) {
656+ debug ! ( "Looking for Technique files in: {:?}" , path) ;
657+
614658 // Use the ignore crate's WalkBuilder to respect
615659 // files excluded by .gitignore
616660 let walker = WalkBuilder :: new ( & path) . build ( ) ;
0 commit comments