From 5454559c0a45d208db963df105f22f5e17f0340a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 9 Feb 2021 21:32:05 +0100 Subject: [PATCH] Show qualified variant pattern completions --- crates/completion/src/completions.rs | 60 ++++++++++++++++++- crates/completion/src/completions/pattern.rs | 28 +++++++++ .../src/completions/unqualified_path.rs | 46 ++------------ crates/completion/src/render/pattern.rs | 6 +- 4 files changed, 95 insertions(+), 45 deletions(-) diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs index c3ce6e51d5..3b582ed07a 100644 --- a/crates/completion/src/completions.rs +++ b/crates/completion/src/completions.rs @@ -15,7 +15,9 @@ pub(crate) mod trait_impl; pub(crate) mod mod_; pub(crate) mod flyimport; -use hir::{ModPath, ScopeDef, Type}; +use std::iter; + +use hir::{known, ModPath, ScopeDef, Type}; use crate::{ item::Builder, @@ -118,7 +120,18 @@ impl Completions { variant: hir::Variant, local_name: Option, ) { - if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, local_name) { + if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, local_name, None) { + self.add(item); + } + } + + pub(crate) fn add_qualified_variant_pat( + &mut self, + ctx: &CompletionContext, + variant: hir::Variant, + path: ModPath, + ) { + if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)) { self.add(item); } } @@ -166,3 +179,46 @@ impl Completions { self.add(item); } } + +fn complete_enum_variants( + acc: &mut Completions, + ctx: &CompletionContext, + ty: &hir::Type, + cb: impl Fn(&mut Completions, &CompletionContext, hir::Variant, hir::ModPath), +) { + if let Some(hir::Adt::Enum(enum_data)) = + iter::successors(Some(ty.clone()), |ty| ty.remove_ref()).last().and_then(|ty| ty.as_adt()) + { + let variants = enum_data.variants(ctx.db); + + let module = if let Some(module) = ctx.scope.module() { + // Compute path from the completion site if available. + module + } else { + // Otherwise fall back to the enum's definition site. + enum_data.module(ctx.db) + }; + + if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { + if impl_.target_ty(ctx.db) == *ty { + for &variant in &variants { + let self_path = hir::ModPath::from_segments( + hir::PathKind::Plain, + iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))), + ); + cb(acc, ctx, variant, self_path); + } + } + } + + for variant in variants { + if let Some(path) = module.find_use_path(ctx.db, hir::ModuleDef::from(variant)) { + // Variants with trivial paths are already added by the existing completion logic, + // so we should avoid adding these twice + if path.segments().len() > 1 { + cb(acc, ctx, variant, path); + } + } + } + } +} diff --git a/crates/completion/src/completions/pattern.rs b/crates/completion/src/completions/pattern.rs index 43a5160cba..9282c38279 100644 --- a/crates/completion/src/completions/pattern.rs +++ b/crates/completion/src/completions/pattern.rs @@ -11,6 +11,12 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { return; } + if let Some(ty) = &ctx.expected_type { + super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| { + acc.add_qualified_variant_pat(ctx, variant, path) + }); + } + // FIXME: ideally, we should look at the type we are matching against and // suggest variants + auto-imports ctx.scope.process_all_names(&mut |name, res| { @@ -286,4 +292,26 @@ impl Foo { "#]], ) } + + #[test] + fn completes_qualified_variant() { + check_snippet( + r#" +enum Foo { + Bar { baz: i32 } +} +impl Foo { + fn foo() { + match {Foo::Bar { baz: 0 }} { + B$0 + } + } +} + "#, + expect![[r#" + bn Self::Bar Self::Bar { baz$1 }$0 + bn Foo::Bar Foo::Bar { baz$1 }$0 + "#]], + ) + } } diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 5112ecc2d9..e9d0ff6658 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs @@ -1,8 +1,6 @@ //! Completion of names from the current scope, e.g. locals and imported items. -use std::iter; - -use hir::{known, Adt, ModuleDef, ScopeDef, Type}; +use hir::ScopeDef; use syntax::AstNode; use test_utils::mark; @@ -21,7 +19,9 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC } if let Some(ty) = &ctx.expected_type { - complete_enum_variants(acc, ctx, ty); + super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| { + acc.add_qualified_enum_variant(ctx, variant, path) + }); } if ctx.is_pat_binding_or_const { @@ -45,44 +45,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC }); } -fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { - if let Some(Adt::Enum(enum_data)) = - iter::successors(Some(ty.clone()), |ty| ty.remove_ref()).last().and_then(|ty| ty.as_adt()) - { - let variants = enum_data.variants(ctx.db); - - let module = if let Some(module) = ctx.scope.module() { - // Compute path from the completion site if available. - module - } else { - // Otherwise fall back to the enum's definition site. - enum_data.module(ctx.db) - }; - - if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { - if impl_.target_ty(ctx.db) == *ty { - for &variant in &variants { - let self_path = hir::ModPath::from_segments( - hir::PathKind::Plain, - iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))), - ); - acc.add_qualified_enum_variant(ctx, variant, self_path.clone()); - } - } - } - - for variant in variants { - if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { - // Variants with trivial paths are already added by the existing completion logic, - // so we should avoid adding these twice - if path.segments().len() > 1 { - acc.add_qualified_enum_variant(ctx, variant, path); - } - } - } - } -} - #[cfg(test)] mod tests { use expect_test::{expect, Expect}; diff --git a/crates/completion/src/render/pattern.rs b/crates/completion/src/render/pattern.rs index 61d8a17e56..465dfe00c5 100644 --- a/crates/completion/src/render/pattern.rs +++ b/crates/completion/src/render/pattern.rs @@ -49,13 +49,17 @@ pub(crate) fn render_variant_pat( ctx: RenderContext<'_>, variant: hir::Variant, local_name: Option, + path: Option, ) -> Option { let _p = profile::span("render_variant_pat"); let fields = variant.fields(ctx.db()); let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?; - let name = local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string(); + let name = match &path { + Some(path) => path.to_string(), + None => local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string(), + }; let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &visible_fields, fields_omitted)?; Some(build_completion(ctx, name, pat, variant))