mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-30 15:03:42 +00:00
Support completion for macros
This commit is contained in:
parent
e2ebb467bd
commit
7de9537ccc
4 changed files with 295 additions and 2 deletions
|
@ -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()",
|
||||||
|
},
|
||||||
]"###
|
]"###
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
},
|
||||||
|
]"##
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue