diff options
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 6 | ||||
-rw-r--r-- | crates/ra_ide_api/src/navigation_target.rs | 16 | ||||
-rw-r--r-- | crates/ra_ide_api/src/references.rs | 106 | ||||
-rw-r--r-- | crates/ra_ide_api/tests/test/main.rs | 7 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/conv.rs | 2 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 38 |
6 files changed, 127 insertions, 48 deletions
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 1746b58ae..57a490fa7 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -56,6 +56,7 @@ pub use crate::{ | |||
56 | completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, | 56 | completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, |
57 | runnables::{Runnable, RunnableKind}, | 57 | runnables::{Runnable, RunnableKind}, |
58 | navigation_target::NavigationTarget, | 58 | navigation_target::NavigationTarget, |
59 | references::ReferenceSearchResult, | ||
59 | }; | 60 | }; |
60 | pub use ra_ide_api_light::{ | 61 | pub use ra_ide_api_light::{ |
61 | Fold, FoldKind, HighlightedRange, Severity, StructureNode, LocalEdit, | 62 | Fold, FoldKind, HighlightedRange, Severity, StructureNode, LocalEdit, |
@@ -319,7 +320,10 @@ impl Analysis { | |||
319 | } | 320 | } |
320 | 321 | ||
321 | /// Finds all usages of the reference at point. | 322 | /// Finds all usages of the reference at point. |
322 | pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { | 323 | pub fn find_all_refs( |
324 | &self, | ||
325 | position: FilePosition, | ||
326 | ) -> Cancelable<Option<ReferenceSearchResult>> { | ||
323 | self.with_db(|db| references::find_all_refs(db, position)) | 327 | self.with_db(|db| references::find_all_refs(db, position)) |
324 | } | 328 | } |
325 | 329 | ||
diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/navigation_target.rs index 004921863..fd001179a 100644 --- a/crates/ra_ide_api/src/navigation_target.rs +++ b/crates/ra_ide_api/src/navigation_target.rs | |||
@@ -23,6 +23,12 @@ pub struct NavigationTarget { | |||
23 | } | 23 | } |
24 | 24 | ||
25 | impl NavigationTarget { | 25 | impl NavigationTarget { |
26 | /// When `focus_range` is specified, returns it. otherwise | ||
27 | /// returns `full_range` | ||
28 | pub fn range(&self) -> TextRange { | ||
29 | self.focus_range.unwrap_or(self.full_range) | ||
30 | } | ||
31 | |||
26 | pub fn name(&self) -> &SmolStr { | 32 | pub fn name(&self) -> &SmolStr { |
27 | &self.name | 33 | &self.name |
28 | } | 34 | } |
@@ -43,14 +49,18 @@ impl NavigationTarget { | |||
43 | self.full_range | 49 | self.full_range |
44 | } | 50 | } |
45 | 51 | ||
46 | /// A "most interesting" range withing the `range_full`. | 52 | /// A "most interesting" range withing the `full_range`. |
47 | /// | 53 | /// |
48 | /// Typically, `range` is the whole syntax node, including doc comments, and | 54 | /// Typically, `full_range` is the whole syntax node, |
49 | /// `focus_range` is the range of the identifier. | 55 | /// including doc comments, and `focus_range` is the range of the identifier. |
50 | pub fn focus_range(&self) -> Option<TextRange> { | 56 | pub fn focus_range(&self) -> Option<TextRange> { |
51 | self.focus_range | 57 | self.focus_range |
52 | } | 58 | } |
53 | 59 | ||
60 | pub(crate) fn from_bind_pat(file_id: FileId, pat: &ast::BindPat) -> NavigationTarget { | ||
61 | NavigationTarget::from_named(file_id, pat) | ||
62 | } | ||
63 | |||
54 | pub(crate) fn from_symbol(symbol: FileSymbol) -> NavigationTarget { | 64 | pub(crate) fn from_symbol(symbol: FileSymbol) -> NavigationTarget { |
55 | NavigationTarget { | 65 | NavigationTarget { |
56 | file_id: symbol.file_id, | 66 | file_id: symbol.file_id, |
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index e7ebf9f6e..a6a0ef8ac 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs | |||
@@ -1,42 +1,77 @@ | |||
1 | use relative_path::{RelativePath, RelativePathBuf}; | 1 | use relative_path::{RelativePath, RelativePathBuf}; |
2 | use hir::{ModuleSource, source_binder}; | 2 | use hir::{ModuleSource, source_binder}; |
3 | use ra_db::{FileId, SourceDatabase}; | 3 | use ra_db::{SourceDatabase}; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | AstNode, SyntaxNode, TextRange, SourceFile, | 5 | AstNode, SyntaxNode, SourceFile, |
6 | ast::{self, NameOwner}, | 6 | ast, |
7 | algo::find_node_at_offset, | 7 | algo::find_node_at_offset, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | db::RootDatabase, | 11 | db::RootDatabase, |
12 | FilePosition, | 12 | FilePosition, |
13 | FileRange, | ||
14 | FileId, | ||
15 | NavigationTarget, | ||
13 | FileSystemEdit, | 16 | FileSystemEdit, |
14 | SourceChange, | 17 | SourceChange, |
15 | SourceFileEdit, | 18 | SourceFileEdit, |
19 | TextRange, | ||
16 | }; | 20 | }; |
17 | 21 | ||
18 | pub(crate) fn find_all_refs(db: &RootDatabase, position: FilePosition) -> Vec<(FileId, TextRange)> { | 22 | #[derive(Debug, Clone)] |
23 | pub struct ReferenceSearchResult { | ||
24 | declaration: NavigationTarget, | ||
25 | references: Vec<FileRange>, | ||
26 | } | ||
27 | |||
28 | impl ReferenceSearchResult { | ||
29 | pub fn declaration(&self) -> &NavigationTarget { | ||
30 | &self.declaration | ||
31 | } | ||
32 | |||
33 | pub fn references(&self) -> &[FileRange] { | ||
34 | &self.references | ||
35 | } | ||
36 | |||
37 | /// Total number of references | ||
38 | /// At least 1 since all valid references should | ||
39 | /// Have a declaration | ||
40 | pub fn len(&self) -> usize { | ||
41 | self.references.len() + 1 | ||
42 | } | ||
43 | } | ||
44 | |||
45 | // allow turning ReferenceSearchResult into an iterator | ||
46 | // over FileRanges | ||
47 | impl IntoIterator for ReferenceSearchResult { | ||
48 | type Item = FileRange; | ||
49 | type IntoIter = ::std::vec::IntoIter<FileRange>; | ||
50 | |||
51 | fn into_iter(mut self) -> Self::IntoIter { | ||
52 | let mut v = Vec::with_capacity(self.len()); | ||
53 | v.push(FileRange { file_id: self.declaration.file_id(), range: self.declaration.range() }); | ||
54 | v.append(&mut self.references); | ||
55 | v.into_iter() | ||
56 | } | ||
57 | } | ||
58 | |||
59 | pub(crate) fn find_all_refs( | ||
60 | db: &RootDatabase, | ||
61 | position: FilePosition, | ||
62 | ) -> Option<ReferenceSearchResult> { | ||
19 | let file = db.parse(position.file_id); | 63 | let file = db.parse(position.file_id); |
20 | // Find the binding associated with the offset | 64 | let (binding, descr) = find_binding(db, &file, position)?; |
21 | let (binding, descr) = match find_binding(db, &file, position) { | 65 | let declaration = NavigationTarget::from_bind_pat(position.file_id, binding); |
22 | None => return Vec::new(), | ||
23 | Some(it) => it, | ||
24 | }; | ||
25 | 66 | ||
26 | let mut ret = binding | 67 | let references = descr |
27 | .name() | 68 | .scopes(db) |
69 | .find_all_refs(binding) | ||
28 | .into_iter() | 70 | .into_iter() |
29 | .map(|name| (position.file_id, name.syntax().range())) | 71 | .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) |
30 | .collect::<Vec<_>>(); | 72 | .collect::<Vec<_>>(); |
31 | ret.extend( | ||
32 | descr | ||
33 | .scopes(db) | ||
34 | .find_all_refs(binding) | ||
35 | .into_iter() | ||
36 | .map(|ref_desc| (position.file_id, ref_desc.range)), | ||
37 | ); | ||
38 | 73 | ||
39 | return ret; | 74 | return Some(ReferenceSearchResult { declaration, references }); |
40 | 75 | ||
41 | fn find_binding<'a>( | 76 | fn find_binding<'a>( |
42 | db: &RootDatabase, | 77 | db: &RootDatabase, |
@@ -88,6 +123,21 @@ fn find_name_and_module_at_offset( | |||
88 | None | 123 | None |
89 | } | 124 | } |
90 | 125 | ||
126 | fn source_edit_from_fileid_range( | ||
127 | file_id: FileId, | ||
128 | range: TextRange, | ||
129 | new_name: &str, | ||
130 | ) -> SourceFileEdit { | ||
131 | SourceFileEdit { | ||
132 | file_id, | ||
133 | edit: { | ||
134 | let mut builder = ra_text_edit::TextEditBuilder::default(); | ||
135 | builder.replace(range, new_name.into()); | ||
136 | builder.finish() | ||
137 | }, | ||
138 | } | ||
139 | } | ||
140 | |||
91 | fn rename_mod( | 141 | fn rename_mod( |
92 | db: &RootDatabase, | 142 | db: &RootDatabase, |
93 | ast_name: &ast::Name, | 143 | ast_name: &ast::Name, |
@@ -150,17 +200,13 @@ fn rename_reference( | |||
150 | position: FilePosition, | 200 | position: FilePosition, |
151 | new_name: &str, | 201 | new_name: &str, |
152 | ) -> Option<SourceChange> { | 202 | ) -> Option<SourceChange> { |
153 | let edit = find_all_refs(db, position) | 203 | let refs = find_all_refs(db, position)?; |
154 | .iter() | 204 | |
155 | .map(|(file_id, text_range)| SourceFileEdit { | 205 | let edit = refs |
156 | file_id: *file_id, | 206 | .into_iter() |
157 | edit: { | 207 | .map(|range| source_edit_from_fileid_range(range.file_id, range.range, new_name)) |
158 | let mut builder = ra_text_edit::TextEditBuilder::default(); | ||
159 | builder.replace(*text_range, new_name.into()); | ||
160 | builder.finish() | ||
161 | }, | ||
162 | }) | ||
163 | .collect::<Vec<_>>(); | 208 | .collect::<Vec<_>>(); |
209 | |||
164 | if edit.is_empty() { | 210 | if edit.is_empty() { |
165 | return None; | 211 | return None; |
166 | } | 212 | } |
diff --git a/crates/ra_ide_api/tests/test/main.rs b/crates/ra_ide_api/tests/test/main.rs index 0526f7584..a83fbe07b 100644 --- a/crates/ra_ide_api/tests/test/main.rs +++ b/crates/ra_ide_api/tests/test/main.rs | |||
@@ -1,7 +1,8 @@ | |||
1 | use insta::assert_debug_snapshot_matches; | 1 | use insta::assert_debug_snapshot_matches; |
2 | use ra_ide_api::{ | 2 | use ra_ide_api::{ |
3 | mock_analysis::{single_file, single_file_with_position, MockAnalysis}, | 3 | mock_analysis::{single_file, single_file_with_position, MockAnalysis}, |
4 | AnalysisChange, CrateGraph, Edition::Edition2018, FileId, Query, NavigationTarget | 4 | AnalysisChange, CrateGraph, Edition::Edition2018, Query, NavigationTarget, |
5 | ReferenceSearchResult, | ||
5 | }; | 6 | }; |
6 | use ra_syntax::{TextRange, SmolStr}; | 7 | use ra_syntax::{TextRange, SmolStr}; |
7 | 8 | ||
@@ -44,9 +45,9 @@ fn test_resolve_crate_root() { | |||
44 | assert_eq!(host.analysis().crate_for(mod_file).unwrap(), vec![crate_id]); | 45 | assert_eq!(host.analysis().crate_for(mod_file).unwrap(), vec![crate_id]); |
45 | } | 46 | } |
46 | 47 | ||
47 | fn get_all_refs(text: &str) -> Vec<(FileId, TextRange)> { | 48 | fn get_all_refs(text: &str) -> ReferenceSearchResult { |
48 | let (analysis, position) = single_file_with_position(text); | 49 | let (analysis, position) = single_file_with_position(text); |
49 | analysis.find_all_refs(position).unwrap() | 50 | analysis.find_all_refs(position).unwrap().unwrap() |
50 | } | 51 | } |
51 | 52 | ||
52 | fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> { | 53 | fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> { |
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 20077a48a..c3192a1e5 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs | |||
@@ -333,7 +333,7 @@ impl TryConvWith for &NavigationTarget { | |||
333 | type Output = Location; | 333 | type Output = Location; |
334 | fn try_conv_with(self, world: &ServerWorld) -> Result<Location> { | 334 | fn try_conv_with(self, world: &ServerWorld) -> Result<Location> { |
335 | let line_index = world.analysis().file_line_index(self.file_id()); | 335 | let line_index = world.analysis().file_line_index(self.file_id()); |
336 | let range = self.focus_range().unwrap_or(self.full_range()); | 336 | let range = self.range(); |
337 | to_location(self.file_id(), range, &world, &line_index) | 337 | to_location(self.file_id(), range, &world, &line_index) |
338 | } | 338 | } |
339 | } | 339 | } |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 09d896c40..9208ee473 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -456,14 +456,16 @@ pub fn handle_prepare_rename( | |||
456 | 456 | ||
457 | // We support renaming references like handle_rename does. | 457 | // We support renaming references like handle_rename does. |
458 | // In the future we may want to reject the renaming of things like keywords here too. | 458 | // In the future we may want to reject the renaming of things like keywords here too. |
459 | let refs = world.analysis().find_all_refs(position)?; | 459 | let refs = match world.analysis().find_all_refs(position)? { |
460 | let r = match refs.first() { | ||
461 | Some(r) => r, | ||
462 | None => return Ok(None), | 460 | None => return Ok(None), |
461 | Some(refs) => refs, | ||
463 | }; | 462 | }; |
463 | |||
464 | // Refs should always have a declaration | ||
465 | let r = refs.declaration(); | ||
464 | let file_id = params.text_document.try_conv_with(&world)?; | 466 | let file_id = params.text_document.try_conv_with(&world)?; |
465 | let line_index = world.analysis().file_line_index(file_id); | 467 | let line_index = world.analysis().file_line_index(file_id); |
466 | let loc = to_location(r.0, r.1, &world, &line_index)?; | 468 | let loc = to_location(r.file_id(), r.range(), &world, &line_index)?; |
467 | 469 | ||
468 | Ok(Some(PrepareRenameResponse::Range(loc.range))) | 470 | Ok(Some(PrepareRenameResponse::Range(loc.range))) |
469 | } | 471 | } |
@@ -501,11 +503,24 @@ pub fn handle_references( | |||
501 | let line_index = world.analysis().file_line_index(file_id); | 503 | let line_index = world.analysis().file_line_index(file_id); |
502 | let offset = params.position.conv_with(&line_index); | 504 | let offset = params.position.conv_with(&line_index); |
503 | 505 | ||
504 | let refs = world.analysis().find_all_refs(FilePosition { file_id, offset })?; | 506 | let refs = match world.analysis().find_all_refs(FilePosition { file_id, offset })? { |
507 | None => return Ok(None), | ||
508 | Some(refs) => refs, | ||
509 | }; | ||
505 | 510 | ||
506 | Ok(Some( | 511 | let locations = if params.context.include_declaration { |
507 | refs.into_iter().filter_map(|r| to_location(r.0, r.1, &world, &line_index).ok()).collect(), | 512 | refs.into_iter() |
508 | )) | 513 | .filter_map(|r| to_location(r.file_id, r.range, &world, &line_index).ok()) |
514 | .collect() | ||
515 | } else { | ||
516 | // Only iterate over the references if include_declaration was false | ||
517 | refs.references() | ||
518 | .iter() | ||
519 | .filter_map(|r| to_location(r.file_id, r.range, &world, &line_index).ok()) | ||
520 | .collect() | ||
521 | }; | ||
522 | |||
523 | Ok(Some(locations)) | ||
509 | } | 524 | } |
510 | 525 | ||
511 | pub fn handle_formatting( | 526 | pub fn handle_formatting( |
@@ -712,11 +727,14 @@ pub fn handle_document_highlight( | |||
712 | let file_id = params.text_document.try_conv_with(&world)?; | 727 | let file_id = params.text_document.try_conv_with(&world)?; |
713 | let line_index = world.analysis().file_line_index(file_id); | 728 | let line_index = world.analysis().file_line_index(file_id); |
714 | 729 | ||
715 | let refs = world.analysis().find_all_refs(params.try_conv_with(&world)?)?; | 730 | let refs = match world.analysis().find_all_refs(params.try_conv_with(&world)?)? { |
731 | None => return Ok(None), | ||
732 | Some(refs) => refs, | ||
733 | }; | ||
716 | 734 | ||
717 | Ok(Some( | 735 | Ok(Some( |
718 | refs.into_iter() | 736 | refs.into_iter() |
719 | .map(|r| DocumentHighlight { range: r.1.conv_with(&line_index), kind: None }) | 737 | .map(|r| DocumentHighlight { range: r.range.conv_with(&line_index), kind: None }) |
720 | .collect(), | 738 | .collect(), |
721 | )) | 739 | )) |
722 | } | 740 | } |