diff options
Diffstat (limited to 'crates/completion/src/render.rs')
-rw-r--r-- | crates/completion/src/render.rs | 789 |
1 files changed, 781 insertions, 8 deletions
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs index 3a14357f3..28731e04f 100644 --- a/crates/completion/src/render.rs +++ b/crates/completion/src/render.rs | |||
@@ -8,11 +8,15 @@ mod enum_variant; | |||
8 | mod const_; | 8 | mod const_; |
9 | mod type_alias; | 9 | mod type_alias; |
10 | 10 | ||
11 | use hir::{Documentation, HasAttrs}; | 11 | use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type}; |
12 | use ide_db::RootDatabase; | 12 | use ide_db::RootDatabase; |
13 | use syntax::TextRange; | 13 | use syntax::TextRange; |
14 | use test_utils::mark; | ||
14 | 15 | ||
15 | use crate::{config::SnippetCap, CompletionContext}; | 16 | use crate::{ |
17 | config::SnippetCap, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, | ||
18 | CompletionScore, | ||
19 | }; | ||
16 | 20 | ||
17 | pub(crate) use crate::render::{ | 21 | pub(crate) use crate::render::{ |
18 | const_::ConstRender, enum_variant::EnumVariantRender, function::FunctionRender, | 22 | const_::ConstRender, enum_variant::EnumVariantRender, function::FunctionRender, |
@@ -25,29 +29,42 @@ pub(crate) struct RenderContext<'a> { | |||
25 | } | 29 | } |
26 | 30 | ||
27 | impl<'a> RenderContext<'a> { | 31 | impl<'a> RenderContext<'a> { |
28 | pub fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> { | 32 | fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> { |
29 | RenderContext { completion } | 33 | RenderContext { completion } |
30 | } | 34 | } |
31 | 35 | ||
32 | pub fn snippet_cap(&self) -> Option<SnippetCap> { | 36 | fn snippet_cap(&self) -> Option<SnippetCap> { |
33 | self.completion.config.snippet_cap.clone() | 37 | self.completion.config.snippet_cap.clone() |
34 | } | 38 | } |
35 | 39 | ||
36 | pub fn db(&self) -> &'a RootDatabase { | 40 | fn db(&self) -> &'a RootDatabase { |
37 | &self.completion.db | 41 | &self.completion.db |
38 | } | 42 | } |
39 | 43 | ||
40 | pub fn source_range(&self) -> TextRange { | 44 | fn source_range(&self) -> TextRange { |
41 | self.completion.source_range() | 45 | self.completion.source_range() |
42 | } | 46 | } |
43 | 47 | ||
44 | pub fn is_deprecated(&self, node: impl HasAttrs) -> bool { | 48 | fn is_deprecated(&self, node: impl HasAttrs) -> bool { |
45 | node.attrs(self.db()).by_key("deprecated").exists() | 49 | node.attrs(self.db()).by_key("deprecated").exists() |
46 | } | 50 | } |
47 | 51 | ||
48 | pub fn docs(&self, node: impl HasAttrs) -> Option<Documentation> { | 52 | fn docs(&self, node: impl HasAttrs) -> Option<Documentation> { |
49 | node.docs(self.db()) | 53 | node.docs(self.db()) |
50 | } | 54 | } |
55 | |||
56 | fn active_name_and_type(&self) -> Option<(String, Type)> { | ||
57 | if let Some(record_field) = &self.completion.record_field_syntax { | ||
58 | mark::hit!(record_field_type_match); | ||
59 | let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; | ||
60 | Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db()))) | ||
61 | } else if let Some(active_parameter) = &self.completion.active_parameter { | ||
62 | mark::hit!(active_param_type_match); | ||
63 | Some((active_parameter.name.clone(), active_parameter.ty.clone())) | ||
64 | } else { | ||
65 | None | ||
66 | } | ||
67 | } | ||
51 | } | 68 | } |
52 | 69 | ||
53 | impl<'a> From<&'a CompletionContext<'a>> for RenderContext<'a> { | 70 | impl<'a> From<&'a CompletionContext<'a>> for RenderContext<'a> { |
@@ -55,3 +72,759 @@ impl<'a> From<&'a CompletionContext<'a>> for RenderContext<'a> { | |||
55 | RenderContext::new(ctx) | 72 | RenderContext::new(ctx) |
56 | } | 73 | } |
57 | } | 74 | } |
75 | |||
76 | #[derive(Debug)] | ||
77 | pub(crate) struct Render<'a> { | ||
78 | ctx: RenderContext<'a>, | ||
79 | } | ||
80 | |||
81 | impl<'a> Render<'a> { | ||
82 | pub(crate) fn new(ctx: RenderContext<'a>) -> Render<'a> { | ||
83 | Render { ctx } | ||
84 | } | ||
85 | |||
86 | pub(crate) fn add_field(&mut self, field: hir::Field, ty: &Type) -> CompletionItem { | ||
87 | let is_deprecated = self.ctx.is_deprecated(field); | ||
88 | let name = field.name(self.ctx.db()); | ||
89 | let mut item = CompletionItem::new( | ||
90 | CompletionKind::Reference, | ||
91 | self.ctx.source_range(), | ||
92 | name.to_string(), | ||
93 | ) | ||
94 | .kind(CompletionItemKind::Field) | ||
95 | .detail(ty.display(self.ctx.db()).to_string()) | ||
96 | .set_documentation(field.docs(self.ctx.db())) | ||
97 | .set_deprecated(is_deprecated); | ||
98 | |||
99 | if let Some(score) = compute_score(&self.ctx, &ty, &name.to_string()) { | ||
100 | item = item.set_score(score); | ||
101 | } | ||
102 | |||
103 | return item.build(); | ||
104 | } | ||
105 | |||
106 | pub(crate) fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { | ||
107 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string()) | ||
108 | .kind(CompletionItemKind::Field) | ||
109 | .detail(ty.display(self.ctx.db()).to_string()) | ||
110 | .build() | ||
111 | } | ||
112 | |||
113 | pub(crate) fn render_resolution( | ||
114 | self, | ||
115 | local_name: String, | ||
116 | resolution: &ScopeDef, | ||
117 | ) -> Option<CompletionItem> { | ||
118 | use hir::ModuleDef::*; | ||
119 | |||
120 | let completion_kind = match resolution { | ||
121 | ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType, | ||
122 | _ => CompletionKind::Reference, | ||
123 | }; | ||
124 | |||
125 | let kind = match resolution { | ||
126 | ScopeDef::ModuleDef(Function(func)) => { | ||
127 | let item = FunctionRender::new(self.ctx, Some(local_name), *func).render(); | ||
128 | return Some(item); | ||
129 | } | ||
130 | ScopeDef::ModuleDef(EnumVariant(var)) => { | ||
131 | let item = EnumVariantRender::new(self.ctx, Some(local_name), *var, None).render(); | ||
132 | return Some(item); | ||
133 | } | ||
134 | ScopeDef::MacroDef(mac) => { | ||
135 | let item = MacroRender::new(self.ctx, local_name, *mac).render(); | ||
136 | return item; | ||
137 | } | ||
138 | |||
139 | ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module, | ||
140 | ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct, | ||
141 | // FIXME: add CompletionItemKind::Union | ||
142 | ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct, | ||
143 | ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum, | ||
144 | ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const, | ||
145 | ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static, | ||
146 | ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait, | ||
147 | ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias, | ||
148 | ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, | ||
149 | ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam, | ||
150 | ScopeDef::Local(..) => CompletionItemKind::Binding, | ||
151 | // (does this need its own kind?) | ||
152 | ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam, | ||
153 | ScopeDef::Unknown => { | ||
154 | let item = CompletionItem::new( | ||
155 | CompletionKind::Reference, | ||
156 | self.ctx.source_range(), | ||
157 | local_name, | ||
158 | ) | ||
159 | .kind(CompletionItemKind::UnresolvedReference) | ||
160 | .build(); | ||
161 | return Some(item); | ||
162 | } | ||
163 | }; | ||
164 | |||
165 | let docs = self.docs(resolution); | ||
166 | |||
167 | let mut item = | ||
168 | CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone()); | ||
169 | if let ScopeDef::Local(local) = resolution { | ||
170 | let ty = local.ty(self.ctx.db()); | ||
171 | if !ty.is_unknown() { | ||
172 | item = item.detail(ty.display(self.ctx.db()).to_string()); | ||
173 | } | ||
174 | }; | ||
175 | |||
176 | let mut ref_match = None; | ||
177 | if let ScopeDef::Local(local) = resolution { | ||
178 | if let Some((active_name, active_type)) = self.ctx.active_name_and_type() { | ||
179 | let ty = local.ty(self.ctx.db()); | ||
180 | if let Some(score) = | ||
181 | compute_score_from_active(&active_type, &active_name, &ty, &local_name) | ||
182 | { | ||
183 | item = item.set_score(score); | ||
184 | } | ||
185 | ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name); | ||
186 | } | ||
187 | } | ||
188 | |||
189 | // Add `<>` for generic types | ||
190 | if self.ctx.completion.is_path_type | ||
191 | && !self.ctx.completion.has_type_args | ||
192 | && self.ctx.completion.config.add_call_parenthesis | ||
193 | { | ||
194 | if let Some(cap) = self.ctx.snippet_cap() { | ||
195 | let has_non_default_type_params = match resolution { | ||
196 | ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(self.ctx.db()), | ||
197 | ScopeDef::ModuleDef(TypeAlias(it)) => { | ||
198 | it.has_non_default_type_params(self.ctx.db()) | ||
199 | } | ||
200 | _ => false, | ||
201 | }; | ||
202 | if has_non_default_type_params { | ||
203 | mark::hit!(inserts_angle_brackets_for_generics); | ||
204 | item = item | ||
205 | .lookup_by(local_name.clone()) | ||
206 | .label(format!("{}<…>", local_name)) | ||
207 | .insert_snippet(cap, format!("{}<$0>", local_name)); | ||
208 | } | ||
209 | } | ||
210 | } | ||
211 | |||
212 | let item = item.kind(kind).set_documentation(docs).set_ref_match(ref_match).build(); | ||
213 | Some(item) | ||
214 | } | ||
215 | |||
216 | fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { | ||
217 | use hir::ModuleDef::*; | ||
218 | match resolution { | ||
219 | ScopeDef::ModuleDef(Module(it)) => it.docs(self.ctx.db()), | ||
220 | ScopeDef::ModuleDef(Adt(it)) => it.docs(self.ctx.db()), | ||
221 | ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(self.ctx.db()), | ||
222 | ScopeDef::ModuleDef(Const(it)) => it.docs(self.ctx.db()), | ||
223 | ScopeDef::ModuleDef(Static(it)) => it.docs(self.ctx.db()), | ||
224 | ScopeDef::ModuleDef(Trait(it)) => it.docs(self.ctx.db()), | ||
225 | ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(self.ctx.db()), | ||
226 | _ => None, | ||
227 | } | ||
228 | } | ||
229 | } | ||
230 | |||
231 | fn compute_score_from_active( | ||
232 | active_type: &Type, | ||
233 | active_name: &str, | ||
234 | ty: &Type, | ||
235 | name: &str, | ||
236 | ) -> Option<CompletionScore> { | ||
237 | // Compute score | ||
238 | // For the same type | ||
239 | if active_type != ty { | ||
240 | return None; | ||
241 | } | ||
242 | |||
243 | let mut res = CompletionScore::TypeMatch; | ||
244 | |||
245 | // If same type + same name then go top position | ||
246 | if active_name == name { | ||
247 | res = CompletionScore::TypeAndNameMatch | ||
248 | } | ||
249 | |||
250 | Some(res) | ||
251 | } | ||
252 | fn refed_type_matches( | ||
253 | active_type: &Type, | ||
254 | active_name: &str, | ||
255 | ty: &Type, | ||
256 | name: &str, | ||
257 | ) -> Option<(Mutability, CompletionScore)> { | ||
258 | let derefed_active = active_type.remove_ref()?; | ||
259 | let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?; | ||
260 | Some(( | ||
261 | if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared }, | ||
262 | score, | ||
263 | )) | ||
264 | } | ||
265 | |||
266 | fn compute_score(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionScore> { | ||
267 | let (active_name, active_type) = ctx.active_name_and_type()?; | ||
268 | compute_score_from_active(&active_type, &active_name, ty, name) | ||
269 | } | ||
270 | |||
271 | #[cfg(test)] | ||
272 | mod tests { | ||
273 | use std::cmp::Reverse; | ||
274 | |||
275 | use expect_test::{expect, Expect}; | ||
276 | use test_utils::mark; | ||
277 | |||
278 | use crate::{ | ||
279 | test_utils::{check_edit, do_completion, get_all_items}, | ||
280 | CompletionConfig, CompletionKind, CompletionScore, | ||
281 | }; | ||
282 | |||
283 | fn check(ra_fixture: &str, expect: Expect) { | ||
284 | let actual = do_completion(ra_fixture, CompletionKind::Reference); | ||
285 | expect.assert_debug_eq(&actual); | ||
286 | } | ||
287 | |||
288 | fn check_scores(ra_fixture: &str, expect: Expect) { | ||
289 | fn display_score(score: Option<CompletionScore>) -> &'static str { | ||
290 | match score { | ||
291 | Some(CompletionScore::TypeMatch) => "[type]", | ||
292 | Some(CompletionScore::TypeAndNameMatch) => "[type+name]", | ||
293 | None => "[]".into(), | ||
294 | } | ||
295 | } | ||
296 | |||
297 | let mut completions = get_all_items(CompletionConfig::default(), ra_fixture); | ||
298 | completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); | ||
299 | let actual = completions | ||
300 | .into_iter() | ||
301 | .filter(|it| it.completion_kind == CompletionKind::Reference) | ||
302 | .map(|it| { | ||
303 | let tag = it.kind().unwrap().tag(); | ||
304 | let score = display_score(it.score()); | ||
305 | format!("{} {} {}\n", tag, it.label(), score) | ||
306 | }) | ||
307 | .collect::<String>(); | ||
308 | expect.assert_eq(&actual); | ||
309 | } | ||
310 | |||
311 | #[test] | ||
312 | fn enum_detail_includes_record_fields() { | ||
313 | check( | ||
314 | r#" | ||
315 | enum Foo { Foo { x: i32, y: i32 } } | ||
316 | |||
317 | fn main() { Foo::Fo<|> } | ||
318 | "#, | ||
319 | expect![[r#" | ||
320 | [ | ||
321 | CompletionItem { | ||
322 | label: "Foo", | ||
323 | source_range: 54..56, | ||
324 | delete: 54..56, | ||
325 | insert: "Foo", | ||
326 | kind: EnumVariant, | ||
327 | detail: "{ x: i32, y: i32 }", | ||
328 | }, | ||
329 | ] | ||
330 | "#]], | ||
331 | ); | ||
332 | } | ||
333 | |||
334 | #[test] | ||
335 | fn enum_detail_doesnt_include_tuple_fields() { | ||
336 | check( | ||
337 | r#" | ||
338 | enum Foo { Foo (i32, i32) } | ||
339 | |||
340 | fn main() { Foo::Fo<|> } | ||
341 | "#, | ||
342 | expect![[r#" | ||
343 | [ | ||
344 | CompletionItem { | ||
345 | label: "Foo(…)", | ||
346 | source_range: 46..48, | ||
347 | delete: 46..48, | ||
348 | insert: "Foo($0)", | ||
349 | kind: EnumVariant, | ||
350 | lookup: "Foo", | ||
351 | detail: "(i32, i32)", | ||
352 | trigger_call_info: true, | ||
353 | }, | ||
354 | ] | ||
355 | "#]], | ||
356 | ); | ||
357 | } | ||
358 | |||
359 | #[test] | ||
360 | fn enum_detail_just_parentheses_for_unit() { | ||
361 | check( | ||
362 | r#" | ||
363 | enum Foo { Foo } | ||
364 | |||
365 | fn main() { Foo::Fo<|> } | ||
366 | "#, | ||
367 | expect![[r#" | ||
368 | [ | ||
369 | CompletionItem { | ||
370 | label: "Foo", | ||
371 | source_range: 35..37, | ||
372 | delete: 35..37, | ||
373 | insert: "Foo", | ||
374 | kind: EnumVariant, | ||
375 | detail: "()", | ||
376 | }, | ||
377 | ] | ||
378 | "#]], | ||
379 | ); | ||
380 | } | ||
381 | |||
382 | #[test] | ||
383 | fn lookup_enums_by_two_qualifiers() { | ||
384 | check( | ||
385 | r#" | ||
386 | mod m { | ||
387 | pub enum Spam { Foo, Bar(i32) } | ||
388 | } | ||
389 | fn main() { let _: m::Spam = S<|> } | ||
390 | "#, | ||
391 | expect![[r#" | ||
392 | [ | ||
393 | CompletionItem { | ||
394 | label: "Spam::Bar(…)", | ||
395 | source_range: 75..76, | ||
396 | delete: 75..76, | ||
397 | insert: "Spam::Bar($0)", | ||
398 | kind: EnumVariant, | ||
399 | lookup: "Spam::Bar", | ||
400 | detail: "(i32)", | ||
401 | trigger_call_info: true, | ||
402 | }, | ||
403 | CompletionItem { | ||
404 | label: "m", | ||
405 | source_range: 75..76, | ||
406 | delete: 75..76, | ||
407 | insert: "m", | ||
408 | kind: Module, | ||
409 | }, | ||
410 | CompletionItem { | ||
411 | label: "m::Spam::Foo", | ||
412 | source_range: 75..76, | ||
413 | delete: 75..76, | ||
414 | insert: "m::Spam::Foo", | ||
415 | kind: EnumVariant, | ||
416 | lookup: "Spam::Foo", | ||
417 | detail: "()", | ||
418 | }, | ||
419 | CompletionItem { | ||
420 | label: "main()", | ||
421 | source_range: 75..76, | ||
422 | delete: 75..76, | ||
423 | insert: "main()$0", | ||
424 | kind: Function, | ||
425 | lookup: "main", | ||
426 | detail: "fn main()", | ||
427 | }, | ||
428 | ] | ||
429 | "#]], | ||
430 | ) | ||
431 | } | ||
432 | |||
433 | #[test] | ||
434 | fn sets_deprecated_flag_in_items() { | ||
435 | check( | ||
436 | r#" | ||
437 | #[deprecated] | ||
438 | fn something_deprecated() {} | ||
439 | #[deprecated(since = "1.0.0")] | ||
440 | fn something_else_deprecated() {} | ||
441 | |||
442 | fn main() { som<|> } | ||
443 | "#, | ||
444 | expect![[r#" | ||
445 | [ | ||
446 | CompletionItem { | ||
447 | label: "main()", | ||
448 | source_range: 121..124, | ||
449 | delete: 121..124, | ||
450 | insert: "main()$0", | ||
451 | kind: Function, | ||
452 | lookup: "main", | ||
453 | detail: "fn main()", | ||
454 | }, | ||
455 | CompletionItem { | ||
456 | label: "something_deprecated()", | ||
457 | source_range: 121..124, | ||
458 | delete: 121..124, | ||
459 | insert: "something_deprecated()$0", | ||
460 | kind: Function, | ||
461 | lookup: "something_deprecated", | ||
462 | detail: "fn something_deprecated()", | ||
463 | deprecated: true, | ||
464 | }, | ||
465 | CompletionItem { | ||
466 | label: "something_else_deprecated()", | ||
467 | source_range: 121..124, | ||
468 | delete: 121..124, | ||
469 | insert: "something_else_deprecated()$0", | ||
470 | kind: Function, | ||
471 | lookup: "something_else_deprecated", | ||
472 | detail: "fn something_else_deprecated()", | ||
473 | deprecated: true, | ||
474 | }, | ||
475 | ] | ||
476 | "#]], | ||
477 | ); | ||
478 | |||
479 | check( | ||
480 | r#" | ||
481 | struct A { #[deprecated] the_field: u32 } | ||
482 | fn foo() { A { the<|> } } | ||
483 | "#, | ||
484 | expect![[r#" | ||
485 | [ | ||
486 | CompletionItem { | ||
487 | label: "the_field", | ||
488 | source_range: 57..60, | ||
489 | delete: 57..60, | ||
490 | insert: "the_field", | ||
491 | kind: Field, | ||
492 | detail: "u32", | ||
493 | deprecated: true, | ||
494 | }, | ||
495 | ] | ||
496 | "#]], | ||
497 | ); | ||
498 | } | ||
499 | |||
500 | #[test] | ||
501 | fn renders_docs() { | ||
502 | check( | ||
503 | r#" | ||
504 | struct S { | ||
505 | /// Field docs | ||
506 | foo: | ||
507 | } | ||
508 | impl S { | ||
509 | /// Method docs | ||
510 | fn bar(self) { self.<|> } | ||
511 | }"#, | ||
512 | expect![[r#" | ||
513 | [ | ||
514 | CompletionItem { | ||
515 | label: "bar()", | ||
516 | source_range: 94..94, | ||
517 | delete: 94..94, | ||
518 | insert: "bar()$0", | ||
519 | kind: Method, | ||
520 | lookup: "bar", | ||
521 | detail: "fn bar(self)", | ||
522 | documentation: Documentation( | ||
523 | "Method docs", | ||
524 | ), | ||
525 | }, | ||
526 | CompletionItem { | ||
527 | label: "foo", | ||
528 | source_range: 94..94, | ||
529 | delete: 94..94, | ||
530 | insert: "foo", | ||
531 | kind: Field, | ||
532 | detail: "{unknown}", | ||
533 | documentation: Documentation( | ||
534 | "Field docs", | ||
535 | ), | ||
536 | }, | ||
537 | ] | ||
538 | "#]], | ||
539 | ); | ||
540 | |||
541 | check( | ||
542 | r#" | ||
543 | use self::my<|>; | ||
544 | |||
545 | /// mod docs | ||
546 | mod my { } | ||
547 | |||
548 | /// enum docs | ||
549 | enum E { | ||
550 | /// variant docs | ||
551 | V | ||
552 | } | ||
553 | use self::E::*; | ||
554 | "#, | ||
555 | expect![[r#" | ||
556 | [ | ||
557 | CompletionItem { | ||
558 | label: "E", | ||
559 | source_range: 10..12, | ||
560 | delete: 10..12, | ||
561 | insert: "E", | ||
562 | kind: Enum, | ||
563 | documentation: Documentation( | ||
564 | "enum docs", | ||
565 | ), | ||
566 | }, | ||
567 | CompletionItem { | ||
568 | label: "V", | ||
569 | source_range: 10..12, | ||
570 | delete: 10..12, | ||
571 | insert: "V", | ||
572 | kind: EnumVariant, | ||
573 | detail: "()", | ||
574 | documentation: Documentation( | ||
575 | "variant docs", | ||
576 | ), | ||
577 | }, | ||
578 | CompletionItem { | ||
579 | label: "my", | ||
580 | source_range: 10..12, | ||
581 | delete: 10..12, | ||
582 | insert: "my", | ||
583 | kind: Module, | ||
584 | documentation: Documentation( | ||
585 | "mod docs", | ||
586 | ), | ||
587 | }, | ||
588 | ] | ||
589 | "#]], | ||
590 | ) | ||
591 | } | ||
592 | |||
593 | #[test] | ||
594 | fn dont_render_attrs() { | ||
595 | check( | ||
596 | r#" | ||
597 | struct S; | ||
598 | impl S { | ||
599 | #[inline] | ||
600 | fn the_method(&self) { } | ||
601 | } | ||
602 | fn foo(s: S) { s.<|> } | ||
603 | "#, | ||
604 | expect![[r#" | ||
605 | [ | ||
606 | CompletionItem { | ||
607 | label: "the_method()", | ||
608 | source_range: 81..81, | ||
609 | delete: 81..81, | ||
610 | insert: "the_method()$0", | ||
611 | kind: Method, | ||
612 | lookup: "the_method", | ||
613 | detail: "fn the_method(&self)", | ||
614 | }, | ||
615 | ] | ||
616 | "#]], | ||
617 | ) | ||
618 | } | ||
619 | |||
620 | #[test] | ||
621 | fn no_call_parens_if_fn_ptr_needed() { | ||
622 | mark::check!(no_call_parens_if_fn_ptr_needed); | ||
623 | check_edit( | ||
624 | "foo", | ||
625 | r#" | ||
626 | fn foo(foo: u8, bar: u8) {} | ||
627 | struct ManualVtable { f: fn(u8, u8) } | ||
628 | |||
629 | fn main() -> ManualVtable { | ||
630 | ManualVtable { f: f<|> } | ||
631 | } | ||
632 | "#, | ||
633 | r#" | ||
634 | fn foo(foo: u8, bar: u8) {} | ||
635 | struct ManualVtable { f: fn(u8, u8) } | ||
636 | |||
637 | fn main() -> ManualVtable { | ||
638 | ManualVtable { f: foo } | ||
639 | } | ||
640 | "#, | ||
641 | ); | ||
642 | } | ||
643 | |||
644 | #[test] | ||
645 | fn no_parens_in_use_item() { | ||
646 | mark::check!(no_parens_in_use_item); | ||
647 | check_edit( | ||
648 | "foo", | ||
649 | r#" | ||
650 | mod m { pub fn foo() {} } | ||
651 | use crate::m::f<|>; | ||
652 | "#, | ||
653 | r#" | ||
654 | mod m { pub fn foo() {} } | ||
655 | use crate::m::foo; | ||
656 | "#, | ||
657 | ); | ||
658 | } | ||
659 | |||
660 | #[test] | ||
661 | fn no_parens_in_call() { | ||
662 | check_edit( | ||
663 | "foo", | ||
664 | r#" | ||
665 | fn foo(x: i32) {} | ||
666 | fn main() { f<|>(); } | ||
667 | "#, | ||
668 | r#" | ||
669 | fn foo(x: i32) {} | ||
670 | fn main() { foo(); } | ||
671 | "#, | ||
672 | ); | ||
673 | check_edit( | ||
674 | "foo", | ||
675 | r#" | ||
676 | struct Foo; | ||
677 | impl Foo { fn foo(&self){} } | ||
678 | fn f(foo: &Foo) { foo.f<|>(); } | ||
679 | "#, | ||
680 | r#" | ||
681 | struct Foo; | ||
682 | impl Foo { fn foo(&self){} } | ||
683 | fn f(foo: &Foo) { foo.foo(); } | ||
684 | "#, | ||
685 | ); | ||
686 | } | ||
687 | |||
688 | #[test] | ||
689 | fn inserts_angle_brackets_for_generics() { | ||
690 | mark::check!(inserts_angle_brackets_for_generics); | ||
691 | check_edit( | ||
692 | "Vec", | ||
693 | r#" | ||
694 | struct Vec<T> {} | ||
695 | fn foo(xs: Ve<|>) | ||
696 | "#, | ||
697 | r#" | ||
698 | struct Vec<T> {} | ||
699 | fn foo(xs: Vec<$0>) | ||
700 | "#, | ||
701 | ); | ||
702 | check_edit( | ||
703 | "Vec", | ||
704 | r#" | ||
705 | type Vec<T> = (T,); | ||
706 | fn foo(xs: Ve<|>) | ||
707 | "#, | ||
708 | r#" | ||
709 | type Vec<T> = (T,); | ||
710 | fn foo(xs: Vec<$0>) | ||
711 | "#, | ||
712 | ); | ||
713 | check_edit( | ||
714 | "Vec", | ||
715 | r#" | ||
716 | struct Vec<T = i128> {} | ||
717 | fn foo(xs: Ve<|>) | ||
718 | "#, | ||
719 | r#" | ||
720 | struct Vec<T = i128> {} | ||
721 | fn foo(xs: Vec) | ||
722 | "#, | ||
723 | ); | ||
724 | check_edit( | ||
725 | "Vec", | ||
726 | r#" | ||
727 | struct Vec<T> {} | ||
728 | fn foo(xs: Ve<|><i128>) | ||
729 | "#, | ||
730 | r#" | ||
731 | struct Vec<T> {} | ||
732 | fn foo(xs: Vec<i128>) | ||
733 | "#, | ||
734 | ); | ||
735 | } | ||
736 | |||
737 | #[test] | ||
738 | fn active_param_score() { | ||
739 | mark::check!(active_param_type_match); | ||
740 | check_scores( | ||
741 | r#" | ||
742 | struct S { foo: i64, bar: u32, baz: u32 } | ||
743 | fn test(bar: u32) { } | ||
744 | fn foo(s: S) { test(s.<|>) } | ||
745 | "#, | ||
746 | expect![[r#" | ||
747 | fd bar [type+name] | ||
748 | fd baz [type] | ||
749 | fd foo [] | ||
750 | "#]], | ||
751 | ); | ||
752 | } | ||
753 | |||
754 | #[test] | ||
755 | fn record_field_scores() { | ||
756 | mark::check!(record_field_type_match); | ||
757 | check_scores( | ||
758 | r#" | ||
759 | struct A { foo: i64, bar: u32, baz: u32 } | ||
760 | struct B { x: (), y: f32, bar: u32 } | ||
761 | fn foo(a: A) { B { bar: a.<|> }; } | ||
762 | "#, | ||
763 | expect![[r#" | ||
764 | fd bar [type+name] | ||
765 | fd baz [type] | ||
766 | fd foo [] | ||
767 | "#]], | ||
768 | ) | ||
769 | } | ||
770 | |||
771 | #[test] | ||
772 | fn record_field_and_call_scores() { | ||
773 | check_scores( | ||
774 | r#" | ||
775 | struct A { foo: i64, bar: u32, baz: u32 } | ||
776 | struct B { x: (), y: f32, bar: u32 } | ||
777 | fn f(foo: i64) { } | ||
778 | fn foo(a: A) { B { bar: f(a.<|>) }; } | ||
779 | "#, | ||
780 | expect![[r#" | ||
781 | fd foo [type+name] | ||
782 | fd bar [] | ||
783 | fd baz [] | ||
784 | "#]], | ||
785 | ); | ||
786 | check_scores( | ||
787 | r#" | ||
788 | struct A { foo: i64, bar: u32, baz: u32 } | ||
789 | struct B { x: (), y: f32, bar: u32 } | ||
790 | fn f(foo: i64) { } | ||
791 | fn foo(a: A) { f(B { bar: a.<|> }); } | ||
792 | "#, | ||
793 | expect![[r#" | ||
794 | fd bar [type+name] | ||
795 | fd baz [type] | ||
796 | fd foo [] | ||
797 | "#]], | ||
798 | ); | ||
799 | } | ||
800 | |||
801 | #[test] | ||
802 | fn prioritize_exact_ref_match() { | ||
803 | check_scores( | ||
804 | r#" | ||
805 | struct WorldSnapshot { _f: () }; | ||
806 | fn go(world: &WorldSnapshot) { go(w<|>) } | ||
807 | "#, | ||
808 | expect![[r#" | ||
809 | bn world [type+name] | ||
810 | st WorldSnapshot [] | ||
811 | fn go(…) [] | ||
812 | "#]], | ||
813 | ); | ||
814 | } | ||
815 | |||
816 | #[test] | ||
817 | fn too_many_arguments() { | ||
818 | check_scores( | ||
819 | r#" | ||
820 | struct Foo; | ||
821 | fn f(foo: &Foo) { f(foo, w<|>) } | ||
822 | "#, | ||
823 | expect![[r#" | ||
824 | st Foo [] | ||
825 | fn f(…) [] | ||
826 | bn foo [] | ||
827 | "#]], | ||
828 | ); | ||
829 | } | ||
830 | } | ||