diff options
Diffstat (limited to 'crates/ide_completion')
18 files changed, 504 insertions, 244 deletions
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 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..905f0b197 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 | ||
@@ -149,24 +151,27 @@ impl<'a> Render<'a> { | |||
149 | CompletionKind::Reference, | 151 | CompletionKind::Reference, |
150 | self.ctx.source_range(), | 152 | self.ctx.source_range(), |
151 | name.to_string(), | 153 | name.to_string(), |
152 | ) | 154 | ); |
153 | .kind(SymbolKind::Field) | 155 | item.kind(SymbolKind::Field) |
154 | .detail(ty.display(self.ctx.db()).to_string()) | 156 | .detail(ty.display(self.ctx.db()).to_string()) |
155 | .set_documentation(field.docs(self.ctx.db())) | 157 | .set_documentation(field.docs(self.ctx.db())) |
156 | .set_deprecated(is_deprecated); | 158 | .set_deprecated(is_deprecated); |
157 | 159 | ||
158 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) { | 160 | item.set_relevance(compute_relevance(&self.ctx, &ty, &name.to_string())); |
159 | item = item.set_relevance(relevance); | ||
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,25 +245,29 @@ 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 | if let ScopeDef::Local(local) = resolution { | 252 | if let ScopeDef::Local(local) = resolution { |
250 | let ty = local.ty(self.ctx.db()); | 253 | let ty = local.ty(self.ctx.db()); |
251 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) { | 254 | |
252 | item = item.set_relevance(relevance) | 255 | let mut relevance = compute_relevance(&self.ctx, &ty, &local_name); |
253 | } | 256 | relevance.is_local = true; |
257 | item.set_relevance(relevance); | ||
258 | |||
254 | if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { | 259 | if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { |
255 | if let Some(ty_without_ref) = expected_type.remove_ref() { | 260 | if ty != expected_type { |
256 | if ty_without_ref == ty { | 261 | if let Some(ty_without_ref) = expected_type.remove_ref() { |
257 | cov_mark::hit!(suggest_ref); | 262 | if relevance_type_match(self.ctx.db().upcast(), &ty, &ty_without_ref) { |
258 | let mutability = if expected_type.is_mutable_reference() { | 263 | cov_mark::hit!(suggest_ref); |
259 | Mutability::Mut | 264 | let mutability = if expected_type.is_mutable_reference() { |
260 | } else { | 265 | Mutability::Mut |
261 | Mutability::Shared | 266 | } else { |
262 | }; | 267 | Mutability::Shared |
263 | item = item.ref_match(mutability) | 268 | }; |
269 | item.ref_match(mutability); | ||
270 | } | ||
264 | } | 271 | } |
265 | } | 272 | } |
266 | } | 273 | } |
@@ -281,21 +288,17 @@ impl<'a> Render<'a> { | |||
281 | }; | 288 | }; |
282 | if has_non_default_type_params { | 289 | if has_non_default_type_params { |
283 | cov_mark::hit!(inserts_angle_brackets_for_generics); | 290 | cov_mark::hit!(inserts_angle_brackets_for_generics); |
284 | item = item | 291 | item.lookup_by(local_name.clone()) |
285 | .lookup_by(local_name.clone()) | ||
286 | .label(format!("{}<…>", local_name)) | 292 | .label(format!("{}<…>", local_name)) |
287 | .insert_snippet(cap, format!("{}<$0>", local_name)); | 293 | .insert_snippet(cap, format!("{}<$0>", local_name)); |
288 | } | 294 | } |
289 | } | 295 | } |
290 | } | 296 | } |
291 | 297 | item.kind(kind) | |
292 | Some( | 298 | .add_import(import_to_add) |
293 | item.kind(kind) | 299 | .set_documentation(self.docs(resolution)) |
294 | .add_import(import_to_add) | 300 | .set_deprecated(self.is_deprecated(resolution)); |
295 | .set_documentation(self.docs(resolution)) | 301 | Some(item.build()) |
296 | .set_deprecated(self.is_deprecated(resolution)) | ||
297 | .build(), | ||
298 | ) | ||
299 | } | 302 | } |
300 | 303 | ||
301 | fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { | 304 | fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { |
@@ -323,23 +326,29 @@ impl<'a> Render<'a> { | |||
323 | } | 326 | } |
324 | } | 327 | } |
325 | 328 | ||
326 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<Relevance> { | 329 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> CompletionRelevance { |
327 | let (expected_name, expected_type) = ctx.expected_name_and_type()?; | 330 | let mut res = CompletionRelevance::default(); |
328 | let mut res = Relevance::default(); | 331 | |
329 | res.exact_type_match = ty == &expected_type; | 332 | if let Some((expected_name, expected_type)) = ctx.expected_name_and_type() { |
330 | res.exact_name_match = name == &expected_name; | 333 | res.exact_type_match = ty == &expected_type; |
331 | Some(res) | 334 | res.exact_name_match = name == &expected_name; |
335 | } | ||
336 | |||
337 | res | ||
338 | } | ||
339 | |||
340 | fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -> bool { | ||
341 | ty == expected_type || ty.autoderef(db).any(|deref_ty| &deref_ty == expected_type) | ||
332 | } | 342 | } |
333 | 343 | ||
334 | #[cfg(test)] | 344 | #[cfg(test)] |
335 | mod tests { | 345 | mod tests { |
336 | use std::cmp::Reverse; | ||
337 | |||
338 | use expect_test::{expect, Expect}; | 346 | use expect_test::{expect, Expect}; |
347 | use itertools::Itertools; | ||
339 | 348 | ||
340 | use crate::{ | 349 | use crate::{ |
341 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, | 350 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, |
342 | CompletionKind, Relevance, | 351 | CompletionKind, CompletionRelevance, |
343 | }; | 352 | }; |
344 | 353 | ||
345 | fn check(ra_fixture: &str, expect: Expect) { | 354 | fn check(ra_fixture: &str, expect: Expect) { |
@@ -348,26 +357,40 @@ mod tests { | |||
348 | } | 357 | } |
349 | 358 | ||
350 | fn check_relevance(ra_fixture: &str, expect: Expect) { | 359 | fn check_relevance(ra_fixture: &str, expect: Expect) { |
351 | fn display_relevance(relevance: Relevance) -> &'static str { | 360 | fn display_relevance(relevance: CompletionRelevance) -> String { |
352 | match relevance { | 361 | let relevance_factors = vec![ |
353 | Relevance { exact_type_match: true, exact_name_match: true } => "[type+name]", | 362 | (relevance.exact_type_match, "type"), |
354 | Relevance { exact_type_match: true, exact_name_match: false } => "[type]", | 363 | (relevance.exact_name_match, "name"), |
355 | Relevance { exact_type_match: false, exact_name_match: true } => "[name]", | 364 | (relevance.is_local, "local"), |
356 | Relevance { exact_type_match: false, exact_name_match: false } => "[]", | 365 | ] |
357 | } | 366 | .into_iter() |
367 | .filter_map(|(cond, desc)| if cond { Some(desc) } else { None }) | ||
368 | .join("+"); | ||
369 | |||
370 | format!("[{}]", relevance_factors) | ||
358 | } | 371 | } |
359 | 372 | ||
360 | let mut completions = get_all_items(TEST_CONFIG, ra_fixture); | 373 | 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() | 374 | .into_iter() |
364 | .filter(|it| it.completion_kind == CompletionKind::Reference) | 375 | .filter(|it| it.completion_kind == CompletionKind::Reference) |
365 | .map(|it| { | 376 | .flat_map(|it| { |
377 | let mut items = vec![]; | ||
378 | |||
366 | let tag = it.kind().unwrap().tag(); | 379 | let tag = it.kind().unwrap().tag(); |
367 | let relevance = display_relevance(it.relevance()); | 380 | let relevance = display_relevance(it.relevance()); |
368 | format!("{} {} {}\n", tag, it.label(), relevance) | 381 | items.push(format!("{} {} {}\n", tag, it.label(), relevance)); |
382 | |||
383 | if let Some((mutability, relevance)) = it.ref_match() { | ||
384 | let label = format!("&{}{}", mutability.as_keyword_for_ref(), it.label()); | ||
385 | let relevance = display_relevance(relevance); | ||
386 | |||
387 | items.push(format!("{} {} {}\n", tag, label, relevance)); | ||
388 | } | ||
389 | |||
390 | items | ||
369 | }) | 391 | }) |
370 | .collect::<String>(); | 392 | .collect::<String>(); |
393 | |||
371 | expect.assert_eq(&actual); | 394 | expect.assert_eq(&actual); |
372 | } | 395 | } |
373 | 396 | ||
@@ -837,9 +860,9 @@ fn test(bar: u32) { } | |||
837 | fn foo(s: S) { test(s.$0) } | 860 | fn foo(s: S) { test(s.$0) } |
838 | "#, | 861 | "#, |
839 | expect![[r#" | 862 | expect![[r#" |
863 | fd foo [] | ||
840 | fd bar [type+name] | 864 | fd bar [type+name] |
841 | fd baz [type] | 865 | fd baz [type] |
842 | fd foo [] | ||
843 | "#]], | 866 | "#]], |
844 | ); | 867 | ); |
845 | } | 868 | } |
@@ -854,9 +877,9 @@ struct B { x: (), y: f32, bar: u32 } | |||
854 | fn foo(a: A) { B { bar: a.$0 }; } | 877 | fn foo(a: A) { B { bar: a.$0 }; } |
855 | "#, | 878 | "#, |
856 | expect![[r#" | 879 | expect![[r#" |
880 | fd foo [] | ||
857 | fd bar [type+name] | 881 | fd bar [type+name] |
858 | fd baz [type] | 882 | fd baz [type] |
859 | fd foo [] | ||
860 | "#]], | 883 | "#]], |
861 | ) | 884 | ) |
862 | } | 885 | } |
@@ -884,9 +907,9 @@ fn f(foo: i64) { } | |||
884 | fn foo(a: A) { f(B { bar: a.$0 }); } | 907 | fn foo(a: A) { f(B { bar: a.$0 }); } |
885 | "#, | 908 | "#, |
886 | expect![[r#" | 909 | expect![[r#" |
910 | fd foo [] | ||
887 | fd bar [type+name] | 911 | fd bar [type+name] |
888 | fd baz [type] | 912 | fd baz [type] |
889 | fd foo [] | ||
890 | "#]], | 913 | "#]], |
891 | ); | 914 | ); |
892 | } | 915 | } |
@@ -899,7 +922,7 @@ struct WorldSnapshot { _f: () }; | |||
899 | fn go(world: &WorldSnapshot) { go(w$0) } | 922 | fn go(world: &WorldSnapshot) { go(w$0) } |
900 | "#, | 923 | "#, |
901 | expect![[r#" | 924 | expect![[r#" |
902 | lc world [type+name] | 925 | lc world [type+name+local] |
903 | st WorldSnapshot [] | 926 | st WorldSnapshot [] |
904 | fn go(…) [] | 927 | fn go(…) [] |
905 | "#]], | 928 | "#]], |
@@ -914,9 +937,9 @@ struct Foo; | |||
914 | fn f(foo: &Foo) { f(foo, w$0) } | 937 | fn f(foo: &Foo) { f(foo, w$0) } |
915 | "#, | 938 | "#, |
916 | expect![[r#" | 939 | expect![[r#" |
940 | lc foo [local] | ||
917 | st Foo [] | 941 | st Foo [] |
918 | fn f(…) [] | 942 | fn f(…) [] |
919 | lc foo [] | ||
920 | "#]], | 943 | "#]], |
921 | ); | 944 | ); |
922 | } | 945 | } |
@@ -976,9 +999,10 @@ fn main() { | |||
976 | Local, | 999 | Local, |
977 | ), | 1000 | ), |
978 | detail: "S", | 1001 | detail: "S", |
979 | relevance: Relevance { | 1002 | relevance: CompletionRelevance { |
980 | exact_name_match: true, | 1003 | exact_name_match: true, |
981 | exact_type_match: false, | 1004 | exact_type_match: false, |
1005 | is_local: true, | ||
982 | }, | 1006 | }, |
983 | ref_match: "&mut ", | 1007 | ref_match: "&mut ", |
984 | }, | 1008 | }, |
@@ -986,4 +1010,120 @@ fn main() { | |||
986 | "#]], | 1010 | "#]], |
987 | ) | 1011 | ) |
988 | } | 1012 | } |
1013 | |||
1014 | #[test] | ||
1015 | fn suggest_deref() { | ||
1016 | check_relevance( | ||
1017 | r#" | ||
1018 | #[lang = "deref"] | ||
1019 | trait Deref { | ||
1020 | type Target; | ||
1021 | fn deref(&self) -> &Self::Target; | ||
1022 | } | ||
1023 | |||
1024 | struct S; | ||
1025 | struct T(S); | ||
1026 | |||
1027 | impl Deref for T { | ||
1028 | type Target = S; | ||
1029 | |||
1030 | fn deref(&self) -> &Self::Target { | ||
1031 | &self.0 | ||
1032 | } | ||
1033 | } | ||
1034 | |||
1035 | fn foo(s: &S) {} | ||
1036 | |||
1037 | fn main() { | ||
1038 | let t = T(S); | ||
1039 | let m = 123; | ||
1040 | |||
1041 | foo($0); | ||
1042 | } | ||
1043 | "#, | ||
1044 | expect![[r#" | ||
1045 | lc m [local] | ||
1046 | lc t [local] | ||
1047 | lc &t [type+local] | ||
1048 | st T [] | ||
1049 | st S [] | ||
1050 | fn main() [] | ||
1051 | tt Deref [] | ||
1052 | fn foo(…) [] | ||
1053 | "#]], | ||
1054 | ) | ||
1055 | } | ||
1056 | |||
1057 | #[test] | ||
1058 | fn suggest_deref_mut() { | ||
1059 | check_relevance( | ||
1060 | r#" | ||
1061 | #[lang = "deref"] | ||
1062 | trait Deref { | ||
1063 | type Target; | ||
1064 | fn deref(&self) -> &Self::Target; | ||
1065 | } | ||
1066 | |||
1067 | #[lang = "deref_mut"] | ||
1068 | pub trait DerefMut: Deref { | ||
1069 | fn deref_mut(&mut self) -> &mut Self::Target; | ||
1070 | } | ||
1071 | |||
1072 | struct S; | ||
1073 | struct T(S); | ||
1074 | |||
1075 | impl Deref for T { | ||
1076 | type Target = S; | ||
1077 | |||
1078 | fn deref(&self) -> &Self::Target { | ||
1079 | &self.0 | ||
1080 | } | ||
1081 | } | ||
1082 | |||
1083 | impl DerefMut for T { | ||
1084 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
1085 | &mut self.0 | ||
1086 | } | ||
1087 | } | ||
1088 | |||
1089 | fn foo(s: &mut S) {} | ||
1090 | |||
1091 | fn main() { | ||
1092 | let t = T(S); | ||
1093 | let m = 123; | ||
1094 | |||
1095 | foo($0); | ||
1096 | } | ||
1097 | "#, | ||
1098 | expect![[r#" | ||
1099 | lc m [local] | ||
1100 | lc t [local] | ||
1101 | lc &mut t [type+local] | ||
1102 | tt DerefMut [] | ||
1103 | tt Deref [] | ||
1104 | fn foo(…) [] | ||
1105 | st T [] | ||
1106 | st S [] | ||
1107 | fn main() [] | ||
1108 | "#]], | ||
1109 | ) | ||
1110 | } | ||
1111 | |||
1112 | #[test] | ||
1113 | fn locals() { | ||
1114 | check_relevance( | ||
1115 | r#" | ||
1116 | fn foo(bar: u32) { | ||
1117 | let baz = 0; | ||
1118 | |||
1119 | f$0 | ||
1120 | } | ||
1121 | "#, | ||
1122 | expect![[r#" | ||
1123 | lc baz [local] | ||
1124 | lc bar [local] | ||
1125 | fn foo(…) [] | ||
1126 | "#]], | ||
1127 | ); | ||
1128 | } | ||
989 | } | 1129 | } |
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> { |