aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs127
-rw-r--r--crates/ra_ide_api/src/navigation_target.rs3
2 files changed, 126 insertions, 4 deletions
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 @@
1use ra_db::{FileId, SourceDatabase}; 1use ra_db::{FileId, SourceDatabase};
2use ra_syntax::{ 2use ra_syntax::{
3 AstNode, ast, 3 AstNode, ast::{self, NameOwner},
4 algo::find_node_at_offset, 4 algo::{find_node_at_offset, visit::{visitor, Visitor}},
5 SyntaxNode,
5}; 6};
6use test_utils::tested_by; 7use test_utils::tested_by;
7use hir::Resolution; 8use hir::Resolution;
@@ -114,7 +115,9 @@ fn name_definition(
114 file_id: FileId, 115 file_id: FileId,
115 name: &ast::Name, 116 name: &ast::Name,
116) -> Option<Vec<NavigationTarget>> { 117) -> Option<Vec<NavigationTarget>> {
117 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { 118 let parent = name.syntax().parent()?;
119
120 if let Some(module) = ast::Module::cast(&parent) {
118 if module.has_semi() { 121 if module.has_semi() {
119 if let Some(child_module) = 122 if let Some(child_module) =
120 hir::source_binder::module_from_declaration(db, file_id, module) 123 hir::source_binder::module_from_declaration(db, file_id, module)
@@ -124,9 +127,33 @@ fn name_definition(
124 } 127 }
125 } 128 }
126 } 129 }
130
131 if let Some(nav) = named_target(file_id, &parent) {
132 return Some(vec![nav]);
133 }
134
127 None 135 None
128} 136}
129 137
138fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> {
139 fn to_nav_target<N: NameOwner>(node: &N, file_id: FileId) -> Option<NavigationTarget> {
140 Some(NavigationTarget::from_named(file_id, node))
141 }
142
143 visitor()
144 .visit(|n: &ast::StructDef| to_nav_target(n, file_id))
145 .visit(|n: &ast::EnumDef| to_nav_target(n, file_id))
146 .visit(|n: &ast::EnumVariant| to_nav_target(n, file_id))
147 .visit(|n: &ast::FnDef| to_nav_target(n, file_id))
148 .visit(|n: &ast::TypeDef| to_nav_target(n, file_id))
149 .visit(|n: &ast::ConstDef| to_nav_target(n, file_id))
150 .visit(|n: &ast::StaticDef| to_nav_target(n, file_id))
151 .visit(|n: &ast::TraitDef| to_nav_target(n, file_id))
152 .visit(|n: &ast::NamedFieldDef| to_nav_target(n, file_id))
153 .visit(|n: &ast::Module| to_nav_target(n, file_id))
154 .accept(node)?
155}
156
130#[cfg(test)] 157#[cfg(test)]
131mod tests { 158mod tests {
132 use test_utils::covers; 159 use test_utils::covers;
@@ -231,4 +258,98 @@ mod tests {
231 "spam NAMED_FIELD_DEF FileId(1) [17; 26) [17; 21)", 258 "spam NAMED_FIELD_DEF FileId(1) [17; 26) [17; 21)",
232 ); 259 );
233 } 260 }
261
262 #[test]
263 fn goto_definition_works_when_used_on_definition_name_itself() {
264 check_goto(
265 "
266 //- /lib.rs
267 struct Foo<|> { value: u32 }
268 ",
269 "Foo STRUCT_DEF FileId(1) [0; 25) [7; 10)",
270 );
271
272 check_goto(
273 r#"
274 //- /lib.rs
275 struct Foo {
276 field<|>: string,
277 }
278 "#,
279 "field NAMED_FIELD_DEF FileId(1) [17; 30) [17; 22)",
280 );
281
282 check_goto(
283 "
284 //- /lib.rs
285 fn foo_test<|>() {
286 }
287 ",
288 "foo_test FN_DEF FileId(1) [0; 17) [3; 11)",
289 );
290
291 check_goto(
292 "
293 //- /lib.rs
294 enum Foo<|> {
295 Variant,
296 }
297 ",
298 "Foo ENUM_DEF FileId(1) [0; 25) [5; 8)",
299 );
300
301 check_goto(
302 "
303 //- /lib.rs
304 enum Foo {
305 Variant1,
306 Variant2<|>,
307 Variant3,
308 }
309 ",
310 "Variant2 ENUM_VARIANT FileId(1) [29; 37) [29; 37)",
311 );
312
313 check_goto(
314 r#"
315 //- /lib.rs
316 static inner<|>: &str = "";
317 "#,
318 "inner STATIC_DEF FileId(1) [0; 24) [7; 12)",
319 );
320
321 check_goto(
322 r#"
323 //- /lib.rs
324 const inner<|>: &str = "";
325 "#,
326 "inner CONST_DEF FileId(1) [0; 23) [6; 11)",
327 );
328
329 check_goto(
330 r#"
331 //- /lib.rs
332 type Thing<|> = Option<()>;
333 "#,
334 "Thing TYPE_DEF FileId(1) [0; 24) [5; 10)",
335 );
336
337 check_goto(
338 r#"
339 //- /lib.rs
340 trait Foo<|> {
341 }
342 "#,
343 "Foo TRAIT_DEF FileId(1) [0; 13) [6; 9)",
344 );
345
346 check_goto(
347 r#"
348 //- /lib.rs
349 mod bar<|> {
350 }
351 "#,
352 "bar MODULE FileId(1) [0; 11) [4; 7)",
353 );
354 }
234} 355}
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 {
198 buf 198 buf
199 } 199 }
200 200
201 fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget { 201 /// Allows `NavigationTarget` to be created from a `NameOwner`
202 pub(crate) fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget {
202 let name = node.name().map(|it| it.text().clone()).unwrap_or_default(); 203 let name = node.name().map(|it| it.text().clone()).unwrap_or_default();
203 let focus_range = node.name().map(|it| it.syntax().range()); 204 let focus_range = node.name().map(|it| it.syntax().range());
204 NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax()) 205 NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax())