Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
4065: Complete unqualified enum names in patterns and expressions r=matklad a=nathanwhit

This PR implements the completion described in rust-lang#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 <[email protected]>
  • Loading branch information
bors[bot] and nathanwhit authored Apr 23, 2020
2 parents 4a18509 + dfde73e commit 278bf35
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 7 deletions.
211 changes: 205 additions & 6 deletions crates/ra_ide/src/completion/complete_unqualified_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -82,7 +103,7 @@ mod tests {
}
"
),
@r###"[]"###
@"[]"
);
}

Expand Down Expand Up @@ -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()",
},
]
"###
)
}
}
13 changes: 12 additions & 1 deletion crates/ra_ide/src/completion/completion_context.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 278bf35

Please sign in to comment.