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 {
() => {}
}
@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()",
+ },
+]
+ "###
+ );
+ }
}
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,
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(
¯o_declaration,
)
.kind(CompletionItemKind::Macro)
- .set_documentation(macro_.docs(ctx.db))
+ .set_documentation(docs)
.detail(detail)
.insert_snippet(macro_declaration + macro_braces_to_insert);