diff options
-rw-r--r-- | crates/completion/src/completions.rs | 60 | ||||
-rw-r--r-- | crates/completion/src/completions/pattern.rs | 28 | ||||
-rw-r--r-- | crates/completion/src/completions/unqualified_path.rs | 46 | ||||
-rw-r--r-- | crates/completion/src/render/pattern.rs | 6 |
4 files changed, 95 insertions, 45 deletions
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs index c3ce6e51d..3b582ed07 100644 --- a/crates/completion/src/completions.rs +++ b/crates/completion/src/completions.rs | |||
@@ -15,7 +15,9 @@ pub(crate) mod trait_impl; | |||
15 | pub(crate) mod mod_; | 15 | pub(crate) mod mod_; |
16 | pub(crate) mod flyimport; | 16 | pub(crate) mod flyimport; |
17 | 17 | ||
18 | use hir::{ModPath, ScopeDef, Type}; | 18 | use std::iter; |
19 | |||
20 | use hir::{known, ModPath, ScopeDef, Type}; | ||
19 | 21 | ||
20 | use crate::{ | 22 | use crate::{ |
21 | item::Builder, | 23 | item::Builder, |
@@ -118,7 +120,18 @@ impl Completions { | |||
118 | variant: hir::Variant, | 120 | variant: hir::Variant, |
119 | local_name: Option<hir::Name>, | 121 | local_name: Option<hir::Name>, |
120 | ) { | 122 | ) { |
121 | if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, local_name) { | 123 | if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, local_name, None) { |
124 | self.add(item); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | pub(crate) fn add_qualified_variant_pat( | ||
129 | &mut self, | ||
130 | ctx: &CompletionContext, | ||
131 | variant: hir::Variant, | ||
132 | path: ModPath, | ||
133 | ) { | ||
134 | if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)) { | ||
122 | self.add(item); | 135 | self.add(item); |
123 | } | 136 | } |
124 | } | 137 | } |
@@ -166,3 +179,46 @@ impl Completions { | |||
166 | self.add(item); | 179 | self.add(item); |
167 | } | 180 | } |
168 | } | 181 | } |
182 | |||
183 | fn complete_enum_variants( | ||
184 | acc: &mut Completions, | ||
185 | ctx: &CompletionContext, | ||
186 | ty: &hir::Type, | ||
187 | cb: impl Fn(&mut Completions, &CompletionContext, hir::Variant, hir::ModPath), | ||
188 | ) { | ||
189 | if let Some(hir::Adt::Enum(enum_data)) = | ||
190 | iter::successors(Some(ty.clone()), |ty| ty.remove_ref()).last().and_then(|ty| ty.as_adt()) | ||
191 | { | ||
192 | let variants = enum_data.variants(ctx.db); | ||
193 | |||
194 | let module = if let Some(module) = ctx.scope.module() { | ||
195 | // Compute path from the completion site if available. | ||
196 | module | ||
197 | } else { | ||
198 | // Otherwise fall back to the enum's definition site. | ||
199 | enum_data.module(ctx.db) | ||
200 | }; | ||
201 | |||
202 | if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { | ||
203 | if impl_.target_ty(ctx.db) == *ty { | ||
204 | for &variant in &variants { | ||
205 | let self_path = hir::ModPath::from_segments( | ||
206 | hir::PathKind::Plain, | ||
207 | iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))), | ||
208 | ); | ||
209 | cb(acc, ctx, variant, self_path); | ||
210 | } | ||
211 | } | ||
212 | } | ||
213 | |||
214 | for variant in variants { | ||
215 | if let Some(path) = module.find_use_path(ctx.db, hir::ModuleDef::from(variant)) { | ||
216 | // Variants with trivial paths are already added by the existing completion logic, | ||
217 | // so we should avoid adding these twice | ||
218 | if path.segments().len() > 1 { | ||
219 | cb(acc, ctx, variant, path); | ||
220 | } | ||
221 | } | ||
222 | } | ||
223 | } | ||
224 | } | ||
diff --git a/crates/completion/src/completions/pattern.rs b/crates/completion/src/completions/pattern.rs index 43a5160cb..9282c3827 100644 --- a/crates/completion/src/completions/pattern.rs +++ b/crates/completion/src/completions/pattern.rs | |||
@@ -11,6 +11,12 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { | |||
11 | return; | 11 | return; |
12 | } | 12 | } |
13 | 13 | ||
14 | if let Some(ty) = &ctx.expected_type { | ||
15 | super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| { | ||
16 | acc.add_qualified_variant_pat(ctx, variant, path) | ||
17 | }); | ||
18 | } | ||
19 | |||
14 | // FIXME: ideally, we should look at the type we are matching against and | 20 | // FIXME: ideally, we should look at the type we are matching against and |
15 | // suggest variants + auto-imports | 21 | // suggest variants + auto-imports |
16 | ctx.scope.process_all_names(&mut |name, res| { | 22 | ctx.scope.process_all_names(&mut |name, res| { |
@@ -286,4 +292,26 @@ impl Foo { | |||
286 | "#]], | 292 | "#]], |
287 | ) | 293 | ) |
288 | } | 294 | } |
295 | |||
296 | #[test] | ||
297 | fn completes_qualified_variant() { | ||
298 | check_snippet( | ||
299 | r#" | ||
300 | enum Foo { | ||
301 | Bar { baz: i32 } | ||
302 | } | ||
303 | impl Foo { | ||
304 | fn foo() { | ||
305 | match {Foo::Bar { baz: 0 }} { | ||
306 | B$0 | ||
307 | } | ||
308 | } | ||
309 | } | ||
310 | "#, | ||
311 | expect![[r#" | ||
312 | bn Self::Bar Self::Bar { baz$1 }$0 | ||
313 | bn Foo::Bar Foo::Bar { baz$1 }$0 | ||
314 | "#]], | ||
315 | ) | ||
316 | } | ||
289 | } | 317 | } |
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 5112ecc2d..e9d0ff665 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs | |||
@@ -1,8 +1,6 @@ | |||
1 | //! Completion of names from the current scope, e.g. locals and imported items. | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use std::iter; | 3 | use hir::ScopeDef; |
4 | |||
5 | use hir::{known, Adt, ModuleDef, ScopeDef, Type}; | ||
6 | use syntax::AstNode; | 4 | use syntax::AstNode; |
7 | use test_utils::mark; | 5 | use test_utils::mark; |
8 | 6 | ||
@@ -21,7 +19,9 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
21 | } | 19 | } |
22 | 20 | ||
23 | if let Some(ty) = &ctx.expected_type { | 21 | if let Some(ty) = &ctx.expected_type { |
24 | complete_enum_variants(acc, ctx, ty); | 22 | super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| { |
23 | acc.add_qualified_enum_variant(ctx, variant, path) | ||
24 | }); | ||
25 | } | 25 | } |
26 | 26 | ||
27 | if ctx.is_pat_binding_or_const { | 27 | if ctx.is_pat_binding_or_const { |
@@ -45,44 +45,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
45 | }); | 45 | }); |
46 | } | 46 | } |
47 | 47 | ||
48 | fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) { | ||
49 | if let Some(Adt::Enum(enum_data)) = | ||
50 | iter::successors(Some(ty.clone()), |ty| ty.remove_ref()).last().and_then(|ty| ty.as_adt()) | ||
51 | { | ||
52 | let variants = enum_data.variants(ctx.db); | ||
53 | |||
54 | let module = if let Some(module) = ctx.scope.module() { | ||
55 | // Compute path from the completion site if available. | ||
56 | module | ||
57 | } else { | ||
58 | // Otherwise fall back to the enum's definition site. | ||
59 | enum_data.module(ctx.db) | ||
60 | }; | ||
61 | |||
62 | if let Some(impl_) = ctx.impl_def.as_ref().and_then(|impl_| ctx.sema.to_def(impl_)) { | ||
63 | if impl_.target_ty(ctx.db) == *ty { | ||
64 | for &variant in &variants { | ||
65 | let self_path = hir::ModPath::from_segments( | ||
66 | hir::PathKind::Plain, | ||
67 | iter::once(known::SELF_TYPE).chain(iter::once(variant.name(ctx.db))), | ||
68 | ); | ||
69 | acc.add_qualified_enum_variant(ctx, variant, self_path.clone()); | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | |||
74 | for variant in variants { | ||
75 | if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { | ||
76 | // Variants with trivial paths are already added by the existing completion logic, | ||
77 | // so we should avoid adding these twice | ||
78 | if path.segments().len() > 1 { | ||
79 | acc.add_qualified_enum_variant(ctx, variant, path); | ||
80 | } | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | } | ||
85 | |||
86 | #[cfg(test)] | 48 | #[cfg(test)] |
87 | mod tests { | 49 | mod tests { |
88 | use expect_test::{expect, Expect}; | 50 | use expect_test::{expect, Expect}; |
diff --git a/crates/completion/src/render/pattern.rs b/crates/completion/src/render/pattern.rs index 61d8a17e5..465dfe00c 100644 --- a/crates/completion/src/render/pattern.rs +++ b/crates/completion/src/render/pattern.rs | |||
@@ -49,13 +49,17 @@ pub(crate) fn render_variant_pat( | |||
49 | ctx: RenderContext<'_>, | 49 | ctx: RenderContext<'_>, |
50 | variant: hir::Variant, | 50 | variant: hir::Variant, |
51 | local_name: Option<Name>, | 51 | local_name: Option<Name>, |
52 | path: Option<hir::ModPath>, | ||
52 | ) -> Option<CompletionItem> { | 53 | ) -> Option<CompletionItem> { |
53 | let _p = profile::span("render_variant_pat"); | 54 | let _p = profile::span("render_variant_pat"); |
54 | 55 | ||
55 | let fields = variant.fields(ctx.db()); | 56 | let fields = variant.fields(ctx.db()); |
56 | let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?; | 57 | let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?; |
57 | 58 | ||
58 | let name = local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string(); | 59 | let name = match &path { |
60 | Some(path) => path.to_string(), | ||
61 | None => local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string(), | ||
62 | }; | ||
59 | let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &visible_fields, fields_omitted)?; | 63 | let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &visible_fields, fields_omitted)?; |
60 | 64 | ||
61 | Some(build_completion(ctx, name, pat, variant)) | 65 | Some(build_completion(ctx, name, pat, variant)) |