ide_completion/completions/
use_.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Completion for use trees

use hir::ScopeDef;
use ide_db::{FxHashSet, SymbolKind};
use syntax::{AstNode, ast, format_smolstr};

use crate::{
    CompletionItem, CompletionItemKind, CompletionRelevance, Completions,
    context::{CompletionContext, PathCompletionCtx, Qualified},
    item::Builder,
};

pub(crate) fn complete_use_path(
    acc: &mut Completions,
    ctx: &CompletionContext<'_>,
    path_ctx @ PathCompletionCtx { qualified, use_tree_parent, .. }: &PathCompletionCtx,
    name_ref: &Option<ast::NameRef>,
) {
    match qualified {
        Qualified::With { path, resolution: Some(resolution), super_chain_len } => {
            acc.add_super_keyword(ctx, *super_chain_len);

            // only show `self` in a new use-tree when the qualifier doesn't end in self
            let not_preceded_by_self = *use_tree_parent
                && !matches!(
                    path.segment().and_then(|it| it.kind()),
                    Some(ast::PathSegmentKind::SelfKw)
                );
            if not_preceded_by_self {
                acc.add_keyword(ctx, "self");
            }

            let mut already_imported_names = FxHashSet::default();
            if let Some(list) = ctx.token.parent_ancestors().find_map(ast::UseTreeList::cast) {
                let use_tree = list.parent_use_tree();
                if use_tree.path().as_ref() == Some(path) {
                    for tree in list.use_trees().filter(|tree| tree.is_simple_path()) {
                        if let Some(name) = tree.path().and_then(|path| path.as_single_name_ref()) {
                            already_imported_names.insert(name.to_string());
                        }
                    }
                }
            }

            match resolution {
                hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
                    let module_scope = module.scope(ctx.db, Some(ctx.module));
                    let unknown_is_current = |name: &hir::Name| {
                        matches!(
                            name_ref,
                            Some(name_ref) if name_ref.syntax().text() == name.as_str()
                        )
                    };
                    for (name, def) in module_scope {
                        if let (Some(attrs), Some(defining_crate)) =
                            (def.attrs(ctx.db), def.krate(ctx.db))
                        {
                            if !ctx.check_stability(Some(&attrs))
                                || ctx.is_doc_hidden(&attrs, defining_crate)
                            {
                                continue;
                            }
                        }
                        let is_name_already_imported =
                            already_imported_names.contains(name.as_str());

                        let add_resolution = match def {
                            ScopeDef::Unknown if unknown_is_current(&name) => {
                                // for `use self::foo$0`, don't suggest `foo` as a completion
                                cov_mark::hit!(dont_complete_current_use);
                                continue;
                            }
                            ScopeDef::ModuleDef(_) | ScopeDef::Unknown => true,
                            _ => false,
                        };

                        if add_resolution {
                            let mut builder = Builder::from_resolution(ctx, path_ctx, name, def);
                            builder.with_relevance(|r| CompletionRelevance {
                                is_name_already_imported,
                                ..r
                            });
                            acc.add(builder.build(ctx.db));
                        }
                    }
                }
                hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
                    cov_mark::hit!(enum_plain_qualified_use_tree);
                    acc.add_enum_variants(ctx, path_ctx, *e);
                }
                _ => {}
            }
        }
        // fresh use tree with leading colon2, only show crate roots
        Qualified::Absolute => {
            cov_mark::hit!(use_tree_crate_roots_only);
            acc.add_crate_roots(ctx, path_ctx);
        }
        // only show modules and non-std enum in a fresh UseTree
        Qualified::No => {
            cov_mark::hit!(unqualified_path_selected_only);
            ctx.process_all_names(&mut |name, res, doc_aliases| {
                match res {
                    ScopeDef::ModuleDef(hir::ModuleDef::Module(module)) => {
                        acc.add_module(ctx, path_ctx, module, name, doc_aliases);
                    }
                    ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
                        // exclude prelude enum
                        let is_builtin =
                            res.krate(ctx.db).is_some_and(|krate| krate.is_builtin(ctx.db));

                        if !is_builtin {
                            let item = CompletionItem::new(
                                CompletionItemKind::SymbolKind(SymbolKind::Enum),
                                ctx.source_range(),
                                format_smolstr!(
                                    "{}::",
                                    e.name(ctx.db).display(ctx.db, ctx.edition)
                                ),
                                ctx.edition,
                            );
                            acc.add(item.build(ctx.db));
                        }
                    }
                    _ => {}
                };
            });
            acc.add_nameref_keywords_with_colon(ctx);
        }
        Qualified::TypeAnchor { .. } | Qualified::With { resolution: None, .. } => {}
    }
}