55
66use super :: completion_context:: CompletionContext ;
77use super :: completion_item:: { CompletionItem , CompletionItemKind , CompletionKind , Completions } ;
8+ use ast:: AttrInput ;
89use ra_syntax:: {
9- ast:: { Attr , AttrKind } ,
10- AstNode ,
10+ ast:: { self , AttrKind } ,
11+ AstNode , SyntaxKind ,
1112} ;
13+ use rustc_hash:: FxHashSet ;
1214
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 ( ) ?;
1717
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+ }
2426
27+ fn complete_attribute_start ( acc : & mut Completions , ctx : & CompletionContext , attribute : & ast:: Attr ) {
2528 for attr_completion in ATTRIBUTES {
2629 let mut item = CompletionItem :: new (
2730 CompletionKind :: Attribute ,
@@ -37,7 +40,7 @@ pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
3740 _ => { }
3841 }
3942
40- if is_inner || !attr_completion. should_be_inner {
43+ if attribute . kind ( ) == AttrKind :: Inner || !attr_completion. should_be_inner {
4144 acc. add ( item) ;
4245 }
4346 }
@@ -126,6 +129,106 @@ const ATTRIBUTES: &[AttrCompletion] = &[
126129 } ,
127130] ;
128131
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+
129232#[ cfg( test) ]
130233mod tests {
131234 use crate :: completion:: { test_utils:: do_completion, CompletionItem , CompletionKind } ;
@@ -135,6 +238,170 @@ mod tests {
135238 do_completion ( code, CompletionKind :: Attribute )
136239 }
137240
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+
138405 #[ test]
139406 fn test_attribute_completion ( ) {
140407 assert_debug_snapshot ! (
0 commit comments