Use name resolution for goto definition

This commit is contained in:
Florian Diebold 2019-01-08 00:30:49 +01:00 committed by Florian Diebold
parent dc2a8d5acc
commit a6590ce231
7 changed files with 179 additions and 16 deletions

View file

@ -15,11 +15,11 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C
match def_id.resolve(ctx.db)? {
hir::Def::Module(module) => {
let module_scope = module.scope(ctx.db)?;
module_scope.entries().for_each(|(name, res)| {
for (name, res) in module_scope.entries() {
CompletionItem::new(CompletionKind::Reference, name.to_string())
.from_resolution(ctx, res)
.add_to(acc)
});
.add_to(acc);
}
}
hir::Def::Enum(e) => {
e.variants(ctx.db)?

View file

@ -42,6 +42,24 @@ pub(crate) fn reference_definition(
return Ok(vec![nav]);
};
}
// Then try module name resolution
if let Some(module) =
hir::source_binder::module_from_child_node(db, file_id, name_ref.syntax())?
{
if let Some(path) = name_ref
.syntax()
.ancestors()
.find_map(ast::Path::cast)
.and_then(hir::Path::from_ast)
{
let resolved = module.resolve_path(db, &path)?;
if let Some(def_id) = resolved.take_types().or(resolved.take_values()) {
if let Some(target) = NavigationTarget::from_def(db, def_id.resolve(db)?)? {
return Ok(vec![target]);
}
}
}
}
// If that fails try the index based approach.
let navs = db
.index_resolve(name_ref)?
@ -104,6 +122,31 @@ mod tests {
);
}
#[test]
fn goto_definition_resolves_correct_name() {
let (analysis, pos) = analysis_and_position(
"
//- /lib.rs
use a::Foo;
mod a;
mod b;
enum E { X(Foo<|>) }
//- /a.rs
struct Foo;
//- /b.rs
struct Foo;
",
);
let symbols = analysis.goto_definition(pos).unwrap().unwrap();
assert_eq_dbg(
r#"[NavigationTarget { file_id: FileId(2), name: "Foo",
kind: STRUCT_DEF, range: [0; 11),
ptr: Some(LocalSyntaxPtr { range: [0; 11), kind: STRUCT_DEF }) }]"#,
&symbols,
);
}
#[test]
fn goto_definition_works_for_module_declaration() {
let (analysis, pos) = analysis_and_position(

View file

@ -33,7 +33,8 @@ mod syntax_highlighting;
use std::{fmt, sync::Arc};
use ra_syntax::{SmolStr, SourceFile, TreePtr, SyntaxKind, TextRange, TextUnit};
use hir::{Def, ModuleSource, Name};
use ra_syntax::{SmolStr, SourceFile, TreePtr, SyntaxKind, SyntaxNode, TextRange, TextUnit, AstNode};
use ra_text_edit::TextEdit;
use ra_db::{SyntaxDatabase, FilesDatabase, LocalSyntaxPtr, BaseDatabase};
use rayon::prelude::*;
@ -268,6 +269,67 @@ impl NavigationTarget {
}
}
fn from_syntax(name: Option<Name>, file_id: FileId, node: &SyntaxNode) -> NavigationTarget {
NavigationTarget {
file_id,
name: name.map(|n| n.to_string().into()).unwrap_or("".into()),
kind: node.kind(),
range: node.range(),
ptr: Some(LocalSyntaxPtr::new(node)),
}
}
// TODO once Def::Item is gone, this should be able to always return a NavigationTarget
fn from_def(db: &db::RootDatabase, def: Def) -> Cancelable<Option<NavigationTarget>> {
Ok(match def {
Def::Struct(s) => {
let (file_id, node) = s.source(db)?;
Some(NavigationTarget::from_syntax(
s.name(db)?,
file_id.original_file(db),
node.syntax(),
))
}
Def::Enum(e) => {
let (file_id, node) = e.source(db)?;
Some(NavigationTarget::from_syntax(
e.name(db)?,
file_id.original_file(db),
node.syntax(),
))
}
Def::EnumVariant(ev) => {
let (file_id, node) = ev.source(db)?;
Some(NavigationTarget::from_syntax(
ev.name(db)?,
file_id.original_file(db),
node.syntax(),
))
}
Def::Function(f) => {
let (file_id, node) = f.source(db)?;
let name = f.signature(db).name().clone();
Some(NavigationTarget::from_syntax(
Some(name),
file_id.original_file(db),
node.syntax(),
))
}
Def::Module(m) => {
let (file_id, source) = m.definition_source(db)?;
let name = m.name(db)?;
match source {
ModuleSource::SourceFile(node) => {
Some(NavigationTarget::from_syntax(name, file_id, node.syntax()))
}
ModuleSource::Module(node) => {
Some(NavigationTarget::from_syntax(name, file_id, node.syntax()))
}
}
}
Def::Item => None,
})
}
pub fn name(&self) -> &SmolStr {
&self.name
}