diff options
Diffstat (limited to 'crates/ide_completion/src/render.rs')
-rw-r--r-- | crates/ide_completion/src/render.rs | 271 |
1 files changed, 158 insertions, 113 deletions
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index eddaaa6f3..db31896e5 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -13,13 +13,15 @@ mod builder_ext; | |||
13 | use hir::{ | 13 | use hir::{ |
14 | AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, | 14 | AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, |
15 | }; | 15 | }; |
16 | use ide_db::{helpers::SnippetCap, RootDatabase, SymbolKind}; | 16 | use ide_db::{ |
17 | helpers::{item_name, SnippetCap}, | ||
18 | RootDatabase, SymbolKind, | ||
19 | }; | ||
17 | use syntax::TextRange; | 20 | use syntax::TextRange; |
18 | use test_utils::mark; | ||
19 | 21 | ||
20 | use crate::{ | 22 | use crate::{ |
21 | item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, | 23 | item::{CompletionRelevance, ImportEdit}, |
22 | CompletionScore, | 24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, |
23 | }; | 25 | }; |
24 | 26 | ||
25 | 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}; |
@@ -51,18 +53,20 @@ pub(crate) fn render_resolution<'a>( | |||
51 | pub(crate) fn render_resolution_with_import<'a>( | 53 | pub(crate) fn render_resolution_with_import<'a>( |
52 | ctx: RenderContext<'a>, | 54 | ctx: RenderContext<'a>, |
53 | import_edit: ImportEdit, | 55 | import_edit: ImportEdit, |
54 | resolution: &ScopeDef, | ||
55 | ) -> Option<CompletionItem> { | 56 | ) -> Option<CompletionItem> { |
57 | let resolution = ScopeDef::from(import_edit.import.original_item); | ||
56 | let local_name = match resolution { | 58 | let local_name = match resolution { |
57 | ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), | 59 | ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), |
58 | ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), | 60 | ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), |
59 | ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), | 61 | ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), |
60 | _ => import_edit.import_path.segments().last()?.to_string(), | 62 | _ => item_name(ctx.db(), import_edit.import.original_item)?.to_string(), |
61 | }; | 63 | }; |
62 | Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| { | 64 | Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map( |
63 | item.completion_kind = CompletionKind::Magic; | 65 | |mut item| { |
64 | item | 66 | item.completion_kind = CompletionKind::Magic; |
65 | }) | 67 | item |
68 | }, | ||
69 | ) | ||
66 | } | 70 | } |
67 | 71 | ||
68 | /// Interface for data and methods required for items rendering. | 72 | /// Interface for data and methods required for items rendering. |
@@ -113,13 +117,13 @@ impl<'a> RenderContext<'a> { | |||
113 | node.docs(self.db()) | 117 | node.docs(self.db()) |
114 | } | 118 | } |
115 | 119 | ||
116 | fn active_name_and_type(&self) -> Option<(String, Type)> { | 120 | fn expected_name_and_type(&self) -> Option<(String, Type)> { |
117 | if let Some(record_field) = &self.completion.record_field_syntax { | 121 | if let Some(record_field) = &self.completion.record_field_syntax { |
118 | mark::hit!(record_field_type_match); | 122 | cov_mark::hit!(record_field_type_match); |
119 | let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; | 123 | let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; |
120 | Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db()))) | 124 | Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db()))) |
121 | } else if let Some(active_parameter) = &self.completion.active_parameter { | 125 | } else if let Some(active_parameter) = &self.completion.active_parameter { |
122 | mark::hit!(active_param_type_match); | 126 | cov_mark::hit!(active_param_type_match); |
123 | Some((active_parameter.name.clone(), active_parameter.ty.clone())) | 127 | Some((active_parameter.name.clone(), active_parameter.ty.clone())) |
124 | } else { | 128 | } else { |
125 | None | 129 | None |
@@ -145,24 +149,29 @@ impl<'a> Render<'a> { | |||
145 | CompletionKind::Reference, | 149 | CompletionKind::Reference, |
146 | self.ctx.source_range(), | 150 | self.ctx.source_range(), |
147 | name.to_string(), | 151 | name.to_string(), |
148 | ) | 152 | ); |
149 | .kind(SymbolKind::Field) | 153 | item.kind(SymbolKind::Field) |
150 | .detail(ty.display(self.ctx.db()).to_string()) | 154 | .detail(ty.display(self.ctx.db()).to_string()) |
151 | .set_documentation(field.docs(self.ctx.db())) | 155 | .set_documentation(field.docs(self.ctx.db())) |
152 | .set_deprecated(is_deprecated); | 156 | .set_deprecated(is_deprecated); |
153 | 157 | ||
154 | if let Some(score) = compute_score(&self.ctx, &ty, &name.to_string()) { | 158 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) { |
155 | item = item.set_score(score); | 159 | item.set_relevance(relevance); |
156 | } | 160 | } |
157 | 161 | ||
158 | item.build() | 162 | item.build() |
159 | } | 163 | } |
160 | 164 | ||
161 | fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { | 165 | fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { |
162 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string()) | 166 | let mut item = CompletionItem::new( |
163 | .kind(SymbolKind::Field) | 167 | CompletionKind::Reference, |
164 | .detail(ty.display(self.ctx.db()).to_string()) | 168 | self.ctx.source_range(), |
165 | .build() | 169 | field.to_string(), |
170 | ); | ||
171 | |||
172 | item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string()); | ||
173 | |||
174 | item.build() | ||
166 | } | 175 | } |
167 | 176 | ||
168 | fn render_resolution( | 177 | fn render_resolution( |
@@ -221,15 +230,13 @@ impl<'a> Render<'a> { | |||
221 | CompletionItemKind::SymbolKind(SymbolKind::SelfParam) | 230 | CompletionItemKind::SymbolKind(SymbolKind::SelfParam) |
222 | } | 231 | } |
223 | ScopeDef::Unknown => { | 232 | ScopeDef::Unknown => { |
224 | let item = CompletionItem::new( | 233 | let mut item = CompletionItem::new( |
225 | CompletionKind::Reference, | 234 | CompletionKind::Reference, |
226 | self.ctx.source_range(), | 235 | self.ctx.source_range(), |
227 | local_name, | 236 | local_name, |
228 | ) | 237 | ); |
229 | .kind(CompletionItemKind::UnresolvedReference) | 238 | item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add); |
230 | .add_import(import_to_add) | 239 | return Some(item.build()); |
231 | .build(); | ||
232 | return Some(item); | ||
233 | } | 240 | } |
234 | }; | 241 | }; |
235 | 242 | ||
@@ -238,20 +245,27 @@ impl<'a> Render<'a> { | |||
238 | if let ScopeDef::Local(local) = resolution { | 245 | if let ScopeDef::Local(local) = resolution { |
239 | let ty = local.ty(self.ctx.db()); | 246 | let ty = local.ty(self.ctx.db()); |
240 | if !ty.is_unknown() { | 247 | if !ty.is_unknown() { |
241 | item = item.detail(ty.display(self.ctx.db()).to_string()); | 248 | item.detail(ty.display(self.ctx.db()).to_string()); |
242 | } | 249 | } |
243 | }; | 250 | }; |
244 | 251 | ||
245 | let mut ref_match = None; | ||
246 | if let ScopeDef::Local(local) = resolution { | 252 | if let ScopeDef::Local(local) = resolution { |
247 | if let Some((active_name, active_type)) = self.ctx.active_name_and_type() { | 253 | let ty = local.ty(self.ctx.db()); |
248 | let ty = local.ty(self.ctx.db()); | 254 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) { |
249 | if let Some(score) = | 255 | item.set_relevance(relevance); |
250 | compute_score_from_active(&active_type, &active_name, &ty, &local_name) | 256 | } |
251 | { | 257 | if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { |
252 | item = item.set_score(score); | 258 | if let Some(ty_without_ref) = expected_type.remove_ref() { |
259 | if ty_without_ref == ty { | ||
260 | cov_mark::hit!(suggest_ref); | ||
261 | let mutability = if expected_type.is_mutable_reference() { | ||
262 | Mutability::Mut | ||
263 | } else { | ||
264 | Mutability::Shared | ||
265 | }; | ||
266 | item.ref_match(mutability); | ||
267 | } | ||
253 | } | 268 | } |
254 | ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name); | ||
255 | } | 269 | } |
256 | } | 270 | } |
257 | 271 | ||
@@ -269,23 +283,18 @@ impl<'a> Render<'a> { | |||
269 | _ => false, | 283 | _ => false, |
270 | }; | 284 | }; |
271 | if has_non_default_type_params { | 285 | if has_non_default_type_params { |
272 | mark::hit!(inserts_angle_brackets_for_generics); | 286 | cov_mark::hit!(inserts_angle_brackets_for_generics); |
273 | item = item | 287 | item.lookup_by(local_name.clone()) |
274 | .lookup_by(local_name.clone()) | ||
275 | .label(format!("{}<…>", local_name)) | 288 | .label(format!("{}<…>", local_name)) |
276 | .insert_snippet(cap, format!("{}<$0>", local_name)); | 289 | .insert_snippet(cap, format!("{}<$0>", local_name)); |
277 | } | 290 | } |
278 | } | 291 | } |
279 | } | 292 | } |
280 | 293 | item.kind(kind) | |
281 | Some( | 294 | .add_import(import_to_add) |
282 | item.kind(kind) | 295 | .set_documentation(self.docs(resolution)) |
283 | .add_import(import_to_add) | 296 | .set_deprecated(self.is_deprecated(resolution)); |
284 | .set_ref_match(ref_match) | 297 | Some(item.build()) |
285 | .set_documentation(self.docs(resolution)) | ||
286 | .set_deprecated(self.is_deprecated(resolution)) | ||
287 | .build(), | ||
288 | ) | ||
289 | } | 298 | } |
290 | 299 | ||
291 | fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { | 300 | fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { |
@@ -313,56 +322,23 @@ impl<'a> Render<'a> { | |||
313 | } | 322 | } |
314 | } | 323 | } |
315 | 324 | ||
316 | fn compute_score_from_active( | 325 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionRelevance> { |
317 | active_type: &Type, | 326 | let (expected_name, expected_type) = ctx.expected_name_and_type()?; |
318 | active_name: &str, | 327 | let mut res = CompletionRelevance::default(); |
319 | ty: &Type, | 328 | res.exact_type_match = ty == &expected_type; |
320 | name: &str, | 329 | res.exact_name_match = name == &expected_name; |
321 | ) -> Option<CompletionScore> { | ||
322 | // Compute score | ||
323 | // For the same type | ||
324 | if active_type != ty { | ||
325 | return None; | ||
326 | } | ||
327 | |||
328 | let mut res = CompletionScore::TypeMatch; | ||
329 | |||
330 | // If same type + same name then go top position | ||
331 | if active_name == name { | ||
332 | res = CompletionScore::TypeAndNameMatch | ||
333 | } | ||
334 | |||
335 | Some(res) | 330 | Some(res) |
336 | } | 331 | } |
337 | fn refed_type_matches( | ||
338 | active_type: &Type, | ||
339 | active_name: &str, | ||
340 | ty: &Type, | ||
341 | name: &str, | ||
342 | ) -> Option<(Mutability, CompletionScore)> { | ||
343 | let derefed_active = active_type.remove_ref()?; | ||
344 | let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?; | ||
345 | Some(( | ||
346 | if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared }, | ||
347 | score, | ||
348 | )) | ||
349 | } | ||
350 | |||
351 | fn compute_score(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionScore> { | ||
352 | let (active_name, active_type) = ctx.active_name_and_type()?; | ||
353 | compute_score_from_active(&active_type, &active_name, ty, name) | ||
354 | } | ||
355 | 332 | ||
356 | #[cfg(test)] | 333 | #[cfg(test)] |
357 | mod tests { | 334 | mod tests { |
358 | use std::cmp::Reverse; | 335 | use std::cmp::Reverse; |
359 | 336 | ||
360 | use expect_test::{expect, Expect}; | 337 | use expect_test::{expect, Expect}; |
361 | use test_utils::mark; | ||
362 | 338 | ||
363 | use crate::{ | 339 | use crate::{ |
364 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, | 340 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, |
365 | CompletionKind, CompletionScore, | 341 | CompletionKind, CompletionRelevance, |
366 | }; | 342 | }; |
367 | 343 | ||
368 | fn check(ra_fixture: &str, expect: Expect) { | 344 | fn check(ra_fixture: &str, expect: Expect) { |
@@ -370,24 +346,27 @@ mod tests { | |||
370 | expect.assert_debug_eq(&actual); | 346 | expect.assert_debug_eq(&actual); |
371 | } | 347 | } |
372 | 348 | ||
373 | fn check_scores(ra_fixture: &str, expect: Expect) { | 349 | fn check_relevance(ra_fixture: &str, expect: Expect) { |
374 | fn display_score(score: Option<CompletionScore>) -> &'static str { | 350 | fn display_relevance(relevance: CompletionRelevance) -> &'static str { |
375 | match score { | 351 | match relevance { |
376 | Some(CompletionScore::TypeMatch) => "[type]", | 352 | CompletionRelevance { exact_type_match: true, exact_name_match: true } => { |
377 | Some(CompletionScore::TypeAndNameMatch) => "[type+name]", | 353 | "[type+name]" |
378 | None => "[]".into(), | 354 | } |
355 | CompletionRelevance { exact_type_match: true, exact_name_match: false } => "[type]", | ||
356 | CompletionRelevance { exact_type_match: false, exact_name_match: true } => "[name]", | ||
357 | CompletionRelevance { exact_type_match: false, exact_name_match: false } => "[]", | ||
379 | } | 358 | } |
380 | } | 359 | } |
381 | 360 | ||
382 | let mut completions = get_all_items(TEST_CONFIG, ra_fixture); | 361 | let mut completions = get_all_items(TEST_CONFIG, ra_fixture); |
383 | completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); | 362 | completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string())); |
384 | let actual = completions | 363 | let actual = completions |
385 | .into_iter() | 364 | .into_iter() |
386 | .filter(|it| it.completion_kind == CompletionKind::Reference) | 365 | .filter(|it| it.completion_kind == CompletionKind::Reference) |
387 | .map(|it| { | 366 | .map(|it| { |
388 | let tag = it.kind().unwrap().tag(); | 367 | let tag = it.kind().unwrap().tag(); |
389 | let score = display_score(it.score()); | 368 | let relevance = display_relevance(it.relevance()); |
390 | format!("{} {} {}\n", tag, it.label(), score) | 369 | format!("{} {} {}\n", tag, it.label(), relevance) |
391 | }) | 370 | }) |
392 | .collect::<String>(); | 371 | .collect::<String>(); |
393 | expect.assert_eq(&actual); | 372 | expect.assert_eq(&actual); |
@@ -734,7 +713,7 @@ fn foo(s: S) { s.$0 } | |||
734 | 713 | ||
735 | #[test] | 714 | #[test] |
736 | fn no_call_parens_if_fn_ptr_needed() { | 715 | fn no_call_parens_if_fn_ptr_needed() { |
737 | mark::check!(no_call_parens_if_fn_ptr_needed); | 716 | cov_mark::check!(no_call_parens_if_fn_ptr_needed); |
738 | check_edit( | 717 | check_edit( |
739 | "foo", | 718 | "foo", |
740 | r#" | 719 | r#" |
@@ -758,7 +737,7 @@ fn main() -> ManualVtable { | |||
758 | 737 | ||
759 | #[test] | 738 | #[test] |
760 | fn no_parens_in_use_item() { | 739 | fn no_parens_in_use_item() { |
761 | mark::check!(no_parens_in_use_item); | 740 | cov_mark::check!(no_parens_in_use_item); |
762 | check_edit( | 741 | check_edit( |
763 | "foo", | 742 | "foo", |
764 | r#" | 743 | r#" |
@@ -802,7 +781,7 @@ fn f(foo: &Foo) { foo.foo(); } | |||
802 | 781 | ||
803 | #[test] | 782 | #[test] |
804 | fn inserts_angle_brackets_for_generics() { | 783 | fn inserts_angle_brackets_for_generics() { |
805 | mark::check!(inserts_angle_brackets_for_generics); | 784 | cov_mark::check!(inserts_angle_brackets_for_generics); |
806 | check_edit( | 785 | check_edit( |
807 | "Vec", | 786 | "Vec", |
808 | r#" | 787 | r#" |
@@ -850,9 +829,9 @@ fn foo(xs: Vec<i128>) | |||
850 | } | 829 | } |
851 | 830 | ||
852 | #[test] | 831 | #[test] |
853 | fn active_param_score() { | 832 | fn active_param_relevance() { |
854 | mark::check!(active_param_type_match); | 833 | cov_mark::check!(active_param_type_match); |
855 | check_scores( | 834 | check_relevance( |
856 | r#" | 835 | r#" |
857 | struct S { foo: i64, bar: u32, baz: u32 } | 836 | struct S { foo: i64, bar: u32, baz: u32 } |
858 | fn test(bar: u32) { } | 837 | fn test(bar: u32) { } |
@@ -867,9 +846,9 @@ fn foo(s: S) { test(s.$0) } | |||
867 | } | 846 | } |
868 | 847 | ||
869 | #[test] | 848 | #[test] |
870 | fn record_field_scores() { | 849 | fn record_field_relevances() { |
871 | mark::check!(record_field_type_match); | 850 | cov_mark::check!(record_field_type_match); |
872 | check_scores( | 851 | check_relevance( |
873 | r#" | 852 | r#" |
874 | struct A { foo: i64, bar: u32, baz: u32 } | 853 | struct A { foo: i64, bar: u32, baz: u32 } |
875 | struct B { x: (), y: f32, bar: u32 } | 854 | struct B { x: (), y: f32, bar: u32 } |
@@ -884,8 +863,8 @@ fn foo(a: A) { B { bar: a.$0 }; } | |||
884 | } | 863 | } |
885 | 864 | ||
886 | #[test] | 865 | #[test] |
887 | fn record_field_and_call_scores() { | 866 | fn record_field_and_call_relevances() { |
888 | check_scores( | 867 | check_relevance( |
889 | r#" | 868 | r#" |
890 | struct A { foo: i64, bar: u32, baz: u32 } | 869 | struct A { foo: i64, bar: u32, baz: u32 } |
891 | struct B { x: (), y: f32, bar: u32 } | 870 | struct B { x: (), y: f32, bar: u32 } |
@@ -898,7 +877,7 @@ fn foo(a: A) { B { bar: f(a.$0) }; } | |||
898 | fd baz [] | 877 | fd baz [] |
899 | "#]], | 878 | "#]], |
900 | ); | 879 | ); |
901 | check_scores( | 880 | check_relevance( |
902 | r#" | 881 | r#" |
903 | struct A { foo: i64, bar: u32, baz: u32 } | 882 | struct A { foo: i64, bar: u32, baz: u32 } |
904 | struct B { x: (), y: f32, bar: u32 } | 883 | struct B { x: (), y: f32, bar: u32 } |
@@ -915,7 +894,7 @@ fn foo(a: A) { f(B { bar: a.$0 }); } | |||
915 | 894 | ||
916 | #[test] | 895 | #[test] |
917 | fn prioritize_exact_ref_match() { | 896 | fn prioritize_exact_ref_match() { |
918 | check_scores( | 897 | check_relevance( |
919 | r#" | 898 | r#" |
920 | struct WorldSnapshot { _f: () }; | 899 | struct WorldSnapshot { _f: () }; |
921 | fn go(world: &WorldSnapshot) { go(w$0) } | 900 | fn go(world: &WorldSnapshot) { go(w$0) } |
@@ -930,7 +909,7 @@ fn go(world: &WorldSnapshot) { go(w$0) } | |||
930 | 909 | ||
931 | #[test] | 910 | #[test] |
932 | fn too_many_arguments() { | 911 | fn too_many_arguments() { |
933 | check_scores( | 912 | check_relevance( |
934 | r#" | 913 | r#" |
935 | struct Foo; | 914 | struct Foo; |
936 | fn f(foo: &Foo) { f(foo, w$0) } | 915 | fn f(foo: &Foo) { f(foo, w$0) } |
@@ -942,4 +921,70 @@ fn f(foo: &Foo) { f(foo, w$0) } | |||
942 | "#]], | 921 | "#]], |
943 | ); | 922 | ); |
944 | } | 923 | } |
924 | |||
925 | #[test] | ||
926 | fn suggest_ref_mut() { | ||
927 | cov_mark::check!(suggest_ref); | ||
928 | check( | ||
929 | r#" | ||
930 | struct S; | ||
931 | fn foo(s: &mut S) {} | ||
932 | fn main() { | ||
933 | let mut s = S; | ||
934 | foo($0); | ||
935 | } | ||
936 | "#, | ||
937 | expect![[r#" | ||
938 | [ | ||
939 | CompletionItem { | ||
940 | label: "S", | ||
941 | source_range: 70..70, | ||
942 | delete: 70..70, | ||
943 | insert: "S", | ||
944 | kind: SymbolKind( | ||
945 | Struct, | ||
946 | ), | ||
947 | }, | ||
948 | CompletionItem { | ||
949 | label: "foo(…)", | ||
950 | source_range: 70..70, | ||
951 | delete: 70..70, | ||
952 | insert: "foo(${1:&mut s})$0", | ||
953 | kind: SymbolKind( | ||
954 | Function, | ||
955 | ), | ||
956 | lookup: "foo", | ||
957 | detail: "-> ()", | ||
958 | trigger_call_info: true, | ||
959 | }, | ||
960 | CompletionItem { | ||
961 | label: "main()", | ||
962 | source_range: 70..70, | ||
963 | delete: 70..70, | ||
964 | insert: "main()$0", | ||
965 | kind: SymbolKind( | ||
966 | Function, | ||
967 | ), | ||
968 | lookup: "main", | ||
969 | detail: "-> ()", | ||
970 | }, | ||
971 | CompletionItem { | ||
972 | label: "s", | ||
973 | source_range: 70..70, | ||
974 | delete: 70..70, | ||
975 | insert: "s", | ||
976 | kind: SymbolKind( | ||
977 | Local, | ||
978 | ), | ||
979 | detail: "S", | ||
980 | relevance: CompletionRelevance { | ||
981 | exact_name_match: true, | ||
982 | exact_type_match: false, | ||
983 | }, | ||
984 | ref_match: "&mut ", | ||
985 | }, | ||
986 | ] | ||
987 | "#]], | ||
988 | ) | ||
989 | } | ||
945 | } | 990 | } |