diff options
Diffstat (limited to 'crates/ide_completion/src/completions/attribute/derive.rs')
-rw-r--r-- | crates/ide_completion/src/completions/attribute/derive.rs | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs new file mode 100644 index 000000000..7b0a778a2 --- /dev/null +++ b/crates/ide_completion/src/completions/attribute/derive.rs | |||
@@ -0,0 +1,142 @@ | |||
1 | //! Completion for derives | ||
2 | use itertools::Itertools; | ||
3 | use rustc_hash::FxHashSet; | ||
4 | use syntax::ast; | ||
5 | |||
6 | use crate::{ | ||
7 | context::CompletionContext, | ||
8 | item::{CompletionItem, CompletionItemKind, CompletionKind}, | ||
9 | Completions, | ||
10 | }; | ||
11 | |||
12 | pub(super) fn complete_derive( | ||
13 | acc: &mut Completions, | ||
14 | ctx: &CompletionContext, | ||
15 | derive_input: ast::TokenTree, | ||
16 | ) { | ||
17 | if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) { | ||
18 | for derive_completion in DEFAULT_DERIVE_COMPLETIONS | ||
19 | .iter() | ||
20 | .filter(|completion| !existing_derives.contains(completion.label)) | ||
21 | { | ||
22 | let mut components = vec![derive_completion.label]; | ||
23 | components.extend( | ||
24 | derive_completion | ||
25 | .dependencies | ||
26 | .iter() | ||
27 | .filter(|&&dependency| !existing_derives.contains(dependency)), | ||
28 | ); | ||
29 | let lookup = components.join(", "); | ||
30 | let label = components.iter().rev().join(", "); | ||
31 | let mut item = | ||
32 | CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label); | ||
33 | item.lookup_by(lookup).kind(CompletionItemKind::Attribute); | ||
34 | item.add_to(acc); | ||
35 | } | ||
36 | |||
37 | for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) { | ||
38 | let mut item = CompletionItem::new( | ||
39 | CompletionKind::Attribute, | ||
40 | ctx.source_range(), | ||
41 | custom_derive_name, | ||
42 | ); | ||
43 | item.kind(CompletionItemKind::Attribute); | ||
44 | item.add_to(acc); | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { | ||
49 | let mut result = FxHashSet::default(); | ||
50 | ctx.scope.process_all_names(&mut |name, scope_def| { | ||
51 | if let hir::ScopeDef::MacroDef(mac) = scope_def { | ||
52 | // FIXME kind() doesn't check whether proc-macro is a derive | ||
53 | if mac.kind() == hir::MacroKind::Derive || mac.kind() == hir::MacroKind::ProcMacro { | ||
54 | result.insert(name.to_string()); | ||
55 | } | ||
56 | } | ||
57 | }); | ||
58 | result | ||
59 | } | ||
60 | |||
61 | struct DeriveCompletion { | ||
62 | label: &'static str, | ||
63 | dependencies: &'static [&'static str], | ||
64 | } | ||
65 | |||
66 | /// Standard Rust derives and the information about their dependencies | ||
67 | /// (the dependencies are needed so that the main derive don't break the compilation when added) | ||
68 | const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[ | ||
69 | DeriveCompletion { label: "Clone", dependencies: &[] }, | ||
70 | DeriveCompletion { label: "Copy", dependencies: &["Clone"] }, | ||
71 | DeriveCompletion { label: "Debug", dependencies: &[] }, | ||
72 | DeriveCompletion { label: "Default", dependencies: &[] }, | ||
73 | DeriveCompletion { label: "Hash", dependencies: &[] }, | ||
74 | DeriveCompletion { label: "PartialEq", dependencies: &[] }, | ||
75 | DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] }, | ||
76 | DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] }, | ||
77 | DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] }, | ||
78 | ]; | ||
79 | |||
80 | #[cfg(test)] | ||
81 | mod tests { | ||
82 | use expect_test::{expect, Expect}; | ||
83 | |||
84 | use crate::{test_utils::completion_list, CompletionKind}; | ||
85 | |||
86 | fn check(ra_fixture: &str, expect: Expect) { | ||
87 | let actual = completion_list(ra_fixture, CompletionKind::Attribute); | ||
88 | expect.assert_eq(&actual); | ||
89 | } | ||
90 | |||
91 | #[test] | ||
92 | fn empty_derive_completion() { | ||
93 | check( | ||
94 | r#" | ||
95 | #[derive($0)] | ||
96 | struct Test {} | ||
97 | "#, | ||
98 | expect![[r#" | ||
99 | at Clone | ||
100 | at Clone, Copy | ||
101 | at Debug | ||
102 | at Default | ||
103 | at Hash | ||
104 | at PartialEq | ||
105 | at PartialEq, Eq | ||
106 | at PartialEq, PartialOrd | ||
107 | at PartialEq, Eq, PartialOrd, Ord | ||
108 | "#]], | ||
109 | ); | ||
110 | } | ||
111 | |||
112 | #[test] | ||
113 | fn no_completion_for_incorrect_derive() { | ||
114 | check( | ||
115 | r#" | ||
116 | #[derive{$0)] | ||
117 | struct Test {} | ||
118 | "#, | ||
119 | expect![[r#""#]], | ||
120 | ) | ||
121 | } | ||
122 | |||
123 | #[test] | ||
124 | fn derive_with_input_completion() { | ||
125 | check( | ||
126 | r#" | ||
127 | #[derive(serde::Serialize, PartialEq, $0)] | ||
128 | struct Test {} | ||
129 | "#, | ||
130 | expect![[r#" | ||
131 | at Clone | ||
132 | at Clone, Copy | ||
133 | at Debug | ||
134 | at Default | ||
135 | at Hash | ||
136 | at Eq | ||
137 | at PartialOrd | ||
138 | at Eq, PartialOrd, Ord | ||
139 | "#]], | ||
140 | ) | ||
141 | } | ||
142 | } | ||