aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r--crates/ide_completion/src/completions/attribute.rs35
-rw-r--r--crates/ide_completion/src/completions/fn_param.rs7
-rw-r--r--crates/ide_completion/src/completions/keyword.rs44
-rw-r--r--crates/ide_completion/src/completions/mod_.rs6
-rw-r--r--crates/ide_completion/src/completions/postfix.rs7
-rw-r--r--crates/ide_completion/src/completions/record.rs15
-rw-r--r--crates/ide_completion/src/completions/snippet.rs25
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs32
-rw-r--r--crates/ide_completion/src/item.rs211
-rw-r--r--crates/ide_completion/src/lib.rs2
-rw-r--r--crates/ide_completion/src/render.rs234
-rw-r--r--crates/ide_completion/src/render/builder_ext.rs6
-rw-r--r--crates/ide_completion/src/render/const_.rs10
-rw-r--r--crates/ide_completion/src/render/enum_variant.rs21
-rw-r--r--crates/ide_completion/src/render/function.rs13
-rw-r--r--crates/ide_completion/src/render/macro_.rs26
-rw-r--r--crates/ide_completion/src/render/pattern.rs18
-rw-r--r--crates/ide_completion/src/render/type_alias.rs10
18 files changed, 445 insertions, 277 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
167fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) { 164fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
168 let builder = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) 165 let mut item = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw);
169 .kind(CompletionItemKind::Keyword); 166 item.kind(CompletionItemKind::Keyword);
170 let builder = match ctx.config.snippet_cap { 167
168 match ctx.config.snippet_cap {
171 Some(cap) => { 169 Some(cap) => {
172 let tmp; 170 let tmp;
173 let snippet = if snippet.ends_with('}') && ctx.incomplete_let { 171 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
@@ -177,11 +175,13 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet
177 } else { 175 } else {
178 snippet 176 snippet
179 }; 177 };
180 builder.insert_snippet(cap, snippet) 178 item.insert_snippet(cap, snippet);
179 }
180 None => {
181 item.insert_text(if snippet.contains('$') { kw } else { snippet });
181 } 182 }
182 None => builder.insert_text(if snippet.contains('$') { kw } else { snippet }),
183 }; 183 };
184 acc.add(builder.build()); 184 item.add_to(acc);
185} 185}
186 186
187#[cfg(test)] 187#[cfg(test)]
diff --git a/crates/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs
index 352fc7c77..4f9415736 100644
--- a/crates/ide_completion/src/completions/mod_.rs
+++ b/crates/ide_completion/src/completions/mod_.rs
@@ -80,9 +80,9 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op
80 if mod_under_caret.semicolon_token().is_none() { 80 if mod_under_caret.semicolon_token().is_none() {
81 label.push(';'); 81 label.push(';');
82 } 82 }
83 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label) 83 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label);
84 .kind(SymbolKind::Module) 84 item.kind(SymbolKind::Module);
85 .add_to(acc) 85 item.add_to(acc)
86 }); 86 });
87 87
88 Some(()) 88 Some(())
diff --git a/crates/ide_completion/src/completions/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
10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { 10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
11 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label) 11 let mut item = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label);
12 .insert_snippet(cap, snippet) 12 item.insert_snippet(cap, snippet).kind(CompletionItemKind::Snippet);
13 .kind(CompletionItemKind::Snippet) 13 item
14} 14}
15 15
16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { 16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
@@ -35,7 +35,7 @@ pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionConte
35 None => return, 35 None => return,
36 }; 36 };
37 37
38 snippet( 38 let mut item = snippet(
39 ctx, 39 ctx,
40 cap, 40 cap,
41 "tmod (Test module)", 41 "tmod (Test module)",
@@ -49,11 +49,11 @@ mod tests {
49 $0 49 $0
50 } 50 }
51}", 51}",
52 ) 52 );
53 .lookup_by("tmod") 53 item.lookup_by("tmod");
54 .add_to(acc); 54 item.add_to(acc);
55 55
56 snippet( 56 let mut item = snippet(
57 ctx, 57 ctx,
58 cap, 58 cap,
59 "tfn (Test function)", 59 "tfn (Test function)",
@@ -62,11 +62,12 @@ mod tests {
62fn ${1:feature}() { 62fn ${1:feature}() {
63 $0 63 $0
64}", 64}",
65 ) 65 );
66 .lookup_by("tfn") 66 item.lookup_by("tfn");
67 .add_to(acc); 67 item.add_to(acc);
68 68
69 snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc); 69 let item = snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}");
70 item.add_to(acc);
70} 71}
71 72
72#[cfg(test)] 73#[cfg(test)]
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index b999540b8..5a7361f8e 100644
--- a/crates/ide_completion/src/completions/trait_impl.rs
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -145,9 +145,8 @@ fn add_function_impl(
145 format!("fn {}(..)", fn_name) 145 format!("fn {}(..)", fn_name)
146 }; 146 };
147 147
148 let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) 148 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label);
149 .lookup_by(fn_name) 149 item.lookup_by(fn_name).set_documentation(func.docs(ctx.db));
150 .set_documentation(func.docs(ctx.db));
151 150
152 let completion_kind = if func.self_param(ctx.db).is_some() { 151 let completion_kind = if func.self_param(ctx.db).is_some() {
153 CompletionItemKind::Method 152 CompletionItemKind::Method
@@ -161,15 +160,15 @@ fn add_function_impl(
161 match ctx.config.snippet_cap { 160 match ctx.config.snippet_cap {
162 Some(cap) => { 161 Some(cap) => {
163 let snippet = format!("{} {{\n $0\n}}", function_decl); 162 let snippet = format!("{} {{\n $0\n}}", function_decl);
164 builder.snippet_edit(cap, TextEdit::replace(range, snippet)) 163 item.snippet_edit(cap, TextEdit::replace(range, snippet));
165 } 164 }
166 None => { 165 None => {
167 let header = format!("{} {{", function_decl); 166 let header = format!("{} {{", function_decl);
168 builder.text_edit(TextEdit::replace(range, header)) 167 item.text_edit(TextEdit::replace(range, header));
169 } 168 }
170 } 169 };
171 .kind(completion_kind) 170 item.kind(completion_kind);
172 .add_to(acc); 171 item.add_to(acc);
173 } 172 }
174} 173}
175 174
@@ -185,12 +184,12 @@ fn add_type_alias_impl(
185 184
186 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end()); 185 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end());
187 186
188 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 187 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
189 .text_edit(TextEdit::replace(range, snippet)) 188 item.text_edit(TextEdit::replace(range, snippet))
190 .lookup_by(alias_name) 189 .lookup_by(alias_name)
191 .kind(SymbolKind::TypeAlias) 190 .kind(SymbolKind::TypeAlias)
192 .set_documentation(type_alias.docs(ctx.db)) 191 .set_documentation(type_alias.docs(ctx.db));
193 .add_to(acc); 192 item.add_to(acc);
194} 193}
195 194
196fn add_const_impl( 195fn add_const_impl(
@@ -208,12 +207,13 @@ fn add_const_impl(
208 let range = 207 let range =
209 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end()); 208 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
210 209
211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 210 let mut item =
212 .text_edit(TextEdit::replace(range, snippet)) 211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
212 item.text_edit(TextEdit::replace(range, snippet))
213 .lookup_by(const_name) 213 .lookup_by(const_name)
214 .kind(SymbolKind::Const) 214 .kind(SymbolKind::Const)
215 .set_documentation(const_.docs(ctx.db)) 215 .set_documentation(const_.docs(ctx.db));
216 .add_to(acc); 216 item.add_to(acc);
217 } 217 }
218 } 218 }
219} 219}
diff --git a/crates/ide_completion/src/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)]
115pub enum CompletionScore { 126pub 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
149impl 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
317impl Builder { 381impl 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)]
504mod 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
24pub use crate::{ 24pub 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::{
20use syntax::TextRange; 20use syntax::TextRange;
21 21
22use crate::{ 22use crate::{
23 item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 23 item::{CompletionRelevance, ImportEdit},
24 CompletionScore, 24 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
25}; 25};
26 26
27use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; 27use 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
320fn compute_score_from_active( 325fn 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}
341fn 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
355fn 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)]
361mod tests { 334mod 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#"
860struct S { foo: i64, bar: u32, baz: u32 } 836struct S { foo: i64, bar: u32, baz: u32 }
861fn test(bar: u32) { } 837fn 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#"
877struct A { foo: i64, bar: u32, baz: u32 } 853struct A { foo: i64, bar: u32, baz: u32 }
878struct B { x: (), y: f32, bar: u32 } 854struct 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#"
893struct A { foo: i64, bar: u32, baz: u32 } 869struct A { foo: i64, bar: u32, baz: u32 }
894struct B { x: (), y: f32, bar: u32 } 870struct 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#"
906struct A { foo: i64, bar: u32, baz: u32 } 882struct A { foo: i64, bar: u32, baz: u32 }
907struct B { x: (), y: f32, bar: u32 } 883struct 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#"
923struct WorldSnapshot { _f: () }; 899struct WorldSnapshot { _f: () };
924fn go(world: &WorldSnapshot) { go(w$0) } 900fn 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#"
938struct Foo; 914struct Foo;
939fn f(foo: &Foo) { f(foo, w$0) } 915fn 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#"
930struct S;
931fn foo(s: &mut S) {}
932fn 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
87fn render_pat( 87fn 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> {