Refactor find_all_refs to return ReferenceSearchResult

This commit is contained in:
Ville Penttinen 2019-02-17 13:38:32 +02:00
parent edd4c1d8a6
commit 85a6bf3424
6 changed files with 127 additions and 48 deletions

View file

@ -56,6 +56,7 @@ pub use crate::{
completion::{CompletionItem, CompletionItemKind, InsertTextFormat},
runnables::{Runnable, RunnableKind},
navigation_target::NavigationTarget,
references::ReferenceSearchResult,
};
pub use ra_ide_api_light::{
Fold, FoldKind, HighlightedRange, Severity, StructureNode, LocalEdit,
@ -319,7 +320,10 @@ impl Analysis {
}
/// Finds all usages of the reference at point.
pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
pub fn find_all_refs(
&self,
position: FilePosition,
) -> Cancelable<Option<ReferenceSearchResult>> {
self.with_db(|db| references::find_all_refs(db, position))
}

View file

@ -23,6 +23,12 @@ pub struct NavigationTarget {
}
impl NavigationTarget {
/// When `focus_range` is specified, returns it. otherwise
/// returns `full_range`
pub fn range(&self) -> TextRange {
self.focus_range.unwrap_or(self.full_range)
}
pub fn name(&self) -> &SmolStr {
&self.name
}
@ -43,14 +49,18 @@ impl NavigationTarget {
self.full_range
}
/// A "most interesting" range withing the `range_full`.
/// A "most interesting" range withing the `full_range`.
///
/// Typically, `range` is the whole syntax node, including doc comments, and
/// `focus_range` is the range of the identifier.
/// Typically, `full_range` is the whole syntax node,
/// including doc comments, and `focus_range` is the range of the identifier.
pub fn focus_range(&self) -> Option<TextRange> {
self.focus_range
}
pub(crate) fn from_bind_pat(file_id: FileId, pat: &ast::BindPat) -> NavigationTarget {
NavigationTarget::from_named(file_id, pat)
}
pub(crate) fn from_symbol(symbol: FileSymbol) -> NavigationTarget {
NavigationTarget {
file_id: symbol.file_id,

View file

@ -1,42 +1,77 @@
use relative_path::{RelativePath, RelativePathBuf};
use hir::{ModuleSource, source_binder};
use ra_db::{FileId, SourceDatabase};
use ra_db::{SourceDatabase};
use ra_syntax::{
AstNode, SyntaxNode, TextRange, SourceFile,
ast::{self, NameOwner},
AstNode, SyntaxNode, SourceFile,
ast,
algo::find_node_at_offset,
};
use crate::{
db::RootDatabase,
FilePosition,
FileRange,
FileId,
NavigationTarget,
FileSystemEdit,
SourceChange,
SourceFileEdit,
TextRange,
};
pub(crate) fn find_all_refs(db: &RootDatabase, position: FilePosition) -> Vec<(FileId, TextRange)> {
#[derive(Debug, Clone)]
pub struct ReferenceSearchResult {
declaration: NavigationTarget,
references: Vec<FileRange>,
}
impl ReferenceSearchResult {
pub fn declaration(&self) -> &NavigationTarget {
&self.declaration
}
pub fn references(&self) -> &[FileRange] {
&self.references
}
/// Total number of references
/// At least 1 since all valid references should
/// Have a declaration
pub fn len(&self) -> usize {
self.references.len() + 1
}
}
// allow turning ReferenceSearchResult into an iterator
// over FileRanges
impl IntoIterator for ReferenceSearchResult {
type Item = FileRange;
type IntoIter = ::std::vec::IntoIter<FileRange>;
fn into_iter(mut self) -> Self::IntoIter {
let mut v = Vec::with_capacity(self.len());
v.push(FileRange { file_id: self.declaration.file_id(), range: self.declaration.range() });
v.append(&mut self.references);
v.into_iter()
}
}
pub(crate) fn find_all_refs(
db: &RootDatabase,
position: FilePosition,
) -> Option<ReferenceSearchResult> {
let file = db.parse(position.file_id);
// Find the binding associated with the offset
let (binding, descr) = match find_binding(db, &file, position) {
None => return Vec::new(),
Some(it) => it,
};
let (binding, descr) = find_binding(db, &file, position)?;
let declaration = NavigationTarget::from_bind_pat(position.file_id, binding);
let mut ret = binding
.name()
let references = descr
.scopes(db)
.find_all_refs(binding)
.into_iter()
.map(|name| (position.file_id, name.syntax().range()))
.map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range })
.collect::<Vec<_>>();
ret.extend(
descr
.scopes(db)
.find_all_refs(binding)
.into_iter()
.map(|ref_desc| (position.file_id, ref_desc.range)),
);
return ret;
return Some(ReferenceSearchResult { declaration, references });
fn find_binding<'a>(
db: &RootDatabase,
@ -88,6 +123,21 @@ fn find_name_and_module_at_offset(
None
}
fn source_edit_from_fileid_range(
file_id: FileId,
range: TextRange,
new_name: &str,
) -> SourceFileEdit {
SourceFileEdit {
file_id,
edit: {
let mut builder = ra_text_edit::TextEditBuilder::default();
builder.replace(range, new_name.into());
builder.finish()
},
}
}
fn rename_mod(
db: &RootDatabase,
ast_name: &ast::Name,
@ -150,17 +200,13 @@ fn rename_reference(
position: FilePosition,
new_name: &str,
) -> Option<SourceChange> {
let edit = find_all_refs(db, position)
.iter()
.map(|(file_id, text_range)| SourceFileEdit {
file_id: *file_id,
edit: {
let mut builder = ra_text_edit::TextEditBuilder::default();
builder.replace(*text_range, new_name.into());
builder.finish()
},
})
let refs = find_all_refs(db, position)?;
let edit = refs
.into_iter()
.map(|range| source_edit_from_fileid_range(range.file_id, range.range, new_name))
.collect::<Vec<_>>();
if edit.is_empty() {
return None;
}