]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/completions/attribute/derive.rs
Merge #10562
[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, SmolStr};
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_paths(derive_input) {
19         for (derive, docs) in get_derive_names_in_scope(ctx) {
20             let label;
21             let (label, lookup) = if let Some(derive_completion) = DEFAULT_DERIVE_COMPLETIONS
22                 .iter()
23                 .find(|derive_completion| derive_completion.label == derive)
24             {
25                 let mut components = vec![derive_completion.label];
26                 components.extend(derive_completion.dependencies.iter().filter(|&&dependency| {
27                     !existing_derives
28                         .iter()
29                         .filter_map(|it| it.as_single_name_ref())
30                         .any(|it| it.text() == dependency)
31                 }));
32                 let lookup = components.join(", ");
33                 label = components.iter().rev().join(", ");
34                 (&*label, Some(lookup))
35             } else if existing_derives
36                 .iter()
37                 .filter_map(|it| it.as_single_name_ref())
38                 .any(|it| it.text().as_str() == derive)
39             {
40                 continue;
41             } else {
42                 (&*derive, None)
43             };
44             let mut item =
45                 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
46             item.kind(CompletionItemKind::Attribute);
47             if let Some(docs) = docs {
48                 item.documentation(docs);
49             }
50             if let Some(lookup) = lookup {
51                 item.lookup_by(lookup);
52             }
53             item.add_to(acc);
54         }
55     }
56 }
57
58 fn get_derive_names_in_scope(
59     ctx: &CompletionContext,
60 ) -> FxHashMap<SmolStr, Option<hir::Documentation>> {
61     let mut result = FxHashMap::default();
62     ctx.process_all_names(&mut |name, scope_def| {
63         if let hir::ScopeDef::MacroDef(mac) = scope_def {
64             if mac.kind() == hir::MacroKind::Derive {
65                 result.insert(name.to_smol_str(), mac.docs(ctx.db));
66             }
67         }
68     });
69     result
70 }
71
72 struct DeriveDependencies {
73     label: &'static str,
74     dependencies: &'static [&'static str],
75 }
76
77 /// Standard Rust derives that have dependencies
78 /// (the dependencies are needed so that the main derive don't break the compilation when added)
79 const DEFAULT_DERIVE_COMPLETIONS: &[DeriveDependencies] = &[
80     DeriveDependencies { label: "Copy", dependencies: &["Clone"] },
81     DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] },
82     DeriveDependencies { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
83     DeriveDependencies { label: "PartialOrd", dependencies: &["PartialEq"] },
84 ];