From 6c61a7b22f70a7359d7bad9509b93a00d73c53bf Mon Sep 17 00:00:00 2001 From: nathanwhit Date: Tue, 21 Apr 2020 14:28:49 -0400 Subject: [PATCH 1/4] Add utility fn for expected type of a node Adds `expected_type_of` to `CompletionContext` to return the expected type of a node, if it is known. --- crates/ra_ide/src/completion/completion_context.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 8b34015950..cfc5c34df0 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -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 { + 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, From 86645097456c6f017e7a85acd638345aab51e35b Mon Sep 17 00:00:00 2001 From: nathanwhit Date: Mon, 20 Apr 2020 14:01:30 -0400 Subject: [PATCH 2/4] Complete unqualified enum variants when possible --- .../completion/complete_unqualified_path.rs | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs index ad5fdcc4ea..ffa3c01a7d 100644 --- a/crates/ra_ide/src/completion/complete_unqualified_path.rs +++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs @@ -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; From 18ad86fddac4c4315df168d84b714cc07b096e0c Mon Sep 17 00:00:00 2001 From: nathanwhit Date: Mon, 20 Apr 2020 15:29:53 -0400 Subject: [PATCH 3/4] Add tests for enum completion Adds tests for completion of enum variants in match arms, if-let statements, and basic expressions. --- .../completion/complete_unqualified_path.rs | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs index ffa3c01a7d..b4da342b5e 100644 --- a/crates/ra_ide/src/completion/complete_unqualified_path.rs +++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs @@ -1130,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()", + }, + ] + "### + ) + } } From dfde73ef90194f90704287876f0e5d3b0cfa2b11 Mon Sep 17 00:00:00 2001 From: nathanwhit Date: Mon, 20 Apr 2020 15:49:50 -0400 Subject: [PATCH 4/4] Update tests to reflect new completions --- crates/ra_ide/src/completion/complete_unqualified_path.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs index b4da342b5e..ad00154a32 100644 --- a/crates/ra_ide/src/completion/complete_unqualified_path.rs +++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs @@ -103,7 +103,7 @@ mod tests { } " ), - @r###"[]"### + @"[]" ); }