diff options
Diffstat (limited to 'crates/ide_completion')
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" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | 13 | cov-mark = { version = "1.1", features = ["thread-local"] } |
14 | itertools = "0.10.0" | 14 | itertools = "0.10.0" |
15 | log = "0.4.8" | 15 | log = "0.4.8" |
16 | rustc-hash = "1.1.0" | 16 | rustc-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 | ||
167 | fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) { | 164 | fn 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; | |||
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.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. |
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/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 | ||
10 | fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { | 10 | fn 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 | ||
16 | pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { | 16 | pub(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 { | |||
62 | fn ${1:feature}() { | 62 | fn ${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 | ||
196 | fn add_const_impl( | 195 | fn 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; | |||
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 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)] | ||
124 | pub 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)] |
132 | pub struct Relevance { | 126 | pub 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 | ||
155 | impl Relevance { | 164 | impl 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)] | ||
522 | mod 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 | ||
24 | pub use crate::{ | 24 | pub 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 | ||
11 | mod builder_ext; | 11 | mod builder_ext; |
12 | 12 | ||
13 | use base_db::Upcast; | ||
13 | use hir::{ | 14 | use hir::{ |
14 | AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, | 15 | db::HirDatabase, AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, |
16 | ScopeDef, Type, | ||
15 | }; | 17 | }; |
16 | use ide_db::{ | 18 | use ide_db::{ |
17 | helpers::{item_name, SnippetCap}, | 19 | helpers::{item_name, SnippetCap}, |
@@ -20,7 +22,7 @@ use ide_db::{ | |||
20 | use syntax::TextRange; | 22 | use syntax::TextRange; |
21 | 23 | ||
22 | use crate::{ | 24 | use 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 | ||
326 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<Relevance> { | 316 | fn 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 | |||
325 | fn 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)] |
335 | mod tests { | 330 | mod 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#" |
835 | struct S { foo: i64, bar: u32, baz: u32 } | 842 | struct S { foo: i64, bar: u32, baz: u32 } |
@@ -837,16 +844,15 @@ fn test(bar: u32) { } | |||
837 | fn foo(s: S) { test(s.$0) } | 844 | fn 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#" |
852 | struct A { foo: i64, bar: u32, baz: u32 } | 858 | struct A { foo: i64, bar: u32, baz: u32 } |
@@ -854,9 +860,9 @@ struct B { x: (), y: f32, bar: u32 } | |||
854 | fn foo(a: A) { B { bar: a.$0 }; } | 860 | fn 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) { } | |||
884 | fn foo(a: A) { f(B { bar: a.$0 }); } | 890 | fn 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: () }; | |||
899 | fn go(world: &WorldSnapshot) { go(w$0) } | 905 | fn 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; | |||
914 | fn f(foo: &Foo) { f(foo, w$0) } | 920 | fn 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#" | ||
934 | struct A { bar: u8 } | ||
935 | fn baz() -> u8 { 0 } | ||
936 | fn bar() -> u8 { 0 } | ||
937 | fn 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#" | ||
952 | fn baz(aaa: u32){} | ||
953 | struct Foo; | ||
954 | impl Foo { | ||
955 | fn aaa(&self) -> u32 { 0 } | ||
956 | fn bbb(&self) -> u32 { 0 } | ||
957 | fn ccc(&self) -> u64 { 0 } | ||
958 | } | ||
959 | fn 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#" | ||
975 | fn baz(aaa: u32){} | ||
976 | struct Foo; | ||
977 | impl Foo { | ||
978 | fn aaa(&self) -> u64 { 0 } | ||
979 | } | ||
980 | fn 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"] | ||
1062 | trait Deref { | ||
1063 | type Target; | ||
1064 | fn deref(&self) -> &Self::Target; | ||
1065 | } | ||
1066 | |||
1067 | struct S; | ||
1068 | struct T(S); | ||
1069 | |||
1070 | impl Deref for T { | ||
1071 | type Target = S; | ||
1072 | |||
1073 | fn deref(&self) -> &Self::Target { | ||
1074 | &self.0 | ||
1075 | } | ||
1076 | } | ||
1077 | |||
1078 | fn foo(s: &S) {} | ||
1079 | |||
1080 | fn 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"] | ||
1105 | trait Deref { | ||
1106 | type Target; | ||
1107 | fn deref(&self) -> &Self::Target; | ||
1108 | } | ||
1109 | |||
1110 | #[lang = "deref_mut"] | ||
1111 | pub trait DerefMut: Deref { | ||
1112 | fn deref_mut(&mut self) -> &mut Self::Target; | ||
1113 | } | ||
1114 | |||
1115 | struct S; | ||
1116 | struct T(S); | ||
1117 | |||
1118 | impl Deref for T { | ||
1119 | type Target = S; | ||
1120 | |||
1121 | fn deref(&self) -> &Self::Target { | ||
1122 | &self.0 | ||
1123 | } | ||
1124 | } | ||
1125 | |||
1126 | impl DerefMut for T { | ||
1127 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
1128 | &mut self.0 | ||
1129 | } | ||
1130 | } | ||
1131 | |||
1132 | fn foo(s: &mut S) {} | ||
1133 | |||
1134 | fn 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#" | ||
1159 | fn 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; | |||
5 | use syntax::ast::Fn; | 5 | use syntax::ast::Fn; |
6 | 6 | ||
7 | use crate::{ | 7 | use 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 | ||
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, |
@@ -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 | ||
87 | fn render_pat( | 69 | fn 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 | |||
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 | } | ||
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> { |