aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-02-17 15:38:33 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-02-17 15:38:33 +0000
commit982f72c022b45629e6adbaef22884359d3495ecf (patch)
treecc1a13dcf026a72d76496fb8ee2643619560f471 /crates
parentf937d11ad892036fa93b25a2c19d10dcebe4ab24 (diff)
parentfd5307e60d268423f7026db28b15bd2b31575396 (diff)
Merge #844
844: Refactor find_all_refs to return ReferenceSearchResult r=vipentti a=vipentti This refactors `find_all_refs` to return a new `ReferenceSearchResult` based on feedback in #839. There are few questions/notes regarding the refactor: 1. Introducing `NavigationTarget::from_bind_pat` this simply forwards the call to `NavigationTarget::from_named`, could we just expose `from_named` directly as `pub(crate)` ? 2. Added an utility method `NavigationTarget::range` since there were few places where you would use `self.focus_range.unwrap_or(self.full_range)` 3. Implementing `IntoIterator` for `ReferenceSearchResult`. This turns `ReferenceSearchResult` into an iterator over `FileRanges` and allows previous code to mostly stay as it was based on the order that `find_all_refs` previously had (declaration first and then the references). I'm not sure if there is a way of doing the conversion to `IntoIter` without the allocation of a new vector 4. Is it possible to have a binding without a name? I'm not sure if the `NavigationTarget::from_bind_pat` can cause some edge-cases that previously were ok This fixes #835. Co-authored-by: Ville Penttinen <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide_api/src/lib.rs6
-rw-r--r--crates/ra_ide_api/src/navigation_target.rs16
-rw-r--r--crates/ra_ide_api/src/references.rs106
-rw-r--r--crates/ra_ide_api/tests/test/main.rs7
-rw-r--r--crates/ra_lsp_server/src/conv.rs2
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs38
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};
60pub use ra_ide_api_light::{ 61pub 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
25impl NavigationTarget { 25impl 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..b7784e577 100644
--- a/crates/ra_ide_api/src/references.rs
+++ b/crates/ra_ide_api/src/references.rs
@@ -1,42 +1,77 @@
1use relative_path::{RelativePath, RelativePathBuf}; 1use relative_path::{RelativePath, RelativePathBuf};
2use hir::{ModuleSource, source_binder}; 2use hir::{ModuleSource, source_binder};
3use ra_db::{FileId, SourceDatabase}; 3use ra_db::{SourceDatabase};
4use ra_syntax::{ 4use 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
10use crate::{ 10use 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
18pub(crate) fn find_all_refs(db: &RootDatabase, position: FilePosition) -> Vec<(FileId, TextRange)> { 22#[derive(Debug, Clone)]
23pub struct ReferenceSearchResult {
24 declaration: NavigationTarget,
25 references: Vec<FileRange>,
26}
27
28impl 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
47impl 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
59pub(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
126fn 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
91fn rename_mod( 141fn 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 @@
1use insta::assert_debug_snapshot_matches; 1use insta::assert_debug_snapshot_matches;
2use ra_ide_api::{ 2use 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};
6use ra_syntax::{TextRange, SmolStr}; 7use 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
47fn get_all_refs(text: &str) -> Vec<(FileId, TextRange)> { 48fn 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
52fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> { 53fn 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
511pub fn handle_formatting( 526pub 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}