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 super::{StyleChain, Template};
use crate::diag::TypResult;
use crate::util::Prehashed;
use crate::Vm;
/// A node that can be realized given some styles.
pub trait Show {
/// 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.
fn pack(self) -> ShowNode
@ -40,8 +42,8 @@ impl ShowNode {
}
impl Show for ShowNode {
fn show(&self, styles: StyleChain) -> Template {
self.0.show(styles)
fn show(&self, vm: &mut Vm, styles: StyleChain) -> TypResult<Template> {
self.0.show(vm, styles)
}
fn pack(self) -> ShowNode {

View File

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

View File

@ -15,6 +15,7 @@ use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use crate::diag::TypResult;
use crate::eval::StyleChain;
use crate::frame::{Element, Frame, Geometry, Shape, Stroke};
use crate::geom::{Align, Linear, Paint, Point, Sides, Size, Spec, Transform};
@ -33,7 +34,7 @@ pub trait Layout {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>>;
) -> TypResult<Vec<Constrained<Arc<Frame>>>>;
/// Convert to a packed node.
fn pack(self) -> LayoutNode
@ -137,7 +138,7 @@ impl Layout for LayoutNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let styles = styles.barred(self.id());
#[cfg(not(feature = "layout-cache"))]
@ -155,10 +156,10 @@ impl Layout for LayoutNode {
// #[track_caller] annotation doesn't work.
#[cfg(feature = "layout-cache")]
if let Some(frames) = vm.layout_cache.get(hash, regions) {
frames
Ok(frames)
} else {
vm.level += 1;
let frames = self.0.layout(vm, regions, styles);
let frames = self.0.layout(vm, regions, styles)?;
vm.level -= 1;
let entry = FramesEntry::new(frames.clone(), vm.level);
@ -175,7 +176,7 @@ impl Layout for LayoutNode {
}
vm.layout_cache.insert(hash, entry);
frames
Ok(frames)
}
}
@ -248,11 +249,11 @@ impl Layout for EmptyNode {
_: &mut Vm,
regions: &Regions,
_: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let size = regions.expand.select(regions.current, Size::zero());
let mut cts = Constraints::new(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,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let is_auto = self.sizing.map_is_none();
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)
};
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];
// 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.base = regions.base.filter(is_rel | is_auto);
frames
Ok(frames)
}
}
@ -325,13 +326,13 @@ impl Layout for FillNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
let mut frames = self.child.layout(vm, regions, styles);
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let mut frames = self.child.layout(vm, regions, styles)?;
for Constrained { item: frame, .. } in &mut frames {
let shape = Shape::filled(Geometry::Rect(frame.size), self.fill);
Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape));
}
frames
Ok(frames)
}
}
@ -350,12 +351,12 @@ impl Layout for StrokeNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
let mut frames = self.child.layout(vm, regions, styles);
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let mut frames = self.child.layout(vm, regions, styles)?;
for Constrained { item: frame, .. } in &mut frames {
let shape = Shape::stroked(Geometry::Rect(frame.size), self.stroke);
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
/// information.
pub fn typeset(&mut self, id: SourceId) -> TypResult<Vec<Arc<Frame>>> {
let module = self.evaluate(id)?;
let frames = module.template.layout(self);
Ok(frames)
self.evaluate(id)?.template.layout(self)
}
/// 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,
regions: &Regions,
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.
let mut pod = regions.clone();
pod.expand &= self.aligns.map_is_none();
@ -39,7 +39,7 @@ impl Layout for AlignNode {
}
// 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
regions.iter().zip(&mut frames)
@ -57,7 +57,7 @@ impl Layout for AlignNode {
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,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
// Separating the infinite space into infinite columns does not make
// much sense.
if regions.current.x.is_infinite() {
@ -59,7 +59,7 @@ impl Layout for ColumnsNode {
};
// 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 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
Ok(finished)
}
}

View File

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

View File

@ -31,7 +31,7 @@ impl Layout for FlowNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let mut layouter = FlowLayouter::new(regions);
for (child, map) in self.0.iter() {
@ -56,12 +56,12 @@ impl Layout for FlowNode {
layouter.layout_spacing(*kind);
}
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.
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.
if self.regions.is_full() {
self.finish_region();
@ -171,9 +176,9 @@ impl FlowLayouter {
// aligned later.
if let Some(placed) = node.downcast::<PlaceNode>() {
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));
return;
return Ok(());
}
}
@ -188,7 +193,7 @@ impl FlowLayouter {
.unwrap_or(Align::Top),
);
let frames = node.layout(vm, &self.regions, styles);
let frames = node.layout(vm, &self.regions, styles)?;
let len = frames.len();
for (i, frame) in frames.into_iter().enumerate() {
// Grow our size, shrink the region and save the frame for later.
@ -202,6 +207,8 @@ impl FlowLayouter {
self.finish_region();
}
}
Ok(())
}
/// Finish the frame for one region.

View File

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

View File

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

View File

@ -39,7 +39,7 @@ impl Layout for ImageNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let img = vm.images.get(self.0);
let pxw = img.width() as f64;
let pxh = img.height() as f64;
@ -91,7 +91,7 @@ impl Layout for ImageNode {
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 {
fn show(&self, styles: StyleChain) -> Template {
fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> {
let mut map = StyleMap::new();
map.set(TextNode::LINK, Some(self.url.clone()));
@ -50,6 +50,6 @@ impl Show for LinkNode {
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> {
fn show(&self, styles: StyleChain) -> Template {
fn show(&self, _: &mut Vm, styles: StyleChain) -> TypResult<Template> {
let em = styles.get(TextNode::SIZE).abs;
let label_indent = styles.get(Self::LABEL_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)),
};
Template::block(GridNode {
Ok(Template::block(GridNode {
tracks: Spec::with_x(vec![
TrackSizing::Linear(label_indent.into()),
TrackSizing::Auto,
@ -54,7 +54,7 @@ impl<const L: Labelling> Show for ListNode<L> {
LayoutNode::default(),
self.child.clone(),
],
})
}))
}
}

View File

@ -22,11 +22,11 @@ impl 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());
if self.display {
template = Template::Block(template.pack());
}
template.monospaced()
Ok(template.monospaced())
}
}

View File

@ -37,10 +37,10 @@ impl Layout for PadNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
// Layout child into padded regions.
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
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 {
/// 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
// that axis.
let width = styles.get(Self::WIDTH).unwrap_or(Length::inf());
@ -103,11 +103,11 @@ impl PageNode {
// Layout the child.
let expand = size.map(Length::is_finite);
let regions = Regions::repeat(size, size, expand);
child
.layout(vm, &regions, styles)
Ok(child
.layout(vm, &regions, styles)?
.into_iter()
.map(|c| c.item)
.collect()
.collect())
}
}

View File

@ -85,7 +85,7 @@ impl Layout for ParNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
// Collect all text into one string used for BiDi analysis.
let text = self.collect_text();
@ -95,10 +95,10 @@ impl Layout for ParNode {
// Prepare paragraph layout by building a representation on which we can
// 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.
layouter.layout(vm, regions.clone())
Ok(layouter.layout(vm, regions.clone()))
}
}
@ -220,7 +220,7 @@ impl<'a> ParLayouter<'a> {
regions: &Regions,
styles: &'a StyleChain<'a>,
bidi: BidiInfo<'a>,
) -> Self {
) -> TypResult<Self> {
let mut items = vec![];
let mut ranges = vec![];
@ -255,7 +255,7 @@ impl<'a> ParLayouter<'a> {
ParChild::Node(node) => {
let size = Size::new(regions.current.x, regions.base.y);
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)));
ranges.push(range);
}
@ -266,7 +266,7 @@ impl<'a> ParLayouter<'a> {
let align = styles.get(ParNode::ALIGN);
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.

View File

@ -26,7 +26,7 @@ impl Layout for PlaceNode {
vm: &mut Vm,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let out_of_flow = self.out_of_flow();
// 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)
};
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];
// 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.exact = regions.current.filter(regions.expand | out_of_flow);
frames
Ok(frames)
}
}

View File

@ -40,7 +40,7 @@ impl 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 foreground = THEME
.settings
@ -87,7 +87,7 @@ impl Show for RawNode {
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,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
let mut frames;
if let Some(child) = &self.0 {
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 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
// 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.expand = Spec::splat(true);
frames = child.layout(vm, &pod, styles);
frames = child.layout(vm, &pod, styles)?;
frames[0].cts = Constraints::tight(regions);
}
} else {
@ -127,7 +127,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
frame.link(url);
}
frames
Ok(frames)
}
}

View File

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

View File

@ -55,7 +55,7 @@ impl 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 secondary = styles.get(Self::SECONDARY);
let thickness = styles.get(Self::THICKNESS);
@ -85,10 +85,10 @@ impl Show for TableNode {
})
.collect();
Template::block(GridNode {
Ok(Template::block(GridNode {
tracks: self.tracks.clone(),
gutter: self.gutter.clone(),
children,
})
}))
}
}

View File

@ -123,8 +123,8 @@ impl StrongNode {
}
impl Show for StrongNode {
fn show(&self, _: StyleChain) -> Template {
self.0.clone().styled(TextNode::STRONG, true)
fn show(&self, _: &mut Vm, _: StyleChain) -> TypResult<Template> {
Ok(self.0.clone().styled(TextNode::STRONG, true))
}
}
@ -140,8 +140,8 @@ impl EmphNode {
}
impl Show for EmphNode {
fn show(&self, _: StyleChain) -> Template {
self.0.clone().styled(TextNode::EMPH, true)
fn show(&self, _: &mut Vm, _: StyleChain) -> TypResult<Template> {
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,
regions: &Regions,
styles: StyleChain,
) -> Vec<Constrained<Arc<Frame>>> {
) -> TypResult<Vec<Constrained<Arc<Frame>>>> {
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 {
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);
}
frames
Ok(frames)
}
}

View File

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