ide_completion/completions/
use_.rs

1//! Completion for use trees
2
3use hir::ScopeDef;
4use ide_db::{FxHashSet, SymbolKind};
5use syntax::{AstNode, ast, format_smolstr};
6
7use crate::{
8    CompletionItem, CompletionItemKind, CompletionRelevance, Completions,
9    context::{CompletionContext, PathCompletionCtx, Qualified},
10    item::Builder,
11};
12
13pub(crate) fn complete_use_path(
14    acc: &mut Completions,
15    ctx: &CompletionContext<'_>,
16    path_ctx @ PathCompletionCtx { qualified, use_tree_parent, .. }: &PathCompletionCtx<'_>,
17    name_ref: &Option<ast::NameRef>,
18) {
19    match qualified {
20        Qualified::With { path, resolution: Some(resolution), super_chain_len } => {
21            acc.add_super_keyword(ctx, *super_chain_len);
22
23            // only show `self` in a new use-tree when the qualifier doesn't end in self
24            let not_preceded_by_self = *use_tree_parent
25                && !matches!(
26                    path.segment().and_then(|it| it.kind()),
27                    Some(ast::PathSegmentKind::SelfKw)
28                );
29            if not_preceded_by_self {
30                acc.add_keyword(ctx, "self");
31            }
32
33            let mut already_imported_names = FxHashSet::default();
34            if let Some(list) = ctx.token.parent_ancestors().find_map(ast::UseTreeList::cast) {
35                let use_tree = list.parent_use_tree();
36                if use_tree.path().as_ref() == Some(path) {
37                    for tree in list.use_trees().filter(|tree| tree.is_simple_path()) {
38                        if let Some(name) = tree.path().and_then(|path| path.as_single_name_ref()) {
39                            already_imported_names.insert(name.to_string());
40                        }
41                    }
42                }
43            }
44
45            match resolution {
46                hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
47                    let module_scope = module.scope(ctx.db, Some(ctx.module));
48                    let unknown_is_current = |name: &hir::Name| {
49                        matches!(
50                            name_ref,
51                            Some(name_ref) if name_ref.syntax().text() == name.as_str()
52                        )
53                    };
54                    for (name, def) in module_scope {
55                        if let (Some(attrs), Some(defining_crate)) =
56                            (def.attrs(ctx.db), def.krate(ctx.db))
57                            && (!ctx.check_stability(Some(&attrs))
58                                || ctx.is_doc_hidden(&attrs, defining_crate))
59                        {
60                            continue;
61                        }
62                        let is_name_already_imported =
63                            already_imported_names.contains(name.as_str());
64
65                        let add_resolution = match def {
66                            ScopeDef::Unknown if unknown_is_current(&name) => {
67                                // for `use self::foo$0`, don't suggest `foo` as a completion
68                                cov_mark::hit!(dont_complete_current_use);
69                                continue;
70                            }
71                            ScopeDef::ModuleDef(_) | ScopeDef::Unknown => true,
72                            _ => false,
73                        };
74
75                        if add_resolution {
76                            let mut builder = Builder::from_resolution(ctx, path_ctx, name, def);
77                            builder.with_relevance(|r| CompletionRelevance {
78                                is_name_already_imported,
79                                ..r
80                            });
81                            acc.add(builder.build(ctx.db));
82                        }
83                    }
84                }
85                hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
86                    cov_mark::hit!(enum_plain_qualified_use_tree);
87                    acc.add_enum_variants(ctx, path_ctx, *e);
88                }
89                _ => {}
90            }
91        }
92        // fresh use tree with leading colon2, only show crate roots
93        Qualified::Absolute => {
94            cov_mark::hit!(use_tree_crate_roots_only);
95            acc.add_crate_roots(ctx, path_ctx);
96        }
97        // only show modules and non-std enum in a fresh UseTree
98        Qualified::No => {
99            cov_mark::hit!(unqualified_path_selected_only);
100            ctx.process_all_names(&mut |name, res, doc_aliases| {
101                match res {
102                    ScopeDef::ModuleDef(hir::ModuleDef::Module(module)) => {
103                        acc.add_module(ctx, path_ctx, module, name, doc_aliases);
104                    }
105                    ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
106                        // exclude prelude enum
107                        let is_builtin =
108                            res.krate(ctx.db).is_some_and(|krate| krate.is_builtin(ctx.db));
109
110                        if !is_builtin {
111                            let item = CompletionItem::new(
112                                CompletionItemKind::SymbolKind(SymbolKind::Enum),
113                                ctx.source_range(),
114                                format_smolstr!(
115                                    "{}::",
116                                    e.name(ctx.db).display(ctx.db, ctx.edition)
117                                ),
118                                ctx.edition,
119                            );
120                            acc.add(item.build(ctx.db));
121                        }
122                    }
123                    _ => {}
124                };
125            });
126            acc.add_nameref_keywords_with_colon(ctx);
127        }
128        Qualified::TypeAnchor { .. } | Qualified::With { resolution: None, .. } => {}
129    }
130}