diff options
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 44 | ||||
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 82 | ||||
-rw-r--r-- | crates/ra_ide_api/src/marks.rs | 1 |
3 files changed, 124 insertions, 3 deletions
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index da33739be..6fa430754 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -74,6 +74,30 @@ pub(crate) fn reference_definition( | |||
74 | return Exact(NavigationTarget::from_field(db, field)); | 74 | return Exact(NavigationTarget::from_field(db, field)); |
75 | }; | 75 | }; |
76 | } | 76 | } |
77 | |||
78 | // It could also be a named field | ||
79 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) { | ||
80 | tested_by!(goto_definition_works_for_named_fields); | ||
81 | |||
82 | let infer_result = function.infer(db); | ||
83 | let syntax_mapping = function.body_syntax_mapping(db); | ||
84 | |||
85 | let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast); | ||
86 | |||
87 | if let Some(expr) = struct_lit.and_then(|lit| syntax_mapping.node_expr(lit.into())) { | ||
88 | let ty = infer_result[expr].clone(); | ||
89 | if let hir::Ty::Adt { def_id, .. } = ty { | ||
90 | if let hir::AdtDef::Struct(s) = def_id { | ||
91 | let hir_path = hir::Path::from_name_ref(name_ref); | ||
92 | let hir_name = hir_path.as_ident().unwrap(); | ||
93 | |||
94 | if let Some(field) = s.field(db, hir_name) { | ||
95 | return Exact(NavigationTarget::from_field(db, field)); | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | } | ||
77 | } | 101 | } |
78 | // Try name resolution | 102 | // Try name resolution |
79 | let resolver = hir::source_binder::resolver_for_node(db, file_id, name_ref.syntax()); | 103 | let resolver = hir::source_binder::resolver_for_node(db, file_id, name_ref.syntax()); |
@@ -256,6 +280,26 @@ mod tests { | |||
256 | } | 280 | } |
257 | 281 | ||
258 | #[test] | 282 | #[test] |
283 | fn goto_definition_works_for_named_fields() { | ||
284 | covers!(goto_definition_works_for_named_fields); | ||
285 | check_goto( | ||
286 | " | ||
287 | //- /lib.rs | ||
288 | struct Foo { | ||
289 | spam: u32, | ||
290 | } | ||
291 | |||
292 | fn bar() -> Foo { | ||
293 | Foo { | ||
294 | spam<|>: 0, | ||
295 | } | ||
296 | } | ||
297 | ", | ||
298 | "spam NAMED_FIELD_DEF FileId(1) [17; 26) [17; 21)", | ||
299 | ); | ||
300 | } | ||
301 | |||
302 | #[test] | ||
259 | fn goto_definition_works_when_used_on_definition_name_itself() { | 303 | fn goto_definition_works_when_used_on_definition_name_itself() { |
260 | check_goto( | 304 | check_goto( |
261 | " | 305 | " |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index ef3b5df29..47913d753 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use ra_db::SourceDatabase; | 1 | use ra_db::SourceDatabase; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | AstNode, SyntaxNode, TreeArc, ast::{self, NameOwner, VisibilityOwner}, | 3 | AstNode, SyntaxNode, TreeArc, ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, |
4 | algo::{find_covering_node, find_node_at_offset, find_leaf_at_offset, visit::{visitor, Visitor}}, | 4 | algo::{find_covering_node, find_node_at_offset, find_leaf_at_offset, visit::{visitor, Visitor}}, |
5 | }; | 5 | }; |
6 | 6 | ||
@@ -179,6 +179,7 @@ impl NavigationTarget { | |||
179 | .visit(doc_comments::<ast::TypeAliasDef>) | 179 | .visit(doc_comments::<ast::TypeAliasDef>) |
180 | .visit(doc_comments::<ast::ConstDef>) | 180 | .visit(doc_comments::<ast::ConstDef>) |
181 | .visit(doc_comments::<ast::StaticDef>) | 181 | .visit(doc_comments::<ast::StaticDef>) |
182 | .visit(doc_comments::<ast::NamedFieldDef>) | ||
182 | .accept(&node)? | 183 | .accept(&node)? |
183 | } | 184 | } |
184 | 185 | ||
@@ -189,6 +190,20 @@ impl NavigationTarget { | |||
189 | // TODO: After type inference is done, add type information to improve the output | 190 | // TODO: After type inference is done, add type information to improve the output |
190 | let node = self.node(db)?; | 191 | let node = self.node(db)?; |
191 | 192 | ||
193 | fn visit_ascribed_node<T>(node: &T, prefix: &str) -> Option<String> | ||
194 | where | ||
195 | T: NameOwner + VisibilityOwner + TypeAscriptionOwner, | ||
196 | { | ||
197 | let mut string = visit_node(node, prefix)?; | ||
198 | |||
199 | if let Some(type_ref) = node.ascribed_type() { | ||
200 | string.push_str(": "); | ||
201 | type_ref.syntax().text().push_to(&mut string); | ||
202 | } | ||
203 | |||
204 | Some(string) | ||
205 | } | ||
206 | |||
192 | fn visit_node<T>(node: &T, label: &str) -> Option<String> | 207 | fn visit_node<T>(node: &T, label: &str) -> Option<String> |
193 | where | 208 | where |
194 | T: NameOwner + VisibilityOwner, | 209 | T: NameOwner + VisibilityOwner, |
@@ -207,8 +222,9 @@ impl NavigationTarget { | |||
207 | .visit(|node: &ast::TraitDef| visit_node(node, "trait ")) | 222 | .visit(|node: &ast::TraitDef| visit_node(node, "trait ")) |
208 | .visit(|node: &ast::Module| visit_node(node, "mod ")) | 223 | .visit(|node: &ast::Module| visit_node(node, "mod ")) |
209 | .visit(|node: &ast::TypeAliasDef| visit_node(node, "type ")) | 224 | .visit(|node: &ast::TypeAliasDef| visit_node(node, "type ")) |
210 | .visit(|node: &ast::ConstDef| visit_node(node, "const ")) | 225 | .visit(|node: &ast::ConstDef| visit_ascribed_node(node, "const ")) |
211 | .visit(|node: &ast::StaticDef| visit_node(node, "static ")) | 226 | .visit(|node: &ast::StaticDef| visit_ascribed_node(node, "static ")) |
227 | .visit(|node: &ast::NamedFieldDef| visit_ascribed_node(node, "")) | ||
212 | .accept(&node)? | 228 | .accept(&node)? |
213 | } | 229 | } |
214 | } | 230 | } |
@@ -321,6 +337,66 @@ mod tests { | |||
321 | } | 337 | } |
322 | 338 | ||
323 | #[test] | 339 | #[test] |
340 | fn hover_shows_struct_field_info() { | ||
341 | // Hovering over the field when instantiating | ||
342 | check_hover_result( | ||
343 | r#" | ||
344 | //- /main.rs | ||
345 | struct Foo { | ||
346 | field_a: u32, | ||
347 | } | ||
348 | |||
349 | fn main() { | ||
350 | let foo = Foo { | ||
351 | field_a<|>: 0, | ||
352 | }; | ||
353 | } | ||
354 | "#, | ||
355 | &["field_a: u32"], | ||
356 | ); | ||
357 | |||
358 | // Hovering over the field in the definition | ||
359 | check_hover_result( | ||
360 | r#" | ||
361 | //- /main.rs | ||
362 | struct Foo { | ||
363 | field_a<|>: u32, | ||
364 | } | ||
365 | |||
366 | fn main() { | ||
367 | let foo = Foo { | ||
368 | field_a: 0, | ||
369 | }; | ||
370 | } | ||
371 | "#, | ||
372 | &["field_a: u32"], | ||
373 | ); | ||
374 | } | ||
375 | |||
376 | #[test] | ||
377 | fn hover_const_static() { | ||
378 | check_hover_result( | ||
379 | r#" | ||
380 | //- /main.rs | ||
381 | fn main() { | ||
382 | const foo<|>: u32 = 0; | ||
383 | } | ||
384 | "#, | ||
385 | &["const foo: u32"], | ||
386 | ); | ||
387 | |||
388 | check_hover_result( | ||
389 | r#" | ||
390 | //- /main.rs | ||
391 | fn main() { | ||
392 | static foo<|>: u32 = 0; | ||
393 | } | ||
394 | "#, | ||
395 | &["static foo: u32"], | ||
396 | ); | ||
397 | } | ||
398 | |||
399 | #[test] | ||
324 | fn hover_some() { | 400 | fn hover_some() { |
325 | let (analysis, position) = single_file_with_position( | 401 | let (analysis, position) = single_file_with_position( |
326 | " | 402 | " |
diff --git a/crates/ra_ide_api/src/marks.rs b/crates/ra_ide_api/src/marks.rs index 44d8fdf82..bcbe0d21b 100644 --- a/crates/ra_ide_api/src/marks.rs +++ b/crates/ra_ide_api/src/marks.rs | |||
@@ -2,6 +2,7 @@ test_utils::marks!( | |||
2 | inserts_parens_for_function_calls | 2 | inserts_parens_for_function_calls |
3 | goto_definition_works_for_methods | 3 | goto_definition_works_for_methods |
4 | goto_definition_works_for_fields | 4 | goto_definition_works_for_fields |
5 | goto_definition_works_for_named_fields | ||
5 | call_info_bad_offset | 6 | call_info_bad_offset |
6 | dont_complete_current_use | 7 | dont_complete_current_use |
7 | ); | 8 | ); |