diff options
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 127 | ||||
-rw-r--r-- | crates/ra_ide_api/src/navigation_target.rs | 3 |
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 @@ | |||
1 | use ra_db::{FileId, SourceDatabase}; | 1 | use ra_db::{FileId, SourceDatabase}; |
2 | use ra_syntax::{ | 2 | use 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 | }; |
6 | use test_utils::tested_by; | 7 | use test_utils::tested_by; |
7 | use hir::Resolution; | 8 | use 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 | ||
138 | fn 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)] |
131 | mod tests { | 158 | mod 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()) |