ide_completion/completions/attribute/
derive.rs

1//! Completion for derives
2use 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        // only show modules in a fresh UseTree
45        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                    // show derive dependencies for `core`/`std` derives
61                    (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
109/// Standard Rust derives that have dependencies
110/// (the dependencies are needed so that the main derive don't break the compilation when added)
111const 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];