aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src')
-rw-r--r--crates/completion/src/completions.rs60
-rw-r--r--crates/completion/src/completions/pattern.rs56
-rw-r--r--crates/completion/src/completions/unqualified_path.rs56
-rw-r--r--crates/completion/src/context.rs16
-rw-r--r--crates/completion/src/render/pattern.rs6
5 files changed, 156 insertions, 38 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;
15pub(crate) mod mod_; 15pub(crate) mod mod_;
16pub(crate) mod flyimport; 16pub(crate) mod flyimport;
17 17
18use hir::{ModPath, ScopeDef, Type}; 18use std::iter;
19
20use hir::{known, ModPath, ScopeDef, Type};
19 21
20use crate::{ 22use 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
183fn 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 595160ff5..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| {
@@ -31,6 +37,14 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
31 _ => false, 37 _ => false,
32 }, 38 },
33 hir::ScopeDef::MacroDef(_) => true, 39 hir::ScopeDef::MacroDef(_) => true,
40 hir::ScopeDef::ImplSelfType(impl_) => match impl_.target_ty(ctx.db).as_adt() {
41 Some(hir::Adt::Struct(strukt)) => {
42 acc.add_struct_pat(ctx, strukt, Some(name.clone()));
43 true
44 }
45 Some(hir::Adt::Enum(_)) => !ctx.is_irrefutable_pat_binding,
46 _ => true,
47 },
34 _ => false, 48 _ => false,
35 }; 49 };
36 if add_resolution { 50 if add_resolution {
@@ -258,4 +272,46 @@ fn main() {
258"#, 272"#,
259 ); 273 );
260 } 274 }
275
276 #[test]
277 fn completes_self_pats() {
278 check_snippet(
279 r#"
280struct Foo(i32);
281impl Foo {
282 fn foo() {
283 match () {
284 $0
285 }
286 }
287}
288 "#,
289 expect![[r#"
290 bn Self Self($1)$0
291 bn Foo Foo($1)$0
292 "#]],
293 )
294 }
295
296 #[test]
297 fn completes_qualified_variant() {
298 check_snippet(
299 r#"
300enum Foo {
301 Bar { baz: i32 }
302}
303impl 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 }
261} 317}
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index e2482f959..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
3use std::iter; 3use hir::ScopeDef;
4
5use hir::{Adt, ModuleDef, ScopeDef, Type};
6use syntax::AstNode; 4use syntax::AstNode;
7use test_utils::mark; 5use 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,32 +45,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
45 }); 45 });
46} 46}
47 47
48fn 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 for variant in variants {
63 if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) {
64 // Variants with trivial paths are already added by the existing completion logic,
65 // so we should avoid adding these twice
66 if path.segments().len() > 1 {
67 acc.add_qualified_enum_variant(ctx, variant, path);
68 }
69 }
70 }
71 }
72}
73
74#[cfg(test)] 48#[cfg(test)]
75mod tests { 49mod tests {
76 use expect_test::{expect, Expect}; 50 use expect_test::{expect, Expect};
@@ -729,6 +703,28 @@ fn f() -> m::E { V$0 }
729 } 703 }
730 704
731 #[test] 705 #[test]
706 fn completes_enum_variant_impl() {
707 check(
708 r#"
709enum Foo { Bar, Baz, Quux }
710impl Foo {
711 fn foo() { match Foo::Bar { Q$0 } }
712}
713"#,
714 expect![[r#"
715 ev Self::Bar ()
716 ev Self::Baz ()
717 ev Self::Quux ()
718 ev Foo::Bar ()
719 ev Foo::Baz ()
720 ev Foo::Quux ()
721 sp Self
722 en Foo
723 "#]],
724 )
725 }
726
727 #[test]
732 fn dont_complete_attr() { 728 fn dont_complete_attr() {
733 check( 729 check(
734 r#" 730 r#"
diff --git a/crates/completion/src/context.rs b/crates/completion/src/context.rs
index b1e8eba85..3db357855 100644
--- a/crates/completion/src/context.rs
+++ b/crates/completion/src/context.rs
@@ -276,6 +276,14 @@ impl<'a> CompletionContext<'a> {
276 }); 276 });
277 } 277 }
278 278
279 fn fill_impl_def(&mut self) {
280 self.impl_def = self
281 .sema
282 .ancestors_with_macros(self.token.parent())
283 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
284 .find_map(ast::Impl::cast);
285 }
286
279 fn fill( 287 fn fill(
280 &mut self, 288 &mut self,
281 original_file: &SyntaxNode, 289 original_file: &SyntaxNode,
@@ -345,6 +353,8 @@ impl<'a> CompletionContext<'a> {
345 self.is_irrefutable_pat_binding = true; 353 self.is_irrefutable_pat_binding = true;
346 } 354 }
347 } 355 }
356
357 self.fill_impl_def();
348 } 358 }
349 if is_node::<ast::Param>(name.syntax()) { 359 if is_node::<ast::Param>(name.syntax()) {
350 self.is_param = true; 360 self.is_param = true;
@@ -372,11 +382,7 @@ impl<'a> CompletionContext<'a> {
372 self.sema.find_node_at_offset_with_macros(&original_file, offset); 382 self.sema.find_node_at_offset_with_macros(&original_file, offset);
373 } 383 }
374 384
375 self.impl_def = self 385 self.fill_impl_def();
376 .sema
377 .ancestors_with_macros(self.token.parent())
378 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
379 .find_map(ast::Impl::cast);
380 386
381 let top_node = name_ref 387 let top_node = name_ref
382 .syntax() 388 .syntax()
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))