]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/completions/attribute/derive.rs
Merge #9144
[rust.git] / crates / ide_completion / src / completions / attribute / derive.rs
1 //! Completion for derives
2 use hir::HasAttrs;
3 use itertools::Itertools;
4 use rustc_hash::FxHashMap;
5 use syntax::ast;
6
7 use crate::{
8     context::CompletionContext,
9     item::{CompletionItem, CompletionItemKind, CompletionKind},
10     Completions,
11 };
12
13 pub(super) fn complete_derive(
14     acc: &mut Completions,
15     ctx: &CompletionContext,
16     derive_input: ast::TokenTree,
17 ) {
18     if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) {
19         for (derive, docs) in get_derive_names_in_scope(ctx) {
20             let (label, lookup) = if let Some(derive_completion) = DEFAULT_DERIVE_COMPLETIONS
21                 .iter()
22                 .find(|derive_completion| derive_completion.label == derive)
23             {
24                 let mut components = vec![derive_completion.label];
25                 components.extend(
26                     derive_completion
27                         .dependencies
28                         .iter()
29                         .filter(|&&dependency| !existing_derives.contains(dependency)),
30                 );
31                 let lookup = components.join(", ");
32                 let label = components.iter().rev().join(", ");
33                 (label, Some(lookup))
34             } else {
35                 (derive, None)
36             };
37             let mut item =
38                 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
39             item.kind(CompletionItemKind::Attribute);
40             if let Some(docs) = docs {
41                 item.documentation(docs);
42             }
43             if let Some(lookup) = lookup {
44                 item.lookup_by(lookup);
45             }
46             item.add_to(acc);
47         }
48     }
49 }
50
51 fn get_derive_names_in_scope(
52     ctx: &CompletionContext,
53 ) -> FxHashMap<String, Option<hir::Documentation>> {
54     let mut result = FxHashMap::default();
55     ctx.scope.process_all_names(&mut |name, scope_def| {
56         if let hir::ScopeDef::MacroDef(mac) = scope_def {
57             if mac.kind() == hir::MacroKind::Derive {
58                 result.insert(name.to_string(), mac.docs(ctx.db));
59             }
60         }
61     });
62     result
63 }
64
65 struct DeriveDependencies {
66     label: &'static str,
67     dependencies: &'static [&'static str],
68 }
69
70 /// Standard Rust derives that have dependencies
71 /// (the dependencies are needed so that the main derive don't break the compilation when added)
72 const DEFAULT_DERIVE_COMPLETIONS: &[DeriveDependencies] = &[
73     DeriveDependencies { label: "Copy", dependencies: &["Clone"] },
74     DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] },
75     DeriveDependencies { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
76     DeriveDependencies { label: "PartialOrd", dependencies: &["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 no_completion_for_incorrect_derive() {
92         check(r#"#[derive{$0)] struct Test;"#, expect![[]])
93     }
94
95     #[test]
96     #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
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     #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
116     fn derive_with_input() {
117         check(
118             r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#,
119             expect![[r#"
120                 at Clone
121                 at Clone, Copy
122                 at Debug
123                 at Default
124                 at Hash
125                 at Eq
126                 at PartialOrd
127                 at Eq, PartialOrd, Ord
128             "#]],
129         )
130     }
131
132     #[test]
133     #[ignore] // FIXME: Fixtures cant test proc-macros/derives yet as we cant specify them in fixtures
134     fn derive_with_input2() {
135         check(
136             r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#,
137             expect![[r#"
138                 at Clone
139                 at Clone, Copy
140                 at Debug
141                 at Default
142                 at Hash
143                 at Eq
144                 at PartialOrd
145                 at Eq, PartialOrd, Ord
146             "#]],
147         )
148     }
149 }