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,68 @@ const ATTRIBUTES: &[AttrCompletion] = &[
126129 } ,
127130] ;
128131
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+
129194#[ cfg( test) ]
130195mod tests {
131196 use crate :: completion:: { test_utils:: do_completion, CompletionItem , CompletionKind } ;
@@ -135,6 +200,170 @@ mod tests {
135200 do_completion ( code, CompletionKind :: Attribute )
136201 }
137202
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+
138367 #[ test]
139368 fn test_attribute_completion ( ) {
140369 assert_debug_snapshot ! (
0 commit comments