From 7de9537cccc7a6338bcd9c892a1fdb4a6008dc1a Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Tue, 10 Sep 2019 13:32:47 +0800 Subject: [PATCH] Support completion for macros --- .../src/completion/complete_path.rs | 36 +++ .../src/completion/complete_scope.rs | 229 ++++++++++++++++++ .../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 d6b5ac9ad7..55c78d305c 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 67fb7ba4ef..e2e1d7872a 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 db7e8348e1..1b706bb131 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 cc59e99d88..a980c56bc0 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) }