aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-09-11 15:49:57 +0100
committerGitHub <[email protected]>2019-09-11 15:49:57 +0100
commit6ce6744e18f25ebcde387178125d820686692df5 (patch)
treee1861982356f9905980537f962dff9560423f901
parent7bbb039fbdd124cb6549eb67bbe7316f03ff40e8 (diff)
parent6353b1621f44e1b0db65ebbe414aa7c5f1864b9d (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.rs2
-rw-r--r--crates/ra_hir/src/nameres/tests/macros.rs2
-rw-r--r--crates/ra_hir/src/ty/tests.rs2
-rw-r--r--crates/ra_ide_api/src/completion.rs2
-rw-r--r--crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs50
-rw-r--r--crates/ra_ide_api/src/completion/complete_path.rs36
-rw-r--r--crates/ra_ide_api/src/completion/complete_scope.rs192
-rw-r--r--crates/ra_ide_api/src/completion/presentation.rs24
-rw-r--r--crates/ra_ide_api/src/display.rs8
-rw-r--r--crates/ra_ide_api/src/hover.rs24
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]
433fn path_quantified_macros() { 433fn 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]
2842fn infer_path_quantified_macros_expanded() { 2842fn 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;
12mod complete_path; 12mod complete_path;
13mod complete_scope; 13mod complete_scope;
14mod complete_postfix; 14mod complete_postfix;
15mod complete_macro_in_item_position;
15 16
16use ra_db::SourceDatabase; 17use 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 @@
1use crate::completion::{CompletionContext, Completions};
2
3pub(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)]
15mod 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
11use crate::display::{const_label, function_label, type_label}; 11use crate::display::{const_label, function_label, macro_label, type_label};
12 12
13impl Completions { 13impl 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;
7mod short_label; 7mod short_label;
8 8
9use ra_syntax::{ 9use 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
64pub(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
64pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String { 70pub(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::{
12use crate::{ 12use 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}