Fallible layout

This commit is contained in:
Laurenz 2022-02-17 17:25:57 +01:00
parent c5e67af22b
commit 35610a8c6a
26 changed files with 174 additions and 140 deletions

View File

@ -4,12 +4,14 @@ use std::hash::{Hash, Hasher};
use std::sync::Arc; use std::sync::Arc;
use super::{StyleChain, Template}; use super::{StyleChain, Template};
use crate::diag::TypResult;
use crate::util::Prehashed; use crate::util::Prehashed;
use crate::Vm;
/// A node that can be realized given some styles. /// A node that can be realized given some styles.
pub trait Show { pub trait Show {
/// Realize the template in the given styles. /// Realize the template in the given styles.
fn show(&self, styles: StyleChain) -> Template; fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template>;
/// Convert to a packed show node. /// Convert to a packed show node.
fn pack(self) -> ShowNode fn pack(self) -> ShowNode
@ -40,8 +42,8 @@ impl ShowNode {
} }
impl Show for ShowNode { impl Show for ShowNode {
fn show(&self, styles: StyleChain) -> Template { fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template> {
self.0.show(styles) self.0.show(vm, styles)
} }
fn pack(self) -> ShowNode { fn pack(self) -> ShowNode {

View File

@ -165,20 +165,22 @@ impl Template {
} }
/// Layout this template into a collection of pages. /// Layout this template into a collection of pages.
pub fn layout(&self, vm: &mut Vm) -> Vec<Arc<Frame>> { pub fn layout(&self, vm: &mut Vm) -> TypResult<Vec<Arc<Frame>>> {
let style_arena = Arena::new(); let style_arena = Arena::new();
let template_arena = Arena::new(); let template_arena = Arena::new();
let mut builder = Builder::new(&style_arena, &template_arena, true); let mut builder = Builder::new(&style_arena, &template_arena, true);
let chain = StyleChain::new(vm.styles); let chain = StyleChain::new(vm.styles);
builder.process(self, chain); builder.process(self, vm, chain)?;
builder.finish_page(true, false, chain); builder.finish_page(true, false, chain);
let mut frames = vec![];
let (pages, shared) = builder.pages.unwrap().finish(); let (pages, shared) = builder.pages.unwrap().finish();
pages for (page, map) in pages.iter() {
.iter() frames.extend(page.layout(vm, map.chain(&shared))?);
.flat_map(|(page, map)| page.layout(vm, map.chain(&shared))) }
.collect()
Ok(frames)
} }
} }
@ -269,12 +271,12 @@ impl Layout for Template {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let style_arena = Arena::new(); let style_arena = Arena::new();
let template_arena = Arena::new(); let template_arena = Arena::new();
let mut builder = Builder::new(&style_arena, &template_arena, false); let mut builder = Builder::new(&style_arena, &template_arena, false);
builder.process(self, styles); builder.process(self, vm, styles)?;
builder.finish_par(styles); builder.finish_par(styles);
let (flow, shared) = builder.flow.finish(); let (flow, shared) = builder.flow.finish();
@ -323,7 +325,12 @@ impl<'a> Builder<'a> {
} }
/// Process a template. /// Process a template.
fn process(&mut self, template: &'a Template, styles: StyleChain<'a>) { fn process(
&mut self,
template: &'a Template,
vm: &mut Vm,
styles: StyleChain<'a>,
) -> TypResult<()> {
match template { match template {
Template::Space => { Template::Space => {
self.par.weak(ParChild::Text(' '.into()), 0, styles); self.par.weak(ParChild::Text(' '.into()), 0, styles);
@ -382,8 +389,9 @@ impl<'a> Builder<'a> {
} }
} }
Template::Show(node) => { Template::Show(node) => {
let template = self.template_arena.alloc(node.show(styles)); let template = node.show(vm, styles)?;
self.process(template, styles.unscoped(node.id())); let stored = self.template_arena.alloc(template);
self.process(stored, vm, styles.unscoped(node.id()))?;
} }
Template::Styled(styled) => { Template::Styled(styled) => {
let (sub, map) = styled.as_ref(); let (sub, map) = styled.as_ref();
@ -397,7 +405,7 @@ impl<'a> Builder<'a> {
None => {} None => {}
} }
self.process(sub, styles); self.process(sub, vm, styles)?;
match interruption { match interruption {
Some(Interruption::Page) => self.finish_page(true, false, styles), Some(Interruption::Page) => self.finish_page(true, false, styles),
@ -407,10 +415,12 @@ impl<'a> Builder<'a> {
} }
Template::Sequence(seq) => { Template::Sequence(seq) => {
for sub in seq.iter() { for sub in seq.iter() {
self.process(sub, styles); self.process(sub, vm, styles)?;
} }
} }
} }
Ok(())
} }
/// Finish the currently built paragraph. /// Finish the currently built paragraph.

View File

@ -15,6 +15,7 @@ use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::sync::Arc; use std::sync::Arc;
use crate::diag::TypResult;
use crate::eval::StyleChain; use crate::eval::StyleChain;
use crate::frame::{Element, Frame, Geometry, Shape, Stroke}; use crate::frame::{Element, Frame, Geometry, Shape, Stroke};
use crate::geom::{Align, Linear, Paint, Point, Sides, Size, Spec, Transform}; use crate::geom::{Align, Linear, Paint, Point, Sides, Size, Spec, Transform};
@ -33,7 +34,7 @@ pub trait Layout {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>>; ) -> TypResult<Vec<Constrained<Arc<Frame>>>>;
/// Convert to a packed node. /// Convert to a packed node.
fn pack(self) -> LayoutNode fn pack(self) -> LayoutNode
@ -137,7 +138,7 @@ impl Layout for LayoutNode {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let styles = styles.barred(self.id()); let styles = styles.barred(self.id());
#[cfg(not(feature = "layout-cache"))] #[cfg(not(feature = "layout-cache"))]
@ -155,10 +156,10 @@ impl Layout for LayoutNode {
// #[track_caller] annotation doesn't work. // #[track_caller] annotation doesn't work.
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
if let Some(frames) = vm.layout_cache.get(hash, regions) { if let Some(frames) = vm.layout_cache.get(hash, regions) {
frames Ok(frames)
} else { } else {
vm.level += 1; vm.level += 1;
let frames = self.0.layout(vm, regions, styles); let frames = self.0.layout(vm, regions, styles)?;
vm.level -= 1; vm.level -= 1;
let entry = FramesEntry::new(frames.clone(), vm.level); let entry = FramesEntry::new(frames.clone(), vm.level);
@ -175,7 +176,7 @@ impl Layout for LayoutNode {
} }
vm.layout_cache.insert(hash, entry); vm.layout_cache.insert(hash, entry);
frames Ok(frames)
} }
} }
@ -248,11 +249,11 @@ impl Layout for EmptyNode {
_: &mut Vm, _: &mut Vm,
regions: &Regions, regions: &Regions,
_: StyleChain, _: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let size = regions.expand.select(regions.current, Size::zero()); let size = regions.expand.select(regions.current, Size::zero());
let mut cts = Constraints::new(regions.expand); let mut cts = Constraints::new(regions.expand);
cts.exact = regions.current.filter(regions.expand); cts.exact = regions.current.filter(regions.expand);
vec![Frame::new(size).constrain(cts)] Ok(vec![Frame::new(size).constrain(cts)])
} }
} }
@ -271,7 +272,7 @@ impl Layout for SizedNode {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let is_auto = self.sizing.map_is_none(); let is_auto = self.sizing.map_is_none();
let is_rel = self.sizing.map(|s| s.map_or(false, Linear::is_relative)); let is_rel = self.sizing.map(|s| s.map_or(false, Linear::is_relative));
@ -292,7 +293,7 @@ impl Layout for SizedNode {
Regions::one(size, base, expand) Regions::one(size, base, expand)
}; };
let mut frames = self.child.layout(vm, &pod, styles); let mut frames = self.child.layout(vm, &pod, styles)?;
let Constrained { item: frame, cts } = &mut frames[0]; let Constrained { item: frame, cts } = &mut frames[0];
// Ensure frame size matches regions size if expansion is on. // Ensure frame size matches regions size if expansion is on.
@ -306,7 +307,7 @@ impl Layout for SizedNode {
cts.exact = regions.current.filter(regions.expand | is_auto); cts.exact = regions.current.filter(regions.expand | is_auto);
cts.base = regions.base.filter(is_rel | is_auto); cts.base = regions.base.filter(is_rel | is_auto);
frames Ok(frames)
} }
} }
@ -325,13 +326,13 @@ impl Layout for FillNode {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let mut frames = self.child.layout(vm, regions, styles); let mut frames = self.child.layout(vm, regions, styles)?;
for Constrained { item: frame, .. } in &mut frames { for Constrained { item: frame, .. } in &mut frames {
let shape = Shape::filled(Geometry::Rect(frame.size), self.fill); let shape = Shape::filled(Geometry::Rect(frame.size), self.fill);
Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape)); Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape));
} }
frames Ok(frames)
} }
} }
@ -350,12 +351,12 @@ impl Layout for StrokeNode {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let mut frames = self.child.layout(vm, regions, styles); let mut frames = self.child.layout(vm, regions, styles)?;
for Constrained { item: frame, .. } in &mut frames { for Constrained { item: frame, .. } in &mut frames {
let shape = Shape::stroked(Geometry::Rect(frame.size), self.stroke); let shape = Shape::stroked(Geometry::Rect(frame.size), self.stroke);
Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape)); Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape));
} }
frames Ok(frames)
} }
} }

View File

@ -283,9 +283,7 @@ impl<'a> Vm<'a> {
/// diagnostics in the form of a vector of error message with file and span /// diagnostics in the form of a vector of error message with file and span
/// information. /// information.
pub fn typeset(&mut self, id: SourceId) -> TypResult<Vec<Arc<Frame>>> { pub fn typeset(&mut self, id: SourceId) -> TypResult<Vec<Arc<Frame>>> {
let module = self.evaluate(id)?; self.evaluate(id)?.template.layout(self)
let frames = module.template.layout(self);
Ok(frames)
} }
/// Resolve a user-entered path (relative to the source file) to be /// Resolve a user-entered path (relative to the source file) to be

View File

@ -27,7 +27,7 @@ impl Layout for AlignNode {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
// The child only needs to expand along an axis if there's no alignment. // The child only needs to expand along an axis if there's no alignment.
let mut pod = regions.clone(); let mut pod = regions.clone();
pod.expand &= self.aligns.map_is_none(); pod.expand &= self.aligns.map_is_none();
@ -39,7 +39,7 @@ impl Layout for AlignNode {
} }
// Layout the child. // Layout the child.
let mut frames = self.child.layout(vm, &pod, passed.chain(&styles)); let mut frames = self.child.layout(vm, &pod, passed.chain(&styles))?;
for ((current, base), Constrained { item: frame, cts }) in for ((current, base), Constrained { item: frame, cts }) in
regions.iter().zip(&mut frames) regions.iter().zip(&mut frames)
@ -57,7 +57,7 @@ impl Layout for AlignNode {
cts.exact = current.filter(regions.expand | cts.exact.map_is_some()); cts.exact = current.filter(regions.expand | cts.exact.map_is_some());
} }
frames Ok(frames)
} }
} }

View File

@ -32,7 +32,7 @@ impl Layout for ColumnsNode {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
// Separating the infinite space into infinite columns does not make // Separating the infinite space into infinite columns does not make
// much sense. // much sense.
if regions.current.x.is_infinite() { if regions.current.x.is_infinite() {
@ -59,7 +59,7 @@ impl Layout for ColumnsNode {
}; };
// Layout the children. // Layout the children.
let mut frames = self.child.layout(vm, &pod, styles).into_iter(); let mut frames = self.child.layout(vm, &pod, styles)?.into_iter();
let dir = styles.get(ParNode::DIR); let dir = styles.get(ParNode::DIR);
let total_regions = (frames.len() as f32 / columns as f32).ceil() as usize; let total_regions = (frames.len() as f32 / columns as f32).ceil() as usize;
@ -102,7 +102,7 @@ impl Layout for ColumnsNode {
finished.push(output.constrain(cts)); finished.push(output.constrain(cts));
} }
finished Ok(finished)
} }
} }

View File

@ -32,15 +32,15 @@ impl<const L: DecoLine> DecoNode<L> {
} }
impl<const L: DecoLine> Show for DecoNode<L> { impl<const L: DecoLine> Show for DecoNode<L> {
fn show(&self, styles: StyleChain) -> Template { fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> {
self.0.clone().styled(TextNode::LINES, vec![Decoration { Ok(self.0.clone().styled(TextNode::LINES, vec![Decoration {
line: L, line: L,
stroke: styles.get(Self::STROKE), stroke: styles.get(Self::STROKE),
thickness: styles.get(Self::THICKNESS), thickness: styles.get(Self::THICKNESS),
offset: styles.get(Self::OFFSET), offset: styles.get(Self::OFFSET),
extent: styles.get(Self::EXTENT), extent: styles.get(Self::EXTENT),
evade: styles.get(Self::EVADE), evade: styles.get(Self::EVADE),
}]) }]))
} }
} }

View File

@ -31,7 +31,7 @@ impl Layout for FlowNode {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let mut layouter = FlowLayouter::new(regions); let mut layouter = FlowLayouter::new(regions);
for (child, map) in self.0.iter() { for (child, map) in self.0.iter() {
@ -56,12 +56,12 @@ impl Layout for FlowNode {
layouter.layout_spacing(*kind); layouter.layout_spacing(*kind);
} }
FlowChild::Node(ref node) => { FlowChild::Node(ref node) => {
layouter.layout_node(vm, node, styles); layouter.layout_node(vm, node, styles)?;
} }
} }
} }
layouter.finish() Ok(layouter.finish())
} }
} }
@ -161,7 +161,12 @@ impl FlowLayouter {
} }
/// Layout a node. /// Layout a node.
pub fn layout_node(&mut self, vm: &mut Vm, node: &LayoutNode, styles: StyleChain) { pub fn layout_node(
&mut self,
vm: &mut Vm,
node: &LayoutNode,
styles: StyleChain,
) -> TypResult<()> {
// Don't even try layouting into a full region. // Don't even try layouting into a full region.
if self.regions.is_full() { if self.regions.is_full() {
self.finish_region(); self.finish_region();
@ -171,9 +176,9 @@ impl FlowLayouter {
// aligned later. // aligned later.
if let Some(placed) = node.downcast::<PlaceNode>() { if let Some(placed) = node.downcast::<PlaceNode>() {
if placed.out_of_flow() { if placed.out_of_flow() {
let frame = node.layout(vm, &self.regions, styles).remove(0); let frame = node.layout(vm, &self.regions, styles)?.remove(0);
self.items.push(FlowItem::Placed(frame.item)); self.items.push(FlowItem::Placed(frame.item));
return; return Ok(());
} }
} }
@ -188,7 +193,7 @@ impl FlowLayouter {
.unwrap_or(Align::Top), .unwrap_or(Align::Top),
); );
let frames = node.layout(vm, &self.regions, styles); let frames = node.layout(vm, &self.regions, styles)?;
let len = frames.len(); let len = frames.len();
for (i, frame) in frames.into_iter().enumerate() { for (i, frame) in frames.into_iter().enumerate() {
// Grow our size, shrink the region and save the frame for later. // Grow our size, shrink the region and save the frame for later.
@ -202,6 +207,8 @@ impl FlowLayouter {
self.finish_region(); self.finish_region();
} }
} }
Ok(())
} }
/// Finish the frame for one region. /// Finish the frame for one region.

View File

@ -38,7 +38,7 @@ impl Layout for GridNode {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
// Prepare grid layout by unifying content and gutter tracks. // Prepare grid layout by unifying content and gutter tracks.
let layouter = GridLayouter::new( let layouter = GridLayouter::new(
self.tracks.as_deref(), self.tracks.as_deref(),
@ -48,7 +48,7 @@ impl Layout for GridNode {
styles, styles,
); );
// Measure the columsna nd layout the grid row-by-row. // Measure the columns and layout the grid row-by-row.
layouter.layout(vm) layouter.layout(vm)
} }
} }
@ -205,19 +205,19 @@ impl<'a> GridLayouter<'a> {
} }
/// Determines the columns sizes and then layouts the grid row-by-row. /// Determines the columns sizes and then layouts the grid row-by-row.
pub fn layout(mut self, vm: &mut Vm) -> Vec<Constrained<Arc<Frame>>> { pub fn layout(mut self, vm: &mut Vm) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
self.measure_columns(vm); self.measure_columns(vm)?;
for y in 0 .. self.rows.len() { for y in 0 .. self.rows.len() {
// Skip to next region if current one is full, but only for content // Skip to next region if current one is full, but only for content
// rows, not for gutter rows. // rows, not for gutter rows.
if y % 2 == 0 && self.regions.is_full() { if y % 2 == 0 && self.regions.is_full() {
self.finish_region(vm); self.finish_region(vm)?;
} }
match self.rows[y] { match self.rows[y] {
TrackSizing::Auto => self.layout_auto_row(vm, y), TrackSizing::Auto => self.layout_auto_row(vm, y)?,
TrackSizing::Linear(v) => self.layout_linear_row(vm, v, y), TrackSizing::Linear(v) => self.layout_linear_row(vm, v, y)?,
TrackSizing::Fractional(v) => { TrackSizing::Fractional(v) => {
self.cts.exact.y = Some(self.full); self.cts.exact.y = Some(self.full);
self.lrows.push(Row::Fr(v, y)); self.lrows.push(Row::Fr(v, y));
@ -226,12 +226,12 @@ impl<'a> GridLayouter<'a> {
} }
} }
self.finish_region(vm); self.finish_region(vm)?;
self.finished Ok(self.finished)
} }
/// Determine all column sizes. /// Determine all column sizes.
fn measure_columns(&mut self, vm: &mut Vm) { fn measure_columns(&mut self, vm: &mut Vm) -> TypResult<()> {
enum Case { enum Case {
/// The column sizing is only determined by specified linear sizes. /// The column sizing is only determined by specified linear sizes.
PurelyLinear, PurelyLinear,
@ -277,7 +277,7 @@ impl<'a> GridLayouter<'a> {
let available = self.regions.current.x - linear; let available = self.regions.current.x - linear;
if available >= Length::zero() { if available >= Length::zero() {
// Determine size of auto columns. // Determine size of auto columns.
let (auto, count) = self.measure_auto_columns(vm, available); let (auto, count) = self.measure_auto_columns(vm, available)?;
// If there is remaining space, distribute it to fractional columns, // If there is remaining space, distribute it to fractional columns,
// otherwise shrink auto columns. // otherwise shrink auto columns.
@ -308,6 +308,8 @@ impl<'a> GridLayouter<'a> {
// Sum up the resolved column sizes once here. // Sum up the resolved column sizes once here.
self.used.x = self.rcols.iter().sum(); self.used.x = self.rcols.iter().sum();
Ok(())
} }
/// Measure the size that is available to auto columns. /// Measure the size that is available to auto columns.
@ -315,7 +317,7 @@ impl<'a> GridLayouter<'a> {
&mut self, &mut self,
vm: &mut Vm, vm: &mut Vm,
available: Length, available: Length,
) -> (Length, usize) { ) -> TypResult<(Length, usize)> {
let mut auto = Length::zero(); let mut auto = Length::zero();
let mut count = 0; let mut count = 0;
@ -340,7 +342,7 @@ impl<'a> GridLayouter<'a> {
pod.base.y = v.resolve(self.regions.base.y); pod.base.y = v.resolve(self.regions.base.y);
} }
let frame = node.layout(vm, &pod, self.styles).remove(0).item; let frame = node.layout(vm, &pod, self.styles)?.remove(0).item;
resolved.set_max(frame.size.x); resolved.set_max(frame.size.x);
} }
} }
@ -350,7 +352,7 @@ impl<'a> GridLayouter<'a> {
count += 1; count += 1;
} }
(auto, count) Ok((auto, count))
} }
/// Distribute remaining space to fractional columns. /// Distribute remaining space to fractional columns.
@ -394,7 +396,7 @@ impl<'a> GridLayouter<'a> {
/// Layout a row with automatic height. Such a row may break across multiple /// Layout a row with automatic height. Such a row may break across multiple
/// regions. /// regions.
fn layout_auto_row(&mut self, vm: &mut Vm, y: usize) { fn layout_auto_row(&mut self, vm: &mut Vm, y: usize) -> TypResult<()> {
let mut resolved: Vec<Length> = vec![]; let mut resolved: Vec<Length> = vec![];
// Determine the size for each region of the row. // Determine the size for each region of the row.
@ -409,7 +411,7 @@ impl<'a> GridLayouter<'a> {
} }
let mut sizes = node let mut sizes = node
.layout(vm, &pod, self.styles) .layout(vm, &pod, self.styles)?
.into_iter() .into_iter()
.map(|frame| frame.item.size.y); .map(|frame| frame.item.size.y);
@ -427,14 +429,14 @@ impl<'a> GridLayouter<'a> {
// Nothing to layout. // Nothing to layout.
if resolved.is_empty() { if resolved.is_empty() {
return; return Ok(());
} }
// Layout into a single region. // Layout into a single region.
if let &[first] = resolved.as_slice() { if let &[first] = resolved.as_slice() {
let frame = self.layout_single_row(vm, first, y); let frame = self.layout_single_row(vm, first, y)?;
self.push_row(frame); self.push_row(frame);
return; return Ok(());
} }
// Expand all but the last region if the space is not // Expand all but the last region if the space is not
@ -449,36 +451,40 @@ impl<'a> GridLayouter<'a> {
} }
// Layout into multiple regions. // Layout into multiple regions.
let frames = self.layout_multi_row(vm, &resolved, y); let frames = self.layout_multi_row(vm, &resolved, y)?;
let len = frames.len(); let len = frames.len();
for (i, frame) in frames.into_iter().enumerate() { for (i, frame) in frames.into_iter().enumerate() {
self.push_row(frame); self.push_row(frame);
if i + 1 < len { if i + 1 < len {
self.cts.exact.y = Some(self.full); self.cts.exact.y = Some(self.full);
self.finish_region(vm); self.finish_region(vm)?;
} }
} }
Ok(())
} }
/// Layout a row with linear height. Such a row cannot break across multiple /// Layout a row with linear height. Such a row cannot break across multiple
/// regions, but it may force a region break. /// regions, but it may force a region break.
fn layout_linear_row(&mut self, vm: &mut Vm, v: Linear, y: usize) { fn layout_linear_row(&mut self, vm: &mut Vm, v: Linear, y: usize) -> TypResult<()> {
let resolved = v.resolve(self.regions.base.y); let resolved = v.resolve(self.regions.base.y);
let frame = self.layout_single_row(vm, resolved, y); let frame = self.layout_single_row(vm, resolved, y)?;
// Skip to fitting region. // Skip to fitting region.
let height = frame.size.y; let height = frame.size.y;
while !self.regions.current.y.fits(height) && !self.regions.in_last() { while !self.regions.current.y.fits(height) && !self.regions.in_last() {
self.cts.max.y = Some(self.used.y + height); self.cts.max.y = Some(self.used.y + height);
self.finish_region(vm); self.finish_region(vm)?;
// Don't skip multiple regions for gutter and don't push a row. // Don't skip multiple regions for gutter and don't push a row.
if y % 2 == 1 { if y % 2 == 1 {
return; return Ok(());
} }
} }
self.push_row(frame); self.push_row(frame);
Ok(())
} }
/// Layout a row with fixed height and return its frame. /// Layout a row with fixed height and return its frame.
@ -487,7 +493,7 @@ impl<'a> GridLayouter<'a> {
vm: &mut Vm, vm: &mut Vm,
height: Length, height: Length,
y: usize, y: usize,
) -> Frame { ) -> TypResult<Frame> {
let mut output = Frame::new(Size::new(self.used.x, height)); let mut output = Frame::new(Size::new(self.used.x, height));
let mut pos = Point::zero(); let mut pos = Point::zero();
@ -502,14 +508,14 @@ impl<'a> GridLayouter<'a> {
.select(self.regions.base, size); .select(self.regions.base, size);
let pod = Regions::one(size, base, Spec::splat(true)); let pod = Regions::one(size, base, Spec::splat(true));
let frame = node.layout(vm, &pod, self.styles).remove(0); let frame = node.layout(vm, &pod, self.styles)?.remove(0);
output.push_frame(pos, frame.item); output.push_frame(pos, frame.item);
} }
pos.x += rcol; pos.x += rcol;
} }
output Ok(output)
} }
/// Layout a row spanning multiple regions. /// Layout a row spanning multiple regions.
@ -518,7 +524,7 @@ impl<'a> GridLayouter<'a> {
vm: &mut Vm, vm: &mut Vm,
heights: &[Length], heights: &[Length],
y: usize, y: usize,
) -> Vec<Frame> { ) -> TypResult<Vec<Frame>> {
// Prepare frames. // Prepare frames.
let mut outputs: Vec<_> = heights let mut outputs: Vec<_> = heights
.iter() .iter()
@ -542,7 +548,7 @@ impl<'a> GridLayouter<'a> {
} }
// Push the layouted frames into the individual output frames. // Push the layouted frames into the individual output frames.
let frames = node.layout(vm, &pod, self.styles); let frames = node.layout(vm, &pod, self.styles)?;
for (output, frame) in outputs.iter_mut().zip(frames) { for (output, frame) in outputs.iter_mut().zip(frames) {
output.push_frame(pos, frame.item); output.push_frame(pos, frame.item);
} }
@ -551,7 +557,7 @@ impl<'a> GridLayouter<'a> {
pos.x += rcol; pos.x += rcol;
} }
outputs Ok(outputs)
} }
/// Push a row frame into the current region. /// Push a row frame into the current region.
@ -562,7 +568,7 @@ impl<'a> GridLayouter<'a> {
} }
/// Finish rows for one region. /// Finish rows for one region.
fn finish_region(&mut self, vm: &mut Vm) { fn finish_region(&mut self, vm: &mut Vm) -> TypResult<()> {
// Determine the size of the grid in this region, expanding fully if // Determine the size of the grid in this region, expanding fully if
// there are fr rows. // there are fr rows.
let mut size = self.used; let mut size = self.used;
@ -584,7 +590,7 @@ impl<'a> GridLayouter<'a> {
Row::Fr(v, y) => { Row::Fr(v, y) => {
let remaining = self.full - self.used.y; let remaining = self.full - self.used.y;
let height = v.resolve(self.fr, remaining); let height = v.resolve(self.fr, remaining);
self.layout_single_row(vm, height, y) self.layout_single_row(vm, height, y)?
} }
}; };
@ -600,6 +606,8 @@ impl<'a> GridLayouter<'a> {
self.used.y = Length::zero(); self.used.y = Length::zero();
self.fr = Fractional::zero(); self.fr = Fractional::zero();
self.cts = Constraints::new(self.expand); self.cts = Constraints::new(self.expand);
Ok(())
} }
/// Get the node in the cell in column `x` and row `y`. /// Get the node in the cell in column `x` and row `y`.

View File

@ -43,7 +43,7 @@ impl HeadingNode {
} }
impl Show for HeadingNode { impl Show for HeadingNode {
fn show(&self, styles: StyleChain) -> Template { fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> {
let mut map = StyleMap::new(); let mut map = StyleMap::new();
let upscale = (1.6 - 0.1 * self.level as f64).max(0.75); let upscale = (1.6 - 0.1 * self.level as f64).max(0.75);
@ -93,6 +93,8 @@ impl Show for HeadingNode {
seq.push(Template::Vertical(below.into())); seq.push(Template::Vertical(below.into()));
} }
Template::block(Template::sequence(seq).styled_with_map(map)) Ok(Template::block(
Template::sequence(seq).styled_with_map(map),
))
} }
} }

View File

@ -19,14 +19,14 @@ impl Layout for HideNode {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let mut frames = self.0.layout(vm, regions, styles); let mut frames = self.0.layout(vm, regions, styles)?;
// Clear the frames. // Clear the frames.
for Constrained { item: frame, .. } in &mut frames { for Constrained { item: frame, .. } in &mut frames {
*frame = Arc::new(Frame { elements: vec![], ..**frame }); *frame = Arc::new(Frame { elements: vec![], ..**frame });
} }
frames Ok(frames)
} }
} }

View File

@ -39,7 +39,7 @@ impl Layout for ImageNode {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let img = vm.images.get(self.0); let img = vm.images.get(self.0);
let pxw = img.width() as f64; let pxw = img.width() as f64;
let pxh = img.height() as f64; let pxh = img.height() as f64;
@ -91,7 +91,7 @@ impl Layout for ImageNode {
frame.link(url); frame.link(url);
} }
vec![frame.constrain(Constraints::tight(regions))] Ok(vec![frame.constrain(Constraints::tight(regions))])
} }
} }

View File

@ -37,7 +37,7 @@ impl LinkNode {
} }
impl Show for LinkNode { impl Show for LinkNode {
fn show(&self, styles: StyleChain) -> Template { fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> {
let mut map = StyleMap::new(); let mut map = StyleMap::new();
map.set(TextNode::LINK, Some(self.url.clone())); map.set(TextNode::LINK, Some(self.url.clone()));
@ -50,6 +50,6 @@ impl Show for LinkNode {
body = body.underlined(); body = body.underlined();
} }
body.styled_with_map(map) Ok(body.styled_with_map(map))
} }
} }

View File

@ -30,7 +30,7 @@ impl<const L: Labelling> ListNode<L> {
} }
impl<const L: Labelling> Show for ListNode<L> { impl<const L: Labelling> Show for ListNode<L> {
fn show(&self, styles: StyleChain) -> Template { fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> {
let em = styles.get(TextNode::SIZE).abs; let em = styles.get(TextNode::SIZE).abs;
let label_indent = styles.get(Self::LABEL_INDENT).resolve(em); let label_indent = styles.get(Self::LABEL_INDENT).resolve(em);
let body_indent = styles.get(Self::BODY_INDENT).resolve(em); let body_indent = styles.get(Self::BODY_INDENT).resolve(em);
@ -40,7 +40,7 @@ impl<const L: Labelling> Show for ListNode<L> {
ORDERED | _ => format_eco!("{}.", self.number.unwrap_or(1)), ORDERED | _ => format_eco!("{}.", self.number.unwrap_or(1)),
}; };
Template::block(GridNode { Ok(Template::block(GridNode {
tracks: Spec::with_x(vec![ tracks: Spec::with_x(vec![
TrackSizing::Linear(label_indent.into()), TrackSizing::Linear(label_indent.into()),
TrackSizing::Auto, TrackSizing::Auto,
@ -54,7 +54,7 @@ impl<const L: Labelling> Show for ListNode<L> {
LayoutNode::default(), LayoutNode::default(),
self.child.clone(), self.child.clone(),
], ],
}) }))
} }
} }

View File

@ -22,11 +22,11 @@ impl MathNode {
} }
impl Show for MathNode { impl Show for MathNode {
fn show(&self, _: StyleChain) -> Template { fn show(&self, _: &mut Vm, _: StyleChain) -> TypResult<Template> {
let mut template = Template::Text(self.formula.trim().into()); let mut template = Template::Text(self.formula.trim().into());
if self.display { if self.display {
template = Template::Block(template.pack()); template = Template::Block(template.pack());
} }
template.monospaced() Ok(template.monospaced())
} }
} }

View File

@ -37,10 +37,10 @@ impl Layout for PadNode {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
// Layout child into padded regions. // Layout child into padded regions.
let pod = regions.map(|size| shrink(size, self.padding)); let pod = regions.map(|size| shrink(size, self.padding));
let mut frames = self.child.layout(vm, &pod, styles); let mut frames = self.child.layout(vm, &pod, styles)?;
for ((current, base), Constrained { item: frame, cts }) in for ((current, base), Constrained { item: frame, cts }) in
regions.iter().zip(&mut frames) regions.iter().zip(&mut frames)
@ -70,7 +70,7 @@ impl Layout for PadNode {
} }
} }
frames Ok(frames)
} }
} }

View File

@ -60,7 +60,7 @@ impl PageNode {
impl PageNode { impl PageNode {
/// Layout the page run into a sequence of frames, one per page. /// Layout the page run into a sequence of frames, one per page.
pub fn layout(&self, vm: &mut Vm, styles: StyleChain) -> Vec<Arc<Frame>> { pub fn layout(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Vec<Arc<Frame>>> {
// When one of the lengths is infinite the page fits its content along // When one of the lengths is infinite the page fits its content along
// that axis. // that axis.
let width = styles.get(Self::WIDTH).unwrap_or(Length::inf()); let width = styles.get(Self::WIDTH).unwrap_or(Length::inf());
@ -103,11 +103,11 @@ impl PageNode {
// Layout the child. // Layout the child.
let expand = size.map(Length::is_finite); let expand = size.map(Length::is_finite);
let regions = Regions::repeat(size, size, expand); let regions = Regions::repeat(size, size, expand);
child Ok(child
.layout(vm, &regions, styles) .layout(vm, &regions, styles)?
.into_iter() .into_iter()
.map(|c| c.item) .map(|c| c.item)
.collect() .collect())
} }
} }

View File

@ -85,7 +85,7 @@ impl Layout for ParNode {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
// Collect all text into one string used for BiDi analysis. // Collect all text into one string used for BiDi analysis.
let text = self.collect_text(); let text = self.collect_text();
@ -95,10 +95,10 @@ impl Layout for ParNode {
// Prepare paragraph layout by building a representation on which we can // Prepare paragraph layout by building a representation on which we can
// do line breaking without layouting each and every line from scratch. // do line breaking without layouting each and every line from scratch.
let layouter = ParLayouter::new(self, vm, regions, &styles, bidi); let layouter = ParLayouter::new(self, vm, regions, &styles, bidi)?;
// Find suitable linebreaks. // Find suitable linebreaks.
layouter.layout(vm, regions.clone()) Ok(layouter.layout(vm, regions.clone()))
} }
} }
@ -220,7 +220,7 @@ impl<'a> ParLayouter<'a> {
regions: &Regions, regions: &Regions,
styles: &'a StyleChain<'a>, styles: &'a StyleChain<'a>,
bidi: BidiInfo<'a>, bidi: BidiInfo<'a>,
) -> Self { ) -> TypResult<Self> {
let mut items = vec![]; let mut items = vec![];
let mut ranges = vec![]; let mut ranges = vec![];
@ -255,7 +255,7 @@ impl<'a> ParLayouter<'a> {
ParChild::Node(node) => { ParChild::Node(node) => {
let size = Size::new(regions.current.x, regions.base.y); let size = Size::new(regions.current.x, regions.base.y);
let pod = Regions::one(size, regions.base, Spec::splat(false)); let pod = Regions::one(size, regions.base, Spec::splat(false));
let frame = node.layout(vm, &pod, styles).remove(0); let frame = node.layout(vm, &pod, styles)?.remove(0);
items.push(ParItem::Frame(Arc::take(frame.item))); items.push(ParItem::Frame(Arc::take(frame.item)));
ranges.push(range); ranges.push(range);
} }
@ -266,7 +266,7 @@ impl<'a> ParLayouter<'a> {
let align = styles.get(ParNode::ALIGN); let align = styles.get(ParNode::ALIGN);
let leading = styles.get(ParNode::LEADING).resolve(em); let leading = styles.get(ParNode::LEADING).resolve(em);
Self { align, leading, bidi, items, ranges } Ok(Self { align, leading, bidi, items, ranges })
} }
/// Find first-fit line breaks and build the paragraph. /// Find first-fit line breaks and build the paragraph.

View File

@ -26,7 +26,7 @@ impl Layout for PlaceNode {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let out_of_flow = self.out_of_flow(); let out_of_flow = self.out_of_flow();
// The pod is the base area of the region because for absolute // The pod is the base area of the region because for absolute
@ -37,7 +37,7 @@ impl Layout for PlaceNode {
Regions::one(regions.base, regions.base, expand) Regions::one(regions.base, regions.base, expand)
}; };
let mut frames = self.0.layout(vm, &pod, styles); let mut frames = self.0.layout(vm, &pod, styles)?;
let Constrained { item: frame, cts } = &mut frames[0]; let Constrained { item: frame, cts } = &mut frames[0];
// If expansion is off, zero all sizes so that we don't take up any // If expansion is off, zero all sizes so that we don't take up any
@ -51,7 +51,7 @@ impl Layout for PlaceNode {
cts.base = regions.base.map(Some); cts.base = regions.base.map(Some);
cts.exact = regions.current.filter(regions.expand | out_of_flow); cts.exact = regions.current.filter(regions.expand | out_of_flow);
frames Ok(frames)
} }
} }

View File

@ -40,7 +40,7 @@ impl RawNode {
} }
impl Show for RawNode { impl Show for RawNode {
fn show(&self, styles: StyleChain) -> Template { fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> {
let lang = styles.get_ref(Self::LANG).as_ref(); let lang = styles.get_ref(Self::LANG).as_ref();
let foreground = THEME let foreground = THEME
.settings .settings
@ -87,7 +87,7 @@ impl Show for RawNode {
template = Template::Block(template.pack()); template = Template::Block(template.pack());
} }
template.monospaced() Ok(template.monospaced())
} }
} }

View File

@ -49,7 +49,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let mut frames; let mut frames;
if let Some(child) = &self.0 { if let Some(child) = &self.0 {
let mut padding = styles.get(Self::PADDING); let mut padding = styles.get(Self::PADDING);
@ -61,7 +61,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
let child = child.clone().padded(Sides::splat(padding)); let child = child.clone().padded(Sides::splat(padding));
let mut pod = Regions::one(regions.current, regions.base, regions.expand); let mut pod = Regions::one(regions.current, regions.base, regions.expand);
frames = child.layout(vm, &pod, styles); frames = child.layout(vm, &pod, styles)?;
// Relayout with full expansion into square region to make sure // Relayout with full expansion into square region to make sure
// the result is really a square or circle. // the result is really a square or circle.
@ -77,7 +77,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
pod.current = Size::splat(length); pod.current = Size::splat(length);
pod.expand = Spec::splat(true); pod.expand = Spec::splat(true);
frames = child.layout(vm, &pod, styles); frames = child.layout(vm, &pod, styles)?;
frames[0].cts = Constraints::tight(regions); frames[0].cts = Constraints::tight(regions);
} }
} else { } else {
@ -127,7 +127,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
frame.link(url); frame.link(url);
} }
frames Ok(frames)
} }
} }

View File

@ -31,7 +31,7 @@ impl Layout for StackNode {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let mut layouter = StackLayouter::new(self.dir, regions); let mut layouter = StackLayouter::new(self.dir, regions);
// Spacing to insert before the next node. // Spacing to insert before the next node.
@ -48,13 +48,13 @@ impl Layout for StackNode {
layouter.layout_spacing(kind); layouter.layout_spacing(kind);
} }
layouter.layout_node(vm, node, styles); layouter.layout_node(vm, node, styles)?;
deferred = self.spacing; deferred = self.spacing;
} }
} }
} }
layouter.finish() Ok(layouter.finish())
} }
} }
@ -163,7 +163,12 @@ impl StackLayouter {
} }
/// Layout an arbitrary node. /// Layout an arbitrary node.
pub fn layout_node(&mut self, vm: &mut Vm, node: &LayoutNode, styles: StyleChain) { pub fn layout_node(
&mut self,
vm: &mut Vm,
node: &LayoutNode,
styles: StyleChain,
) -> TypResult<()> {
if self.regions.is_full() { if self.regions.is_full() {
self.finish_region(); self.finish_region();
} }
@ -174,7 +179,7 @@ impl StackLayouter {
.and_then(|node| node.aligns.get(self.axis)) .and_then(|node| node.aligns.get(self.axis))
.unwrap_or(self.dir.start().into()); .unwrap_or(self.dir.start().into());
let frames = node.layout(vm, &self.regions, styles); let frames = node.layout(vm, &self.regions, styles)?;
let len = frames.len(); let len = frames.len();
for (i, frame) in frames.into_iter().enumerate() { for (i, frame) in frames.into_iter().enumerate() {
// Grow our size, shrink the region and save the frame for later. // Grow our size, shrink the region and save the frame for later.
@ -188,6 +193,8 @@ impl StackLayouter {
self.finish_region(); self.finish_region();
} }
} }
Ok(())
} }
/// Advance to the next region. /// Advance to the next region.

View File

@ -55,7 +55,7 @@ impl TableNode {
} }
impl Show for TableNode { impl Show for TableNode {
fn show(&self, styles: StyleChain) -> Template { fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> {
let primary = styles.get(Self::PRIMARY); let primary = styles.get(Self::PRIMARY);
let secondary = styles.get(Self::SECONDARY); let secondary = styles.get(Self::SECONDARY);
let thickness = styles.get(Self::THICKNESS); let thickness = styles.get(Self::THICKNESS);
@ -85,10 +85,10 @@ impl Show for TableNode {
}) })
.collect(); .collect();
Template::block(GridNode { Ok(Template::block(GridNode {
tracks: self.tracks.clone(), tracks: self.tracks.clone(),
gutter: self.gutter.clone(), gutter: self.gutter.clone(),
children, children,
}) }))
} }
} }

View File

@ -123,8 +123,8 @@ impl StrongNode {
} }
impl Show for StrongNode { impl Show for StrongNode {
fn show(&self, _: StyleChain) -> Template { fn show(&self, _: &mut Vm, _: StyleChain) -> TypResult<Template> {
self.0.clone().styled(TextNode::STRONG, true) Ok(self.0.clone().styled(TextNode::STRONG, true))
} }
} }
@ -140,8 +140,8 @@ impl EmphNode {
} }
impl Show for EmphNode { impl Show for EmphNode {
fn show(&self, _: StyleChain) -> Template { fn show(&self, _: &mut Vm, _: StyleChain) -> TypResult<Template> {
self.0.clone().styled(TextNode::EMPH, true) Ok(self.0.clone().styled(TextNode::EMPH, true))
} }
} }

View File

@ -49,9 +49,9 @@ impl<const T: TransformKind> Layout for TransformNode<T> {
vm: &mut Vm, vm: &mut Vm,
regions: &Regions, regions: &Regions,
styles: StyleChain, styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> { ) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON); let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON);
let mut frames = self.child.layout(vm, regions, styles); let mut frames = self.child.layout(vm, regions, styles)?;
for Constrained { item: frame, .. } in &mut frames { for Constrained { item: frame, .. } in &mut frames {
let Spec { x, y } = origin.zip(frame.size).map(|(o, s)| o.resolve(s)); let Spec { x, y } = origin.zip(frame.size).map(|(o, s)| o.resolve(s));
@ -62,7 +62,7 @@ impl<const T: TransformKind> Layout for TransformNode<T> {
Arc::make_mut(frame).transform(transform); Arc::make_mut(frame).transform(transform);
} }
frames Ok(frames)
} }
} }

View File

@ -275,14 +275,13 @@ fn test_part(
ok &= test_reparse(ctx.sources.get(id).src(), i, rng); ok &= test_reparse(ctx.sources.get(id).src(), i, rng);
let mut vm = Vm::new(ctx); let mut vm = Vm::new(ctx);
let (frames, mut errors) = match vm.evaluate(id) { let (frames, mut errors) = match vm.typeset(id) {
Ok(module) => { Ok(mut frames) => {
let module = vm.evaluate(id).unwrap();
if debug { if debug {
println!("Template: {:#?}", module.template); println!("Template: {:#?}", module.template);
} }
let mut frames = module.template.layout(&mut vm);
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
(ok &= test_incremental(ctx, i, &module.template, &frames)); (ok &= test_incremental(ctx, i, &module.template, &frames));
@ -499,7 +498,7 @@ fn test_incremental(
ctx.layout_cache.turnaround(); ctx.layout_cache.turnaround();
let cached = silenced(|| template.layout(&mut Vm::new(ctx))); let cached = silenced(|| template.layout(&mut Vm::new(ctx)).unwrap());
let total = reference.levels() - 1; let total = reference.levels() - 1;
let misses = ctx let misses = ctx
.layout_cache .layout_cache