diff --git a/src/eval/node.rs b/src/eval/node.rs index 43cb906b7..ffa1eee6c 100644 --- a/src/eval/node.rs +++ b/src/eval/node.rs @@ -372,13 +372,13 @@ impl Packer { return; } - if !self.par.styles.compatible(&styles, ParNode::has_property) { + if !self.par.styles.compatible(styles, ParNode::has_property) { self.parbreak(None); self.par.styles = styles.clone(); return; } - self.par.styles.intersect(&styles); + self.par.styles.intersect(styles); } /// Break to a new page if the `styles` contain page styles that are @@ -389,7 +389,7 @@ impl Packer { return; } - if self.top && !self.flow.styles.compatible(&styles, PageNode::has_property) { + if self.top && !self.flow.styles.compatible(styles, PageNode::has_property) { self.pagebreak(); self.flow.styles = styles.clone(); return; diff --git a/src/export/pdf.rs b/src/export/pdf.rs index b5f4c009e..f0fd10f66 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -390,7 +390,7 @@ impl<'a> PageExporter<'a> { // Make the coordinate system start at the top-left. self.bottom = frame.size.y.to_f32(); self.content.transform([1.0, 0.0, 0.0, -1.0, 0.0, self.bottom]); - self.write_frame(&frame); + self.write_frame(frame); Page { size: frame.size, content: self.content, diff --git a/src/export/subset.rs b/src/export/subset.rs index b58e042e3..68d455020 100644 --- a/src/export/subset.rs +++ b/src/export/subset.rs @@ -656,7 +656,7 @@ mod cff { if count > 0 { let offsize = usize::from(s.read::()?); - if offsize < 1 || offsize > 4 { + if !matches!(offsize, 1 ..= 4) { return None; } diff --git a/src/geom/paint.rs b/src/geom/paint.rs index 66bfb17c0..0eba9f2f5 100644 --- a/src/geom/paint.rs +++ b/src/geom/paint.rs @@ -72,7 +72,7 @@ impl RgbaColor { } impl FromStr for RgbaColor { - type Err = ParseRgbaError; + type Err = RgbaError; /// Constructs a new color from hex strings like the following: /// - `#aef` (shorthand, with leading hashtag), @@ -83,7 +83,7 @@ impl FromStr for RgbaColor { fn from_str(hex_str: &str) -> Result { let hex_str = hex_str.strip_prefix('#').unwrap_or(hex_str); if !hex_str.is_ascii() { - return Err(ParseRgbaError); + return Err(RgbaError); } let len = hex_str.len(); @@ -92,7 +92,7 @@ impl FromStr for RgbaColor { let alpha = len == 4 || len == 8; if !long && !short { - return Err(ParseRgbaError); + return Err(RgbaError); } let mut values: [u8; 4] = [255; 4]; @@ -102,7 +102,7 @@ impl FromStr for RgbaColor { let pos = elem * item_len; let item = &hex_str[pos .. (pos + item_len)]; - values[elem] = u8::from_str_radix(item, 16).map_err(|_| ParseRgbaError)?; + values[elem] = u8::from_str_radix(item, 16).map_err(|_| RgbaError)?; if short { // Duplicate number for shorthand notation, i.e. `a` -> `aa` @@ -134,15 +134,15 @@ impl Debug for RgbaColor { /// The error when parsing an [`RgbaColor`] from a string fails. #[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct ParseRgbaError; +pub struct RgbaError; -impl Display for ParseRgbaError { +impl Display for RgbaError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.pad("invalid color") + f.pad("invalid hex string") } } -impl std::error::Error for ParseRgbaError {} +impl std::error::Error for RgbaError {} #[cfg(test)] mod tests { @@ -166,7 +166,7 @@ mod tests { fn test_parse_invalid_colors() { #[track_caller] fn test(hex: &str) { - assert_eq!(RgbaColor::from_str(hex), Err(ParseRgbaError)); + assert_eq!(RgbaColor::from_str(hex), Err(RgbaError)); } test("12345"); diff --git a/src/geom/scalar.rs b/src/geom/scalar.rs index 066246a9c..948ea7ecd 100644 --- a/src/geom/scalar.rs +++ b/src/geom/scalar.rs @@ -27,7 +27,7 @@ impl Debug for Scalar { impl Ord for Scalar { fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(&other).expect("float is NaN") + self.partial_cmp(other).expect("float is NaN") } } diff --git a/src/image.rs b/src/image.rs index a7b625037..3d80896fc 100644 --- a/src/image.rs +++ b/src/image.rs @@ -67,7 +67,7 @@ impl ImageStore { Entry::Vacant(entry) => { let buffer = self.loader.load(path)?; let ext = path.extension().and_then(OsStr::to_str).unwrap_or_default(); - let image = Image::parse(&buffer, &ext)?; + let image = Image::parse(&buffer, ext)?; let id = ImageId(self.images.len() as u32); if let Some(callback) = &self.on_load { callback(id, &image); diff --git a/src/layout/incremental.rs b/src/layout/incremental.rs index 60b198ff9..4c046051b 100644 --- a/src/layout/incremental.rs +++ b/src/layout/incremental.rs @@ -377,22 +377,14 @@ pub struct PatternProperties { impl PatternProperties { /// Check if it is vital to keep an entry based on its properties. pub fn must_keep(&self) -> bool { - if self.top_level && !self.mature { - // Keep an undo stack. - true - } else if self.all_zeros && !self.mature { - // Keep the most recently created items, even if they have not yet - // been used. - true - } else if self.multi_use && !self.abandoned { - true - } else if self.hit { - true - } else if self.sparse { - true - } else { - false - } + // Keep an undo stack. + (self.top_level && !self.mature) + // Keep the most recently created items, even if they have not yet + // been used. + || (self.all_zeros && !self.mature) + || (self.multi_use && !self.abandoned) + || self.hit + || self.sparse } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 114e74914..4c982bb85 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -282,7 +282,10 @@ impl Debug for PackedNode { impl PartialEq for PackedNode { fn eq(&self, other: &Self) -> bool { - Rc::as_ptr(&self.node) as *const () == Rc::as_ptr(&other.node) as *const () + std::ptr::eq( + Rc::as_ptr(&self.node) as *const (), + Rc::as_ptr(&other.node) as *const (), + ) } } diff --git a/src/lib.rs b/src/lib.rs index 79b1a8f26..fd1291ed3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,10 @@ //! [cache]: layout::LayoutCache //! [PDF]: export::pdf +#![allow(clippy::len_without_is_empty)] +#![allow(clippy::or_fun_call)] +#![allow(clippy::try_err)] + #[macro_use] pub mod util; #[macro_use] diff --git a/src/library/align.rs b/src/library/align.rs index 16c449053..5f21fa378 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -6,16 +6,14 @@ use super::ParNode; /// `align`: Configure the alignment along the layouting axes. pub fn align(_: &mut EvalContext, args: &mut Args) -> TypResult { let aligns: Spec<_> = args.find().unwrap_or_default(); - let body: Node = args.expect("body")?; + let body: PackedNode = args.expect("body")?; let mut styles = Styles::new(); if let Some(align) = aligns.x { styles.set(ParNode::ALIGN, align); } - Ok(Value::block( - body.into_block().styled(styles).aligned(aligns), - )) + Ok(Value::block(body.styled(styles).aligned(aligns))) } dynamic! { diff --git a/src/library/columns.rs b/src/library/columns.rs index df55fb521..f5d06e563 100644 --- a/src/library/columns.rs +++ b/src/library/columns.rs @@ -5,13 +5,10 @@ use super::ParNode; /// `columns`: Set content into multiple columns. pub fn columns(_: &mut EvalContext, args: &mut Args) -> TypResult { - let columns = args.expect("column count")?; - let gutter = args.named("gutter")?.unwrap_or(Relative::new(0.04).into()); - let body: Node = args.expect("body")?; Ok(Value::block(ColumnsNode { - columns, - gutter, - child: body.into_block(), + columns: args.expect("column count")?, + gutter: args.named("gutter")?.unwrap_or(Relative::new(0.04).into()), + child: args.expect("body")?, })) } diff --git a/src/library/flow.rs b/src/library/flow.rs index 810e566ae..10d570224 100644 --- a/src/library/flow.rs +++ b/src/library/flow.rs @@ -18,7 +18,7 @@ impl Layout for FlowNode { ctx: &mut LayoutContext, regions: &Regions, ) -> Vec>> { - FlowLayouter::new(self, regions).layout(ctx) + FlowLayouter::new(self, regions.clone()).layout(ctx) } } @@ -32,12 +32,12 @@ impl Debug for FlowNode { /// A child of a flow node. #[derive(Hash)] pub enum FlowChild { - /// A paragraph/block break. - Break(Styles), /// Vertical spacing between other children. Spacing(SpacingNode), /// An arbitrary node. Node(PackedNode), + /// A paragraph/block break. + Break(Styles), /// Skip the rest of the region and move to the next. Skip, } @@ -46,9 +46,9 @@ impl FlowChild { /// A reference to the child's styles. pub fn styles(&self) -> Option<&Styles> { match self { - Self::Break(styles) => Some(styles), Self::Spacing(node) => Some(&node.styles), Self::Node(node) => Some(&node.styles), + Self::Break(styles) => Some(styles), Self::Skip => None, } } @@ -56,9 +56,9 @@ impl FlowChild { /// A mutable reference to the child's styles. pub fn styles_mut(&mut self) -> Option<&mut Styles> { match self { - Self::Break(styles) => Some(styles), Self::Spacing(node) => Some(&mut node.styles), Self::Node(node) => Some(&mut node.styles), + Self::Break(styles) => Some(styles), Self::Skip => None, } } @@ -67,14 +67,14 @@ impl FlowChild { impl Debug for FlowChild { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { + Self::Spacing(node) => node.fmt(f), + Self::Node(node) => node.fmt(f), Self::Break(styles) => { if f.alternate() { styles.fmt(f)?; } write!(f, "Break") } - Self::Spacing(node) => node.fmt(f), - Self::Node(node) => node.fmt(f), Self::Skip => f.pad("Skip"), } } @@ -84,10 +84,10 @@ impl Debug for FlowChild { struct FlowLayouter<'a> { /// The flow node to layout. children: &'a [FlowChild], - /// Whether the flow should expand to fill the region. - expand: Spec, /// The regions to layout children into. regions: Regions, + /// Whether the flow should expand to fill the region. + expand: Spec, /// The full size of `regions.current` that was available before we started /// subtracting. full: Size, @@ -115,19 +115,18 @@ enum FlowItem { impl<'a> FlowLayouter<'a> { /// Create a new flow layouter. - fn new(flow: &'a FlowNode, regions: &Regions) -> Self { + fn new(flow: &'a FlowNode, mut regions: Regions) -> Self { let expand = regions.expand; let full = regions.current; // Disable vertical expansion for children. - let mut regions = regions.clone(); regions.expand.y = false; Self { children: &flow.0, + regions, expand, full, - regions, used: Size::zero(), fr: Fractional::zero(), items: vec![], @@ -139,6 +138,16 @@ impl<'a> FlowLayouter<'a> { fn layout(mut self, ctx: &mut LayoutContext) -> Vec>> { for child in self.children { match child { + FlowChild::Spacing(node) => { + self.layout_spacing(node.kind); + } + FlowChild::Node(node) => { + if self.regions.is_full() { + self.finish_region(); + } + + self.layout_node(ctx, node); + } FlowChild::Break(styles) => { let chain = styles.chain(&ctx.styles); let em = chain.get(TextNode::SIZE).abs; @@ -148,20 +157,6 @@ impl<'a> FlowLayouter<'a> { FlowChild::Skip => { self.finish_region(); } - FlowChild::Spacing(node) => match node.kind { - SpacingKind::Linear(v) => self.layout_absolute(v), - SpacingKind::Fractional(v) => { - self.items.push(FlowItem::Fractional(v)); - self.fr += v; - } - }, - FlowChild::Node(node) => { - if self.regions.is_full() { - self.finish_region(); - } - - self.layout_node(ctx, node); - } } } @@ -169,6 +164,17 @@ impl<'a> FlowLayouter<'a> { self.finished } + /// Layout spacing. + fn layout_spacing(&mut self, spacing: SpacingKind) { + match spacing { + SpacingKind::Linear(v) => self.layout_absolute(v), + SpacingKind::Fractional(v) => { + self.items.push(FlowItem::Fractional(v)); + self.fr += v; + } + } + } + /// Layout absolute spacing. fn layout_absolute(&mut self, amount: Linear) { // Resolve the linear, limiting it to the remaining available space. diff --git a/src/library/grid.rs b/src/library/grid.rs index 3292cfe0d..cc636afc9 100644 --- a/src/library/grid.rs +++ b/src/library/grid.rs @@ -15,7 +15,7 @@ pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult { column_gutter.unwrap_or_else(|| base_gutter.clone()), row_gutter.unwrap_or(base_gutter), ), - children: args.all().map(Node::into_block).collect(), + children: args.all().collect(), })) } @@ -87,8 +87,6 @@ castable! { struct GridLayouter<'a> { /// The children of the grid. children: &'a [PackedNode], - /// Whether the grid should expand to fill the region. - expand: Spec, /// The column tracks including gutter tracks. cols: Vec, /// The row tracks including gutter tracks. @@ -97,6 +95,10 @@ struct GridLayouter<'a> { regions: Regions, /// Resolved column sizes. rcols: Vec, + /// Rows in the current region. + lrows: Vec, + /// Whether the grid should expand to fill the region. + expand: Spec, /// The full height of the current region. full: Length, /// The used-up size of the current region. The horizontal size is @@ -104,8 +106,6 @@ struct GridLayouter<'a> { used: Size, /// The sum of fractional ratios in the current region. fr: Fractional, - /// Rows in the current region. - lrows: Vec, /// Constraints for the active region. cts: Constraints, /// Frames for finished regions. @@ -161,22 +161,26 @@ impl<'a> GridLayouter<'a> { cols.pop(); rows.pop(); + let expand = regions.expand; + let full = regions.current.y; + let rcols = vec![Length::zero(); cols.len()]; + let lrows = vec![]; + // We use the regions for auto row measurement. Since at that moment, // columns are already sized, we can enable horizontal expansion. - let expand = regions.expand; regions.expand = Spec::new(true, false); Self { children: &grid.children, - expand, - rcols: vec![Length::zero(); cols.len()], cols, rows, - full: regions.current.y, regions, + rcols, + lrows, + expand, + full, used: Size::zero(), fr: Fractional::zero(), - lrows: vec![], cts: Constraints::new(expand), finished: vec![], } diff --git a/src/library/heading.rs b/src/library/heading.rs index a0b787f34..f2ed3194a 100644 --- a/src/library/heading.rs +++ b/src/library/heading.rs @@ -16,7 +16,7 @@ pub struct HeadingNode { #[properties] impl HeadingNode { /// The heading's font family. - pub const FAMILY: Smart = Smart::Auto; + pub const FAMILY: Smart = Smart::Auto; /// The fill color of heading in the text. Just the surrounding text color /// if `auto`. pub const FILL: Smart = Smart::Auto; @@ -25,7 +25,7 @@ impl HeadingNode { impl Construct for HeadingNode { fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult { Ok(Node::block(Self { - child: args.expect::("body")?.into_block(), + child: args.expect("body")?, level: args.named("level")?.unwrap_or(1), })) } @@ -50,8 +50,9 @@ impl Layout for HeadingNode { ctx.styles.set(TextNode::SIZE, Relative::new(upscale).into()); if let Smart::Custom(family) = ctx.styles.get_ref(Self::FAMILY) { - let list: Vec<_> = std::iter::once(FontFamily::named(family)) - .chain(ctx.styles.get_ref(TextNode::FAMILY_LIST).iter().cloned()) + let list = std::iter::once(family) + .chain(ctx.styles.get_ref(TextNode::FAMILY_LIST)) + .cloned() .collect(); ctx.styles.set(TextNode::FAMILY_LIST, list); } diff --git a/src/library/list.rs b/src/library/list.rs index f407d8e92..765cc22bc 100644 --- a/src/library/list.rs +++ b/src/library/list.rs @@ -26,12 +26,7 @@ impl Construct for ListNode { fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult { Ok(args .all() - .map(|node: Node| { - Node::block(Self { - child: node.into_block(), - labelling: L::default(), - }) - }) + .map(|child: PackedNode| Node::block(Self { child, labelling: L::default() })) .sum()) } } diff --git a/src/library/mod.rs b/src/library/mod.rs index 61d3ccc04..0cdabcb0b 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -193,3 +193,9 @@ castable! { Expected: "string", Value::Str(string) => string.into(), } + +castable! { + PackedNode, + Expected: "node", + Value::Node(node) => node.into_block(), +} diff --git a/src/library/pad.rs b/src/library/pad.rs index 70d88b412..96da7a0a2 100644 --- a/src/library/pad.rs +++ b/src/library/pad.rs @@ -9,7 +9,7 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult { let top = args.named("top")?; let right = args.named("right")?; let bottom = args.named("bottom")?; - let body: Node = args.expect("body")?; + let body: PackedNode = args.expect("body")?; let padding = Sides::new( left.or(all).unwrap_or_default(), top.or(all).unwrap_or_default(), @@ -17,7 +17,7 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult { bottom.or(all).unwrap_or_default(), ); - Ok(Value::block(body.into_block().padded(padding))) + Ok(Value::block(body.padded(padding))) } /// A node that adds padding to its child. diff --git a/src/library/page.rs b/src/library/page.rs index 00f2dfc6a..520cdaef0 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -44,8 +44,8 @@ impl PageNode { impl Construct for PageNode { fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult { Ok(Node::Page(Self { - child: args.expect::("body")?.into_block(), styles: Styles::new(), + child: args.expect("body")?, })) } } @@ -114,6 +114,7 @@ impl PageNode { bottom: ctx.styles.get(Self::BOTTOM).unwrap_or(default.bottom), }; + // Realize columns with columns node. let columns = ctx.styles.get(Self::COLUMNS); let child = if columns.get() > 1 { ColumnsNode { @@ -126,14 +127,14 @@ impl PageNode { self.child.clone() }; - // Pad the child. - let padded = PadNode { child, padding }.pack(); + // Realize margins with padding node. + let child = PadNode { child, padding }.pack(); // Layout the child. let expand = size.map(Length::is_finite); let regions = Regions::repeat(size, size, expand); let mut frames: Vec<_> = - padded.layout(ctx, ®ions).into_iter().map(|c| c.item).collect(); + child.layout(ctx, ®ions).into_iter().map(|c| c.item).collect(); // Add background fill if requested. if let Some(fill) = ctx.styles.get(Self::FILL) { @@ -238,12 +239,12 @@ macro_rules! papers { } impl FromStr for Paper { - type Err = ParsePaperError; + type Err = PaperError; fn from_str(name: &str) -> Result { match name.to_lowercase().as_str() { $($($pats)* => Ok(Self::$var),)* - _ => Err(ParsePaperError), + _ => Err(PaperError), } } } @@ -413,12 +414,12 @@ castable! { /// The error when parsing a [`Paper`] from a string fails. #[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct ParsePaperError; +pub struct PaperError; -impl Display for ParsePaperError { +impl Display for PaperError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.pad("invalid paper name") } } -impl std::error::Error for ParsePaperError {} +impl std::error::Error for PaperError {} diff --git a/src/library/par.rs b/src/library/par.rs index 2c12f9f64..2bedc98b8 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -30,7 +30,7 @@ impl ParNode { impl Construct for ParNode { fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult { // Lift to a block so that it doesn't merge with adjacent stuff. - Ok(Node::Block(args.expect::("body")?.into_block())) + Ok(Node::Block(args.expect("body")?)) } } @@ -60,10 +60,8 @@ impl Set for ParNode { bail!(span, "must be horizontal"); } Some(v) - } else if let Some(dir) = dir { - Some(if dir == Dir::LTR { Align::Left } else { Align::Right }) } else { - None + dir.map(|dir| dir.start().into()) }; styles.set_opt(Self::DIR, dir); @@ -85,7 +83,8 @@ impl Layout for ParNode { let text = self.collect_text(); // Find out the BiDi embedding levels. - let bidi = BidiInfo::new(&text, Level::from_dir(ctx.styles.get(Self::DIR))); + let level = Level::from_dir(ctx.styles.get(Self::DIR)); + let bidi = BidiInfo::new(&text, level); // Prepare paragraph layout by building a representation on which we can // do line breaking without layouting each and every line from scratch. @@ -255,7 +254,7 @@ impl<'a> ParLayouter<'a> { let subrange = start .. cursor; let text = &bidi.text[subrange.clone()]; let styles = node.styles.chain(&ctx.styles); - let shaped = shape(&mut ctx.fonts, text, styles, level.dir()); + let shaped = shape(ctx.fonts, text, styles, level.dir()); items.push(ParItem::Text(shaped)); ranges.push(subrange); } @@ -446,7 +445,7 @@ impl<'a> LineLayout<'a> { // empty string. if !range.is_empty() || rest.is_empty() { // Reshape that part. - let reshaped = shaped.reshape(&mut ctx.fonts, range); + let reshaped = shaped.reshape(ctx.fonts, range); last = Some(ParItem::Text(reshaped)); } @@ -467,7 +466,7 @@ impl<'a> LineLayout<'a> { // Reshape if necessary. if range.len() < shaped.text.len() { if !range.is_empty() { - let reshaped = shaped.reshape(&mut ctx.fonts, range); + let reshaped = shaped.reshape(ctx.fonts, range); first = Some(ParItem::Text(reshaped)); } @@ -531,7 +530,7 @@ impl<'a> LineLayout<'a> { match item { ParItem::Absolute(v) => offset += *v, ParItem::Fractional(v) => offset += v.resolve(self.fr, remaining), - ParItem::Text(shaped) => position(shaped.build(&ctx.fonts)), + ParItem::Text(shaped) => position(shaped.build(ctx.fonts)), ParItem::Frame(frame) => position(frame.clone()), } } diff --git a/src/library/placed.rs b/src/library/placed.rs index bdf3e2cd3..5d98edc4d 100644 --- a/src/library/placed.rs +++ b/src/library/placed.rs @@ -8,9 +8,9 @@ pub fn place(_: &mut EvalContext, args: &mut Args) -> TypResult { let aligns = args.find().unwrap_or(Spec::new(Some(Align::Left), None)); let tx = args.named("dx")?.unwrap_or_default(); let ty = args.named("dy")?.unwrap_or_default(); - let body: Node = args.expect("body")?; + let body: PackedNode = args.expect("body")?; Ok(Value::block(PlacedNode( - body.into_block().moved(Point::new(tx, ty)).aligned(aligns), + body.moved(Point::new(tx, ty)).aligned(aligns), ))) } diff --git a/src/library/shape.rs b/src/library/shape.rs index 3eb1a794a..15a857ef6 100644 --- a/src/library/shape.rs +++ b/src/library/shape.rs @@ -79,9 +79,7 @@ fn shape_impl( } // The shape's contents. - let child = args - .find() - .map(|body: Node| body.into_block().padded(Sides::splat(padding))); + let child = args.find().map(|body: PackedNode| body.padded(Sides::splat(padding))); Ok(Value::inline( ShapeNode { kind, fill, stroke, child } diff --git a/src/library/sized.rs b/src/library/sized.rs index 49b515cfb..81ca69c2e 100644 --- a/src/library/sized.rs +++ b/src/library/sized.rs @@ -6,16 +6,14 @@ use super::prelude::*; pub fn box_(_: &mut EvalContext, args: &mut Args) -> TypResult { let width = args.named("width")?; let height = args.named("height")?; - let body: Node = args.find().unwrap_or_default(); - Ok(Value::inline( - body.into_block().sized(Spec::new(width, height)), - )) + let body: PackedNode = args.find().unwrap_or_default(); + Ok(Value::inline(body.sized(Spec::new(width, height)))) } /// `block`: Place content into the flow. pub fn block(_: &mut EvalContext, args: &mut Args) -> TypResult { - let body: Node = args.find().unwrap_or_default(); - Ok(Value::block(body.into_block())) + let body: PackedNode = args.find().unwrap_or_default(); + Ok(Value::block(body)) } /// A node that sizes its child. diff --git a/src/library/stack.rs b/src/library/stack.rs index dfe5e2455..d7748378c 100644 --- a/src/library/stack.rs +++ b/src/library/stack.rs @@ -1,33 +1,15 @@ //! Side-by-side layout of nodes along an axis. -use std::fmt::{self, Debug, Formatter}; - use super::prelude::*; use super::{AlignNode, SpacingKind, SpacingNode}; /// `stack`: Stack children along an axis. pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult { - let dir = args.named("dir")?.unwrap_or(Dir::TTB); - let spacing = args.named("spacing")?; - - let mut children = vec![]; - let mut deferred = None; - - // Build the list of stack children. - for child in args.all() { - match child { - StackChild::Spacing(_) => deferred = None, - StackChild::Node(_) => { - if let Some(v) = deferred { - children.push(StackChild::spacing(v)); - } - deferred = spacing; - } - } - children.push(child); - } - - Ok(Value::block(StackNode { dir, children })) + Ok(Value::block(StackNode { + dir: args.named("dir")?.unwrap_or(Dir::TTB), + spacing: args.named("spacing")?, + children: args.all().collect(), + })) } /// A node that stacks its children. @@ -35,6 +17,8 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult { pub struct StackNode { /// The stacking direction. pub dir: Dir, + /// The spacing between non-spacing children. + pub spacing: Option, /// The children to be stacked. pub children: Vec, } @@ -45,7 +29,7 @@ impl Layout for StackNode { ctx: &mut LayoutContext, regions: &Regions, ) -> Vec>> { - StackLayouter::new(self, regions).layout(ctx) + StackLayouter::new(self, regions.clone()).layout(ctx) } } @@ -58,9 +42,8 @@ pub enum StackChild { Node(PackedNode), } -impl StackChild { - /// Create a spacing node from a spacing kind. - pub fn spacing(kind: SpacingKind) -> Self { +impl From for StackChild { + fn from(kind: SpacingKind) -> Self { Self::Spacing(SpacingNode { kind, styles: Styles::new() }) } } @@ -77,23 +60,27 @@ impl Debug for StackChild { castable! { StackChild, Expected: "linear, fractional or template", - Value::Length(v) => Self::spacing(SpacingKind::Linear(v.into())), - Value::Relative(v) => Self::spacing(SpacingKind::Linear(v.into())), - Value::Linear(v) => Self::spacing(SpacingKind::Linear(v)), - Value::Fractional(v) => Self::spacing(SpacingKind::Fractional(v)), + Value::Length(v) => SpacingKind::Linear(v.into()).into(), + Value::Relative(v) => SpacingKind::Linear(v.into()).into(), + Value::Linear(v) => SpacingKind::Linear(v).into(), + Value::Fractional(v) => SpacingKind::Fractional(v).into(), Value::Node(v) => Self::Node(v.into_block()), } /// Performs stack layout. struct StackLayouter<'a> { - /// The stack node to layout. - stack: &'a StackNode, - /// The axis of the block direction. + /// The flow node to layout. + children: &'a [StackChild], + /// The stacking direction. + dir: Dir, + /// The axis of the stacking direction. axis: SpecAxis, - /// Whether the stack should expand to fill the region. - expand: Spec, + /// The spacing between non-spacing children. + spacing: Option, /// The regions to layout children into. regions: Regions, + /// Whether the stack should expand to fill the region. + expand: Spec, /// The full size of `regions.current` that was available before we started /// subtracting. full: Size, @@ -119,21 +106,23 @@ enum StackItem { impl<'a> StackLayouter<'a> { /// Create a new stack layouter. - fn new(stack: &'a StackNode, regions: &Regions) -> Self { - let axis = stack.dir.axis(); + fn new(stack: &'a StackNode, mut regions: Regions) -> Self { + let dir = stack.dir; + let axis = dir.axis(); let expand = regions.expand; let full = regions.current; // Disable expansion along the block axis for children. - let mut regions = regions.clone(); regions.expand.set(axis, false); Self { - stack, + children: &stack.children, + dir, axis, + spacing: stack.spacing, + regions, expand, full, - regions, used: Gen::zero(), fr: Fractional::zero(), items: vec![], @@ -143,21 +132,26 @@ impl<'a> StackLayouter<'a> { /// Layout all children. fn layout(mut self, ctx: &mut LayoutContext) -> Vec>> { - for child in &self.stack.children { + // Spacing to insert before the next node. + let mut deferred = None; + + for child in self.children { match child { - StackChild::Spacing(node) => match node.kind { - SpacingKind::Linear(v) => self.layout_absolute(v), - SpacingKind::Fractional(v) => { - self.items.push(StackItem::Fractional(v)); - self.fr += v; - } - }, + StackChild::Spacing(node) => { + self.layout_spacing(node.kind); + deferred = None; + } StackChild::Node(node) => { + if let Some(kind) = deferred { + self.layout_spacing(kind); + } + if self.regions.is_full() { self.finish_region(); } self.layout_node(ctx, node); + deferred = self.spacing; } } } @@ -166,6 +160,17 @@ impl<'a> StackLayouter<'a> { self.finished } + /// Layout spacing. + fn layout_spacing(&mut self, spacing: SpacingKind) { + match spacing { + SpacingKind::Linear(v) => self.layout_absolute(v), + SpacingKind::Fractional(v) => { + self.items.push(StackItem::Fractional(v)); + self.fr += v; + } + } + } + /// Layout absolute spacing. fn layout_absolute(&mut self, amount: Linear) { // Resolve the linear, limiting it to the remaining available space. @@ -183,7 +188,7 @@ impl<'a> StackLayouter<'a> { let align = node .downcast::() .and_then(|node| node.aligns.get(self.axis)) - .unwrap_or(self.stack.dir.start().into()); + .unwrap_or(self.dir.start().into()); let frames = node.layout(ctx, &self.regions); let len = frames.len(); @@ -218,7 +223,7 @@ impl<'a> StackLayouter<'a> { let mut output = Frame::new(size); let mut cursor = Length::zero(); - let mut ruler: Align = self.stack.dir.start().into(); + let mut ruler: Align = self.dir.start().into(); // Place all frames. for item in self.items.drain(..) { @@ -230,7 +235,7 @@ impl<'a> StackLayouter<'a> { cursor += v.resolve(self.fr, remaining); } StackItem::Frame(frame, align) => { - if self.stack.dir.is_positive() { + if self.dir.is_positive() { ruler = ruler.max(align); } else { ruler = ruler.min(align); @@ -240,7 +245,7 @@ impl<'a> StackLayouter<'a> { let parent = size.get(self.axis); let child = frame.size.get(self.axis); let block = ruler.resolve(parent - self.used.main) - + if self.stack.dir.is_positive() { + + if self.dir.is_positive() { cursor } else { self.used.main - child - cursor diff --git a/src/library/text.rs b/src/library/text.rs index df71fec9e..26e949fc9 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -173,13 +173,6 @@ pub enum FontFamily { Named(NamedFamily), } -impl FontFamily { - /// Create a named font family variant, directly from a string. - pub fn named(string: &str) -> Self { - Self::Named(NamedFamily::new(string)) - } -} - impl Debug for FontFamily { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { @@ -193,13 +186,13 @@ impl Debug for FontFamily { dynamic! { FontFamily: "font family", - Value::Str(string) => Self::named(&string), + Value::Str(string) => Self::Named(NamedFamily::new(&string)), } castable! { Vec, Expected: "string, generic family or array thereof", - Value::Str(string) => vec![FontFamily::named(&string)], + Value::Str(string) => vec![FontFamily::Named(NamedFamily::new(&string))], Value::Array(values) => { values.into_iter().filter_map(|v| v.cast().ok()).collect() }, diff --git a/src/library/transform.rs b/src/library/transform.rs index ef468d7b4..03f0a84c9 100644 --- a/src/library/transform.rs +++ b/src/library/transform.rs @@ -28,15 +28,13 @@ pub fn rotate(_: &mut EvalContext, args: &mut Args) -> TypResult { } fn transform_impl(args: &mut Args, transform: Transform) -> TypResult { - let body: Node = args.expect("body")?; + let body: PackedNode = args.expect("body")?; let origin = args .named("origin")? .unwrap_or(Spec::splat(None)) .unwrap_or(Align::CENTER_HORIZON); - Ok(Value::inline( - body.into_block().transformed(transform, origin), - )) + Ok(Value::inline(body.transformed(transform, origin))) } /// A node that transforms its child without affecting layout. diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 0a2f73f5b..10aaad234 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -275,13 +275,13 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult { Some(NodeKind::Error(_, _)) => { p.eat(); - Err(()) + Err(ParseError) } // Nothing. _ => { p.expected("expression"); - Err(()) + Err(ParseError) } } } @@ -428,7 +428,7 @@ fn item(p: &mut Parser) -> ParseResult { marker.end(p, error); p.eat(); expr(p).ok(); - Err(()) + Err(ParseError) } })?; @@ -519,7 +519,7 @@ fn args(p: &mut Parser, direct: bool, brackets: bool) -> ParseResult { Some(NodeKind::LeftBracket) if brackets => {} _ => { p.expected("argument list"); - return Err(()); + return Err(ParseError); } } @@ -689,7 +689,7 @@ fn ident(p: &mut Parser) -> ParseResult { } _ => { p.expected("identifier"); - Err(()) + Err(ParseError) } } } @@ -701,7 +701,7 @@ fn body(p: &mut Parser) -> ParseResult { Some(NodeKind::LeftBrace) => block(p), _ => { p.expected_at("body"); - return Err(()); + return Err(ParseError); } } Ok(()) diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 503158a94..af8a7c5ca 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -1,13 +1,10 @@ +use std::fmt::{self, Display, Formatter}; use std::mem; use super::{TokenMode, Tokens}; use crate::syntax::{ErrorPos, Green, GreenData, GreenNode, NodeKind}; use crate::util::EcoString; -/// Allows parser methods to use the try operator. Not exposed as the parser -/// recovers from all errors. -pub(crate) type ParseResult = Result; - /// A convenient token-based parser. pub struct Parser<'s> { /// An iterator over the source tokens. @@ -121,7 +118,7 @@ impl<'s> Parser<'s> { if !eaten { self.expected_at(t.as_str()); } - if eaten { Ok(()) } else { Err(()) } + if eaten { Ok(()) } else { Err(ParseError) } } /// Eat, debug-asserting that the token is the given one. @@ -448,3 +445,19 @@ pub enum Group { /// A group for import items, ended by a semicolon, line break or `from`. Imports, } + +/// Allows parser methods to use the try operator. Never returned top-level +/// because the parser recovers from all errors. +pub type ParseResult = Result; + +/// The error type for parsing. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct ParseError; + +impl Display for ParseError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.pad("failed to parse") + } +} + +impl std::error::Error for ParseError {} diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 62ecb8cd4..e74829b0c 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -21,6 +21,7 @@ pub trait Pretty { } /// A buffer into which items can be pretty printed. +#[derive(Default)] pub struct Printer { buf: String, } @@ -28,7 +29,7 @@ pub struct Printer { impl Printer { /// Create a new pretty printer. pub fn new() -> Self { - Self { buf: String::new() } + Self::default() } /// Push a character into the buffer.