1use comemo::Track;
2use smallvec::smallvec;
3use typst_library::diag::SourceResult;
4use typst_library::engine::Engine;
5use typst_library::foundations::{Content, Context, Depth, Packed, StyleChain};
6use typst_library::introspection::Locator;
7use typst_library::layout::grid::resolve::{Cell, CellGrid};
8use typst_library::layout::{Axes, Fragment, HAlignment, Regions, Sizing, VAlignment};
9use typst_library::model::{EnumElem, ListElem, Numbering, ParElem, ParbreakElem};
10use typst_library::text::TextElem;
11
12use crate::grid::GridLayouter;
13
14#[typst_macros::time(span = elem.span())]
16pub fn layout_list(
17 elem: &Packed<ListElem>,
18 engine: &mut Engine,
19 locator: Locator,
20 styles: StyleChain,
21 regions: Regions,
22) -> SourceResult<Fragment> {
23 let indent = elem.indent(styles);
24 let body_indent = elem.body_indent(styles);
25 let tight = elem.tight(styles);
26 let gutter = elem.spacing(styles).unwrap_or_else(|| {
27 if tight {
28 ParElem::leading_in(styles).into()
29 } else {
30 ParElem::spacing_in(styles).into()
31 }
32 });
33
34 let Depth(depth) = ListElem::depth_in(styles);
35 let marker = elem
36 .marker(styles)
37 .resolve(engine, styles, depth)?
38 .aligned(HAlignment::Start + VAlignment::Top);
40
41 let mut cells = vec![];
42 let mut locator = locator.split();
43
44 for item in &elem.children {
45 let mut body = item.body.clone();
47 if !tight {
48 body += ParbreakElem::shared();
49 }
50
51 cells.push(Cell::new(Content::empty(), locator.next(&())));
52 cells.push(Cell::new(marker.clone(), locator.next(&marker.span())));
53 cells.push(Cell::new(Content::empty(), locator.next(&())));
54 cells.push(Cell::new(
55 body.styled(ListElem::set_depth(Depth(1))),
56 locator.next(&item.body.span()),
57 ));
58 }
59
60 let grid = CellGrid::new(
61 Axes::with_x(&[
62 Sizing::Rel(indent.into()),
63 Sizing::Auto,
64 Sizing::Rel(body_indent.into()),
65 Sizing::Auto,
66 ]),
67 Axes::with_y(&[gutter.into()]),
68 cells,
69 );
70 let layouter = GridLayouter::new(&grid, regions, styles, elem.span());
71
72 layouter.layout(engine)
73}
74
75#[typst_macros::time(span = elem.span())]
77pub fn layout_enum(
78 elem: &Packed<EnumElem>,
79 engine: &mut Engine,
80 locator: Locator,
81 styles: StyleChain,
82 regions: Regions,
83) -> SourceResult<Fragment> {
84 let numbering = elem.numbering(styles);
85 let reversed = elem.reversed(styles);
86 let indent = elem.indent(styles);
87 let body_indent = elem.body_indent(styles);
88 let tight = elem.tight(styles);
89 let gutter = elem.spacing(styles).unwrap_or_else(|| {
90 if tight {
91 ParElem::leading_in(styles).into()
92 } else {
93 ParElem::spacing_in(styles).into()
94 }
95 });
96
97 let mut cells = vec![];
98 let mut locator = locator.split();
99 let mut number =
100 elem.start(styles)
101 .unwrap_or_else(|| if reversed { elem.children.len() } else { 1 });
102 let mut parents = EnumElem::parents_in(styles);
103
104 let full = elem.full(styles);
105
106 let number_align = elem.number_align(styles);
111
112 for item in &elem.children {
113 number = item.number(styles).unwrap_or(number);
114
115 let context = Context::new(None, Some(styles));
116 let resolved = if full {
117 parents.push(number);
118 let content = numbering.apply(engine, context.track(), &parents)?.display();
119 parents.pop();
120 content
121 } else {
122 match numbering {
123 Numbering::Pattern(pattern) => {
124 TextElem::packed(pattern.apply_kth(parents.len(), number))
125 }
126 other => other.apply(engine, context.track(), &[number])?.display(),
127 }
128 };
129
130 let resolved =
133 resolved.aligned(number_align).styled(TextElem::set_overhang(false));
134
135 let mut body = item.body.clone();
137 if !tight {
138 body += ParbreakElem::shared();
139 }
140
141 cells.push(Cell::new(Content::empty(), locator.next(&())));
142 cells.push(Cell::new(resolved, locator.next(&())));
143 cells.push(Cell::new(Content::empty(), locator.next(&())));
144 cells.push(Cell::new(
145 body.styled(EnumElem::set_parents(smallvec![number])),
146 locator.next(&item.body.span()),
147 ));
148 number =
149 if reversed { number.saturating_sub(1) } else { number.saturating_add(1) };
150 }
151
152 let grid = CellGrid::new(
153 Axes::with_x(&[
154 Sizing::Rel(indent.into()),
155 Sizing::Auto,
156 Sizing::Rel(body_indent.into()),
157 Sizing::Auto,
158 ]),
159 Axes::with_y(&[gutter.into()]),
160 cells,
161 );
162 let layouter = GridLayouter::new(&grid, regions, styles, elem.span());
163
164 layouter.layout(engine)
165}