Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/completions/use_.rs')
-rw-r--r--crates/ide-completion/src/completions/use_.rs112
1 files changed, 112 insertions, 0 deletions
diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs
new file mode 100644
index 0000000000..25eb3fb908
--- /dev/null
+++ b/crates/ide-completion/src/completions/use_.rs
@@ -0,0 +1,112 @@
+//! Completion for use trees
+
+use hir::ScopeDef;
+use ide_db::FxHashSet;
+use syntax::{ast, AstNode};
+
+use crate::{
+ context::{CompletionContext, PathCompletionCtx, PathKind, PathQualifierCtx},
+ item::Builder,
+ CompletionRelevance, Completions,
+};
+
+pub(crate) fn complete_use_tree(acc: &mut Completions, ctx: &CompletionContext) {
+ let (&is_absolute_path, qualifier) = match &ctx.path_context {
+ Some(PathCompletionCtx {
+ kind: Some(PathKind::Use), is_absolute_path, qualifier, ..
+ }) => (is_absolute_path, qualifier),
+ _ => return,
+ };
+
+ match qualifier {
+ Some(PathQualifierCtx { path, resolution, is_super_chain, use_tree_parent }) => {
+ if *is_super_chain {
+ acc.add_keyword(ctx, "super::");
+ }
+ // 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 resolution = match resolution {
+ Some(it) => it,
+ None => return,
+ };
+
+ let mut already_imported_names = FxHashSet::default();
+ if let Some(list) = ctx.token.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!(
+ ctx.name_ref(),
+ Some(name_ref) if name_ref.syntax().text() == name.to_smol_str().as_str()
+ )
+ };
+ for (name, def) in module_scope {
+ let is_name_already_imported = name
+ .as_text()
+ .map_or(false, |text| already_imported_names.contains(text.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, name, def);
+ builder.set_relevance(CompletionRelevance {
+ is_name_already_imported,
+ ..Default::default()
+ });
+ acc.add(builder.build());
+ }
+ }
+ }
+ hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
+ cov_mark::hit!(enum_plain_qualified_use_tree);
+ e.variants(ctx.db)
+ .into_iter()
+ .for_each(|variant| acc.add_enum_variant(ctx, variant, None));
+ }
+ _ => {}
+ }
+ }
+ // fresh use tree with leading colon2, only show crate roots
+ None if is_absolute_path => {
+ cov_mark::hit!(use_tree_crate_roots_only);
+ acc.add_crate_roots(ctx);
+ }
+ // only show modules in a fresh UseTree
+ None => {
+ cov_mark::hit!(unqualified_path_only_modules_in_import);
+ ctx.process_all_names(&mut |name, res| {
+ if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
+ acc.add_resolution(ctx, name, res);
+ }
+ });
+ acc.add_nameref_keywords_with_colon(ctx);
+ }
+ }
+}