aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src/completions/attribute/derive.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src/completions/attribute/derive.rs')
-rw-r--r--crates/ide_completion/src/completions/attribute/derive.rs142
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
2use itertools::Itertools;
3use rustc_hash::FxHashSet;
4use syntax::ast;
5
6use crate::{
7 context::CompletionContext,
8 item::{CompletionItem, CompletionItemKind, CompletionKind},
9 Completions,
10};
11
12pub(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}
48fn 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
61struct 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)
68const 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)]
81mod 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)]
96struct 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)]
117struct 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)]
128struct 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}