diff options
Diffstat (limited to 'crates/ide_completion')
19 files changed, 446 insertions, 278 deletions
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml index 84aa40736..585ecca50 100644 --- a/crates/ide_completion/Cargo.toml +++ b/crates/ide_completion/Cargo.toml | |||
@@ -22,11 +22,11 @@ text_edit = { path = "../text_edit", version = "0.0.0" } | |||
22 | base_db = { path = "../base_db", version = "0.0.0" } | 22 | base_db = { path = "../base_db", version = "0.0.0" } |
23 | ide_db = { path = "../ide_db", version = "0.0.0" } | 23 | ide_db = { path = "../ide_db", version = "0.0.0" } |
24 | profile = { path = "../profile", version = "0.0.0" } | 24 | profile = { path = "../profile", version = "0.0.0" } |
25 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
26 | 25 | ||
27 | # completions crate should depend only on the top-level `hir` package. if you need | 26 | # completions crate should depend only on the top-level `hir` package. if you need |
28 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. | 27 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. |
29 | hir = { path = "../hir", version = "0.0.0" } | 28 | hir = { path = "../hir", version = "0.0.0" } |
30 | 29 | ||
31 | [dev-dependencies] | 30 | [dev-dependencies] |
31 | test_utils = { path = "../test_utils" } | ||
32 | expect-test = "1.1" | 32 | expect-test = "1.1" |
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/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/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/item.rs b/crates/ide_completion/src/item.rs index 9b039e3e5..3febab32b 100644 --- a/crates/ide_completion/src/item.rs +++ b/crates/ide_completion/src/item.rs | |||
@@ -63,12 +63,18 @@ pub struct CompletionItem { | |||
63 | /// after completion. | 63 | /// after completion. |
64 | trigger_call_info: bool, | 64 | trigger_call_info: bool, |
65 | 65 | ||
66 | /// Score is useful to pre select or display in better order completion items | 66 | /// We use this to sort completion. Relevance records facts like "do the |
67 | score: Option<CompletionScore>, | 67 | /// types align precisely?". We can't sort by relevances directly, they are |
68 | /// only partially ordered. | ||
69 | /// | ||
70 | /// Note that Relevance ignores fuzzy match score. We compute Relevance for | ||
71 | /// all possible items, and then separately build an ordered completion list | ||
72 | /// based on relevance and fuzzy matching with the already typed identifier. | ||
73 | relevance: CompletionRelevance, | ||
68 | 74 | ||
69 | /// 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 |
70 | /// possible match. | 76 | /// possible match. |
71 | ref_match: Option<(Mutability, CompletionScore)>, | 77 | ref_match: Option<Mutability>, |
72 | 78 | ||
73 | /// The import data to add to completion's edits. | 79 | /// The import data to add to completion's edits. |
74 | import_to_add: Option<ImportEdit>, | 80 | import_to_add: Option<ImportEdit>, |
@@ -101,8 +107,13 @@ impl fmt::Debug for CompletionItem { | |||
101 | if self.deprecated { | 107 | if self.deprecated { |
102 | s.field("deprecated", &true); | 108 | s.field("deprecated", &true); |
103 | } | 109 | } |
104 | if let Some(score) = &self.score { | 110 | |
105 | s.field("score", score); | 111 | if self.relevance != CompletionRelevance::default() { |
112 | s.field("relevance", &self.relevance); | ||
113 | } | ||
114 | |||
115 | if let Some(mutability) = &self.ref_match { | ||
116 | s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref())); | ||
106 | } | 117 | } |
107 | if self.trigger_call_info { | 118 | if self.trigger_call_info { |
108 | s.field("trigger_call_info", &true); | 119 | s.field("trigger_call_info", &true); |
@@ -111,12 +122,59 @@ impl fmt::Debug for CompletionItem { | |||
111 | } | 122 | } |
112 | } | 123 | } |
113 | 124 | ||
114 | #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)] | 125 | #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)] |
115 | pub enum CompletionScore { | 126 | pub struct CompletionRelevance { |
116 | /// If only type match | 127 | /// This is set in cases like these: |
117 | TypeMatch, | 128 | /// |
118 | /// If type and name match | 129 | /// ``` |
119 | TypeAndNameMatch, | 130 | /// fn f(spam: String) {} |
131 | /// fn main { | ||
132 | /// let spam = 92; | ||
133 | /// f($0) // name of local matches the name of param | ||
134 | /// } | ||
135 | /// ``` | ||
136 | pub exact_name_match: bool, | ||
137 | /// This is set in cases like these: | ||
138 | /// | ||
139 | /// ``` | ||
140 | /// fn f(spam: String) {} | ||
141 | /// fn main { | ||
142 | /// let foo = String::new(); | ||
143 | /// f($0) // type of local matches the type of param | ||
144 | /// } | ||
145 | /// ``` | ||
146 | pub exact_type_match: bool, | ||
147 | } | ||
148 | |||
149 | impl CompletionRelevance { | ||
150 | /// Provides a relevance score. Higher values are more relevant. | ||
151 | /// | ||
152 | /// The absolute value of the relevance score is not meaningful, for | ||
153 | /// example a value of 0 doesn't mean "not relevant", rather | ||
154 | /// it means "least relevant". The score value should only be used | ||
155 | /// for relative ordering. | ||
156 | /// | ||
157 | /// See is_relevant if you need to make some judgement about score | ||
158 | /// in an absolute sense. | ||
159 | pub fn score(&self) -> u32 { | ||
160 | let mut score = 0; | ||
161 | |||
162 | if self.exact_name_match { | ||
163 | score += 1; | ||
164 | } | ||
165 | if self.exact_type_match { | ||
166 | score += 1; | ||
167 | } | ||
168 | |||
169 | score | ||
170 | } | ||
171 | |||
172 | /// Returns true when the score for this threshold is above | ||
173 | /// some threshold such that we think it is especially likely | ||
174 | /// to be relevant. | ||
175 | pub fn is_relevant(&self) -> bool { | ||
176 | self.score() > 0 | ||
177 | } | ||
120 | } | 178 | } |
121 | 179 | ||
122 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 180 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
@@ -208,9 +266,9 @@ impl CompletionItem { | |||
208 | lookup: None, | 266 | lookup: None, |
209 | kind: None, | 267 | kind: None, |
210 | text_edit: None, | 268 | text_edit: None, |
211 | deprecated: None, | 269 | deprecated: false, |
212 | trigger_call_info: None, | 270 | trigger_call_info: None, |
213 | score: None, | 271 | relevance: CompletionRelevance::default(), |
214 | ref_match: None, | 272 | ref_match: None, |
215 | import_to_add: None, | 273 | import_to_add: None, |
216 | } | 274 | } |
@@ -253,16 +311,22 @@ impl CompletionItem { | |||
253 | self.deprecated | 311 | self.deprecated |
254 | } | 312 | } |
255 | 313 | ||
256 | pub fn score(&self) -> Option<CompletionScore> { | 314 | pub fn relevance(&self) -> CompletionRelevance { |
257 | self.score | 315 | self.relevance |
258 | } | 316 | } |
259 | 317 | ||
260 | pub fn trigger_call_info(&self) -> bool { | 318 | pub fn trigger_call_info(&self) -> bool { |
261 | self.trigger_call_info | 319 | self.trigger_call_info |
262 | } | 320 | } |
263 | 321 | ||
264 | pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> { | 322 | pub fn ref_match(&self) -> Option<(Mutability, CompletionRelevance)> { |
265 | self.ref_match | 323 | // Relevance of the ref match should be the same as the original |
324 | // match, but with exact type match set because self.ref_match | ||
325 | // is only set if there is an exact type match. | ||
326 | let mut relevance = self.relevance; | ||
327 | relevance.exact_type_match = true; | ||
328 | |||
329 | self.ref_match.map(|mutability| (mutability, relevance)) | ||
266 | } | 330 | } |
267 | 331 | ||
268 | pub fn import_to_add(&self) -> Option<&ImportEdit> { | 332 | pub fn import_to_add(&self) -> Option<&ImportEdit> { |
@@ -308,10 +372,10 @@ pub(crate) struct Builder { | |||
308 | lookup: Option<String>, | 372 | lookup: Option<String>, |
309 | kind: Option<CompletionItemKind>, | 373 | kind: Option<CompletionItemKind>, |
310 | text_edit: Option<TextEdit>, | 374 | text_edit: Option<TextEdit>, |
311 | deprecated: Option<bool>, | 375 | deprecated: bool, |
312 | trigger_call_info: Option<bool>, | 376 | trigger_call_info: Option<bool>, |
313 | score: Option<CompletionScore>, | 377 | relevance: CompletionRelevance, |
314 | ref_match: Option<(Mutability, CompletionScore)>, | 378 | ref_match: Option<Mutability>, |
315 | } | 379 | } |
316 | 380 | ||
317 | impl Builder { | 381 | impl Builder { |
@@ -355,49 +419,49 @@ impl Builder { | |||
355 | lookup, | 419 | lookup, |
356 | kind: self.kind, | 420 | kind: self.kind, |
357 | completion_kind: self.completion_kind, | 421 | completion_kind: self.completion_kind, |
358 | deprecated: self.deprecated.unwrap_or(false), | 422 | deprecated: self.deprecated, |
359 | trigger_call_info: self.trigger_call_info.unwrap_or(false), | 423 | trigger_call_info: self.trigger_call_info.unwrap_or(false), |
360 | score: self.score, | 424 | relevance: self.relevance, |
361 | ref_match: self.ref_match, | 425 | ref_match: self.ref_match, |
362 | import_to_add: self.import_to_add, | 426 | import_to_add: self.import_to_add, |
363 | } | 427 | } |
364 | } | 428 | } |
365 | pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { | 429 | pub(crate) fn lookup_by(&mut self, lookup: impl Into<String>) -> &mut Builder { |
366 | self.lookup = Some(lookup.into()); | 430 | self.lookup = Some(lookup.into()); |
367 | self | 431 | self |
368 | } | 432 | } |
369 | pub(crate) fn label(mut self, label: impl Into<String>) -> Builder { | 433 | pub(crate) fn label(&mut self, label: impl Into<String>) -> &mut Builder { |
370 | self.label = label.into(); | 434 | self.label = label.into(); |
371 | self | 435 | self |
372 | } | 436 | } |
373 | pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder { | 437 | pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder { |
374 | self.insert_text = Some(insert_text.into()); | 438 | self.insert_text = Some(insert_text.into()); |
375 | self | 439 | self |
376 | } | 440 | } |
377 | pub(crate) fn insert_snippet( | 441 | pub(crate) fn insert_snippet( |
378 | mut self, | 442 | &mut self, |
379 | _cap: SnippetCap, | 443 | _cap: SnippetCap, |
380 | snippet: impl Into<String>, | 444 | snippet: impl Into<String>, |
381 | ) -> Builder { | 445 | ) -> &mut Builder { |
382 | self.insert_text_format = InsertTextFormat::Snippet; | 446 | self.insert_text_format = InsertTextFormat::Snippet; |
383 | self.insert_text(snippet) | 447 | self.insert_text(snippet) |
384 | } | 448 | } |
385 | pub(crate) fn kind(mut self, kind: impl Into<CompletionItemKind>) -> Builder { | 449 | pub(crate) fn kind(&mut self, kind: impl Into<CompletionItemKind>) -> &mut Builder { |
386 | self.kind = Some(kind.into()); | 450 | self.kind = Some(kind.into()); |
387 | self | 451 | self |
388 | } | 452 | } |
389 | pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder { | 453 | pub(crate) fn text_edit(&mut self, edit: TextEdit) -> &mut Builder { |
390 | self.text_edit = Some(edit); | 454 | self.text_edit = Some(edit); |
391 | self | 455 | self |
392 | } | 456 | } |
393 | pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder { | 457 | pub(crate) fn snippet_edit(&mut self, _cap: SnippetCap, edit: TextEdit) -> &mut Builder { |
394 | self.insert_text_format = InsertTextFormat::Snippet; | 458 | self.insert_text_format = InsertTextFormat::Snippet; |
395 | self.text_edit(edit) | 459 | self.text_edit(edit) |
396 | } | 460 | } |
397 | pub(crate) fn detail(self, detail: impl Into<String>) -> Builder { | 461 | pub(crate) fn detail(&mut self, detail: impl Into<String>) -> &mut Builder { |
398 | self.set_detail(Some(detail)) | 462 | self.set_detail(Some(detail)) |
399 | } | 463 | } |
400 | pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { | 464 | pub(crate) fn set_detail(&mut self, detail: Option<impl Into<String>>) -> &mut Builder { |
401 | self.detail = detail.map(Into::into); | 465 | self.detail = detail.map(Into::into); |
402 | if let Some(detail) = &self.detail { | 466 | if let Some(detail) = &self.detail { |
403 | if never!(detail.contains('\n'), "multiline detail:\n{}", detail) { | 467 | if never!(detail.contains('\n'), "multiline detail:\n{}", detail) { |
@@ -407,34 +471,91 @@ impl Builder { | |||
407 | self | 471 | self |
408 | } | 472 | } |
409 | #[allow(unused)] | 473 | #[allow(unused)] |
410 | pub(crate) fn documentation(self, docs: Documentation) -> Builder { | 474 | pub(crate) fn documentation(&mut self, docs: Documentation) -> &mut Builder { |
411 | self.set_documentation(Some(docs)) | 475 | self.set_documentation(Some(docs)) |
412 | } | 476 | } |
413 | pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder { | 477 | pub(crate) fn set_documentation(&mut self, docs: Option<Documentation>) -> &mut Builder { |
414 | self.documentation = docs.map(Into::into); | 478 | self.documentation = docs.map(Into::into); |
415 | self | 479 | self |
416 | } | 480 | } |
417 | pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder { | 481 | pub(crate) fn set_deprecated(&mut self, deprecated: bool) -> &mut Builder { |
418 | self.deprecated = Some(deprecated); | 482 | self.deprecated = deprecated; |
419 | self | 483 | self |
420 | } | 484 | } |
421 | pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder { | 485 | pub(crate) fn set_relevance(&mut self, relevance: CompletionRelevance) -> &mut Builder { |
422 | self.score = Some(score); | 486 | self.relevance = relevance; |
423 | self | 487 | self |
424 | } | 488 | } |
425 | pub(crate) fn trigger_call_info(mut self) -> Builder { | 489 | pub(crate) fn trigger_call_info(&mut self) -> &mut Builder { |
426 | self.trigger_call_info = Some(true); | 490 | self.trigger_call_info = Some(true); |
427 | self | 491 | self |
428 | } | 492 | } |
429 | pub(crate) fn add_import(mut self, import_to_add: Option<ImportEdit>) -> Builder { | 493 | pub(crate) fn add_import(&mut self, import_to_add: Option<ImportEdit>) -> &mut Builder { |
430 | self.import_to_add = import_to_add; | 494 | self.import_to_add = import_to_add; |
431 | self | 495 | self |
432 | } | 496 | } |
433 | pub(crate) fn set_ref_match( | 497 | pub(crate) fn ref_match(&mut self, mutability: Mutability) -> &mut Builder { |
434 | mut self, | 498 | self.ref_match = Some(mutability); |
435 | ref_match: Option<(Mutability, CompletionScore)>, | ||
436 | ) -> Builder { | ||
437 | self.ref_match = ref_match; | ||
438 | self | 499 | self |
439 | } | 500 | } |
440 | } | 501 | } |
502 | |||
503 | #[cfg(test)] | ||
504 | mod tests { | ||
505 | use itertools::Itertools; | ||
506 | use test_utils::assert_eq_text; | ||
507 | |||
508 | use super::CompletionRelevance; | ||
509 | |||
510 | /// Check that these are CompletionRelevance are sorted in ascending order | ||
511 | /// by their relevance score. | ||
512 | /// | ||
513 | /// We want to avoid making assertions about the absolute score of any | ||
514 | /// item, but we do want to assert whether each is >, <, or == to the | ||
515 | /// others. | ||
516 | /// | ||
517 | /// If provided vec![vec![a], vec![b, c], vec![d]], then this will assert: | ||
518 | /// a.score < b.score == c.score < d.score | ||
519 | fn check_relevance_score_ordered(expected_relevance_order: Vec<Vec<CompletionRelevance>>) { | ||
520 | let expected = format!("{:#?}", &expected_relevance_order); | ||
521 | |||
522 | let actual_relevance_order = expected_relevance_order | ||
523 | .into_iter() | ||
524 | .flatten() | ||
525 | .map(|r| (r.score(), r)) | ||
526 | .sorted_by_key(|(score, _r)| *score) | ||
527 | .fold( | ||
528 | (u32::MIN, vec![vec![]]), | ||
529 | |(mut currently_collecting_score, mut out), (score, r)| { | ||
530 | if currently_collecting_score == score { | ||
531 | out.last_mut().unwrap().push(r); | ||
532 | } else { | ||
533 | currently_collecting_score = score; | ||
534 | out.push(vec![r]); | ||
535 | } | ||
536 | (currently_collecting_score, out) | ||
537 | }, | ||
538 | ) | ||
539 | .1; | ||
540 | |||
541 | let actual = format!("{:#?}", &actual_relevance_order); | ||
542 | |||
543 | assert_eq_text!(&expected, &actual); | ||
544 | } | ||
545 | |||
546 | #[test] | ||
547 | fn relevance_score() { | ||
548 | // This test asserts that the relevance score for these items is ascending, and | ||
549 | // that any items in the same vec have the same score. | ||
550 | let expected_relevance_order = vec![ | ||
551 | vec![CompletionRelevance::default()], | ||
552 | vec![ | ||
553 | CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() }, | ||
554 | CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }, | ||
555 | ], | ||
556 | vec![CompletionRelevance { exact_name_match: true, exact_type_match: true }], | ||
557 | ]; | ||
558 | |||
559 | check_relevance_score_ordered(expected_relevance_order); | ||
560 | } | ||
561 | } | ||
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index a0c8c374d..263554ecf 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs | |||
@@ -23,7 +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::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat}, | 26 | item::{CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit, InsertTextFormat}, |
27 | }; | 27 | }; |
28 | 28 | ||
29 | //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 fae5685e2..db31896e5 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -20,8 +20,8 @@ use ide_db::{ | |||
20 | use syntax::TextRange; | 20 | use syntax::TextRange; |
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, | 23 | item::{CompletionRelevance, ImportEdit}, |
24 | CompletionScore, | 24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; | 27 | use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; |
@@ -117,7 +117,7 @@ impl<'a> RenderContext<'a> { | |||
117 | node.docs(self.db()) | 117 | node.docs(self.db()) |
118 | } | 118 | } |
119 | 119 | ||
120 | fn active_name_and_type(&self) -> Option<(String, Type)> { | 120 | fn expected_name_and_type(&self) -> Option<(String, Type)> { |
121 | if let Some(record_field) = &self.completion.record_field_syntax { | 121 | if let Some(record_field) = &self.completion.record_field_syntax { |
122 | cov_mark::hit!(record_field_type_match); | 122 | cov_mark::hit!(record_field_type_match); |
123 | let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; | 123 | let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; |
@@ -149,24 +149,29 @@ impl<'a> Render<'a> { | |||
149 | CompletionKind::Reference, | 149 | CompletionKind::Reference, |
150 | self.ctx.source_range(), | 150 | self.ctx.source_range(), |
151 | name.to_string(), | 151 | name.to_string(), |
152 | ) | 152 | ); |
153 | .kind(SymbolKind::Field) | 153 | item.kind(SymbolKind::Field) |
154 | .detail(ty.display(self.ctx.db()).to_string()) | 154 | .detail(ty.display(self.ctx.db()).to_string()) |
155 | .set_documentation(field.docs(self.ctx.db())) | 155 | .set_documentation(field.docs(self.ctx.db())) |
156 | .set_deprecated(is_deprecated); | 156 | .set_deprecated(is_deprecated); |
157 | 157 | ||
158 | if let Some(score) = compute_score(&self.ctx, &ty, &name.to_string()) { | 158 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) { |
159 | item = item.set_score(score); | 159 | item.set_relevance(relevance); |
160 | } | 160 | } |
161 | 161 | ||
162 | item.build() | 162 | item.build() |
163 | } | 163 | } |
164 | 164 | ||
165 | fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { | 165 | fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { |
166 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string()) | 166 | let mut item = CompletionItem::new( |
167 | .kind(SymbolKind::Field) | 167 | CompletionKind::Reference, |
168 | .detail(ty.display(self.ctx.db()).to_string()) | 168 | self.ctx.source_range(), |
169 | .build() | 169 | field.to_string(), |
170 | ); | ||
171 | |||
172 | item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string()); | ||
173 | |||
174 | item.build() | ||
170 | } | 175 | } |
171 | 176 | ||
172 | fn render_resolution( | 177 | fn render_resolution( |
@@ -225,15 +230,13 @@ impl<'a> Render<'a> { | |||
225 | CompletionItemKind::SymbolKind(SymbolKind::SelfParam) | 230 | CompletionItemKind::SymbolKind(SymbolKind::SelfParam) |
226 | } | 231 | } |
227 | ScopeDef::Unknown => { | 232 | ScopeDef::Unknown => { |
228 | let item = CompletionItem::new( | 233 | let mut item = CompletionItem::new( |
229 | CompletionKind::Reference, | 234 | CompletionKind::Reference, |
230 | self.ctx.source_range(), | 235 | self.ctx.source_range(), |
231 | local_name, | 236 | local_name, |
232 | ) | 237 | ); |
233 | .kind(CompletionItemKind::UnresolvedReference) | 238 | item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add); |
234 | .add_import(import_to_add) | 239 | return Some(item.build()); |
235 | .build(); | ||
236 | return Some(item); | ||
237 | } | 240 | } |
238 | }; | 241 | }; |
239 | 242 | ||
@@ -242,20 +245,27 @@ impl<'a> Render<'a> { | |||
242 | if let ScopeDef::Local(local) = resolution { | 245 | if let ScopeDef::Local(local) = resolution { |
243 | let ty = local.ty(self.ctx.db()); | 246 | let ty = local.ty(self.ctx.db()); |
244 | if !ty.is_unknown() { | 247 | if !ty.is_unknown() { |
245 | item = item.detail(ty.display(self.ctx.db()).to_string()); | 248 | item.detail(ty.display(self.ctx.db()).to_string()); |
246 | } | 249 | } |
247 | }; | 250 | }; |
248 | 251 | ||
249 | let mut ref_match = None; | ||
250 | if let ScopeDef::Local(local) = resolution { | 252 | if let ScopeDef::Local(local) = resolution { |
251 | if let Some((active_name, active_type)) = self.ctx.active_name_and_type() { | 253 | let ty = local.ty(self.ctx.db()); |
252 | let ty = local.ty(self.ctx.db()); | 254 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) { |
253 | if let Some(score) = | 255 | item.set_relevance(relevance); |
254 | compute_score_from_active(&active_type, &active_name, &ty, &local_name) | 256 | } |
255 | { | 257 | if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { |
256 | item = item.set_score(score); | 258 | if let Some(ty_without_ref) = expected_type.remove_ref() { |
259 | if ty_without_ref == ty { | ||
260 | cov_mark::hit!(suggest_ref); | ||
261 | let mutability = if expected_type.is_mutable_reference() { | ||
262 | Mutability::Mut | ||
263 | } else { | ||
264 | Mutability::Shared | ||
265 | }; | ||
266 | item.ref_match(mutability); | ||
267 | } | ||
257 | } | 268 | } |
258 | ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name); | ||
259 | } | 269 | } |
260 | } | 270 | } |
261 | 271 | ||
@@ -274,22 +284,17 @@ impl<'a> Render<'a> { | |||
274 | }; | 284 | }; |
275 | if has_non_default_type_params { | 285 | if has_non_default_type_params { |
276 | cov_mark::hit!(inserts_angle_brackets_for_generics); | 286 | cov_mark::hit!(inserts_angle_brackets_for_generics); |
277 | item = item | 287 | item.lookup_by(local_name.clone()) |
278 | .lookup_by(local_name.clone()) | ||
279 | .label(format!("{}<…>", local_name)) | 288 | .label(format!("{}<…>", local_name)) |
280 | .insert_snippet(cap, format!("{}<$0>", local_name)); | 289 | .insert_snippet(cap, format!("{}<$0>", local_name)); |
281 | } | 290 | } |
282 | } | 291 | } |
283 | } | 292 | } |
284 | 293 | item.kind(kind) | |
285 | Some( | 294 | .add_import(import_to_add) |
286 | item.kind(kind) | 295 | .set_documentation(self.docs(resolution)) |
287 | .add_import(import_to_add) | 296 | .set_deprecated(self.is_deprecated(resolution)); |
288 | .set_ref_match(ref_match) | 297 | Some(item.build()) |
289 | .set_documentation(self.docs(resolution)) | ||
290 | .set_deprecated(self.is_deprecated(resolution)) | ||
291 | .build(), | ||
292 | ) | ||
293 | } | 298 | } |
294 | 299 | ||
295 | fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { | 300 | fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { |
@@ -317,45 +322,13 @@ impl<'a> Render<'a> { | |||
317 | } | 322 | } |
318 | } | 323 | } |
319 | 324 | ||
320 | fn compute_score_from_active( | 325 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionRelevance> { |
321 | active_type: &Type, | 326 | let (expected_name, expected_type) = ctx.expected_name_and_type()?; |
322 | active_name: &str, | 327 | let mut res = CompletionRelevance::default(); |
323 | ty: &Type, | 328 | res.exact_type_match = ty == &expected_type; |
324 | name: &str, | 329 | res.exact_name_match = name == &expected_name; |
325 | ) -> Option<CompletionScore> { | ||
326 | // Compute score | ||
327 | // For the same type | ||
328 | if active_type != ty { | ||
329 | return None; | ||
330 | } | ||
331 | |||
332 | let mut res = CompletionScore::TypeMatch; | ||
333 | |||
334 | // If same type + same name then go top position | ||
335 | if active_name == name { | ||
336 | res = CompletionScore::TypeAndNameMatch | ||
337 | } | ||
338 | |||
339 | Some(res) | 330 | Some(res) |
340 | } | 331 | } |
341 | fn refed_type_matches( | ||
342 | active_type: &Type, | ||
343 | active_name: &str, | ||
344 | ty: &Type, | ||
345 | name: &str, | ||
346 | ) -> Option<(Mutability, CompletionScore)> { | ||
347 | let derefed_active = active_type.remove_ref()?; | ||
348 | let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?; | ||
349 | Some(( | ||
350 | if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared }, | ||
351 | score, | ||
352 | )) | ||
353 | } | ||
354 | |||
355 | fn compute_score(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionScore> { | ||
356 | let (active_name, active_type) = ctx.active_name_and_type()?; | ||
357 | compute_score_from_active(&active_type, &active_name, ty, name) | ||
358 | } | ||
359 | 332 | ||
360 | #[cfg(test)] | 333 | #[cfg(test)] |
361 | mod tests { | 334 | mod tests { |
@@ -365,7 +338,7 @@ mod tests { | |||
365 | 338 | ||
366 | use crate::{ | 339 | use crate::{ |
367 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, | 340 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, |
368 | CompletionKind, CompletionScore, | 341 | CompletionKind, CompletionRelevance, |
369 | }; | 342 | }; |
370 | 343 | ||
371 | fn check(ra_fixture: &str, expect: Expect) { | 344 | fn check(ra_fixture: &str, expect: Expect) { |
@@ -373,24 +346,27 @@ mod tests { | |||
373 | expect.assert_debug_eq(&actual); | 346 | expect.assert_debug_eq(&actual); |
374 | } | 347 | } |
375 | 348 | ||
376 | fn check_scores(ra_fixture: &str, expect: Expect) { | 349 | fn check_relevance(ra_fixture: &str, expect: Expect) { |
377 | fn display_score(score: Option<CompletionScore>) -> &'static str { | 350 | fn display_relevance(relevance: CompletionRelevance) -> &'static str { |
378 | match score { | 351 | match relevance { |
379 | Some(CompletionScore::TypeMatch) => "[type]", | 352 | CompletionRelevance { exact_type_match: true, exact_name_match: true } => { |
380 | Some(CompletionScore::TypeAndNameMatch) => "[type+name]", | 353 | "[type+name]" |
381 | None => "[]".into(), | 354 | } |
355 | CompletionRelevance { exact_type_match: true, exact_name_match: false } => "[type]", | ||
356 | CompletionRelevance { exact_type_match: false, exact_name_match: true } => "[name]", | ||
357 | CompletionRelevance { exact_type_match: false, exact_name_match: false } => "[]", | ||
382 | } | 358 | } |
383 | } | 359 | } |
384 | 360 | ||
385 | let mut completions = get_all_items(TEST_CONFIG, ra_fixture); | 361 | let mut completions = get_all_items(TEST_CONFIG, ra_fixture); |
386 | completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); | 362 | completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string())); |
387 | let actual = completions | 363 | let actual = completions |
388 | .into_iter() | 364 | .into_iter() |
389 | .filter(|it| it.completion_kind == CompletionKind::Reference) | 365 | .filter(|it| it.completion_kind == CompletionKind::Reference) |
390 | .map(|it| { | 366 | .map(|it| { |
391 | let tag = it.kind().unwrap().tag(); | 367 | let tag = it.kind().unwrap().tag(); |
392 | let score = display_score(it.score()); | 368 | let relevance = display_relevance(it.relevance()); |
393 | format!("{} {} {}\n", tag, it.label(), score) | 369 | format!("{} {} {}\n", tag, it.label(), relevance) |
394 | }) | 370 | }) |
395 | .collect::<String>(); | 371 | .collect::<String>(); |
396 | expect.assert_eq(&actual); | 372 | expect.assert_eq(&actual); |
@@ -853,9 +829,9 @@ fn foo(xs: Vec<i128>) | |||
853 | } | 829 | } |
854 | 830 | ||
855 | #[test] | 831 | #[test] |
856 | fn active_param_score() { | 832 | fn active_param_relevance() { |
857 | cov_mark::check!(active_param_type_match); | 833 | cov_mark::check!(active_param_type_match); |
858 | check_scores( | 834 | check_relevance( |
859 | r#" | 835 | r#" |
860 | struct S { foo: i64, bar: u32, baz: u32 } | 836 | struct S { foo: i64, bar: u32, baz: u32 } |
861 | fn test(bar: u32) { } | 837 | fn test(bar: u32) { } |
@@ -870,9 +846,9 @@ fn foo(s: S) { test(s.$0) } | |||
870 | } | 846 | } |
871 | 847 | ||
872 | #[test] | 848 | #[test] |
873 | fn record_field_scores() { | 849 | fn record_field_relevances() { |
874 | cov_mark::check!(record_field_type_match); | 850 | cov_mark::check!(record_field_type_match); |
875 | check_scores( | 851 | check_relevance( |
876 | r#" | 852 | r#" |
877 | struct A { foo: i64, bar: u32, baz: u32 } | 853 | struct A { foo: i64, bar: u32, baz: u32 } |
878 | struct B { x: (), y: f32, bar: u32 } | 854 | struct B { x: (), y: f32, bar: u32 } |
@@ -887,8 +863,8 @@ fn foo(a: A) { B { bar: a.$0 }; } | |||
887 | } | 863 | } |
888 | 864 | ||
889 | #[test] | 865 | #[test] |
890 | fn record_field_and_call_scores() { | 866 | fn record_field_and_call_relevances() { |
891 | check_scores( | 867 | check_relevance( |
892 | r#" | 868 | r#" |
893 | struct A { foo: i64, bar: u32, baz: u32 } | 869 | struct A { foo: i64, bar: u32, baz: u32 } |
894 | struct B { x: (), y: f32, bar: u32 } | 870 | struct B { x: (), y: f32, bar: u32 } |
@@ -901,7 +877,7 @@ fn foo(a: A) { B { bar: f(a.$0) }; } | |||
901 | fd baz [] | 877 | fd baz [] |
902 | "#]], | 878 | "#]], |
903 | ); | 879 | ); |
904 | check_scores( | 880 | check_relevance( |
905 | r#" | 881 | r#" |
906 | struct A { foo: i64, bar: u32, baz: u32 } | 882 | struct A { foo: i64, bar: u32, baz: u32 } |
907 | struct B { x: (), y: f32, bar: u32 } | 883 | struct B { x: (), y: f32, bar: u32 } |
@@ -918,7 +894,7 @@ fn foo(a: A) { f(B { bar: a.$0 }); } | |||
918 | 894 | ||
919 | #[test] | 895 | #[test] |
920 | fn prioritize_exact_ref_match() { | 896 | fn prioritize_exact_ref_match() { |
921 | check_scores( | 897 | check_relevance( |
922 | r#" | 898 | r#" |
923 | struct WorldSnapshot { _f: () }; | 899 | struct WorldSnapshot { _f: () }; |
924 | fn go(world: &WorldSnapshot) { go(w$0) } | 900 | fn go(world: &WorldSnapshot) { go(w$0) } |
@@ -933,7 +909,7 @@ fn go(world: &WorldSnapshot) { go(w$0) } | |||
933 | 909 | ||
934 | #[test] | 910 | #[test] |
935 | fn too_many_arguments() { | 911 | fn too_many_arguments() { |
936 | check_scores( | 912 | check_relevance( |
937 | r#" | 913 | r#" |
938 | struct Foo; | 914 | struct Foo; |
939 | fn f(foo: &Foo) { f(foo, w$0) } | 915 | fn f(foo: &Foo) { f(foo, w$0) } |
@@ -945,4 +921,70 @@ fn f(foo: &Foo) { f(foo, w$0) } | |||
945 | "#]], | 921 | "#]], |
946 | ); | 922 | ); |
947 | } | 923 | } |
924 | |||
925 | #[test] | ||
926 | fn suggest_ref_mut() { | ||
927 | cov_mark::check!(suggest_ref); | ||
928 | check( | ||
929 | r#" | ||
930 | struct S; | ||
931 | fn foo(s: &mut S) {} | ||
932 | fn main() { | ||
933 | let mut s = S; | ||
934 | foo($0); | ||
935 | } | ||
936 | "#, | ||
937 | expect![[r#" | ||
938 | [ | ||
939 | CompletionItem { | ||
940 | label: "S", | ||
941 | source_range: 70..70, | ||
942 | delete: 70..70, | ||
943 | insert: "S", | ||
944 | kind: SymbolKind( | ||
945 | Struct, | ||
946 | ), | ||
947 | }, | ||
948 | CompletionItem { | ||
949 | label: "foo(…)", | ||
950 | source_range: 70..70, | ||
951 | delete: 70..70, | ||
952 | insert: "foo(${1:&mut s})$0", | ||
953 | kind: SymbolKind( | ||
954 | Function, | ||
955 | ), | ||
956 | lookup: "foo", | ||
957 | detail: "-> ()", | ||
958 | trigger_call_info: true, | ||
959 | }, | ||
960 | CompletionItem { | ||
961 | label: "main()", | ||
962 | source_range: 70..70, | ||
963 | delete: 70..70, | ||
964 | insert: "main()$0", | ||
965 | kind: SymbolKind( | ||
966 | Function, | ||
967 | ), | ||
968 | lookup: "main", | ||
969 | detail: "-> ()", | ||
970 | }, | ||
971 | CompletionItem { | ||
972 | label: "s", | ||
973 | source_range: 70..70, | ||
974 | delete: 70..70, | ||
975 | insert: "s", | ||
976 | kind: SymbolKind( | ||
977 | Local, | ||
978 | ), | ||
979 | detail: "S", | ||
980 | relevance: CompletionRelevance { | ||
981 | exact_name_match: true, | ||
982 | exact_type_match: false, | ||
983 | }, | ||
984 | ref_match: "&mut ", | ||
985 | }, | ||
986 | ] | ||
987 | "#]], | ||
988 | ) | ||
989 | } | ||
948 | } | 990 | } |
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..f4dabe3d1 100644 --- a/crates/ide_completion/src/render/function.rs +++ b/crates/ide_completion/src/render/function.rs | |||
@@ -41,16 +41,21 @@ 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 | item.build() | ||
54 | } | 59 | } |
55 | 60 | ||
56 | fn detail(&self) -> String { | 61 | 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..ca2926125 100644 --- a/crates/ide_completion/src/render/pattern.rs +++ b/crates/ide_completion/src/render/pattern.rs | |||
@@ -69,19 +69,19 @@ fn build_completion( | |||
69 | ctx: RenderContext<'_>, | 69 | ctx: RenderContext<'_>, |
70 | name: String, | 70 | name: String, |
71 | pat: String, | 71 | pat: String, |
72 | item: impl HasAttrs + Copy, | 72 | def: impl HasAttrs + Copy, |
73 | ) -> CompletionItem { | 73 | ) -> CompletionItem { |
74 | let completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) | 74 | let mut item = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name); |
75 | .kind(CompletionItemKind::Binding) | 75 | item.kind(CompletionItemKind::Binding) |
76 | .set_documentation(ctx.docs(item)) | 76 | .set_documentation(ctx.docs(def)) |
77 | .set_deprecated(ctx.is_deprecated(item)) | 77 | .set_deprecated(ctx.is_deprecated(def)) |
78 | .detail(&pat); | 78 | .detail(&pat); |
79 | let completion = if let Some(snippet_cap) = ctx.snippet_cap() { | 79 | if let Some(snippet_cap) = ctx.snippet_cap() { |
80 | completion.insert_snippet(snippet_cap, pat) | 80 | item.insert_snippet(snippet_cap, pat); |
81 | } else { | 81 | } else { |
82 | completion.insert_text(pat) | 82 | item.insert_text(pat); |
83 | }; | 83 | }; |
84 | completion.build() | 84 | item.build() |
85 | } | 85 | } |
86 | 86 | ||
87 | fn render_pat( | 87 | fn render_pat( |
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> { |