mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +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 crate::completion::{CompletionContext, Completions};
|
||||
use hir::{Adt, ModuleDef};
|
||||
use ra_syntax::AstNode;
|
||||
|
||||
pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
if !ctx.is_trivial_path {
|
||||
return;
|
||||
}
|
||||
|
||||
if ctx.is_pat_binding_or_const
|
||||
if (!ctx.is_trivial_path && !ctx.is_pat_binding_or_const)
|
||||
|| ctx.record_lit_syntax.is_some()
|
||||
|| ctx.record_pat_syntax.is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
complete_enum_variants(acc, ctx);
|
||||
|
||||
if ctx.is_pat_binding_or_const {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.scope().process_all_names(&mut |name, res| {
|
||||
if ctx.use_item_syntax.is_some() {
|
||||
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)]
|
||||
mod tests {
|
||||
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
|
||||
|
||||
use hir::{Semantics, SemanticsScope};
|
||||
use hir::{Semantics, SemanticsScope, Type};
|
||||
use ra_db::SourceDatabase;
|
||||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::{
|
||||
|
@ -168,6 +168,17 @@ impl<'a> CompletionContext<'a> {
|
|||
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(
|
||||
&mut self,
|
||||
original_file: &SyntaxNode,
|
||||
|
|
Loading…
Reference in a new issue