diff options
-rw-r--r-- | crates/ide_completion/src/render.rs | 182 | ||||
-rw-r--r-- | crates/ide_completion/src/render/enum_variant.rs | 13 | ||||
-rw-r--r-- | crates/ide_completion/src/render/function.rs | 27 | ||||
-rw-r--r-- | docs/dev/style.md | 21 |
4 files changed, 193 insertions, 50 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 | } |
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs index e8cfcc0c7..374247b05 100644 --- a/crates/ide_completion/src/render/enum_variant.rs +++ b/crates/ide_completion/src/render/enum_variant.rs | |||
@@ -6,7 +6,8 @@ use itertools::Itertools; | |||
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | item::{CompletionItem, CompletionKind, ImportEdit}, | 8 | item::{CompletionItem, CompletionKind, ImportEdit}, |
9 | render::{builder_ext::Params, RenderContext}, | 9 | render::{builder_ext::Params, compute_exact_type_match, compute_ref_match, RenderContext}, |
10 | CompletionRelevance, | ||
10 | }; | 11 | }; |
11 | 12 | ||
12 | pub(crate) fn render_variant<'a>( | 13 | pub(crate) fn render_variant<'a>( |
@@ -74,6 +75,16 @@ impl<'a> EnumRender<'a> { | |||
74 | item.lookup_by(self.short_qualified_name); | 75 | item.lookup_by(self.short_qualified_name); |
75 | } | 76 | } |
76 | 77 | ||
78 | let ty = self.variant.parent_enum(self.ctx.completion.db).ty(self.ctx.completion.db); | ||
79 | item.set_relevance(CompletionRelevance { | ||
80 | exact_type_match: compute_exact_type_match(self.ctx.completion, &ty), | ||
81 | ..CompletionRelevance::default() | ||
82 | }); | ||
83 | |||
84 | if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ty) { | ||
85 | item.ref_match(ref_match); | ||
86 | } | ||
87 | |||
77 | item.build() | 88 | item.build() |
78 | } | 89 | } |
79 | 90 | ||
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs index 47e26a5d8..194ea135e 100644 --- a/crates/ide_completion/src/render/function.rs +++ b/crates/ide_completion/src/render/function.rs | |||
@@ -6,7 +6,10 @@ use syntax::ast::Fn; | |||
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | item::{CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, ImportEdit}, | 8 | item::{CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, ImportEdit}, |
9 | render::{builder_ext::Params, RenderContext}, | 9 | render::{ |
10 | builder_ext::Params, compute_exact_name_match, compute_exact_type_match, compute_ref_match, | ||
11 | RenderContext, | ||
12 | }, | ||
10 | }; | 13 | }; |
11 | 14 | ||
12 | pub(crate) fn render_fn<'a>( | 15 | pub(crate) fn render_fn<'a>( |
@@ -52,23 +55,19 @@ impl<'a> FunctionRender<'a> { | |||
52 | self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), | 55 | self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), |
53 | ) | 56 | ) |
54 | .detail(self.detail()) | 57 | .detail(self.detail()) |
55 | .add_call_parens(self.ctx.completion, self.name, params) | 58 | .add_call_parens(self.ctx.completion, self.name.clone(), params) |
56 | .add_import(import_to_add); | 59 | .add_import(import_to_add); |
57 | 60 | ||
58 | let mut relevance = CompletionRelevance::default(); | 61 | let ret_type = self.func.ret_type(self.ctx.db()); |
59 | if let Some(expected_type) = &self.ctx.completion.expected_type { | 62 | item.set_relevance(CompletionRelevance { |
60 | let ret_ty = self.func.ret_type(self.ctx.db()); | 63 | exact_type_match: compute_exact_type_match(self.ctx.completion, &ret_type), |
64 | exact_name_match: compute_exact_name_match(self.ctx.completion, self.name.clone()), | ||
65 | ..CompletionRelevance::default() | ||
66 | }); | ||
61 | 67 | ||
62 | // We don't ever consider a function which returns unit type to be an | 68 | if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ret_type) { |
63 | // exact type match, since nearly always this is not meaningful to the | 69 | item.ref_match(ref_match); |
64 | // user. | ||
65 | relevance.exact_type_match = &ret_ty == expected_type && !ret_ty.is_unit(); | ||
66 | } | 70 | } |
67 | if let Some(expected_name) = &self.ctx.completion.expected_name { | ||
68 | relevance.exact_name_match = | ||
69 | expected_name == &self.func.name(self.ctx.db()).to_string(); | ||
70 | } | ||
71 | item.set_relevance(relevance); | ||
72 | 71 | ||
73 | item.build() | 72 | item.build() |
74 | } | 73 | } |
diff --git a/docs/dev/style.md b/docs/dev/style.md index c4eb7bc7a..e4a1672ca 100644 --- a/docs/dev/style.md +++ b/docs/dev/style.md | |||
@@ -787,6 +787,27 @@ assert!(0 > x); | |||
787 | 787 | ||
788 | **Rationale:** Less-then comparisons are more intuitive, they correspond spatially to [real line](https://en.wikipedia.org/wiki/Real_line). | 788 | **Rationale:** Less-then comparisons are more intuitive, they correspond spatially to [real line](https://en.wikipedia.org/wiki/Real_line). |
789 | 789 | ||
790 | ## If-let | ||
791 | |||
792 | Avoid `if let ... { } else { }` construct, use `match` instead. | ||
793 | |||
794 | ```rust | ||
795 | // GOOD | ||
796 | match ctx.expected_type.as_ref() { | ||
797 | Some(expected_type) => completion_ty == expected_type && !expected_type.is_unit(), | ||
798 | None => false, | ||
799 | } | ||
800 | |||
801 | // BAD | ||
802 | if let Some(expected_type) = ctx.expected_type.as_ref() { | ||
803 | completion_ty == expected_type && !expected_type.is_unit() | ||
804 | } else { | ||
805 | false | ||
806 | } | ||
807 | ``` | ||
808 | |||
809 | **Rational:** `match` is almost always more compact. | ||
810 | The `else` branch can get a more precise pattern: `None` or `Err(_)` instead of `_`. | ||
790 | 811 | ||
791 | ## Token names | 812 | ## Token names |
792 | 813 | ||