diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-09-11 15:49:57 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2019-09-11 15:49:57 +0100 |
commit | 6ce6744e18f25ebcde387178125d820686692df5 (patch) | |
tree | e1861982356f9905980537f962dff9560423f901 | |
parent | 7bbb039fbdd124cb6549eb67bbe7316f03ff40e8 (diff) | |
parent | 6353b1621f44e1b0db65ebbe414aa7c5f1864b9d (diff) |
Merge #1796
1796: Support completion for macros r=matklad a=uHOOCCOOHu
This is based on #1795 , and fixes #1727
Also prettify hover text of macros.
Some screenshorts below:
Completion in item place.
<img width="416" alt="Screenshot_20190910_134056" src="https://user-images.githubusercontent.com/14816024/64587159-fa72da00-d3d0-11e9-86bb-c98f169ec08d.png">
After pressing `tab`.
<img width="313" alt="Screenshot_20190910_134111" src="https://user-images.githubusercontent.com/14816024/64587160-fa72da00-d3d0-11e9-9464-21e3f6957bd7.png">
Complete macros from `std`.
<img width="588" alt="Screenshot_20190910_134147" src="https://user-images.githubusercontent.com/14816024/64587161-fb0b7080-d3d0-11e9-866e-5161f0d1b546.png">
Hover text.
<img width="521" alt="Screenshot_20190910_134242" src="https://user-images.githubusercontent.com/14816024/64587162-fb0b7080-d3d0-11e9-8f09-ad17e3f6702a.png">
Co-authored-by: uHOOCCOOHu <[email protected]>
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests/macros.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs | 50 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_path.rs | 36 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_scope.rs | 192 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/presentation.rs | 24 | ||||
-rw-r--r-- | crates/ra_ide_api/src/display.rs | 8 | ||||
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 24 |
10 files changed, 334 insertions, 8 deletions
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 7488d75a5..3d8691f53 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -416,7 +416,7 @@ impl CrateDefMap { | |||
416 | ); | 416 | ); |
417 | } | 417 | } |
418 | 418 | ||
419 | // Since it is a quantified path here, it should not contains legacy macros | 419 | // Since it is a qualified path here, it should not contains legacy macros |
420 | match self[module.module_id].scope.get(&segment.name) { | 420 | match self[module.module_id].scope.get(&segment.name) { |
421 | Some(res) => res.def, | 421 | Some(res) => res.def, |
422 | _ => { | 422 | _ => { |
diff --git a/crates/ra_hir/src/nameres/tests/macros.rs b/crates/ra_hir/src/nameres/tests/macros.rs index 20ee63c67..6e0bc437e 100644 --- a/crates/ra_hir/src/nameres/tests/macros.rs +++ b/crates/ra_hir/src/nameres/tests/macros.rs | |||
@@ -430,7 +430,7 @@ fn macro_use_can_be_aliased() { | |||
430 | } | 430 | } |
431 | 431 | ||
432 | #[test] | 432 | #[test] |
433 | fn path_quantified_macros() { | 433 | fn path_qualified_macros() { |
434 | let map = def_map( | 434 | let map = def_map( |
435 | " | 435 | " |
436 | //- /main.rs | 436 | //- /main.rs |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index c60e72abf..869ae13f1 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -2839,7 +2839,7 @@ fn main() { | |||
2839 | } | 2839 | } |
2840 | 2840 | ||
2841 | #[test] | 2841 | #[test] |
2842 | fn infer_path_quantified_macros_expanded() { | 2842 | fn infer_path_qualified_macros_expanded() { |
2843 | assert_snapshot!( | 2843 | assert_snapshot!( |
2844 | infer(r#" | 2844 | infer(r#" |
2845 | #[macro_export] | 2845 | #[macro_export] |
diff --git a/crates/ra_ide_api/src/completion.rs b/crates/ra_ide_api/src/completion.rs index a4f080adc..0ad414831 100644 --- a/crates/ra_ide_api/src/completion.rs +++ b/crates/ra_ide_api/src/completion.rs | |||
@@ -12,6 +12,7 @@ mod complete_snippet; | |||
12 | mod complete_path; | 12 | mod complete_path; |
13 | mod complete_scope; | 13 | mod complete_scope; |
14 | mod complete_postfix; | 14 | mod complete_postfix; |
15 | mod complete_macro_in_item_position; | ||
15 | 16 | ||
16 | use ra_db::SourceDatabase; | 17 | use ra_db::SourceDatabase; |
17 | 18 | ||
@@ -69,5 +70,6 @@ pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Opti | |||
69 | complete_record_pattern::complete_record_pattern(&mut acc, &ctx); | 70 | complete_record_pattern::complete_record_pattern(&mut acc, &ctx); |
70 | complete_pattern::complete_pattern(&mut acc, &ctx); | 71 | complete_pattern::complete_pattern(&mut acc, &ctx); |
71 | complete_postfix::complete_postfix(&mut acc, &ctx); | 72 | complete_postfix::complete_postfix(&mut acc, &ctx); |
73 | complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); | ||
72 | Some(acc) | 74 | Some(acc) |
73 | } | 75 | } |
diff --git a/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs new file mode 100644 index 000000000..708dc9777 --- /dev/null +++ b/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs | |||
@@ -0,0 +1,50 @@ | |||
1 | use crate::completion::{CompletionContext, Completions}; | ||
2 | |||
3 | pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { | ||
4 | // Show only macros in top level. | ||
5 | if ctx.is_new_item { | ||
6 | for (name, res) in ctx.analyzer.all_names(ctx.db) { | ||
7 | if res.get_macros().is_some() { | ||
8 | acc.add_resolution(ctx, name.to_string(), &res.only_macros()); | ||
9 | } | ||
10 | } | ||
11 | } | ||
12 | } | ||
13 | |||
14 | #[cfg(test)] | ||
15 | mod tests { | ||
16 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | ||
17 | use insta::assert_debug_snapshot; | ||
18 | |||
19 | fn do_reference_completion(code: &str) -> Vec<CompletionItem> { | ||
20 | do_completion(code, CompletionKind::Reference) | ||
21 | } | ||
22 | |||
23 | #[test] | ||
24 | fn completes_macros_as_item() { | ||
25 | assert_debug_snapshot!( | ||
26 | do_reference_completion( | ||
27 | " | ||
28 | //- /main.rs | ||
29 | macro_rules! foo { | ||
30 | () => {} | ||
31 | } | ||
32 | |||
33 | fn foo() {} | ||
34 | |||
35 | <|> | ||
36 | " | ||
37 | ), | ||
38 | @r##"[ | ||
39 | CompletionItem { | ||
40 | label: "foo", | ||
41 | source_range: [46; 46), | ||
42 | delete: [46; 46), | ||
43 | insert: "foo!", | ||
44 | kind: Macro, | ||
45 | detail: "macro_rules! foo", | ||
46 | }, | ||
47 | ]"## | ||
48 | ); | ||
49 | } | ||
50 | } | ||
diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index d6b5ac9ad..31e7dffe8 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs | |||
@@ -587,4 +587,40 @@ mod tests { | |||
587 | ]"### | 587 | ]"### |
588 | ); | 588 | ); |
589 | } | 589 | } |
590 | |||
591 | #[test] | ||
592 | fn completes_qualified_macros() { | ||
593 | assert_debug_snapshot!( | ||
594 | do_reference_completion( | ||
595 | " | ||
596 | #[macro_export] | ||
597 | macro_rules! foo { | ||
598 | () => {} | ||
599 | } | ||
600 | |||
601 | fn main() { | ||
602 | let _ = crate::<|> | ||
603 | } | ||
604 | " | ||
605 | ), | ||
606 | @r###"[ | ||
607 | CompletionItem { | ||
608 | label: "foo", | ||
609 | source_range: [179; 179), | ||
610 | delete: [179; 179), | ||
611 | insert: "foo!", | ||
612 | kind: Macro, | ||
613 | detail: "#[macro_export]\nmacro_rules! foo", | ||
614 | }, | ||
615 | CompletionItem { | ||
616 | label: "main", | ||
617 | source_range: [179; 179), | ||
618 | delete: [179; 179), | ||
619 | insert: "main()$0", | ||
620 | kind: Function, | ||
621 | detail: "fn main()", | ||
622 | }, | ||
623 | ]"### | ||
624 | ); | ||
625 | } | ||
590 | } | 626 | } |
diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs index 67fb7ba4e..2062e7300 100644 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ b/crates/ra_ide_api/src/completion/complete_scope.rs | |||
@@ -532,4 +532,196 @@ mod tests { | |||
532 | ]"# | 532 | ]"# |
533 | ); | 533 | ); |
534 | } | 534 | } |
535 | |||
536 | #[test] | ||
537 | fn completes_macros_as_value() { | ||
538 | assert_debug_snapshot!( | ||
539 | do_reference_completion( | ||
540 | " | ||
541 | //- /main.rs | ||
542 | macro_rules! foo { | ||
543 | () => {} | ||
544 | } | ||
545 | |||
546 | #[macro_use] | ||
547 | mod m1 { | ||
548 | macro_rules! bar { | ||
549 | () => {} | ||
550 | } | ||
551 | } | ||
552 | |||
553 | mod m2 { | ||
554 | macro_rules! nope { | ||
555 | () => {} | ||
556 | } | ||
557 | |||
558 | #[macro_export] | ||
559 | macro_rules! baz { | ||
560 | () => {} | ||
561 | } | ||
562 | } | ||
563 | |||
564 | fn main() { | ||
565 | let v = <|> | ||
566 | } | ||
567 | " | ||
568 | ), | ||
569 | @r##"[ | ||
570 | CompletionItem { | ||
571 | label: "bar", | ||
572 | source_range: [252; 252), | ||
573 | delete: [252; 252), | ||
574 | insert: "bar!", | ||
575 | kind: Macro, | ||
576 | detail: "macro_rules! bar", | ||
577 | }, | ||
578 | CompletionItem { | ||
579 | label: "baz", | ||
580 | source_range: [252; 252), | ||
581 | delete: [252; 252), | ||
582 | insert: "baz!", | ||
583 | kind: Macro, | ||
584 | detail: "#[macro_export]\nmacro_rules! baz", | ||
585 | }, | ||
586 | CompletionItem { | ||
587 | label: "foo", | ||
588 | source_range: [252; 252), | ||
589 | delete: [252; 252), | ||
590 | insert: "foo!", | ||
591 | kind: Macro, | ||
592 | detail: "macro_rules! foo", | ||
593 | }, | ||
594 | CompletionItem { | ||
595 | label: "m1", | ||
596 | source_range: [252; 252), | ||
597 | delete: [252; 252), | ||
598 | insert: "m1", | ||
599 | kind: Module, | ||
600 | }, | ||
601 | CompletionItem { | ||
602 | label: "m2", | ||
603 | source_range: [252; 252), | ||
604 | delete: [252; 252), | ||
605 | insert: "m2", | ||
606 | kind: Module, | ||
607 | }, | ||
608 | CompletionItem { | ||
609 | label: "main", | ||
610 | source_range: [252; 252), | ||
611 | delete: [252; 252), | ||
612 | insert: "main()$0", | ||
613 | kind: Function, | ||
614 | detail: "fn main()", | ||
615 | }, | ||
616 | ]"## | ||
617 | ); | ||
618 | } | ||
619 | |||
620 | #[test] | ||
621 | fn completes_both_macro_and_value() { | ||
622 | assert_debug_snapshot!( | ||
623 | do_reference_completion( | ||
624 | " | ||
625 | //- /main.rs | ||
626 | macro_rules! foo { | ||
627 | () => {} | ||
628 | } | ||
629 | |||
630 | fn foo() { | ||
631 | <|> | ||
632 | } | ||
633 | " | ||
634 | ), | ||
635 | @r##"[ | ||
636 | CompletionItem { | ||
637 | label: "foo", | ||
638 | source_range: [49; 49), | ||
639 | delete: [49; 49), | ||
640 | insert: "foo!", | ||
641 | kind: Macro, | ||
642 | detail: "macro_rules! foo", | ||
643 | }, | ||
644 | CompletionItem { | ||
645 | label: "foo", | ||
646 | source_range: [49; 49), | ||
647 | delete: [49; 49), | ||
648 | insert: "foo()$0", | ||
649 | kind: Function, | ||
650 | detail: "fn foo()", | ||
651 | }, | ||
652 | ]"## | ||
653 | ); | ||
654 | } | ||
655 | |||
656 | #[test] | ||
657 | fn completes_macros_as_type() { | ||
658 | assert_debug_snapshot!( | ||
659 | do_reference_completion( | ||
660 | " | ||
661 | //- /main.rs | ||
662 | macro_rules! foo { | ||
663 | () => {} | ||
664 | } | ||
665 | |||
666 | fn main() { | ||
667 | let x: <|> | ||
668 | } | ||
669 | " | ||
670 | ), | ||
671 | @r##"[ | ||
672 | CompletionItem { | ||
673 | label: "foo", | ||
674 | source_range: [57; 57), | ||
675 | delete: [57; 57), | ||
676 | insert: "foo!", | ||
677 | kind: Macro, | ||
678 | detail: "macro_rules! foo", | ||
679 | }, | ||
680 | CompletionItem { | ||
681 | label: "main", | ||
682 | source_range: [57; 57), | ||
683 | delete: [57; 57), | ||
684 | insert: "main()$0", | ||
685 | kind: Function, | ||
686 | detail: "fn main()", | ||
687 | }, | ||
688 | ]"## | ||
689 | ); | ||
690 | } | ||
691 | |||
692 | #[test] | ||
693 | fn completes_macros_as_stmt() { | ||
694 | assert_debug_snapshot!( | ||
695 | do_reference_completion( | ||
696 | " | ||
697 | //- /main.rs | ||
698 | macro_rules! foo { | ||
699 | () => {} | ||
700 | } | ||
701 | |||
702 | fn main() { | ||
703 | <|> | ||
704 | } | ||
705 | " | ||
706 | ), | ||
707 | @r##"[ | ||
708 | CompletionItem { | ||
709 | label: "foo", | ||
710 | source_range: [50; 50), | ||
711 | delete: [50; 50), | ||
712 | insert: "foo!", | ||
713 | kind: Macro, | ||
714 | detail: "macro_rules! foo", | ||
715 | }, | ||
716 | CompletionItem { | ||
717 | label: "main", | ||
718 | source_range: [50; 50), | ||
719 | delete: [50; 50), | ||
720 | insert: "main()$0", | ||
721 | kind: Function, | ||
722 | detail: "fn main()", | ||
723 | }, | ||
724 | ]"## | ||
725 | ); | ||
726 | } | ||
535 | } | 727 | } |
diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs index db7e8348e..1b706bb13 100644 --- a/crates/ra_ide_api/src/completion/presentation.rs +++ b/crates/ra_ide_api/src/completion/presentation.rs | |||
@@ -8,7 +8,7 @@ use crate::completion::{ | |||
8 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, | 8 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::display::{const_label, function_label, type_label}; | 11 | use crate::display::{const_label, function_label, macro_label, type_label}; |
12 | 12 | ||
13 | impl Completions { | 13 | impl Completions { |
14 | pub(crate) fn add_field( | 14 | pub(crate) fn add_field( |
@@ -43,8 +43,14 @@ impl Completions { | |||
43 | ) { | 43 | ) { |
44 | use hir::ModuleDef::*; | 44 | use hir::ModuleDef::*; |
45 | 45 | ||
46 | if let Some(macro_) = resolution.get_macros() { | ||
47 | self.add_macro(ctx, Some(local_name.clone()), macro_); | ||
48 | } | ||
49 | |||
46 | let def = resolution.as_ref().take_types().or_else(|| resolution.as_ref().take_values()); | 50 | let def = resolution.as_ref().take_types().or_else(|| resolution.as_ref().take_values()); |
47 | let def = match def { | 51 | let def = match def { |
52 | // Only insert once if it is just a macro name | ||
53 | None if resolution.get_macros().is_some() => return, | ||
48 | None => { | 54 | None => { |
49 | self.add(CompletionItem::new( | 55 | self.add(CompletionItem::new( |
50 | CompletionKind::Reference, | 56 | CompletionKind::Reference, |
@@ -98,6 +104,22 @@ impl Completions { | |||
98 | self.add_function_with_name(ctx, None, func) | 104 | self.add_function_with_name(ctx, None, func) |
99 | } | 105 | } |
100 | 106 | ||
107 | fn add_macro(&mut self, ctx: &CompletionContext, name: Option<String>, macro_: hir::MacroDef) { | ||
108 | let ast_node = macro_.source(ctx.db).ast; | ||
109 | if let Some(name) = name { | ||
110 | let detail = macro_label(&ast_node); | ||
111 | |||
112 | let builder = | ||
113 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone()) | ||
114 | .kind(CompletionItemKind::Macro) | ||
115 | .set_documentation(macro_.docs(ctx.db)) | ||
116 | .detail(detail) | ||
117 | .insert_snippet(format!("{}!", name)); | ||
118 | |||
119 | self.add(builder); | ||
120 | } | ||
121 | } | ||
122 | |||
101 | fn add_function_with_name( | 123 | fn add_function_with_name( |
102 | &mut self, | 124 | &mut self, |
103 | ctx: &CompletionContext, | 125 | ctx: &CompletionContext, |
diff --git a/crates/ra_ide_api/src/display.rs b/crates/ra_ide_api/src/display.rs index cc59e99d8..a980c56bc 100644 --- a/crates/ra_ide_api/src/display.rs +++ b/crates/ra_ide_api/src/display.rs | |||
@@ -7,7 +7,7 @@ mod structure; | |||
7 | mod short_label; | 7 | mod short_label; |
8 | 8 | ||
9 | use ra_syntax::{ | 9 | use ra_syntax::{ |
10 | ast::{self, AstNode, TypeParamsOwner}, | 10 | ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, |
11 | SyntaxKind::{ATTR, COMMENT}, | 11 | SyntaxKind::{ATTR, COMMENT}, |
12 | }; | 12 | }; |
13 | 13 | ||
@@ -61,6 +61,12 @@ pub(crate) fn where_predicates<N: TypeParamsOwner>(node: &N) -> Vec<String> { | |||
61 | res | 61 | res |
62 | } | 62 | } |
63 | 63 | ||
64 | pub(crate) fn macro_label(node: &ast::MacroCall) -> String { | ||
65 | let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default(); | ||
66 | let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" }; | ||
67 | format!("{}macro_rules! {}", vis, name) | ||
68 | } | ||
69 | |||
64 | 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 { |
65 | rust_code_markup_with_doc::<_, &str>(val, None) | 71 | rust_code_markup_with_doc::<_, &str>(val, None) |
66 | } | 72 | } |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 1981e62d3..4b7847de8 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -12,8 +12,8 @@ use ra_syntax::{ | |||
12 | use crate::{ | 12 | use crate::{ |
13 | db::RootDatabase, | 13 | db::RootDatabase, |
14 | display::{ | 14 | display::{ |
15 | description_from_symbol, docs_from_symbol, rust_code_markup, rust_code_markup_with_doc, | 15 | description_from_symbol, docs_from_symbol, macro_label, rust_code_markup, |
16 | ShortLabel, | 16 | rust_code_markup_with_doc, ShortLabel, |
17 | }, | 17 | }, |
18 | name_ref_kind::{classify_name_ref, NameRefKind::*}, | 18 | name_ref_kind::{classify_name_ref, NameRefKind::*}, |
19 | FilePosition, FileRange, RangeInfo, | 19 | FilePosition, FileRange, RangeInfo, |
@@ -108,7 +108,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
108 | Some(Method(it)) => res.extend(from_def_source(db, it)), | 108 | Some(Method(it)) => res.extend(from_def_source(db, it)), |
109 | Some(Macro(it)) => { | 109 | Some(Macro(it)) => { |
110 | let src = it.source(db); | 110 | let src = it.source(db); |
111 | res.extend(hover_text(src.ast.doc_comment_text(), None)); | 111 | res.extend(hover_text(src.ast.doc_comment_text(), Some(macro_label(&src.ast)))); |
112 | } | 112 | } |
113 | Some(FieldAccess(it)) => { | 113 | Some(FieldAccess(it)) => { |
114 | let src = it.source(db); | 114 | let src = it.source(db); |
@@ -700,4 +700,22 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
700 | assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); | 700 | assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); |
701 | assert_eq!(hover.info.is_exact(), true); | 701 | assert_eq!(hover.info.is_exact(), true); |
702 | } | 702 | } |
703 | |||
704 | #[test] | ||
705 | fn test_hover_macro_invocation() { | ||
706 | let (analysis, position) = single_file_with_position( | ||
707 | " | ||
708 | macro_rules! foo { | ||
709 | () => {} | ||
710 | } | ||
711 | |||
712 | fn f() { | ||
713 | fo<|>o!(); | ||
714 | } | ||
715 | ", | ||
716 | ); | ||
717 | let hover = analysis.hover(position).unwrap().unwrap(); | ||
718 | assert_eq!(trim_markup_opt(hover.info.first()), Some("macro_rules! foo")); | ||
719 | assert_eq!(hover.info.is_exact(), true); | ||
720 | } | ||
703 | } | 721 | } |