diff options
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r-- | crates/ide_completion/src/completions/pattern.rs | 116 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/postfix/format_like.rs | 2 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/unqualified_path.rs | 88 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 295 | ||||
-rw-r--r-- | crates/ide_completion/src/item.rs | 37 | ||||
-rw-r--r-- | crates/ide_completion/src/render.rs | 424 | ||||
-rw-r--r-- | crates/ide_completion/src/render/enum_variant.rs | 13 | ||||
-rw-r--r-- | crates/ide_completion/src/render/function.rs | 20 | ||||
-rw-r--r-- | crates/ide_completion/src/render/pattern.rs | 36 |
9 files changed, 825 insertions, 206 deletions
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index 9282c3827..46cef58f0 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs | |||
@@ -11,10 +11,13 @@ 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 { | 14 | if !ctx.is_irrefutable_pat_binding { |
15 | super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| { | 15 | if let Some(ty) = ctx.expected_type.as_ref() { |
16 | acc.add_qualified_variant_pat(ctx, variant, path) | 16 | super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| { |
17 | }); | 17 | acc.add_qualified_variant_pat(ctx, variant, path.clone()); |
18 | acc.add_qualified_enum_variant(ctx, variant, path); | ||
19 | }); | ||
20 | } | ||
18 | } | 21 | } |
19 | 22 | ||
20 | // FIXME: ideally, we should look at the type we are matching against and | 23 | // FIXME: ideally, we should look at the type we are matching against and |
@@ -85,7 +88,7 @@ static FOO: E = E::X; | |||
85 | struct Bar { f: u32 } | 88 | struct Bar { f: u32 } |
86 | 89 | ||
87 | fn foo() { | 90 | fn foo() { |
88 | match E::X { $0 } | 91 | match E::X { a$0 } |
89 | } | 92 | } |
90 | "#, | 93 | "#, |
91 | expect![[r#" | 94 | expect![[r#" |
@@ -106,10 +109,11 @@ macro_rules! m { ($e:expr) => { $e } } | |||
106 | enum E { X } | 109 | enum E { X } |
107 | 110 | ||
108 | fn foo() { | 111 | fn foo() { |
109 | m!(match E::X { $0 }) | 112 | m!(match E::X { a$0 }) |
110 | } | 113 | } |
111 | "#, | 114 | "#, |
112 | expect![[r#" | 115 | expect![[r#" |
116 | ev E::X () | ||
113 | en E | 117 | en E |
114 | ma m!(…) macro_rules! m | 118 | ma m!(…) macro_rules! m |
115 | "#]], | 119 | "#]], |
@@ -129,7 +133,7 @@ static FOO: E = E::X; | |||
129 | struct Bar { f: u32 } | 133 | struct Bar { f: u32 } |
130 | 134 | ||
131 | fn foo() { | 135 | fn foo() { |
132 | let $0 | 136 | let a$0 |
133 | } | 137 | } |
134 | "#, | 138 | "#, |
135 | expect![[r#" | 139 | expect![[r#" |
@@ -147,7 +151,7 @@ enum E { X } | |||
147 | static FOO: E = E::X; | 151 | static FOO: E = E::X; |
148 | struct Bar { f: u32 } | 152 | struct Bar { f: u32 } |
149 | 153 | ||
150 | fn foo($0) { | 154 | fn foo(a$0) { |
151 | } | 155 | } |
152 | "#, | 156 | "#, |
153 | expect![[r#" | 157 | expect![[r#" |
@@ -163,7 +167,7 @@ fn foo($0) { | |||
163 | struct Bar { f: u32 } | 167 | struct Bar { f: u32 } |
164 | 168 | ||
165 | fn foo() { | 169 | fn foo() { |
166 | let $0 | 170 | let a$0 |
167 | } | 171 | } |
168 | "#, | 172 | "#, |
169 | expect![[r#" | 173 | expect![[r#" |
@@ -179,7 +183,7 @@ fn foo() { | |||
179 | struct Foo { bar: String, baz: String } | 183 | struct Foo { bar: String, baz: String } |
180 | struct Bar(String, String); | 184 | struct Bar(String, String); |
181 | struct Baz; | 185 | struct Baz; |
182 | fn outer($0) {} | 186 | fn outer(a$0) {} |
183 | "#, | 187 | "#, |
184 | expect![[r#" | 188 | expect![[r#" |
185 | bn Foo Foo { bar$1, baz$2 }: Foo$0 | 189 | bn Foo Foo { bar$1, baz$2 }: Foo$0 |
@@ -196,7 +200,7 @@ struct Foo { bar: String, baz: String } | |||
196 | struct Bar(String, String); | 200 | struct Bar(String, String); |
197 | struct Baz; | 201 | struct Baz; |
198 | fn outer() { | 202 | fn outer() { |
199 | let $0 | 203 | let a$0 |
200 | } | 204 | } |
201 | "#, | 205 | "#, |
202 | expect![[r#" | 206 | expect![[r#" |
@@ -215,7 +219,7 @@ struct Bar(String, String); | |||
215 | struct Baz; | 219 | struct Baz; |
216 | fn outer() { | 220 | fn outer() { |
217 | match () { | 221 | match () { |
218 | $0 | 222 | a$0 |
219 | } | 223 | } |
220 | } | 224 | } |
221 | "#, | 225 | "#, |
@@ -239,7 +243,7 @@ use foo::*; | |||
239 | 243 | ||
240 | fn outer() { | 244 | fn outer() { |
241 | match () { | 245 | match () { |
242 | $0 | 246 | a$0 |
243 | } | 247 | } |
244 | } | 248 | } |
245 | "#, | 249 | "#, |
@@ -258,7 +262,7 @@ fn outer() { | |||
258 | struct Foo(i32); | 262 | struct Foo(i32); |
259 | fn main() { | 263 | fn main() { |
260 | match Foo(92) { | 264 | match Foo(92) { |
261 | $0(92) => (), | 265 | a$0(92) => (), |
262 | } | 266 | } |
263 | } | 267 | } |
264 | "#, | 268 | "#, |
@@ -281,7 +285,7 @@ struct Foo(i32); | |||
281 | impl Foo { | 285 | impl Foo { |
282 | fn foo() { | 286 | fn foo() { |
283 | match () { | 287 | match () { |
284 | $0 | 288 | a$0 |
285 | } | 289 | } |
286 | } | 290 | } |
287 | } | 291 | } |
@@ -314,4 +318,86 @@ impl Foo { | |||
314 | "#]], | 318 | "#]], |
315 | ) | 319 | ) |
316 | } | 320 | } |
321 | |||
322 | #[test] | ||
323 | fn completes_enum_variant_matcharm() { | ||
324 | check( | ||
325 | r#" | ||
326 | enum Foo { Bar, Baz, Quux } | ||
327 | |||
328 | fn main() { | ||
329 | let foo = Foo::Quux; | ||
330 | match foo { Qu$0 } | ||
331 | } | ||
332 | "#, | ||
333 | expect![[r#" | ||
334 | ev Foo::Bar () | ||
335 | ev Foo::Baz () | ||
336 | ev Foo::Quux () | ||
337 | en Foo | ||
338 | "#]], | ||
339 | ) | ||
340 | } | ||
341 | |||
342 | #[test] | ||
343 | fn completes_enum_variant_matcharm_ref() { | ||
344 | check( | ||
345 | r#" | ||
346 | enum Foo { Bar, Baz, Quux } | ||
347 | |||
348 | fn main() { | ||
349 | let foo = Foo::Quux; | ||
350 | match &foo { Qu$0 } | ||
351 | } | ||
352 | "#, | ||
353 | expect![[r#" | ||
354 | ev Foo::Bar () | ||
355 | ev Foo::Baz () | ||
356 | ev Foo::Quux () | ||
357 | en Foo | ||
358 | "#]], | ||
359 | ) | ||
360 | } | ||
361 | |||
362 | #[test] | ||
363 | fn completes_enum_variant_iflet() { | ||
364 | check( | ||
365 | r#" | ||
366 | enum Foo { Bar, Baz, Quux } | ||
367 | |||
368 | fn main() { | ||
369 | let foo = Foo::Quux; | ||
370 | if let Qu$0 = foo { } | ||
371 | } | ||
372 | "#, | ||
373 | expect![[r#" | ||
374 | ev Foo::Bar () | ||
375 | ev Foo::Baz () | ||
376 | ev Foo::Quux () | ||
377 | en Foo | ||
378 | "#]], | ||
379 | ) | ||
380 | } | ||
381 | |||
382 | #[test] | ||
383 | fn completes_enum_variant_impl() { | ||
384 | check( | ||
385 | r#" | ||
386 | enum Foo { Bar, Baz, Quux } | ||
387 | impl Foo { | ||
388 | fn foo() { match Foo::Bar { Q$0 } } | ||
389 | } | ||
390 | "#, | ||
391 | expect![[r#" | ||
392 | ev Self::Bar () | ||
393 | ev Self::Baz () | ||
394 | ev Self::Quux () | ||
395 | ev Foo::Bar () | ||
396 | ev Foo::Baz () | ||
397 | ev Foo::Quux () | ||
398 | sp Self | ||
399 | en Foo | ||
400 | "#]], | ||
401 | ) | ||
402 | } | ||
317 | } | 403 | } |
diff --git a/crates/ide_completion/src/completions/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs index 3afc63021..cee4eec10 100644 --- a/crates/ide_completion/src/completions/postfix/format_like.rs +++ b/crates/ide_completion/src/completions/postfix/format_like.rs | |||
@@ -59,7 +59,7 @@ pub(crate) fn add_format_like_completions( | |||
59 | /// Checks whether provided item is a string literal. | 59 | /// Checks whether provided item is a string literal. |
60 | fn string_literal_contents(item: &ast::String) -> Option<String> { | 60 | fn string_literal_contents(item: &ast::String) -> Option<String> { |
61 | let item = item.text(); | 61 | let item = item.text(); |
62 | if item.len() >= 2 && item.starts_with("\"") && item.ends_with("\"") { | 62 | if item.len() >= 2 && item.starts_with('\"') && item.ends_with('\"') { |
63 | return Some(item[1..item.len() - 1].to_owned()); | 63 | return Some(item[1..item.len() - 1].to_owned()); |
64 | } | 64 | } |
65 | 65 | ||
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index e4bf4a043..1b8b063e7 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -6,7 +6,7 @@ use syntax::AstNode; | |||
6 | use crate::{CompletionContext, Completions}; | 6 | use crate::{CompletionContext, Completions}; |
7 | 7 | ||
8 | pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 8 | pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
9 | if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { | 9 | if !ctx.is_trivial_path { |
10 | return; | 10 | return; |
11 | } | 11 | } |
12 | if ctx.record_lit_syntax.is_some() | 12 | if ctx.record_lit_syntax.is_some() |
@@ -23,10 +23,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
23 | }); | 23 | }); |
24 | } | 24 | } |
25 | 25 | ||
26 | if ctx.is_pat_binding_or_const { | ||
27 | return; | ||
28 | } | ||
29 | |||
30 | ctx.scope.process_all_names(&mut |name, res| { | 26 | ctx.scope.process_all_names(&mut |name, res| { |
31 | if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { | 27 | if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { |
32 | cov_mark::hit!(skip_lifetime_completion); | 28 | cov_mark::hit!(skip_lifetime_completion); |
@@ -609,66 +605,6 @@ fn main() { $0 } | |||
609 | } | 605 | } |
610 | 606 | ||
611 | #[test] | 607 | #[test] |
612 | fn completes_enum_variant_matcharm() { | ||
613 | check( | ||
614 | r#" | ||
615 | enum Foo { Bar, Baz, Quux } | ||
616 | |||
617 | fn main() { | ||
618 | let foo = Foo::Quux; | ||
619 | match foo { Qu$0 } | ||
620 | } | ||
621 | "#, | ||
622 | expect![[r#" | ||
623 | ev Foo::Bar () | ||
624 | ev Foo::Baz () | ||
625 | ev Foo::Quux () | ||
626 | en Foo | ||
627 | "#]], | ||
628 | ) | ||
629 | } | ||
630 | |||
631 | #[test] | ||
632 | fn completes_enum_variant_matcharm_ref() { | ||
633 | check( | ||
634 | r#" | ||
635 | enum Foo { Bar, Baz, Quux } | ||
636 | |||
637 | fn main() { | ||
638 | let foo = Foo::Quux; | ||
639 | match &foo { Qu$0 } | ||
640 | } | ||
641 | "#, | ||
642 | expect![[r#" | ||
643 | ev Foo::Bar () | ||
644 | ev Foo::Baz () | ||
645 | ev Foo::Quux () | ||
646 | en Foo | ||
647 | "#]], | ||
648 | ) | ||
649 | } | ||
650 | |||
651 | #[test] | ||
652 | fn completes_enum_variant_iflet() { | ||
653 | check( | ||
654 | r#" | ||
655 | enum Foo { Bar, Baz, Quux } | ||
656 | |||
657 | fn main() { | ||
658 | let foo = Foo::Quux; | ||
659 | if let Qu$0 = foo { } | ||
660 | } | ||
661 | "#, | ||
662 | expect![[r#" | ||
663 | ev Foo::Bar () | ||
664 | ev Foo::Baz () | ||
665 | ev Foo::Quux () | ||
666 | en Foo | ||
667 | "#]], | ||
668 | ) | ||
669 | } | ||
670 | |||
671 | #[test] | ||
672 | fn completes_enum_variant_basic_expr() { | 608 | fn completes_enum_variant_basic_expr() { |
673 | check( | 609 | check( |
674 | r#" | 610 | r#" |
@@ -701,28 +637,6 @@ fn f() -> m::E { V$0 } | |||
701 | } | 637 | } |
702 | 638 | ||
703 | #[test] | 639 | #[test] |
704 | fn completes_enum_variant_impl() { | ||
705 | check( | ||
706 | r#" | ||
707 | enum Foo { Bar, Baz, Quux } | ||
708 | impl Foo { | ||
709 | fn foo() { match Foo::Bar { Q$0 } } | ||
710 | } | ||
711 | "#, | ||
712 | expect![[r#" | ||
713 | ev Self::Bar () | ||
714 | ev Self::Baz () | ||
715 | ev Self::Quux () | ||
716 | ev Foo::Bar () | ||
717 | ev Foo::Baz () | ||
718 | ev Foo::Quux () | ||
719 | sp Self | ||
720 | en Foo | ||
721 | "#]], | ||
722 | ) | ||
723 | } | ||
724 | |||
725 | #[test] | ||
726 | fn dont_complete_attr() { | 640 | fn dont_complete_attr() { |
727 | check( | 641 | check( |
728 | r#" | 642 | r#" |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 17d9a3adf..e6cc6329c 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -35,6 +35,7 @@ pub(crate) struct CompletionContext<'a> { | |||
35 | /// The token before the cursor, in the macro-expanded file. | 35 | /// The token before the cursor, in the macro-expanded file. |
36 | pub(super) token: SyntaxToken, | 36 | pub(super) token: SyntaxToken, |
37 | pub(super) krate: Option<hir::Crate>, | 37 | pub(super) krate: Option<hir::Crate>, |
38 | pub(super) expected_name: Option<String>, | ||
38 | pub(super) expected_type: Option<Type>, | 39 | pub(super) expected_type: Option<Type>, |
39 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 40 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
40 | pub(super) function_syntax: Option<ast::Fn>, | 41 | pub(super) function_syntax: Option<ast::Fn>, |
@@ -135,6 +136,7 @@ impl<'a> CompletionContext<'a> { | |||
135 | original_token, | 136 | original_token, |
136 | token, | 137 | token, |
137 | krate, | 138 | krate, |
139 | expected_name: None, | ||
138 | expected_type: None, | 140 | expected_type: None, |
139 | name_ref_syntax: None, | 141 | name_ref_syntax: None, |
140 | function_syntax: None, | 142 | function_syntax: None, |
@@ -290,23 +292,95 @@ impl<'a> CompletionContext<'a> { | |||
290 | file_with_fake_ident: SyntaxNode, | 292 | file_with_fake_ident: SyntaxNode, |
291 | offset: TextSize, | 293 | offset: TextSize, |
292 | ) { | 294 | ) { |
293 | // FIXME: this is wrong in at least two cases: | 295 | let expected = { |
294 | // * when there's no token `foo($0)` | 296 | let mut node = self.token.parent(); |
295 | // * when there is a token, but it happens to have type of it's own | 297 | loop { |
296 | self.expected_type = self | 298 | let ret = match_ast! { |
297 | .token | ||
298 | .ancestors() | ||
299 | .find_map(|node| { | ||
300 | let ty = match_ast! { | ||
301 | match node { | 299 | match node { |
302 | ast::Pat(it) => self.sema.type_of_pat(&it), | 300 | ast::LetStmt(it) => { |
303 | ast::Expr(it) => self.sema.type_of_expr(&it), | 301 | cov_mark::hit!(expected_type_let_with_leading_char); |
304 | _ => return None, | 302 | cov_mark::hit!(expected_type_let_without_leading_char); |
303 | let ty = it.pat() | ||
304 | .and_then(|pat| self.sema.type_of_pat(&pat)); | ||
305 | let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() { | ||
306 | Some(ident.syntax().text().to_string()) | ||
307 | } else { | ||
308 | None | ||
309 | }; | ||
310 | |||
311 | (ty, name) | ||
312 | }, | ||
313 | ast::ArgList(it) => { | ||
314 | cov_mark::hit!(expected_type_fn_param_with_leading_char); | ||
315 | cov_mark::hit!(expected_type_fn_param_without_leading_char); | ||
316 | ActiveParameter::at_token( | ||
317 | &self.sema, | ||
318 | self.token.clone(), | ||
319 | ).map(|ap| (Some(ap.ty), Some(ap.name))) | ||
320 | .unwrap_or((None, None)) | ||
321 | }, | ||
322 | ast::RecordExprFieldList(it) => { | ||
323 | cov_mark::hit!(expected_type_struct_field_without_leading_char); | ||
324 | self.token.prev_sibling_or_token() | ||
325 | .and_then(|se| se.into_node()) | ||
326 | .and_then(|node| ast::RecordExprField::cast(node)) | ||
327 | .and_then(|rf| self.sema.resolve_record_field(&rf)) | ||
328 | .map(|f|( | ||
329 | Some(f.0.signature_ty(self.db)), | ||
330 | Some(f.0.name(self.db).to_string()), | ||
331 | )) | ||
332 | .unwrap_or((None, None)) | ||
333 | }, | ||
334 | ast::RecordExprField(it) => { | ||
335 | cov_mark::hit!(expected_type_struct_field_with_leading_char); | ||
336 | self.sema | ||
337 | .resolve_record_field(&it) | ||
338 | .map(|f|( | ||
339 | Some(f.0.signature_ty(self.db)), | ||
340 | Some(f.0.name(self.db).to_string()), | ||
341 | )) | ||
342 | .unwrap_or((None, None)) | ||
343 | }, | ||
344 | ast::MatchExpr(it) => { | ||
345 | cov_mark::hit!(expected_type_match_arm_without_leading_char); | ||
346 | let ty = it.expr() | ||
347 | .and_then(|e| self.sema.type_of_expr(&e)); | ||
348 | |||
349 | (ty, None) | ||
350 | }, | ||
351 | ast::IdentPat(it) => { | ||
352 | cov_mark::hit!(expected_type_if_let_with_leading_char); | ||
353 | cov_mark::hit!(expected_type_match_arm_with_leading_char); | ||
354 | let ty = self.sema.type_of_pat(&ast::Pat::from(it)); | ||
355 | |||
356 | (ty, None) | ||
357 | }, | ||
358 | ast::Fn(it) => { | ||
359 | cov_mark::hit!(expected_type_fn_ret_with_leading_char); | ||
360 | cov_mark::hit!(expected_type_fn_ret_without_leading_char); | ||
361 | let ty = self.token.ancestors() | ||
362 | .find_map(|ancestor| ast::Expr::cast(ancestor)) | ||
363 | .and_then(|expr| self.sema.type_of_expr(&expr)); | ||
364 | |||
365 | (ty, None) | ||
366 | }, | ||
367 | _ => { | ||
368 | match node.parent() { | ||
369 | Some(n) => { | ||
370 | node = n; | ||
371 | continue; | ||
372 | }, | ||
373 | None => (None, None), | ||
374 | } | ||
375 | }, | ||
305 | } | 376 | } |
306 | }; | 377 | }; |
307 | Some(ty) | 378 | |
308 | }) | 379 | break ret; |
309 | .flatten(); | 380 | } |
381 | }; | ||
382 | self.expected_type = expected.0; | ||
383 | self.expected_name = expected.1; | ||
310 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); | 384 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); |
311 | 385 | ||
312 | // First, let's try to complete a reference to some declaration. | 386 | // First, let's try to complete a reference to some declaration. |
@@ -535,3 +609,196 @@ fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<ast::Path> { | |||
535 | let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?; | 609 | let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?; |
536 | use_tree.path() | 610 | use_tree.path() |
537 | } | 611 | } |
612 | |||
613 | #[cfg(test)] | ||
614 | mod tests { | ||
615 | use expect_test::{expect, Expect}; | ||
616 | use hir::HirDisplay; | ||
617 | |||
618 | use crate::test_utils::{position, TEST_CONFIG}; | ||
619 | |||
620 | use super::CompletionContext; | ||
621 | |||
622 | fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) { | ||
623 | let (db, pos) = position(ra_fixture); | ||
624 | let completion_context = CompletionContext::new(&db, pos, &TEST_CONFIG).unwrap(); | ||
625 | |||
626 | let ty = completion_context | ||
627 | .expected_type | ||
628 | .map(|t| t.display_test(&db).to_string()) | ||
629 | .unwrap_or("?".to_owned()); | ||
630 | |||
631 | let name = completion_context.expected_name.unwrap_or("?".to_owned()); | ||
632 | |||
633 | expect.assert_eq(&format!("ty: {}, name: {}", ty, name)); | ||
634 | } | ||
635 | |||
636 | #[test] | ||
637 | fn expected_type_let_without_leading_char() { | ||
638 | cov_mark::check!(expected_type_let_without_leading_char); | ||
639 | check_expected_type_and_name( | ||
640 | r#" | ||
641 | fn foo() { | ||
642 | let x: u32 = $0; | ||
643 | } | ||
644 | "#, | ||
645 | expect![[r#"ty: u32, name: x"#]], | ||
646 | ); | ||
647 | } | ||
648 | |||
649 | #[test] | ||
650 | fn expected_type_let_with_leading_char() { | ||
651 | cov_mark::check!(expected_type_let_with_leading_char); | ||
652 | check_expected_type_and_name( | ||
653 | r#" | ||
654 | fn foo() { | ||
655 | let x: u32 = c$0; | ||
656 | } | ||
657 | "#, | ||
658 | expect![[r#"ty: u32, name: x"#]], | ||
659 | ); | ||
660 | } | ||
661 | |||
662 | #[test] | ||
663 | fn expected_type_fn_param_without_leading_char() { | ||
664 | cov_mark::check!(expected_type_fn_param_without_leading_char); | ||
665 | check_expected_type_and_name( | ||
666 | r#" | ||
667 | fn foo() { | ||
668 | bar($0); | ||
669 | } | ||
670 | |||
671 | fn bar(x: u32) {} | ||
672 | "#, | ||
673 | expect![[r#"ty: u32, name: x"#]], | ||
674 | ); | ||
675 | } | ||
676 | |||
677 | #[test] | ||
678 | fn expected_type_fn_param_with_leading_char() { | ||
679 | cov_mark::check!(expected_type_fn_param_with_leading_char); | ||
680 | check_expected_type_and_name( | ||
681 | r#" | ||
682 | fn foo() { | ||
683 | bar(c$0); | ||
684 | } | ||
685 | |||
686 | fn bar(x: u32) {} | ||
687 | "#, | ||
688 | expect![[r#"ty: u32, name: x"#]], | ||
689 | ); | ||
690 | } | ||
691 | |||
692 | #[test] | ||
693 | fn expected_type_struct_field_without_leading_char() { | ||
694 | cov_mark::check!(expected_type_struct_field_without_leading_char); | ||
695 | check_expected_type_and_name( | ||
696 | r#" | ||
697 | struct Foo { a: u32 } | ||
698 | fn foo() { | ||
699 | Foo { a: $0 }; | ||
700 | } | ||
701 | "#, | ||
702 | expect![[r#"ty: u32, name: a"#]], | ||
703 | ) | ||
704 | } | ||
705 | |||
706 | #[test] | ||
707 | fn expected_type_struct_field_with_leading_char() { | ||
708 | cov_mark::check!(expected_type_struct_field_with_leading_char); | ||
709 | check_expected_type_and_name( | ||
710 | r#" | ||
711 | struct Foo { a: u32 } | ||
712 | fn foo() { | ||
713 | Foo { a: c$0 }; | ||
714 | } | ||
715 | "#, | ||
716 | expect![[r#"ty: u32, name: a"#]], | ||
717 | ); | ||
718 | } | ||
719 | |||
720 | #[test] | ||
721 | fn expected_type_match_arm_without_leading_char() { | ||
722 | cov_mark::check!(expected_type_match_arm_without_leading_char); | ||
723 | check_expected_type_and_name( | ||
724 | r#" | ||
725 | enum E { X } | ||
726 | fn foo() { | ||
727 | match E::X { $0 } | ||
728 | } | ||
729 | "#, | ||
730 | expect![[r#"ty: E, name: ?"#]], | ||
731 | ); | ||
732 | } | ||
733 | |||
734 | #[test] | ||
735 | fn expected_type_match_arm_with_leading_char() { | ||
736 | cov_mark::check!(expected_type_match_arm_with_leading_char); | ||
737 | check_expected_type_and_name( | ||
738 | r#" | ||
739 | enum E { X } | ||
740 | fn foo() { | ||
741 | match E::X { c$0 } | ||
742 | } | ||
743 | "#, | ||
744 | expect![[r#"ty: E, name: ?"#]], | ||
745 | ); | ||
746 | } | ||
747 | |||
748 | #[test] | ||
749 | fn expected_type_if_let_without_leading_char() { | ||
750 | check_expected_type_and_name( | ||
751 | r#" | ||
752 | enum Foo { Bar, Baz, Quux } | ||
753 | |||
754 | fn foo() { | ||
755 | let f = Foo::Quux; | ||
756 | if let $0 = f { } | ||
757 | } | ||
758 | "#, | ||
759 | expect![[r#"ty: (), name: ?"#]], | ||
760 | ) // FIXME should be `ty: u32, name: ?` | ||
761 | } | ||
762 | |||
763 | #[test] | ||
764 | fn expected_type_if_let_with_leading_char() { | ||
765 | cov_mark::check!(expected_type_if_let_with_leading_char); | ||
766 | check_expected_type_and_name( | ||
767 | r#" | ||
768 | enum Foo { Bar, Baz, Quux } | ||
769 | |||
770 | fn foo() { | ||
771 | let f = Foo::Quux; | ||
772 | if let c$0 = f { } | ||
773 | } | ||
774 | "#, | ||
775 | expect![[r#"ty: Foo, name: ?"#]], | ||
776 | ) | ||
777 | } | ||
778 | |||
779 | #[test] | ||
780 | fn expected_type_fn_ret_without_leading_char() { | ||
781 | cov_mark::check!(expected_type_fn_ret_without_leading_char); | ||
782 | check_expected_type_and_name( | ||
783 | r#" | ||
784 | fn foo() -> u32 { | ||
785 | $0 | ||
786 | } | ||
787 | "#, | ||
788 | expect![[r#"ty: (), name: ?"#]], | ||
789 | ) // FIXME this should be `ty: u32, name: ?` | ||
790 | } | ||
791 | |||
792 | #[test] | ||
793 | fn expected_type_fn_ret_with_leading_char() { | ||
794 | cov_mark::check!(expected_type_fn_ret_with_leading_char); | ||
795 | check_expected_type_and_name( | ||
796 | r#" | ||
797 | fn foo() -> u32 { | ||
798 | c$0 | ||
799 | } | ||
800 | "#, | ||
801 | expect![[r#"ty: u32, name: ?"#]], | ||
802 | ) | ||
803 | } | ||
804 | } | ||
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs index 3febab32b..9a4b5217a 100644 --- a/crates/ide_completion/src/item.rs +++ b/crates/ide_completion/src/item.rs | |||
@@ -144,6 +144,21 @@ pub struct CompletionRelevance { | |||
144 | /// } | 144 | /// } |
145 | /// ``` | 145 | /// ``` |
146 | pub exact_type_match: bool, | 146 | pub exact_type_match: bool, |
147 | /// This is set in cases like these: | ||
148 | /// | ||
149 | /// ``` | ||
150 | /// fn foo(bar: u32) { | ||
151 | /// $0 // `bar` is local | ||
152 | /// } | ||
153 | /// ``` | ||
154 | /// | ||
155 | /// ``` | ||
156 | /// fn foo() { | ||
157 | /// let bar = 0; | ||
158 | /// $0 // `bar` is local | ||
159 | /// } | ||
160 | /// ``` | ||
161 | pub is_local: bool, | ||
147 | } | 162 | } |
148 | 163 | ||
149 | impl CompletionRelevance { | 164 | impl CompletionRelevance { |
@@ -163,6 +178,9 @@ impl CompletionRelevance { | |||
163 | score += 1; | 178 | score += 1; |
164 | } | 179 | } |
165 | if self.exact_type_match { | 180 | if self.exact_type_match { |
181 | score += 3; | ||
182 | } | ||
183 | if self.is_local { | ||
166 | score += 1; | 184 | score += 1; |
167 | } | 185 | } |
168 | 186 | ||
@@ -551,9 +569,24 @@ mod tests { | |||
551 | vec![CompletionRelevance::default()], | 569 | vec![CompletionRelevance::default()], |
552 | vec![ | 570 | vec![ |
553 | CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() }, | 571 | CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() }, |
554 | CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }, | 572 | CompletionRelevance { is_local: true, ..CompletionRelevance::default() }, |
555 | ], | 573 | ], |
556 | vec![CompletionRelevance { exact_name_match: true, exact_type_match: true }], | 574 | vec![CompletionRelevance { |
575 | exact_name_match: true, | ||
576 | is_local: true, | ||
577 | ..CompletionRelevance::default() | ||
578 | }], | ||
579 | vec![CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }], | ||
580 | vec![CompletionRelevance { | ||
581 | exact_name_match: true, | ||
582 | exact_type_match: true, | ||
583 | ..CompletionRelevance::default() | ||
584 | }], | ||
585 | vec![CompletionRelevance { | ||
586 | exact_name_match: true, | ||
587 | exact_type_match: true, | ||
588 | is_local: true, | ||
589 | }], | ||
557 | ]; | 590 | ]; |
558 | 591 | ||
559 | check_relevance_score_ordered(expected_relevance_order); | 592 | check_relevance_score_ordered(expected_relevance_order); |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index d9bf52582..36655667c 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -20,8 +20,8 @@ use ide_db::{ | |||
20 | use syntax::TextRange; | 20 | use syntax::TextRange; |
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | item::{CompletionRelevance, ImportEdit}, | 23 | item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, |
24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, | 24 | CompletionRelevance, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; | 27 | use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; |
@@ -116,19 +116,6 @@ impl<'a> RenderContext<'a> { | |||
116 | fn docs(&self, node: impl HasAttrs) -> Option<Documentation> { | 116 | fn docs(&self, node: impl HasAttrs) -> Option<Documentation> { |
117 | node.docs(self.db()) | 117 | node.docs(self.db()) |
118 | } | 118 | } |
119 | |||
120 | fn expected_name_and_type(&self) -> Option<(String, Type)> { | ||
121 | if let Some(record_field) = &self.completion.record_field_syntax { | ||
122 | cov_mark::hit!(record_field_type_match); | ||
123 | let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; | ||
124 | Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db()))) | ||
125 | } else if let Some(active_parameter) = &self.completion.active_parameter { | ||
126 | cov_mark::hit!(active_param_type_match); | ||
127 | Some((active_parameter.name.clone(), active_parameter.ty.clone())) | ||
128 | } else { | ||
129 | None | ||
130 | } | ||
131 | } | ||
132 | } | 119 | } |
133 | 120 | ||
134 | /// Generic renderer for completion items. | 121 | /// Generic renderer for completion items. |
@@ -155,8 +142,14 @@ impl<'a> Render<'a> { | |||
155 | .set_documentation(field.docs(self.ctx.db())) | 142 | .set_documentation(field.docs(self.ctx.db())) |
156 | .set_deprecated(is_deprecated); | 143 | .set_deprecated(is_deprecated); |
157 | 144 | ||
158 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) { | 145 | item.set_relevance(CompletionRelevance { |
159 | item.set_relevance(relevance); | 146 | exact_type_match: compute_exact_type_match(self.ctx.completion, ty), |
147 | exact_name_match: compute_exact_name_match(self.ctx.completion, name.to_string()), | ||
148 | ..CompletionRelevance::default() | ||
149 | }); | ||
150 | |||
151 | if let Some(ref_match) = compute_ref_match(self.ctx.completion, ty) { | ||
152 | item.ref_match(ref_match); | ||
160 | } | 153 | } |
161 | 154 | ||
162 | item.build() | 155 | item.build() |
@@ -247,27 +240,18 @@ impl<'a> Render<'a> { | |||
247 | if !ty.is_unknown() { | 240 | if !ty.is_unknown() { |
248 | item.detail(ty.display(self.ctx.db()).to_string()); | 241 | item.detail(ty.display(self.ctx.db()).to_string()); |
249 | } | 242 | } |
250 | }; | ||
251 | 243 | ||
252 | if let ScopeDef::Local(local) = resolution { | 244 | item.set_relevance(CompletionRelevance { |
253 | let ty = local.ty(self.ctx.db()); | 245 | exact_type_match: compute_exact_type_match(self.ctx.completion, &ty), |
254 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) { | 246 | exact_name_match: compute_exact_name_match(self.ctx.completion, local_name.clone()), |
255 | item.set_relevance(relevance); | 247 | is_local: true, |
256 | } | 248 | ..CompletionRelevance::default() |
257 | if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { | 249 | }); |
258 | if let Some(ty_without_ref) = expected_type.remove_ref() { | 250 | |
259 | if ty_without_ref == ty { | 251 | if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ty) { |
260 | cov_mark::hit!(suggest_ref); | 252 | item.ref_match(ref_match); |
261 | let mutability = if expected_type.is_mutable_reference() { | ||
262 | Mutability::Mut | ||
263 | } else { | ||
264 | Mutability::Shared | ||
265 | }; | ||
266 | item.ref_match(mutability); | ||
267 | } | ||
268 | } | ||
269 | } | 253 | } |
270 | } | 254 | }; |
271 | 255 | ||
272 | // Add `<>` for generic types | 256 | // Add `<>` for generic types |
273 | if self.ctx.completion.is_path_type | 257 | if self.ctx.completion.is_path_type |
@@ -322,19 +306,50 @@ impl<'a> Render<'a> { | |||
322 | } | 306 | } |
323 | } | 307 | } |
324 | 308 | ||
325 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionRelevance> { | 309 | fn compute_exact_type_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> bool { |
326 | let (expected_name, expected_type) = ctx.expected_name_and_type()?; | 310 | if let Some(expected_type) = ctx.expected_type.as_ref() { |
327 | let mut res = CompletionRelevance::default(); | 311 | // We don't ever consider unit type to be an exact type match, since |
328 | res.exact_type_match = ty == &expected_type; | 312 | // nearly always this is not meaningful to the user. |
329 | res.exact_name_match = name == &expected_name; | 313 | completion_ty == expected_type && !expected_type.is_unit() |
330 | Some(res) | 314 | } else { |
315 | false | ||
316 | } | ||
317 | } | ||
318 | |||
319 | fn compute_exact_name_match(ctx: &CompletionContext, completion_name: impl Into<String>) -> bool { | ||
320 | let completion_name = completion_name.into(); | ||
321 | |||
322 | Some(&completion_name) == ctx.expected_name.as_ref() | ||
323 | } | ||
324 | |||
325 | fn compute_ref_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> Option<Mutability> { | ||
326 | let mut ref_match = None; | ||
327 | if let Some(expected_type) = &ctx.expected_type { | ||
328 | if completion_ty != expected_type { | ||
329 | if let Some(expected_type_without_ref) = expected_type.remove_ref() { | ||
330 | if completion_ty == &expected_type_without_ref | ||
331 | || completion_ty | ||
332 | .autoderef(ctx.db) | ||
333 | .any(|deref_ty| deref_ty == expected_type_without_ref) | ||
334 | { | ||
335 | cov_mark::hit!(suggest_ref); | ||
336 | let mutability = if expected_type.is_mutable_reference() { | ||
337 | Mutability::Mut | ||
338 | } else { | ||
339 | Mutability::Shared | ||
340 | }; | ||
341 | ref_match = Some(mutability); | ||
342 | } | ||
343 | } | ||
344 | } | ||
345 | }; | ||
346 | ref_match | ||
331 | } | 347 | } |
332 | 348 | ||
333 | #[cfg(test)] | 349 | #[cfg(test)] |
334 | mod tests { | 350 | mod tests { |
335 | use std::cmp::Reverse; | ||
336 | |||
337 | use expect_test::{expect, Expect}; | 351 | use expect_test::{expect, Expect}; |
352 | use itertools::Itertools; | ||
338 | 353 | ||
339 | use crate::{ | 354 | use crate::{ |
340 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, | 355 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, |
@@ -347,28 +362,40 @@ mod tests { | |||
347 | } | 362 | } |
348 | 363 | ||
349 | fn check_relevance(ra_fixture: &str, expect: Expect) { | 364 | fn check_relevance(ra_fixture: &str, expect: Expect) { |
350 | fn display_relevance(relevance: CompletionRelevance) -> &'static str { | 365 | fn display_relevance(relevance: CompletionRelevance) -> String { |
351 | match relevance { | 366 | let relevance_factors = vec![ |
352 | CompletionRelevance { exact_type_match: true, exact_name_match: true } => { | 367 | (relevance.exact_type_match, "type"), |
353 | "[type+name]" | 368 | (relevance.exact_name_match, "name"), |
354 | } | 369 | (relevance.is_local, "local"), |
355 | CompletionRelevance { exact_type_match: true, exact_name_match: false } => "[type]", | 370 | ] |
356 | CompletionRelevance { exact_type_match: false, exact_name_match: true } => "[name]", | 371 | .into_iter() |
357 | CompletionRelevance { exact_type_match: false, exact_name_match: false } => "[]", | 372 | .filter_map(|(cond, desc)| if cond { Some(desc) } else { None }) |
358 | } | 373 | .join("+"); |
374 | |||
375 | format!("[{}]", relevance_factors) | ||
359 | } | 376 | } |
360 | 377 | ||
361 | let mut completions = get_all_items(TEST_CONFIG, ra_fixture); | 378 | let actual = get_all_items(TEST_CONFIG, ra_fixture) |
362 | completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string())); | ||
363 | let actual = completions | ||
364 | .into_iter() | 379 | .into_iter() |
365 | .filter(|it| it.completion_kind == CompletionKind::Reference) | 380 | .filter(|it| it.completion_kind == CompletionKind::Reference) |
366 | .map(|it| { | 381 | .flat_map(|it| { |
382 | let mut items = vec![]; | ||
383 | |||
367 | let tag = it.kind().unwrap().tag(); | 384 | let tag = it.kind().unwrap().tag(); |
368 | let relevance = display_relevance(it.relevance()); | 385 | let relevance = display_relevance(it.relevance()); |
369 | format!("{} {} {}\n", tag, it.label(), relevance) | 386 | items.push(format!("{} {} {}\n", tag, it.label(), relevance)); |
387 | |||
388 | if let Some((mutability, relevance)) = it.ref_match() { | ||
389 | let label = format!("&{}{}", mutability.as_keyword_for_ref(), it.label()); | ||
390 | let relevance = display_relevance(relevance); | ||
391 | |||
392 | items.push(format!("{} {} {}\n", tag, label, relevance)); | ||
393 | } | ||
394 | |||
395 | items | ||
370 | }) | 396 | }) |
371 | .collect::<String>(); | 397 | .collect::<String>(); |
398 | |||
372 | expect.assert_eq(&actual); | 399 | expect.assert_eq(&actual); |
373 | } | 400 | } |
374 | 401 | ||
@@ -508,6 +535,11 @@ fn main() { let _: m::Spam = S$0 } | |||
508 | ), | 535 | ), |
509 | lookup: "Spam::Bar", | 536 | lookup: "Spam::Bar", |
510 | detail: "(i32)", | 537 | detail: "(i32)", |
538 | relevance: CompletionRelevance { | ||
539 | exact_name_match: false, | ||
540 | exact_type_match: true, | ||
541 | is_local: false, | ||
542 | }, | ||
511 | trigger_call_info: true, | 543 | trigger_call_info: true, |
512 | }, | 544 | }, |
513 | CompletionItem { | 545 | CompletionItem { |
@@ -529,6 +561,11 @@ fn main() { let _: m::Spam = S$0 } | |||
529 | ), | 561 | ), |
530 | lookup: "Spam::Foo", | 562 | lookup: "Spam::Foo", |
531 | detail: "()", | 563 | detail: "()", |
564 | relevance: CompletionRelevance { | ||
565 | exact_name_match: false, | ||
566 | exact_type_match: true, | ||
567 | is_local: false, | ||
568 | }, | ||
532 | }, | 569 | }, |
533 | CompletionItem { | 570 | CompletionItem { |
534 | label: "main()", | 571 | label: "main()", |
@@ -868,7 +905,6 @@ fn foo(xs: Vec<i128>) | |||
868 | 905 | ||
869 | #[test] | 906 | #[test] |
870 | fn active_param_relevance() { | 907 | fn active_param_relevance() { |
871 | cov_mark::check!(active_param_type_match); | ||
872 | check_relevance( | 908 | check_relevance( |
873 | r#" | 909 | r#" |
874 | struct S { foo: i64, bar: u32, baz: u32 } | 910 | struct S { foo: i64, bar: u32, baz: u32 } |
@@ -876,16 +912,15 @@ fn test(bar: u32) { } | |||
876 | fn foo(s: S) { test(s.$0) } | 912 | fn foo(s: S) { test(s.$0) } |
877 | "#, | 913 | "#, |
878 | expect![[r#" | 914 | expect![[r#" |
915 | fd foo [] | ||
879 | fd bar [type+name] | 916 | fd bar [type+name] |
880 | fd baz [type] | 917 | fd baz [type] |
881 | fd foo [] | ||
882 | "#]], | 918 | "#]], |
883 | ); | 919 | ); |
884 | } | 920 | } |
885 | 921 | ||
886 | #[test] | 922 | #[test] |
887 | fn record_field_relevances() { | 923 | fn record_field_relevances() { |
888 | cov_mark::check!(record_field_type_match); | ||
889 | check_relevance( | 924 | check_relevance( |
890 | r#" | 925 | r#" |
891 | struct A { foo: i64, bar: u32, baz: u32 } | 926 | struct A { foo: i64, bar: u32, baz: u32 } |
@@ -893,9 +928,9 @@ struct B { x: (), y: f32, bar: u32 } | |||
893 | fn foo(a: A) { B { bar: a.$0 }; } | 928 | fn foo(a: A) { B { bar: a.$0 }; } |
894 | "#, | 929 | "#, |
895 | expect![[r#" | 930 | expect![[r#" |
931 | fd foo [] | ||
896 | fd bar [type+name] | 932 | fd bar [type+name] |
897 | fd baz [type] | 933 | fd baz [type] |
898 | fd foo [] | ||
899 | "#]], | 934 | "#]], |
900 | ) | 935 | ) |
901 | } | 936 | } |
@@ -923,9 +958,9 @@ fn f(foo: i64) { } | |||
923 | fn foo(a: A) { f(B { bar: a.$0 }); } | 958 | fn foo(a: A) { f(B { bar: a.$0 }); } |
924 | "#, | 959 | "#, |
925 | expect![[r#" | 960 | expect![[r#" |
961 | fd foo [] | ||
926 | fd bar [type+name] | 962 | fd bar [type+name] |
927 | fd baz [type] | 963 | fd baz [type] |
928 | fd foo [] | ||
929 | "#]], | 964 | "#]], |
930 | ); | 965 | ); |
931 | } | 966 | } |
@@ -938,7 +973,7 @@ struct WorldSnapshot { _f: () }; | |||
938 | fn go(world: &WorldSnapshot) { go(w$0) } | 973 | fn go(world: &WorldSnapshot) { go(w$0) } |
939 | "#, | 974 | "#, |
940 | expect![[r#" | 975 | expect![[r#" |
941 | lc world [type+name] | 976 | lc world [type+name+local] |
942 | st WorldSnapshot [] | 977 | st WorldSnapshot [] |
943 | fn go(…) [] | 978 | fn go(…) [] |
944 | "#]], | 979 | "#]], |
@@ -953,9 +988,69 @@ struct Foo; | |||
953 | fn f(foo: &Foo) { f(foo, w$0) } | 988 | fn f(foo: &Foo) { f(foo, w$0) } |
954 | "#, | 989 | "#, |
955 | expect![[r#" | 990 | expect![[r#" |
991 | lc foo [local] | ||
956 | st Foo [] | 992 | st Foo [] |
957 | fn f(…) [] | 993 | fn f(…) [] |
958 | lc foo [] | 994 | "#]], |
995 | ); | ||
996 | } | ||
997 | |||
998 | #[test] | ||
999 | fn score_fn_type_and_name_match() { | ||
1000 | check_relevance( | ||
1001 | r#" | ||
1002 | struct A { bar: u8 } | ||
1003 | fn baz() -> u8 { 0 } | ||
1004 | fn bar() -> u8 { 0 } | ||
1005 | fn f() { A { bar: b$0 }; } | ||
1006 | "#, | ||
1007 | expect![[r#" | ||
1008 | fn baz() [type] | ||
1009 | st A [] | ||
1010 | fn bar() [type+name] | ||
1011 | fn f() [] | ||
1012 | "#]], | ||
1013 | ); | ||
1014 | } | ||
1015 | |||
1016 | #[test] | ||
1017 | fn score_method_type_and_name_match() { | ||
1018 | check_relevance( | ||
1019 | r#" | ||
1020 | fn baz(aaa: u32){} | ||
1021 | struct Foo; | ||
1022 | impl Foo { | ||
1023 | fn aaa(&self) -> u32 { 0 } | ||
1024 | fn bbb(&self) -> u32 { 0 } | ||
1025 | fn ccc(&self) -> u64 { 0 } | ||
1026 | } | ||
1027 | fn f() { | ||
1028 | baz(Foo.$0 | ||
1029 | } | ||
1030 | "#, | ||
1031 | expect![[r#" | ||
1032 | me aaa() [type+name] | ||
1033 | me bbb() [type] | ||
1034 | me ccc() [] | ||
1035 | "#]], | ||
1036 | ); | ||
1037 | } | ||
1038 | |||
1039 | #[test] | ||
1040 | fn score_method_name_match_only() { | ||
1041 | check_relevance( | ||
1042 | r#" | ||
1043 | fn baz(aaa: u32){} | ||
1044 | struct Foo; | ||
1045 | impl Foo { | ||
1046 | fn aaa(&self) -> u64 { 0 } | ||
1047 | } | ||
1048 | fn f() { | ||
1049 | baz(Foo.$0 | ||
1050 | } | ||
1051 | "#, | ||
1052 | expect![[r#" | ||
1053 | me aaa() [name] | ||
959 | "#]], | 1054 | "#]], |
960 | ); | 1055 | ); |
961 | } | 1056 | } |
@@ -1018,6 +1113,7 @@ fn main() { | |||
1018 | relevance: CompletionRelevance { | 1113 | relevance: CompletionRelevance { |
1019 | exact_name_match: true, | 1114 | exact_name_match: true, |
1020 | exact_type_match: false, | 1115 | exact_type_match: false, |
1116 | is_local: true, | ||
1021 | }, | 1117 | }, |
1022 | ref_match: "&mut ", | 1118 | ref_match: "&mut ", |
1023 | }, | 1119 | }, |
@@ -1025,4 +1121,202 @@ fn main() { | |||
1025 | "#]], | 1121 | "#]], |
1026 | ) | 1122 | ) |
1027 | } | 1123 | } |
1124 | |||
1125 | #[test] | ||
1126 | fn suggest_deref() { | ||
1127 | check_relevance( | ||
1128 | r#" | ||
1129 | #[lang = "deref"] | ||
1130 | trait Deref { | ||
1131 | type Target; | ||
1132 | fn deref(&self) -> &Self::Target; | ||
1133 | } | ||
1134 | |||
1135 | struct S; | ||
1136 | struct T(S); | ||
1137 | |||
1138 | impl Deref for T { | ||
1139 | type Target = S; | ||
1140 | |||
1141 | fn deref(&self) -> &Self::Target { | ||
1142 | &self.0 | ||
1143 | } | ||
1144 | } | ||
1145 | |||
1146 | fn foo(s: &S) {} | ||
1147 | |||
1148 | fn main() { | ||
1149 | let t = T(S); | ||
1150 | let m = 123; | ||
1151 | |||
1152 | foo($0); | ||
1153 | } | ||
1154 | "#, | ||
1155 | expect![[r#" | ||
1156 | lc m [local] | ||
1157 | lc t [local] | ||
1158 | lc &t [type+local] | ||
1159 | st T [] | ||
1160 | st S [] | ||
1161 | fn main() [] | ||
1162 | tt Deref [] | ||
1163 | fn foo(…) [] | ||
1164 | "#]], | ||
1165 | ) | ||
1166 | } | ||
1167 | |||
1168 | #[test] | ||
1169 | fn suggest_deref_mut() { | ||
1170 | check_relevance( | ||
1171 | r#" | ||
1172 | #[lang = "deref"] | ||
1173 | trait Deref { | ||
1174 | type Target; | ||
1175 | fn deref(&self) -> &Self::Target; | ||
1176 | } | ||
1177 | |||
1178 | #[lang = "deref_mut"] | ||
1179 | pub trait DerefMut: Deref { | ||
1180 | fn deref_mut(&mut self) -> &mut Self::Target; | ||
1181 | } | ||
1182 | |||
1183 | struct S; | ||
1184 | struct T(S); | ||
1185 | |||
1186 | impl Deref for T { | ||
1187 | type Target = S; | ||
1188 | |||
1189 | fn deref(&self) -> &Self::Target { | ||
1190 | &self.0 | ||
1191 | } | ||
1192 | } | ||
1193 | |||
1194 | impl DerefMut for T { | ||
1195 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
1196 | &mut self.0 | ||
1197 | } | ||
1198 | } | ||
1199 | |||
1200 | fn foo(s: &mut S) {} | ||
1201 | |||
1202 | fn main() { | ||
1203 | let t = T(S); | ||
1204 | let m = 123; | ||
1205 | |||
1206 | foo($0); | ||
1207 | } | ||
1208 | "#, | ||
1209 | expect![[r#" | ||
1210 | lc m [local] | ||
1211 | lc t [local] | ||
1212 | lc &mut t [type+local] | ||
1213 | tt DerefMut [] | ||
1214 | tt Deref [] | ||
1215 | fn foo(…) [] | ||
1216 | st T [] | ||
1217 | st S [] | ||
1218 | fn main() [] | ||
1219 | "#]], | ||
1220 | ) | ||
1221 | } | ||
1222 | |||
1223 | #[test] | ||
1224 | fn locals() { | ||
1225 | check_relevance( | ||
1226 | r#" | ||
1227 | fn foo(bar: u32) { | ||
1228 | let baz = 0; | ||
1229 | |||
1230 | f$0 | ||
1231 | } | ||
1232 | "#, | ||
1233 | expect![[r#" | ||
1234 | lc baz [local] | ||
1235 | lc bar [local] | ||
1236 | fn foo(…) [] | ||
1237 | "#]], | ||
1238 | ); | ||
1239 | } | ||
1240 | |||
1241 | #[test] | ||
1242 | fn enum_owned() { | ||
1243 | check_relevance( | ||
1244 | r#" | ||
1245 | enum Foo { A, B } | ||
1246 | fn foo() { | ||
1247 | bar($0); | ||
1248 | } | ||
1249 | fn bar(t: Foo) {} | ||
1250 | "#, | ||
1251 | expect![[r#" | ||
1252 | ev Foo::A [type] | ||
1253 | ev Foo::B [type] | ||
1254 | en Foo [] | ||
1255 | fn bar(…) [] | ||
1256 | fn foo() [] | ||
1257 | "#]], | ||
1258 | ); | ||
1259 | } | ||
1260 | |||
1261 | #[test] | ||
1262 | fn enum_ref() { | ||
1263 | check_relevance( | ||
1264 | r#" | ||
1265 | enum Foo { A, B } | ||
1266 | fn foo() { | ||
1267 | bar($0); | ||
1268 | } | ||
1269 | fn bar(t: &Foo) {} | ||
1270 | "#, | ||
1271 | expect![[r#" | ||
1272 | ev Foo::A [] | ||
1273 | ev &Foo::A [type] | ||
1274 | ev Foo::B [] | ||
1275 | ev &Foo::B [type] | ||
1276 | en Foo [] | ||
1277 | fn bar(…) [] | ||
1278 | fn foo() [] | ||
1279 | "#]], | ||
1280 | ); | ||
1281 | } | ||
1282 | |||
1283 | #[test] | ||
1284 | fn suggest_deref_fn_ret() { | ||
1285 | check_relevance( | ||
1286 | r#" | ||
1287 | #[lang = "deref"] | ||
1288 | trait Deref { | ||
1289 | type Target; | ||
1290 | fn deref(&self) -> &Self::Target; | ||
1291 | } | ||
1292 | |||
1293 | struct S; | ||
1294 | struct T(S); | ||
1295 | |||
1296 | impl Deref for T { | ||
1297 | type Target = S; | ||
1298 | |||
1299 | fn deref(&self) -> &Self::Target { | ||
1300 | &self.0 | ||
1301 | } | ||
1302 | } | ||
1303 | |||
1304 | fn foo(s: &S) {} | ||
1305 | fn bar() -> T {} | ||
1306 | |||
1307 | fn main() { | ||
1308 | foo($0); | ||
1309 | } | ||
1310 | "#, | ||
1311 | expect![[r#" | ||
1312 | tt Deref [] | ||
1313 | fn bar() [] | ||
1314 | fn &bar() [type] | ||
1315 | fn foo(…) [] | ||
1316 | st T [] | ||
1317 | st S [] | ||
1318 | fn main() [] | ||
1319 | "#]], | ||
1320 | ) | ||
1321 | } | ||
1028 | } | 1322 | } |
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs index e8cfcc0c7..374247b05 100644 --- a/crates/ide_completion/src/render/enum_variant.rs +++ b/crates/ide_completion/src/render/enum_variant.rs | |||
@@ -6,7 +6,8 @@ use itertools::Itertools; | |||
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | item::{CompletionItem, CompletionKind, ImportEdit}, | 8 | item::{CompletionItem, CompletionKind, ImportEdit}, |
9 | render::{builder_ext::Params, RenderContext}, | 9 | render::{builder_ext::Params, compute_exact_type_match, compute_ref_match, RenderContext}, |
10 | CompletionRelevance, | ||
10 | }; | 11 | }; |
11 | 12 | ||
12 | pub(crate) fn render_variant<'a>( | 13 | pub(crate) fn render_variant<'a>( |
@@ -74,6 +75,16 @@ impl<'a> EnumRender<'a> { | |||
74 | item.lookup_by(self.short_qualified_name); | 75 | item.lookup_by(self.short_qualified_name); |
75 | } | 76 | } |
76 | 77 | ||
78 | let ty = self.variant.parent_enum(self.ctx.completion.db).ty(self.ctx.completion.db); | ||
79 | item.set_relevance(CompletionRelevance { | ||
80 | exact_type_match: compute_exact_type_match(self.ctx.completion, &ty), | ||
81 | ..CompletionRelevance::default() | ||
82 | }); | ||
83 | |||
84 | if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ty) { | ||
85 | item.ref_match(ref_match); | ||
86 | } | ||
87 | |||
77 | item.build() | 88 | item.build() |
78 | } | 89 | } |
79 | 90 | ||
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs index b6b67e7a7..010303182 100644 --- a/crates/ide_completion/src/render/function.rs +++ b/crates/ide_completion/src/render/function.rs | |||
@@ -6,8 +6,11 @@ use itertools::Itertools; | |||
6 | use syntax::ast::Fn; | 6 | use syntax::ast::Fn; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
9 | item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, | 9 | item::{CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, ImportEdit}, |
10 | render::{builder_ext::Params, RenderContext}, | 10 | render::{ |
11 | builder_ext::Params, compute_exact_name_match, compute_exact_type_match, compute_ref_match, | ||
12 | RenderContext, | ||
13 | }, | ||
11 | }; | 14 | }; |
12 | 15 | ||
13 | pub(crate) fn render_fn<'a>( | 16 | pub(crate) fn render_fn<'a>( |
@@ -53,9 +56,20 @@ impl<'a> FunctionRender<'a> { | |||
53 | self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), | 56 | self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), |
54 | ) | 57 | ) |
55 | .detail(self.detail()) | 58 | .detail(self.detail()) |
56 | .add_call_parens(self.ctx.completion, self.name, params) | 59 | .add_call_parens(self.ctx.completion, self.name.clone(), params) |
57 | .add_import(import_to_add); | 60 | .add_import(import_to_add); |
58 | 61 | ||
62 | let ret_type = self.func.ret_type(self.ctx.db()); | ||
63 | item.set_relevance(CompletionRelevance { | ||
64 | exact_type_match: compute_exact_type_match(self.ctx.completion, &ret_type), | ||
65 | exact_name_match: compute_exact_name_match(self.ctx.completion, self.name.clone()), | ||
66 | ..CompletionRelevance::default() | ||
67 | }); | ||
68 | |||
69 | if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ret_type) { | ||
70 | item.ref_match(ref_match); | ||
71 | } | ||
72 | |||
59 | item.build() | 73 | item.build() |
60 | } | 74 | } |
61 | 75 | ||
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs index ca2926125..b4e80f424 100644 --- a/crates/ide_completion/src/render/pattern.rs +++ b/crates/ide_completion/src/render/pattern.rs | |||
@@ -6,24 +6,6 @@ use itertools::Itertools; | |||
6 | 6 | ||
7 | use crate::{item::CompletionKind, render::RenderContext, CompletionItem, CompletionItemKind}; | 7 | use crate::{item::CompletionKind, render::RenderContext, CompletionItem, CompletionItemKind}; |
8 | 8 | ||
9 | fn visible_fields( | ||
10 | ctx: &RenderContext<'_>, | ||
11 | fields: &[hir::Field], | ||
12 | item: impl HasAttrs, | ||
13 | ) -> Option<(Vec<hir::Field>, bool)> { | ||
14 | let module = ctx.completion.scope.module()?; | ||
15 | let n_fields = fields.len(); | ||
16 | let fields = fields | ||
17 | .into_iter() | ||
18 | .filter(|field| field.is_visible_from(ctx.db(), module)) | ||
19 | .copied() | ||
20 | .collect::<Vec<_>>(); | ||
21 | |||
22 | let fields_omitted = | ||
23 | n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists(); | ||
24 | Some((fields, fields_omitted)) | ||
25 | } | ||
26 | |||
27 | pub(crate) fn render_struct_pat( | 9 | pub(crate) fn render_struct_pat( |
28 | ctx: RenderContext<'_>, | 10 | ctx: RenderContext<'_>, |
29 | strukt: hir::Struct, | 11 | strukt: hir::Struct, |
@@ -148,3 +130,21 @@ fn render_tuple_as_pat(fields: &[hir::Field], name: &str, fields_omitted: bool) | |||
148 | name = name | 130 | name = name |
149 | ) | 131 | ) |
150 | } | 132 | } |
133 | |||
134 | fn visible_fields( | ||
135 | ctx: &RenderContext<'_>, | ||
136 | fields: &[hir::Field], | ||
137 | item: impl HasAttrs, | ||
138 | ) -> Option<(Vec<hir::Field>, bool)> { | ||
139 | let module = ctx.completion.scope.module()?; | ||
140 | let n_fields = fields.len(); | ||
141 | let fields = fields | ||
142 | .into_iter() | ||
143 | .filter(|field| field.is_visible_from(ctx.db(), module)) | ||
144 | .copied() | ||
145 | .collect::<Vec<_>>(); | ||
146 | |||
147 | let fields_omitted = | ||
148 | n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists(); | ||
149 | Some((fields, fields_omitted)) | ||
150 | } | ||