Support completion for macros

This commit is contained in:
uHOOCCOOHu 2019-09-10 13:32:47 +08:00
parent e2ebb467bd
commit 7de9537ccc
No known key found for this signature in database
GPG key ID: CED392DE0C483D00
4 changed files with 295 additions and 2 deletions

View file

@ -584,6 +584,42 @@ mod tests {
kind: Function, kind: Function,
detail: "fn foo()", 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()",
},
]"### ]"###
); );
} }

View file

@ -6,6 +6,15 @@ use rustc_hash::FxHashMap;
use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { 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 { if !ctx.is_trivial_path {
return; 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",
},
]"##
);
}
} }

View file

@ -8,7 +8,7 @@ use crate::completion::{
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 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 { impl Completions {
pub(crate) fn add_field( pub(crate) fn add_field(
@ -43,8 +43,14 @@ impl Completions {
) { ) {
use hir::ModuleDef::*; 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 = resolution.as_ref().take_types().or_else(|| resolution.as_ref().take_values());
let def = match def { let def = match def {
// Only insert once if it is just a macro name
None if resolution.get_macros().is_some() => return,
None => { None => {
self.add(CompletionItem::new( self.add(CompletionItem::new(
CompletionKind::Reference, CompletionKind::Reference,
@ -98,6 +104,22 @@ impl Completions {
self.add_function_with_name(ctx, None, func) self.add_function_with_name(ctx, None, func)
} }
fn add_macro(&mut self, ctx: &CompletionContext, name: Option<String>, 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( fn add_function_with_name(
&mut self, &mut self,
ctx: &CompletionContext, ctx: &CompletionContext,

View file

@ -7,7 +7,7 @@ mod structure;
mod short_label; mod short_label;
use ra_syntax::{ use ra_syntax::{
ast::{self, AstNode, TypeParamsOwner}, ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner},
SyntaxKind::{ATTR, COMMENT}, SyntaxKind::{ATTR, COMMENT},
}; };
@ -61,6 +61,12 @@ pub(crate) fn where_predicates<N: TypeParamsOwner>(node: &N) -> Vec<String> {
res 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<CODE: AsRef<str>>(val: CODE) -> String { pub(crate) fn rust_code_markup<CODE: AsRef<str>>(val: CODE) -> String {
rust_code_markup_with_doc::<_, &str>(val, None) rust_code_markup_with_doc::<_, &str>(val, None)
} }