From 24d50ebcd1fabedc8d0c468f8785b0aeb1fb176f Mon Sep 17 00:00:00 2001 From: oxalica Date: Mon, 21 Oct 2019 02:16:01 +0800 Subject: Guess macro braces from docs --- .../completion/complete_macro_in_item_position.rs | 62 +++++++++++++++++++++- crates/ra_ide_api/src/completion/presentation.rs | 36 +++++++++++-- 2 files changed, 91 insertions(+), 7 deletions(-) diff --git a/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs index d808b2357..09f743c66 100644 --- a/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs +++ b/crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs @@ -56,6 +56,16 @@ mod tests { do_reference_completion( " //- /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 { () => {} } @@ -68,13 +78,61 @@ mod tests { @r##"[ CompletionItem { label: "vec!", - source_range: [46; 46), - delete: [46; 46), + source_range: [280; 280), + delete: [280; 280), insert: "vec![$0]", kind: Macro, 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()", + }, +] + "### + ); + } } diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs index 20242d293..aed4ce6d4 100644 --- a/crates/ra_ide_api/src/completion/presentation.rs +++ b/crates/ra_ide_api/src/completion/presentation.rs @@ -131,6 +131,33 @@ impl Completions { 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(¯o_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( &mut self, ctx: &CompletionContext, @@ -141,10 +168,9 @@ impl Completions { if let Some(name) = name { let detail = macro_label(&ast_node); - let macro_braces_to_insert = match name.as_str() { - "vec" => "[$0]", - _ => "($0)", - }; + let docs = macro_.docs(ctx.db); + let macro_braces_to_insert = + self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str())); let macro_declaration = name + "!"; let builder = CompletionItem::new( @@ -153,7 +179,7 @@ impl Completions { ¯o_declaration, ) .kind(CompletionItemKind::Macro) - .set_documentation(macro_.docs(ctx.db)) + .set_documentation(docs) .detail(detail) .insert_snippet(macro_declaration + macro_braces_to_insert); -- cgit v1.2.3