Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide_completion/src/completions/pattern.rs')
| -rw-r--r-- | crates/ide_completion/src/completions/pattern.rs | 145 |
1 files changed, 139 insertions, 6 deletions
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index a140ca4239..438230c58f 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs @@ -1,20 +1,52 @@ //! Completes constants and paths in unqualified patterns. -use hir::db::DefDatabase; +use hir::{db::DefDatabase, AssocItem, ScopeDef}; +use rustc_hash::FxHashSet; +use syntax::ast::Pat; use crate::{ - context::{PatternContext, PatternRefutability}, + context::{PathCompletionCtx, PathQualifierCtx, PatternRefutability}, CompletionContext, Completions, }; /// Completes constants and paths in unqualified patterns. pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { - let refutable = match ctx.pattern_ctx { - Some(PatternContext { refutability, .. }) if ctx.path_context.is_none() => { - refutability == PatternRefutability::Refutable - } + let patctx = match &ctx.pattern_ctx { + Some(ctx) => ctx, _ => return, }; + let refutable = patctx.refutability == PatternRefutability::Refutable; + + if let Some(path_ctx) = &ctx.path_context { + pattern_path_completion(acc, ctx, path_ctx); + return; + } + + match patctx.parent_pat.as_ref() { + Some(Pat::RangePat(_) | Pat::BoxPat(_)) => (), + Some(Pat::RefPat(r)) => { + if r.mut_token().is_none() { + acc.add_keyword(ctx, "mut"); + } + } + _ => { + let tok = ctx.token.text_range().start(); + match (patctx.ref_token.as_ref(), patctx.mut_token.as_ref()) { + (None, None) => { + acc.add_keyword(ctx, "ref"); + acc.add_keyword(ctx, "mut"); + } + (None, Some(m)) if tok < m.text_range().start() => { + acc.add_keyword(ctx, "ref"); + } + (Some(r), None) if tok > r.text_range().end() => { + acc.add_keyword(ctx, "mut"); + } + _ => (), + } + } + } + let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1; if let Some(hir::Adt::Enum(e)) = @@ -63,3 +95,104 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { } }); } + +fn pattern_path_completion( + acc: &mut Completions, + ctx: &CompletionContext, + PathCompletionCtx { qualifier, is_absolute_path, .. }: &PathCompletionCtx, +) { + match qualifier { + Some(PathQualifierCtx { resolution, is_super_chain, .. }) => { + if *is_super_chain { + acc.add_keyword(ctx, "super::"); + } + + let resolution = match resolution { + Some(it) => it, + None => return, + }; + + match resolution { + hir::PathResolution::Def(hir::ModuleDef::Module(module)) => { + let module_scope = module.scope(ctx.db, ctx.module); + for (name, def) in module_scope { + let add_resolution = match def { + ScopeDef::MacroDef(m) if m.is_fn_like() => true, + ScopeDef::ModuleDef(_) => true, + _ => false, + }; + + if add_resolution { + acc.add_resolution(ctx, name, def); + } + } + } + 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)); + } + res @ (hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_)) => { + if let Some(krate) = ctx.krate { + let ty = match res { + hir::PathResolution::TypeParam(param) => param.ty(ctx.db), + hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), + _ => return, + }; + + // Note associated consts cannot be referenced in patterns + if let Some(hir::Adt::Enum(e)) = ty.as_adt() { + e.variants(ctx.db) + .into_iter() + .for_each(|variant| acc.add_enum_variant(ctx, variant, None)); + } + + let traits_in_scope = ctx.scope.visible_traits(); + let mut seen = FxHashSet::default(); + ty.iterate_path_candidates( + ctx.db, + krate, + &traits_in_scope, + ctx.module, + None, + |_ty, item| { + // We might iterate candidates of a trait multiple times here, so deduplicate + // them. + if let AssocItem::TypeAlias(ta) = item { + if seen.insert(item) { + acc.add_type_alias(ctx, ta); + } + } + None::<()> + }, + ); + } + } + _ => {} + } + } + // qualifier can only be none here if we are in a TuplePat or RecordPat in which case special characters have to follow the path + // so executing the rest of this completion doesn't make sense + // fresh use tree with leading colon2, only show crate roots + None if *is_absolute_path => { + cov_mark::hit!(use_tree_crate_roots_only); + ctx.process_all_names(&mut |name, res| match res { + ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => { + acc.add_resolution(ctx, name, res); + } + _ => (), + }); + } + // 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); + } + }); + ["self::", "super::", "crate::"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw)); + } + } +} |