diff options
Diffstat (limited to 'crates/ide_completion/src/render.rs')
-rw-r--r-- | crates/ide_completion/src/render.rs | 182 |
1 files changed, 147 insertions, 35 deletions
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 2514dda7c..09a27de71 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -10,10 +10,8 @@ pub(crate) mod type_alias; | |||
10 | 10 | ||
11 | mod builder_ext; | 11 | mod builder_ext; |
12 | 12 | ||
13 | use base_db::Upcast; | ||
14 | use hir::{ | 13 | use hir::{ |
15 | db::HirDatabase, AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, | 14 | AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, |
16 | ScopeDef, Type, | ||
17 | }; | 15 | }; |
18 | use ide_db::{ | 16 | use ide_db::{ |
19 | helpers::{item_name, SnippetCap}, | 17 | helpers::{item_name, SnippetCap}, |
@@ -22,8 +20,8 @@ use ide_db::{ | |||
22 | use syntax::TextRange; | 20 | use syntax::TextRange; |
23 | 21 | ||
24 | use crate::{ | 22 | use crate::{ |
25 | item::{CompletionRelevance, ImportEdit}, | 23 | item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, |
26 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, | 24 | CompletionRelevance, |
27 | }; | 25 | }; |
28 | 26 | ||
29 | use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; | 27 | use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; |
@@ -144,7 +142,15 @@ impl<'a> Render<'a> { | |||
144 | .set_documentation(field.docs(self.ctx.db())) | 142 | .set_documentation(field.docs(self.ctx.db())) |
145 | .set_deprecated(is_deprecated); | 143 | .set_deprecated(is_deprecated); |
146 | 144 | ||
147 | item.set_relevance(compute_relevance(&self.ctx, &ty, &name.to_string())); | 145 | item.set_relevance(CompletionRelevance { |
146 | exact_type_match: compute_exact_type_match(self.ctx.completion, ty), | ||
147 | exact_name_match: compute_exact_name_match(self.ctx.completion, name.to_string()), | ||
148 | ..CompletionRelevance::default() | ||
149 | }); | ||
150 | |||
151 | if let Some(ref_match) = compute_ref_match(self.ctx.completion, ty) { | ||
152 | item.ref_match(ref_match); | ||
153 | } | ||
148 | 154 | ||
149 | item.build() | 155 | item.build() |
150 | } | 156 | } |
@@ -234,31 +240,18 @@ impl<'a> Render<'a> { | |||
234 | if !ty.is_unknown() { | 240 | if !ty.is_unknown() { |
235 | item.detail(ty.display(self.ctx.db()).to_string()); | 241 | item.detail(ty.display(self.ctx.db()).to_string()); |
236 | } | 242 | } |
237 | }; | ||
238 | 243 | ||
239 | if let ScopeDef::Local(local) = resolution { | 244 | item.set_relevance(CompletionRelevance { |
240 | let ty = local.ty(self.ctx.db()); | 245 | exact_type_match: compute_exact_type_match(self.ctx.completion, &ty), |
246 | exact_name_match: compute_exact_name_match(self.ctx.completion, local_name.clone()), | ||
247 | is_local: true, | ||
248 | ..CompletionRelevance::default() | ||
249 | }); | ||
241 | 250 | ||
242 | let mut relevance = compute_relevance(&self.ctx, &ty, &local_name); | 251 | if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ty) { |
243 | relevance.is_local = true; | 252 | item.ref_match(ref_match); |
244 | item.set_relevance(relevance); | ||
245 | |||
246 | if let Some(expected_type) = self.ctx.completion.expected_type.as_ref() { | ||
247 | if &ty != expected_type { | ||
248 | if let Some(ty_without_ref) = expected_type.remove_ref() { | ||
249 | if relevance_type_match(self.ctx.db().upcast(), &ty, &ty_without_ref) { | ||
250 | cov_mark::hit!(suggest_ref); | ||
251 | let mutability = if expected_type.is_mutable_reference() { | ||
252 | Mutability::Mut | ||
253 | } else { | ||
254 | Mutability::Shared | ||
255 | }; | ||
256 | item.ref_match(mutability); | ||
257 | } | ||
258 | } | ||
259 | } | ||
260 | } | 253 | } |
261 | } | 254 | }; |
262 | 255 | ||
263 | // Add `<>` for generic types | 256 | // Add `<>` for generic types |
264 | if self.ctx.completion.is_path_type | 257 | if self.ctx.completion.is_path_type |
@@ -313,17 +306,44 @@ impl<'a> Render<'a> { | |||
313 | } | 306 | } |
314 | } | 307 | } |
315 | 308 | ||
316 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> CompletionRelevance { | 309 | fn compute_exact_type_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> bool { |
317 | let mut res = CompletionRelevance::default(); | 310 | if let Some(expected_type) = ctx.expected_type.as_ref() { |
311 | // We don't ever consider unit type to be an exact type match, since | ||
312 | // nearly always this is not meaningful to the user. | ||
313 | completion_ty == expected_type && !expected_type.is_unit() | ||
314 | } else { | ||
315 | false | ||
316 | } | ||
317 | } | ||
318 | 318 | ||
319 | res.exact_type_match = Some(ty) == ctx.completion.expected_type.as_ref(); | 319 | fn compute_exact_name_match(ctx: &CompletionContext, completion_name: impl Into<String>) -> bool { |
320 | res.exact_name_match = Some(name) == ctx.completion.expected_name.as_deref(); | 320 | let completion_name = completion_name.into(); |
321 | 321 | ||
322 | res | 322 | Some(&completion_name) == ctx.expected_name.as_ref() |
323 | } | 323 | } |
324 | 324 | ||
325 | fn relevance_type_match(db: &dyn HirDatabase, ty: &Type, expected_type: &Type) -> bool { | 325 | fn compute_ref_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> Option<Mutability> { |
326 | ty == expected_type || ty.autoderef(db).any(|deref_ty| &deref_ty == expected_type) | 326 | let mut ref_match = None; |
327 | if let Some(expected_type) = &ctx.expected_type { | ||
328 | if completion_ty != expected_type { | ||
329 | if let Some(expected_type_without_ref) = expected_type.remove_ref() { | ||
330 | if completion_ty == &expected_type_without_ref | ||
331 | || completion_ty | ||
332 | .autoderef(ctx.db) | ||
333 | .any(|deref_ty| deref_ty == expected_type_without_ref) | ||
334 | { | ||
335 | cov_mark::hit!(suggest_ref); | ||
336 | let mutability = if expected_type.is_mutable_reference() { | ||
337 | Mutability::Mut | ||
338 | } else { | ||
339 | Mutability::Shared | ||
340 | }; | ||
341 | ref_match = Some(mutability); | ||
342 | } | ||
343 | } | ||
344 | } | ||
345 | }; | ||
346 | ref_match | ||
327 | } | 347 | } |
328 | 348 | ||
329 | #[cfg(test)] | 349 | #[cfg(test)] |
@@ -477,6 +497,11 @@ fn main() { let _: m::Spam = S$0 } | |||
477 | ), | 497 | ), |
478 | lookup: "Spam::Bar", | 498 | lookup: "Spam::Bar", |
479 | detail: "(i32)", | 499 | detail: "(i32)", |
500 | relevance: CompletionRelevance { | ||
501 | exact_name_match: false, | ||
502 | exact_type_match: true, | ||
503 | is_local: false, | ||
504 | }, | ||
480 | trigger_call_info: true, | 505 | trigger_call_info: true, |
481 | }, | 506 | }, |
482 | CompletionItem { | 507 | CompletionItem { |
@@ -498,6 +523,11 @@ fn main() { let _: m::Spam = S$0 } | |||
498 | ), | 523 | ), |
499 | lookup: "Spam::Foo", | 524 | lookup: "Spam::Foo", |
500 | detail: "()", | 525 | detail: "()", |
526 | relevance: CompletionRelevance { | ||
527 | exact_name_match: false, | ||
528 | exact_type_match: true, | ||
529 | is_local: false, | ||
530 | }, | ||
501 | }, | 531 | }, |
502 | CompletionItem { | 532 | CompletionItem { |
503 | label: "main()", | 533 | label: "main()", |
@@ -1169,4 +1199,86 @@ fn foo(bar: u32) { | |||
1169 | "#]], | 1199 | "#]], |
1170 | ); | 1200 | ); |
1171 | } | 1201 | } |
1202 | |||
1203 | #[test] | ||
1204 | fn enum_owned() { | ||
1205 | check_relevance( | ||
1206 | r#" | ||
1207 | enum Foo { A, B } | ||
1208 | fn foo() { | ||
1209 | bar($0); | ||
1210 | } | ||
1211 | fn bar(t: Foo) {} | ||
1212 | "#, | ||
1213 | expect![[r#" | ||
1214 | ev Foo::A [type] | ||
1215 | ev Foo::B [type] | ||
1216 | en Foo [] | ||
1217 | fn bar(…) [] | ||
1218 | fn foo() [] | ||
1219 | "#]], | ||
1220 | ); | ||
1221 | } | ||
1222 | |||
1223 | #[test] | ||
1224 | fn enum_ref() { | ||
1225 | check_relevance( | ||
1226 | r#" | ||
1227 | enum Foo { A, B } | ||
1228 | fn foo() { | ||
1229 | bar($0); | ||
1230 | } | ||
1231 | fn bar(t: &Foo) {} | ||
1232 | "#, | ||
1233 | expect![[r#" | ||
1234 | ev Foo::A [] | ||
1235 | ev &Foo::A [type] | ||
1236 | ev Foo::B [] | ||
1237 | ev &Foo::B [type] | ||
1238 | en Foo [] | ||
1239 | fn bar(…) [] | ||
1240 | fn foo() [] | ||
1241 | "#]], | ||
1242 | ); | ||
1243 | } | ||
1244 | |||
1245 | #[test] | ||
1246 | fn suggest_deref_fn_ret() { | ||
1247 | check_relevance( | ||
1248 | r#" | ||
1249 | #[lang = "deref"] | ||
1250 | trait Deref { | ||
1251 | type Target; | ||
1252 | fn deref(&self) -> &Self::Target; | ||
1253 | } | ||
1254 | |||
1255 | struct S; | ||
1256 | struct T(S); | ||
1257 | |||
1258 | impl Deref for T { | ||
1259 | type Target = S; | ||
1260 | |||
1261 | fn deref(&self) -> &Self::Target { | ||
1262 | &self.0 | ||
1263 | } | ||
1264 | } | ||
1265 | |||
1266 | fn foo(s: &S) {} | ||
1267 | fn bar() -> T {} | ||
1268 | |||
1269 | fn main() { | ||
1270 | foo($0); | ||
1271 | } | ||
1272 | "#, | ||
1273 | expect![[r#" | ||
1274 | tt Deref [] | ||
1275 | fn bar() [] | ||
1276 | fn &bar() [type] | ||
1277 | fn foo(…) [] | ||
1278 | st T [] | ||
1279 | st S [] | ||
1280 | fn main() [] | ||
1281 | "#]], | ||
1282 | ) | ||
1283 | } | ||
1172 | } | 1284 | } |