Add refactoring and go-to-definition for multifile

This commit is contained in:
Sofia 2025-08-04 00:07:31 +03:00
parent 109fedb624
commit 766a853b48

View File

@ -27,7 +27,7 @@ mod analysis;
struct Backend { struct Backend {
client: Client, client: Client,
analysis: DashMap<PathBuf, StaticAnalysis>, analysis: DashMap<PathBuf, StaticAnalysis>,
module_to_url: DashMap<SourceModuleId, PathBuf>, module_to_path: DashMap<SourceModuleId, PathBuf>,
path_to_module: DashMap<PathBuf, SourceModuleId>, path_to_module: DashMap<PathBuf, SourceModuleId>,
module_id_counter: Mutex<SourceModuleId>, module_id_counter: Mutex<SourceModuleId>,
} }
@ -80,6 +80,8 @@ impl LanguageServer for Backend {
}, },
)), )),
references_provider: Some(OneOf::Left(true)), references_provider: Some(OneOf::Left(true)),
definition_provider: Some(OneOf::Left(true)),
rename_provider: Some(OneOf::Left(true)),
..Default::default() ..Default::default()
}; };
Ok(InitializeResult { Ok(InitializeResult {
@ -250,34 +252,34 @@ impl LanguageServer for Backend {
}))) })))
} }
// async fn goto_definition(&self, params: GotoDefinitionParams) -> jsonrpc::Result<Option<GotoDefinitionResponse>> { async fn goto_definition(&self, params: GotoDefinitionParams) -> jsonrpc::Result<Option<GotoDefinitionResponse>> {
// let path = PathBuf::from(params.text_document_position_params.text_document.uri.path()); let path = PathBuf::from(params.text_document_position_params.text_document.uri.path());
// let analysis = self.analysis.get(&path); let analysis = self.analysis.get(&path);
// let position = params.text_document_position_params.position; let position = params.text_document_position_params.position;
// if let Some(analysis) = &analysis { if let Some(analysis) = &analysis {
// let token = analysis.tokens.iter().enumerate().find(|(_, tok)| { let token = analysis.tokens.iter().enumerate().find(|(_, tok)| {
// tok.position.1 == position.line + 1 tok.position.1 == position.line + 1
// && (tok.position.0 <= position.character + 1 && (tok.position.0 <= position.character + 1
// && (tok.position.0 + tok.token.len() as u32) > position.character + 1) && (tok.position.0 + tok.token.len() as u32) > position.character + 1)
// }); });
// if let Some(token) = token { if let Some(token) = token {
// if let Some((module_id, def_token)) = analysis.find_definition(token.0, &self.state_map()) { if let Some((module_id, def_token)) = analysis.find_definition(token.0, &self.state_map()) {
// return if let Some(path) = self.module_to_url.get(&module_id) { return if let Some(path) = self.module_to_path.get(&module_id) {
// Ok(Some(GotoDefinitionResponse::Scalar(lsp_types::Location { Ok(Some(GotoDefinitionResponse::Scalar(lsp_types::Location {
// uri: Url::from_file_path(path.value()).unwrap(), uri: Url::from_file_path(path.value()).unwrap(),
// range: token_to_range(def_token), range: token_to_range(def_token),
// }))) })))
// } else { } else {
// Ok(None) Ok(None)
// }; };
// } }
// } }
// }; };
// Ok(None) Ok(None)
// } }
async fn references(&self, params: ReferenceParams) -> jsonrpc::Result<Option<Vec<Location>>> { async fn references(&self, params: ReferenceParams) -> jsonrpc::Result<Option<Vec<Location>>> {
let path = PathBuf::from(params.text_document_position.text_document.uri.path()); let path = PathBuf::from(params.text_document_position.text_document.uri.path());
@ -296,7 +298,7 @@ impl LanguageServer for Backend {
let mut locations = Vec::new(); let mut locations = Vec::new();
if let Some(reference_tokens) = reference_tokens { if let Some(reference_tokens) = reference_tokens {
for (module_id, symbol_idx) in reference_tokens { for (module_id, symbol_idx) in reference_tokens {
if let Some(path) = self.module_to_url.get(&module_id) { if let Some(path) = self.module_to_path.get(&module_id) {
let url = Url::from_file_path(path.value()).unwrap(); let url = Url::from_file_path(path.value()).unwrap();
if let Some(inner_analysis) = self.analysis.get(path.value()) { if let Some(inner_analysis) = self.analysis.get(path.value()) {
if let Some(token_idx) = inner_analysis.state.symbol_to_token.get(&symbol_idx) { if let Some(token_idx) = inner_analysis.state.symbol_to_token.get(&symbol_idx) {
@ -320,48 +322,59 @@ impl LanguageServer for Backend {
} }
} }
// async fn rename(&self, params: RenameParams) -> jsonrpc::Result<Option<WorkspaceEdit>> { async fn rename(&self, params: RenameParams) -> jsonrpc::Result<Option<WorkspaceEdit>> {
// let path = PathBuf::from(params.text_document_position.text_document.uri.path()); let path = PathBuf::from(params.text_document_position.text_document.uri.path());
// let analysis = self.analysis.get(&path); let analysis = self.analysis.get(&path);
// let position = params.text_document_position.position; let position = params.text_document_position.position;
// if let Some(analysis) = &analysis { if let Some(analysis) = &analysis {
// let token = analysis.tokens.iter().enumerate().find(|(_, tok)| { let token = analysis.tokens.iter().enumerate().find(|(_, tok)| {
// tok.position.1 == position.line + 1 tok.position.1 == position.line + 1
// && (tok.position.0 <= position.character + 1 && (tok.position.0 <= position.character + 1
// && (tok.position.0 + tok.token.len() as u32) > position.character + 1) && (tok.position.0 + tok.token.len() as u32) > position.character + 1)
// }); });
// if let Some(token) = token { if let Some(token) = token {
// let tokens = analysis.find_references(token.0, &self.state_map()).map(|symbols| { let symbols = analysis.find_references(token.0, &self.state_map());
// symbols let mut changes: HashMap<Url, Vec<TextEdit>> = HashMap::new();
// .iter() if let Some(symbols) = symbols {
// .map(|symbol_id| analysis.state.symbol_to_token.get(&symbol_id).cloned().unwrap()) for (module_id, symbol_id) in symbols {
// .collect::<Vec<_>>() let path = self.module_to_path.get(&module_id);
// }); if let Some(path) = path {
// let mut edits = Vec::new(); let url = Url::from_file_path(path.value()).unwrap();
// if let Some(tokens) = tokens { let analysis = self.analysis.get(&path.clone());
// for token_idx in tokens {
// let token = analysis.tokens.get(token_idx).unwrap(); if let Some(analysis) = analysis {
// edits.push(TextEdit { if let Some(token_idx) = analysis.state.symbol_to_token.get(&symbol_id) {
// range: token_to_range(token), let token = analysis.tokens.get(*token_idx).unwrap();
// new_text: params.new_name.clone(),
// }); // edits = changes.get(k)
// } let edit = TextEdit {
// } range: token_to_range(token),
// let mut changes = HashMap::new(); new_text: params.new_name.clone(),
// changes.insert(params.text_document_position.text_document.uri, edits); };
// Ok(Some(WorkspaceEdit { if let Some(edits) = changes.get_mut(&url) {
// changes: Some(changes), edits.push(edit);
// document_changes: None, } else {
// change_annotations: None, changes.insert(url, vec![edit]);
// })) }
// } else { }
// Ok(None) }
// } }
// } else { }
// Ok(None) }
// }
// } Ok(Some(WorkspaceEdit {
changes: Some(changes),
document_changes: None,
change_annotations: None,
}))
} else {
Ok(None)
}
} else {
Ok(None)
}
}
} }
fn token_to_range(token: &FullToken) -> lsp_types::Range { fn token_to_range(token: &FullToken) -> lsp_types::Range {
@ -409,7 +422,7 @@ impl Backend {
let module_id = lock.increment(); let module_id = lock.increment();
drop(lock); drop(lock);
self.path_to_module.insert(file_path.clone(), module_id); self.path_to_module.insert(file_path.clone(), module_id);
self.module_to_url.insert(module_id, file_path.clone()); self.module_to_path.insert(module_id, file_path.clone());
module_id module_id
}; };
@ -510,7 +523,7 @@ async fn main() {
let (service, socket) = LspService::new(|client| Backend { let (service, socket) = LspService::new(|client| Backend {
client, client,
analysis: DashMap::new(), analysis: DashMap::new(),
module_to_url: DashMap::new(), module_to_path: DashMap::new(),
path_to_module: DashMap::new(), path_to_module: DashMap::new(),
module_id_counter: Mutex::new(SourceModuleId(0)), module_id_counter: Mutex::new(SourceModuleId(0)),
}); });