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,106 @@ const ATTRIBUTES: &[AttrCompletion] = &[
126
129
} ,
127
130
] ;
128
131
132
+ fn complete_derive ( acc : & mut Completions , ctx : & CompletionContext , derive_input : ast:: TokenTree ) {
133
+ if let Ok ( existing_derives) = parse_derive_input ( derive_input) {
134
+ for derive_completion in DEFAULT_DERIVE_COMPLETIONS
135
+ . into_iter ( )
136
+ . filter ( |completion| !existing_derives. contains ( completion. label ) )
137
+ {
138
+ let mut label = derive_completion. label . to_owned ( ) ;
139
+ for dependency in derive_completion
140
+ . dependencies
141
+ . into_iter ( )
142
+ . filter ( |& & dependency| !existing_derives. contains ( dependency) )
143
+ {
144
+ label. push_str ( ", " ) ;
145
+ label. push_str ( dependency) ;
146
+ }
147
+ acc. add (
148
+ CompletionItem :: new ( CompletionKind :: Attribute , ctx. source_range ( ) , label)
149
+ . kind ( CompletionItemKind :: Attribute ) ,
150
+ ) ;
151
+ }
152
+
153
+ for custom_derive_name in get_derive_names_in_scope ( ctx) . difference ( & existing_derives) {
154
+ acc. add (
155
+ CompletionItem :: new (
156
+ CompletionKind :: Attribute ,
157
+ ctx. source_range ( ) ,
158
+ custom_derive_name,
159
+ )
160
+ . kind ( CompletionItemKind :: Attribute ) ,
161
+ ) ;
162
+ }
163
+ }
164
+ }
165
+
166
+ fn parse_derive_input ( derive_input : ast:: TokenTree ) -> Result < FxHashSet < String > , ( ) > {
167
+ match ( derive_input. left_delimiter_token ( ) , derive_input. right_delimiter_token ( ) ) {
168
+ ( Some ( left_paren) , Some ( right_paren) )
169
+ if left_paren. kind ( ) == SyntaxKind :: L_PAREN
170
+ && right_paren. kind ( ) == SyntaxKind :: R_PAREN =>
171
+ {
172
+ let mut input_derives = FxHashSet :: default ( ) ;
173
+ let mut current_derive = String :: new ( ) ;
174
+ for token in derive_input
175
+ . syntax ( )
176
+ . children_with_tokens ( )
177
+ . filter_map ( |token| token. into_token ( ) )
178
+ . skip_while ( |token| token != & left_paren)
179
+ . skip ( 1 )
180
+ . take_while ( |token| token != & right_paren)
181
+ {
182
+ if SyntaxKind :: COMMA == token. kind ( ) {
183
+ if !current_derive. is_empty ( ) {
184
+ input_derives. insert ( current_derive) ;
185
+ current_derive = String :: new ( ) ;
186
+ }
187
+ } else {
188
+ current_derive. push_str ( token. to_string ( ) . trim ( ) ) ;
189
+ }
190
+ }
191
+
192
+ if !current_derive. is_empty ( ) {
193
+ input_derives. insert ( current_derive) ;
194
+ }
195
+ Ok ( input_derives)
196
+ }
197
+ _ => Err ( ( ) ) ,
198
+ }
199
+ }
200
+
201
+ fn get_derive_names_in_scope ( ctx : & CompletionContext ) -> FxHashSet < String > {
202
+ let mut result = FxHashSet :: default ( ) ;
203
+ ctx. scope ( ) . process_all_names ( & mut |name, scope_def| {
204
+ if let hir:: ScopeDef :: MacroDef ( mac) = scope_def {
205
+ if mac. is_derive_macro ( ) {
206
+ result. insert ( name. to_string ( ) ) ;
207
+ }
208
+ }
209
+ } ) ;
210
+ result
211
+ }
212
+
213
+ struct DeriveCompletion {
214
+ label : & ' static str ,
215
+ dependencies : & ' static [ & ' static str ] ,
216
+ }
217
+
218
+ /// Standard Rust derives and the information about their dependencies
219
+ /// (the dependencies are needed so that the main derive don't break the compilation when added)
220
+ const DEFAULT_DERIVE_COMPLETIONS : & [ DeriveCompletion ] = & [
221
+ DeriveCompletion { label : "Clone" , dependencies : & [ ] } ,
222
+ DeriveCompletion { label : "Copy" , dependencies : & [ "Clone" ] } ,
223
+ DeriveCompletion { label : "Debug" , dependencies : & [ ] } ,
224
+ DeriveCompletion { label : "Default" , dependencies : & [ ] } ,
225
+ DeriveCompletion { label : "Hash" , dependencies : & [ ] } ,
226
+ DeriveCompletion { label : "PartialEq" , dependencies : & [ ] } ,
227
+ DeriveCompletion { label : "Eq" , dependencies : & [ "PartialEq" ] } ,
228
+ DeriveCompletion { label : "PartialOrd" , dependencies : & [ "PartialEq" ] } ,
229
+ DeriveCompletion { label : "Ord" , dependencies : & [ "PartialOrd" , "Eq" , "PartialEq" ] } ,
230
+ ] ;
231
+
129
232
#[ cfg( test) ]
130
233
mod tests {
131
234
use crate :: completion:: { test_utils:: do_completion, CompletionItem , CompletionKind } ;
@@ -135,6 +238,170 @@ mod tests {
135
238
do_completion ( code, CompletionKind :: Attribute )
136
239
}
137
240
241
+ #[ test]
242
+ fn empty_derive_completion ( ) {
243
+ assert_debug_snapshot ! (
244
+ do_attr_completion(
245
+ r"
246
+ #[derive(<|>)]
247
+ struct Test {}
248
+ " ,
249
+ ) ,
250
+ @r###"
251
+ [
252
+ CompletionItem {
253
+ label: "Clone",
254
+ source_range: 30..30,
255
+ delete: 30..30,
256
+ insert: "Clone",
257
+ kind: Attribute,
258
+ },
259
+ CompletionItem {
260
+ label: "Copy, Clone",
261
+ source_range: 30..30,
262
+ delete: 30..30,
263
+ insert: "Copy, Clone",
264
+ kind: Attribute,
265
+ },
266
+ CompletionItem {
267
+ label: "Debug",
268
+ source_range: 30..30,
269
+ delete: 30..30,
270
+ insert: "Debug",
271
+ kind: Attribute,
272
+ },
273
+ CompletionItem {
274
+ label: "Default",
275
+ source_range: 30..30,
276
+ delete: 30..30,
277
+ insert: "Default",
278
+ kind: Attribute,
279
+ },
280
+ CompletionItem {
281
+ label: "Eq, PartialEq",
282
+ source_range: 30..30,
283
+ delete: 30..30,
284
+ insert: "Eq, PartialEq",
285
+ kind: Attribute,
286
+ },
287
+ CompletionItem {
288
+ label: "Hash",
289
+ source_range: 30..30,
290
+ delete: 30..30,
291
+ insert: "Hash",
292
+ kind: Attribute,
293
+ },
294
+ CompletionItem {
295
+ label: "Ord, PartialOrd, Eq, PartialEq",
296
+ source_range: 30..30,
297
+ delete: 30..30,
298
+ insert: "Ord, PartialOrd, Eq, PartialEq",
299
+ kind: Attribute,
300
+ },
301
+ CompletionItem {
302
+ label: "PartialEq",
303
+ source_range: 30..30,
304
+ delete: 30..30,
305
+ insert: "PartialEq",
306
+ kind: Attribute,
307
+ },
308
+ CompletionItem {
309
+ label: "PartialOrd, PartialEq",
310
+ source_range: 30..30,
311
+ delete: 30..30,
312
+ insert: "PartialOrd, PartialEq",
313
+ kind: Attribute,
314
+ },
315
+ ]
316
+ "###
317
+ ) ;
318
+ }
319
+
320
+ #[ test]
321
+ fn no_completion_for_incorrect_derive ( ) {
322
+ assert_debug_snapshot ! (
323
+ do_attr_completion(
324
+ r"
325
+ #[derive{<|>)]
326
+ struct Test {}
327
+ " ,
328
+ ) ,
329
+ @"[]"
330
+ ) ;
331
+ }
332
+
333
+ #[ test]
334
+ fn derive_with_input_completion ( ) {
335
+ assert_debug_snapshot ! (
336
+ do_attr_completion(
337
+ r"
338
+ #[derive(serde::Serialize, PartialEq, <|>)]
339
+ struct Test {}
340
+ " ,
341
+ ) ,
342
+ @r###"
343
+ [
344
+ CompletionItem {
345
+ label: "Clone",
346
+ source_range: 59..59,
347
+ delete: 59..59,
348
+ insert: "Clone",
349
+ kind: Attribute,
350
+ },
351
+ CompletionItem {
352
+ label: "Copy, Clone",
353
+ source_range: 59..59,
354
+ delete: 59..59,
355
+ insert: "Copy, Clone",
356
+ kind: Attribute,
357
+ },
358
+ CompletionItem {
359
+ label: "Debug",
360
+ source_range: 59..59,
361
+ delete: 59..59,
362
+ insert: "Debug",
363
+ kind: Attribute,
364
+ },
365
+ CompletionItem {
366
+ label: "Default",
367
+ source_range: 59..59,
368
+ delete: 59..59,
369
+ insert: "Default",
370
+ kind: Attribute,
371
+ },
372
+ CompletionItem {
373
+ label: "Eq",
374
+ source_range: 59..59,
375
+ delete: 59..59,
376
+ insert: "Eq",
377
+ kind: Attribute,
378
+ },
379
+ CompletionItem {
380
+ label: "Hash",
381
+ source_range: 59..59,
382
+ delete: 59..59,
383
+ insert: "Hash",
384
+ kind: Attribute,
385
+ },
386
+ CompletionItem {
387
+ label: "Ord, PartialOrd, Eq",
388
+ source_range: 59..59,
389
+ delete: 59..59,
390
+ insert: "Ord, PartialOrd, Eq",
391
+ kind: Attribute,
392
+ },
393
+ CompletionItem {
394
+ label: "PartialOrd",
395
+ source_range: 59..59,
396
+ delete: 59..59,
397
+ insert: "PartialOrd",
398
+ kind: Attribute,
399
+ },
400
+ ]
401
+ "###
402
+ ) ;
403
+ }
404
+
138
405
#[ test]
139
406
fn test_attribute_completion ( ) {
140
407
assert_debug_snapshot ! (
0 commit comments