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