diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-04-23 16:35:31 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-04-23 16:35:31 +0100 |
commit | 278bf351e3d9bf074b76f89afa04c4727d55fba6 (patch) | |
tree | 5c0dcf6a4e47a90f1d650fd7c33a6438a4bc3262 /crates/ra_ide/src | |
parent | 4a18509a98149fb3d5c73dbf0c6de4cfa8d4596c (diff) | |
parent | dfde73ef90194f90704287876f0e5d3b0cfa2b11 (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/ra_ide/src')
-rw-r--r-- | crates/ra_ide/src/completion/complete_unqualified_path.rs | 211 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/completion_context.rs | 13 |
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; | |||
4 | use test_utils::tested_by; | 4 | use test_utils::tested_by; |
5 | 5 | ||
6 | use crate::completion::{CompletionContext, Completions}; | 6 | use crate::completion::{CompletionContext, Completions}; |
7 | use hir::{Adt, ModuleDef}; | ||
7 | use ra_syntax::AstNode; | 8 | use ra_syntax::AstNode; |
8 | 9 | ||
9 | pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 10 | pub(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 | ||
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 | |||
34 | #[cfg(test)] | 55 | #[cfg(test)] |
35 | mod tests { | 56 | mod 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 | ||
3 | use hir::{Semantics, SemanticsScope}; | 3 | use hir::{Semantics, SemanticsScope, Type}; |
4 | use ra_db::SourceDatabase; | 4 | use ra_db::SourceDatabase; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use 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, |