]> git.lizzy.rs Git - rust.git/commitdiff
Guess macro braces from docs
authoroxalica <oxalicc@pm.me>
Sun, 20 Oct 2019 18:16:01 +0000 (02:16 +0800)
committeroxalica <oxalicc@pm.me>
Sun, 20 Oct 2019 18:16:01 +0000 (02:16 +0800)
crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs
crates/ra_ide_api/src/completion/presentation.rs

index d808b23571c29c1cb8944dab9d571d0f8e7b13ba..09f743c660cb2220b784e47506b040c39dbc9ddb 100644 (file)
@@ -56,6 +56,16 @@ fn completes_vec_macros_with_square_brackets() {
             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 @@ fn foo() {}
             @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()",
+    },
+]
+        "###
+        );
+    }
 }
index 20242d293c0e89fad00e9881f889fe440180d71b..aed4ce6d44bc8d0655dab72fd2864281f413d297 100644 (file)
@@ -131,6 +131,33 @@ pub(crate) fn add_function(&mut self, ctx: &CompletionContext, func: hir::Functi
         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(
         &mut self,
         ctx: &CompletionContext,
@@ -141,10 +168,9 @@ pub(crate) fn add_macro(
         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 @@ pub(crate) fn add_macro(
                 &macro_declaration,
             )
             .kind(CompletionItemKind::Macro)
-            .set_documentation(macro_.docs(ctx.db))
+            .set_documentation(docs)
             .detail(detail)
             .insert_snippet(macro_declaration + macro_braces_to_insert);