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/keyword.rs8
-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
6 files changed, 164 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/keyword.rs b/crates/completion/src/completions/keyword.rs
index 47e146128..eb81f9765 100644
--- a/crates/completion/src/completions/keyword.rs
+++ b/crates/completion/src/completions/keyword.rs
@@ -88,6 +88,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
88 add_keyword(ctx, acc, "loop", "loop {$0}"); 88 add_keyword(ctx, acc, "loop", "loop {$0}");
89 add_keyword(ctx, acc, "if", "if $0 {}"); 89 add_keyword(ctx, acc, "if", "if $0 {}");
90 add_keyword(ctx, acc, "if let", "if let $1 = $0 {}"); 90 add_keyword(ctx, acc, "if let", "if let $1 = $0 {}");
91 add_keyword(ctx, acc, "for", "for $1 in $0 {}");
91 } 92 }
92 93
93 if ctx.if_is_prev || ctx.block_expr_parent { 94 if ctx.if_is_prev || ctx.block_expr_parent {
@@ -258,6 +259,7 @@ mod tests {
258 kw loop 259 kw loop
259 kw if 260 kw if
260 kw if let 261 kw if let
262 kw for
261 kw let 263 kw let
262 kw mod 264 kw mod
263 kw const 265 kw const
@@ -284,6 +286,7 @@ mod tests {
284 kw loop 286 kw loop
285 kw if 287 kw if
286 kw if let 288 kw if let
289 kw for
287 kw let 290 kw let
288 kw mod 291 kw mod
289 kw const 292 kw const
@@ -310,6 +313,7 @@ mod tests {
310 kw loop 313 kw loop
311 kw if 314 kw if
312 kw if let 315 kw if let
316 kw for
313 kw let 317 kw let
314 kw else 318 kw else
315 kw else if 319 kw else if
@@ -343,6 +347,7 @@ fn quux() -> i32 {
343 kw loop 347 kw loop
344 kw if 348 kw if
345 kw if let 349 kw if let
350 kw for
346 kw unsafe 351 kw unsafe
347 kw return 352 kw return
348 "#]], 353 "#]],
@@ -391,6 +396,7 @@ fn quux() -> i32 {
391 kw loop 396 kw loop
392 kw if 397 kw if
393 kw if let 398 kw if let
399 kw for
394 kw let 400 kw let
395 kw mod 401 kw mod
396 kw const 402 kw const
@@ -549,6 +555,7 @@ pub mod future {
549 kw loop 555 kw loop
550 kw if 556 kw if
551 kw if let 557 kw if let
558 kw for
552 kw return 559 kw return
553 "#]], 560 "#]],
554 ) 561 )
@@ -607,6 +614,7 @@ fn foo() {
607 kw loop 614 kw loop
608 kw if 615 kw if
609 kw if let 616 kw if let
617 kw for
610 kw return 618 kw return
611 "#]], 619 "#]],
612 ); 620 );
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))