Better label for macros completion

This commit is contained in:
Aleksey Kladov 2020-04-24 13:15:39 +02:00
parent 44e6c2cb54
commit 62e08fa53d
5 changed files with 59 additions and 48 deletions

View File

@ -41,7 +41,7 @@ mod tests {
@r###"
[
CompletionItem {
label: "foo!",
label: "foo!(…)",
source_range: [46; 46),
delete: [46; 46),
insert: "foo!($0)",
@ -81,7 +81,7 @@ mod tests {
@r###"
[
CompletionItem {
label: "vec!",
label: "vec![…]",
source_range: [280; 280),
delete: [280; 280),
insert: "vec![$0]",
@ -118,7 +118,7 @@ mod tests {
@r###"
[
CompletionItem {
label: "foo!",
label: "foo! {…}",
source_range: [163; 163),
delete: [163; 163),
insert: "foo! {$0}",

View File

@ -125,7 +125,7 @@ mod tests {
kind: Enum,
},
CompletionItem {
label: "m!",
label: "m!(…)",
source_range: [151; 151),
delete: [151; 151),
insert: "m!($0)",

View File

@ -869,7 +869,7 @@ mod tests {
@r###"
[
CompletionItem {
label: "foo!",
label: "foo!(…)",
source_range: [179; 179),
delete: [179; 179),
insert: "foo!($0)",

View File

@ -733,7 +733,7 @@ mod tests {
@r###"
[
CompletionItem {
label: "bar!",
label: "bar!(…)",
source_range: [252; 252),
delete: [252; 252),
insert: "bar!($0)",
@ -741,7 +741,7 @@ mod tests {
detail: "macro_rules! bar",
},
CompletionItem {
label: "baz!",
label: "baz!(…)",
source_range: [252; 252),
delete: [252; 252),
insert: "baz!($0)",
@ -749,7 +749,7 @@ mod tests {
detail: "#[macro_export]\nmacro_rules! baz",
},
CompletionItem {
label: "foo!",
label: "foo!(…)",
source_range: [252; 252),
delete: [252; 252),
insert: "foo!($0)",
@ -802,7 +802,7 @@ mod tests {
@r###"
[
CompletionItem {
label: "foo!",
label: "foo!(…)",
source_range: [49; 49),
delete: [49; 49),
insert: "foo!($0)",
@ -841,7 +841,7 @@ mod tests {
@r###"
[
CompletionItem {
label: "foo!",
label: "foo!(…)",
source_range: [57; 57),
delete: [57; 57),
insert: "foo!($0)",
@ -880,7 +880,7 @@ mod tests {
@r###"
[
CompletionItem {
label: "foo!",
label: "foo!(…)",
source_range: [50; 50),
delete: [50; 50),
insert: "foo!($0)",
@ -953,7 +953,7 @@ mod tests {
@r###"
[
CompletionItem {
label: "m!",
label: "m!(…)",
source_range: [145; 145),
delete: [145; 145),
insert: "m!($0)",
@ -1006,7 +1006,7 @@ mod tests {
@r###"
[
CompletionItem {
label: "m!",
label: "m!(…)",
source_range: [145; 146),
delete: [145; 146),
insert: "m!($0)",
@ -1059,7 +1059,7 @@ mod tests {
@r###"
[
CompletionItem {
label: "m!",
label: "m!(…)",
source_range: [145; 146),
delete: [145; 146),
insert: "m!($0)",

View File

@ -133,29 +133,6 @@ impl Completions {
completion_item.kind(kind).set_documentation(docs).add_to(self)
}
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.ends_with(|c: char| 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(
&mut self,
ctx: &CompletionContext,
@ -177,21 +154,27 @@ impl Completions {
let detail = macro_label(&ast_node);
let docs = macro_.docs(ctx.db);
let macro_declaration = format!("{}!", name);
let mut builder =
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), &macro_declaration)
.kind(CompletionItemKind::Macro)
.set_documentation(docs.clone())
.set_deprecated(is_deprecated(macro_, ctx.db))
.detail(detail);
let mut builder = CompletionItem::new(
CompletionKind::Reference,
ctx.source_range(),
&format!("{}!", name),
)
.kind(CompletionItemKind::Macro)
.set_documentation(docs.clone())
.set_deprecated(is_deprecated(macro_, ctx.db))
.detail(detail);
let needs_bang = ctx.use_item_syntax.is_none() && !ctx.is_macro_call;
builder = match ctx.config.snippet_cap {
Some(cap) if ctx.use_item_syntax.is_none() && !ctx.is_macro_call => {
let macro_braces_to_insert =
self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str()));
builder.insert_snippet(cap, macro_declaration + macro_braces_to_insert)
Some(cap) if needs_bang => {
let docs = docs.as_ref().map_or("", |s| s.as_str());
let (bra, ket) = guess_macro_braces(&name, docs);
builder
.insert_snippet(cap, format!("{}!{}$0{}", name, bra, ket))
.label(format!("{}!{}{}", name, bra, ket))
}
None if needs_bang => builder.insert_text(format!("{}!", name)),
_ => {
tested_by!(dont_insert_macro_call_parens_unncessary);
builder.insert_text(name)
@ -404,6 +387,34 @@ fn is_deprecated(node: impl HasAttrs, db: &RootDatabase) -> bool {
node.attrs(db).by_key("deprecated").exists()
}
fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static 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.ends_with(|c: char| 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.
let (_vote, (bra, ket)) = votes
.iter()
.zip(&[(" {", "}"), ("[", "]"), ("(", ")")])
.max_by_key(|&(&vote, _)| vote)
.unwrap();
(*bra, *ket)
}
#[cfg(test)]
mod tests {
use insta::assert_debug_snapshot;