diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-10-21 12:42:05 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2019-10-21 12:42:05 +0100 |
commit | e4810a302b4badb024d22da50cfa4aae64184493 (patch) | |
tree | 8734d9ff3bc11208169b49f8f3845035b8e4556a /crates/ra_ide_api/src/completion | |
parent | f9d060bf442edeba0b3f3f28dbe1e6cb19e6303e (diff) | |
parent | 24d50ebcd1fabedc8d0c468f8785b0aeb1fb176f (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]>
Diffstat (limited to 'crates/ra_ide_api/src/completion')
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_macro_in_item_position.rs | 62 | ||||
-rw-r--r-- | 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 { | |||
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(¯o_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 | ¯o_declaration, | 179 | ¯o_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 | ||