1use std::fmt::{Display, Result, Write};
5
6use itertools::{Either, Itertools};
7
8use crate::expression_tree::MinMaxOp;
9use crate::langtype::{StructName, Type};
10use crate::layout::Orientation;
11
12use super::{
13 Animation, CompilationUnit, EvaluationContext, Expression, LocalMemberIndex,
14 LocalMemberReference, MemberReference, ParentScope, SubComponentIdx,
15};
16
17pub fn pretty_print(root: &CompilationUnit, writer: &mut dyn Write) -> Result {
18 PrettyPrinter { writer, indentation: 0 }.print_root(root)
19}
20
21struct DisplayType<'a>(&'a Type);
24impl Display for DisplayType<'_> {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result {
26 match self.0 {
27 Type::Struct(s) => match &s.name {
28 StructName::Builtin(b) if b.slint_name().is_none() => {
29 write!(f, "{}", <&str>::from(b))
30 }
31 _ => write!(f, "{}", self.0),
32 },
33 _ => write!(f, "{}", self.0),
34 }
35 }
36}
37
38struct PrettyPrinter<'a> {
39 writer: &'a mut dyn Write,
40 indentation: usize,
41}
42
43impl PrettyPrinter<'_> {
44 fn print_root(&mut self, root: &CompilationUnit) -> Result {
45 for (idx, g) in root.globals.iter_enumerated() {
46 if !g.is_builtin {
47 self.print_global(root, idx, g)?;
48 }
49 }
50 for c in &root.used_sub_components {
53 self.print_component(root, *c, None)?
54 }
55 for p in &root.public_components {
56 self.print_component(root, p.item_tree.root, None)?
57 }
58 if let Some(p) = &root.popup_menu {
59 self.print_component(root, p.item_tree.root, None)?
60 }
61
62 Ok(())
63 }
64
65 fn print_component(
66 &mut self,
67 root: &CompilationUnit,
68 sc_idx: SubComponentIdx,
69 parent: Option<&ParentScope<'_>>,
70 ) -> Result {
71 let ctx = EvaluationContext::new_sub_component(root, sc_idx, (), parent);
72 let sc = &root.sub_components[sc_idx];
73 writeln!(self.writer, "component {} {{", sc.name)?;
74 self.indentation += 1;
75 for p in &sc.properties {
76 self.indent()?;
77 writeln!(
78 self.writer,
79 "property <{}> {}; //use={}",
80 DisplayType(&p.ty),
81 p.name,
82 p.use_count.get()
83 )?;
84 }
85 for c in &sc.callbacks {
86 self.indent()?;
87 writeln!(
88 self.writer,
89 "callback {} ({}) -> {};",
90 c.name,
91 c.args.iter().map(|t| DisplayType(t).to_string()).join(", "),
92 DisplayType(&c.ret_ty),
93 )?;
94 }
95 for f in &sc.functions {
96 self.indent()?;
97 writeln!(
98 self.writer,
99 "function {} ({}) -> {} {{ {} }}; ",
100 f.name,
101 f.args.iter().map(|t| DisplayType(t).to_string()).join(", "),
102 DisplayType(&f.ret_ty),
103 DisplayExpression(&f.code.borrow(), &ctx)
104 )?;
105 }
106 for twb in &sc.two_way_bindings {
107 self.indent()?;
108 writeln!(
109 self.writer,
110 "{} <=> {}{}{};",
111 DisplayLocalRef(&twb.prop1, &ctx),
112 DisplayPropertyRef(&twb.prop2, &ctx),
113 if twb.field_access.is_empty() { "" } else { "." },
114 twb.field_access.join(".")
115 )?
116 }
117 for (p, init) in &sc.property_init {
118 self.indent()?;
119 write!(
120 self.writer,
121 "{}: {}",
122 DisplayPropertyRef(p, &ctx),
123 DisplayExpression(&init.expression.borrow(), &ctx)
124 )?;
125 match &init.animation {
126 Some(Animation::Static(a)) => {
127 write!(self.writer, " animate {}", DisplayExpression(a, &ctx))?
128 }
129 Some(Animation::Transition(a)) => {
130 write!(self.writer, " animate transition {}", DisplayExpression(a, &ctx))?
131 }
132 None => {}
133 }
134 writeln!(
135 self.writer,
136 ";{}",
137 if init.kind == super::BindingKind::Constant { " /*const*/" } else { "" }
138 )?
139 }
140 for (p, a) in &sc.animations {
141 self.indent()?;
142 writeln!(
143 self.writer,
144 "animate {} {{ {} }};",
145 DisplayLocalRef(p, &ctx),
146 DisplayExpression(a, &ctx)
147 )?
148 }
149 for (p, e) in &sc.change_callbacks {
150 self.indent()?;
151 writeln!(
152 self.writer,
153 "changed {} => {};",
154 DisplayPropertyRef(p, &ctx),
155 DisplayExpression(&e.borrow(), &ctx),
156 )?
157 }
158 for e in &sc.pre_init_code {
159 self.indent()?;
160 writeln!(self.writer, "pre-init => {};", DisplayExpression(&e.borrow(), &ctx))?
161 }
162 for e in &sc.init_code {
163 self.indent()?;
164 writeln!(self.writer, "init => {};", DisplayExpression(&e.borrow(), &ctx))?
165 }
166 for (name, e) in
167 [("layout-info-h", &sc.layout_info_h), ("layout-info-v", &sc.layout_info_v)]
168 {
169 self.indent()?;
170 writeln!(self.writer, "{}: {};", name, DisplayExpression(&e.borrow(), &ctx))?
171 }
172 if let Some(e) = &sc.grid_layout_input_for_repeated {
173 self.indent()?;
174 writeln!(
175 self.writer,
176 "grid-layout-input-for-repeated: {};",
177 DisplayExpression(&e.borrow(), &ctx)
178 )?
179 }
180 if let Some(e) = &sc.flexbox_layout_item_info_for_repeated {
181 self.indent()?;
182 writeln!(
183 self.writer,
184 "flexbox-layout-item-info-for-repeated: {};",
185 DisplayExpression(&e.borrow(), &ctx)
186 )?
187 }
188 if let Some((cross_o, e)) = &sc.cross_axis_self_alignment_for_repeated {
189 self.indent()?;
190 writeln!(
191 self.writer,
192 "cross-axis-self-alignment-for-repeated ({cross_o:?}): {};",
193 DisplayExpression(&e.borrow(), &ctx)
194 )?
195 }
196 if let Some((main_o, e)) = &sc.layout_order_for_repeated {
197 self.indent()?;
198 writeln!(
199 self.writer,
200 "layout-order-for-repeated ({main_o:?}): {};",
201 DisplayExpression(&e.borrow(), &ctx)
202 )?
203 }
204 for (i, c) in sc.grid_layout_children.iter_enumerated() {
205 self.indent()?;
206 writeln!(
207 self.writer,
208 "grid-layout-child[{}] {{ h: {}; v: {} }};",
209 usize::from(i),
210 DisplayExpression(&c.layout_info_h.borrow(), &ctx),
211 DisplayExpression(&c.layout_info_v.borrow(), &ctx)
212 )?
213 }
214 for t in &sc.timers {
215 self.indent()?;
216 writeln!(
217 self.writer,
218 "timer {{ interval: {}; running: {}; triggered => {} }};",
219 DisplayExpression(&t.interval.borrow(), &ctx),
220 DisplayExpression(&t.running.borrow(), &ctx),
221 DisplayExpression(&t.triggered.borrow(), &ctx)
222 )?
223 }
224 for ssc in &sc.sub_components {
225 self.indent()?;
226 writeln!(self.writer, "{} := {} {{}};", ssc.name, root.sub_components[ssc.ty].name)?;
227 }
228 for (item, geom) in std::iter::zip(&sc.items, &sc.geometries) {
229 self.indent()?;
230 let geometry = geom.as_ref().map_or(String::new(), |geom| {
231 format!("geometry: {}", DisplayExpression(&geom.borrow(), &ctx))
232 });
233 writeln!(self.writer, "{} := {} {{ {geometry} }};", item.name, item.ty.class_name)?;
234 }
235 for ((item_index, prop), e) in &sc.accessible_prop {
236 self.indent()?;
237 writeln!(
238 self.writer,
239 "{}.accessible-{}: {};",
240 item_name_in_tree(root, sc, *item_index)
241 .unwrap_or_else(|| format!("@{item_index}")),
242 crate::generator::to_kebab_case(prop),
243 DisplayExpression(&e.borrow(), &ctx)
244 )?
245 }
246 for (idx, r) in sc.repeated.iter_enumerated() {
247 self.indent()?;
248 write!(
249 self.writer,
250 "{} {} : /*@repeater({})*/ ",
251 if r.index_prop.is_none() && r.data_prop.is_none() { "if" } else { "for in" },
252 DisplayExpression(&r.model.borrow(), &ctx),
253 usize::from(idx)
254 )?;
255 self.print_component(root, r.sub_tree.root, Some(&ParentScope::new(&ctx, Some(idx))))?
256 }
257 for (i, t) in sc.menu_item_trees.iter().enumerate() {
258 self.indent()?;
259 write!(self.writer, "menu : /*@menu({i})*/ ")?;
260 self.print_component(root, t.root, Some(&ParentScope::new(&ctx, None)))?
261 }
262 for (i, w) in sc.popup_windows.iter().enumerate() {
263 self.indent()?;
264 let parent = ParentScope::new(&ctx, None);
265 let popup_ctx =
267 EvaluationContext::new_sub_component(root, w.item_tree.root, (), Some(&parent));
268 write!(
269 self.writer,
270 "{} at {} : /*@popup({i})*/ ",
271 if w.is_tooltip { "tooltip" } else { "popup" },
272 DisplayExpression(&w.position.borrow(), &popup_ctx)
273 )?;
274 self.print_component(root, w.item_tree.root, Some(&parent))?
275 }
276 self.indentation -= 1;
277 self.indent()?;
278 writeln!(self.writer, "}}")
279 }
280
281 fn print_global(
282 &mut self,
283 root: &CompilationUnit,
284 idx: super::GlobalIdx,
285 global: &super::GlobalComponent,
286 ) -> Result {
287 let ctx = EvaluationContext::new_global(root, idx, ());
288 if global.exported {
289 write!(self.writer, "export ")?;
290 }
291 let aliases = global.aliases.join(",");
292 let aliases = if aliases.is_empty() { String::new() } else { format!(" /*{aliases}*/") };
293 let emission = if global.from_library {
294 " /*from library*/"
295 } else if !global.must_generate() {
296 " /*not generated*/"
297 } else {
298 ""
299 };
300 writeln!(self.writer, "global {} {{{aliases}{emission}", global.name)?;
301 self.indentation += 1;
302 for (p, is_const) in std::iter::zip(&global.properties, &global.const_properties) {
303 self.indent()?;
304 writeln!(
305 self.writer,
306 "property <{}> {}; //use={}{}",
307 DisplayType(&p.ty),
308 p.name,
309 p.use_count.get(),
310 if *is_const { " const" } else { "" }
311 )?;
312 }
313 for c in &global.callbacks {
314 self.indent()?;
315 writeln!(
316 self.writer,
317 "callback {} ({}) -> {};",
318 c.name,
319 c.args.iter().map(|t| DisplayType(t).to_string()).join(", "),
320 DisplayType(&c.ret_ty),
321 )?;
322 }
323 for (p, init) in &global.init_values {
324 self.indent()?;
325 match p {
326 LocalMemberIndex::Property(p) => {
327 writeln!(
328 self.writer,
329 "{}: {}{};",
330 global.properties[*p].name,
331 DisplayExpression(&init.expression.borrow(), &ctx,),
332 if init.kind == super::BindingKind::Constant { "/*const*/" } else { "" }
333 )?;
334 }
335 LocalMemberIndex::Callback(c) => {
336 writeln!(
337 self.writer,
338 "{} => {};",
339 global.callbacks[*c].name,
340 DisplayExpression(&init.expression.borrow(), &ctx,),
341 )?;
342 }
343 _ => unreachable!(),
344 }
345 }
346
347 for (p, e) in &global.change_callbacks {
348 self.indent()?;
349 writeln!(
350 self.writer,
351 "changed {} => {};",
352 global.properties[*p].name,
353 DisplayExpression(&e.borrow(), &ctx),
354 )?
355 }
356 for f in &global.functions {
357 self.indent()?;
358 writeln!(
359 self.writer,
360 "function {} ({}) -> {} {{ {} }}; ",
361 f.name,
362 f.args.iter().map(ToString::to_string).join(", "),
363 f.ret_ty,
364 DisplayExpression(&f.code.borrow(), &ctx)
365 )?;
366 }
367 self.indentation -= 1;
368 self.indent()?;
369 writeln!(self.writer, "}}")
370 }
371
372 fn indent(&mut self) -> Result {
373 for _ in 0..self.indentation {
374 self.writer.write_str(" ")?;
375 }
376 Ok(())
377 }
378}
379
380fn item_name_in_tree(
383 root: &CompilationUnit,
384 sc: &super::SubComponent,
385 tree_index: u32,
386) -> Option<String> {
387 if let Some(item) = sc.items.iter().find(|i| i.index_in_tree == tree_index) {
388 return Some(item.name.to_string());
389 }
390 let ssc = sc.sub_components.iter().find(|s| s.index_in_tree == tree_index)?;
391 Some(format!("{}.{}", ssc.name, item_name_in_tree(root, &root.sub_components[ssc.ty], 0)?))
392}
393
394pub struct DisplayPropertyRef<'a, T>(pub &'a MemberReference, pub &'a EvaluationContext<'a, T>);
395impl<T> Display for DisplayPropertyRef<'_, T> {
396 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result {
397 let ctx = self.1;
398 match &self.0 {
399 MemberReference::Relative { parent_level, local_reference } => {
400 print_local_ref(f, ctx, local_reference, *parent_level)
401 }
402 MemberReference::Global { global_index, member } => {
403 let g = &ctx.compilation_unit.globals[*global_index];
404 match member {
405 LocalMemberIndex::Property(property_index) => {
406 write!(f, "{}.{}", g.name, g.properties[*property_index].name)
407 }
408 LocalMemberIndex::Callback(callback_index) => {
409 write!(f, "{}.{}", g.name, g.callbacks[*callback_index].name)
410 }
411 LocalMemberIndex::Function(function_index) => {
412 write!(f, "{}.{}", g.name, g.functions[*function_index].name)
413 }
414 _ => write!(f, "<invalid reference in global>"),
415 }
416 }
417 }
418 }
419}
420
421pub struct DisplayLocalRef<'a, T>(pub &'a LocalMemberReference, pub &'a EvaluationContext<'a, T>);
422impl<T> Display for DisplayLocalRef<'_, T> {
423 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result {
424 print_local_ref(f, self.1, self.0, 0)
425 }
426}
427
428fn print_local_ref<T>(
429 f: &mut std::fmt::Formatter<'_>,
430 ctx: &EvaluationContext<T>,
431 local_ref: &LocalMemberReference,
432 parent_level: usize,
433) -> Result {
434 if let Some(g) = ctx.current_global() {
435 match &local_ref.reference {
436 LocalMemberIndex::Property(property_index) => {
437 write!(f, "{}.{}", g.name, g.properties[*property_index].name)
438 }
439 LocalMemberIndex::Callback(callback_index) => {
440 write!(f, "{}.{}", g.name, g.callbacks[*callback_index].name)
441 }
442 LocalMemberIndex::Function(function_index) => {
443 write!(f, "{}.{}", g.name, g.functions[*function_index].name)
444 }
445 _ => write!(f, "<invalid reference in global>"),
446 }
447 } else {
448 let Some(s) = ctx.parent_sub_component_idx(parent_level) else {
449 return write!(f, "<invalid parent reference>");
450 };
451 let mut sc = &ctx.compilation_unit.sub_components[s];
452
453 for i in &local_ref.sub_component_path {
454 write!(f, "{}.", sc.sub_components[*i].name)?;
455 sc = &ctx.compilation_unit.sub_components[sc.sub_components[*i].ty];
456 }
457 match &local_ref.reference {
458 LocalMemberIndex::Property(property_index) => {
459 write!(f, "{}", sc.properties[*property_index].name)
460 }
461 LocalMemberIndex::Callback(callback_index) => {
462 write!(f, "{}", sc.callbacks[*callback_index].name)
463 }
464 LocalMemberIndex::Function(function_index) => {
465 write!(f, "{}", sc.functions[*function_index].name)
466 }
467 LocalMemberIndex::Native { item_index, prop_name, .. } => {
468 let i = &sc.items[*item_index];
469 write!(f, "{}.{}", i.name, prop_name)
470 }
471 LocalMemberIndex::Timer(timer_index) => {
472 write!(f, "timer#{}", usize::from(*timer_index))
473 }
474 }
475 }
476}
477
478pub struct DisplayExpression<'a, T>(pub &'a Expression, pub &'a EvaluationContext<'a, T>);
479impl<'a, T> Display for DisplayExpression<'a, T> {
480 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result {
481 let ctx = self.1;
482 let e = |e: &'a Expression| DisplayExpression(e, ctx);
483 match self.0 {
484 Expression::StringLiteral(x) => write!(f, "{x:?}"),
485 Expression::NumberLiteral(x) => write!(f, "{x:?}"),
486 Expression::BoolLiteral(x) => write!(f, "{x:?}"),
487 Expression::KeysLiteral(keys) => {
488 write!(f, "@keys({keys})",)
489 }
490 Expression::PropertyReference(x) => write!(f, "{}", DisplayPropertyRef(x, ctx)),
491 Expression::FunctionParameterReference { index } => write!(f, "arg_{index}"),
492 Expression::StoreLocalVariable { name, value } => {
493 write!(f, "{} = {}", name, e(value))
494 }
495 Expression::ReadLocalVariable { name, .. } => write!(f, "{name}"),
496 Expression::StructFieldAccess { base, name } => write!(f, "{}.{}", e(base), name),
497 Expression::ArrayIndex { array, index } => write!(f, "{}[{}]", e(array), e(index)),
498 Expression::Cast { from, to } => write!(f, "{} /*as {:?}*/", e(from), to),
499 Expression::CodeBlock(v) => {
500 write!(f, "{{ {} }}", v.iter().map(e).join("; "))
501 }
502 Expression::BuiltinFunctionCall { function, arguments, .. } => {
503 write!(f, "{:?}({})", function, arguments.iter().map(e).join(", "))
504 }
505 Expression::CallBackCall { callback, arguments } => {
506 write!(
507 f,
508 "{}({})",
509 DisplayPropertyRef(callback, ctx),
510 arguments.iter().map(e).join(", ")
511 )
512 }
513 Expression::FunctionCall { function, arguments } => {
514 write!(
515 f,
516 "{}({})",
517 DisplayPropertyRef(function, ctx),
518 arguments.iter().map(e).join(", ")
519 )
520 }
521 Expression::ItemMemberFunctionCall { function } => {
522 write!(f, "{}()", DisplayPropertyRef(function, ctx))
523 }
524 Expression::ExtraBuiltinFunctionCall { function, arguments, .. } => {
525 write!(f, "{}({})", function, arguments.iter().map(e).join(", "))
526 }
527 Expression::PropertyAssignment { property, value } => {
528 write!(f, "{} = {}", DisplayPropertyRef(property, ctx), e(value))
529 }
530 Expression::ModelDataAssignment { level, value } => {
531 write!(f, "data_{} = {}", level, e(value))
532 }
533 Expression::ArrayIndexAssignment { array, index, value } => {
534 write!(f, "{}[{}] = {}", e(array), e(index), e(value))
535 }
536 Expression::SliceIndexAssignment { slice_name, index, value } => {
537 write!(f, "{}[{}] = {}", slice_name, index, e(value))
538 }
539 Expression::BinaryExpression { lhs, rhs, op } => {
540 write!(f, "({} {} {})", e(lhs), op, e(rhs))
541 }
542 Expression::UnaryOp { sub, op } => write!(f, "{}{}", op, e(sub)),
543 Expression::ImageReference { resource_ref, nine_slice } => {
544 write!(f, "{resource_ref:?}")?;
545 if let Some(nine_slice) = &nine_slice {
546 write!(f, "nine-slice({nine_slice:?})")?;
547 }
548 Ok(())
549 }
550 Expression::Condition { condition, true_expr, false_expr } => {
551 write!(f, "({} ? {} : {})", e(condition), e(true_expr), e(false_expr))
552 }
553 Expression::Array { values, .. } => {
554 write!(f, "[{}]", values.iter().map(e).join(", "))
555 }
556 Expression::Struct { values, .. } => write!(
557 f,
558 "{{ {} }}",
559 values.iter().map(|(k, v)| format!("{}: {}", k, e(v))).join(", ")
560 ),
561 Expression::EasingCurve(x) => write!(f, "{x:?}"),
562 Expression::MouseCursor(x) => write!(f, "{x:?}"),
563 Expression::LinearGradient { angle, stops } => write!(
564 f,
565 "@linear-gradient({}, {})",
566 e(angle),
567 stops.iter().map(|(e1, e2)| format!("{} {}", e(e1), e(e2))).join(", ")
568 ),
569 Expression::RadialGradient { center, radius, stops } => {
570 let center_str = center
571 .as_ref()
572 .map(|(cx, cy)| format!(" at {} {}", e(cx), e(cy)))
573 .unwrap_or_default();
574 let radius_str = radius.as_ref().map(|r| format!(" {}", e(r))).unwrap_or_default();
575 write!(
576 f,
577 "@radial-gradient(circle{radius_str}{center_str}, {})",
578 stops.iter().map(|(e1, e2)| format!("{} {}", e(e1), e(e2))).join(", ")
579 )
580 }
581 Expression::ConicGradient { from_angle, center, stops } => {
582 let center_str = center
583 .as_ref()
584 .map(|(cx, cy)| format!(" at {} {}", e(cx), e(cy)))
585 .unwrap_or_default();
586 write!(
587 f,
588 "@conic-gradient(from {}{center_str}, {})",
589 e(from_angle),
590 stops.iter().map(|(e1, e2)| format!("{} {}", e(e1), e(e2))).join(", ")
591 )
592 }
593 Expression::EnumerationValue(x) => write!(f, "{x}"),
594 Expression::LayoutCacheAccess {
595 layout_cache_prop,
596 index,
597 repeater_index: None,
598 ..
599 } => {
600 write!(f, "{}[{}]", DisplayPropertyRef(layout_cache_prop, ctx), index)
601 }
602 Expression::LayoutCacheAccess {
603 layout_cache_prop,
604 index,
605 repeater_index: Some(ri),
606 entries_per_item,
607 } => {
608 write!(
609 f,
610 "{0}[{0}[{1}] + {2} * {3}]",
611 DisplayPropertyRef(layout_cache_prop, ctx),
612 index,
613 e(ri),
614 entries_per_item
615 )
616 }
617 Expression::GridRepeaterCacheAccess {
618 layout_cache_prop,
619 index,
620 repeater_index,
621 stride,
622 child_offset,
623 inner_repeater_index,
624 entries_per_item,
625 } => {
626 if let Some(inner_idx) = inner_repeater_index {
627 write!(
628 f,
629 "{0}[{0}[{1}] + {2} * {3} + {4} * {5} + {6}]",
630 DisplayPropertyRef(layout_cache_prop, ctx),
631 index,
632 e(repeater_index),
633 e(stride),
634 e(inner_idx),
635 entries_per_item,
636 child_offset
637 )
638 } else {
639 write!(
640 f,
641 "{0}[{0}[{1}] + {2} * {3} + {4}]",
642 DisplayPropertyRef(layout_cache_prop, ctx),
643 index,
644 e(repeater_index),
645 e(stride),
646 child_offset
647 )
648 }
649 }
650 Expression::WithLayoutItemInfo {
651 cells_variable,
652 repeater_indices_var_name,
653 repeater_steps_var_name,
654 elements,
655 orientation,
656 repeated_cross_size,
657 sub_expression,
658 } => {
659 write!(
660 f,
661 "{{ {} = [{}] /*{}*/; ",
662 cells_variable,
663 elements
664 .iter()
665 .map(|x| match x {
666 Either::Left(x) => e(x).to_string(),
667 Either::Right(r) => match &r.cross_width {
668 Some(w) => format!(
669 "@repeater({} at cross-width {})",
670 usize::from(r.repeater_index),
671 e(w)
672 ),
673 None => format!("@repeater({})", usize::from(r.repeater_index)),
674 },
675 })
676 .join(", "),
677 match orientation {
678 Orientation::Horizontal => "horizontal",
679 Orientation::Vertical => "vertical",
680 }
681 )?;
682 if let Some(v) = repeater_indices_var_name {
683 write!(f, "{v} = @repeater-indices; ")?;
684 }
685 if let Some(v) = repeater_steps_var_name {
686 write!(f, "{v} = @repeater-steps; ")?;
687 }
688 if let Some(s) = repeated_cross_size {
689 write!(f, "@repeated-cross-size = {}; ", e(s))?;
690 }
691 write!(f, "{} }}", e(sub_expression))
692 }
693 Expression::WithFlexboxLayoutItemInfo { .. } => {
694 write!(f, "WithFlexboxLayoutItemInfo(TODO)",)
695 }
696 Expression::BoxLayoutInfoOrthoWithMeasure { .. } => {
697 write!(f, "BoxLayoutInfoOrthoWithMeasure(TODO)",)
698 }
699 Expression::FlexboxLayoutInfoCrossAxisWithMeasure { .. } => {
700 write!(f, "FlexboxLayoutInfoCrossAxisWithMeasure(TODO)",)
701 }
702 Expression::SolveFlexboxLayoutWithMeasure { .. } => {
703 write!(f, "SolveFlexboxLayoutWithMeasure(TODO)",)
704 }
705 Expression::WithGridInputData { .. } => write!(f, "WithGridInputData(TODO)",),
706 Expression::MinMax { ty: _, op, lhs, rhs } => match op {
707 MinMaxOp::Min => write!(f, "min({}, {})", e(lhs), e(rhs)),
708 MinMaxOp::Max => write!(f, "max({}, {})", e(lhs), e(rhs)),
709 },
710 Expression::EmptyComponentFactory => write!(f, "<empty-component-factory>",),
711 Expression::EmptyDataTransfer => write!(f, "<empty-data-transfer>",),
712 Expression::TranslationReference { format_args, string_index, plural } => {
713 match plural {
714 Some(plural) => write!(
715 f,
716 "@tr({:?} % {}, {})",
717 string_index,
718 DisplayExpression(plural, ctx),
719 DisplayExpression(format_args, ctx)
720 ),
721 None => write!(
722 f,
723 "@tr({:?}, {})",
724 string_index,
725 DisplayExpression(format_args, ctx)
726 ),
727 }
728 }
729 Expression::Closure { arg_name, expression } => {
730 let display_name = arg_name.strip_prefix("local_").unwrap_or(arg_name);
731 write!(f, "({}) => {}", display_name, e(expression))
732 }
733 Expression::DebugHook { expression, id } => {
734 write!(f, "debug-hook({id:?}, {})", DisplayExpression(expression, ctx))
735 }
736 }
737 }
738}