Skip to content

Commit 278bf35

Browse files
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 <nathan.whitaker01@gmail.com>
2 parents 4a18509 + dfde73e commit 278bf35

File tree

2 files changed

+217
-7
lines changed

2 files changed

+217
-7
lines changed

crates/ra_ide/src/completion/complete_unqualified_path.rs

Lines changed: 205 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,23 @@ use hir::ScopeDef;
44
use test_utils::tested_by;
55

66
use crate::completion::{CompletionContext, Completions};
7+
use hir::{Adt, ModuleDef};
78
use ra_syntax::AstNode;
89

910
pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
10-
if !ctx.is_trivial_path {
11-
return;
12-
}
13-
14-
if ctx.is_pat_binding_or_const
11+
if (!ctx.is_trivial_path && !ctx.is_pat_binding_or_const)
1512
|| ctx.record_lit_syntax.is_some()
1613
|| ctx.record_pat_syntax.is_some()
1714
{
1815
return;
1916
}
2017

18+
complete_enum_variants(acc, ctx);
19+
20+
if ctx.is_pat_binding_or_const {
21+
return;
22+
}
23+
2124
ctx.scope().process_all_names(&mut |name, res| {
2225
if ctx.use_item_syntax.is_some() {
2326
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
3134
});
3235
}
3336

37+
fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext) {
38+
if let Some(ty) = ctx.expected_type_of(&ctx.token.parent()) {
39+
if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
40+
let variants = enum_data.variants(ctx.db);
41+
let module = enum_data.module(ctx.db);
42+
for variant in variants {
43+
if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) {
44+
// Variants with trivial paths are already added by the existing completion logic,
45+
// so we should avoid adding these twice
46+
if path.segments.len() > 1 {
47+
acc.add_enum_variant(ctx, variant, Some(path.to_string()));
48+
}
49+
}
50+
}
51+
}
52+
}
53+
}
54+
3455
#[cfg(test)]
3556
mod tests {
3657
use insta::assert_debug_snapshot;
@@ -82,7 +103,7 @@ mod tests {
82103
}
83104
"
84105
),
85-
@r###"[]"###
106+
@"[]"
86107
);
87108
}
88109

@@ -1109,4 +1130,182 @@ mod tests {
11091130
"###
11101131
);
11111132
}
1133+
#[test]
1134+
fn completes_enum_variant_matcharm() {
1135+
assert_debug_snapshot!(
1136+
do_reference_completion(
1137+
r"
1138+
enum Foo {
1139+
Bar,
1140+
Baz,
1141+
Quux
1142+
}
1143+
1144+
fn main() {
1145+
let foo = Foo::Quux;
1146+
1147+
match foo {
1148+
Qu<|>
1149+
}
1150+
}
1151+
"
1152+
),
1153+
@r###"
1154+
[
1155+
CompletionItem {
1156+
label: "Foo",
1157+
source_range: [248; 250),
1158+
delete: [248; 250),
1159+
insert: "Foo",
1160+
kind: Enum,
1161+
},
1162+
CompletionItem {
1163+
label: "Foo::Bar",
1164+
source_range: [248; 250),
1165+
delete: [248; 250),
1166+
insert: "Foo::Bar",
1167+
kind: EnumVariant,
1168+
detail: "()",
1169+
},
1170+
CompletionItem {
1171+
label: "Foo::Baz",
1172+
source_range: [248; 250),
1173+
delete: [248; 250),
1174+
insert: "Foo::Baz",
1175+
kind: EnumVariant,
1176+
detail: "()",
1177+
},
1178+
CompletionItem {
1179+
label: "Foo::Quux",
1180+
source_range: [248; 250),
1181+
delete: [248; 250),
1182+
insert: "Foo::Quux",
1183+
kind: EnumVariant,
1184+
detail: "()",
1185+
},
1186+
]
1187+
"###
1188+
)
1189+
}
1190+
1191+
#[test]
1192+
fn completes_enum_variant_iflet() {
1193+
assert_debug_snapshot!(
1194+
do_reference_completion(
1195+
r"
1196+
enum Foo {
1197+
Bar,
1198+
Baz,
1199+
Quux
1200+
}
1201+
1202+
fn main() {
1203+
let foo = Foo::Quux;
1204+
1205+
if let Qu<|> = foo {
1206+
1207+
}
1208+
}
1209+
"
1210+
),
1211+
@r###"
1212+
[
1213+
CompletionItem {
1214+
label: "Foo",
1215+
source_range: [219; 221),
1216+
delete: [219; 221),
1217+
insert: "Foo",
1218+
kind: Enum,
1219+
},
1220+
CompletionItem {
1221+
label: "Foo::Bar",
1222+
source_range: [219; 221),
1223+
delete: [219; 221),
1224+
insert: "Foo::Bar",
1225+
kind: EnumVariant,
1226+
detail: "()",
1227+
},
1228+
CompletionItem {
1229+
label: "Foo::Baz",
1230+
source_range: [219; 221),
1231+
delete: [219; 221),
1232+
insert: "Foo::Baz",
1233+
kind: EnumVariant,
1234+
detail: "()",
1235+
},
1236+
CompletionItem {
1237+
label: "Foo::Quux",
1238+
source_range: [219; 221),
1239+
delete: [219; 221),
1240+
insert: "Foo::Quux",
1241+
kind: EnumVariant,
1242+
detail: "()",
1243+
},
1244+
]
1245+
"###
1246+
)
1247+
}
1248+
1249+
#[test]
1250+
fn completes_enum_variant_basic_expr() {
1251+
assert_debug_snapshot!(
1252+
do_reference_completion(
1253+
r"
1254+
enum Foo {
1255+
Bar,
1256+
Baz,
1257+
Quux
1258+
}
1259+
1260+
fn main() {
1261+
let foo: Foo = Q<|>
1262+
}
1263+
"
1264+
),
1265+
@r###"
1266+
[
1267+
CompletionItem {
1268+
label: "Foo",
1269+
source_range: [185; 186),
1270+
delete: [185; 186),
1271+
insert: "Foo",
1272+
kind: Enum,
1273+
},
1274+
CompletionItem {
1275+
label: "Foo::Bar",
1276+
source_range: [185; 186),
1277+
delete: [185; 186),
1278+
insert: "Foo::Bar",
1279+
kind: EnumVariant,
1280+
detail: "()",
1281+
},
1282+
CompletionItem {
1283+
label: "Foo::Baz",
1284+
source_range: [185; 186),
1285+
delete: [185; 186),
1286+
insert: "Foo::Baz",
1287+
kind: EnumVariant,
1288+
detail: "()",
1289+
},
1290+
CompletionItem {
1291+
label: "Foo::Quux",
1292+
source_range: [185; 186),
1293+
delete: [185; 186),
1294+
insert: "Foo::Quux",
1295+
kind: EnumVariant,
1296+
detail: "()",
1297+
},
1298+
CompletionItem {
1299+
label: "main()",
1300+
source_range: [185; 186),
1301+
delete: [185; 186),
1302+
insert: "main()$0",
1303+
kind: Function,
1304+
lookup: "main",
1305+
detail: "fn main()",
1306+
},
1307+
]
1308+
"###
1309+
)
1310+
}
11121311
}

crates/ra_ide/src/completion/completion_context.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! FIXME: write short doc here
22
3-
use hir::{Semantics, SemanticsScope};
3+
use hir::{Semantics, SemanticsScope, Type};
44
use ra_db::SourceDatabase;
55
use ra_ide_db::RootDatabase;
66
use ra_syntax::{
@@ -168,6 +168,17 @@ impl<'a> CompletionContext<'a> {
168168
self.sema.scope_at_offset(&self.token.parent(), self.offset)
169169
}
170170

171+
pub(crate) fn expected_type_of(&self, node: &SyntaxNode) -> Option<Type> {
172+
for ancestor in node.ancestors() {
173+
if let Some(pat) = ast::Pat::cast(ancestor.clone()) {
174+
return self.sema.type_of_pat(&pat);
175+
} else if let Some(expr) = ast::Expr::cast(ancestor) {
176+
return self.sema.type_of_expr(&expr);
177+
}
178+
}
179+
None
180+
}
181+
171182
fn fill(
172183
&mut self,
173184
original_file: &SyntaxNode,

0 commit comments

Comments
 (0)