aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-04-23 16:35:31 +0100
committerGitHub <[email protected]>2020-04-23 16:35:31 +0100
commit278bf351e3d9bf074b76f89afa04c4727d55fba6 (patch)
tree5c0dcf6a4e47a90f1d650fd7c33a6438a4bc3262 /crates
parent4a18509a98149fb3d5c73dbf0c6de4cfa8d4596c (diff)
parentdfde73ef90194f90704287876f0e5d3b0cfa2b11 (diff)
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 <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs211
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs13
2 files changed, 217 insertions, 7 deletions
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index ad5fdcc4e..ad00154a3 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;
4use test_utils::tested_by; 4use test_utils::tested_by;
5 5
6use crate::completion::{CompletionContext, Completions}; 6use crate::completion::{CompletionContext, Completions};
7use hir::{Adt, ModuleDef};
7use ra_syntax::AstNode; 8use ra_syntax::AstNode;
8 9
9pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
10 if !ctx.is_trivial_path { 11 if (!ctx.is_trivial_path && !ctx.is_pat_binding_or_const)
11 return;
12 }
13
14 if ctx.is_pat_binding_or_const
15 || ctx.record_lit_syntax.is_some() 12 || ctx.record_lit_syntax.is_some()
16 || ctx.record_pat_syntax.is_some() 13 || ctx.record_pat_syntax.is_some()
17 { 14 {
18 return; 15 return;
19 } 16 }
20 17
18 complete_enum_variants(acc, ctx);
19
20 if ctx.is_pat_binding_or_const {
21 return;
22 }
23
21 ctx.scope().process_all_names(&mut |name, res| { 24 ctx.scope().process_all_names(&mut |name, res| {
22 if ctx.use_item_syntax.is_some() { 25 if ctx.use_item_syntax.is_some() {
23 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { 26 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
31 }); 34 });
32} 35}
33 36
37fn 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
34#[cfg(test)] 55#[cfg(test)]
35mod tests { 56mod tests {
36 use insta::assert_debug_snapshot; 57 use insta::assert_debug_snapshot;
@@ -82,7 +103,7 @@ mod tests {
82 } 103 }
83 " 104 "
84 ), 105 ),
85 @r###"[]"### 106 @"[]"
86 ); 107 );
87 } 108 }
88 109
@@ -1109,4 +1130,182 @@ mod tests {
1109 "### 1130 "###
1110 ); 1131 );
1111 } 1132 }
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 }
1112} 1311}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 8b3401595..cfc5c34df 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{Semantics, SemanticsScope}; 3use hir::{Semantics, SemanticsScope, Type};
4use ra_db::SourceDatabase; 4use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
@@ -168,6 +168,17 @@ impl<'a> CompletionContext<'a> {
168 self.sema.scope_at_offset(&self.token.parent(), self.offset) 168 self.sema.scope_at_offset(&self.token.parent(), self.offset)
169 } 169 }
170 170
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
171 fn fill( 182 fn fill(
172 &mut self, 183 &mut self,
173 original_file: &SyntaxNode, 184 original_file: &SyntaxNode,