]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/completions/attribute/derive.rs
Merge #9655
[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 if existing_derives.contains(&derive) {
35                 continue;
36             } else {
37                 (derive, None)
38             };
39             let mut item =
40                 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
41             item.kind(CompletionItemKind::Attribute);
42             if let Some(docs) = docs {
43                 item.documentation(docs);
44             }
45             if let Some(lookup) = lookup {
46                 item.lookup_by(lookup);
47             }
48             item.add_to(acc);
49         }
50     }
51 }
52
53 fn get_derive_names_in_scope(
54     ctx: &CompletionContext,
55 ) -> FxHashMap<String, Option<hir::Documentation>> {
56     let mut result = FxHashMap::default();
57     ctx.scope.process_all_names(&mut |name, scope_def| {
58         if let hir::ScopeDef::MacroDef(mac) = scope_def {
59             if mac.kind() == hir::MacroKind::Derive {
60                 result.insert(name.to_string(), mac.docs(ctx.db));
61             }
62         }
63     });
64     result
65 }
66
67 struct DeriveDependencies {
68     label: &'static str,
69     dependencies: &'static [&'static str],
70 }
71
72 /// Standard Rust derives that have dependencies
73 /// (the dependencies are needed so that the main derive don't break the compilation when added)
74 const DEFAULT_DERIVE_COMPLETIONS: &[DeriveDependencies] = &[
75     DeriveDependencies { label: "Copy", dependencies: &["Clone"] },
76     DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] },
77     DeriveDependencies { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
78     DeriveDependencies { label: "PartialOrd", dependencies: &["PartialEq"] },
79 ];