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