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.rs188
-rw-r--r--crates/ide_completion/src/lib.rs5
-rw-r--r--crates/ide_completion/src/render.rs280
-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, 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
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 14afec603..9a4b5217a 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -70,7 +70,7 @@ pub struct CompletionItem {
70 /// Note that Relevance ignores fuzzy match score. We compute Relevance for 70 /// Note that Relevance ignores fuzzy match score. We compute Relevance for
71 /// all possible items, and then separately build an ordered completion list 71 /// all possible items, and then separately build an ordered completion list
72 /// based on relevance and fuzzy matching with the already typed identifier. 72 /// based on relevance and fuzzy matching with the already typed identifier.
73 relevance: Relevance, 73 relevance: CompletionRelevance,
74 74
75 /// Indicates that a reference or mutable reference to this variable is a 75 /// Indicates that a reference or mutable reference to this variable is a
76 /// possible match. 76 /// possible match.
@@ -107,9 +107,11 @@ impl fmt::Debug for CompletionItem {
107 if self.deprecated { 107 if self.deprecated {
108 s.field("deprecated", &true); 108 s.field("deprecated", &true);
109 } 109 }
110 if self.relevance.is_relevant() { 110
111 if self.relevance != CompletionRelevance::default() {
111 s.field("relevance", &self.relevance); 112 s.field("relevance", &self.relevance);
112 } 113 }
114
113 if let Some(mutability) = &self.ref_match { 115 if let Some(mutability) = &self.ref_match {
114 s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref())); 116 s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref()));
115 } 117 }
@@ -120,16 +122,8 @@ impl fmt::Debug for CompletionItem {
120 } 122 }
121} 123}
122 124
123#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
124pub enum CompletionScore {
125 /// If only type match
126 TypeMatch,
127 /// If type and name match
128 TypeAndNameMatch,
129}
130
131#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)] 125#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
132pub struct Relevance { 126pub struct CompletionRelevance {
133 /// This is set in cases like these: 127 /// This is set in cases like these:
134 /// 128 ///
135 /// ``` 129 /// ```
@@ -150,11 +144,54 @@ pub struct Relevance {
150 /// } 144 /// }
151 /// ``` 145 /// ```
152 pub exact_type_match: bool, 146 pub exact_type_match: bool,
147 /// This is set in cases like these:
148 ///
149 /// ```
150 /// fn foo(bar: u32) {
151 /// $0 // `bar` is local
152 /// }
153 /// ```
154 ///
155 /// ```
156 /// fn foo() {
157 /// let bar = 0;
158 /// $0 // `bar` is local
159 /// }
160 /// ```
161 pub is_local: bool,
153} 162}
154 163
155impl Relevance { 164impl CompletionRelevance {
165 /// Provides a relevance score. Higher values are more relevant.
166 ///
167 /// The absolute value of the relevance score is not meaningful, for
168 /// example a value of 0 doesn't mean "not relevant", rather
169 /// it means "least relevant". The score value should only be used
170 /// for relative ordering.
171 ///
172 /// See is_relevant if you need to make some judgement about score
173 /// in an absolute sense.
174 pub fn score(&self) -> u32 {
175 let mut score = 0;
176
177 if self.exact_name_match {
178 score += 1;
179 }
180 if self.exact_type_match {
181 score += 3;
182 }
183 if self.is_local {
184 score += 1;
185 }
186
187 score
188 }
189
190 /// Returns true when the score for this threshold is above
191 /// some threshold such that we think it is especially likely
192 /// to be relevant.
156 pub fn is_relevant(&self) -> bool { 193 pub fn is_relevant(&self) -> bool {
157 self != &Relevance::default() 194 self.score() > 0
158 } 195 }
159} 196}
160 197
@@ -249,7 +286,7 @@ impl CompletionItem {
249 text_edit: None, 286 text_edit: None,
250 deprecated: false, 287 deprecated: false,
251 trigger_call_info: None, 288 trigger_call_info: None,
252 relevance: Relevance::default(), 289 relevance: CompletionRelevance::default(),
253 ref_match: None, 290 ref_match: None,
254 import_to_add: None, 291 import_to_add: None,
255 } 292 }
@@ -292,7 +329,7 @@ impl CompletionItem {
292 self.deprecated 329 self.deprecated
293 } 330 }
294 331
295 pub fn relevance(&self) -> Relevance { 332 pub fn relevance(&self) -> CompletionRelevance {
296 self.relevance 333 self.relevance
297 } 334 }
298 335
@@ -300,8 +337,14 @@ impl CompletionItem {
300 self.trigger_call_info 337 self.trigger_call_info
301 } 338 }
302 339
303 pub fn ref_match(&self) -> Option<Mutability> { 340 pub fn ref_match(&self) -> Option<(Mutability, CompletionRelevance)> {
304 self.ref_match 341 // Relevance of the ref match should be the same as the original
342 // match, but with exact type match set because self.ref_match
343 // is only set if there is an exact type match.
344 let mut relevance = self.relevance;
345 relevance.exact_type_match = true;
346
347 self.ref_match.map(|mutability| (mutability, relevance))
305 } 348 }
306 349
307 pub fn import_to_add(&self) -> Option<&ImportEdit> { 350 pub fn import_to_add(&self) -> Option<&ImportEdit> {
@@ -349,7 +392,7 @@ pub(crate) struct Builder {
349 text_edit: Option<TextEdit>, 392 text_edit: Option<TextEdit>,
350 deprecated: bool, 393 deprecated: bool,
351 trigger_call_info: Option<bool>, 394 trigger_call_info: Option<bool>,
352 relevance: Relevance, 395 relevance: CompletionRelevance,
353 ref_match: Option<Mutability>, 396 ref_match: Option<Mutability>,
354} 397}
355 398
@@ -401,42 +444,42 @@ impl Builder {
401 import_to_add: self.import_to_add, 444 import_to_add: self.import_to_add,
402 } 445 }
403 } 446 }
404 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { 447 pub(crate) fn lookup_by(&mut self, lookup: impl Into<String>) -> &mut Builder {
405 self.lookup = Some(lookup.into()); 448 self.lookup = Some(lookup.into());
406 self 449 self
407 } 450 }
408 pub(crate) fn label(mut self, label: impl Into<String>) -> Builder { 451 pub(crate) fn label(&mut self, label: impl Into<String>) -> &mut Builder {
409 self.label = label.into(); 452 self.label = label.into();
410 self 453 self
411 } 454 }
412 pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder { 455 pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder {
413 self.insert_text = Some(insert_text.into()); 456 self.insert_text = Some(insert_text.into());
414 self 457 self
415 } 458 }
416 pub(crate) fn insert_snippet( 459 pub(crate) fn insert_snippet(
417 mut self, 460 &mut self,
418 _cap: SnippetCap, 461 _cap: SnippetCap,
419 snippet: impl Into<String>, 462 snippet: impl Into<String>,
420 ) -> Builder { 463 ) -> &mut Builder {
421 self.insert_text_format = InsertTextFormat::Snippet; 464 self.insert_text_format = InsertTextFormat::Snippet;
422 self.insert_text(snippet) 465 self.insert_text(snippet)
423 } 466 }
424 pub(crate) fn kind(mut self, kind: impl Into<CompletionItemKind>) -> Builder { 467 pub(crate) fn kind(&mut self, kind: impl Into<CompletionItemKind>) -> &mut Builder {
425 self.kind = Some(kind.into()); 468 self.kind = Some(kind.into());
426 self 469 self
427 } 470 }
428 pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder { 471 pub(crate) fn text_edit(&mut self, edit: TextEdit) -> &mut Builder {
429 self.text_edit = Some(edit); 472 self.text_edit = Some(edit);
430 self 473 self
431 } 474 }
432 pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder { 475 pub(crate) fn snippet_edit(&mut self, _cap: SnippetCap, edit: TextEdit) -> &mut Builder {
433 self.insert_text_format = InsertTextFormat::Snippet; 476 self.insert_text_format = InsertTextFormat::Snippet;
434 self.text_edit(edit) 477 self.text_edit(edit)
435 } 478 }
436 pub(crate) fn detail(self, detail: impl Into<String>) -> Builder { 479 pub(crate) fn detail(&mut self, detail: impl Into<String>) -> &mut Builder {
437 self.set_detail(Some(detail)) 480 self.set_detail(Some(detail))
438 } 481 }
439 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { 482 pub(crate) fn set_detail(&mut self, detail: Option<impl Into<String>>) -> &mut Builder {
440 self.detail = detail.map(Into::into); 483 self.detail = detail.map(Into::into);
441 if let Some(detail) = &self.detail { 484 if let Some(detail) = &self.detail {
442 if never!(detail.contains('\n'), "multiline detail:\n{}", detail) { 485 if never!(detail.contains('\n'), "multiline detail:\n{}", detail) {
@@ -446,31 +489,106 @@ impl Builder {
446 self 489 self
447 } 490 }
448 #[allow(unused)] 491 #[allow(unused)]
449 pub(crate) fn documentation(self, docs: Documentation) -> Builder { 492 pub(crate) fn documentation(&mut self, docs: Documentation) -> &mut Builder {
450 self.set_documentation(Some(docs)) 493 self.set_documentation(Some(docs))
451 } 494 }
452 pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder { 495 pub(crate) fn set_documentation(&mut self, docs: Option<Documentation>) -> &mut Builder {
453 self.documentation = docs.map(Into::into); 496 self.documentation = docs.map(Into::into);
454 self 497 self
455 } 498 }
456 pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder { 499 pub(crate) fn set_deprecated(&mut self, deprecated: bool) -> &mut Builder {
457 self.deprecated = deprecated; 500 self.deprecated = deprecated;
458 self 501 self
459 } 502 }
460 pub(crate) fn set_relevance(mut self, relevance: Relevance) -> Builder { 503 pub(crate) fn set_relevance(&mut self, relevance: CompletionRelevance) -> &mut Builder {
461 self.relevance = relevance; 504 self.relevance = relevance;
462 self 505 self
463 } 506 }
464 pub(crate) fn trigger_call_info(mut self) -> Builder { 507 pub(crate) fn trigger_call_info(&mut self) -> &mut Builder {
465 self.trigger_call_info = Some(true); 508 self.trigger_call_info = Some(true);
466 self 509 self
467 } 510 }
468 pub(crate) fn add_import(mut self, import_to_add: Option<ImportEdit>) -> Builder { 511 pub(crate) fn add_import(&mut self, import_to_add: Option<ImportEdit>) -> &mut Builder {
469 self.import_to_add = import_to_add; 512 self.import_to_add = import_to_add;
470 self 513 self
471 } 514 }
472 pub(crate) fn ref_match(mut self, mutability: Mutability) -> Builder { 515 pub(crate) fn ref_match(&mut self, mutability: Mutability) -> &mut Builder {
473 self.ref_match = Some(mutability); 516 self.ref_match = Some(mutability);
474 self 517 self
475 } 518 }
476} 519}
520
521#[cfg(test)]
522mod tests {
523 use itertools::Itertools;
524 use test_utils::assert_eq_text;
525
526 use super::CompletionRelevance;
527
528 /// Check that these are CompletionRelevance are sorted in ascending order
529 /// by their relevance score.
530 ///
531 /// We want to avoid making assertions about the absolute score of any
532 /// item, but we do want to assert whether each is >, <, or == to the
533 /// others.
534 ///
535 /// If provided vec![vec![a], vec![b, c], vec![d]], then this will assert:
536 /// a.score < b.score == c.score < d.score
537 fn check_relevance_score_ordered(expected_relevance_order: Vec<Vec<CompletionRelevance>>) {
538 let expected = format!("{:#?}", &expected_relevance_order);
539
540 let actual_relevance_order = expected_relevance_order
541 .into_iter()
542 .flatten()
543 .map(|r| (r.score(), r))
544 .sorted_by_key(|(score, _r)| *score)
545 .fold(
546 (u32::MIN, vec![vec![]]),
547 |(mut currently_collecting_score, mut out), (score, r)| {
548 if currently_collecting_score == score {
549 out.last_mut().unwrap().push(r);
550 } else {
551 currently_collecting_score = score;
552 out.push(vec![r]);
553 }
554 (currently_collecting_score, out)
555 },
556 )
557 .1;
558
559 let actual = format!("{:#?}", &actual_relevance_order);
560
561 assert_eq_text!(&expected, &actual);
562 }
563
564 #[test]
565 fn relevance_score() {
566 // This test asserts that the relevance score for these items is ascending, and
567 // that any items in the same vec have the same score.
568 let expected_relevance_order = vec![
569 vec![CompletionRelevance::default()],
570 vec![
571 CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() },
572 CompletionRelevance { is_local: true, ..CompletionRelevance::default() },
573 ],
574 vec![CompletionRelevance {
575 exact_name_match: true,
576 is_local: true,
577 ..CompletionRelevance::default()
578 }],
579 vec![CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }],
580 vec![CompletionRelevance {
581 exact_name_match: true,
582 exact_type_match: true,
583 ..CompletionRelevance::default()
584 }],
585 vec![CompletionRelevance {
586 exact_name_match: true,
587 exact_type_match: true,
588 is_local: true,
589 }],
590 ];
591
592 check_relevance_score_ordered(expected_relevance_order);
593 }
594}
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index d46f521a0..263554ecf 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -23,10 +23,7 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi
23 23
24pub use crate::{ 24pub use crate::{
25 config::CompletionConfig, 25 config::CompletionConfig,
26 item::{ 26 item::{CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit, InsertTextFormat},
27 CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat,
28 Relevance,
29 },
30}; 27};
31 28
32//FIXME: split the following feature into fine-grained features. 29//FIXME: split the following feature into fine-grained features.
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index 8c8b149a1..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
11mod builder_ext; 11mod builder_ext;
12 12
13use base_db::Upcast;
13use hir::{ 14use hir::{
14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, 15 db::HirDatabase, AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability,
16 ScopeDef, Type,
15}; 17};
16use ide_db::{ 18use ide_db::{
17 helpers::{item_name, SnippetCap}, 19 helpers::{item_name, SnippetCap},
@@ -20,7 +22,7 @@ use ide_db::{
20use syntax::TextRange; 22use syntax::TextRange;
21 23
22use crate::{ 24use crate::{
23 item::{ImportEdit, Relevance}, 25 item::{CompletionRelevance, ImportEdit},
24 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 26 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
25}; 27};
26 28
@@ -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
326fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<Relevance> { 329fn 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
340fn 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)]
335mod tests { 345mod 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) { }
837fn foo(s: S) { test(s.$0) } 860fn 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 }
854fn foo(a: A) { B { bar: a.$0 }; } 877fn 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) { }
884fn foo(a: A) { f(B { bar: a.$0 }); } 907fn 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: () };
899fn go(world: &WorldSnapshot) { go(w$0) } 922fn 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;
914fn f(foo: &Foo) { f(foo, w$0) } 937fn 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"]
1019trait Deref {
1020 type Target;
1021 fn deref(&self) -> &Self::Target;
1022}
1023
1024struct S;
1025struct T(S);
1026
1027impl Deref for T {
1028 type Target = S;
1029
1030 fn deref(&self) -> &Self::Target {
1031 &self.0
1032 }
1033}
1034
1035fn foo(s: &S) {}
1036
1037fn 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"]
1062trait Deref {
1063 type Target;
1064 fn deref(&self) -> &Self::Target;
1065}
1066
1067#[lang = "deref_mut"]
1068pub trait DerefMut: Deref {
1069 fn deref_mut(&mut self) -> &mut Self::Target;
1070}
1071
1072struct S;
1073struct T(S);
1074
1075impl Deref for T {
1076 type Target = S;
1077
1078 fn deref(&self) -> &Self::Target {
1079 &self.0
1080 }
1081}
1082
1083impl DerefMut for T {
1084 fn deref_mut(&mut self) -> &mut Self::Target {
1085 &mut self.0
1086 }
1087}
1088
1089fn foo(s: &mut S) {}
1090
1091fn 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#"
1116fn 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
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> {