From 7de9537cccc7a6338bcd9c892a1fdb4a6008dc1a Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Tue, 10 Sep 2019 13:32:47 +0800 Subject: Support completion for macros --- crates/ra_ide_api/src/completion/complete_path.rs | 36 ++++ crates/ra_ide_api/src/completion/complete_scope.rs | 229 +++++++++++++++++++++ crates/ra_ide_api/src/completion/presentation.rs | 24 ++- crates/ra_ide_api/src/display.rs | 8 +- 4 files changed, 295 insertions(+), 2 deletions(-) diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index d6b5ac9ad..55c78d305 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs @@ -584,6 +584,42 @@ mod tests { kind: Function, detail: "fn foo()", }, +]"### + ); + } + + #[test] + fn completes_quantified_macros() { + assert_debug_snapshot!( + do_reference_completion( + " + #[macro_export] + macro_rules! foo { + () => {} + } + + fn main() { + let _ = crate::<|> + } + " + ), + @r###"[ + CompletionItem { + label: "foo", + source_range: [179; 179), + delete: [179; 179), + insert: "foo!", + kind: Macro, + detail: "#[macro_export]\nmacro_rules! foo", + }, + CompletionItem { + label: "main", + source_range: [179; 179), + delete: [179; 179), + insert: "main()$0", + kind: Function, + detail: "fn main()", + }, ]"### ); } diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs index 67fb7ba4e..e2e1d7872 100644 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ b/crates/ra_ide_api/src/completion/complete_scope.rs @@ -6,6 +6,15 @@ use rustc_hash::FxHashMap; use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { + // Show only macros in top level. + if ctx.is_new_item { + for (name, res) in ctx.analyzer.all_names(ctx.db) { + if res.get_macros().is_some() { + acc.add_resolution(ctx, name.to_string(), &res.only_macros()); + } + } + } + if !ctx.is_trivial_path { return; } @@ -532,4 +541,224 @@ mod tests { ]"# ); } + + #[test] + fn completes_macros_as_value() { + assert_debug_snapshot!( + do_reference_completion( + " + //- /main.rs + macro_rules! foo { + () => {} + } + + #[macro_use] + mod m1 { + macro_rules! bar { + () => {} + } + } + + mod m2 { + macro_rules! nope { + () => {} + } + + #[macro_export] + macro_rules! baz { + () => {} + } + } + + fn main() { + let v = <|> + } + " + ), + @r##"[ + CompletionItem { + label: "bar", + source_range: [252; 252), + delete: [252; 252), + insert: "bar!", + kind: Macro, + detail: "macro_rules! bar", + }, + CompletionItem { + label: "baz", + source_range: [252; 252), + delete: [252; 252), + insert: "baz!", + kind: Macro, + detail: "#[macro_export]\nmacro_rules! baz", + }, + CompletionItem { + label: "foo", + source_range: [252; 252), + delete: [252; 252), + insert: "foo!", + kind: Macro, + detail: "macro_rules! foo", + }, + CompletionItem { + label: "m1", + source_range: [252; 252), + delete: [252; 252), + insert: "m1", + kind: Module, + }, + CompletionItem { + label: "m2", + source_range: [252; 252), + delete: [252; 252), + insert: "m2", + kind: Module, + }, + CompletionItem { + label: "main", + source_range: [252; 252), + delete: [252; 252), + insert: "main()$0", + kind: Function, + detail: "fn main()", + }, +]"## + ); + } + + #[test] + fn completes_both_macro_and_value() { + assert_debug_snapshot!( + do_reference_completion( + " + //- /main.rs + macro_rules! foo { + () => {} + } + + fn foo() { + <|> + } + " + ), + @r##"[ + CompletionItem { + label: "foo", + source_range: [49; 49), + delete: [49; 49), + insert: "foo!", + kind: Macro, + detail: "macro_rules! foo", + }, + CompletionItem { + label: "foo", + source_range: [49; 49), + delete: [49; 49), + insert: "foo()$0", + kind: Function, + detail: "fn foo()", + }, +]"## + ); + } + + #[test] + fn completes_macros_as_type() { + assert_debug_snapshot!( + do_reference_completion( + " + //- /main.rs + macro_rules! foo { + () => {} + } + + fn main() { + let x: <|> + } + " + ), + @r##"[ + CompletionItem { + label: "foo", + source_range: [57; 57), + delete: [57; 57), + insert: "foo!", + kind: Macro, + detail: "macro_rules! foo", + }, + CompletionItem { + label: "main", + source_range: [57; 57), + delete: [57; 57), + insert: "main()$0", + kind: Function, + detail: "fn main()", + }, +]"## + ); + } + + #[test] + fn completes_macros_as_stmt() { + assert_debug_snapshot!( + do_reference_completion( + " + //- /main.rs + macro_rules! foo { + () => {} + } + + fn main() { + <|> + } + " + ), + @r##"[ + CompletionItem { + label: "foo", + source_range: [50; 50), + delete: [50; 50), + insert: "foo!", + kind: Macro, + detail: "macro_rules! foo", + }, + CompletionItem { + label: "main", + source_range: [50; 50), + delete: [50; 50), + insert: "main()$0", + kind: Function, + detail: "fn main()", + }, +]"## + ); + } + + #[test] + fn completes_macros_as_item() { + assert_debug_snapshot!( + do_reference_completion( + " + //- /main.rs + macro_rules! foo { + () => {} + } + + fn foo() {} + + <|> + " + ), + @r##"[ + CompletionItem { + label: "foo", + source_range: [46; 46), + delete: [46; 46), + insert: "foo!", + kind: Macro, + detail: "macro_rules! foo", + }, +]"## + ); + } } 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::{ CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, }; -use crate::display::{const_label, function_label, type_label}; +use crate::display::{const_label, function_label, macro_label, type_label}; impl Completions { pub(crate) fn add_field( @@ -43,8 +43,14 @@ impl Completions { ) { use hir::ModuleDef::*; + if let Some(macro_) = resolution.get_macros() { + self.add_macro(ctx, Some(local_name.clone()), macro_); + } + let def = resolution.as_ref().take_types().or_else(|| resolution.as_ref().take_values()); let def = match def { + // Only insert once if it is just a macro name + None if resolution.get_macros().is_some() => return, None => { self.add(CompletionItem::new( CompletionKind::Reference, @@ -98,6 +104,22 @@ impl Completions { self.add_function_with_name(ctx, None, func) } + fn add_macro(&mut self, ctx: &CompletionContext, name: Option, macro_: hir::MacroDef) { + let ast_node = macro_.source(ctx.db).ast; + if let Some(name) = name { + let detail = macro_label(&ast_node); + + let builder = + CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone()) + .kind(CompletionItemKind::Macro) + .set_documentation(macro_.docs(ctx.db)) + .detail(detail) + .insert_snippet(format!("{}!", name)); + + self.add(builder); + } + } + fn add_function_with_name( &mut self, 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; mod short_label; use ra_syntax::{ - ast::{self, AstNode, TypeParamsOwner}, + ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, SyntaxKind::{ATTR, COMMENT}, }; @@ -61,6 +61,12 @@ pub(crate) fn where_predicates(node: &N) -> Vec { res } +pub(crate) fn macro_label(node: &ast::MacroCall) -> String { + let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default(); + let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" }; + format!("{}macro_rules! {}", vis, name) +} + pub(crate) fn rust_code_markup>(val: CODE) -> String { rust_code_markup_with_doc::<_, &str>(val, None) } -- cgit v1.2.3 From c66a789a045c3c521620af73c1f06314fbe315bb Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Tue, 10 Sep 2019 13:33:02 +0800 Subject: Show macro definition in hover text --- crates/ra_ide_api/src/hover.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) 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::{ use crate::{ db::RootDatabase, display::{ - description_from_symbol, docs_from_symbol, rust_code_markup, rust_code_markup_with_doc, - ShortLabel, + description_from_symbol, docs_from_symbol, macro_label, rust_code_markup, + rust_code_markup_with_doc, ShortLabel, }, name_ref_kind::{classify_name_ref, NameRefKind::*}, FilePosition, FileRange, RangeInfo, @@ -108,7 +108,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option res.extend(from_def_source(db, it)), Some(Macro(it)) => { let src = it.source(db); - res.extend(hover_text(src.ast.doc_comment_text(), None)); + res.extend(hover_text(src.ast.doc_comment_text(), Some(macro_label(&src.ast)))); } Some(FieldAccess(it)) => { let src = it.source(db); @@ -700,4 +700,22 @@ fn func(foo: i32) { if true { <|>foo; }; } assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); assert_eq!(hover.info.is_exact(), true); } + + #[test] + fn test_hover_macro_invocation() { + let (analysis, position) = single_file_with_position( + " + macro_rules! foo { + () => {} + } + + fn f() { + fo<|>o!(); + } + ", + ); + let hover = analysis.hover(position).unwrap().unwrap(); + assert_eq!(trim_markup_opt(hover.info.first()), Some("macro_rules! foo")); + assert_eq!(hover.info.is_exact(), true); + } } -- cgit v1.2.3 From c033d18700e86c9bb028344f28e2eb7d670fe241 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Wed, 11 Sep 2019 22:39:02 +0800 Subject: Fix typo --- crates/ra_hir/src/nameres.rs | 2 +- crates/ra_hir/src/nameres/tests/macros.rs | 2 +- crates/ra_hir/src/ty/tests.rs | 2 +- crates/ra_ide_api/src/completion/complete_path.rs | 2 +- 4 files changed, 4 insertions(+), 4 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 { ); } - // Since it is a quantified path here, it should not contains legacy macros + // Since it is a qualified path here, it should not contains legacy macros match self[module.module_id].scope.get(&segment.name) { Some(res) => res.def, _ => { 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() { } #[test] -fn path_quantified_macros() { +fn path_qualified_macros() { let map = def_map( " //- /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() { } #[test] -fn infer_path_quantified_macros_expanded() { +fn infer_path_qualified_macros_expanded() { assert_snapshot!( infer(r#" #[macro_export] diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index 55c78d305..31e7dffe8 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs @@ -589,7 +589,7 @@ mod tests { } #[test] - fn completes_quantified_macros() { + fn completes_qualified_macros() { assert_debug_snapshot!( do_reference_completion( " -- cgit v1.2.3 From 6353b1621f44e1b0db65ebbe414aa7c5f1864b9d Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Wed, 11 Sep 2019 22:44:44 +0800 Subject: Split out `complete_macro_in_item_position` --- crates/ra_ide_api/src/completion.rs | 2 + .../completion/complete_macro_in_item_position.rs | 50 ++++++++++++++++++++++ crates/ra_ide_api/src/completion/complete_scope.rs | 37 ---------------- 3 files changed, 52 insertions(+), 37 deletions(-) create mode 100644 crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs 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; mod complete_path; mod complete_scope; mod complete_postfix; +mod complete_macro_in_item_position; use ra_db::SourceDatabase; @@ -69,5 +70,6 @@ pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Opti complete_record_pattern::complete_record_pattern(&mut acc, &ctx); complete_pattern::complete_pattern(&mut acc, &ctx); complete_postfix::complete_postfix(&mut acc, &ctx); + complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); Some(acc) } 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 @@ +use crate::completion::{CompletionContext, Completions}; + +pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { + // Show only macros in top level. + if ctx.is_new_item { + for (name, res) in ctx.analyzer.all_names(ctx.db) { + if res.get_macros().is_some() { + acc.add_resolution(ctx, name.to_string(), &res.only_macros()); + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::completion::{do_completion, CompletionItem, CompletionKind}; + use insta::assert_debug_snapshot; + + fn do_reference_completion(code: &str) -> Vec { + do_completion(code, CompletionKind::Reference) + } + + #[test] + fn completes_macros_as_item() { + assert_debug_snapshot!( + do_reference_completion( + " + //- /main.rs + macro_rules! foo { + () => {} + } + + fn foo() {} + + <|> + " + ), + @r##"[ + CompletionItem { + label: "foo", + source_range: [46; 46), + delete: [46; 46), + insert: "foo!", + kind: Macro, + detail: "macro_rules! foo", + }, +]"## + ); + } +} diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs index e2e1d7872..2062e7300 100644 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ b/crates/ra_ide_api/src/completion/complete_scope.rs @@ -6,15 +6,6 @@ use rustc_hash::FxHashMap; use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { - // Show only macros in top level. - if ctx.is_new_item { - for (name, res) in ctx.analyzer.all_names(ctx.db) { - if res.get_macros().is_some() { - acc.add_resolution(ctx, name.to_string(), &res.only_macros()); - } - } - } - if !ctx.is_trivial_path { return; } @@ -730,34 +721,6 @@ mod tests { kind: Function, detail: "fn main()", }, -]"## - ); - } - - #[test] - fn completes_macros_as_item() { - assert_debug_snapshot!( - do_reference_completion( - " - //- /main.rs - macro_rules! foo { - () => {} - } - - fn foo() {} - - <|> - " - ), - @r##"[ - CompletionItem { - label: "foo", - source_range: [46; 46), - delete: [46; 46), - insert: "foo!", - kind: Macro, - detail: "macro_rules! foo", - }, ]"## ); } -- cgit v1.2.3