mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Show qualified variant pattern completions
This commit is contained in:
parent
5d4ae1c8e3
commit
5454559c0a
4 changed files with 95 additions and 45 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in a new issue