Group references by FileId

This commit is contained in:
Lukas Wirth 2021-01-12 00:05:07 +01:00
parent 52fa926f00
commit fbdb32adfc
10 changed files with 322 additions and 254 deletions

View file

@ -3,8 +3,8 @@
use indexmap::IndexMap;
use hir::Semantics;
use ide_db::call_info::FnCallNode;
use ide_db::RootDatabase;
use ide_db::{call_info::FnCallNode, search::FileReferences};
use syntax::{ast, AstNode, TextRange};
use crate::{
@ -47,22 +47,23 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
let mut calls = CallLocations::default();
for reference in refs.info.references() {
let file_id = reference.file_range.file_id;
for &FileReferences { file_id, ref references } in refs.info.references() {
let file = sema.parse(file_id);
let file = file.syntax();
let token = file.token_at_offset(reference.file_range.range.start()).next()?;
let token = sema.descend_into_macros(token);
let syntax = token.parent();
for reference in references {
let token = file.token_at_offset(reference.range.start()).next()?;
let token = sema.descend_into_macros(token);
let syntax = token.parent();
// This target is the containing function
if let Some(nav) = syntax.ancestors().find_map(|node| {
let fn_ = ast::Fn::cast(node)?;
let def = sema.to_def(&fn_)?;
def.try_to_nav(sema.db)
}) {
let relative_range = reference.file_range.range;
calls.add(&nav, relative_range);
// This target is the containing function
if let Some(nav) = syntax.ancestors().find_map(|node| {
let fn_ = ast::Fn::cast(node)?;
let def = sema.to_def(&fn_)?;
def.try_to_nav(sema.db)
}) {
let relative_range = reference.range;
calls.add(&nav, relative_range);
}
}
}

View file

@ -92,7 +92,7 @@ pub use ide_db::base_db::{
};
pub use ide_db::{
call_info::CallInfo,
search::{Reference, ReferenceAccess, ReferenceKind},
search::{FileReference, ReferenceAccess, ReferenceKind},
};
pub use ide_db::{
label::Label,

View file

@ -14,8 +14,7 @@ pub(crate) mod rename;
use hir::Semantics;
use ide_db::{
defs::{Definition, NameClass, NameRefClass},
search::Reference,
search::{ReferenceAccess, ReferenceKind, SearchScope},
search::{FileReference, FileReferences, ReferenceAccess, ReferenceKind, SearchScope},
RootDatabase,
};
use syntax::{
@ -29,7 +28,7 @@ use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeI
#[derive(Debug, Clone)]
pub struct ReferenceSearchResult {
declaration: Declaration,
references: Vec<Reference>,
references: Vec<FileReferences>,
}
#[derive(Debug, Clone)]
@ -48,7 +47,7 @@ impl ReferenceSearchResult {
&self.declaration.nav
}
pub fn references(&self) -> &[Reference] {
pub fn references(&self) -> &[FileReferences] {
&self.references
}
@ -63,20 +62,22 @@ impl ReferenceSearchResult {
// allow turning ReferenceSearchResult into an iterator
// over References
impl IntoIterator for ReferenceSearchResult {
type Item = Reference;
type IntoIter = std::vec::IntoIter<Reference>;
type Item = FileReferences;
type IntoIter = std::vec::IntoIter<FileReferences>;
fn into_iter(mut self) -> Self::IntoIter {
let mut v = Vec::with_capacity(self.len());
v.push(Reference {
file_range: FileRange {
file_id: self.declaration.nav.file_id,
range: self.declaration.nav.focus_or_full_range(),
},
v.append(&mut self.references);
let decl_ref = FileReference {
range: self.declaration.nav.focus_or_full_range(),
kind: self.declaration.kind,
access: self.declaration.access,
});
v.append(&mut self.references);
};
let file_id = self.declaration.nav.file_id;
match v.iter_mut().find(|it| it.file_id == file_id) {
Some(file_refs) => file_refs.references.push(decl_ref),
None => v.push(FileReferences { file_id, references: vec![decl_ref] }),
}
v.into_iter()
}
}
@ -109,13 +110,11 @@ pub(crate) fn find_all_refs(
let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?;
let references = def
.usages(sema)
.set_scope(search_scope)
.all()
.into_iter()
.filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)
.collect();
let mut references = def.usages(sema).set_scope(search_scope).all();
references.iter_mut().for_each(|it| {
it.references.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)
});
references.retain(|r| !r.references.is_empty());
let nav = def.try_to_nav(sema.db)?;
let decl_range = nav.focus_or_full_range();
@ -255,7 +254,8 @@ fn try_find_self_references(
syntax: &SyntaxNode,
position: FilePosition,
) -> Option<RangeInfo<ReferenceSearchResult>> {
let self_token = syntax.token_at_offset(position.offset).find(|t| t.kind() == T![self])?;
let FilePosition { file_id, offset } = position;
let self_token = syntax.token_at_offset(offset).find(|t| t.kind() == T![self])?;
let parent = self_token.parent();
match_ast! {
match parent {
@ -276,7 +276,7 @@ fn try_find_self_references(
let declaration = Declaration {
nav: NavigationTarget {
file_id: position.file_id,
file_id,
full_range: self_param.syntax().text_range(),
focus_range: Some(param_self_token.text_range()),
name: param_self_token.text().clone(),
@ -295,25 +295,29 @@ fn try_find_self_references(
let references = function
.body()
.map(|body| {
body.syntax()
.descendants()
.filter_map(ast::PathExpr::cast)
.filter_map(|expr| {
let path = expr.path()?;
if path.qualifier().is_none() {
path.segment()?.self_token()
} else {
None
}
})
.map(|token| Reference {
file_range: FileRange { file_id: position.file_id, range: token.text_range() },
kind: ReferenceKind::SelfKw,
access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration
})
.collect()
FileReferences {
file_id,
references: body
.syntax()
.descendants()
.filter_map(ast::PathExpr::cast)
.filter_map(|expr| {
let path = expr.path()?;
if path.qualifier().is_none() {
path.segment()?.self_token()
} else {
None
}
})
.map(|token| FileReference {
range: token.text_range(),
kind: ReferenceKind::SelfKw,
access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration
})
.collect(),
}
})
.unwrap_or_default();
.map_or_else(Vec::default, |it| vec![it]);
Some(RangeInfo::new(
param_self_token.text_range(),
@ -324,7 +328,7 @@ fn try_find_self_references(
#[cfg(test)]
mod tests {
use expect_test::{expect, Expect};
use ide_db::base_db::FileId;
use ide_db::{base_db::FileId, search::FileReferences};
use stdx::format_to;
use crate::{fixture, SearchScope};
@ -1018,12 +1022,14 @@ impl Foo {
actual += "\n\n";
}
for r in &refs.references {
format_to!(actual, "{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind);
if let Some(access) = r.access {
format_to!(actual, " {:?}", access);
for FileReferences { file_id, references } in refs.references {
for r in references {
format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind);
if let Some(access) = r.access {
format_to!(actual, " {:?}", access);
}
actual += "\n";
}
actual += "\n";
}
expect.assert_eq(&actual)
}

View file

@ -6,9 +6,10 @@ use std::{
};
use hir::{Module, ModuleDef, ModuleSource, Semantics};
use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt};
use ide_db::{
base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt},
defs::{Definition, NameClass, NameRefClass},
search::FileReferences,
RootDatabase,
};
use syntax::{
@ -20,8 +21,8 @@ use test_utils::mark;
use text_edit::TextEdit;
use crate::{
FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, ReferenceSearchResult,
SourceChange, SourceFileEdit, TextRange, TextSize,
FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, ReferenceSearchResult, SourceChange,
SourceFileEdit, TextRange, TextSize,
};
type RenameResult<T> = Result<T, RenameError>;
@ -173,39 +174,45 @@ fn find_all_refs(
.ok_or_else(|| format_err!("No references found at position"))
}
fn source_edit_from_reference(
fn source_edit_from_references(
sema: &Semantics<RootDatabase>,
reference: Reference,
&FileReferences { file_id, ref references }: &FileReferences,
new_name: &str,
) -> SourceFileEdit {
let mut replacement_text = String::new();
let range = match reference.kind {
ReferenceKind::FieldShorthandForField => {
mark::hit!(test_rename_struct_field_for_shorthand);
replacement_text.push_str(new_name);
replacement_text.push_str(": ");
TextRange::new(reference.file_range.range.start(), reference.file_range.range.start())
}
ReferenceKind::FieldShorthandForLocal => {
mark::hit!(test_rename_local_for_field_shorthand);
replacement_text.push_str(": ");
replacement_text.push_str(new_name);
TextRange::new(reference.file_range.range.end(), reference.file_range.range.end())
}
ReferenceKind::RecordFieldExprOrPat => {
mark::hit!(test_rename_field_expr_pat);
replacement_text.push_str(new_name);
edit_text_range_for_record_field_expr_or_pat(sema, reference.file_range, new_name)
}
_ => {
replacement_text.push_str(new_name);
reference.file_range.range
}
};
SourceFileEdit {
file_id: reference.file_range.file_id,
edit: TextEdit::replace(range, replacement_text),
let mut edit = TextEdit::builder();
for reference in references {
let mut replacement_text = String::new();
let range = match reference.kind {
ReferenceKind::FieldShorthandForField => {
mark::hit!(test_rename_struct_field_for_shorthand);
replacement_text.push_str(new_name);
replacement_text.push_str(": ");
TextRange::new(reference.range.start(), reference.range.start())
}
ReferenceKind::FieldShorthandForLocal => {
mark::hit!(test_rename_local_for_field_shorthand);
replacement_text.push_str(": ");
replacement_text.push_str(new_name);
TextRange::new(reference.range.end(), reference.range.end())
}
ReferenceKind::RecordFieldExprOrPat => {
mark::hit!(test_rename_field_expr_pat);
replacement_text.push_str(new_name);
edit_text_range_for_record_field_expr_or_pat(
sema,
FileRange { file_id, range: reference.range },
new_name,
)
}
_ => {
replacement_text.push_str(new_name);
reference.range
}
};
edit.replace(range, replacement_text);
}
SourceFileEdit { file_id, edit: edit.finish() }
}
fn edit_text_range_for_record_field_expr_or_pat(
@ -277,9 +284,9 @@ fn rename_mod(
let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
let ref_edits = refs
.references
.into_iter()
.map(|reference| source_edit_from_reference(sema, reference, new_name));
.references()
.iter()
.map(|reference| source_edit_from_references(sema, reference, new_name));
source_file_edits.extend(ref_edits);
Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
@ -331,17 +338,10 @@ fn rename_to_self(
let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs
.into_iter()
.partition(|reference| param_range.intersect(reference.file_range.range).is_some());
if param_ref.is_empty() {
bail!("Parameter to rename not found");
}
let mut edits = usages
.into_iter()
.map(|reference| source_edit_from_reference(sema, reference, "self"))
let mut edits = refs
.references()
.iter()
.map(|reference| source_edit_from_references(sema, reference, "self"))
.collect::<Vec<_>>();
edits.push(SourceFileEdit {
@ -467,7 +467,7 @@ fn rename_reference(
let edit = refs
.into_iter()
.map(|reference| source_edit_from_reference(sema, reference, new_name))
.map(|reference| source_edit_from_references(sema, &reference, new_name))
.collect::<Vec<_>>();
Ok(RangeInfo::new(range, SourceChange::from(edit)))