mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-16 01:38:13 +00:00
Merge #143
143: Implement Find All References and Rename for local variables r=matklad a=kjeremy Expose `find_all_refs` in `Analysis`. This currently only works for local variables. Use this in the LSP to implement find all references and rename. Co-authored-by: Jeremy A. Kolb <jkolb@ara.com>
This commit is contained in:
commit
2ded93a78a
10 changed files with 174 additions and 15 deletions
|
@ -257,6 +257,38 @@ impl AnalysisImpl {
|
|||
vec![]
|
||||
}
|
||||
|
||||
pub fn find_all_refs(&self, file_id: FileId, offset: TextUnit, _token: &JobToken) -> Vec<(FileId, TextRange)> {
|
||||
let root = self.root(file_id);
|
||||
let file = root.syntax(file_id);
|
||||
let syntax = file.syntax();
|
||||
|
||||
let mut ret = vec![];
|
||||
|
||||
// Find the symbol we are looking for
|
||||
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, offset) {
|
||||
|
||||
// We are only handing local references for now
|
||||
if let Some(resolved) = resolve_local_name(&file, offset, name_ref) {
|
||||
|
||||
ret.push((file_id, resolved.1));
|
||||
|
||||
if let Some(fn_def) = find_node_at_offset::<ast::FnDef>(syntax, offset) {
|
||||
|
||||
let refs : Vec<_> = fn_def.syntax().descendants()
|
||||
.filter_map(ast::NameRef::cast)
|
||||
.filter(|n: &ast::NameRef| resolve_local_name(&file, n.syntax().range().start(), *n) == Some(resolved.clone()))
|
||||
.collect();
|
||||
|
||||
for r in refs {
|
||||
ret.push((file_id, r.syntax().range()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> {
|
||||
let root = self.root(file_id);
|
||||
let module_tree = root.module_tree();
|
||||
|
|
|
@ -217,6 +217,9 @@ impl Analysis {
|
|||
self.imp
|
||||
.approximately_resolve_symbol(file_id, offset, token)
|
||||
}
|
||||
pub fn find_all_refs(&self, file_id: FileId, offset: TextUnit, token: &JobToken) -> Vec<(FileId, TextRange)> {
|
||||
self.imp.find_all_refs(file_id, offset, token)
|
||||
}
|
||||
pub fn parent_module(&self, file_id: FileId) -> Vec<(FileId, FileSymbol)> {
|
||||
self.imp.parent_module(file_id)
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ use std::sync::Arc;
|
|||
use ra_analysis::{
|
||||
Analysis, AnalysisHost, CrateGraph, CrateId, FileId, FileResolver, FnDescriptor, JobHandle,
|
||||
};
|
||||
use ra_syntax::TextRange;
|
||||
|
||||
use relative_path::{RelativePath, RelativePathBuf};
|
||||
use rustc_hash::FxHashMap;
|
||||
use test_utils::{assert_eq_dbg, extract_offset};
|
||||
|
@ -225,3 +227,43 @@ fn bar() {
|
|||
assert_eq!(desc.ret_type, None);
|
||||
assert_eq!(param, Some(1));
|
||||
}
|
||||
|
||||
fn get_all_refs(text: &str) -> Vec<(FileId, TextRange)> {
|
||||
let (offset, code) = extract_offset(text);
|
||||
let code = code.as_str();
|
||||
|
||||
let (_handle, token) = JobHandle::new();
|
||||
let snap = analysis(&[("/lib.rs", code)]);
|
||||
|
||||
snap.find_all_refs(FileId(1), offset, &token)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_all_refs_for_local() {
|
||||
let code = r#"
|
||||
fn main() {
|
||||
let mut i = 1;
|
||||
let j = 1;
|
||||
i = i<|> + j;
|
||||
|
||||
{
|
||||
i = 0;
|
||||
}
|
||||
|
||||
i = 5;
|
||||
}"#;
|
||||
|
||||
let refs = get_all_refs(code);
|
||||
assert_eq!(refs.len(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_all_refs_for_param_inside() {
|
||||
let code = r#"
|
||||
fn foo(i : u32) -> u32 {
|
||||
i<|>
|
||||
}"#;
|
||||
|
||||
let refs = get_all_refs(code);
|
||||
assert_eq!(refs.len(), 2);
|
||||
}
|
|
@ -270,7 +270,6 @@ pub fn resolve_local_name<'a>(
|
|||
.filter(|entry| shadowed.insert(entry.name()))
|
||||
.filter(|entry| entry.name() == name_ref.text())
|
||||
.nth(0);
|
||||
eprintln!("ret = {:?}", ret);
|
||||
ret
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use languageserver_types::{
|
|||
CodeActionProviderCapability, CompletionOptions, DocumentOnTypeFormattingOptions,
|
||||
ExecuteCommandOptions, FoldingRangeProviderCapability, ServerCapabilities,
|
||||
SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind,
|
||||
TextDocumentSyncOptions,
|
||||
TextDocumentSyncOptions, RenameProviderCapability, RenameOptions
|
||||
};
|
||||
|
||||
pub fn server_capabilities() -> ServerCapabilities {
|
||||
|
@ -27,7 +27,7 @@ pub fn server_capabilities() -> ServerCapabilities {
|
|||
definition_provider: Some(true),
|
||||
type_definition_provider: None,
|
||||
implementation_provider: None,
|
||||
references_provider: None,
|
||||
references_provider: Some(true),
|
||||
document_highlight_provider: None,
|
||||
document_symbol_provider: Some(true),
|
||||
workspace_symbol_provider: Some(true),
|
||||
|
@ -40,7 +40,9 @@ pub fn server_capabilities() -> ServerCapabilities {
|
|||
more_trigger_character: None,
|
||||
}),
|
||||
folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
|
||||
rename_provider: None,
|
||||
rename_provider: Some(RenameProviderCapability::Options(RenameOptions{
|
||||
prepare_provider: Some(true)
|
||||
})),
|
||||
color_provider: None,
|
||||
execute_command_provider: Some(ExecuteCommandOptions {
|
||||
commands: vec!["apply_code_action".to_string()],
|
||||
|
|
|
@ -4,6 +4,7 @@ use languageserver_types::{
|
|||
CodeActionResponse, Command, CompletionItem, CompletionItemKind, Diagnostic,
|
||||
DiagnosticSeverity, DocumentSymbol, FoldingRange, FoldingRangeKind, FoldingRangeParams,
|
||||
InsertTextFormat, Location, Position, SymbolInformation, TextDocumentIdentifier, TextEdit,
|
||||
RenameParams, WorkspaceEdit, PrepareRenameResponse
|
||||
};
|
||||
use ra_analysis::{FileId, FoldKind, JobToken, Query, RunnableKind};
|
||||
use ra_syntax::text_utils::contains_offset_nonstrict;
|
||||
|
@ -17,6 +18,8 @@ use crate::{
|
|||
Result,
|
||||
};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn handle_syntax_tree(
|
||||
world: ServerWorld,
|
||||
params: req::SyntaxTreeParams,
|
||||
|
@ -460,6 +463,81 @@ pub fn handle_signature_help(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn handle_prepare_rename(
|
||||
world: ServerWorld,
|
||||
params: req::TextDocumentPositionParams,
|
||||
token: JobToken,
|
||||
) -> Result<Option<PrepareRenameResponse>> {
|
||||
let file_id = params.text_document.try_conv_with(&world)?;
|
||||
let line_index = world.analysis().file_line_index(file_id);
|
||||
let offset = params.position.conv_with(&line_index);
|
||||
|
||||
// We support renaming references like handle_rename does.
|
||||
// In the future we may want to reject the renaming of things like keywords here too.
|
||||
let refs = world.analysis().find_all_refs(file_id, offset, &token);
|
||||
if refs.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let r = refs.first().unwrap();
|
||||
let loc = to_location(r.0, r.1, &world, &line_index)?;
|
||||
|
||||
Ok(Some(PrepareRenameResponse::Range(loc.range)))
|
||||
}
|
||||
|
||||
pub fn handle_rename(
|
||||
world: ServerWorld,
|
||||
params: RenameParams,
|
||||
token: JobToken,
|
||||
) -> Result<Option<WorkspaceEdit>> {
|
||||
let file_id = params.text_document.try_conv_with(&world)?;
|
||||
let line_index = world.analysis().file_line_index(file_id);
|
||||
let offset = params.position.conv_with(&line_index);
|
||||
|
||||
if params.new_name.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let refs = world.analysis().find_all_refs(file_id, offset, &token);
|
||||
if refs.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut changes = HashMap::new();
|
||||
for r in refs {
|
||||
if let Ok(loc) = to_location(r.0, r.1, &world, &line_index) {
|
||||
changes.entry(loc.uri).or_insert(Vec::new()).push(
|
||||
TextEdit {
|
||||
range: loc.range,
|
||||
new_text: params.new_name.clone()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(WorkspaceEdit {
|
||||
changes: Some(changes),
|
||||
|
||||
// TODO: return this instead if client/server support it. See #144
|
||||
document_changes : None,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn handle_references(
|
||||
world: ServerWorld,
|
||||
params: req::ReferenceParams,
|
||||
token: JobToken,
|
||||
) -> Result<Option<Vec<Location>>> {
|
||||
let file_id = params.text_document.try_conv_with(&world)?;
|
||||
let line_index = world.analysis().file_line_index(file_id);
|
||||
let offset = params.position.conv_with(&line_index);
|
||||
|
||||
let refs = world.analysis().find_all_refs(file_id, offset, &token);
|
||||
|
||||
Ok(Some(refs.into_iter()
|
||||
.filter_map(|r| to_location(r.0, r.1, &world, &line_index).ok())
|
||||
.collect()))
|
||||
}
|
||||
|
||||
pub fn handle_code_action(
|
||||
world: ServerWorld,
|
||||
params: req::CodeActionParams,
|
||||
|
|
|
@ -248,6 +248,9 @@ fn on_request(
|
|||
.on::<req::CodeActionRequest>(handlers::handle_code_action)?
|
||||
.on::<req::FoldingRangeRequest>(handlers::handle_folding_range)?
|
||||
.on::<req::SignatureHelpRequest>(handlers::handle_signature_help)?
|
||||
.on::<req::PrepareRenameRequest>(handlers::handle_prepare_rename)?
|
||||
.on::<req::Rename>(handlers::handle_rename)?
|
||||
.on::<req::References>(handlers::handle_references)?
|
||||
.finish();
|
||||
match req {
|
||||
Ok((id, handle)) => {
|
||||
|
|
|
@ -7,7 +7,7 @@ pub use languageserver_types::{
|
|||
CompletionResponse, DocumentOnTypeFormattingParams, DocumentSymbolParams,
|
||||
DocumentSymbolResponse, ExecuteCommandParams, Hover, InitializeResult,
|
||||
PublishDiagnosticsParams, SignatureHelp, TextDocumentEdit, TextDocumentPositionParams,
|
||||
TextEdit, WorkspaceSymbolParams,
|
||||
TextEdit, WorkspaceSymbolParams, ReferenceParams,
|
||||
};
|
||||
|
||||
pub enum SyntaxTree {}
|
||||
|
|
16
editors/code/package-lock.json
generated
16
editors/code/package-lock.json
generated
|
@ -2074,8 +2074,7 @@
|
|||
"semver": {
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
|
||||
"integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw=="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
|
@ -2509,9 +2508,9 @@
|
|||
}
|
||||
},
|
||||
"vsce": {
|
||||
"version": "1.51.1",
|
||||
"resolved": "https://registry.npmjs.org/vsce/-/vsce-1.51.1.tgz",
|
||||
"integrity": "sha512-Hf2HE9O/MRQHxUUgWHAm7mOkz0K5swuF2smaE/sP7+OWp/5DdIPFwmLEYCCZHxG25l3GBRoO0dAL8S5w//et+g==",
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/vsce/-/vsce-1.52.0.tgz",
|
||||
"integrity": "sha512-k+KYoTx1sacpYf2BHTA7GN82MNSlf2N4EuppFWwtTN/Sh6fWzIJafxxCNBCDK0H+5NDWfRGZheBY8C3/HOE2ZA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cheerio": "1.0.0-rc.2",
|
||||
|
@ -2561,10 +2560,11 @@
|
|||
"integrity": "sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg=="
|
||||
},
|
||||
"vscode-languageclient": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-4.4.2.tgz",
|
||||
"integrity": "sha512-9TUzsg1UM6n1UEyPlWbDf7tK1wJAK7UGFRmGDN8sz4KmbbDiVRh6YicaB/5oRSVTpuV47PdJpYlOl3SJ0RiK1Q==",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-5.1.1.tgz",
|
||||
"integrity": "sha512-jMxshi+BPRQFNG8GB00dJv7ldqMda0be26laYYll/udtJuHbog6RqK10GSxHWDN0PgY0b0m5fePyTk3bq8a0TA==",
|
||||
"requires": {
|
||||
"semver": "5.5.1",
|
||||
"vscode-languageserver-protocol": "3.13.0"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"singleQuote": true
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-languageclient": "^4.4.0"
|
||||
"vscode-languageclient": "^5.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^2.2.42",
|
||||
|
@ -37,7 +37,7 @@
|
|||
"tslint": "^5.11.0",
|
||||
"tslint-config-prettier": "^1.15.0",
|
||||
"typescript": "^2.6.1",
|
||||
"vsce": "^1.51.1",
|
||||
"vsce": "^1.52.0",
|
||||
"vscode": "^1.1.21"
|
||||
},
|
||||
"activationEvents": [
|
||||
|
|
Loading…
Reference in a new issue