Skip to content

Commit 4073389

Browse files
committed
Add symbol cache to support project-wide search
1 parent db5e0aa commit 4073389

File tree

1 file changed

+75
-31
lines changed

1 file changed

+75
-31
lines changed

src/editor/server.rs

Lines changed: 75 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -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

3133
impl 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

Comments
 (0)