diff options
Diffstat (limited to 'crates/ra_ide_api/src/completion/presentation.rs')
-rw-r--r-- | crates/ra_ide_api/src/completion/presentation.rs | 389 |
1 files changed, 288 insertions, 101 deletions
diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs index 48028a2f9..aed4ce6d4 100644 --- a/crates/ra_ide_api/src/completion/presentation.rs +++ b/crates/ra_ide_api/src/completion/presentation.rs | |||
@@ -1,12 +1,12 @@ | |||
1 | //! This modules takes care of rendering various definitions as completion items. | 1 | //! This modules takes care of rendering various definitions as completion items. |
2 | 2 | ||
3 | use hir::{Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk}; | 3 | use hir::{db::HirDatabase, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk}; |
4 | use join_to_string::join; | 4 | use join_to_string::join; |
5 | use ra_syntax::ast::NameOwner; | 5 | use ra_syntax::ast::NameOwner; |
6 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
7 | 7 | ||
8 | use crate::completion::{ | 8 | use crate::completion::{ |
9 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, | 9 | db, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::display::{const_label, function_label, macro_label, type_label}; | 12 | use crate::display::{const_label, function_label, macro_label, type_label}; |
@@ -44,54 +44,56 @@ impl Completions { | |||
44 | ) { | 44 | ) { |
45 | use hir::ModuleDef::*; | 45 | use hir::ModuleDef::*; |
46 | 46 | ||
47 | let mut completion_kind = CompletionKind::Reference; | 47 | let completion_kind = match resolution { |
48 | let (kind, docs) = match resolution { | 48 | ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType, |
49 | ScopeDef::ModuleDef(Module(it)) => (CompletionItemKind::Module, it.docs(ctx.db)), | 49 | _ => CompletionKind::Reference, |
50 | }; | ||
51 | |||
52 | let kind = match resolution { | ||
53 | ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module, | ||
50 | ScopeDef::ModuleDef(Function(func)) => { | 54 | ScopeDef::ModuleDef(Function(func)) => { |
51 | return self.add_function_with_name(ctx, Some(local_name), *func); | 55 | return self.add_function_with_name(ctx, Some(local_name), *func); |
52 | } | 56 | } |
53 | ScopeDef::ModuleDef(Adt(hir::Adt::Struct(it))) => { | 57 | ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct, |
54 | (CompletionItemKind::Struct, it.docs(ctx.db)) | 58 | // FIXME: add CompletionItemKind::Union |
55 | } | 59 | ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct, |
56 | ScopeDef::ModuleDef(Adt(hir::Adt::Union(it))) => { | 60 | ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum, |
57 | (CompletionItemKind::Struct, it.docs(ctx.db)) | 61 | |
58 | } | 62 | ScopeDef::ModuleDef(EnumVariant(..)) => CompletionItemKind::EnumVariant, |
59 | ScopeDef::ModuleDef(Adt(hir::Adt::Enum(it))) => { | 63 | ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const, |
60 | (CompletionItemKind::Enum, it.docs(ctx.db)) | 64 | ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static, |
61 | } | 65 | ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait, |
62 | ScopeDef::ModuleDef(EnumVariant(it)) => { | 66 | ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias, |
63 | (CompletionItemKind::EnumVariant, it.docs(ctx.db)) | 67 | ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, |
64 | } | 68 | ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam, |
65 | ScopeDef::ModuleDef(Const(it)) => (CompletionItemKind::Const, it.docs(ctx.db)), | 69 | ScopeDef::LocalBinding(..) => CompletionItemKind::Binding, |
66 | ScopeDef::ModuleDef(Static(it)) => (CompletionItemKind::Static, it.docs(ctx.db)), | 70 | // (does this need its own kind?) |
67 | ScopeDef::ModuleDef(Trait(it)) => (CompletionItemKind::Trait, it.docs(ctx.db)), | 71 | ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam, |
68 | ScopeDef::ModuleDef(TypeAlias(it)) => (CompletionItemKind::TypeAlias, it.docs(ctx.db)), | ||
69 | ScopeDef::ModuleDef(BuiltinType(..)) => { | ||
70 | completion_kind = CompletionKind::BuiltinType; | ||
71 | (CompletionItemKind::BuiltinType, None) | ||
72 | } | ||
73 | ScopeDef::GenericParam(..) => (CompletionItemKind::TypeParam, None), | ||
74 | ScopeDef::LocalBinding(..) => (CompletionItemKind::Binding, None), | ||
75 | ScopeDef::SelfType(..) => ( | ||
76 | CompletionItemKind::TypeParam, // (does this need its own kind?) | ||
77 | None, | ||
78 | ), | ||
79 | ScopeDef::MacroDef(mac) => { | 72 | ScopeDef::MacroDef(mac) => { |
80 | self.add_macro(ctx, Some(local_name), *mac); | 73 | return self.add_macro(ctx, Some(local_name), *mac); |
81 | return; | ||
82 | } | 74 | } |
83 | ScopeDef::Unknown => { | 75 | ScopeDef::Unknown => { |
84 | self.add(CompletionItem::new( | 76 | return self.add(CompletionItem::new( |
85 | CompletionKind::Reference, | 77 | CompletionKind::Reference, |
86 | ctx.source_range(), | 78 | ctx.source_range(), |
87 | local_name, | 79 | local_name, |
88 | )); | 80 | )); |
89 | return; | ||
90 | } | 81 | } |
91 | }; | 82 | }; |
92 | 83 | ||
84 | let docs = match resolution { | ||
85 | ScopeDef::ModuleDef(Module(it)) => it.docs(ctx.db), | ||
86 | ScopeDef::ModuleDef(Adt(it)) => it.docs(ctx.db), | ||
87 | ScopeDef::ModuleDef(EnumVariant(it)) => it.docs(ctx.db), | ||
88 | ScopeDef::ModuleDef(Const(it)) => it.docs(ctx.db), | ||
89 | ScopeDef::ModuleDef(Static(it)) => it.docs(ctx.db), | ||
90 | ScopeDef::ModuleDef(Trait(it)) => it.docs(ctx.db), | ||
91 | ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(ctx.db), | ||
92 | _ => None, | ||
93 | }; | ||
94 | |||
93 | let mut completion_item = | 95 | let mut completion_item = |
94 | CompletionItem::new(completion_kind, ctx.source_range(), local_name); | 96 | CompletionItem::new(completion_kind, ctx.source_range(), local_name.clone()); |
95 | if let ScopeDef::LocalBinding(pat_id) = resolution { | 97 | if let ScopeDef::LocalBinding(pat_id) = resolution { |
96 | let ty = ctx | 98 | let ty = ctx |
97 | .analyzer | 99 | .analyzer |
@@ -100,6 +102,28 @@ impl Completions { | |||
100 | .map(|t| t.display(ctx.db).to_string()); | 102 | .map(|t| t.display(ctx.db).to_string()); |
101 | completion_item = completion_item.set_detail(ty); | 103 | completion_item = completion_item.set_detail(ty); |
102 | }; | 104 | }; |
105 | |||
106 | // If not an import, add parenthesis automatically. | ||
107 | if ctx.is_path_type | ||
108 | && !ctx.has_type_args | ||
109 | && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis") | ||
110 | { | ||
111 | let generic_def: Option<hir::GenericDef> = match resolution { | ||
112 | ScopeDef::ModuleDef(Adt(it)) => Some((*it).into()), | ||
113 | ScopeDef::ModuleDef(TypeAlias(it)) => Some((*it).into()), | ||
114 | _ => None, | ||
115 | }; | ||
116 | if let Some(def) = generic_def { | ||
117 | if has_non_default_type_params(def, ctx.db) { | ||
118 | tested_by!(inserts_angle_brackets_for_generics); | ||
119 | completion_item = completion_item | ||
120 | .lookup_by(local_name.clone()) | ||
121 | .label(format!("{}<…>", local_name)) | ||
122 | .insert_snippet(format!("{}<$0>", local_name)); | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
103 | completion_item.kind(kind).set_documentation(docs).add_to(self) | 127 | completion_item.kind(kind).set_documentation(docs).add_to(self) |
104 | } | 128 | } |
105 | 129 | ||
@@ -107,6 +131,33 @@ impl Completions { | |||
107 | self.add_function_with_name(ctx, None, func) | 131 | self.add_function_with_name(ctx, None, func) |
108 | } | 132 | } |
109 | 133 | ||
134 | fn guess_macro_braces(&self, macro_name: &str, docs: &str) -> &'static str { | ||
135 | let mut votes = [0, 0, 0]; | ||
136 | for (idx, s) in docs.match_indices(¯o_name) { | ||
137 | let (before, after) = (&docs[..idx], &docs[idx + s.len()..]); | ||
138 | // Ensure to match the full word | ||
139 | if after.starts_with("!") | ||
140 | && before | ||
141 | .chars() | ||
142 | .rev() | ||
143 | .next() | ||
144 | .map_or(true, |c| c != '_' && !c.is_ascii_alphanumeric()) | ||
145 | { | ||
146 | // It may have spaces before the braces like `foo! {}` | ||
147 | match after[1..].chars().find(|&c| !c.is_whitespace()) { | ||
148 | Some('{') => votes[0] += 1, | ||
149 | Some('[') => votes[1] += 1, | ||
150 | Some('(') => votes[2] += 1, | ||
151 | _ => {} | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | // Insert a space before `{}`. | ||
157 | // We prefer the last one when some votes equal. | ||
158 | *votes.iter().zip(&[" {$0}", "[$0]", "($0)"]).max_by_key(|&(&vote, _)| vote).unwrap().1 | ||
159 | } | ||
160 | |||
110 | pub(crate) fn add_macro( | 161 | pub(crate) fn add_macro( |
111 | &mut self, | 162 | &mut self, |
112 | ctx: &CompletionContext, | 163 | ctx: &CompletionContext, |
@@ -117,10 +168,9 @@ impl Completions { | |||
117 | if let Some(name) = name { | 168 | if let Some(name) = name { |
118 | let detail = macro_label(&ast_node); | 169 | let detail = macro_label(&ast_node); |
119 | 170 | ||
120 | let macro_braces_to_insert = match name.as_str() { | 171 | let docs = macro_.docs(ctx.db); |
121 | "vec" => "[$0]", | 172 | let macro_braces_to_insert = |
122 | _ => "($0)", | 173 | self.guess_macro_braces(&name, docs.as_ref().map_or("", |s| s.as_str())); |
123 | }; | ||
124 | let macro_declaration = name + "!"; | 174 | let macro_declaration = name + "!"; |
125 | 175 | ||
126 | let builder = CompletionItem::new( | 176 | let builder = CompletionItem::new( |
@@ -129,7 +179,7 @@ impl Completions { | |||
129 | ¯o_declaration, | 179 | ¯o_declaration, |
130 | ) | 180 | ) |
131 | .kind(CompletionItemKind::Macro) | 181 | .kind(CompletionItemKind::Macro) |
132 | .set_documentation(macro_.docs(ctx.db)) | 182 | .set_documentation(docs) |
133 | .detail(detail) | 183 | .detail(detail) |
134 | .insert_snippet(macro_declaration + macro_braces_to_insert); | 184 | .insert_snippet(macro_declaration + macro_braces_to_insert); |
135 | 185 | ||
@@ -148,28 +198,31 @@ impl Completions { | |||
148 | let ast_node = func.source(ctx.db).ast; | 198 | let ast_node = func.source(ctx.db).ast; |
149 | let detail = function_label(&ast_node); | 199 | let detail = function_label(&ast_node); |
150 | 200 | ||
151 | let mut builder = CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name) | 201 | let mut builder = |
152 | .kind(if data.has_self_param() { | 202 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone()) |
153 | CompletionItemKind::Method | 203 | .kind(if data.has_self_param() { |
154 | } else { | 204 | CompletionItemKind::Method |
155 | CompletionItemKind::Function | 205 | } else { |
156 | }) | 206 | CompletionItemKind::Function |
157 | .set_documentation(func.docs(ctx.db)) | 207 | }) |
158 | .detail(detail); | 208 | .set_documentation(func.docs(ctx.db)) |
159 | // If not an import, add parenthesis automatically. | 209 | .detail(detail); |
210 | |||
211 | // Add `<>` for generic types | ||
160 | if ctx.use_item_syntax.is_none() | 212 | if ctx.use_item_syntax.is_none() |
161 | && !ctx.is_call | 213 | && !ctx.is_call |
162 | && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis") | 214 | && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis") |
163 | { | 215 | { |
164 | tested_by!(inserts_parens_for_function_calls); | 216 | tested_by!(inserts_parens_for_function_calls); |
165 | let snippet = | 217 | let (snippet, label) = |
166 | if data.params().is_empty() || data.has_self_param() && data.params().len() == 1 { | 218 | if data.params().is_empty() || data.has_self_param() && data.params().len() == 1 { |
167 | format!("{}()$0", data.name()) | 219 | (format!("{}()$0", data.name()), format!("{}()", name)) |
168 | } else { | 220 | } else { |
169 | format!("{}($0)", data.name()) | 221 | (format!("{}($0)", data.name()), format!("{}(…)", name)) |
170 | }; | 222 | }; |
171 | builder = builder.insert_snippet(snippet); | 223 | builder = builder.lookup_by(name.clone()).label(label).insert_snippet(snippet); |
172 | } | 224 | } |
225 | |||
173 | self.add(builder) | 226 | self.add(builder) |
174 | } | 227 | } |
175 | 228 | ||
@@ -213,7 +266,6 @@ impl Completions { | |||
213 | .separator(", ") | 266 | .separator(", ") |
214 | .surround_with("(", ")") | 267 | .surround_with("(", ")") |
215 | .to_string(); | 268 | .to_string(); |
216 | |||
217 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) | 269 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) |
218 | .kind(CompletionItemKind::EnumVariant) | 270 | .kind(CompletionItemKind::EnumVariant) |
219 | .set_documentation(variant.docs(ctx.db)) | 271 | .set_documentation(variant.docs(ctx.db)) |
@@ -222,6 +274,11 @@ impl Completions { | |||
222 | } | 274 | } |
223 | } | 275 | } |
224 | 276 | ||
277 | fn has_non_default_type_params(def: hir::GenericDef, db: &db::RootDatabase) -> bool { | ||
278 | let subst = db.generic_defaults(def); | ||
279 | subst.iter().any(|ty| ty == &Ty::Unknown) | ||
280 | } | ||
281 | |||
225 | #[cfg(test)] | 282 | #[cfg(test)] |
226 | mod tests { | 283 | mod tests { |
227 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; | 284 | use crate::completion::{do_completion, CompletionItem, CompletionKind}; |
@@ -242,24 +299,28 @@ mod tests { | |||
242 | fn main() { no_<|> } | 299 | fn main() { no_<|> } |
243 | " | 300 | " |
244 | ), | 301 | ), |
245 | @r###"[ | 302 | @r###" |
246 | CompletionItem { | 303 | [ |
247 | label: "main", | 304 | CompletionItem { |
248 | source_range: [61; 64), | 305 | label: "main()", |
249 | delete: [61; 64), | 306 | source_range: [61; 64), |
250 | insert: "main()$0", | 307 | delete: [61; 64), |
251 | kind: Function, | 308 | insert: "main()$0", |
252 | detail: "fn main()", | 309 | kind: Function, |
253 | }, | 310 | lookup: "main", |
254 | CompletionItem { | 311 | detail: "fn main()", |
255 | label: "no_args", | 312 | }, |
256 | source_range: [61; 64), | 313 | CompletionItem { |
257 | delete: [61; 64), | 314 | label: "no_args()", |
258 | insert: "no_args()$0", | 315 | source_range: [61; 64), |
259 | kind: Function, | 316 | delete: [61; 64), |
260 | detail: "fn no_args()", | 317 | insert: "no_args()$0", |
261 | }, | 318 | kind: Function, |
262 | ]"### | 319 | lookup: "no_args", |
320 | detail: "fn no_args()", | ||
321 | }, | ||
322 | ] | ||
323 | "### | ||
263 | ); | 324 | ); |
264 | assert_debug_snapshot!( | 325 | assert_debug_snapshot!( |
265 | do_reference_completion( | 326 | do_reference_completion( |
@@ -268,24 +329,28 @@ mod tests { | |||
268 | fn main() { with_<|> } | 329 | fn main() { with_<|> } |
269 | " | 330 | " |
270 | ), | 331 | ), |
271 | @r###"[ | 332 | @r###" |
272 | CompletionItem { | 333 | [ |
273 | label: "main", | 334 | CompletionItem { |
274 | source_range: [80; 85), | 335 | label: "main()", |
275 | delete: [80; 85), | 336 | source_range: [80; 85), |
276 | insert: "main()$0", | 337 | delete: [80; 85), |
277 | kind: Function, | 338 | insert: "main()$0", |
278 | detail: "fn main()", | 339 | kind: Function, |
279 | }, | 340 | lookup: "main", |
280 | CompletionItem { | 341 | detail: "fn main()", |
281 | label: "with_args", | 342 | }, |
282 | source_range: [80; 85), | 343 | CompletionItem { |
283 | delete: [80; 85), | 344 | label: "with_args(…)", |
284 | insert: "with_args($0)", | 345 | source_range: [80; 85), |
285 | kind: Function, | 346 | delete: [80; 85), |
286 | detail: "fn with_args(x: i32, y: String)", | 347 | insert: "with_args($0)", |
287 | }, | 348 | kind: Function, |
288 | ]"### | 349 | lookup: "with_args", |
350 | detail: "fn with_args(x: i32, y: String)", | ||
351 | }, | ||
352 | ] | ||
353 | "### | ||
289 | ); | 354 | ); |
290 | assert_debug_snapshot!( | 355 | assert_debug_snapshot!( |
291 | do_reference_completion( | 356 | do_reference_completion( |
@@ -299,16 +364,19 @@ mod tests { | |||
299 | } | 364 | } |
300 | " | 365 | " |
301 | ), | 366 | ), |
302 | @r###"[ | 367 | @r###" |
303 | CompletionItem { | 368 | [ |
304 | label: "foo", | 369 | CompletionItem { |
305 | source_range: [163; 164), | 370 | label: "foo()", |
306 | delete: [163; 164), | 371 | source_range: [163; 164), |
307 | insert: "foo()$0", | 372 | delete: [163; 164), |
308 | kind: Method, | 373 | insert: "foo()$0", |
309 | detail: "fn foo(&self)", | 374 | kind: Method, |
310 | }, | 375 | lookup: "foo", |
311 | ]"### | 376 | detail: "fn foo(&self)", |
377 | }, | ||
378 | ] | ||
379 | "### | ||
312 | ); | 380 | ); |
313 | } | 381 | } |
314 | 382 | ||
@@ -389,4 +457,123 @@ mod tests { | |||
389 | ]"# | 457 | ]"# |
390 | ); | 458 | ); |
391 | } | 459 | } |
460 | |||
461 | #[test] | ||
462 | fn inserts_angle_brackets_for_generics() { | ||
463 | covers!(inserts_angle_brackets_for_generics); | ||
464 | assert_debug_snapshot!( | ||
465 | do_reference_completion( | ||
466 | r" | ||
467 | struct Vec<T> {} | ||
468 | fn foo(xs: Ve<|>) | ||
469 | " | ||
470 | ), | ||
471 | @r###" | ||
472 | [ | ||
473 | CompletionItem { | ||
474 | label: "Vec<…>", | ||
475 | source_range: [61; 63), | ||
476 | delete: [61; 63), | ||
477 | insert: "Vec<$0>", | ||
478 | kind: Struct, | ||
479 | lookup: "Vec", | ||
480 | }, | ||
481 | CompletionItem { | ||
482 | label: "foo(…)", | ||
483 | source_range: [61; 63), | ||
484 | delete: [61; 63), | ||
485 | insert: "foo($0)", | ||
486 | kind: Function, | ||
487 | lookup: "foo", | ||
488 | detail: "fn foo(xs: Ve)", | ||
489 | }, | ||
490 | ] | ||
491 | "### | ||
492 | ); | ||
493 | assert_debug_snapshot!( | ||
494 | do_reference_completion( | ||
495 | r" | ||
496 | type Vec<T> = (T,); | ||
497 | fn foo(xs: Ve<|>) | ||
498 | " | ||
499 | ), | ||
500 | @r###" | ||
501 | [ | ||
502 | CompletionItem { | ||
503 | label: "Vec<…>", | ||
504 | source_range: [64; 66), | ||
505 | delete: [64; 66), | ||
506 | insert: "Vec<$0>", | ||
507 | kind: TypeAlias, | ||
508 | lookup: "Vec", | ||
509 | }, | ||
510 | CompletionItem { | ||
511 | label: "foo(…)", | ||
512 | source_range: [64; 66), | ||
513 | delete: [64; 66), | ||
514 | insert: "foo($0)", | ||
515 | kind: Function, | ||
516 | lookup: "foo", | ||
517 | detail: "fn foo(xs: Ve)", | ||
518 | }, | ||
519 | ] | ||
520 | "### | ||
521 | ); | ||
522 | assert_debug_snapshot!( | ||
523 | do_reference_completion( | ||
524 | r" | ||
525 | struct Vec<T = i128> {} | ||
526 | fn foo(xs: Ve<|>) | ||
527 | " | ||
528 | ), | ||
529 | @r###" | ||
530 | [ | ||
531 | CompletionItem { | ||
532 | label: "Vec", | ||
533 | source_range: [68; 70), | ||
534 | delete: [68; 70), | ||
535 | insert: "Vec", | ||
536 | kind: Struct, | ||
537 | }, | ||
538 | CompletionItem { | ||
539 | label: "foo(…)", | ||
540 | source_range: [68; 70), | ||
541 | delete: [68; 70), | ||
542 | insert: "foo($0)", | ||
543 | kind: Function, | ||
544 | lookup: "foo", | ||
545 | detail: "fn foo(xs: Ve)", | ||
546 | }, | ||
547 | ] | ||
548 | "### | ||
549 | ); | ||
550 | assert_debug_snapshot!( | ||
551 | do_reference_completion( | ||
552 | r" | ||
553 | struct Vec<T> {} | ||
554 | fn foo(xs: Ve<|><i128>) | ||
555 | " | ||
556 | ), | ||
557 | @r###" | ||
558 | [ | ||
559 | CompletionItem { | ||
560 | label: "Vec", | ||
561 | source_range: [61; 63), | ||
562 | delete: [61; 63), | ||
563 | insert: "Vec", | ||
564 | kind: Struct, | ||
565 | }, | ||
566 | CompletionItem { | ||
567 | label: "foo(…)", | ||
568 | source_range: [61; 63), | ||
569 | delete: [61; 63), | ||
570 | insert: "foo($0)", | ||
571 | kind: Function, | ||
572 | lookup: "foo", | ||
573 | detail: "fn foo(xs: Ve<i128>)", | ||
574 | }, | ||
575 | ] | ||
576 | "### | ||
577 | ); | ||
578 | } | ||
392 | } | 579 | } |