From 2a3abe2ce3ebaa0411012cc1be6829c9cb6ea16f Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Sat, 23 Feb 2019 11:02:42 +0200 Subject: Fix goto def not working when cursor was over the name of a def We now allow goto_definition to return the named NavigationTarget if the cursor is on the name of a definition. --- crates/ra_ide_api/src/goto_definition.rs | 127 ++++++++++++++++++++++++++++- crates/ra_ide_api/src/navigation_target.rs | 3 +- 2 files changed, 126 insertions(+), 4 deletions(-) (limited to 'crates/ra_ide_api') diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 96ed8c8e9..a4ee77d94 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs @@ -1,7 +1,8 @@ use ra_db::{FileId, SourceDatabase}; use ra_syntax::{ - AstNode, ast, - algo::find_node_at_offset, + AstNode, ast::{self, NameOwner}, + algo::{find_node_at_offset, visit::{visitor, Visitor}}, + SyntaxNode, }; use test_utils::tested_by; use hir::Resolution; @@ -114,7 +115,9 @@ fn name_definition( file_id: FileId, name: &ast::Name, ) -> Option> { - if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { + let parent = name.syntax().parent()?; + + if let Some(module) = ast::Module::cast(&parent) { if module.has_semi() { if let Some(child_module) = hir::source_binder::module_from_declaration(db, file_id, module) @@ -124,9 +127,33 @@ fn name_definition( } } } + + if let Some(nav) = named_target(file_id, &parent) { + return Some(vec![nav]); + } + None } +fn named_target(file_id: FileId, node: &SyntaxNode) -> Option { + fn to_nav_target(node: &N, file_id: FileId) -> Option { + Some(NavigationTarget::from_named(file_id, node)) + } + + visitor() + .visit(|n: &ast::StructDef| to_nav_target(n, file_id)) + .visit(|n: &ast::EnumDef| to_nav_target(n, file_id)) + .visit(|n: &ast::EnumVariant| to_nav_target(n, file_id)) + .visit(|n: &ast::FnDef| to_nav_target(n, file_id)) + .visit(|n: &ast::TypeDef| to_nav_target(n, file_id)) + .visit(|n: &ast::ConstDef| to_nav_target(n, file_id)) + .visit(|n: &ast::StaticDef| to_nav_target(n, file_id)) + .visit(|n: &ast::TraitDef| to_nav_target(n, file_id)) + .visit(|n: &ast::NamedFieldDef| to_nav_target(n, file_id)) + .visit(|n: &ast::Module| to_nav_target(n, file_id)) + .accept(node)? +} + #[cfg(test)] mod tests { use test_utils::covers; @@ -231,4 +258,98 @@ mod tests { "spam NAMED_FIELD_DEF FileId(1) [17; 26) [17; 21)", ); } + + #[test] + fn goto_definition_works_when_used_on_definition_name_itself() { + check_goto( + " + //- /lib.rs + struct Foo<|> { value: u32 } + ", + "Foo STRUCT_DEF FileId(1) [0; 25) [7; 10)", + ); + + check_goto( + r#" + //- /lib.rs + struct Foo { + field<|>: string, + } + "#, + "field NAMED_FIELD_DEF FileId(1) [17; 30) [17; 22)", + ); + + check_goto( + " + //- /lib.rs + fn foo_test<|>() { + } + ", + "foo_test FN_DEF FileId(1) [0; 17) [3; 11)", + ); + + check_goto( + " + //- /lib.rs + enum Foo<|> { + Variant, + } + ", + "Foo ENUM_DEF FileId(1) [0; 25) [5; 8)", + ); + + check_goto( + " + //- /lib.rs + enum Foo { + Variant1, + Variant2<|>, + Variant3, + } + ", + "Variant2 ENUM_VARIANT FileId(1) [29; 37) [29; 37)", + ); + + check_goto( + r#" + //- /lib.rs + static inner<|>: &str = ""; + "#, + "inner STATIC_DEF FileId(1) [0; 24) [7; 12)", + ); + + check_goto( + r#" + //- /lib.rs + const inner<|>: &str = ""; + "#, + "inner CONST_DEF FileId(1) [0; 23) [6; 11)", + ); + + check_goto( + r#" + //- /lib.rs + type Thing<|> = Option<()>; + "#, + "Thing TYPE_DEF FileId(1) [0; 24) [5; 10)", + ); + + check_goto( + r#" + //- /lib.rs + trait Foo<|> { + } + "#, + "Foo TRAIT_DEF FileId(1) [0; 13) [6; 9)", + ); + + check_goto( + r#" + //- /lib.rs + mod bar<|> { + } + "#, + "bar MODULE FileId(1) [0; 11) [4; 7)", + ); + } } diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/navigation_target.rs index fd001179a..e9240283e 100644 --- a/crates/ra_ide_api/src/navigation_target.rs +++ b/crates/ra_ide_api/src/navigation_target.rs @@ -198,7 +198,8 @@ impl NavigationTarget { buf } - fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget { + /// Allows `NavigationTarget` to be created from a `NameOwner` + pub(crate) fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget { let name = node.name().map(|it| it.text().clone()).unwrap_or_default(); let focus_range = node.name().map(|it| it.syntax().range()); NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax()) -- cgit v1.2.3