5
5
6
6
use super :: completion_context:: CompletionContext ;
7
7
use super :: completion_item:: { CompletionItem , CompletionItemKind , CompletionKind , Completions } ;
8
+ use ast:: AttrInput ;
8
9
use ra_syntax:: {
9
- ast:: { Attr , AttrKind } ,
10
- AstNode ,
10
+ ast:: { self , AttrKind } ,
11
+ AstNode , SyntaxKind ,
11
12
} ;
13
+ use rustc_hash:: FxHashSet ;
12
14
13
- pub ( super ) fn complete_attribute ( acc : & mut Completions , ctx : & CompletionContext ) {
14
- if !ctx. is_attribute {
15
- return ;
16
- }
15
+ pub ( super ) fn complete_attribute ( acc : & mut Completions , ctx : & CompletionContext ) -> Option < ( ) > {
16
+ let attribute = ctx. attribute_under_caret . as_ref ( ) ?;
17
17
18
- let is_inner = ctx
19
- . original_token
20
- . ancestors ( )
21
- . find_map ( Attr :: cast)
22
- . map ( |attr| attr. kind ( ) == AttrKind :: Inner )
23
- . unwrap_or ( false ) ;
18
+ match ( attribute. path ( ) , attribute. input ( ) ) {
19
+ ( Some ( path) , Some ( AttrInput :: TokenTree ( token_tree) ) ) if path. to_string ( ) == "derive" => {
20
+ complete_derive ( acc, ctx, token_tree)
21
+ }
22
+ _ => complete_attribute_start ( acc, ctx, attribute) ,
23
+ }
24
+ Some ( ( ) )
25
+ }
24
26
27
+ fn complete_attribute_start ( acc : & mut Completions , ctx : & CompletionContext , attribute : & ast:: Attr ) {
25
28
for attr_completion in ATTRIBUTES {
26
29
let mut item = CompletionItem :: new (
27
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
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
194
#[ cfg( test) ]
130
195
mod tests {
131
196
use crate :: completion:: { test_utils:: do_completion, CompletionItem , CompletionKind } ;
@@ -135,6 +200,170 @@ mod tests {
135
200
do_completion ( code, CompletionKind :: Attribute )
136
201
}
137
202
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
+
138
367
#[ test]
139
368
fn test_attribute_completion ( ) {
140
369
assert_debug_snapshot ! (
0 commit comments