mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 12:54:58 +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 mod_;
|
||||||
pub(crate) mod flyimport;
|
pub(crate) mod flyimport;
|
||||||
|
|
||||||
use hir::{ModPath, ScopeDef, Type};
|
use std::iter;
|
||||||
|
|
||||||
|
use hir::{known, ModPath, ScopeDef, Type};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
item::Builder,
|
item::Builder,
|
||||||
|
@ -118,7 +120,18 @@ impl Completions {
|
||||||
variant: hir::Variant,
|
variant: hir::Variant,
|
||||||
local_name: Option<hir::Name>,
|
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);
|
self.add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,3 +179,46 @@ impl Completions {
|
||||||
self.add(item);
|
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;
|
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
|
// FIXME: ideally, we should look at the type we are matching against and
|
||||||
// suggest variants + auto-imports
|
// suggest variants + auto-imports
|
||||||
ctx.scope.process_all_names(&mut |name, res| {
|
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.
|
//! Completion of names from the current scope, e.g. locals and imported items.
|
||||||
|
|
||||||
use std::iter;
|
use hir::ScopeDef;
|
||||||
|
|
||||||
use hir::{known, Adt, ModuleDef, ScopeDef, Type};
|
|
||||||
use syntax::AstNode;
|
use syntax::AstNode;
|
||||||
use test_utils::mark;
|
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 {
|
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 {
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use expect_test::{expect, Expect};
|
use expect_test::{expect, Expect};
|
||||||
|
|
|
@ -49,13 +49,17 @@ pub(crate) fn render_variant_pat(
|
||||||
ctx: RenderContext<'_>,
|
ctx: RenderContext<'_>,
|
||||||
variant: hir::Variant,
|
variant: hir::Variant,
|
||||||
local_name: Option<Name>,
|
local_name: Option<Name>,
|
||||||
|
path: Option<hir::ModPath>,
|
||||||
) -> Option<CompletionItem> {
|
) -> Option<CompletionItem> {
|
||||||
let _p = profile::span("render_variant_pat");
|
let _p = profile::span("render_variant_pat");
|
||||||
|
|
||||||
let fields = variant.fields(ctx.db());
|
let fields = variant.fields(ctx.db());
|
||||||
let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?;
|
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)?;
|
let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &visible_fields, fields_omitted)?;
|
||||||
|
|
||||||
Some(build_completion(ctx, name, pat, variant))
|
Some(build_completion(ctx, name, pat, variant))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue