diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-10-20 13:51:30 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-10-20 13:51:30 +0100 |
commit | 2ded93a78a108c1f7e0dd0a9036c88c786f21dce (patch) | |
tree | 4656be95dde6ebd9a38e680dba161aa53357acfb /crates | |
parent | 4dbf0379ccd5c7643d48658f0ecc224add5a5c5c (diff) | |
parent | 3de77908eb52362e1acc3feed6186a18d8026f6e (diff) |
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 <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 32 | ||||
-rw-r--r-- | crates/ra_analysis/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/ra_analysis/tests/tests.rs | 42 | ||||
-rw-r--r-- | crates/ra_editor/src/scope/fn_scope.rs | 1 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/caps.rs | 8 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 78 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/mod.rs | 3 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/req.rs | 2 |
8 files changed, 164 insertions, 5 deletions
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index f1403cb5d..a67b1717a 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -257,6 +257,38 @@ impl AnalysisImpl { | |||
257 | vec![] | 257 | vec![] |
258 | } | 258 | } |
259 | 259 | ||
260 | pub fn find_all_refs(&self, file_id: FileId, offset: TextUnit, _token: &JobToken) -> Vec<(FileId, TextRange)> { | ||
261 | let root = self.root(file_id); | ||
262 | let file = root.syntax(file_id); | ||
263 | let syntax = file.syntax(); | ||
264 | |||
265 | let mut ret = vec![]; | ||
266 | |||
267 | // Find the symbol we are looking for | ||
268 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, offset) { | ||
269 | |||
270 | // We are only handing local references for now | ||
271 | if let Some(resolved) = resolve_local_name(&file, offset, name_ref) { | ||
272 | |||
273 | ret.push((file_id, resolved.1)); | ||
274 | |||
275 | if let Some(fn_def) = find_node_at_offset::<ast::FnDef>(syntax, offset) { | ||
276 | |||
277 | let refs : Vec<_> = fn_def.syntax().descendants() | ||
278 | .filter_map(ast::NameRef::cast) | ||
279 | .filter(|n: &ast::NameRef| resolve_local_name(&file, n.syntax().range().start(), *n) == Some(resolved.clone())) | ||
280 | .collect(); | ||
281 | |||
282 | for r in refs { | ||
283 | ret.push((file_id, r.syntax().range())); | ||
284 | } | ||
285 | } | ||
286 | } | ||
287 | } | ||
288 | |||
289 | ret | ||
290 | } | ||
291 | |||
260 | pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> { | 292 | pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> { |
261 | let root = self.root(file_id); | 293 | let root = self.root(file_id); |
262 | let module_tree = root.module_tree(); | 294 | let module_tree = root.module_tree(); |
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index 2eeacaabe..46cc0722b 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -217,6 +217,9 @@ impl Analysis { | |||
217 | self.imp | 217 | self.imp |
218 | .approximately_resolve_symbol(file_id, offset, token) | 218 | .approximately_resolve_symbol(file_id, offset, token) |
219 | } | 219 | } |
220 | pub fn find_all_refs(&self, file_id: FileId, offset: TextUnit, token: &JobToken) -> Vec<(FileId, TextRange)> { | ||
221 | self.imp.find_all_refs(file_id, offset, token) | ||
222 | } | ||
220 | pub fn parent_module(&self, file_id: FileId) -> Vec<(FileId, FileSymbol)> { | 223 | pub fn parent_module(&self, file_id: FileId) -> Vec<(FileId, FileSymbol)> { |
221 | self.imp.parent_module(file_id) | 224 | self.imp.parent_module(file_id) |
222 | } | 225 | } |
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs index e0c637d65..0c2c69ea0 100644 --- a/crates/ra_analysis/tests/tests.rs +++ b/crates/ra_analysis/tests/tests.rs | |||
@@ -10,6 +10,8 @@ use std::sync::Arc; | |||
10 | use ra_analysis::{ | 10 | use ra_analysis::{ |
11 | Analysis, AnalysisHost, CrateGraph, CrateId, FileId, FileResolver, FnDescriptor, JobHandle, | 11 | Analysis, AnalysisHost, CrateGraph, CrateId, FileId, FileResolver, FnDescriptor, JobHandle, |
12 | }; | 12 | }; |
13 | use ra_syntax::TextRange; | ||
14 | |||
13 | use relative_path::{RelativePath, RelativePathBuf}; | 15 | use relative_path::{RelativePath, RelativePathBuf}; |
14 | use rustc_hash::FxHashMap; | 16 | use rustc_hash::FxHashMap; |
15 | use test_utils::{assert_eq_dbg, extract_offset}; | 17 | use test_utils::{assert_eq_dbg, extract_offset}; |
@@ -225,3 +227,43 @@ fn bar() { | |||
225 | assert_eq!(desc.ret_type, None); | 227 | assert_eq!(desc.ret_type, None); |
226 | assert_eq!(param, Some(1)); | 228 | assert_eq!(param, Some(1)); |
227 | } | 229 | } |
230 | |||
231 | fn get_all_refs(text: &str) -> Vec<(FileId, TextRange)> { | ||
232 | let (offset, code) = extract_offset(text); | ||
233 | let code = code.as_str(); | ||
234 | |||
235 | let (_handle, token) = JobHandle::new(); | ||
236 | let snap = analysis(&[("/lib.rs", code)]); | ||
237 | |||
238 | snap.find_all_refs(FileId(1), offset, &token) | ||
239 | } | ||
240 | |||
241 | #[test] | ||
242 | fn test_find_all_refs_for_local() { | ||
243 | let code = r#" | ||
244 | fn main() { | ||
245 | let mut i = 1; | ||
246 | let j = 1; | ||
247 | i = i<|> + j; | ||
248 | |||
249 | { | ||
250 | i = 0; | ||
251 | } | ||
252 | |||
253 | i = 5; | ||
254 | }"#; | ||
255 | |||
256 | let refs = get_all_refs(code); | ||
257 | assert_eq!(refs.len(), 5); | ||
258 | } | ||
259 | |||
260 | #[test] | ||
261 | fn test_find_all_refs_for_param_inside() { | ||
262 | let code = r#" | ||
263 | fn foo(i : u32) -> u32 { | ||
264 | i<|> | ||
265 | }"#; | ||
266 | |||
267 | let refs = get_all_refs(code); | ||
268 | assert_eq!(refs.len(), 2); | ||
269 | } \ No newline at end of file | ||
diff --git a/crates/ra_editor/src/scope/fn_scope.rs b/crates/ra_editor/src/scope/fn_scope.rs index 9088e5a60..f10bdf657 100644 --- a/crates/ra_editor/src/scope/fn_scope.rs +++ b/crates/ra_editor/src/scope/fn_scope.rs | |||
@@ -270,7 +270,6 @@ pub fn resolve_local_name<'a>( | |||
270 | .filter(|entry| shadowed.insert(entry.name())) | 270 | .filter(|entry| shadowed.insert(entry.name())) |
271 | .filter(|entry| entry.name() == name_ref.text()) | 271 | .filter(|entry| entry.name() == name_ref.text()) |
272 | .nth(0); | 272 | .nth(0); |
273 | eprintln!("ret = {:?}", ret); | ||
274 | ret | 273 | ret |
275 | } | 274 | } |
276 | 275 | ||
diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs index 1dd495791..b6436b646 100644 --- a/crates/ra_lsp_server/src/caps.rs +++ b/crates/ra_lsp_server/src/caps.rs | |||
@@ -2,7 +2,7 @@ use languageserver_types::{ | |||
2 | CodeActionProviderCapability, CompletionOptions, DocumentOnTypeFormattingOptions, | 2 | CodeActionProviderCapability, CompletionOptions, DocumentOnTypeFormattingOptions, |
3 | ExecuteCommandOptions, FoldingRangeProviderCapability, ServerCapabilities, | 3 | ExecuteCommandOptions, FoldingRangeProviderCapability, ServerCapabilities, |
4 | SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, | 4 | SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, |
5 | TextDocumentSyncOptions, | 5 | TextDocumentSyncOptions, RenameProviderCapability, RenameOptions |
6 | }; | 6 | }; |
7 | 7 | ||
8 | pub fn server_capabilities() -> ServerCapabilities { | 8 | pub fn server_capabilities() -> ServerCapabilities { |
@@ -27,7 +27,7 @@ pub fn server_capabilities() -> ServerCapabilities { | |||
27 | definition_provider: Some(true), | 27 | definition_provider: Some(true), |
28 | type_definition_provider: None, | 28 | type_definition_provider: None, |
29 | implementation_provider: None, | 29 | implementation_provider: None, |
30 | references_provider: None, | 30 | references_provider: Some(true), |
31 | document_highlight_provider: None, | 31 | document_highlight_provider: None, |
32 | document_symbol_provider: Some(true), | 32 | document_symbol_provider: Some(true), |
33 | workspace_symbol_provider: Some(true), | 33 | workspace_symbol_provider: Some(true), |
@@ -40,7 +40,9 @@ pub fn server_capabilities() -> ServerCapabilities { | |||
40 | more_trigger_character: None, | 40 | more_trigger_character: None, |
41 | }), | 41 | }), |
42 | folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), | 42 | folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), |
43 | rename_provider: None, | 43 | rename_provider: Some(RenameProviderCapability::Options(RenameOptions{ |
44 | prepare_provider: Some(true) | ||
45 | })), | ||
44 | color_provider: None, | 46 | color_provider: None, |
45 | execute_command_provider: Some(ExecuteCommandOptions { | 47 | execute_command_provider: Some(ExecuteCommandOptions { |
46 | commands: vec!["apply_code_action".to_string()], | 48 | commands: vec!["apply_code_action".to_string()], |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index c25b63852..639fe4553 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -4,6 +4,7 @@ use languageserver_types::{ | |||
4 | CodeActionResponse, Command, CompletionItem, CompletionItemKind, Diagnostic, | 4 | CodeActionResponse, Command, CompletionItem, CompletionItemKind, Diagnostic, |
5 | DiagnosticSeverity, DocumentSymbol, FoldingRange, FoldingRangeKind, FoldingRangeParams, | 5 | DiagnosticSeverity, DocumentSymbol, FoldingRange, FoldingRangeKind, FoldingRangeParams, |
6 | InsertTextFormat, Location, Position, SymbolInformation, TextDocumentIdentifier, TextEdit, | 6 | InsertTextFormat, Location, Position, SymbolInformation, TextDocumentIdentifier, TextEdit, |
7 | RenameParams, WorkspaceEdit, PrepareRenameResponse | ||
7 | }; | 8 | }; |
8 | use ra_analysis::{FileId, FoldKind, JobToken, Query, RunnableKind}; | 9 | use ra_analysis::{FileId, FoldKind, JobToken, Query, RunnableKind}; |
9 | use ra_syntax::text_utils::contains_offset_nonstrict; | 10 | use ra_syntax::text_utils::contains_offset_nonstrict; |
@@ -17,6 +18,8 @@ use crate::{ | |||
17 | Result, | 18 | Result, |
18 | }; | 19 | }; |
19 | 20 | ||
21 | use std::collections::HashMap; | ||
22 | |||
20 | pub fn handle_syntax_tree( | 23 | pub fn handle_syntax_tree( |
21 | world: ServerWorld, | 24 | world: ServerWorld, |
22 | params: req::SyntaxTreeParams, | 25 | params: req::SyntaxTreeParams, |
@@ -460,6 +463,81 @@ pub fn handle_signature_help( | |||
460 | } | 463 | } |
461 | } | 464 | } |
462 | 465 | ||
466 | pub fn handle_prepare_rename( | ||
467 | world: ServerWorld, | ||
468 | params: req::TextDocumentPositionParams, | ||
469 | token: JobToken, | ||
470 | ) -> Result<Option<PrepareRenameResponse>> { | ||
471 | let file_id = params.text_document.try_conv_with(&world)?; | ||
472 | let line_index = world.analysis().file_line_index(file_id); | ||
473 | let offset = params.position.conv_with(&line_index); | ||
474 | |||
475 | // We support renaming references like handle_rename does. | ||
476 | // In the future we may want to reject the renaming of things like keywords here too. | ||
477 | let refs = world.analysis().find_all_refs(file_id, offset, &token); | ||
478 | if refs.is_empty() { | ||
479 | return Ok(None); | ||
480 | } | ||
481 | |||
482 | let r = refs.first().unwrap(); | ||
483 | let loc = to_location(r.0, r.1, &world, &line_index)?; | ||
484 | |||
485 | Ok(Some(PrepareRenameResponse::Range(loc.range))) | ||
486 | } | ||
487 | |||
488 | pub fn handle_rename( | ||
489 | world: ServerWorld, | ||
490 | params: RenameParams, | ||
491 | token: JobToken, | ||
492 | ) -> Result<Option<WorkspaceEdit>> { | ||
493 | let file_id = params.text_document.try_conv_with(&world)?; | ||
494 | let line_index = world.analysis().file_line_index(file_id); | ||
495 | let offset = params.position.conv_with(&line_index); | ||
496 | |||
497 | if params.new_name.is_empty() { | ||
498 | return Ok(None); | ||
499 | } | ||
500 | |||
501 | let refs = world.analysis().find_all_refs(file_id, offset, &token); | ||
502 | if refs.is_empty() { | ||
503 | return Ok(None); | ||
504 | } | ||
505 | |||
506 | let mut changes = HashMap::new(); | ||
507 | for r in refs { | ||
508 | if let Ok(loc) = to_location(r.0, r.1, &world, &line_index) { | ||
509 | changes.entry(loc.uri).or_insert(Vec::new()).push( | ||
510 | TextEdit { | ||
511 | range: loc.range, | ||
512 | new_text: params.new_name.clone() | ||
513 | }); | ||
514 | } | ||
515 | } | ||
516 | |||
517 | Ok(Some(WorkspaceEdit { | ||
518 | changes: Some(changes), | ||
519 | |||
520 | // TODO: return this instead if client/server support it. See #144 | ||
521 | document_changes : None, | ||
522 | })) | ||
523 | } | ||
524 | |||
525 | pub fn handle_references( | ||
526 | world: ServerWorld, | ||
527 | params: req::ReferenceParams, | ||
528 | token: JobToken, | ||
529 | ) -> Result<Option<Vec<Location>>> { | ||
530 | let file_id = params.text_document.try_conv_with(&world)?; | ||
531 | let line_index = world.analysis().file_line_index(file_id); | ||
532 | let offset = params.position.conv_with(&line_index); | ||
533 | |||
534 | let refs = world.analysis().find_all_refs(file_id, offset, &token); | ||
535 | |||
536 | Ok(Some(refs.into_iter() | ||
537 | .filter_map(|r| to_location(r.0, r.1, &world, &line_index).ok()) | ||
538 | .collect())) | ||
539 | } | ||
540 | |||
463 | pub fn handle_code_action( | 541 | pub fn handle_code_action( |
464 | world: ServerWorld, | 542 | world: ServerWorld, |
465 | params: req::CodeActionParams, | 543 | params: req::CodeActionParams, |
diff --git a/crates/ra_lsp_server/src/main_loop/mod.rs b/crates/ra_lsp_server/src/main_loop/mod.rs index a11baf4aa..165f2e78f 100644 --- a/crates/ra_lsp_server/src/main_loop/mod.rs +++ b/crates/ra_lsp_server/src/main_loop/mod.rs | |||
@@ -248,6 +248,9 @@ fn on_request( | |||
248 | .on::<req::CodeActionRequest>(handlers::handle_code_action)? | 248 | .on::<req::CodeActionRequest>(handlers::handle_code_action)? |
249 | .on::<req::FoldingRangeRequest>(handlers::handle_folding_range)? | 249 | .on::<req::FoldingRangeRequest>(handlers::handle_folding_range)? |
250 | .on::<req::SignatureHelpRequest>(handlers::handle_signature_help)? | 250 | .on::<req::SignatureHelpRequest>(handlers::handle_signature_help)? |
251 | .on::<req::PrepareRenameRequest>(handlers::handle_prepare_rename)? | ||
252 | .on::<req::Rename>(handlers::handle_rename)? | ||
253 | .on::<req::References>(handlers::handle_references)? | ||
251 | .finish(); | 254 | .finish(); |
252 | match req { | 255 | match req { |
253 | Ok((id, handle)) => { | 256 | Ok((id, handle)) => { |
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index b76bfbcbc..6cd04d84c 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs | |||
@@ -7,7 +7,7 @@ pub use languageserver_types::{ | |||
7 | CompletionResponse, DocumentOnTypeFormattingParams, DocumentSymbolParams, | 7 | CompletionResponse, DocumentOnTypeFormattingParams, DocumentSymbolParams, |
8 | DocumentSymbolResponse, ExecuteCommandParams, Hover, InitializeResult, | 8 | DocumentSymbolResponse, ExecuteCommandParams, Hover, InitializeResult, |
9 | PublishDiagnosticsParams, SignatureHelp, TextDocumentEdit, TextDocumentPositionParams, | 9 | PublishDiagnosticsParams, SignatureHelp, TextDocumentEdit, TextDocumentPositionParams, |
10 | TextEdit, WorkspaceSymbolParams, | 10 | TextEdit, WorkspaceSymbolParams, ReferenceParams, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | pub enum SyntaxTree {} | 13 | pub enum SyntaxTree {} |