diff options
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r-- | crates/ra_ide/src/completion/complete_dot.rs | 140 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_keyword.rs | 1 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_path.rs | 142 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_pattern.rs | 18 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_postfix.rs | 65 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_record_literal.rs | 25 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_record_pattern.rs | 28 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_scope.rs | 70 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_context.rs | 111 | ||||
-rw-r--r-- | crates/ra_ide/src/display.rs | 14 | ||||
-rw-r--r-- | crates/ra_ide/src/hover.rs | 114 | ||||
-rw-r--r-- | crates/ra_ide/src/inlay_hints.rs | 32 | ||||
-rw-r--r-- | crates/ra_ide/src/lib.rs | 12 | ||||
-rw-r--r-- | crates/ra_ide/src/mock_analysis.rs | 10 | ||||
-rw-r--r-- | crates/ra_ide/src/parent_module.rs | 1 |
15 files changed, 703 insertions, 80 deletions
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 9145aa183..f275305e2 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs | |||
@@ -38,7 +38,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
38 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 38 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { |
39 | for receiver in receiver.autoderef(ctx.db) { | 39 | for receiver in receiver.autoderef(ctx.db) { |
40 | for (field, ty) in receiver.fields(ctx.db) { | 40 | for (field, ty) in receiver.fields(ctx.db) { |
41 | if ctx.module.map_or(false, |m| !field.is_visible_from(ctx.db, m)) { | 41 | if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { |
42 | // Skip private field. FIXME: If the definition location of the | 42 | // Skip private field. FIXME: If the definition location of the |
43 | // field is editable, we should show the completion | 43 | // field is editable, we should show the completion |
44 | continue; | 44 | continue; |
@@ -53,11 +53,14 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty | |||
53 | } | 53 | } |
54 | 54 | ||
55 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 55 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { |
56 | if let Some(krate) = ctx.module.map(|it| it.krate()) { | 56 | if let Some(krate) = ctx.krate { |
57 | let mut seen_methods = FxHashSet::default(); | 57 | let mut seen_methods = FxHashSet::default(); |
58 | let traits_in_scope = ctx.scope().traits_in_scope(); | 58 | let traits_in_scope = ctx.scope().traits_in_scope(); |
59 | receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { | 59 | receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { |
60 | if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) { | 60 | if func.has_self_param(ctx.db) |
61 | && ctx.scope().module().map_or(true, |m| func.is_visible_from(ctx.db, m)) | ||
62 | && seen_methods.insert(func.name(ctx.db)) | ||
63 | { | ||
61 | acc.add_function(ctx, func); | 64 | acc.add_function(ctx, func); |
62 | } | 65 | } |
63 | None::<()> | 66 | None::<()> |
@@ -308,6 +311,39 @@ mod tests { | |||
308 | } | 311 | } |
309 | 312 | ||
310 | #[test] | 313 | #[test] |
314 | fn test_method_completion_private() { | ||
315 | assert_debug_snapshot!( | ||
316 | do_ref_completion( | ||
317 | r" | ||
318 | struct A {} | ||
319 | mod m { | ||
320 | impl super::A { | ||
321 | fn private_method(&self) {} | ||
322 | pub(super) fn the_method(&self) {} | ||
323 | } | ||
324 | } | ||
325 | fn foo(a: A) { | ||
326 | a.<|> | ||
327 | } | ||
328 | ", | ||
329 | ), | ||
330 | @r###" | ||
331 | [ | ||
332 | CompletionItem { | ||
333 | label: "the_method()", | ||
334 | source_range: [256; 256), | ||
335 | delete: [256; 256), | ||
336 | insert: "the_method()$0", | ||
337 | kind: Method, | ||
338 | lookup: "the_method", | ||
339 | detail: "pub(super) fn the_method(&self)", | ||
340 | }, | ||
341 | ] | ||
342 | "### | ||
343 | ); | ||
344 | } | ||
345 | |||
346 | #[test] | ||
311 | fn test_trait_method_completion() { | 347 | fn test_trait_method_completion() { |
312 | assert_debug_snapshot!( | 348 | assert_debug_snapshot!( |
313 | do_ref_completion( | 349 | do_ref_completion( |
@@ -584,4 +620,102 @@ mod tests { | |||
584 | "### | 620 | "### |
585 | ); | 621 | ); |
586 | } | 622 | } |
623 | |||
624 | #[test] | ||
625 | fn works_in_simple_macro_1() { | ||
626 | assert_debug_snapshot!( | ||
627 | do_ref_completion( | ||
628 | r" | ||
629 | macro_rules! m { ($e:expr) => { $e } } | ||
630 | struct A { the_field: u32 } | ||
631 | fn foo(a: A) { | ||
632 | m!(a.x<|>) | ||
633 | } | ||
634 | ", | ||
635 | ), | ||
636 | @r###" | ||
637 | [ | ||
638 | CompletionItem { | ||
639 | label: "the_field", | ||
640 | source_range: [156; 157), | ||
641 | delete: [156; 157), | ||
642 | insert: "the_field", | ||
643 | kind: Field, | ||
644 | detail: "u32", | ||
645 | }, | ||
646 | ] | ||
647 | "### | ||
648 | ); | ||
649 | } | ||
650 | |||
651 | #[test] | ||
652 | fn works_in_simple_macro_recursive() { | ||
653 | assert_debug_snapshot!( | ||
654 | do_ref_completion( | ||
655 | r" | ||
656 | macro_rules! m { ($e:expr) => { $e } } | ||
657 | struct A { the_field: u32 } | ||
658 | fn foo(a: A) { | ||
659 | m!(a.x<|>) | ||
660 | } | ||
661 | ", | ||
662 | ), | ||
663 | @r###" | ||
664 | [ | ||
665 | CompletionItem { | ||
666 | label: "the_field", | ||
667 | source_range: [156; 157), | ||
668 | delete: [156; 157), | ||
669 | insert: "the_field", | ||
670 | kind: Field, | ||
671 | detail: "u32", | ||
672 | }, | ||
673 | ] | ||
674 | "### | ||
675 | ); | ||
676 | } | ||
677 | |||
678 | #[test] | ||
679 | fn works_in_simple_macro_2() { | ||
680 | // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery | ||
681 | assert_debug_snapshot!( | ||
682 | do_ref_completion( | ||
683 | r" | ||
684 | macro_rules! m { ($e:expr) => { $e } } | ||
685 | struct A { the_field: u32 } | ||
686 | fn foo(a: A) { | ||
687 | m!(a.<|>) | ||
688 | } | ||
689 | ", | ||
690 | ), | ||
691 | @r###"[]"### | ||
692 | ); | ||
693 | } | ||
694 | |||
695 | #[test] | ||
696 | fn works_in_simple_macro_recursive_1() { | ||
697 | assert_debug_snapshot!( | ||
698 | do_ref_completion( | ||
699 | r" | ||
700 | macro_rules! m { ($e:expr) => { $e } } | ||
701 | struct A { the_field: u32 } | ||
702 | fn foo(a: A) { | ||
703 | m!(m!(m!(a.x<|>))) | ||
704 | } | ||
705 | ", | ||
706 | ), | ||
707 | @r###" | ||
708 | [ | ||
709 | CompletionItem { | ||
710 | label: "the_field", | ||
711 | source_range: [162; 163), | ||
712 | delete: [162; 163), | ||
713 | insert: "the_field", | ||
714 | kind: Field, | ||
715 | detail: "u32", | ||
716 | }, | ||
717 | ] | ||
718 | "### | ||
719 | ); | ||
720 | } | ||
587 | } | 721 | } |
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index eb7cd9ac2..e1c0ffb1f 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs | |||
@@ -79,6 +79,7 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte | |||
79 | } | 79 | } |
80 | 80 | ||
81 | fn is_in_loop_body(leaf: &SyntaxToken) -> bool { | 81 | fn is_in_loop_body(leaf: &SyntaxToken) -> bool { |
82 | // FIXME move this to CompletionContext and make it handle macros | ||
82 | for node in leaf.parent().ancestors() { | 83 | for node in leaf.parent().ancestors() { |
83 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { | 84 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { |
84 | break; | 85 | break; |
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs index 1a9699466..3c4a70561 100644 --- a/crates/ra_ide/src/completion/complete_path.rs +++ b/crates/ra_ide/src/completion/complete_path.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! Completion of paths, including when writing a single name. | 1 | //! Completion of paths, i.e. `some::prefix::<|>`. |
2 | 2 | ||
3 | use hir::{Adt, PathResolution, ScopeDef}; | 3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; |
4 | use ra_syntax::AstNode; | 4 | use ra_syntax::AstNode; |
5 | use test_utils::tested_by; | 5 | use test_utils::tested_by; |
6 | 6 | ||
@@ -15,9 +15,10 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
15 | Some(PathResolution::Def(def)) => def, | 15 | Some(PathResolution::Def(def)) => def, |
16 | _ => return, | 16 | _ => return, |
17 | }; | 17 | }; |
18 | let context_module = ctx.scope().module(); | ||
18 | match def { | 19 | match def { |
19 | hir::ModuleDef::Module(module) => { | 20 | hir::ModuleDef::Module(module) => { |
20 | let module_scope = module.scope(ctx.db); | 21 | let module_scope = module.scope(ctx.db, context_module); |
21 | for (name, def) in module_scope { | 22 | for (name, def) in module_scope { |
22 | if ctx.use_item_syntax.is_some() { | 23 | if ctx.use_item_syntax.is_some() { |
23 | if let ScopeDef::Unknown = def { | 24 | if let ScopeDef::Unknown = def { |
@@ -47,10 +48,13 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
47 | }; | 48 | }; |
48 | // Iterate assoc types separately | 49 | // Iterate assoc types separately |
49 | // FIXME: complete T::AssocType | 50 | // FIXME: complete T::AssocType |
50 | let krate = ctx.module.map(|m| m.krate()); | 51 | let krate = ctx.krate; |
51 | if let Some(krate) = krate { | 52 | if let Some(krate) = krate { |
52 | let traits_in_scope = ctx.scope().traits_in_scope(); | 53 | let traits_in_scope = ctx.scope().traits_in_scope(); |
53 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { | 54 | ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { |
55 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | ||
56 | return None; | ||
57 | } | ||
54 | match item { | 58 | match item { |
55 | hir::AssocItem::Function(func) => { | 59 | hir::AssocItem::Function(func) => { |
56 | if !func.has_self_param(ctx.db) { | 60 | if !func.has_self_param(ctx.db) { |
@@ -64,6 +68,9 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
64 | }); | 68 | }); |
65 | 69 | ||
66 | ty.iterate_impl_items(ctx.db, krate, |item| { | 70 | ty.iterate_impl_items(ctx.db, krate, |item| { |
71 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | ||
72 | return None; | ||
73 | } | ||
67 | match item { | 74 | match item { |
68 | hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} | 75 | hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {} |
69 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), | 76 | hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty), |
@@ -74,6 +81,9 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { | |||
74 | } | 81 | } |
75 | hir::ModuleDef::Trait(t) => { | 82 | hir::ModuleDef::Trait(t) => { |
76 | for item in t.items(ctx.db) { | 83 | for item in t.items(ctx.db) { |
84 | if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { | ||
85 | continue; | ||
86 | } | ||
77 | match item { | 87 | match item { |
78 | hir::AssocItem::Function(func) => { | 88 | hir::AssocItem::Function(func) => { |
79 | if !func.has_self_param(ctx.db) { | 89 | if !func.has_self_param(ctx.db) { |
@@ -170,6 +180,41 @@ mod tests { | |||
170 | } | 180 | } |
171 | 181 | ||
172 | #[test] | 182 | #[test] |
183 | fn path_visibility() { | ||
184 | assert_debug_snapshot!( | ||
185 | do_reference_completion( | ||
186 | r" | ||
187 | use self::my::<|>; | ||
188 | |||
189 | mod my { | ||
190 | struct Bar; | ||
191 | pub struct Foo; | ||
192 | pub use Bar as PublicBar; | ||
193 | } | ||
194 | " | ||
195 | ), | ||
196 | @r###" | ||
197 | [ | ||
198 | CompletionItem { | ||
199 | label: "Foo", | ||
200 | source_range: [31; 31), | ||
201 | delete: [31; 31), | ||
202 | insert: "Foo", | ||
203 | kind: Struct, | ||
204 | }, | ||
205 | CompletionItem { | ||
206 | label: "PublicBar", | ||
207 | source_range: [31; 31), | ||
208 | delete: [31; 31), | ||
209 | insert: "PublicBar", | ||
210 | kind: Struct, | ||
211 | }, | ||
212 | ] | ||
213 | "### | ||
214 | ); | ||
215 | } | ||
216 | |||
217 | #[test] | ||
173 | fn completes_use_item_starting_with_self() { | 218 | fn completes_use_item_starting_with_self() { |
174 | assert_debug_snapshot!( | 219 | assert_debug_snapshot!( |
175 | do_reference_completion( | 220 | do_reference_completion( |
@@ -177,7 +222,7 @@ mod tests { | |||
177 | use self::m::<|>; | 222 | use self::m::<|>; |
178 | 223 | ||
179 | mod m { | 224 | mod m { |
180 | struct Bar; | 225 | pub struct Bar; |
181 | } | 226 | } |
182 | " | 227 | " |
183 | ), | 228 | ), |
@@ -502,6 +547,60 @@ mod tests { | |||
502 | } | 547 | } |
503 | 548 | ||
504 | #[test] | 549 | #[test] |
550 | fn associated_item_visibility() { | ||
551 | assert_debug_snapshot!( | ||
552 | do_reference_completion( | ||
553 | " | ||
554 | //- /lib.rs | ||
555 | struct S; | ||
556 | |||
557 | mod m { | ||
558 | impl super::S { | ||
559 | pub(super) fn public_method() { } | ||
560 | fn private_method() { } | ||
561 | pub(super) type PublicType = u32; | ||
562 | type PrivateType = u32; | ||
563 | pub(super) const PUBLIC_CONST: u32 = 1; | ||
564 | const PRIVATE_CONST: u32 = 1; | ||
565 | } | ||
566 | } | ||
567 | |||
568 | fn foo() { let _ = S::<|> } | ||
569 | " | ||
570 | ), | ||
571 | @r###" | ||
572 | [ | ||
573 | CompletionItem { | ||
574 | label: "PUBLIC_CONST", | ||
575 | source_range: [302; 302), | ||
576 | delete: [302; 302), | ||
577 | insert: "PUBLIC_CONST", | ||
578 | kind: Const, | ||
579 | detail: "pub(super) const PUBLIC_CONST: u32 = 1;", | ||
580 | }, | ||
581 | CompletionItem { | ||
582 | label: "PublicType", | ||
583 | source_range: [302; 302), | ||
584 | delete: [302; 302), | ||
585 | insert: "PublicType", | ||
586 | kind: TypeAlias, | ||
587 | detail: "pub(super) type PublicType = u32;", | ||
588 | }, | ||
589 | CompletionItem { | ||
590 | label: "public_method()", | ||
591 | source_range: [302; 302), | ||
592 | delete: [302; 302), | ||
593 | insert: "public_method()$0", | ||
594 | kind: Function, | ||
595 | lookup: "public_method", | ||
596 | detail: "pub(super) fn public_method()", | ||
597 | }, | ||
598 | ] | ||
599 | "### | ||
600 | ); | ||
601 | } | ||
602 | |||
603 | #[test] | ||
505 | fn completes_enum_associated_method() { | 604 | fn completes_enum_associated_method() { |
506 | assert_debug_snapshot!( | 605 | assert_debug_snapshot!( |
507 | do_reference_completion( | 606 | do_reference_completion( |
@@ -835,4 +934,37 @@ mod tests { | |||
835 | "### | 934 | "### |
836 | ); | 935 | ); |
837 | } | 936 | } |
937 | |||
938 | #[test] | ||
939 | fn completes_in_simple_macro_call() { | ||
940 | let completions = do_reference_completion( | ||
941 | r#" | ||
942 | macro_rules! m { ($e:expr) => { $e } } | ||
943 | fn main() { m!(self::f<|>); } | ||
944 | fn foo() {} | ||
945 | "#, | ||
946 | ); | ||
947 | assert_debug_snapshot!(completions, @r###" | ||
948 | [ | ||
949 | CompletionItem { | ||
950 | label: "foo()", | ||
951 | source_range: [93; 94), | ||
952 | delete: [93; 94), | ||
953 | insert: "foo()$0", | ||
954 | kind: Function, | ||
955 | lookup: "foo", | ||
956 | detail: "fn foo()", | ||
957 | }, | ||
958 | CompletionItem { | ||
959 | label: "main()", | ||
960 | source_range: [93; 94), | ||
961 | delete: [93; 94), | ||
962 | insert: "main()$0", | ||
963 | kind: Function, | ||
964 | lookup: "main", | ||
965 | detail: "fn main()", | ||
966 | }, | ||
967 | ] | ||
968 | "###); | ||
969 | } | ||
838 | } | 970 | } |
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs index c2c6ca002..fa8aeceda 100644 --- a/crates/ra_ide/src/completion/complete_pattern.rs +++ b/crates/ra_ide/src/completion/complete_pattern.rs | |||
@@ -86,4 +86,22 @@ mod tests { | |||
86 | ] | 86 | ] |
87 | "###); | 87 | "###); |
88 | } | 88 | } |
89 | |||
90 | #[test] | ||
91 | fn completes_in_simple_macro_call() { | ||
92 | // FIXME: doesn't work yet because of missing error recovery in macro expansion | ||
93 | let completions = complete( | ||
94 | r" | ||
95 | macro_rules! m { ($e:expr) => { $e } } | ||
96 | enum E { X } | ||
97 | |||
98 | fn foo() { | ||
99 | m!(match E::X { | ||
100 | <|> | ||
101 | }) | ||
102 | } | ||
103 | ", | ||
104 | ); | ||
105 | assert_debug_snapshot!(completions, @r###"[]"###); | ||
106 | } | ||
89 | } | 107 | } |
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs index 8a74f993a..65ecea125 100644 --- a/crates/ra_ide/src/completion/complete_postfix.rs +++ b/crates/ra_ide/src/completion/complete_postfix.rs | |||
@@ -67,8 +67,8 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
67 | 67 | ||
68 | fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { | 68 | fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { |
69 | let edit = { | 69 | let edit = { |
70 | let receiver_range = | 70 | let receiver_syntax = ctx.dot_receiver.as_ref().expect("no receiver available").syntax(); |
71 | ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range(); | 71 | let receiver_range = ctx.sema.original_range(receiver_syntax).range; |
72 | let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); | 72 | let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); |
73 | TextEdit::replace(delete_range, snippet.to_string()) | 73 | TextEdit::replace(delete_range, snippet.to_string()) |
74 | }; | 74 | }; |
@@ -279,4 +279,65 @@ mod tests { | |||
279 | "### | 279 | "### |
280 | ); | 280 | ); |
281 | } | 281 | } |
282 | |||
283 | #[test] | ||
284 | fn works_in_simple_macro() { | ||
285 | assert_debug_snapshot!( | ||
286 | do_postfix_completion( | ||
287 | r#" | ||
288 | macro_rules! m { ($e:expr) => { $e } } | ||
289 | fn main() { | ||
290 | let bar: u8 = 12; | ||
291 | m!(bar.b<|>) | ||
292 | } | ||
293 | "#, | ||
294 | ), | ||
295 | @r###" | ||
296 | [ | ||
297 | CompletionItem { | ||
298 | label: "box", | ||
299 | source_range: [149; 150), | ||
300 | delete: [145; 150), | ||
301 | insert: "Box::new(bar)", | ||
302 | detail: "Box::new(expr)", | ||
303 | }, | ||
304 | CompletionItem { | ||
305 | label: "dbg", | ||
306 | source_range: [149; 150), | ||
307 | delete: [145; 150), | ||
308 | insert: "dbg!(bar)", | ||
309 | detail: "dbg!(expr)", | ||
310 | }, | ||
311 | CompletionItem { | ||
312 | label: "match", | ||
313 | source_range: [149; 150), | ||
314 | delete: [145; 150), | ||
315 | insert: "match bar {\n ${1:_} => {$0\\},\n}", | ||
316 | detail: "match expr {}", | ||
317 | }, | ||
318 | CompletionItem { | ||
319 | label: "not", | ||
320 | source_range: [149; 150), | ||
321 | delete: [145; 150), | ||
322 | insert: "!bar", | ||
323 | detail: "!expr", | ||
324 | }, | ||
325 | CompletionItem { | ||
326 | label: "ref", | ||
327 | source_range: [149; 150), | ||
328 | delete: [145; 150), | ||
329 | insert: "&bar", | ||
330 | detail: "&expr", | ||
331 | }, | ||
332 | CompletionItem { | ||
333 | label: "refm", | ||
334 | source_range: [149; 150), | ||
335 | delete: [145; 150), | ||
336 | insert: "&mut bar", | ||
337 | detail: "&mut expr", | ||
338 | }, | ||
339 | ] | ||
340 | "### | ||
341 | ); | ||
342 | } | ||
282 | } | 343 | } |
diff --git a/crates/ra_ide/src/completion/complete_record_literal.rs b/crates/ra_ide/src/completion/complete_record_literal.rs index f98353d76..be6e4194f 100644 --- a/crates/ra_ide/src/completion/complete_record_literal.rs +++ b/crates/ra_ide/src/completion/complete_record_literal.rs | |||
@@ -153,4 +153,29 @@ mod tests { | |||
153 | ] | 153 | ] |
154 | "###); | 154 | "###); |
155 | } | 155 | } |
156 | |||
157 | #[test] | ||
158 | fn test_record_literal_field_in_simple_macro() { | ||
159 | let completions = complete( | ||
160 | r" | ||
161 | macro_rules! m { ($e:expr) => { $e } } | ||
162 | struct A { the_field: u32 } | ||
163 | fn foo() { | ||
164 | m!(A { the<|> }) | ||
165 | } | ||
166 | ", | ||
167 | ); | ||
168 | assert_debug_snapshot!(completions, @r###" | ||
169 | [ | ||
170 | CompletionItem { | ||
171 | label: "the_field", | ||
172 | source_range: [137; 140), | ||
173 | delete: [137; 140), | ||
174 | insert: "the_field", | ||
175 | kind: Field, | ||
176 | detail: "u32", | ||
177 | }, | ||
178 | ] | ||
179 | "###); | ||
180 | } | ||
156 | } | 181 | } |
diff --git a/crates/ra_ide/src/completion/complete_record_pattern.rs b/crates/ra_ide/src/completion/complete_record_pattern.rs index 9bdeae49f..687c57d3e 100644 --- a/crates/ra_ide/src/completion/complete_record_pattern.rs +++ b/crates/ra_ide/src/completion/complete_record_pattern.rs | |||
@@ -87,4 +87,32 @@ mod tests { | |||
87 | ] | 87 | ] |
88 | "###); | 88 | "###); |
89 | } | 89 | } |
90 | |||
91 | #[test] | ||
92 | fn test_record_pattern_field_in_simple_macro() { | ||
93 | let completions = complete( | ||
94 | r" | ||
95 | macro_rules! m { ($e:expr) => { $e } } | ||
96 | struct S { foo: u32 } | ||
97 | |||
98 | fn process(f: S) { | ||
99 | m!(match f { | ||
100 | S { f<|>: 92 } => (), | ||
101 | }) | ||
102 | } | ||
103 | ", | ||
104 | ); | ||
105 | assert_debug_snapshot!(completions, @r###" | ||
106 | [ | ||
107 | CompletionItem { | ||
108 | label: "foo", | ||
109 | source_range: [171; 172), | ||
110 | delete: [171; 172), | ||
111 | insert: "foo", | ||
112 | kind: Field, | ||
113 | detail: "u32", | ||
114 | }, | ||
115 | ] | ||
116 | "###); | ||
117 | } | ||
90 | } | 118 | } |
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index 2b9a0e556..eb3c8cf1b 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use crate::completion::{CompletionContext, Completions}; | 3 | use crate::completion::{CompletionContext, Completions}; |
4 | 4 | ||
@@ -797,4 +797,72 @@ mod tests { | |||
797 | "### | 797 | "### |
798 | ) | 798 | ) |
799 | } | 799 | } |
800 | |||
801 | #[test] | ||
802 | fn completes_in_simple_macro_1() { | ||
803 | assert_debug_snapshot!( | ||
804 | do_reference_completion( | ||
805 | r" | ||
806 | macro_rules! m { ($e:expr) => { $e } } | ||
807 | fn quux(x: i32) { | ||
808 | let y = 92; | ||
809 | m!(<|>); | ||
810 | } | ||
811 | " | ||
812 | ), | ||
813 | @"[]" | ||
814 | ); | ||
815 | } | ||
816 | |||
817 | #[test] | ||
818 | fn completes_in_simple_macro_2() { | ||
819 | assert_debug_snapshot!( | ||
820 | do_reference_completion( | ||
821 | r" | ||
822 | macro_rules! m { ($e:expr) => { $e } } | ||
823 | fn quux(x: i32) { | ||
824 | let y = 92; | ||
825 | m!(x<|>); | ||
826 | } | ||
827 | " | ||
828 | ), | ||
829 | @r###" | ||
830 | [ | ||
831 | CompletionItem { | ||
832 | label: "m!", | ||
833 | source_range: [145; 146), | ||
834 | delete: [145; 146), | ||
835 | insert: "m!($0)", | ||
836 | kind: Macro, | ||
837 | detail: "macro_rules! m", | ||
838 | }, | ||
839 | CompletionItem { | ||
840 | label: "quux(…)", | ||
841 | source_range: [145; 146), | ||
842 | delete: [145; 146), | ||
843 | insert: "quux(${1:x})$0", | ||
844 | kind: Function, | ||
845 | lookup: "quux", | ||
846 | detail: "fn quux(x: i32)", | ||
847 | }, | ||
848 | CompletionItem { | ||
849 | label: "x", | ||
850 | source_range: [145; 146), | ||
851 | delete: [145; 146), | ||
852 | insert: "x", | ||
853 | kind: Binding, | ||
854 | detail: "i32", | ||
855 | }, | ||
856 | CompletionItem { | ||
857 | label: "y", | ||
858 | source_range: [145; 146), | ||
859 | delete: [145; 146), | ||
860 | insert: "y", | ||
861 | kind: Binding, | ||
862 | detail: "i32", | ||
863 | }, | ||
864 | ] | ||
865 | "### | ||
866 | ); | ||
867 | } | ||
800 | } | 868 | } |
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 9aa5a705d..40535c09e 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs | |||
@@ -5,7 +5,7 @@ use ra_db::SourceDatabase; | |||
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | algo::{find_covering_element, find_node_at_offset}, | 7 | algo::{find_covering_element, find_node_at_offset}, |
8 | ast, AstNode, SourceFile, | 8 | ast, AstNode, |
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxNode, SyntaxToken, TextRange, TextUnit, | 10 | SyntaxNode, SyntaxToken, TextRange, TextUnit, |
11 | }; | 11 | }; |
@@ -20,8 +20,11 @@ pub(crate) struct CompletionContext<'a> { | |||
20 | pub(super) sema: Semantics<'a, RootDatabase>, | 20 | pub(super) sema: Semantics<'a, RootDatabase>, |
21 | pub(super) db: &'a RootDatabase, | 21 | pub(super) db: &'a RootDatabase, |
22 | pub(super) offset: TextUnit, | 22 | pub(super) offset: TextUnit, |
23 | /// The token before the cursor, in the original file. | ||
24 | pub(super) original_token: SyntaxToken, | ||
25 | /// The token before the cursor, in the macro-expanded file. | ||
23 | pub(super) token: SyntaxToken, | 26 | pub(super) token: SyntaxToken, |
24 | pub(super) module: Option<hir::Module>, | 27 | pub(super) krate: Option<hir::Crate>, |
25 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 28 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
26 | pub(super) function_syntax: Option<ast::FnDef>, | 29 | pub(super) function_syntax: Option<ast::FnDef>, |
27 | pub(super) use_item_syntax: Option<ast::UseItem>, | 30 | pub(super) use_item_syntax: Option<ast::UseItem>, |
@@ -67,15 +70,20 @@ impl<'a> CompletionContext<'a> { | |||
67 | let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); | 70 | let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); |
68 | parse.reparse(&edit).tree() | 71 | parse.reparse(&edit).tree() |
69 | }; | 72 | }; |
73 | let fake_ident_token = | ||
74 | file_with_fake_ident.syntax().token_at_offset(position.offset).right_biased().unwrap(); | ||
70 | 75 | ||
71 | let module = sema.to_module_def(position.file_id); | 76 | let krate = sema.to_module_def(position.file_id).map(|m| m.krate()); |
72 | let token = original_file.syntax().token_at_offset(position.offset).left_biased()?; | 77 | let original_token = |
78 | original_file.syntax().token_at_offset(position.offset).left_biased()?; | ||
79 | let token = sema.descend_into_macros(original_token.clone()); | ||
73 | let mut ctx = CompletionContext { | 80 | let mut ctx = CompletionContext { |
74 | sema, | 81 | sema, |
75 | db, | 82 | db, |
83 | original_token, | ||
76 | token, | 84 | token, |
77 | offset: position.offset, | 85 | offset: position.offset, |
78 | module, | 86 | krate, |
79 | name_ref_syntax: None, | 87 | name_ref_syntax: None, |
80 | function_syntax: None, | 88 | function_syntax: None, |
81 | use_item_syntax: None, | 89 | use_item_syntax: None, |
@@ -95,15 +103,57 @@ impl<'a> CompletionContext<'a> { | |||
95 | has_type_args: false, | 103 | has_type_args: false, |
96 | dot_receiver_is_ambiguous_float_literal: false, | 104 | dot_receiver_is_ambiguous_float_literal: false, |
97 | }; | 105 | }; |
98 | ctx.fill(&original_file, file_with_fake_ident, position.offset); | 106 | |
107 | let mut original_file = original_file.syntax().clone(); | ||
108 | let mut hypothetical_file = file_with_fake_ident.syntax().clone(); | ||
109 | let mut offset = position.offset; | ||
110 | let mut fake_ident_token = fake_ident_token; | ||
111 | |||
112 | // Are we inside a macro call? | ||
113 | while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = ( | ||
114 | find_node_at_offset::<ast::MacroCall>(&original_file, offset), | ||
115 | find_node_at_offset::<ast::MacroCall>(&hypothetical_file, offset), | ||
116 | ) { | ||
117 | if actual_macro_call.path().as_ref().map(|s| s.syntax().text()) | ||
118 | != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text()) | ||
119 | { | ||
120 | break; | ||
121 | } | ||
122 | let hypothetical_args = match macro_call_with_fake_ident.token_tree() { | ||
123 | Some(tt) => tt, | ||
124 | None => break, | ||
125 | }; | ||
126 | if let (Some(actual_expansion), Some(hypothetical_expansion)) = ( | ||
127 | ctx.sema.expand(&actual_macro_call), | ||
128 | ctx.sema.expand_hypothetical( | ||
129 | &actual_macro_call, | ||
130 | &hypothetical_args, | ||
131 | fake_ident_token, | ||
132 | ), | ||
133 | ) { | ||
134 | let new_offset = hypothetical_expansion.1.text_range().start(); | ||
135 | if new_offset >= actual_expansion.text_range().end() { | ||
136 | break; | ||
137 | } | ||
138 | original_file = actual_expansion; | ||
139 | hypothetical_file = hypothetical_expansion.0; | ||
140 | fake_ident_token = hypothetical_expansion.1; | ||
141 | offset = new_offset; | ||
142 | } else { | ||
143 | break; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | ctx.fill(&original_file, hypothetical_file, offset); | ||
99 | Some(ctx) | 148 | Some(ctx) |
100 | } | 149 | } |
101 | 150 | ||
102 | // The range of the identifier that is being completed. | 151 | // The range of the identifier that is being completed. |
103 | pub(crate) fn source_range(&self) -> TextRange { | 152 | pub(crate) fn source_range(&self) -> TextRange { |
153 | // check kind of macro-expanded token, but use range of original token | ||
104 | match self.token.kind() { | 154 | match self.token.kind() { |
105 | // workaroud when completion is triggered by trigger characters. | 155 | // workaroud when completion is triggered by trigger characters. |
106 | IDENT => self.token.text_range(), | 156 | IDENT => self.original_token.text_range(), |
107 | _ => TextRange::offset_len(self.offset, 0.into()), | 157 | _ => TextRange::offset_len(self.offset, 0.into()), |
108 | } | 158 | } |
109 | } | 159 | } |
@@ -114,27 +164,24 @@ impl<'a> CompletionContext<'a> { | |||
114 | 164 | ||
115 | fn fill( | 165 | fn fill( |
116 | &mut self, | 166 | &mut self, |
117 | original_file: &ast::SourceFile, | 167 | original_file: &SyntaxNode, |
118 | file_with_fake_ident: ast::SourceFile, | 168 | file_with_fake_ident: SyntaxNode, |
119 | offset: TextUnit, | 169 | offset: TextUnit, |
120 | ) { | 170 | ) { |
121 | // First, let's try to complete a reference to some declaration. | 171 | // First, let's try to complete a reference to some declaration. |
122 | if let Some(name_ref) = | 172 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { |
123 | find_node_at_offset::<ast::NameRef>(file_with_fake_ident.syntax(), offset) | ||
124 | { | ||
125 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. | 173 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. |
126 | // See RFC#1685. | 174 | // See RFC#1685. |
127 | if is_node::<ast::Param>(name_ref.syntax()) { | 175 | if is_node::<ast::Param>(name_ref.syntax()) { |
128 | self.is_param = true; | 176 | self.is_param = true; |
129 | return; | 177 | return; |
130 | } | 178 | } |
131 | self.classify_name_ref(original_file, name_ref); | 179 | self.classify_name_ref(original_file, name_ref, offset); |
132 | } | 180 | } |
133 | 181 | ||
134 | // Otherwise, see if this is a declaration. We can use heuristics to | 182 | // Otherwise, see if this is a declaration. We can use heuristics to |
135 | // suggest declaration names, see `CompletionKind::Magic`. | 183 | // suggest declaration names, see `CompletionKind::Magic`. |
136 | if let Some(name) = find_node_at_offset::<ast::Name>(file_with_fake_ident.syntax(), offset) | 184 | if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) { |
137 | { | ||
138 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { | 185 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { |
139 | let parent = bind_pat.syntax().parent(); | 186 | let parent = bind_pat.syntax().parent(); |
140 | if parent.clone().and_then(ast::MatchArm::cast).is_some() | 187 | if parent.clone().and_then(ast::MatchArm::cast).is_some() |
@@ -148,23 +195,29 @@ impl<'a> CompletionContext<'a> { | |||
148 | return; | 195 | return; |
149 | } | 196 | } |
150 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { | 197 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { |
151 | self.record_lit_pat = find_node_at_offset(original_file.syntax(), self.offset); | 198 | self.record_lit_pat = |
199 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
152 | } | 200 | } |
153 | } | 201 | } |
154 | } | 202 | } |
155 | 203 | ||
156 | fn classify_name_ref(&mut self, original_file: &SourceFile, name_ref: ast::NameRef) { | 204 | fn classify_name_ref( |
205 | &mut self, | ||
206 | original_file: &SyntaxNode, | ||
207 | name_ref: ast::NameRef, | ||
208 | offset: TextUnit, | ||
209 | ) { | ||
157 | self.name_ref_syntax = | 210 | self.name_ref_syntax = |
158 | find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start()); | 211 | find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); |
159 | let name_range = name_ref.syntax().text_range(); | 212 | let name_range = name_ref.syntax().text_range(); |
160 | if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { | 213 | if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { |
161 | self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); | 214 | self.record_lit_syntax = |
215 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
162 | } | 216 | } |
163 | 217 | ||
164 | self.impl_def = self | 218 | self.impl_def = self |
165 | .token | 219 | .sema |
166 | .parent() | 220 | .ancestors_with_macros(self.token.parent()) |
167 | .ancestors() | ||
168 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 221 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
169 | .find_map(ast::ImplDef::cast); | 222 | .find_map(ast::ImplDef::cast); |
170 | 223 | ||
@@ -183,12 +236,12 @@ impl<'a> CompletionContext<'a> { | |||
183 | _ => (), | 236 | _ => (), |
184 | } | 237 | } |
185 | 238 | ||
186 | self.use_item_syntax = self.token.parent().ancestors().find_map(ast::UseItem::cast); | 239 | self.use_item_syntax = |
240 | self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast); | ||
187 | 241 | ||
188 | self.function_syntax = self | 242 | self.function_syntax = self |
189 | .token | 243 | .sema |
190 | .parent() | 244 | .ancestors_with_macros(self.token.parent()) |
191 | .ancestors() | ||
192 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 245 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
193 | .find_map(ast::FnDef::cast); | 246 | .find_map(ast::FnDef::cast); |
194 | 247 | ||
@@ -242,7 +295,7 @@ impl<'a> CompletionContext<'a> { | |||
242 | 295 | ||
243 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { | 296 | if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { |
244 | if let Some(if_expr) = | 297 | if let Some(if_expr) = |
245 | find_node_at_offset::<ast::IfExpr>(original_file.syntax(), off) | 298 | self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off) |
246 | { | 299 | { |
247 | if if_expr.syntax().text_range().end() | 300 | if if_expr.syntax().text_range().end() |
248 | < name_ref.syntax().text_range().start() | 301 | < name_ref.syntax().text_range().start() |
@@ -259,7 +312,7 @@ impl<'a> CompletionContext<'a> { | |||
259 | self.dot_receiver = field_expr | 312 | self.dot_receiver = field_expr |
260 | .expr() | 313 | .expr() |
261 | .map(|e| e.syntax().text_range()) | 314 | .map(|e| e.syntax().text_range()) |
262 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | 315 | .and_then(|r| find_node_with_range(original_file, r)); |
263 | self.dot_receiver_is_ambiguous_float_literal = | 316 | self.dot_receiver_is_ambiguous_float_literal = |
264 | if let Some(ast::Expr::Literal(l)) = &self.dot_receiver { | 317 | if let Some(ast::Expr::Literal(l)) = &self.dot_receiver { |
265 | match l.kind() { | 318 | match l.kind() { |
@@ -275,7 +328,7 @@ impl<'a> CompletionContext<'a> { | |||
275 | self.dot_receiver = method_call_expr | 328 | self.dot_receiver = method_call_expr |
276 | .expr() | 329 | .expr() |
277 | .map(|e| e.syntax().text_range()) | 330 | .map(|e| e.syntax().text_range()) |
278 | .and_then(|r| find_node_with_range(original_file.syntax(), r)); | 331 | .and_then(|r| find_node_with_range(original_file, r)); |
279 | self.is_call = true; | 332 | self.is_call = true; |
280 | } | 333 | } |
281 | } | 334 | } |
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs index 1c26a8697..eaeaaa2b4 100644 --- a/crates/ra_ide/src/display.rs +++ b/crates/ra_ide/src/display.rs | |||
@@ -68,17 +68,23 @@ pub(crate) fn macro_label(node: &ast::MacroCall) -> String { | |||
68 | } | 68 | } |
69 | 69 | ||
70 | pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { | 70 | pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { |
71 | rust_code_markup_with_doc::<_, &str>(val, None) | 71 | rust_code_markup_with_doc::<_, &str>(val, None, None) |
72 | } | 72 | } |
73 | 73 | ||
74 | pub(crate) fn rust_code_markup_with_doc<CODE, DOC>(val: CODE, doc: Option<DOC>) -> String | 74 | pub(crate) fn rust_code_markup_with_doc<CODE, DOC>( |
75 | val: CODE, | ||
76 | doc: Option<DOC>, | ||
77 | mod_path: Option<String>, | ||
78 | ) -> String | ||
75 | where | 79 | where |
76 | CODE: AsRef<str>, | 80 | CODE: AsRef<str>, |
77 | DOC: AsRef<str>, | 81 | DOC: AsRef<str>, |
78 | { | 82 | { |
83 | let mod_path = | ||
84 | mod_path.filter(|path| !path.is_empty()).map(|path| path + "\n").unwrap_or_default(); | ||
79 | if let Some(doc) = doc { | 85 | if let Some(doc) = doc { |
80 | format!("```rust\n{}\n```\n\n{}", val.as_ref(), doc.as_ref()) | 86 | format!("```rust\n{}{}\n```\n\n{}", mod_path, val.as_ref(), doc.as_ref()) |
81 | } else { | 87 | } else { |
82 | format!("```rust\n{}\n```", val.as_ref()) | 88 | format!("```rust\n{}{}\n```", mod_path, val.as_ref()) |
83 | } | 89 | } |
84 | } | 90 | } |
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index e9c682557..25e038a55 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -1,6 +1,10 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{Adt, HasSource, HirDisplay, Semantics}; | 3 | use hir::{ |
4 | Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, | ||
5 | ModuleSource, Semantics, | ||
6 | }; | ||
7 | use ra_db::SourceDatabase; | ||
4 | use ra_ide_db::{ | 8 | use ra_ide_db::{ |
5 | defs::{classify_name, classify_name_ref, Definition}, | 9 | defs::{classify_name, classify_name_ref, Definition}, |
6 | RootDatabase, | 10 | RootDatabase, |
@@ -16,6 +20,8 @@ use crate::{ | |||
16 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, | 20 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, |
17 | FilePosition, RangeInfo, | 21 | FilePosition, RangeInfo, |
18 | }; | 22 | }; |
23 | use itertools::Itertools; | ||
24 | use std::iter::once; | ||
19 | 25 | ||
20 | /// Contains the results when hovering over an item | 26 | /// Contains the results when hovering over an item |
21 | #[derive(Debug, Clone)] | 27 | #[derive(Debug, Clone)] |
@@ -83,44 +89,86 @@ impl HoverResult { | |||
83 | } | 89 | } |
84 | } | 90 | } |
85 | 91 | ||
86 | fn hover_text(docs: Option<String>, desc: Option<String>) -> Option<String> { | 92 | fn hover_text( |
87 | match (desc, docs) { | 93 | docs: Option<String>, |
88 | (Some(desc), docs) => Some(rust_code_markup_with_doc(desc, docs)), | 94 | desc: Option<String>, |
89 | (None, Some(docs)) => Some(docs), | 95 | mod_path: Option<String>, |
96 | ) -> Option<String> { | ||
97 | match (desc, docs, mod_path) { | ||
98 | (Some(desc), docs, mod_path) => Some(rust_code_markup_with_doc(desc, docs, mod_path)), | ||
99 | (None, Some(docs), _) => Some(docs), | ||
100 | _ => None, | ||
101 | } | ||
102 | } | ||
103 | |||
104 | fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> { | ||
105 | match def { | ||
106 | Definition::StructField(f) => Some(f.parent_def(db).name(db)), | ||
107 | Definition::Local(l) => l.parent(db).name(db), | ||
108 | Definition::ModuleDef(md) => match md { | ||
109 | ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) { | ||
110 | AssocItemContainer::Trait(t) => Some(t.name(db)), | ||
111 | AssocItemContainer::ImplDef(i) => i.target_ty(db).as_adt().map(|adt| adt.name(db)), | ||
112 | }, | ||
113 | ModuleDef::EnumVariant(e) => Some(e.parent_enum(db).name(db)), | ||
114 | _ => None, | ||
115 | }, | ||
116 | Definition::SelfType(i) => i.target_ty(db).as_adt().map(|adt| adt.name(db)), | ||
90 | _ => None, | 117 | _ => None, |
91 | } | 118 | } |
119 | .map(|name| name.to_string()) | ||
120 | } | ||
121 | |||
122 | fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { | ||
123 | let mod_path = def.module(db).map(|module| { | ||
124 | once(db.crate_graph()[module.krate().into()].display_name.clone()) | ||
125 | .chain( | ||
126 | module | ||
127 | .path_to_root(db) | ||
128 | .into_iter() | ||
129 | .rev() | ||
130 | .map(|it| it.name(db).map(|name| name.to_string())), | ||
131 | ) | ||
132 | .chain(once(definition_owner_name(db, def))) | ||
133 | .flatten() | ||
134 | .join("::") | ||
135 | }); | ||
136 | mod_path | ||
92 | } | 137 | } |
93 | 138 | ||
94 | fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> { | 139 | fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> { |
140 | let mod_path = determine_mod_path(db, &def); | ||
95 | return match def { | 141 | return match def { |
96 | Definition::Macro(it) => { | 142 | Definition::Macro(it) => { |
97 | let src = it.source(db); | 143 | let src = it.source(db); |
98 | hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value))) | 144 | hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)), mod_path) |
99 | } | 145 | } |
100 | Definition::StructField(it) => { | 146 | Definition::StructField(it) => { |
101 | let src = it.source(db); | 147 | let src = it.source(db); |
102 | match src.value { | 148 | match src.value { |
103 | hir::FieldSource::Named(it) => hover_text(it.doc_comment_text(), it.short_label()), | 149 | FieldSource::Named(it) => { |
150 | hover_text(it.doc_comment_text(), it.short_label(), mod_path) | ||
151 | } | ||
104 | _ => None, | 152 | _ => None, |
105 | } | 153 | } |
106 | } | 154 | } |
107 | Definition::ModuleDef(it) => match it { | 155 | Definition::ModuleDef(it) => match it { |
108 | hir::ModuleDef::Module(it) => match it.definition_source(db).value { | 156 | ModuleDef::Module(it) => match it.definition_source(db).value { |
109 | hir::ModuleSource::Module(it) => { | 157 | ModuleSource::Module(it) => { |
110 | hover_text(it.doc_comment_text(), it.short_label()) | 158 | hover_text(it.doc_comment_text(), it.short_label(), mod_path) |
111 | } | 159 | } |
112 | _ => None, | 160 | _ => None, |
113 | }, | 161 | }, |
114 | hir::ModuleDef::Function(it) => from_def_source(db, it), | 162 | ModuleDef::Function(it) => from_def_source(db, it, mod_path), |
115 | hir::ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it), | 163 | ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it, mod_path), |
116 | hir::ModuleDef::Adt(Adt::Union(it)) => from_def_source(db, it), | 164 | ModuleDef::Adt(Adt::Union(it)) => from_def_source(db, it, mod_path), |
117 | hir::ModuleDef::Adt(Adt::Enum(it)) => from_def_source(db, it), | 165 | ModuleDef::Adt(Adt::Enum(it)) => from_def_source(db, it, mod_path), |
118 | hir::ModuleDef::EnumVariant(it) => from_def_source(db, it), | 166 | ModuleDef::EnumVariant(it) => from_def_source(db, it, mod_path), |
119 | hir::ModuleDef::Const(it) => from_def_source(db, it), | 167 | ModuleDef::Const(it) => from_def_source(db, it, mod_path), |
120 | hir::ModuleDef::Static(it) => from_def_source(db, it), | 168 | ModuleDef::Static(it) => from_def_source(db, it, mod_path), |
121 | hir::ModuleDef::Trait(it) => from_def_source(db, it), | 169 | ModuleDef::Trait(it) => from_def_source(db, it, mod_path), |
122 | hir::ModuleDef::TypeAlias(it) => from_def_source(db, it), | 170 | ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), |
123 | hir::ModuleDef::BuiltinType(it) => Some(it.to_string()), | 171 | ModuleDef::BuiltinType(it) => Some(it.to_string()), |
124 | }, | 172 | }, |
125 | Definition::Local(it) => { | 173 | Definition::Local(it) => { |
126 | Some(rust_code_markup(it.ty(db).display_truncated(db, None).to_string())) | 174 | Some(rust_code_markup(it.ty(db).display_truncated(db, None).to_string())) |
@@ -131,13 +179,13 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin | |||
131 | } | 179 | } |
132 | }; | 180 | }; |
133 | 181 | ||
134 | fn from_def_source<A, D>(db: &RootDatabase, def: D) -> Option<String> | 182 | fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String> |
135 | where | 183 | where |
136 | D: HasSource<Ast = A>, | 184 | D: HasSource<Ast = A>, |
137 | A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, | 185 | A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, |
138 | { | 186 | { |
139 | let src = def.source(db); | 187 | let src = def.source(db); |
140 | hover_text(src.value.doc_comment_text(), src.value.short_label()) | 188 | hover_text(src.value.doc_comment_text(), src.value.short_label(), mod_path) |
141 | } | 189 | } |
142 | } | 190 | } |
143 | 191 | ||
@@ -345,7 +393,7 @@ mod tests { | |||
345 | }; | 393 | }; |
346 | } | 394 | } |
347 | "#, | 395 | "#, |
348 | &["field_a: u32"], | 396 | &["Foo\nfield_a: u32"], |
349 | ); | 397 | ); |
350 | 398 | ||
351 | // Hovering over the field in the definition | 399 | // Hovering over the field in the definition |
@@ -362,7 +410,7 @@ mod tests { | |||
362 | }; | 410 | }; |
363 | } | 411 | } |
364 | "#, | 412 | "#, |
365 | &["field_a: u32"], | 413 | &["Foo\nfield_a: u32"], |
366 | ); | 414 | ); |
367 | } | 415 | } |
368 | 416 | ||
@@ -415,7 +463,7 @@ fn main() { | |||
415 | ", | 463 | ", |
416 | ); | 464 | ); |
417 | let hover = analysis.hover(position).unwrap().unwrap(); | 465 | let hover = analysis.hover(position).unwrap().unwrap(); |
418 | assert_eq!(trim_markup_opt(hover.info.first()), Some("Some")); | 466 | assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\nSome")); |
419 | 467 | ||
420 | let (analysis, position) = single_file_with_position( | 468 | let (analysis, position) = single_file_with_position( |
421 | " | 469 | " |
@@ -442,6 +490,7 @@ fn main() { | |||
442 | } | 490 | } |
443 | "#, | 491 | "#, |
444 | &[" | 492 | &[" |
493 | Option | ||
445 | None | 494 | None |
446 | ``` | 495 | ``` |
447 | 496 | ||
@@ -462,6 +511,7 @@ The None variant | |||
462 | } | 511 | } |
463 | "#, | 512 | "#, |
464 | &[" | 513 | &[" |
514 | Option | ||
465 | Some | 515 | Some |
466 | ``` | 516 | ``` |
467 | 517 | ||
@@ -528,21 +578,23 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
528 | fn test_hover_infer_associated_method_exact() { | 578 | fn test_hover_infer_associated_method_exact() { |
529 | let (analysis, position) = single_file_with_position( | 579 | let (analysis, position) = single_file_with_position( |
530 | " | 580 | " |
531 | struct Thing { x: u32 } | 581 | mod wrapper { |
582 | struct Thing { x: u32 } | ||
532 | 583 | ||
533 | impl Thing { | 584 | impl Thing { |
534 | fn new() -> Thing { | 585 | fn new() -> Thing { |
535 | Thing { x: 0 } | 586 | Thing { x: 0 } |
587 | } | ||
536 | } | 588 | } |
537 | } | 589 | } |
538 | 590 | ||
539 | fn main() { | 591 | fn main() { |
540 | let foo_test = Thing::new<|>(); | 592 | let foo_test = wrapper::Thing::new<|>(); |
541 | } | 593 | } |
542 | ", | 594 | ", |
543 | ); | 595 | ); |
544 | let hover = analysis.hover(position).unwrap().unwrap(); | 596 | let hover = analysis.hover(position).unwrap().unwrap(); |
545 | assert_eq!(trim_markup_opt(hover.info.first()), Some("fn new() -> Thing")); | 597 | assert_eq!(trim_markup_opt(hover.info.first()), Some("wrapper::Thing\nfn new() -> Thing")); |
546 | assert_eq!(hover.info.is_exact(), true); | 598 | assert_eq!(hover.info.is_exact(), true); |
547 | } | 599 | } |
548 | 600 | ||
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 69098a630..cf0cbdbd0 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -119,6 +119,12 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ | |||
119 | return true; | 119 | return true; |
120 | } | 120 | } |
121 | 121 | ||
122 | if let Some(Adt::Struct(s)) = pat_ty.as_adt() { | ||
123 | if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.syntax().to_string() { | ||
124 | return true; | ||
125 | } | ||
126 | } | ||
127 | |||
122 | for node in bind_pat.syntax().ancestors() { | 128 | for node in bind_pat.syntax().ancestors() { |
123 | match_ast! { | 129 | match_ast! { |
124 | match node { | 130 | match node { |
@@ -943,4 +949,30 @@ fn main() { | |||
943 | "### | 949 | "### |
944 | ); | 950 | ); |
945 | } | 951 | } |
952 | |||
953 | #[test] | ||
954 | fn unit_structs_have_no_type_hints() { | ||
955 | let (analysis, file_id) = single_file( | ||
956 | r#" | ||
957 | enum CustomResult<T, E> { | ||
958 | Ok(T), | ||
959 | Err(E), | ||
960 | } | ||
961 | use CustomResult::*; | ||
962 | |||
963 | struct SyntheticSyntax; | ||
964 | |||
965 | fn main() { | ||
966 | match Ok(()) { | ||
967 | Ok(_) => (), | ||
968 | Err(SyntheticSyntax) => (), | ||
969 | } | ||
970 | }"#, | ||
971 | ); | ||
972 | |||
973 | assert_debug_snapshot!(analysis.inlay_hints(file_id, Some(8)).unwrap(), @r###" | ||
974 | [] | ||
975 | "### | ||
976 | ); | ||
977 | } | ||
946 | } | 978 | } |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 4dfe0553e..c60e86aea 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -211,7 +211,13 @@ impl Analysis { | |||
211 | // Default to enable test for single file. | 211 | // Default to enable test for single file. |
212 | let mut cfg_options = CfgOptions::default(); | 212 | let mut cfg_options = CfgOptions::default(); |
213 | cfg_options.insert_atom("test".into()); | 213 | cfg_options.insert_atom("test".into()); |
214 | crate_graph.add_crate_root(file_id, Edition::Edition2018, cfg_options, Env::default()); | 214 | crate_graph.add_crate_root( |
215 | file_id, | ||
216 | Edition::Edition2018, | ||
217 | None, | ||
218 | cfg_options, | ||
219 | Env::default(), | ||
220 | ); | ||
215 | change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); | 221 | change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); |
216 | change.set_crate_graph(crate_graph); | 222 | change.set_crate_graph(crate_graph); |
217 | host.apply_change(change); | 223 | host.apply_change(change); |
@@ -415,12 +421,12 @@ impl Analysis { | |||
415 | 421 | ||
416 | /// Returns the edition of the given crate. | 422 | /// Returns the edition of the given crate. |
417 | pub fn crate_edition(&self, crate_id: CrateId) -> Cancelable<Edition> { | 423 | pub fn crate_edition(&self, crate_id: CrateId) -> Cancelable<Edition> { |
418 | self.with_db(|db| db.crate_graph().edition(crate_id)) | 424 | self.with_db(|db| db.crate_graph()[crate_id].edition) |
419 | } | 425 | } |
420 | 426 | ||
421 | /// Returns the root file of the given crate. | 427 | /// Returns the root file of the given crate. |
422 | pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { | 428 | pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { |
423 | self.with_db(|db| db.crate_graph().crate_root(crate_id)) | 429 | self.with_db(|db| db.crate_graph()[crate_id].root_file_id) |
424 | } | 430 | } |
425 | 431 | ||
426 | /// Returns the set of possible targets to run for the current file. | 432 | /// Returns the set of possible targets to run for the current file. |
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index f4cd6deb7..90f84b052 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs | |||
@@ -99,13 +99,19 @@ impl MockAnalysis { | |||
99 | root_crate = Some(crate_graph.add_crate_root( | 99 | root_crate = Some(crate_graph.add_crate_root( |
100 | file_id, | 100 | file_id, |
101 | Edition2018, | 101 | Edition2018, |
102 | None, | ||
102 | cfg_options, | 103 | cfg_options, |
103 | Env::default(), | 104 | Env::default(), |
104 | )); | 105 | )); |
105 | } else if path.ends_with("/lib.rs") { | 106 | } else if path.ends_with("/lib.rs") { |
106 | let other_crate = | ||
107 | crate_graph.add_crate_root(file_id, Edition2018, cfg_options, Env::default()); | ||
108 | let crate_name = path.parent().unwrap().file_name().unwrap(); | 107 | let crate_name = path.parent().unwrap().file_name().unwrap(); |
108 | let other_crate = crate_graph.add_crate_root( | ||
109 | file_id, | ||
110 | Edition2018, | ||
111 | Some(crate_name.to_owned()), | ||
112 | cfg_options, | ||
113 | Env::default(), | ||
114 | ); | ||
109 | if let Some(root_crate) = root_crate { | 115 | if let Some(root_crate) = root_crate { |
110 | crate_graph | 116 | crate_graph |
111 | .add_dep(root_crate, CrateName::new(crate_name).unwrap(), other_crate) | 117 | .add_dep(root_crate, CrateName::new(crate_name).unwrap(), other_crate) |
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index 2c4bdb039..b73cefd97 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs | |||
@@ -133,6 +133,7 @@ mod tests { | |||
133 | let crate_id = crate_graph.add_crate_root( | 133 | let crate_id = crate_graph.add_crate_root( |
134 | root_file, | 134 | root_file, |
135 | Edition2018, | 135 | Edition2018, |
136 | None, | ||
136 | CfgOptions::default(), | 137 | CfgOptions::default(), |
137 | Env::default(), | 138 | Env::default(), |
138 | ); | 139 | ); |