ide_completion/completions/attribute/
derive.rs1use hir::ScopeDef;
3use ide_db::{SymbolKind, documentation::HasDocs};
4use itertools::Itertools;
5use syntax::{SmolStr, ToSmolStr};
6
7use crate::{
8 Completions,
9 context::{CompletionContext, ExistingDerives, PathCompletionCtx, Qualified},
10 item::CompletionItem,
11};
12
13pub(crate) fn complete_derive_path(
14 acc: &mut Completions,
15 ctx: &CompletionContext<'_>,
16 path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
17 existing_derives: &ExistingDerives,
18) {
19 let core = ctx.famous_defs().core();
20
21 match qualified {
22 Qualified::With {
23 resolution: Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))),
24 super_chain_len,
25 ..
26 } => {
27 acc.add_super_keyword(ctx, *super_chain_len);
28
29 for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
30 match def {
31 ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac))
32 if !existing_derives.contains(&mac) && mac.is_derive(ctx.db) =>
33 {
34 acc.add_macro(ctx, path_ctx, mac, name)
35 }
36 ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
37 acc.add_module(ctx, path_ctx, m, name, vec![])
38 }
39 _ => (),
40 }
41 }
42 }
43 Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
44 Qualified::No => {
46 ctx.process_all_names(&mut |name, def, doc_aliases| {
47 let mac = match def {
48 ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac))
49 if !existing_derives.contains(&mac) && mac.is_derive(ctx.db) =>
50 {
51 mac
52 }
53 ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
54 return acc.add_module(ctx, path_ctx, m, name, doc_aliases);
55 }
56 _ => return,
57 };
58
59 match (core, mac.module(ctx.db).krate(ctx.db)) {
60 (Some(core), mac_krate) if core == mac_krate => {}
62 _ => return acc.add_macro(ctx, path_ctx, mac, name),
63 };
64
65 let name_ = name.display_no_db(ctx.edition).to_smolstr();
66 let find = DEFAULT_DERIVE_DEPENDENCIES
67 .iter()
68 .find(|derive_completion| derive_completion.label == name_);
69
70 match find {
71 Some(derive_completion) => {
72 let mut components = vec![derive_completion.label];
73 components.extend(derive_completion.dependencies.iter().filter(
74 |&&dependency| {
75 !existing_derives.iter().map(|it| it.name(ctx.db)).any(|it| {
76 it.display_no_db(ctx.edition).to_smolstr() == dependency
77 })
78 },
79 ));
80 let lookup = components.join(", ");
81 let label = Itertools::intersperse(components.into_iter().rev(), ", ");
82
83 let mut item = CompletionItem::new(
84 SymbolKind::Derive,
85 ctx.source_range(),
86 SmolStr::from_iter(label),
87 ctx.edition,
88 );
89 if let Some(docs) = mac.docs(ctx.db) {
90 item.documentation(docs);
91 }
92 item.lookup_by(lookup);
93 item.add_to(acc, ctx.db);
94 }
95 None => acc.add_macro(ctx, path_ctx, mac, name),
96 }
97 });
98 acc.add_nameref_keywords_with_colon(ctx);
99 }
100 Qualified::TypeAnchor { .. } | Qualified::With { .. } => {}
101 }
102}
103
104struct DeriveDependencies {
105 label: &'static str,
106 dependencies: &'static [&'static str],
107}
108
109const DEFAULT_DERIVE_DEPENDENCIES: &[DeriveDependencies] = &[
112 DeriveDependencies { label: "Copy", dependencies: &["Clone"] },
113 DeriveDependencies { label: "Eq", dependencies: &["PartialEq"] },
114 DeriveDependencies { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
115 DeriveDependencies { label: "PartialOrd", dependencies: &["PartialEq"] },
116];