]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/completions/attribute/derive.rs
Merge #9062
[rust.git] / crates / ide_completion / src / completions / attribute / derive.rs
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
49 fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
50     let mut result = FxHashSet::default();
51     ctx.scope.process_all_names(&mut |name, scope_def| {
52         if let hir::ScopeDef::MacroDef(mac) = scope_def {
53             if mac.kind() == hir::MacroKind::Derive {
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 no_completion_for_incorrect_derive() {
93         check(r#"#[derive{$0)] struct Test;"#, expect![[]])
94     }
95
96     #[test]
97     fn empty_derive() {
98         check(
99             r#"#[derive($0)] struct Test;"#,
100             expect![[r#"
101                 at Clone
102                 at Clone, Copy
103                 at Debug
104                 at Default
105                 at Hash
106                 at PartialEq
107                 at PartialEq, Eq
108                 at PartialEq, PartialOrd
109                 at PartialEq, Eq, PartialOrd, Ord
110             "#]],
111         );
112     }
113
114     #[test]
115     fn derive_with_input() {
116         check(
117             r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#,
118             expect![[r#"
119                 at Clone
120                 at Clone, Copy
121                 at Debug
122                 at Default
123                 at Hash
124                 at Eq
125                 at PartialOrd
126                 at Eq, PartialOrd, Ord
127             "#]],
128         )
129     }
130
131     #[test]
132     fn derive_with_input2() {
133         check(
134             r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#,
135             expect![[r#"
136                 at Clone
137                 at Clone, Copy
138                 at Debug
139                 at Default
140                 at Hash
141                 at Eq
142                 at PartialOrd
143                 at Eq, PartialOrd, Ord
144             "#]],
145         )
146     }
147 }