Unnamed repository; edit this file 'description' to name the repository.
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
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
//! 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))
                            && (!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, .. } => {}
    }
}