diff options
Diffstat (limited to 'crates/ra_ide/src/completion/complete_attribute.rs')
-rw-r--r-- | crates/ra_ide/src/completion/complete_attribute.rs | 255 |
1 files changed, 242 insertions, 13 deletions
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs index 8bf952798..8934b45fe 100644 --- a/crates/ra_ide/src/completion/complete_attribute.rs +++ b/crates/ra_ide/src/completion/complete_attribute.rs | |||
@@ -5,23 +5,26 @@ | |||
5 | 5 | ||
6 | use super::completion_context::CompletionContext; | 6 | use super::completion_context::CompletionContext; |
7 | use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions}; | 7 | use super::completion_item::{CompletionItem, CompletionItemKind, CompletionKind, Completions}; |
8 | use ast::AttrInput; | ||
8 | use ra_syntax::{ | 9 | use ra_syntax::{ |
9 | ast::{Attr, AttrKind}, | 10 | ast::{self, AttrKind}, |
10 | AstNode, | 11 | AstNode, SyntaxKind, |
11 | }; | 12 | }; |
13 | use rustc_hash::FxHashSet; | ||
12 | 14 | ||
13 | pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) { | 15 | pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { |
14 | if !ctx.is_attribute { | 16 | let attribute = ctx.attribute_under_caret.as_ref()?; |
15 | return; | ||
16 | } | ||
17 | 17 | ||
18 | let is_inner = ctx | 18 | match (attribute.path(), attribute.input()) { |
19 | .original_token | 19 | (Some(path), Some(AttrInput::TokenTree(token_tree))) if path.to_string() == "derive" => { |
20 | .ancestors() | 20 | complete_derive(acc, ctx, token_tree) |
21 | .find_map(Attr::cast) | 21 | } |
22 | .map(|attr| attr.kind() == AttrKind::Inner) | 22 | _ => complete_attribute_start(acc, ctx, attribute), |
23 | .unwrap_or(false); | 23 | } |
24 | Some(()) | ||
25 | } | ||
24 | 26 | ||
27 | fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) { | ||
25 | for attr_completion in ATTRIBUTES { | 28 | for attr_completion in ATTRIBUTES { |
26 | let mut item = CompletionItem::new( | 29 | let mut item = CompletionItem::new( |
27 | CompletionKind::Attribute, | 30 | CompletionKind::Attribute, |
@@ -37,7 +40,7 @@ pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) | |||
37 | _ => {} | 40 | _ => {} |
38 | } | 41 | } |
39 | 42 | ||
40 | if is_inner || !attr_completion.should_be_inner { | 43 | if attribute.kind() == AttrKind::Inner || !attr_completion.should_be_inner { |
41 | acc.add(item); | 44 | acc.add(item); |
42 | } | 45 | } |
43 | } | 46 | } |
@@ -126,6 +129,68 @@ const ATTRIBUTES: &[AttrCompletion] = &[ | |||
126 | }, | 129 | }, |
127 | ]; | 130 | ]; |
128 | 131 | ||
132 | fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) { | ||
133 | // TODO kb autodetect derive macros | ||
134 | // https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Find.20all.20possible.20derive.20macro.20values.3F/near/195955580 | ||
135 | |||
136 | if let Ok(existing_derives) = parse_derive_input(derive_input) { | ||
137 | for derive_completion in DERIVE_COMPLETIONS | ||
138 | .into_iter() | ||
139 | .filter(|completion| !existing_derives.contains(completion.label)) | ||
140 | { | ||
141 | let mut label = derive_completion.label.to_owned(); | ||
142 | for dependency in derive_completion | ||
143 | .dependencies | ||
144 | .into_iter() | ||
145 | .filter(|&&dependency| !existing_derives.contains(dependency)) | ||
146 | { | ||
147 | label.push_str(", "); | ||
148 | label.push_str(dependency); | ||
149 | } | ||
150 | let item = CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label) | ||
151 | .kind(CompletionItemKind::Attribute); | ||
152 | acc.add(item); | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | |||
157 | fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> { | ||
158 | match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) { | ||
159 | (Some(left_paren), Some(right_paren)) | ||
160 | if left_paren.kind() == SyntaxKind::L_PAREN | ||
161 | && right_paren.kind() == SyntaxKind::R_PAREN => | ||
162 | { | ||
163 | Ok(derive_input | ||
164 | .syntax() | ||
165 | .children_with_tokens() | ||
166 | .filter_map(|child| child.into_token()) | ||
167 | .skip_while(|child| child != &left_paren) | ||
168 | .take_while(|child| child != &right_paren) | ||
169 | .filter(|child| child.kind() == SyntaxKind::IDENT) | ||
170 | .map(|child| child.to_string()) | ||
171 | .collect()) | ||
172 | } | ||
173 | _ => Err(()), | ||
174 | } | ||
175 | } | ||
176 | |||
177 | struct DeriveCompletion { | ||
178 | label: &'static str, | ||
179 | dependencies: &'static [&'static str], | ||
180 | } | ||
181 | |||
182 | const DERIVE_COMPLETIONS: &[DeriveCompletion] = &[ | ||
183 | DeriveCompletion { label: "Clone", dependencies: &[] }, | ||
184 | DeriveCompletion { label: "Copy", dependencies: &["Clone"] }, | ||
185 | DeriveCompletion { label: "Debug", dependencies: &[] }, | ||
186 | DeriveCompletion { label: "Default", dependencies: &[] }, | ||
187 | DeriveCompletion { label: "Hash", dependencies: &[] }, | ||
188 | DeriveCompletion { label: "PartialEq", dependencies: &[] }, | ||
189 | DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] }, | ||
190 | DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] }, | ||
191 | DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] }, | ||
192 | ]; | ||
193 | |||
129 | #[cfg(test)] | 194 | #[cfg(test)] |
130 | mod tests { | 195 | mod tests { |
131 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; | 196 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; |
@@ -136,6 +201,170 @@ mod tests { | |||
136 | } | 201 | } |
137 | 202 | ||
138 | #[test] | 203 | #[test] |
204 | fn empty_derive_completion() { | ||
205 | assert_debug_snapshot!( | ||
206 | do_attr_completion( | ||
207 | r" | ||
208 | #[derive(<|>)] | ||
209 | struct Test {} | ||
210 | ", | ||
211 | ), | ||
212 | @r###" | ||
213 | [ | ||
214 | CompletionItem { | ||
215 | label: "Clone", | ||
216 | source_range: 30..30, | ||
217 | delete: 30..30, | ||
218 | insert: "Clone", | ||
219 | kind: Attribute, | ||
220 | }, | ||
221 | CompletionItem { | ||
222 | label: "Copy, Clone", | ||
223 | source_range: 30..30, | ||
224 | delete: 30..30, | ||
225 | insert: "Copy, Clone", | ||
226 | kind: Attribute, | ||
227 | }, | ||
228 | CompletionItem { | ||
229 | label: "Debug", | ||
230 | source_range: 30..30, | ||
231 | delete: 30..30, | ||
232 | insert: "Debug", | ||
233 | kind: Attribute, | ||
234 | }, | ||
235 | CompletionItem { | ||
236 | label: "Default", | ||
237 | source_range: 30..30, | ||
238 | delete: 30..30, | ||
239 | insert: "Default", | ||
240 | kind: Attribute, | ||
241 | }, | ||
242 | CompletionItem { | ||
243 | label: "Eq, PartialEq", | ||
244 | source_range: 30..30, | ||
245 | delete: 30..30, | ||
246 | insert: "Eq, PartialEq", | ||
247 | kind: Attribute, | ||
248 | }, | ||
249 | CompletionItem { | ||
250 | label: "Hash", | ||
251 | source_range: 30..30, | ||
252 | delete: 30..30, | ||
253 | insert: "Hash", | ||
254 | kind: Attribute, | ||
255 | }, | ||
256 | CompletionItem { | ||
257 | label: "Ord, PartialOrd, Eq, PartialEq", | ||
258 | source_range: 30..30, | ||
259 | delete: 30..30, | ||
260 | insert: "Ord, PartialOrd, Eq, PartialEq", | ||
261 | kind: Attribute, | ||
262 | }, | ||
263 | CompletionItem { | ||
264 | label: "PartialEq", | ||
265 | source_range: 30..30, | ||
266 | delete: 30..30, | ||
267 | insert: "PartialEq", | ||
268 | kind: Attribute, | ||
269 | }, | ||
270 | CompletionItem { | ||
271 | label: "PartialOrd, PartialEq", | ||
272 | source_range: 30..30, | ||
273 | delete: 30..30, | ||
274 | insert: "PartialOrd, PartialEq", | ||
275 | kind: Attribute, | ||
276 | }, | ||
277 | ] | ||
278 | "### | ||
279 | ); | ||
280 | } | ||
281 | |||
282 | #[test] | ||
283 | fn no_completion_for_incorrect_derive() { | ||
284 | assert_debug_snapshot!( | ||
285 | do_attr_completion( | ||
286 | r" | ||
287 | #[derive{<|>)] | ||
288 | struct Test {} | ||
289 | ", | ||
290 | ), | ||
291 | @"[]" | ||
292 | ); | ||
293 | } | ||
294 | |||
295 | #[test] | ||
296 | fn derive_with_input_completion() { | ||
297 | assert_debug_snapshot!( | ||
298 | do_attr_completion( | ||
299 | r" | ||
300 | #[derive(Whatever, PartialEq, <|>)] | ||
301 | struct Test {} | ||
302 | ", | ||
303 | ), | ||
304 | @r###" | ||
305 | [ | ||
306 | CompletionItem { | ||
307 | label: "Clone", | ||
308 | source_range: 51..51, | ||
309 | delete: 51..51, | ||
310 | insert: "Clone", | ||
311 | kind: Attribute, | ||
312 | }, | ||
313 | CompletionItem { | ||
314 | label: "Copy, Clone", | ||
315 | source_range: 51..51, | ||
316 | delete: 51..51, | ||
317 | insert: "Copy, Clone", | ||
318 | kind: Attribute, | ||
319 | }, | ||
320 | CompletionItem { | ||
321 | label: "Debug", | ||
322 | source_range: 51..51, | ||
323 | delete: 51..51, | ||
324 | insert: "Debug", | ||
325 | kind: Attribute, | ||
326 | }, | ||
327 | CompletionItem { | ||
328 | label: "Default", | ||
329 | source_range: 51..51, | ||
330 | delete: 51..51, | ||
331 | insert: "Default", | ||
332 | kind: Attribute, | ||
333 | }, | ||
334 | CompletionItem { | ||
335 | label: "Eq", | ||
336 | source_range: 51..51, | ||
337 | delete: 51..51, | ||
338 | insert: "Eq", | ||
339 | kind: Attribute, | ||
340 | }, | ||
341 | CompletionItem { | ||
342 | label: "Hash", | ||
343 | source_range: 51..51, | ||
344 | delete: 51..51, | ||
345 | insert: "Hash", | ||
346 | kind: Attribute, | ||
347 | }, | ||
348 | CompletionItem { | ||
349 | label: "Ord, PartialOrd, Eq", | ||
350 | source_range: 51..51, | ||
351 | delete: 51..51, | ||
352 | insert: "Ord, PartialOrd, Eq", | ||
353 | kind: Attribute, | ||
354 | }, | ||
355 | CompletionItem { | ||
356 | label: "PartialOrd", | ||
357 | source_range: 51..51, | ||
358 | delete: 51..51, | ||
359 | insert: "PartialOrd", | ||
360 | kind: Attribute, | ||
361 | }, | ||
362 | ] | ||
363 | "### | ||
364 | ); | ||
365 | } | ||
366 | |||
367 | #[test] | ||
139 | fn test_attribute_completion() { | 368 | fn test_attribute_completion() { |
140 | assert_debug_snapshot!( | 369 | assert_debug_snapshot!( |
141 | do_attr_completion( | 370 | do_attr_completion( |