aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion')
-rw-r--r--crates/ide_completion/Cargo.toml2
-rw-r--r--crates/ide_completion/src/completions/attribute.rs35
-rw-r--r--crates/ide_completion/src/completions/fn_param.rs7
-rw-r--r--crates/ide_completion/src/completions/keyword.rs44
-rw-r--r--crates/ide_completion/src/completions/mod_.rs6
-rw-r--r--crates/ide_completion/src/completions/pattern.rs116
-rw-r--r--crates/ide_completion/src/completions/postfix.rs7
-rw-r--r--crates/ide_completion/src/completions/postfix/format_like.rs2
-rw-r--r--crates/ide_completion/src/completions/record.rs15
-rw-r--r--crates/ide_completion/src/completions/snippet.rs25
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs32
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs88
-rw-r--r--crates/ide_completion/src/context.rs295
-rw-r--r--crates/ide_completion/src/item.rs188
-rw-r--r--crates/ide_completion/src/lib.rs5
-rw-r--r--crates/ide_completion/src/render.rs355
-rw-r--r--crates/ide_completion/src/render/builder_ext.rs6
-rw-r--r--crates/ide_completion/src/render/const_.rs10
-rw-r--r--crates/ide_completion/src/render/enum_variant.rs21
-rw-r--r--crates/ide_completion/src/render/function.rs30
-rw-r--r--crates/ide_completion/src/render/macro_.rs26
-rw-r--r--crates/ide_completion/src/render/pattern.rs54
-rw-r--r--crates/ide_completion/src/render/type_alias.rs10
23 files changed, 982 insertions, 397 deletions
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml
index 585ecca50..6bd8a5500 100644
--- a/crates/ide_completion/Cargo.toml
+++ b/crates/ide_completion/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13cov-mark = "1.1" 13cov-mark = { version = "1.1", features = ["thread-local"] }
14itertools = "0.10.0" 14itertools = "0.10.0"
15log = "0.4.8" 15log = "0.4.8"
16rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index cb05e85fc..e846678b4 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -45,15 +45,15 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
45 CompletionKind::Attribute, 45 CompletionKind::Attribute,
46 ctx.source_range(), 46 ctx.source_range(),
47 attr_completion.label, 47 attr_completion.label,
48 ) 48 );
49 .kind(CompletionItemKind::Attribute); 49 item.kind(CompletionItemKind::Attribute);
50 50
51 if let Some(lookup) = attr_completion.lookup { 51 if let Some(lookup) = attr_completion.lookup {
52 item = item.lookup_by(lookup); 52 item.lookup_by(lookup);
53 } 53 }
54 54
55 if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) { 55 if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) {
56 item = item.insert_snippet(cap, snippet); 56 item.insert_snippet(cap, snippet);
57 } 57 }
58 58
59 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { 59 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
@@ -168,16 +168,20 @@ fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input:
168 ); 168 );
169 let lookup = components.join(", "); 169 let lookup = components.join(", ");
170 let label = components.iter().rev().join(", "); 170 let label = components.iter().rev().join(", ");
171 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label) 171 let mut item =
172 .lookup_by(lookup) 172 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
173 .kind(CompletionItemKind::Attribute) 173 item.lookup_by(lookup).kind(CompletionItemKind::Attribute);
174 .add_to(acc) 174 item.add_to(acc);
175 } 175 }
176 176
177 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) { 177 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
178 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), custom_derive_name) 178 let mut item = CompletionItem::new(
179 .kind(CompletionItemKind::Attribute) 179 CompletionKind::Attribute,
180 .add_to(acc) 180 ctx.source_range(),
181 custom_derive_name,
182 );
183 item.kind(CompletionItemKind::Attribute);
184 item.add_to(acc);
181 } 185 }
182 } 186 }
183} 187}
@@ -193,14 +197,13 @@ fn complete_lint(
193 .into_iter() 197 .into_iter()
194 .filter(|completion| !existing_lints.contains(completion.label)) 198 .filter(|completion| !existing_lints.contains(completion.label))
195 { 199 {
196 CompletionItem::new( 200 let mut item = CompletionItem::new(
197 CompletionKind::Attribute, 201 CompletionKind::Attribute,
198 ctx.source_range(), 202 ctx.source_range(),
199 lint_completion.label, 203 lint_completion.label,
200 ) 204 );
201 .kind(CompletionItemKind::Attribute) 205 item.kind(CompletionItemKind::Attribute).detail(lint_completion.description);
202 .detail(lint_completion.description) 206 item.add_to(acc)
203 .add_to(acc)
204 } 207 }
205 } 208 }
206} 209}
diff --git a/crates/ide_completion/src/completions/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs
index 1bcc8727f..0243dce56 100644
--- a/crates/ide_completion/src/completions/fn_param.rs
+++ b/crates/ide_completion/src/completions/fn_param.rs
@@ -54,10 +54,9 @@ pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
54 } 54 }
55 55
56 params.into_iter().for_each(|(label, lookup)| { 56 params.into_iter().for_each(|(label, lookup)| {
57 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) 57 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label);
58 .kind(CompletionItemKind::Binding) 58 item.kind(CompletionItemKind::Binding).lookup_by(lookup);
59 .lookup_by(lookup) 59 item.add_to(acc)
60 .add_to(acc)
61 }); 60 });
62} 61}
63 62
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 80aa9fb06..b635e0ca3 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -12,21 +12,19 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
12 12
13 if ctx.use_item_syntax.is_some() { 13 if ctx.use_item_syntax.is_some() {
14 if ctx.path_qual.is_none() { 14 if ctx.path_qual.is_none() {
15 CompletionItem::new(CompletionKind::Keyword, source_range, "crate::") 15 let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "crate::");
16 .kind(CompletionItemKind::Keyword) 16 item.kind(CompletionItemKind::Keyword).insert_text("crate::");
17 .insert_text("crate::") 17 item.add_to(acc);
18 .add_to(acc);
19 } 18 }
20 CompletionItem::new(CompletionKind::Keyword, source_range, "self") 19 let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "self");
21 .kind(CompletionItemKind::Keyword) 20 item.kind(CompletionItemKind::Keyword);
22 .add_to(acc); 21 item.add_to(acc);
23 if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) 22 if iter::successors(ctx.path_qual.clone(), |p| p.qualifier())
24 .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) 23 .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
25 { 24 {
26 CompletionItem::new(CompletionKind::Keyword, source_range, "super::") 25 let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "super::");
27 .kind(CompletionItemKind::Keyword) 26 item.kind(CompletionItemKind::Keyword).insert_text("super::");
28 .insert_text("super::") 27 item.add_to(acc);
29 .add_to(acc);
30 } 28 }
31 } 29 }
32 30
@@ -34,11 +32,10 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
34 if let Some(receiver) = &ctx.dot_receiver { 32 if let Some(receiver) = &ctx.dot_receiver {
35 if let Some(ty) = ctx.sema.type_of_expr(receiver) { 33 if let Some(ty) = ctx.sema.type_of_expr(receiver) {
36 if ty.impls_future(ctx.db) { 34 if ty.impls_future(ctx.db) {
37 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await") 35 let mut item =
38 .kind(CompletionItemKind::Keyword) 36 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await");
39 .detail("expr.await") 37 item.kind(CompletionItemKind::Keyword).detail("expr.await").insert_text("await");
40 .insert_text("await") 38 item.add_to(acc);
41 .add_to(acc);
42 } 39 }
43 }; 40 };
44 } 41 }
@@ -165,9 +162,10 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
165} 162}
166 163
167fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) { 164fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
168 let builder = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) 165 let mut item = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw);
169 .kind(CompletionItemKind::Keyword); 166 item.kind(CompletionItemKind::Keyword);
170 let builder = match ctx.config.snippet_cap { 167
168 match ctx.config.snippet_cap {
171 Some(cap) => { 169 Some(cap) => {
172 let tmp; 170 let tmp;
173 let snippet = if snippet.ends_with('}') && ctx.incomplete_let { 171 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
@@ -177,11 +175,13 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet
177 } else { 175 } else {
178 snippet 176 snippet
179 }; 177 };
180 builder.insert_snippet(cap, snippet) 178 item.insert_snippet(cap, snippet);
179 }
180 None => {
181 item.insert_text(if snippet.contains('$') { kw } else { snippet });
181 } 182 }
182 None => builder.insert_text(if snippet.contains('$') { kw } else { snippet }),
183 }; 183 };
184 acc.add(builder.build()); 184 item.add_to(acc);
185} 185}
186 186
187#[cfg(test)] 187#[cfg(test)]
diff --git a/crates/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs
index 352fc7c77..4f9415736 100644
--- a/crates/ide_completion/src/completions/mod_.rs
+++ b/crates/ide_completion/src/completions/mod_.rs
@@ -80,9 +80,9 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op
80 if mod_under_caret.semicolon_token().is_none() { 80 if mod_under_caret.semicolon_token().is_none() {
81 label.push(';'); 81 label.push(';');
82 } 82 }
83 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label) 83 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label);
84 .kind(SymbolKind::Module) 84 item.kind(SymbolKind::Module);
85 .add_to(acc) 85 item.add_to(acc)
86 }); 86 });
87 87
88 Some(()) 88 Some(())
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;
85struct Bar { f: u32 } 88struct Bar { f: u32 }
86 89
87fn foo() { 90fn 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 } }
106enum E { X } 109enum E { X }
107 110
108fn foo() { 111fn 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;
129struct Bar { f: u32 } 133struct Bar { f: u32 }
130 134
131fn foo() { 135fn 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 }
147static FOO: E = E::X; 151static FOO: E = E::X;
148struct Bar { f: u32 } 152struct Bar { f: u32 }
149 153
150fn foo($0) { 154fn foo(a$0) {
151} 155}
152"#, 156"#,
153 expect![[r#" 157 expect![[r#"
@@ -163,7 +167,7 @@ fn foo($0) {
163struct Bar { f: u32 } 167struct Bar { f: u32 }
164 168
165fn foo() { 169fn foo() {
166 let $0 170 let a$0
167} 171}
168"#, 172"#,
169 expect![[r#" 173 expect![[r#"
@@ -179,7 +183,7 @@ fn foo() {
179struct Foo { bar: String, baz: String } 183struct Foo { bar: String, baz: String }
180struct Bar(String, String); 184struct Bar(String, String);
181struct Baz; 185struct Baz;
182fn outer($0) {} 186fn 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 }
196struct Bar(String, String); 200struct Bar(String, String);
197struct Baz; 201struct Baz;
198fn outer() { 202fn 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);
215struct Baz; 219struct Baz;
216fn outer() { 220fn outer() {
217 match () { 221 match () {
218 $0 222 a$0
219 } 223 }
220} 224}
221"#, 225"#,
@@ -239,7 +243,7 @@ use foo::*;
239 243
240fn outer() { 244fn outer() {
241 match () { 245 match () {
242 $0 246 a$0
243 } 247 }
244} 248}
245"#, 249"#,
@@ -258,7 +262,7 @@ fn outer() {
258struct Foo(i32); 262struct Foo(i32);
259fn main() { 263fn 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);
281impl Foo { 285impl 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#"
326enum Foo { Bar, Baz, Quux }
327
328fn 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#"
346enum Foo { Bar, Baz, Quux }
347
348fn 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#"
366enum Foo { Bar, Baz, Quux }
367
368fn 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#"
386enum Foo { Bar, Baz, Quux }
387impl 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.rs b/crates/ide_completion/src/completions/postfix.rs
index d45ad7944..ac69b720a 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -297,10 +297,9 @@ fn postfix_snippet(
297 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end()); 297 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
298 TextEdit::replace(delete_range, snippet.to_string()) 298 TextEdit::replace(delete_range, snippet.to_string())
299 }; 299 };
300 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) 300 let mut item = CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label);
301 .detail(detail) 301 item.detail(detail).kind(CompletionItemKind::Snippet).snippet_edit(cap, edit);
302 .kind(CompletionItemKind::Snippet) 302 item
303 .snippet_edit(cap, edit)
304} 303}
305 304
306#[cfg(test)] 305#[cfg(test)]
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.
60fn string_literal_contents(item: &ast::String) -> Option<String> { 60fn 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/record.rs b/crates/ide_completion/src/completions/record.rs
index 0a7927eb8..2f95b8687 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -22,16 +22,13 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
22 let completion_text = completion_text 22 let completion_text = completion_text
23 .strip_prefix(ctx.token.to_string().as_str()) 23 .strip_prefix(ctx.token.to_string().as_str())
24 .unwrap_or(completion_text); 24 .unwrap_or(completion_text);
25 acc.add( 25 let mut item = CompletionItem::new(
26 CompletionItem::new( 26 CompletionKind::Snippet,
27 CompletionKind::Snippet, 27 ctx.source_range(),
28 ctx.source_range(), 28 "..Default::default()",
29 "..Default::default()",
30 )
31 .insert_text(completion_text)
32 .kind(SymbolKind::Field)
33 .build(),
34 ); 29 );
30 item.insert_text(completion_text).kind(SymbolKind::Field);
31 item.add_to(acc);
35 } 32 }
36 33
37 missing_fields 34 missing_fields
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index df17a15c5..7f7830976 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -8,9 +8,9 @@ use crate::{
8}; 8};
9 9
10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { 10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
11 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label) 11 let mut item = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label);
12 .insert_snippet(cap, snippet) 12 item.insert_snippet(cap, snippet).kind(CompletionItemKind::Snippet);
13 .kind(CompletionItemKind::Snippet) 13 item
14} 14}
15 15
16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { 16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
@@ -35,7 +35,7 @@ pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionConte
35 None => return, 35 None => return,
36 }; 36 };
37 37
38 snippet( 38 let mut item = snippet(
39 ctx, 39 ctx,
40 cap, 40 cap,
41 "tmod (Test module)", 41 "tmod (Test module)",
@@ -49,11 +49,11 @@ mod tests {
49 $0 49 $0
50 } 50 }
51}", 51}",
52 ) 52 );
53 .lookup_by("tmod") 53 item.lookup_by("tmod");
54 .add_to(acc); 54 item.add_to(acc);
55 55
56 snippet( 56 let mut item = snippet(
57 ctx, 57 ctx,
58 cap, 58 cap,
59 "tfn (Test function)", 59 "tfn (Test function)",
@@ -62,11 +62,12 @@ mod tests {
62fn ${1:feature}() { 62fn ${1:feature}() {
63 $0 63 $0
64}", 64}",
65 ) 65 );
66 .lookup_by("tfn") 66 item.lookup_by("tfn");
67 .add_to(acc); 67 item.add_to(acc);
68 68
69 snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc); 69 let item = snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}");
70 item.add_to(acc);
70} 71}
71 72
72#[cfg(test)] 73#[cfg(test)]
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index b999540b8..5a7361f8e 100644
--- a/crates/ide_completion/src/completions/trait_impl.rs
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -145,9 +145,8 @@ fn add_function_impl(
145 format!("fn {}(..)", fn_name) 145 format!("fn {}(..)", fn_name)
146 }; 146 };
147 147
148 let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) 148 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label);
149 .lookup_by(fn_name) 149 item.lookup_by(fn_name).set_documentation(func.docs(ctx.db));
150 .set_documentation(func.docs(ctx.db));
151 150
152 let completion_kind = if func.self_param(ctx.db).is_some() { 151 let completion_kind = if func.self_param(ctx.db).is_some() {
153 CompletionItemKind::Method 152 CompletionItemKind::Method
@@ -161,15 +160,15 @@ fn add_function_impl(
161 match ctx.config.snippet_cap { 160 match ctx.config.snippet_cap {
162 Some(cap) => { 161 Some(cap) => {
163 let snippet = format!("{} {{\n $0\n}}", function_decl); 162 let snippet = format!("{} {{\n $0\n}}", function_decl);
164 builder.snippet_edit(cap, TextEdit::replace(range, snippet)) 163 item.snippet_edit(cap, TextEdit::replace(range, snippet));
165 } 164 }
166 None => { 165 None => {
167 let header = format!("{} {{", function_decl); 166 let header = format!("{} {{", function_decl);
168 builder.text_edit(TextEdit::replace(range, header)) 167 item.text_edit(TextEdit::replace(range, header));
169 } 168 }
170 } 169 };
171 .kind(completion_kind) 170 item.kind(completion_kind);
172 .add_to(acc); 171 item.add_to(acc);
173 } 172 }
174} 173}
175 174
@@ -185,12 +184,12 @@ fn add_type_alias_impl(
185 184
186 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end()); 185 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end());
187 186
188 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 187 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
189 .text_edit(TextEdit::replace(range, snippet)) 188 item.text_edit(TextEdit::replace(range, snippet))
190 .lookup_by(alias_name) 189 .lookup_by(alias_name)
191 .kind(SymbolKind::TypeAlias) 190 .kind(SymbolKind::TypeAlias)
192 .set_documentation(type_alias.docs(ctx.db)) 191 .set_documentation(type_alias.docs(ctx.db));
193 .add_to(acc); 192 item.add_to(acc);
194} 193}
195 194
196fn add_const_impl( 195fn add_const_impl(
@@ -208,12 +207,13 @@ fn add_const_impl(
208 let range = 207 let range =
209 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end()); 208 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
210 209
211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 210 let mut item =
212 .text_edit(TextEdit::replace(range, snippet)) 211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
212 item.text_edit(TextEdit::replace(range, snippet))
213 .lookup_by(const_name) 213 .lookup_by(const_name)
214 .kind(SymbolKind::Const) 214 .kind(SymbolKind::Const)
215 .set_documentation(const_.docs(ctx.db)) 215 .set_documentation(const_.docs(ctx.db));
216 .add_to(acc); 216 item.add_to(acc);
217 } 217 }
218 } 218 }
219} 219}
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index 044dfd160..5ef80f6a7 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;
6use crate::{CompletionContext, Completions}; 6use crate::{CompletionContext, Completions};
7 7
8pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 8pub(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#"
615enum Foo { Bar, Baz, Quux }
616
617fn 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#"
635enum Foo { Bar, Baz, Quux }
636
637fn 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#"
655enum Foo { Bar, Baz, Quux }
656
657fn 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#"
707enum Foo { Bar, Baz, Quux }
708impl 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)]
614mod 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#"
641fn 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#"
654fn 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#"
667fn foo() {
668 bar($0);
669}
670
671fn 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#"
682fn foo() {
683 bar(c$0);
684}
685
686fn 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#"
697struct Foo { a: u32 }
698fn 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#"
711struct Foo { a: u32 }
712fn 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#"
725enum E { X }
726fn 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#"
739enum E { X }
740fn 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#"
752enum Foo { Bar, Baz, Quux }
753
754fn 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#"
768enum Foo { Bar, Baz, Quux }
769
770fn 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#"
784fn 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#"
797fn 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 14afec603..9a4b5217a 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -70,7 +70,7 @@ pub struct CompletionItem {
70 /// Note that Relevance ignores fuzzy match score. We compute Relevance for 70 /// Note that Relevance ignores fuzzy match score. We compute Relevance for
71 /// all possible items, and then separately build an ordered completion list 71 /// all possible items, and then separately build an ordered completion list
72 /// based on relevance and fuzzy matching with the already typed identifier. 72 /// based on relevance and fuzzy matching with the already typed identifier.
73 relevance: Relevance, 73 relevance: CompletionRelevance,
74 74
75 /// Indicates that a reference or mutable reference to this variable is a 75 /// Indicates that a reference or mutable reference to this variable is a
76 /// possible match. 76 /// possible match.
@@ -107,9 +107,11 @@ impl fmt::Debug for CompletionItem {
107 if self.deprecated { 107 if self.deprecated {
108 s.field("deprecated", &true); 108 s.field("deprecated", &true);
109 } 109 }
110 if self.relevance.is_relevant() { 110
111 if self.relevance != CompletionRelevance::default() {
111 s.field("relevance", &self.relevance); 112 s.field("relevance", &self.relevance);
112 } 113 }
114
113 if let Some(mutability) = &self.ref_match { 115 if let Some(mutability) = &self.ref_match {
114 s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref())); 116 s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref()));
115 } 117 }
@@ -120,16 +122,8 @@ impl fmt::Debug for CompletionItem {
120 } 122 }
121} 123}
122 124
123#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
124pub enum CompletionScore {
125 /// If only type match
126 TypeMatch,
127 /// If type and name match
128 TypeAndNameMatch,
129}
130
131#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)] 125#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
132pub struct Relevance { 126pub struct CompletionRelevance {
133 /// This is set in cases like these: 127 /// This is set in cases like these:
134 /// 128 ///
135 /// ``` 129 /// ```
@@ -150,11 +144,54 @@ pub struct Relevance {
150 /// } 144 /// }
151 /// ``` 145 /// ```
152 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,
153} 162}
154 163
155impl Relevance { 164impl CompletionRelevance {
165 /// Provides a relevance score. Higher values are more relevant.
166 ///
167 /// The absolute value of the relevance score is not meaningful, for
168 /// example a value of 0 doesn't mean "not relevant", rather
169 /// it means "least relevant". The score value should only be used
170 /// for relative ordering.
171 ///
172 /// See is_relevant if you need to make some judgement about score
173 /// in an absolute sense.
174 pub fn score(&self) -> u32 {
175 let mut score = 0;
176
177 if self.exact_name_match {
178 score += 1;
179 }
180 if self.exact_type_match {
181 score += 3;
182 }
183 if self.is_local {
184 score += 1;
185 }
186
187 score
188 }
189
190 /// Returns true when the score for this threshold is above
191 /// some threshold such that we think it is especially likely
192 /// to be relevant.
156 pub fn is_relevant(&self) -> bool { 193 pub fn is_relevant(&self) -> bool {
157 self != &Relevance::default() 194 self.score() > 0
158 } 195 }
159} 196}
160 197
@@ -249,7 +286,7 @@ impl CompletionItem {
249 text_edit: None, 286 text_edit: None,
250 deprecated: false, 287 deprecated: false,
251 trigger_call_info: None, 288 trigger_call_info: None,
252 relevance: Relevance::default(), 289 relevance: CompletionRelevance::default(),
253 ref_match: None, 290 ref_match: None,
254 import_to_add: None, 291 import_to_add: None,
255 } 292 }
@@ -292,7 +329,7 @@ impl CompletionItem {
292 self.deprecated 329 self.deprecated
293 } 330 }
294 331
295 pub fn relevance(&self) -> Relevance { 332 pub fn relevance(&self) -> CompletionRelevance {
296 self.relevance 333 self.relevance
297 } 334 }
298 335
@@ -300,8 +337,14 @@ impl CompletionItem {
300 self.trigger_call_info 337 self.trigger_call_info
301 } 338 }
302 339
303 pub fn ref_match(&self) -> Option<Mutability> { 340 pub fn ref_match(&self) -> Option<(Mutability, CompletionRelevance)> {
304 self.ref_match 341 // Relevance of the ref match should be the same as the original
342 // match, but with exact type match set because self.ref_match
343 // is only set if there is an exact type match.
344 let mut relevance = self.relevance;
345 relevance.exact_type_match = true;
346
347 self.ref_match.map(|mutability| (mutability, relevance))
305 } 348 }
306 349
307 pub fn import_to_add(&self) -> Option<&ImportEdit> { 350 pub fn import_to_add(&self) -> Option<&ImportEdit> {
@@ -349,7 +392,7 @@ pub(crate) struct Builder {
349 text_edit: Option<TextEdit>, 392 text_edit: Option<TextEdit>,
350 deprecated: bool, 393 deprecated: bool,
351 trigger_call_info: Option<bool>, 394 trigger_call_info: Option<bool>,
352 relevance: Relevance, 395 relevance: CompletionRelevance,
353 ref_match: Option<Mutability>, 396 ref_match: Option<Mutability>,
354} 397}
355 398
@@ -401,42 +444,42 @@ impl Builder {
401 import_to_add: self.import_to_add, 444 import_to_add: self.import_to_add,
402 } 445 }
403 } 446 }
404 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { 447 pub(crate) fn lookup_by(&mut self, lookup: impl Into<String>) -> &mut Builder {
405 self.lookup = Some(lookup.into()); 448 self.lookup = Some(lookup.into());
406 self 449 self
407 } 450 }
408 pub(crate) fn label(mut self, label: impl Into<String>) -> Builder { 451 pub(crate) fn label(&mut self, label: impl Into<String>) -> &mut Builder {
409 self.label = label.into(); 452 self.label = label.into();
410 self 453 self
411 } 454 }
412 pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder { 455 pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder {
413 self.insert_text = Some(insert_text.into()); 456 self.insert_text = Some(insert_text.into());
414 self 457 self
415 } 458 }
416 pub(crate) fn insert_snippet( 459 pub(crate) fn insert_snippet(
417 mut self, 460 &mut self,
418 _cap: SnippetCap, 461 _cap: SnippetCap,
419 snippet: impl Into<String>, 462 snippet: impl Into<String>,
420 ) -> Builder { 463 ) -> &mut Builder {
421 self.insert_text_format = InsertTextFormat::Snippet; 464 self.insert_text_format = InsertTextFormat::Snippet;
422 self.insert_text(snippet) 465 self.insert_text(snippet)
423 } 466 }
424 pub(crate) fn kind(mut self, kind: impl Into<CompletionItemKind>) -> Builder { 467 pub(crate) fn kind(&mut self, kind: impl Into<CompletionItemKind>) -> &mut Builder {
425 self.kind = Some(kind.into()); 468 self.kind = Some(kind.into());
426 self 469 self
427 } 470 }
428 pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder { 471 pub(crate) fn text_edit(&mut self, edit: TextEdit) -> &mut Builder {
429 self.text_edit = Some(edit); 472 self.text_edit = Some(edit);
430 self 473 self
431 } 474 }
432 pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder { 475 pub(crate) fn snippet_edit(&mut self, _cap: SnippetCap, edit: TextEdit) -> &mut Builder {
433 self.insert_text_format = InsertTextFormat::Snippet; 476 self.insert_text_format = InsertTextFormat::Snippet;
434 self.text_edit(edit) 477 self.text_edit(edit)
435 } 478 }
436 pub(crate) fn detail(self, detail: impl Into<String>) -> Builder { 479 pub(crate) fn detail(&mut self, detail: impl Into<String>) -> &mut Builder {
437 self.set_detail(Some(detail)) 480 self.set_detail(Some(detail))
438 } 481 }
439 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { 482 pub(crate) fn set_detail(&mut self, detail: Option<impl Into<String>>) -> &mut Builder {
440 self.detail = detail.map(Into::into); 483 self.detail = detail.map(Into::into);
441 if let Some(detail) = &self.detail { 484 if let Some(detail) = &self.detail {
442 if never!(detail.contains('\n'), "multiline detail:\n{}", detail) { 485 if never!(detail.contains('\n'), "multiline detail:\n{}", detail) {
@@ -446,31 +489,106 @@ impl Builder {
446 self 489 self
447 } 490 }
448 #[allow(unused)] 491 #[allow(unused)]
449 pub(crate) fn documentation(self, docs: Documentation) -> Builder { 492 pub(crate) fn documentation(&mut self, docs: Documentation) -> &mut Builder {
450 self.set_documentation(Some(docs)) 493 self.set_documentation(Some(docs))
451 } 494 }
452 pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder { 495 pub(crate) fn set_documentation(&mut self, docs: Option<Documentation>) -> &mut Builder {
453 self.documentation = docs.map(Into::into); 496 self.documentation = docs.map(Into::into);
454 self 497 self
455 } 498 }
456 pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder { 499 pub(crate) fn set_deprecated(&mut self, deprecated: bool) -> &mut Builder {
457 self.deprecated = deprecated; 500 self.deprecated = deprecated;
458 self 501 self
459 } 502 }
460 pub(crate) fn set_relevance(mut self, relevance: Relevance) -> Builder { 503 pub(crate) fn set_relevance(&mut self, relevance: CompletionRelevance) -> &mut Builder {
461 self.relevance = relevance; 504 self.relevance = relevance;
462 self 505 self
463 } 506 }
464 pub(crate) fn trigger_call_info(mut self) -> Builder { 507 pub(crate) fn trigger_call_info(&mut self) -> &mut Builder {
465 self.trigger_call_info = Some(true); 508 self.trigger_call_info = Some(true);
466 self 509 self
467 } 510 }
468 pub(crate) fn add_import(mut self, import_to_add: Option<ImportEdit>) -> Builder { 511 pub(crate) fn add_import(&mut self, import_to_add: Option<ImportEdit>) -> &mut Builder {
469 self.import_to_add = import_to_add; 512 self.import_to_add = import_to_add;
470 self 513 self
471 } 514 }
472 pub(crate) fn ref_match(mut self, mutability: Mutability) -> Builder { 515 pub(crate) fn ref_match(&mut self, mutability: Mutability) -> &mut Builder {
473 self.ref_match = Some(mutability); 516 self.ref_match = Some(mutability);
474 self 517 self
475 } 518 }
476} 519}
520
521#[cfg(test)]
522mod tests {
523 use itertools::Itertools;
524 use test_utils::assert_eq_text;
525
526 use super::CompletionRelevance;
527
528 /// Check that these are CompletionRelevance are sorted in ascending order
529 /// by their relevance score.
530 ///
531 /// We want to avoid making assertions about the absolute score of any
532 /// item, but we do want to assert whether each is >, <, or == to the
533 /// others.
534 ///
535 /// If provided vec![vec![a], vec![b, c], vec![d]], then this will assert:
536 /// a.score < b.score == c.score < d.score
537 fn check_relevance_score_ordered(expected_relevance_order: Vec<Vec<CompletionRelevance>>) {
538 let expected = format!("{:#?}", &expected_relevance_order);
539
540 let actual_relevance_order = expected_relevance_order
541 .into_iter()
542 .flatten()
543 .map(|r| (r.score(), r))
544 .sorted_by_key(|(score, _r)| *score)
545 .fold(
546 (u32::MIN, vec![vec![]]),
547 |(mut currently_collecting_score, mut out), (score, r)| {
548 if currently_collecting_score == score {
549 out.last_mut().unwrap().push(r);
550 } else {
551 currently_collecting_score = score;
552 out.push(vec![r]);
553 }
554 (currently_collecting_score, out)
555 },
556 )
557 .1;
558
559 let actual = format!("{:#?}", &actual_relevance_order);
560
561 assert_eq_text!(&expected, &actual);
562 }
563
564 #[test]
565 fn relevance_score() {
566 // This test asserts that the relevance score for these items is ascending, and
567 // that any items in the same vec have the same score.
568 let expected_relevance_order = vec![
569 vec![CompletionRelevance::default()],
570 vec![
571 CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() },
572 CompletionRelevance { is_local: true, ..CompletionRelevance::default() },
573 ],
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 }],
590 ];
591
592 check_relevance_score_ordered(expected_relevance_order);
593 }
594}
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index d46f521a0..263554ecf 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -23,10 +23,7 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi
23 23
24pub use crate::{ 24pub use crate::{
25 config::CompletionConfig, 25 config::CompletionConfig,
26 item::{ 26 item::{CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit, InsertTextFormat},
27 CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat,
28 Relevance,
29 },
30}; 27};
31 28
32//FIXME: split the following feature into fine-grained features. 29//FIXME: split the following feature into fine-grained features.
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 8c8b149a1..2514dda7c 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -10,8 +10,10 @@ pub(crate) mod type_alias;
10 10
11mod builder_ext; 11mod builder_ext;
12 12
13use base_db::Upcast;
13use hir::{ 14use hir::{
14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, 15 db::HirDatabase, AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability,
16 ScopeDef, Type,
15}; 17};
16use ide_db::{ 18use ide_db::{
17 helpers::{item_name, SnippetCap}, 19 helpers::{item_name, SnippetCap},
@@ -20,7 +22,7 @@ use ide_db::{
20use syntax::TextRange; 22use syntax::TextRange;
21 23
22use crate::{ 24use crate::{
23 item::{ImportEdit, Relevance}, 25 item::{CompletionRelevance, ImportEdit},
24 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 26 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
25}; 27};
26 28
@@ -116,19 +118,6 @@ impl<'a> RenderContext<'a> {
116 fn docs(&self, node: impl HasAttrs) -> Option<Documentation> { 118 fn docs(&self, node: impl HasAttrs) -> Option<Documentation> {
117 node.docs(self.db()) 119 node.docs(self.db())
118 } 120 }
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} 121}
133 122
134/// Generic renderer for completion items. 123/// Generic renderer for completion items.
@@ -149,24 +138,27 @@ impl<'a> Render<'a> {
149 CompletionKind::Reference, 138 CompletionKind::Reference,
150 self.ctx.source_range(), 139 self.ctx.source_range(),
151 name.to_string(), 140 name.to_string(),
152 ) 141 );
153 .kind(SymbolKind::Field) 142 item.kind(SymbolKind::Field)
154 .detail(ty.display(self.ctx.db()).to_string()) 143 .detail(ty.display(self.ctx.db()).to_string())
155 .set_documentation(field.docs(self.ctx.db())) 144 .set_documentation(field.docs(self.ctx.db()))
156 .set_deprecated(is_deprecated); 145 .set_deprecated(is_deprecated);
157 146
158 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) { 147 item.set_relevance(compute_relevance(&self.ctx, &ty, &name.to_string()));
159 item = item.set_relevance(relevance);
160 }
161 148
162 item.build() 149 item.build()
163 } 150 }
164 151
165 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { 152 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem {
166 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string()) 153 let mut item = CompletionItem::new(
167 .kind(SymbolKind::Field) 154 CompletionKind::Reference,
168 .detail(ty.display(self.ctx.db()).to_string()) 155 self.ctx.source_range(),
169 .build() 156 field.to_string(),
157 );
158
159 item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string());
160
161 item.build()
170 } 162 }
171 163
172 fn render_resolution( 164 fn render_resolution(
@@ -225,15 +217,13 @@ impl<'a> Render<'a> {
225 CompletionItemKind::SymbolKind(SymbolKind::SelfParam) 217 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
226 } 218 }
227 ScopeDef::Unknown => { 219 ScopeDef::Unknown => {
228 let item = CompletionItem::new( 220 let mut item = CompletionItem::new(
229 CompletionKind::Reference, 221 CompletionKind::Reference,
230 self.ctx.source_range(), 222 self.ctx.source_range(),
231 local_name, 223 local_name,
232 ) 224 );
233 .kind(CompletionItemKind::UnresolvedReference) 225 item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add);
234 .add_import(import_to_add) 226 return Some(item.build());
235 .build();
236 return Some(item);
237 } 227 }
238 }; 228 };
239 229
@@ -242,25 +232,29 @@ impl<'a> Render<'a> {
242 if let ScopeDef::Local(local) = resolution { 232 if let ScopeDef::Local(local) = resolution {
243 let ty = local.ty(self.ctx.db()); 233 let ty = local.ty(self.ctx.db());
244 if !ty.is_unknown() { 234 if !ty.is_unknown() {
245 item = item.detail(ty.display(self.ctx.db()).to_string()); 235 item.detail(ty.display(self.ctx.db()).to_string());
246 } 236 }
247 }; 237 };
248 238
249 if let ScopeDef::Local(local) = resolution { 239 if let ScopeDef::Local(local) = resolution {
250 let ty = local.ty(self.ctx.db()); 240 let ty = local.ty(self.ctx.db());
251 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) { 241
252 item = item.set_relevance(relevance) 242 let mut relevance = compute_relevance(&self.ctx, &ty, &local_name);
253 } 243 relevance.is_local = true;
254 if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { 244 item.set_relevance(relevance);
255 if let Some(ty_without_ref) = expected_type.remove_ref() { 245
256 if ty_without_ref == ty { 246 if let Some(expected_type) = self.ctx.completion.expected_type.as_ref() {
257 cov_mark::hit!(suggest_ref); 247 if &ty != expected_type {
258 let mutability = if expected_type.is_mutable_reference() { 248 if let Some(ty_without_ref) = expected_type.remove_ref() {
259 Mutability::Mut 249 if relevance_type_match(self.ctx.db().upcast(), &ty, &ty_without_ref) {
260 } else { 250 cov_mark::hit!(suggest_ref);
261 Mutability::Shared 251 let mutability = if expected_type.is_mutable_reference() {
262 }; 252 Mutability::Mut
263 item = item.ref_match(mutability) 253 } else {
254 Mutability::Shared
255 };
256 item.ref_match(mutability);
257 }
264 } 258 }
265 } 259 }
266 } 260 }
@@ -281,21 +275,17 @@ impl<'a> Render<'a> {
281 }; 275 };
282 if has_non_default_type_params { 276 if has_non_default_type_params {
283 cov_mark::hit!(inserts_angle_brackets_for_generics); 277 cov_mark::hit!(inserts_angle_brackets_for_generics);
284 item = item 278 item.lookup_by(local_name.clone())
285 .lookup_by(local_name.clone())
286 .label(format!("{}<…>", local_name)) 279 .label(format!("{}<…>", local_name))
287 .insert_snippet(cap, format!("{}<$0>", local_name)); 280 .insert_snippet(cap, format!("{}<$0>", local_name));
288 } 281 }
289 } 282 }
290 } 283 }
291 284 item.kind(kind)
292 Some( 285 .add_import(import_to_add)
293 item.kind(kind) 286 .set_documentation(self.docs(resolution))
294 .add_import(import_to_add) 287 .set_deprecated(self.is_deprecated(resolution));
295 .set_documentation(self.docs(resolution)) 288 Some(item.build())
296 .set_deprecated(self.is_deprecated(resolution))
297 .build(),
298 )
299 } 289 }
300 290
301 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { 291 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> {
@@ -323,23 +313,27 @@ impl<'a> Render<'a> {
323 } 313 }
324} 314}
325 315
326fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<Relevance> { 316fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> CompletionRelevance {
327 let (expected_name, expected_type) = ctx.expected_name_and_type()?; 317 let mut res = CompletionRelevance::default();
328 let mut res = Relevance::default(); 318
329 res.exact_type_match = ty == &expected_type; 319 res.exact_type_match = Some(ty) == ctx.completion.expected_type.as_ref();
330 res.exact_name_match = name == &expected_name; 320 res.exact_name_match = Some(name) == ctx.completion.expected_name.as_deref();
331 Some(res) 321
322 res
323}
324
325fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -> bool {
326 ty == expected_type || ty.autoderef(db).any(|deref_ty| &deref_ty == expected_type)
332} 327}
333 328
334#[cfg(test)] 329#[cfg(test)]
335mod tests { 330mod tests {
336 use std::cmp::Reverse;
337
338 use expect_test::{expect, Expect}; 331 use expect_test::{expect, Expect};
332 use itertools::Itertools;
339 333
340 use crate::{ 334 use crate::{
341 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, 335 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
342 CompletionKind, Relevance, 336 CompletionKind, CompletionRelevance,
343 }; 337 };
344 338
345 fn check(ra_fixture: &str, expect: Expect) { 339 fn check(ra_fixture: &str, expect: Expect) {
@@ -348,26 +342,40 @@ mod tests {
348 } 342 }
349 343
350 fn check_relevance(ra_fixture: &str, expect: Expect) { 344 fn check_relevance(ra_fixture: &str, expect: Expect) {
351 fn display_relevance(relevance: Relevance) -> &'static str { 345 fn display_relevance(relevance: CompletionRelevance) -> String {
352 match relevance { 346 let relevance_factors = vec![
353 Relevance { exact_type_match: true, exact_name_match: true } => "[type+name]", 347 (relevance.exact_type_match, "type"),
354 Relevance { exact_type_match: true, exact_name_match: false } => "[type]", 348 (relevance.exact_name_match, "name"),
355 Relevance { exact_type_match: false, exact_name_match: true } => "[name]", 349 (relevance.is_local, "local"),
356 Relevance { exact_type_match: false, exact_name_match: false } => "[]", 350 ]
357 } 351 .into_iter()
352 .filter_map(|(cond, desc)| if cond { Some(desc) } else { None })
353 .join("+");
354
355 format!("[{}]", relevance_factors)
358 } 356 }
359 357
360 let mut completions = get_all_items(TEST_CONFIG, ra_fixture); 358 let actual = get_all_items(TEST_CONFIG, ra_fixture)
361 completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string()));
362 let actual = completions
363 .into_iter() 359 .into_iter()
364 .filter(|it| it.completion_kind == CompletionKind::Reference) 360 .filter(|it| it.completion_kind == CompletionKind::Reference)
365 .map(|it| { 361 .flat_map(|it| {
362 let mut items = vec![];
363
366 let tag = it.kind().unwrap().tag(); 364 let tag = it.kind().unwrap().tag();
367 let relevance = display_relevance(it.relevance()); 365 let relevance = display_relevance(it.relevance());
368 format!("{} {} {}\n", tag, it.label(), relevance) 366 items.push(format!("{} {} {}\n", tag, it.label(), relevance));
367
368 if let Some((mutability, relevance)) = it.ref_match() {
369 let label = format!("&{}{}", mutability.as_keyword_for_ref(), it.label());
370 let relevance = display_relevance(relevance);
371
372 items.push(format!("{} {} {}\n", tag, label, relevance));
373 }
374
375 items
369 }) 376 })
370 .collect::<String>(); 377 .collect::<String>();
378
371 expect.assert_eq(&actual); 379 expect.assert_eq(&actual);
372 } 380 }
373 381
@@ -829,7 +837,6 @@ fn foo(xs: Vec<i128>)
829 837
830 #[test] 838 #[test]
831 fn active_param_relevance() { 839 fn active_param_relevance() {
832 cov_mark::check!(active_param_type_match);
833 check_relevance( 840 check_relevance(
834 r#" 841 r#"
835struct S { foo: i64, bar: u32, baz: u32 } 842struct S { foo: i64, bar: u32, baz: u32 }
@@ -837,16 +844,15 @@ fn test(bar: u32) { }
837fn foo(s: S) { test(s.$0) } 844fn foo(s: S) { test(s.$0) }
838"#, 845"#,
839 expect![[r#" 846 expect![[r#"
847 fd foo []
840 fd bar [type+name] 848 fd bar [type+name]
841 fd baz [type] 849 fd baz [type]
842 fd foo []
843 "#]], 850 "#]],
844 ); 851 );
845 } 852 }
846 853
847 #[test] 854 #[test]
848 fn record_field_relevances() { 855 fn record_field_relevances() {
849 cov_mark::check!(record_field_type_match);
850 check_relevance( 856 check_relevance(
851 r#" 857 r#"
852struct A { foo: i64, bar: u32, baz: u32 } 858struct A { foo: i64, bar: u32, baz: u32 }
@@ -854,9 +860,9 @@ struct B { x: (), y: f32, bar: u32 }
854fn foo(a: A) { B { bar: a.$0 }; } 860fn foo(a: A) { B { bar: a.$0 }; }
855"#, 861"#,
856 expect![[r#" 862 expect![[r#"
863 fd foo []
857 fd bar [type+name] 864 fd bar [type+name]
858 fd baz [type] 865 fd baz [type]
859 fd foo []
860 "#]], 866 "#]],
861 ) 867 )
862 } 868 }
@@ -884,9 +890,9 @@ fn f(foo: i64) { }
884fn foo(a: A) { f(B { bar: a.$0 }); } 890fn foo(a: A) { f(B { bar: a.$0 }); }
885"#, 891"#,
886 expect![[r#" 892 expect![[r#"
893 fd foo []
887 fd bar [type+name] 894 fd bar [type+name]
888 fd baz [type] 895 fd baz [type]
889 fd foo []
890 "#]], 896 "#]],
891 ); 897 );
892 } 898 }
@@ -899,7 +905,7 @@ struct WorldSnapshot { _f: () };
899fn go(world: &WorldSnapshot) { go(w$0) } 905fn go(world: &WorldSnapshot) { go(w$0) }
900"#, 906"#,
901 expect![[r#" 907 expect![[r#"
902 lc world [type+name] 908 lc world [type+name+local]
903 st WorldSnapshot [] 909 st WorldSnapshot []
904 fn go(…) [] 910 fn go(…) []
905 "#]], 911 "#]],
@@ -914,9 +920,69 @@ struct Foo;
914fn f(foo: &Foo) { f(foo, w$0) } 920fn f(foo: &Foo) { f(foo, w$0) }
915"#, 921"#,
916 expect![[r#" 922 expect![[r#"
923 lc foo [local]
917 st Foo [] 924 st Foo []
918 fn f(…) [] 925 fn f(…) []
919 lc foo [] 926 "#]],
927 );
928 }
929
930 #[test]
931 fn score_fn_type_and_name_match() {
932 check_relevance(
933 r#"
934struct A { bar: u8 }
935fn baz() -> u8 { 0 }
936fn bar() -> u8 { 0 }
937fn f() { A { bar: b$0 }; }
938"#,
939 expect![[r#"
940 fn baz() [type]
941 st A []
942 fn bar() [type+name]
943 fn f() []
944 "#]],
945 );
946 }
947
948 #[test]
949 fn score_method_type_and_name_match() {
950 check_relevance(
951 r#"
952fn baz(aaa: u32){}
953struct Foo;
954impl Foo {
955fn aaa(&self) -> u32 { 0 }
956fn bbb(&self) -> u32 { 0 }
957fn ccc(&self) -> u64 { 0 }
958}
959fn f() {
960 baz(Foo.$0
961}
962"#,
963 expect![[r#"
964 me aaa() [type+name]
965 me bbb() [type]
966 me ccc() []
967 "#]],
968 );
969 }
970
971 #[test]
972 fn score_method_name_match_only() {
973 check_relevance(
974 r#"
975fn baz(aaa: u32){}
976struct Foo;
977impl Foo {
978fn aaa(&self) -> u64 { 0 }
979}
980fn f() {
981 baz(Foo.$0
982}
983"#,
984 expect![[r#"
985 me aaa() [name]
920 "#]], 986 "#]],
921 ); 987 );
922 } 988 }
@@ -976,9 +1042,10 @@ fn main() {
976 Local, 1042 Local,
977 ), 1043 ),
978 detail: "S", 1044 detail: "S",
979 relevance: Relevance { 1045 relevance: CompletionRelevance {
980 exact_name_match: true, 1046 exact_name_match: true,
981 exact_type_match: false, 1047 exact_type_match: false,
1048 is_local: true,
982 }, 1049 },
983 ref_match: "&mut ", 1050 ref_match: "&mut ",
984 }, 1051 },
@@ -986,4 +1053,120 @@ fn main() {
986 "#]], 1053 "#]],
987 ) 1054 )
988 } 1055 }
1056
1057 #[test]
1058 fn suggest_deref() {
1059 check_relevance(
1060 r#"
1061#[lang = "deref"]
1062trait Deref {
1063 type Target;
1064 fn deref(&self) -> &Self::Target;
1065}
1066
1067struct S;
1068struct T(S);
1069
1070impl Deref for T {
1071 type Target = S;
1072
1073 fn deref(&self) -> &Self::Target {
1074 &self.0
1075 }
1076}
1077
1078fn foo(s: &S) {}
1079
1080fn main() {
1081 let t = T(S);
1082 let m = 123;
1083
1084 foo($0);
1085}
1086 "#,
1087 expect![[r#"
1088 lc m [local]
1089 lc t [local]
1090 lc &t [type+local]
1091 st T []
1092 st S []
1093 fn main() []
1094 tt Deref []
1095 fn foo(…) []
1096 "#]],
1097 )
1098 }
1099
1100 #[test]
1101 fn suggest_deref_mut() {
1102 check_relevance(
1103 r#"
1104#[lang = "deref"]
1105trait Deref {
1106 type Target;
1107 fn deref(&self) -> &Self::Target;
1108}
1109
1110#[lang = "deref_mut"]
1111pub trait DerefMut: Deref {
1112 fn deref_mut(&mut self) -> &mut Self::Target;
1113}
1114
1115struct S;
1116struct T(S);
1117
1118impl Deref for T {
1119 type Target = S;
1120
1121 fn deref(&self) -> &Self::Target {
1122 &self.0
1123 }
1124}
1125
1126impl DerefMut for T {
1127 fn deref_mut(&mut self) -> &mut Self::Target {
1128 &mut self.0
1129 }
1130}
1131
1132fn foo(s: &mut S) {}
1133
1134fn main() {
1135 let t = T(S);
1136 let m = 123;
1137
1138 foo($0);
1139}
1140 "#,
1141 expect![[r#"
1142 lc m [local]
1143 lc t [local]
1144 lc &mut t [type+local]
1145 tt DerefMut []
1146 tt Deref []
1147 fn foo(…) []
1148 st T []
1149 st S []
1150 fn main() []
1151 "#]],
1152 )
1153 }
1154
1155 #[test]
1156 fn locals() {
1157 check_relevance(
1158 r#"
1159fn foo(bar: u32) {
1160 let baz = 0;
1161
1162 f$0
1163}
1164"#,
1165 expect![[r#"
1166 lc baz [local]
1167 lc bar [local]
1168 fn foo(…) []
1169 "#]],
1170 );
1171 }
989} 1172}
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs
index 95a7596c1..6d062b3b9 100644
--- a/crates/ide_completion/src/render/builder_ext.rs
+++ b/crates/ide_completion/src/render/builder_ext.rs
@@ -52,11 +52,11 @@ impl Builder {
52 } 52 }
53 53
54 pub(super) fn add_call_parens( 54 pub(super) fn add_call_parens(
55 mut self, 55 &mut self,
56 ctx: &CompletionContext, 56 ctx: &CompletionContext,
57 name: String, 57 name: String,
58 params: Params, 58 params: Params,
59 ) -> Builder { 59 ) -> &mut Builder {
60 if !self.should_add_parens(ctx) { 60 if !self.should_add_parens(ctx) {
61 return self; 61 return self;
62 } 62 }
@@ -71,7 +71,7 @@ impl Builder {
71 let (snippet, label) = if params.is_empty() { 71 let (snippet, label) = if params.is_empty() {
72 (format!("{}()$0", name), format!("{}()", name)) 72 (format!("{}()$0", name), format!("{}()", name))
73 } else { 73 } else {
74 self = self.trigger_call_info(); 74 self.trigger_call_info();
75 let snippet = match (ctx.config.add_call_argument_snippets, params) { 75 let snippet = match (ctx.config.add_call_argument_snippets, params) {
76 (true, Params::Named(params)) => { 76 (true, Params::Named(params)) => {
77 let function_params_snippet = 77 let function_params_snippet =
diff --git a/crates/ide_completion/src/render/const_.rs b/crates/ide_completion/src/render/const_.rs
index 5010b642a..8add369e4 100644
--- a/crates/ide_completion/src/render/const_.rs
+++ b/crates/ide_completion/src/render/const_.rs
@@ -36,17 +36,17 @@ impl<'a> ConstRender<'a> {
36 let name = self.name()?; 36 let name = self.name()?;
37 let detail = self.detail(); 37 let detail = self.detail();
38 38
39 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) 39 let mut item =
40 .kind(SymbolKind::Const) 40 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name);
41 item.kind(SymbolKind::Const)
41 .set_documentation(self.ctx.docs(self.const_)) 42 .set_documentation(self.ctx.docs(self.const_))
42 .set_deprecated( 43 .set_deprecated(
43 self.ctx.is_deprecated(self.const_) 44 self.ctx.is_deprecated(self.const_)
44 || self.ctx.is_deprecated_assoc_item(self.const_), 45 || self.ctx.is_deprecated_assoc_item(self.const_),
45 ) 46 )
46 .detail(detail) 47 .detail(detail);
47 .build();
48 48
49 Some(item) 49 Some(item.build())
50 } 50 }
51 51
52 fn name(&self) -> Option<String> { 52 fn name(&self) -> Option<String> {
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
index ed055c1fb..e8cfcc0c7 100644
--- a/crates/ide_completion/src/render/enum_variant.rs
+++ b/crates/ide_completion/src/render/enum_variant.rs
@@ -55,27 +55,26 @@ impl<'a> EnumRender<'a> {
55 } 55 }
56 56
57 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { 57 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
58 let mut builder = CompletionItem::new( 58 let mut item = CompletionItem::new(
59 CompletionKind::Reference, 59 CompletionKind::Reference,
60 self.ctx.source_range(), 60 self.ctx.source_range(),
61 self.qualified_name.clone(), 61 self.qualified_name.clone(),
62 ) 62 );
63 .kind(SymbolKind::Variant) 63 item.kind(SymbolKind::Variant)
64 .set_documentation(self.variant.docs(self.ctx.db())) 64 .set_documentation(self.variant.docs(self.ctx.db()))
65 .set_deprecated(self.ctx.is_deprecated(self.variant)) 65 .set_deprecated(self.ctx.is_deprecated(self.variant))
66 .add_import(import_to_add) 66 .add_import(import_to_add)
67 .detail(self.detail()); 67 .detail(self.detail());
68 68
69 if self.variant_kind == StructKind::Tuple { 69 if self.variant_kind == StructKind::Tuple {
70 cov_mark::hit!(inserts_parens_for_tuple_enums); 70 cov_mark::hit!(inserts_parens_for_tuple_enums);
71 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len()); 71 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len());
72 builder = 72 item.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
73 builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
74 } else if self.path.is_some() { 73 } else if self.path.is_some() {
75 builder = builder.lookup_by(self.short_qualified_name); 74 item.lookup_by(self.short_qualified_name);
76 } 75 }
77 76
78 builder.build() 77 item.build()
79 } 78 }
80 79
81 fn detail(&self) -> String { 80 fn detail(&self) -> String {
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index 5931945a8..47e26a5d8 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -5,7 +5,7 @@ use ide_db::SymbolKind;
5use syntax::ast::Fn; 5use syntax::ast::Fn;
6 6
7use crate::{ 7use crate::{
8 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, 8 item::{CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, ImportEdit},
9 render::{builder_ext::Params, RenderContext}, 9 render::{builder_ext::Params, RenderContext},
10}; 10};
11 11
@@ -41,16 +41,36 @@ impl<'a> FunctionRender<'a> {
41 41
42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { 42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
43 let params = self.params(); 43 let params = self.params();
44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) 44 let mut item = CompletionItem::new(
45 .kind(self.kind()) 45 CompletionKind::Reference,
46 self.ctx.source_range(),
47 self.name.clone(),
48 );
49 item.kind(self.kind())
46 .set_documentation(self.ctx.docs(self.func)) 50 .set_documentation(self.ctx.docs(self.func))
47 .set_deprecated( 51 .set_deprecated(
48 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), 52 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
49 ) 53 )
50 .detail(self.detail()) 54 .detail(self.detail())
51 .add_call_parens(self.ctx.completion, self.name, params) 55 .add_call_parens(self.ctx.completion, self.name, params)
52 .add_import(import_to_add) 56 .add_import(import_to_add);
53 .build() 57
58 let mut relevance = CompletionRelevance::default();
59 if let Some(expected_type) = &self.ctx.completion.expected_type {
60 let ret_ty = self.func.ret_type(self.ctx.db());
61
62 // We don't ever consider a function which returns unit type to be an
63 // exact type match, since nearly always this is not meaningful to the
64 // user.
65 relevance.exact_type_match = &ret_ty == expected_type && !ret_ty.is_unit();
66 }
67 if let Some(expected_name) = &self.ctx.completion.expected_name {
68 relevance.exact_name_match =
69 expected_name == &self.func.name(self.ctx.db()).to_string();
70 }
71 item.set_relevance(relevance);
72
73 item.build()
54 } 74 }
55 75
56 fn detail(&self) -> String { 76 fn detail(&self) -> String {
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index a6cf3e479..3fa21ba7c 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -39,29 +39,31 @@ impl<'a> MacroRender<'a> {
39 } 39 }
40 40
41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> { 41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
42 let mut builder = 42 let mut item =
43 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label()) 43 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label());
44 .kind(SymbolKind::Macro) 44 item.kind(SymbolKind::Macro)
45 .set_documentation(self.docs.clone()) 45 .set_documentation(self.docs.clone())
46 .set_deprecated(self.ctx.is_deprecated(self.macro_)) 46 .set_deprecated(self.ctx.is_deprecated(self.macro_))
47 .add_import(import_to_add) 47 .add_import(import_to_add)
48 .set_detail(self.detail()); 48 .set_detail(self.detail());
49 49
50 let needs_bang = self.needs_bang(); 50 let needs_bang = self.needs_bang();
51 builder = match self.ctx.snippet_cap() { 51 match self.ctx.snippet_cap() {
52 Some(cap) if needs_bang => { 52 Some(cap) if needs_bang => {
53 let snippet = self.snippet(); 53 let snippet = self.snippet();
54 let lookup = self.lookup(); 54 let lookup = self.lookup();
55 builder.insert_snippet(cap, snippet).lookup_by(lookup) 55 item.insert_snippet(cap, snippet).lookup_by(lookup);
56 }
57 None if needs_bang => {
58 item.insert_text(self.banged_name());
56 } 59 }
57 None if needs_bang => builder.insert_text(self.banged_name()),
58 _ => { 60 _ => {
59 cov_mark::hit!(dont_insert_macro_call_parens_unncessary); 61 cov_mark::hit!(dont_insert_macro_call_parens_unncessary);
60 builder.insert_text(&self.name) 62 item.insert_text(&self.name);
61 } 63 }
62 }; 64 };
63 65
64 Some(builder.build()) 66 Some(item.build())
65 } 67 }
66 68
67 fn needs_bang(&self) -> bool { 69 fn needs_bang(&self) -> bool {
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs
index 465dfe00c..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
7use crate::{item::CompletionKind, render::RenderContext, CompletionItem, CompletionItemKind}; 7use crate::{item::CompletionKind, render::RenderContext, CompletionItem, CompletionItemKind};
8 8
9fn 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
27pub(crate) fn render_struct_pat( 9pub(crate) fn render_struct_pat(
28 ctx: RenderContext<'_>, 10 ctx: RenderContext<'_>,
29 strukt: hir::Struct, 11 strukt: hir::Struct,
@@ -69,19 +51,19 @@ fn build_completion(
69 ctx: RenderContext<'_>, 51 ctx: RenderContext<'_>,
70 name: String, 52 name: String,
71 pat: String, 53 pat: String,
72 item: impl HasAttrs + Copy, 54 def: impl HasAttrs + Copy,
73) -> CompletionItem { 55) -> CompletionItem {
74 let completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) 56 let mut item = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name);
75 .kind(CompletionItemKind::Binding) 57 item.kind(CompletionItemKind::Binding)
76 .set_documentation(ctx.docs(item)) 58 .set_documentation(ctx.docs(def))
77 .set_deprecated(ctx.is_deprecated(item)) 59 .set_deprecated(ctx.is_deprecated(def))
78 .detail(&pat); 60 .detail(&pat);
79 let completion = if let Some(snippet_cap) = ctx.snippet_cap() { 61 if let Some(snippet_cap) = ctx.snippet_cap() {
80 completion.insert_snippet(snippet_cap, pat) 62 item.insert_snippet(snippet_cap, pat);
81 } else { 63 } else {
82 completion.insert_text(pat) 64 item.insert_text(pat);
83 }; 65 };
84 completion.build() 66 item.build()
85} 67}
86 68
87fn render_pat( 69fn render_pat(
@@ -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
134fn 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}
diff --git a/crates/ide_completion/src/render/type_alias.rs b/crates/ide_completion/src/render/type_alias.rs
index bd97c3692..e47b4c745 100644
--- a/crates/ide_completion/src/render/type_alias.rs
+++ b/crates/ide_completion/src/render/type_alias.rs
@@ -36,17 +36,17 @@ impl<'a> TypeAliasRender<'a> {
36 let name = self.name()?; 36 let name = self.name()?;
37 let detail = self.detail(); 37 let detail = self.detail();
38 38
39 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) 39 let mut item =
40 .kind(SymbolKind::TypeAlias) 40 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name);
41 item.kind(SymbolKind::TypeAlias)
41 .set_documentation(self.ctx.docs(self.type_alias)) 42 .set_documentation(self.ctx.docs(self.type_alias))
42 .set_deprecated( 43 .set_deprecated(
43 self.ctx.is_deprecated(self.type_alias) 44 self.ctx.is_deprecated(self.type_alias)
44 || self.ctx.is_deprecated_assoc_item(self.type_alias), 45 || self.ctx.is_deprecated_assoc_item(self.type_alias),
45 ) 46 )
46 .detail(detail) 47 .detail(detail);
47 .build();
48 48
49 Some(item) 49 Some(item.build())
50 } 50 }
51 51
52 fn name(&self) -> Option<String> { 52 fn name(&self) -> Option<String> {