Guess macro braces from docs

This commit is contained in:
oxalica 2019-10-21 02:16:01 +08:00
parent 6b9bd7bdd2
commit 24d50ebcd1
No known key found for this signature in database
GPG key ID: CED392DE0C483D00
2 changed files with 91 additions and 7 deletions

View file

@ -56,6 +56,16 @@ mod tests {
do_reference_completion( do_reference_completion(
" "
//- /main.rs //- /main.rs
/// Creates a [`Vec`] containing the arguments.
///
/// - Create a [`Vec`] containing a given list of elements:
///
/// ```
/// let v = vec![1, 2, 3];
/// assert_eq!(v[0], 1);
/// assert_eq!(v[1], 2);
/// assert_eq!(v[2], 3);
/// ```
macro_rules! vec { macro_rules! vec {
() => {} () => {}
} }
@ -68,13 +78,61 @@ mod tests {
@r##"[ @r##"[
CompletionItem { CompletionItem {
label: "vec!", label: "vec!",
source_range: [46; 46), source_range: [280; 280),
delete: [46; 46), delete: [280; 280),
insert: "vec![$0]", insert: "vec![$0]",
kind: Macro, kind: Macro,
detail: "macro_rules! vec", detail: "macro_rules! vec",
documentation: Documentation(
"Creates a [`Vec`] containing the arguments.\n\n- Create a [`Vec`] containing a given list of elements:\n\n```\nlet v = vec![1, 2, 3];\nassert_eq!(v[0], 1);\nassert_eq!(v[1], 2);\nassert_eq!(v[2], 3);\n```",
),
}, },
]"## ]"##
); );
} }
#[test]
fn completes_macros_braces_guessing() {
assert_debug_snapshot!(
do_reference_completion(
"
//- /main.rs
/// Foo
///
/// Not call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.
/// Call as `let _=foo! { hello world };`
macro_rules! foo {
() => {}
}
fn main() {
<|>
}
"
),
@r###"[
CompletionItem {
label: "foo!",
source_range: [163; 163),
delete: [163; 163),
insert: "foo! {$0}",
kind: Macro,
detail: "macro_rules! foo",
documentation: Documentation(
"Foo\n\nNot call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.\nCall as `let _=foo! { hello world };`",
),
},
CompletionItem {
label: "main()",
source_range: [163; 163),
delete: [163; 163),
insert: "main()$0",
kind: Function,
lookup: "main",
detail: "fn main()",
},
]
"###
);
}
} }

View file

@ -131,6 +131,33 @@ impl Completions {
self.add_function_with_name(ctx, None, func) self.add_function_with_name(ctx, None, func)
} }
fn guess_macro_braces(&self, macro_name: &str, docs: &str) -> &'static str {
let mut votes = [0, 0, 0];
for (idx, s) in docs.match_indices(&macro_name) {
let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
// Ensure to match the full word
if after.starts_with("!")
&& before
.chars()
.rev()
.next()
.map_or(true, |c| c != '_' && !c.is_ascii_alphanumeric())
{
// It may have spaces before the braces like `foo! {}`
match after[1..].chars().find(|&c| !c.is_whitespace()) {
Some('{') => votes[0] += 1,
Some('[') => votes[1] += 1,
Some('(') => votes[2] += 1,
_ => {}
}
}
}
// Insert a space before `{}`.
// We prefer the last one when some votes equal.
*votes.iter().zip(&[" {$0}", "[$0]", "($0)"]).max_by_key(|&(&vote, _)| vote).unwrap().1
}
pub(crate) fn add_macro( pub(crate) fn add_macro(
&mut self, &mut self,
ctx: &CompletionContext, ctx: &CompletionContext,
@ -141,10 +168,9 @@ impl Completions {
if let Some(name) = name { if let Some(name) = name {
let detail = macro_label(&ast_node); let detail = macro_label(&ast_node);
let macro_braces_to_insert = match name.as_str() { let docs = macro_.docs(ctx.db);
"vec" => "[$0]", let macro_braces_to_insert =
_ => "($0)", self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str()));
};
let macro_declaration = name + "!"; let macro_declaration = name + "!";
let builder = CompletionItem::new( let builder = CompletionItem::new(
@ -153,7 +179,7 @@ impl Completions {
&macro_declaration, &macro_declaration,
) )
.kind(CompletionItemKind::Macro) .kind(CompletionItemKind::Macro)
.set_documentation(macro_.docs(ctx.db)) .set_documentation(docs)
.detail(detail) .detail(detail)
.insert_snippet(macro_declaration + macro_braces_to_insert); .insert_snippet(macro_declaration + macro_braces_to_insert);