mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 22:24:14 +00:00
Merge #4065
4065: Complete unqualified enum names in patterns and expressions r=matklad a=nathanwhit This PR implements the completion described in #4014. The result looks like so for patterns: <img width="542" alt="Screen Shot 2020-04-20 at 3 53 55 PM" src="https://user-images.githubusercontent.com/17734409/79794010-8f529400-831f-11ea-9673-f838aa9bc962.png"> and for `expr`s: <img width="620" alt="Screen Shot 2020-04-21 at 3 51 24 PM" src="https://user-images.githubusercontent.com/17734409/79908784-d73ded80-83e9-11ea-991d-921f0cb27e6f.png"> I'm not confident that the completion text itself is very robust, as it will unconditionally add completions for enum variants with the form `Enum::Variant`. This means (I believe) it would still suggest `Enum::Variant` even if the local name is changed i.e. `use Enum as Foo` or the variants are brought into scope such as through `use Enum::*`. Co-authored-by: nathanwhit <nathan.whitaker01@gmail.com>
This commit is contained in:
commit
278bf351e3
2 changed files with 217 additions and 7 deletions
|
@ -4,20 +4,23 @@ use hir::ScopeDef;
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::completion::{CompletionContext, Completions};
|
use crate::completion::{CompletionContext, Completions};
|
||||||
|
use hir::{Adt, ModuleDef};
|
||||||
use ra_syntax::AstNode;
|
use ra_syntax::AstNode;
|
||||||
|
|
||||||
pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
if !ctx.is_trivial_path {
|
if (!ctx.is_trivial_path && !ctx.is_pat_binding_or_const)
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.is_pat_binding_or_const
|
|
||||||
|| ctx.record_lit_syntax.is_some()
|
|| ctx.record_lit_syntax.is_some()
|
||||||
|| ctx.record_pat_syntax.is_some()
|
|| ctx.record_pat_syntax.is_some()
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
complete_enum_variants(acc, ctx);
|
||||||
|
|
||||||
|
if ctx.is_pat_binding_or_const {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ctx.scope().process_all_names(&mut |name, res| {
|
ctx.scope().process_all_names(&mut |name, res| {
|
||||||
if ctx.use_item_syntax.is_some() {
|
if ctx.use_item_syntax.is_some() {
|
||||||
if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
|
if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
|
||||||
|
@ -31,6 +34,24 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
|
if let Some(ty) = ctx.expected_type_of(&ctx.token.parent()) {
|
||||||
|
if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
|
||||||
|
let variants = enum_data.variants(ctx.db);
|
||||||
|
let module = enum_data.module(ctx.db);
|
||||||
|
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_enum_variant(ctx, variant, Some(path.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use insta::assert_debug_snapshot;
|
use insta::assert_debug_snapshot;
|
||||||
|
@ -82,7 +103,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
),
|
),
|
||||||
@r###"[]"###
|
@"[]"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1109,4 +1130,182 @@ mod tests {
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn completes_enum_variant_matcharm() {
|
||||||
|
assert_debug_snapshot!(
|
||||||
|
do_reference_completion(
|
||||||
|
r"
|
||||||
|
enum Foo {
|
||||||
|
Bar,
|
||||||
|
Baz,
|
||||||
|
Quux
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = Foo::Quux;
|
||||||
|
|
||||||
|
match foo {
|
||||||
|
Qu<|>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
[
|
||||||
|
CompletionItem {
|
||||||
|
label: "Foo",
|
||||||
|
source_range: [248; 250),
|
||||||
|
delete: [248; 250),
|
||||||
|
insert: "Foo",
|
||||||
|
kind: Enum,
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
label: "Foo::Bar",
|
||||||
|
source_range: [248; 250),
|
||||||
|
delete: [248; 250),
|
||||||
|
insert: "Foo::Bar",
|
||||||
|
kind: EnumVariant,
|
||||||
|
detail: "()",
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
label: "Foo::Baz",
|
||||||
|
source_range: [248; 250),
|
||||||
|
delete: [248; 250),
|
||||||
|
insert: "Foo::Baz",
|
||||||
|
kind: EnumVariant,
|
||||||
|
detail: "()",
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
label: "Foo::Quux",
|
||||||
|
source_range: [248; 250),
|
||||||
|
delete: [248; 250),
|
||||||
|
insert: "Foo::Quux",
|
||||||
|
kind: EnumVariant,
|
||||||
|
detail: "()",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"###
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_enum_variant_iflet() {
|
||||||
|
assert_debug_snapshot!(
|
||||||
|
do_reference_completion(
|
||||||
|
r"
|
||||||
|
enum Foo {
|
||||||
|
Bar,
|
||||||
|
Baz,
|
||||||
|
Quux
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo = Foo::Quux;
|
||||||
|
|
||||||
|
if let Qu<|> = foo {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
[
|
||||||
|
CompletionItem {
|
||||||
|
label: "Foo",
|
||||||
|
source_range: [219; 221),
|
||||||
|
delete: [219; 221),
|
||||||
|
insert: "Foo",
|
||||||
|
kind: Enum,
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
label: "Foo::Bar",
|
||||||
|
source_range: [219; 221),
|
||||||
|
delete: [219; 221),
|
||||||
|
insert: "Foo::Bar",
|
||||||
|
kind: EnumVariant,
|
||||||
|
detail: "()",
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
label: "Foo::Baz",
|
||||||
|
source_range: [219; 221),
|
||||||
|
delete: [219; 221),
|
||||||
|
insert: "Foo::Baz",
|
||||||
|
kind: EnumVariant,
|
||||||
|
detail: "()",
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
label: "Foo::Quux",
|
||||||
|
source_range: [219; 221),
|
||||||
|
delete: [219; 221),
|
||||||
|
insert: "Foo::Quux",
|
||||||
|
kind: EnumVariant,
|
||||||
|
detail: "()",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"###
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_enum_variant_basic_expr() {
|
||||||
|
assert_debug_snapshot!(
|
||||||
|
do_reference_completion(
|
||||||
|
r"
|
||||||
|
enum Foo {
|
||||||
|
Bar,
|
||||||
|
Baz,
|
||||||
|
Quux
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo: Foo = Q<|>
|
||||||
|
}
|
||||||
|
"
|
||||||
|
),
|
||||||
|
@r###"
|
||||||
|
[
|
||||||
|
CompletionItem {
|
||||||
|
label: "Foo",
|
||||||
|
source_range: [185; 186),
|
||||||
|
delete: [185; 186),
|
||||||
|
insert: "Foo",
|
||||||
|
kind: Enum,
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
label: "Foo::Bar",
|
||||||
|
source_range: [185; 186),
|
||||||
|
delete: [185; 186),
|
||||||
|
insert: "Foo::Bar",
|
||||||
|
kind: EnumVariant,
|
||||||
|
detail: "()",
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
label: "Foo::Baz",
|
||||||
|
source_range: [185; 186),
|
||||||
|
delete: [185; 186),
|
||||||
|
insert: "Foo::Baz",
|
||||||
|
kind: EnumVariant,
|
||||||
|
detail: "()",
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
label: "Foo::Quux",
|
||||||
|
source_range: [185; 186),
|
||||||
|
delete: [185; 186),
|
||||||
|
insert: "Foo::Quux",
|
||||||
|
kind: EnumVariant,
|
||||||
|
detail: "()",
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
label: "main()",
|
||||||
|
source_range: [185; 186),
|
||||||
|
delete: [185; 186),
|
||||||
|
insert: "main()$0",
|
||||||
|
kind: Function,
|
||||||
|
lookup: "main",
|
||||||
|
detail: "fn main()",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"###
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
use hir::{Semantics, SemanticsScope};
|
use hir::{Semantics, SemanticsScope, Type};
|
||||||
use ra_db::SourceDatabase;
|
use ra_db::SourceDatabase;
|
||||||
use ra_ide_db::RootDatabase;
|
use ra_ide_db::RootDatabase;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
|
@ -168,6 +168,17 @@ impl<'a> CompletionContext<'a> {
|
||||||
self.sema.scope_at_offset(&self.token.parent(), self.offset)
|
self.sema.scope_at_offset(&self.token.parent(), self.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn expected_type_of(&self, node: &SyntaxNode) -> Option<Type> {
|
||||||
|
for ancestor in node.ancestors() {
|
||||||
|
if let Some(pat) = ast::Pat::cast(ancestor.clone()) {
|
||||||
|
return self.sema.type_of_pat(&pat);
|
||||||
|
} else if let Some(expr) = ast::Expr::cast(ancestor) {
|
||||||
|
return self.sema.type_of_expr(&expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn fill(
|
fn fill(
|
||||||
&mut self,
|
&mut self,
|
||||||
original_file: &SyntaxNode,
|
original_file: &SyntaxNode,
|
||||||
|
|
Loading…
Reference in a new issue