Skip to content

Commit 189f9e4

Browse files
committed
Lookup symbols in all Technique files in project
1 parent 00c4651 commit 189f9e4

File tree

2 files changed

+101
-13
lines changed

2 files changed

+101
-13
lines changed

src/editor/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ pub(crate) fn run_language_server() {
2222

2323
// extract any initialization parameters passed from the editor.
2424
if let Ok(params) = connection.initialize(capabilities) {
25-
let _params = serde_json::from_value::<InitializeParams>(params).unwrap();
25+
let params = serde_json::from_value::<InitializeParams>(params).unwrap();
2626

2727
info!("Technique Language Server starting on stdin");
2828

29-
let server = server::TechniqueLanguageServer::new();
29+
let server = server::TechniqueLanguageServer::new(params);
3030

3131
if let Err(e) = server.run(connection) {
3232
eprintln!("Server error: {}", e);

src/editor/server.rs

Lines changed: 99 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1+
use ignore::WalkBuilder;
12
use std::collections::HashMap;
2-
use std::path::Path;
3+
use std::fs;
4+
use std::path::{Path, PathBuf};
35

46
use lsp_server::{Connection, Message, Notification, Request, Response};
57
use lsp_types::{
68
Diagnostic, DiagnosticSeverity, DidChangeTextDocumentParams, DidCloseTextDocumentParams,
79
DidOpenTextDocumentParams, DidSaveTextDocumentParams, DocumentFormattingParams,
8-
DocumentSymbolParams, DocumentSymbolResponse, InitializedParams, Location, Position,
9-
PublishDiagnosticsParams, Range, SymbolInformation, SymbolKind, TextEdit, Uri,
10-
WorkspaceSymbolParams,
10+
DocumentSymbolParams, DocumentSymbolResponse, InitializeParams, InitializedParams, Location,
11+
Position, PublishDiagnosticsParams, Range, SymbolInformation, SymbolKind, TextEdit, Uri,
12+
WorkspaceFolder, WorkspaceSymbolParams,
1113
};
1214
use serde_json::{from_value, to_value, Value};
1315
use technique::formatting::Identity;
@@ -22,12 +24,16 @@ use crate::problem::{calculate_column_number, calculate_line_number};
2224
pub struct TechniqueLanguageServer {
2325
/// Map from URI to document content
2426
documents: HashMap<Uri, String>,
27+
/// Workspace folders provided during initialization
28+
folders: Option<Vec<WorkspaceFolder>>,
2529
}
2630

2731
impl TechniqueLanguageServer {
28-
pub fn new() -> Self {
32+
pub fn new(params: InitializeParams) -> Self {
33+
let folders = params.workspace_folders;
2934
Self {
3035
documents: HashMap::new(),
36+
folders,
3137
}
3238
}
3339

@@ -400,10 +406,16 @@ impl TechniqueLanguageServer {
400406
.to_lowercase();
401407
debug!("Workspace symbol request: query={:?}", query);
402408

403-
let mut all_symbols = Vec::new();
409+
let mut result = Vec::new();
410+
let mut searched = std::collections::HashSet::new();
404411

405-
// Search through all open documents
412+
// First, search through documents we know are open (they have the
413+
// most up to date, possibly modified, content)
406414
for (uri, content) in &self.documents {
415+
searched.insert(PathBuf::from(
416+
uri.path()
417+
.as_str(),
418+
));
407419
let path = Path::new(
408420
uri.path()
409421
.as_str(),
@@ -421,13 +433,46 @@ impl TechniqueLanguageServer {
421433
.to_lowercase()
422434
.contains(&query)
423435
{
424-
all_symbols.push(symbol);
436+
result.push(symbol);
437+
}
438+
}
439+
}
440+
}
441+
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) {
448+
continue;
449+
}
450+
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+
}
425470
}
426471
}
427472
}
428473
}
429474

430-
Ok(Some(all_symbols))
475+
Ok(Some(result))
431476
}
432477

433478
fn extract_symbols_from_document(
@@ -528,6 +573,49 @@ impl TechniqueLanguageServer {
528573
Ok(())
529574
}
530575

576+
/// Find all Technique files in the workspace so they can be scanned for
577+
/// procedure names and other symbols.
578+
fn find_technique_files(&self) -> Vec<PathBuf> {
579+
let mut paths = Vec::new();
580+
581+
if let Some(ref folders) = self.folders {
582+
for folder in folders {
583+
let raw = folder
584+
.uri
585+
.as_str();
586+
587+
// Strip file:// from path if present
588+
let filename = if raw.starts_with("file://") {
589+
&raw[7..]
590+
} else {
591+
raw
592+
};
593+
594+
let path = Path::new(filename);
595+
if path.exists() {
596+
// Use the ignore crate's WalkBuilder to respect
597+
// files excluded by .gitignore
598+
let walker = WalkBuilder::new(&path).build();
599+
600+
for entry in walker {
601+
if let Ok(entry) = entry {
602+
let path = entry.path();
603+
if path.is_file() {
604+
if let Some(ext) = path.extension() {
605+
if ext == "tq" {
606+
paths.push(path.to_path_buf());
607+
}
608+
}
609+
}
610+
}
611+
}
612+
}
613+
}
614+
}
615+
616+
paths
617+
}
618+
531619
fn convert_parsing_errors(
532620
&self,
533621
_uri: &Uri,
@@ -694,14 +782,14 @@ impl TechniqueLanguageServer {
694782

695783
/// Calculate the byte offset of a substring within a parent string using
696784
/// pointer arithmetic.
697-
///
785+
///
698786
/// Returns None if the substring is not actually part of the parent string,
699787
/// checking first to see if the substring pointer is actually within the
700788
/// bounds of the parent string.
701789
fn calculate_slice_offset(parent: &str, substring: &str) -> Option<usize> {
702790
let parent_ptr = parent.as_ptr() as usize;
703791
let substring_ptr = substring.as_ptr() as usize;
704-
792+
705793
if substring_ptr >= parent_ptr && substring_ptr < parent_ptr + parent.len() {
706794
Some(substring_ptr - parent_ptr)
707795
} else {

0 commit comments

Comments
 (0)