]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/completions/attribute/derive.rs
Merge #9290
[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     fn empty_derive() {
97         // FIXME: Add build-in derives to fixture.
98         check(r#"#[derive($0)] struct Test;"#, expect![[r#""#]]);
99     }
100
101     #[test]
102     fn derive_with_input() {
103         // FIXME: Add build-in derives to fixture.
104         check(r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#, expect![[r#""#]])
105     }
106
107     #[test]
108     fn derive_with_input2() {
109         // FIXME: Add build-in derives to fixture.
110         check(r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#, expect![[r#""#]])
111     }
112 }