aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-10-21 12:42:05 +0100
committerGitHub <[email protected]>2019-10-21 12:42:05 +0100
commite4810a302b4badb024d22da50cfa4aae64184493 (patch)
tree8734d9ff3bc11208169b49f8f3845035b8e4556a
parentf9d060bf442edeba0b3f3f28dbe1e6cb19e6303e (diff)
parent24d50ebcd1fabedc8d0c468f8785b0aeb1fb176f (diff)
Merge #2039
2039: Guess macro braces from docs r=matklad a=oxalica Instead of hard-code `vec` to have snippet with bracket `vec![]`, we try to find the "most common used brace kind" from documentation of the macro, which usually contains some example code. It also works better with extern macros. We can suggest braces for `thread_local! {}` now. Co-authored-by: oxalica <[email protected]>
-rw-r--r--crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs62
-rw-r--r--crates/ra_ide_api/src/completion/presentation.rs36
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 {
56 do_reference_completion( 56 do_reference_completion(
57 " 57 "
58 //- /main.rs 58 //- /main.rs
59 /// Creates a [`Vec`] containing the arguments.
60 ///
61 /// - Create a [`Vec`] containing a given list of elements:
62 ///
63 /// ```
64 /// let v = vec![1, 2, 3];
65 /// assert_eq!(v[0], 1);
66 /// assert_eq!(v[1], 2);
67 /// assert_eq!(v[2], 3);
68 /// ```
59 macro_rules! vec { 69 macro_rules! vec {
60 () => {} 70 () => {}
61 } 71 }
@@ -68,13 +78,61 @@ mod tests {
68 @r##"[ 78 @r##"[
69 CompletionItem { 79 CompletionItem {
70 label: "vec!", 80 label: "vec!",
71 source_range: [46; 46), 81 source_range: [280; 280),
72 delete: [46; 46), 82 delete: [280; 280),
73 insert: "vec![$0]", 83 insert: "vec![$0]",
74 kind: Macro, 84 kind: Macro,
75 detail: "macro_rules! vec", 85 detail: "macro_rules! vec",
86 documentation: Documentation(
87 "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```",
88 ),
76 }, 89 },
77]"## 90]"##
78 ); 91 );
79 } 92 }
93
94 #[test]
95 fn completes_macros_braces_guessing() {
96 assert_debug_snapshot!(
97 do_reference_completion(
98 "
99 //- /main.rs
100 /// Foo
101 ///
102 /// Not call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.
103 /// Call as `let _=foo! { hello world };`
104 macro_rules! foo {
105 () => {}
106 }
107
108 fn main() {
109 <|>
110 }
111 "
112 ),
113 @r###"[
114 CompletionItem {
115 label: "foo!",
116 source_range: [163; 163),
117 delete: [163; 163),
118 insert: "foo! {$0}",
119 kind: Macro,
120 detail: "macro_rules! foo",
121 documentation: Documentation(
122 "Foo\n\nNot call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.\nCall as `let _=foo! { hello world };`",
123 ),
124 },
125 CompletionItem {
126 label: "main()",
127 source_range: [163; 163),
128 delete: [163; 163),
129 insert: "main()$0",
130 kind: Function,
131 lookup: "main",
132 detail: "fn main()",
133 },
134]
135 "###
136 );
137 }
80} 138}
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 {
131 self.add_function_with_name(ctx, None, func) 131 self.add_function_with_name(ctx, None, func)
132 } 132 }
133 133
134 fn guess_macro_braces(&self, macro_name: &str, docs: &str) -> &'static str {
135 let mut votes = [0, 0, 0];
136 for (idx, s) in docs.match_indices(&macro_name) {
137 let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
138 // Ensure to match the full word
139 if after.starts_with("!")
140 && before
141 .chars()
142 .rev()
143 .next()
144 .map_or(true, |c| c != '_' && !c.is_ascii_alphanumeric())
145 {
146 // It may have spaces before the braces like `foo! {}`
147 match after[1..].chars().find(|&c| !c.is_whitespace()) {
148 Some('{') => votes[0] += 1,
149 Some('[') => votes[1] += 1,
150 Some('(') => votes[2] += 1,
151 _ => {}
152 }
153 }
154 }
155
156 // Insert a space before `{}`.
157 // We prefer the last one when some votes equal.
158 *votes.iter().zip(&[" {$0}", "[$0]", "($0)"]).max_by_key(|&(&vote, _)| vote).unwrap().1
159 }
160
134 pub(crate) fn add_macro( 161 pub(crate) fn add_macro(
135 &mut self, 162 &mut self,
136 ctx: &CompletionContext, 163 ctx: &CompletionContext,
@@ -141,10 +168,9 @@ impl Completions {
141 if let Some(name) = name { 168 if let Some(name) = name {
142 let detail = macro_label(&ast_node); 169 let detail = macro_label(&ast_node);
143 170
144 let macro_braces_to_insert = match name.as_str() { 171 let docs = macro_.docs(ctx.db);
145 "vec" => "[$0]", 172 let macro_braces_to_insert =
146 _ => "($0)", 173 self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str()));
147 };
148 let macro_declaration = name + "!"; 174 let macro_declaration = name + "!";
149 175
150 let builder = CompletionItem::new( 176 let builder = CompletionItem::new(
@@ -153,7 +179,7 @@ impl Completions {
153 &macro_declaration, 179 &macro_declaration,
154 ) 180 )
155 .kind(CompletionItemKind::Macro) 181 .kind(CompletionItemKind::Macro)
156 .set_documentation(macro_.docs(ctx.db)) 182 .set_documentation(docs)
157 .detail(detail) 183 .detail(detail)
158 .insert_snippet(macro_declaration + macro_braces_to_insert); 184 .insert_snippet(macro_declaration + macro_braces_to_insert);
159 185