2738: [Draft] Adds a way to limits reference search by StructLiteral r=matklad a=mikhail-m1

first draft for #2549 

Co-authored-by: Mikhail Modin <mikhailm1@gmail.com>
This commit is contained in:
bors[bot] 2020-01-08 14:33:05 +00:00 committed by GitHub
commit 928ecd069a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 100 additions and 18 deletions

View file

@ -18,7 +18,10 @@ use hir::InFile;
use once_cell::unsync::Lazy;
use ra_db::{SourceDatabase, SourceDatabaseExt};
use ra_prof::profile;
use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode, TextUnit};
use ra_syntax::{
algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextUnit,
TokenAtOffset,
};
use crate::{
db::RootDatabase, display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo,
@ -35,7 +38,20 @@ pub use self::search_scope::SearchScope;
#[derive(Debug, Clone)]
pub struct ReferenceSearchResult {
declaration: NavigationTarget,
references: Vec<FileRange>,
declaration_kind: ReferenceKind,
references: Vec<Reference>,
}
#[derive(Debug, Clone)]
pub struct Reference {
pub file_range: FileRange,
pub kind: ReferenceKind,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ReferenceKind {
StructLiteral,
Other,
}
impl ReferenceSearchResult {
@ -43,7 +59,7 @@ impl ReferenceSearchResult {
&self.declaration
}
pub fn references(&self) -> &[FileRange] {
pub fn references(&self) -> &[Reference] {
&self.references
}
@ -58,12 +74,18 @@ impl ReferenceSearchResult {
// allow turning ReferenceSearchResult into an iterator
// over FileRanges
impl IntoIterator for ReferenceSearchResult {
type Item = FileRange;
type IntoIter = std::vec::IntoIter<FileRange>;
type Item = Reference;
type IntoIter = std::vec::IntoIter<Reference>;
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.push(Reference {
file_range: FileRange {
file_id: self.declaration.file_id(),
range: self.declaration.range(),
},
kind: self.declaration_kind,
});
v.append(&mut self.references);
v.into_iter()
}
@ -71,11 +93,24 @@ impl IntoIterator for ReferenceSearchResult {
pub(crate) fn find_all_refs(
db: &RootDatabase,
position: FilePosition,
mut position: FilePosition,
search_scope: Option<SearchScope>,
) -> Option<RangeInfo<ReferenceSearchResult>> {
let parse = db.parse(position.file_id);
let syntax = parse.tree().syntax().clone();
let token = syntax.token_at_offset(position.offset);
let mut search_kind = ReferenceKind::Other;
if let TokenAtOffset::Between(ref left, ref right) = token {
if (right.kind() == SyntaxKind::L_CURLY || right.kind() == SyntaxKind::L_PAREN)
&& left.kind() != SyntaxKind::IDENT
{
position = FilePosition { offset: left.text_range().start(), ..position };
search_kind = ReferenceKind::StructLiteral;
}
}
let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?;
let declaration = match def.kind {
@ -96,9 +131,15 @@ pub(crate) fn find_all_refs(
}
};
let references = process_definition(db, def, name, search_scope);
let references = process_definition(db, def, name, search_scope)
.into_iter()
.filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)
.collect();
Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references }))
Some(RangeInfo::new(
range,
ReferenceSearchResult { declaration, references, declaration_kind: ReferenceKind::Other },
))
}
fn find_name<'a>(
@ -122,7 +163,7 @@ fn process_definition(
def: NameDefinition,
name: String,
scope: SearchScope,
) -> Vec<FileRange> {
) -> Vec<Reference> {
let _p = profile("process_definition");
let pat = name.as_str();
@ -146,7 +187,21 @@ fn process_definition(
}
if let Some(d) = classify_name_ref(db, InFile::new(file_id.into(), &name_ref)) {
if d == def {
refs.push(FileRange { file_id, range });
let kind = if name_ref
.syntax()
.ancestors()
.find_map(ast::RecordLit::cast)
.and_then(|l| l.path())
.and_then(|p| p.segment())
.and_then(|p| p.name_ref())
.map(|n| n == name_ref)
.unwrap_or(false)
{
ReferenceKind::StructLiteral
} else {
ReferenceKind::Other
};
refs.push(Reference { file_range: FileRange { file_id, range }, kind });
}
}
}
@ -162,6 +217,24 @@ mod tests {
ReferenceSearchResult, SearchScope,
};
#[test]
fn test_struct_literal() {
let code = r#"
struct Foo <|>{
a: i32,
}
impl Foo {
fn f() -> i32 { 42 }
}
fn main() {
let f: Foo;
f = Foo {a: Foo::f()};
}"#;
let refs = get_all_refs(code);
assert_eq!(refs.len(), 2);
}
#[test]
fn test_find_all_refs_for_local() {
let code = r#"

View file

@ -110,7 +110,13 @@ fn rename_reference(
let edit = refs
.into_iter()
.map(|range| source_edit_from_file_id_range(range.file_id, range.range, new_name))
.map(|reference| {
source_edit_from_file_id_range(
reference.file_range.file_id,
reference.file_range.range,
new_name,
)
})
.collect::<Vec<_>>();
if edit.is_empty() {

View file

@ -531,8 +531,8 @@ pub fn handle_references(
let locations = if params.context.include_declaration {
refs.into_iter()
.filter_map(|r| {
let line_index = world.analysis().file_line_index(r.file_id).ok()?;
to_location(r.file_id, r.range, &world, &line_index).ok()
let line_index = world.analysis().file_line_index(r.file_range.file_id).ok()?;
to_location(r.file_range.file_id, r.file_range.range, &world, &line_index).ok()
})
.collect()
} else {
@ -540,8 +540,8 @@ pub fn handle_references(
refs.references()
.iter()
.filter_map(|r| {
let line_index = world.analysis().file_line_index(r.file_id).ok()?;
to_location(r.file_id, r.range, &world, &line_index).ok()
let line_index = world.analysis().file_line_index(r.file_range.file_id).ok()?;
to_location(r.file_range.file_id, r.file_range.range, &world, &line_index).ok()
})
.collect()
};
@ -830,8 +830,11 @@ pub fn handle_document_highlight(
Ok(Some(
refs.into_iter()
.filter(|r| r.file_id == file_id)
.map(|r| DocumentHighlight { range: r.range.conv_with(&line_index), kind: None })
.filter(|r| r.file_range.file_id == file_id)
.map(|r| DocumentHighlight {
range: r.file_range.range.conv_with(&line_index),
kind: None,
})
.collect(),
))
}