From 5454559c0a45d208db963df105f22f5e17f0340a Mon Sep 17 00:00:00 2001
From: Lukas Wirth <lukastw97@gmail.com>
Date: Tue, 9 Feb 2021 21:32:05 +0100
Subject: Show qualified variant pattern completions

---
 crates/completion/src/completions.rs               | 60 +++++++++++++++++++++-
 crates/completion/src/completions/pattern.rs       | 28 ++++++++++
 .../completion/src/completions/unqualified_path.rs | 46 ++---------------
 crates/completion/src/render/pattern.rs            |  6 ++-
 4 files changed, 95 insertions(+), 45 deletions(-)

(limited to 'crates/completion/src')

diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs
index c3ce6e51d..3b582ed07 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<hir::Name>,
     ) {
-        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 43a5160cb..9282c3827 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 5112ecc2d..e9d0ff665 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 61d8a17e5..465dfe00c 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<Name>,
+    path: Option<hir::ModPath>,
 ) -> Option<CompletionItem> {
     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))
-- 
cgit v1.2.3