Make clippy a bit happier

This commit is contained in:
Laurenz 2021-12-30 11:37:11 +01:00
parent fef5502517
commit f5dcb84e36
28 changed files with 210 additions and 198 deletions

View File

@ -372,13 +372,13 @@ impl Packer {
return; return;
} }
if !self.par.styles.compatible(&styles, ParNode::has_property) { if !self.par.styles.compatible(styles, ParNode::has_property) {
self.parbreak(None); self.parbreak(None);
self.par.styles = styles.clone(); self.par.styles = styles.clone();
return; return;
} }
self.par.styles.intersect(&styles); self.par.styles.intersect(styles);
} }
/// Break to a new page if the `styles` contain page styles that are /// Break to a new page if the `styles` contain page styles that are
@ -389,7 +389,7 @@ impl Packer {
return; 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.pagebreak();
self.flow.styles = styles.clone(); self.flow.styles = styles.clone();
return; return;

View File

@ -390,7 +390,7 @@ impl<'a> PageExporter<'a> {
// Make the coordinate system start at the top-left. // Make the coordinate system start at the top-left.
self.bottom = frame.size.y.to_f32(); self.bottom = frame.size.y.to_f32();
self.content.transform([1.0, 0.0, 0.0, -1.0, 0.0, self.bottom]); self.content.transform([1.0, 0.0, 0.0, -1.0, 0.0, self.bottom]);
self.write_frame(&frame); self.write_frame(frame);
Page { Page {
size: frame.size, size: frame.size,
content: self.content, content: self.content,

View File

@ -656,7 +656,7 @@ mod cff {
if count > 0 { if count > 0 {
let offsize = usize::from(s.read::<u8>()?); let offsize = usize::from(s.read::<u8>()?);
if offsize < 1 || offsize > 4 { if !matches!(offsize, 1 ..= 4) {
return None; return None;
} }

View File

@ -72,7 +72,7 @@ impl RgbaColor {
} }
impl FromStr for RgbaColor { impl FromStr for RgbaColor {
type Err = ParseRgbaError; type Err = RgbaError;
/// Constructs a new color from hex strings like the following: /// Constructs a new color from hex strings like the following:
/// - `#aef` (shorthand, with leading hashtag), /// - `#aef` (shorthand, with leading hashtag),
@ -83,7 +83,7 @@ impl FromStr for RgbaColor {
fn from_str(hex_str: &str) -> Result<Self, Self::Err> { fn from_str(hex_str: &str) -> Result<Self, Self::Err> {
let hex_str = hex_str.strip_prefix('#').unwrap_or(hex_str); let hex_str = hex_str.strip_prefix('#').unwrap_or(hex_str);
if !hex_str.is_ascii() { if !hex_str.is_ascii() {
return Err(ParseRgbaError); return Err(RgbaError);
} }
let len = hex_str.len(); let len = hex_str.len();
@ -92,7 +92,7 @@ impl FromStr for RgbaColor {
let alpha = len == 4 || len == 8; let alpha = len == 4 || len == 8;
if !long && !short { if !long && !short {
return Err(ParseRgbaError); return Err(RgbaError);
} }
let mut values: [u8; 4] = [255; 4]; let mut values: [u8; 4] = [255; 4];
@ -102,7 +102,7 @@ impl FromStr for RgbaColor {
let pos = elem * item_len; let pos = elem * item_len;
let item = &hex_str[pos .. (pos + 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 { if short {
// Duplicate number for shorthand notation, i.e. `a` -> `aa` // 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. /// The error when parsing an [`RgbaColor`] from a string fails.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[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 { 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)] #[cfg(test)]
mod tests { mod tests {
@ -166,7 +166,7 @@ mod tests {
fn test_parse_invalid_colors() { fn test_parse_invalid_colors() {
#[track_caller] #[track_caller]
fn test(hex: &str) { fn test(hex: &str) {
assert_eq!(RgbaColor::from_str(hex), Err(ParseRgbaError)); assert_eq!(RgbaColor::from_str(hex), Err(RgbaError));
} }
test("12345"); test("12345");

View File

@ -27,7 +27,7 @@ impl Debug for Scalar {
impl Ord for Scalar { impl Ord for Scalar {
fn cmp(&self, other: &Self) -> Ordering { fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(&other).expect("float is NaN") self.partial_cmp(other).expect("float is NaN")
} }
} }

View File

@ -67,7 +67,7 @@ impl ImageStore {
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
let buffer = self.loader.load(path)?; let buffer = self.loader.load(path)?;
let ext = path.extension().and_then(OsStr::to_str).unwrap_or_default(); 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); let id = ImageId(self.images.len() as u32);
if let Some(callback) = &self.on_load { if let Some(callback) = &self.on_load {
callback(id, &image); callback(id, &image);

View File

@ -377,22 +377,14 @@ pub struct PatternProperties {
impl PatternProperties { impl PatternProperties {
/// Check if it is vital to keep an entry based on its properties. /// Check if it is vital to keep an entry based on its properties.
pub fn must_keep(&self) -> bool { pub fn must_keep(&self) -> bool {
if self.top_level && !self.mature {
// Keep an undo stack. // Keep an undo stack.
true (self.top_level && !self.mature)
} else if self.all_zeros && !self.mature {
// Keep the most recently created items, even if they have not yet // Keep the most recently created items, even if they have not yet
// been used. // been used.
true || (self.all_zeros && !self.mature)
} else if self.multi_use && !self.abandoned { || (self.multi_use && !self.abandoned)
true || self.hit
} else if self.hit { || self.sparse
true
} else if self.sparse {
true
} else {
false
}
} }
} }

View File

@ -282,7 +282,10 @@ impl Debug for PackedNode {
impl PartialEq for PackedNode { impl PartialEq for PackedNode {
fn eq(&self, other: &Self) -> bool { 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 (),
)
} }
} }

View File

@ -30,6 +30,10 @@
//! [cache]: layout::LayoutCache //! [cache]: layout::LayoutCache
//! [PDF]: export::pdf //! [PDF]: export::pdf
#![allow(clippy::len_without_is_empty)]
#![allow(clippy::or_fun_call)]
#![allow(clippy::try_err)]
#[macro_use] #[macro_use]
pub mod util; pub mod util;
#[macro_use] #[macro_use]

View File

@ -6,16 +6,14 @@ use super::ParNode;
/// `align`: Configure the alignment along the layouting axes. /// `align`: Configure the alignment along the layouting axes.
pub fn align(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { pub fn align(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let aligns: Spec<_> = args.find().unwrap_or_default(); 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(); let mut styles = Styles::new();
if let Some(align) = aligns.x { if let Some(align) = aligns.x {
styles.set(ParNode::ALIGN, align); styles.set(ParNode::ALIGN, align);
} }
Ok(Value::block( Ok(Value::block(body.styled(styles).aligned(aligns)))
body.into_block().styled(styles).aligned(aligns),
))
} }
dynamic! { dynamic! {

View File

@ -5,13 +5,10 @@ use super::ParNode;
/// `columns`: Set content into multiple columns. /// `columns`: Set content into multiple columns.
pub fn columns(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { pub fn columns(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
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 { Ok(Value::block(ColumnsNode {
columns, columns: args.expect("column count")?,
gutter, gutter: args.named("gutter")?.unwrap_or(Relative::new(0.04).into()),
child: body.into_block(), child: args.expect("body")?,
})) }))
} }

View File

@ -18,7 +18,7 @@ impl Layout for FlowNode {
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
regions: &Regions, regions: &Regions,
) -> Vec<Constrained<Rc<Frame>>> { ) -> Vec<Constrained<Rc<Frame>>> {
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. /// A child of a flow node.
#[derive(Hash)] #[derive(Hash)]
pub enum FlowChild { pub enum FlowChild {
/// A paragraph/block break.
Break(Styles),
/// Vertical spacing between other children. /// Vertical spacing between other children.
Spacing(SpacingNode), Spacing(SpacingNode),
/// An arbitrary node. /// An arbitrary node.
Node(PackedNode), Node(PackedNode),
/// A paragraph/block break.
Break(Styles),
/// Skip the rest of the region and move to the next. /// Skip the rest of the region and move to the next.
Skip, Skip,
} }
@ -46,9 +46,9 @@ impl FlowChild {
/// A reference to the child's styles. /// A reference to the child's styles.
pub fn styles(&self) -> Option<&Styles> { pub fn styles(&self) -> Option<&Styles> {
match self { match self {
Self::Break(styles) => Some(styles),
Self::Spacing(node) => Some(&node.styles), Self::Spacing(node) => Some(&node.styles),
Self::Node(node) => Some(&node.styles), Self::Node(node) => Some(&node.styles),
Self::Break(styles) => Some(styles),
Self::Skip => None, Self::Skip => None,
} }
} }
@ -56,9 +56,9 @@ impl FlowChild {
/// A mutable reference to the child's styles. /// A mutable reference to the child's styles.
pub fn styles_mut(&mut self) -> Option<&mut Styles> { pub fn styles_mut(&mut self) -> Option<&mut Styles> {
match self { match self {
Self::Break(styles) => Some(styles),
Self::Spacing(node) => Some(&mut node.styles), Self::Spacing(node) => Some(&mut node.styles),
Self::Node(node) => Some(&mut node.styles), Self::Node(node) => Some(&mut node.styles),
Self::Break(styles) => Some(styles),
Self::Skip => None, Self::Skip => None,
} }
} }
@ -67,14 +67,14 @@ impl FlowChild {
impl Debug for FlowChild { impl Debug for FlowChild {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Self::Spacing(node) => node.fmt(f),
Self::Node(node) => node.fmt(f),
Self::Break(styles) => { Self::Break(styles) => {
if f.alternate() { if f.alternate() {
styles.fmt(f)?; styles.fmt(f)?;
} }
write!(f, "Break") write!(f, "Break")
} }
Self::Spacing(node) => node.fmt(f),
Self::Node(node) => node.fmt(f),
Self::Skip => f.pad("Skip"), Self::Skip => f.pad("Skip"),
} }
} }
@ -84,10 +84,10 @@ impl Debug for FlowChild {
struct FlowLayouter<'a> { struct FlowLayouter<'a> {
/// The flow node to layout. /// The flow node to layout.
children: &'a [FlowChild], children: &'a [FlowChild],
/// Whether the flow should expand to fill the region.
expand: Spec<bool>,
/// The regions to layout children into. /// The regions to layout children into.
regions: Regions, regions: Regions,
/// Whether the flow should expand to fill the region.
expand: Spec<bool>,
/// The full size of `regions.current` that was available before we started /// The full size of `regions.current` that was available before we started
/// subtracting. /// subtracting.
full: Size, full: Size,
@ -115,19 +115,18 @@ enum FlowItem {
impl<'a> FlowLayouter<'a> { impl<'a> FlowLayouter<'a> {
/// Create a new flow layouter. /// 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 expand = regions.expand;
let full = regions.current; let full = regions.current;
// Disable vertical expansion for children. // Disable vertical expansion for children.
let mut regions = regions.clone();
regions.expand.y = false; regions.expand.y = false;
Self { Self {
children: &flow.0, children: &flow.0,
regions,
expand, expand,
full, full,
regions,
used: Size::zero(), used: Size::zero(),
fr: Fractional::zero(), fr: Fractional::zero(),
items: vec![], items: vec![],
@ -139,6 +138,16 @@ impl<'a> FlowLayouter<'a> {
fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> { fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> {
for child in self.children { for child in self.children {
match child { 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) => { FlowChild::Break(styles) => {
let chain = styles.chain(&ctx.styles); let chain = styles.chain(&ctx.styles);
let em = chain.get(TextNode::SIZE).abs; let em = chain.get(TextNode::SIZE).abs;
@ -148,20 +157,6 @@ impl<'a> FlowLayouter<'a> {
FlowChild::Skip => { FlowChild::Skip => {
self.finish_region(); 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 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. /// Layout absolute spacing.
fn layout_absolute(&mut self, amount: Linear) { fn layout_absolute(&mut self, amount: Linear) {
// Resolve the linear, limiting it to the remaining available space. // Resolve the linear, limiting it to the remaining available space.

View File

@ -15,7 +15,7 @@ pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
column_gutter.unwrap_or_else(|| base_gutter.clone()), column_gutter.unwrap_or_else(|| base_gutter.clone()),
row_gutter.unwrap_or(base_gutter), 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> { struct GridLayouter<'a> {
/// The children of the grid. /// The children of the grid.
children: &'a [PackedNode], children: &'a [PackedNode],
/// Whether the grid should expand to fill the region.
expand: Spec<bool>,
/// The column tracks including gutter tracks. /// The column tracks including gutter tracks.
cols: Vec<TrackSizing>, cols: Vec<TrackSizing>,
/// The row tracks including gutter tracks. /// The row tracks including gutter tracks.
@ -97,6 +95,10 @@ struct GridLayouter<'a> {
regions: Regions, regions: Regions,
/// Resolved column sizes. /// Resolved column sizes.
rcols: Vec<Length>, rcols: Vec<Length>,
/// Rows in the current region.
lrows: Vec<Row>,
/// Whether the grid should expand to fill the region.
expand: Spec<bool>,
/// The full height of the current region. /// The full height of the current region.
full: Length, full: Length,
/// The used-up size of the current region. The horizontal size is /// The used-up size of the current region. The horizontal size is
@ -104,8 +106,6 @@ struct GridLayouter<'a> {
used: Size, used: Size,
/// The sum of fractional ratios in the current region. /// The sum of fractional ratios in the current region.
fr: Fractional, fr: Fractional,
/// Rows in the current region.
lrows: Vec<Row>,
/// Constraints for the active region. /// Constraints for the active region.
cts: Constraints, cts: Constraints,
/// Frames for finished regions. /// Frames for finished regions.
@ -161,22 +161,26 @@ impl<'a> GridLayouter<'a> {
cols.pop(); cols.pop();
rows.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, // We use the regions for auto row measurement. Since at that moment,
// columns are already sized, we can enable horizontal expansion. // columns are already sized, we can enable horizontal expansion.
let expand = regions.expand;
regions.expand = Spec::new(true, false); regions.expand = Spec::new(true, false);
Self { Self {
children: &grid.children, children: &grid.children,
expand,
rcols: vec![Length::zero(); cols.len()],
cols, cols,
rows, rows,
full: regions.current.y,
regions, regions,
rcols,
lrows,
expand,
full,
used: Size::zero(), used: Size::zero(),
fr: Fractional::zero(), fr: Fractional::zero(),
lrows: vec![],
cts: Constraints::new(expand), cts: Constraints::new(expand),
finished: vec![], finished: vec![],
} }

View File

@ -16,7 +16,7 @@ pub struct HeadingNode {
#[properties] #[properties]
impl HeadingNode { impl HeadingNode {
/// The heading's font family. /// The heading's font family.
pub const FAMILY: Smart<String> = Smart::Auto; pub const FAMILY: Smart<FontFamily> = Smart::Auto;
/// The fill color of heading in the text. Just the surrounding text color /// The fill color of heading in the text. Just the surrounding text color
/// if `auto`. /// if `auto`.
pub const FILL: Smart<Paint> = Smart::Auto; pub const FILL: Smart<Paint> = Smart::Auto;
@ -25,7 +25,7 @@ impl HeadingNode {
impl Construct for HeadingNode { impl Construct for HeadingNode {
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> { fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> {
Ok(Node::block(Self { Ok(Node::block(Self {
child: args.expect::<Node>("body")?.into_block(), child: args.expect("body")?,
level: args.named("level")?.unwrap_or(1), level: args.named("level")?.unwrap_or(1),
})) }))
} }
@ -50,8 +50,9 @@ impl Layout for HeadingNode {
ctx.styles.set(TextNode::SIZE, Relative::new(upscale).into()); ctx.styles.set(TextNode::SIZE, Relative::new(upscale).into());
if let Smart::Custom(family) = ctx.styles.get_ref(Self::FAMILY) { if let Smart::Custom(family) = ctx.styles.get_ref(Self::FAMILY) {
let list: Vec<_> = std::iter::once(FontFamily::named(family)) let list = std::iter::once(family)
.chain(ctx.styles.get_ref(TextNode::FAMILY_LIST).iter().cloned()) .chain(ctx.styles.get_ref(TextNode::FAMILY_LIST))
.cloned()
.collect(); .collect();
ctx.styles.set(TextNode::FAMILY_LIST, list); ctx.styles.set(TextNode::FAMILY_LIST, list);
} }

View File

@ -26,12 +26,7 @@ impl<L: Labelling> Construct for ListNode<L> {
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> { fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> {
Ok(args Ok(args
.all() .all()
.map(|node: Node| { .map(|child: PackedNode| Node::block(Self { child, labelling: L::default() }))
Node::block(Self {
child: node.into_block(),
labelling: L::default(),
})
})
.sum()) .sum())
} }
} }

View File

@ -193,3 +193,9 @@ castable! {
Expected: "string", Expected: "string",
Value::Str(string) => string.into(), Value::Str(string) => string.into(),
} }
castable! {
PackedNode,
Expected: "node",
Value::Node(node) => node.into_block(),
}

View File

@ -9,7 +9,7 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let top = args.named("top")?; let top = args.named("top")?;
let right = args.named("right")?; let right = args.named("right")?;
let bottom = args.named("bottom")?; let bottom = args.named("bottom")?;
let body: Node = args.expect("body")?; let body: PackedNode = args.expect("body")?;
let padding = Sides::new( let padding = Sides::new(
left.or(all).unwrap_or_default(), left.or(all).unwrap_or_default(),
top.or(all).unwrap_or_default(), top.or(all).unwrap_or_default(),
@ -17,7 +17,7 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
bottom.or(all).unwrap_or_default(), 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. /// A node that adds padding to its child.

View File

@ -44,8 +44,8 @@ impl PageNode {
impl Construct for PageNode { impl Construct for PageNode {
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> { fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> {
Ok(Node::Page(Self { Ok(Node::Page(Self {
child: args.expect::<Node>("body")?.into_block(),
styles: Styles::new(), styles: Styles::new(),
child: args.expect("body")?,
})) }))
} }
} }
@ -114,6 +114,7 @@ impl PageNode {
bottom: ctx.styles.get(Self::BOTTOM).unwrap_or(default.bottom), bottom: ctx.styles.get(Self::BOTTOM).unwrap_or(default.bottom),
}; };
// Realize columns with columns node.
let columns = ctx.styles.get(Self::COLUMNS); let columns = ctx.styles.get(Self::COLUMNS);
let child = if columns.get() > 1 { let child = if columns.get() > 1 {
ColumnsNode { ColumnsNode {
@ -126,14 +127,14 @@ impl PageNode {
self.child.clone() self.child.clone()
}; };
// Pad the child. // Realize margins with padding node.
let padded = PadNode { child, padding }.pack(); let child = PadNode { child, padding }.pack();
// 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);
let mut frames: Vec<_> = let mut frames: Vec<_> =
padded.layout(ctx, &regions).into_iter().map(|c| c.item).collect(); child.layout(ctx, &regions).into_iter().map(|c| c.item).collect();
// Add background fill if requested. // Add background fill if requested.
if let Some(fill) = ctx.styles.get(Self::FILL) { if let Some(fill) = ctx.styles.get(Self::FILL) {
@ -238,12 +239,12 @@ macro_rules! papers {
} }
impl FromStr for Paper { impl FromStr for Paper {
type Err = ParsePaperError; type Err = PaperError;
fn from_str(name: &str) -> Result<Self, Self::Err> { fn from_str(name: &str) -> Result<Self, Self::Err> {
match name.to_lowercase().as_str() { match name.to_lowercase().as_str() {
$($($pats)* => Ok(Self::$var),)* $($($pats)* => Ok(Self::$var),)*
_ => Err(ParsePaperError), _ => Err(PaperError),
} }
} }
} }
@ -413,12 +414,12 @@ castable! {
/// The error when parsing a [`Paper`] from a string fails. /// The error when parsing a [`Paper`] from a string fails.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[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 { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad("invalid paper name") f.pad("invalid paper name")
} }
} }
impl std::error::Error for ParsePaperError {} impl std::error::Error for PaperError {}

View File

@ -30,7 +30,7 @@ impl ParNode {
impl Construct for ParNode { impl Construct for ParNode {
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> { fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Node> {
// Lift to a block so that it doesn't merge with adjacent stuff. // Lift to a block so that it doesn't merge with adjacent stuff.
Ok(Node::Block(args.expect::<Node>("body")?.into_block())) Ok(Node::Block(args.expect("body")?))
} }
} }
@ -60,10 +60,8 @@ impl Set for ParNode {
bail!(span, "must be horizontal"); bail!(span, "must be horizontal");
} }
Some(v) Some(v)
} else if let Some(dir) = dir {
Some(if dir == Dir::LTR { Align::Left } else { Align::Right })
} else { } else {
None dir.map(|dir| dir.start().into())
}; };
styles.set_opt(Self::DIR, dir); styles.set_opt(Self::DIR, dir);
@ -85,7 +83,8 @@ impl Layout for ParNode {
let text = self.collect_text(); let text = self.collect_text();
// Find out the BiDi embedding levels. // 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 // 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.
@ -255,7 +254,7 @@ impl<'a> ParLayouter<'a> {
let subrange = start .. cursor; let subrange = start .. cursor;
let text = &bidi.text[subrange.clone()]; let text = &bidi.text[subrange.clone()];
let styles = node.styles.chain(&ctx.styles); 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)); items.push(ParItem::Text(shaped));
ranges.push(subrange); ranges.push(subrange);
} }
@ -446,7 +445,7 @@ impl<'a> LineLayout<'a> {
// empty string. // empty string.
if !range.is_empty() || rest.is_empty() { if !range.is_empty() || rest.is_empty() {
// Reshape that part. // Reshape that part.
let reshaped = shaped.reshape(&mut ctx.fonts, range); let reshaped = shaped.reshape(ctx.fonts, range);
last = Some(ParItem::Text(reshaped)); last = Some(ParItem::Text(reshaped));
} }
@ -467,7 +466,7 @@ impl<'a> LineLayout<'a> {
// Reshape if necessary. // Reshape if necessary.
if range.len() < shaped.text.len() { if range.len() < shaped.text.len() {
if !range.is_empty() { if !range.is_empty() {
let reshaped = shaped.reshape(&mut ctx.fonts, range); let reshaped = shaped.reshape(ctx.fonts, range);
first = Some(ParItem::Text(reshaped)); first = Some(ParItem::Text(reshaped));
} }
@ -531,7 +530,7 @@ impl<'a> LineLayout<'a> {
match item { match item {
ParItem::Absolute(v) => offset += *v, ParItem::Absolute(v) => offset += *v,
ParItem::Fractional(v) => offset += v.resolve(self.fr, remaining), 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()), ParItem::Frame(frame) => position(frame.clone()),
} }
} }

View File

@ -8,9 +8,9 @@ pub fn place(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let aligns = args.find().unwrap_or(Spec::new(Some(Align::Left), None)); let aligns = args.find().unwrap_or(Spec::new(Some(Align::Left), None));
let tx = args.named("dx")?.unwrap_or_default(); let tx = args.named("dx")?.unwrap_or_default();
let ty = args.named("dy")?.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( Ok(Value::block(PlacedNode(
body.into_block().moved(Point::new(tx, ty)).aligned(aligns), body.moved(Point::new(tx, ty)).aligned(aligns),
))) )))
} }

View File

@ -79,9 +79,7 @@ fn shape_impl(
} }
// The shape's contents. // The shape's contents.
let child = args let child = args.find().map(|body: PackedNode| body.padded(Sides::splat(padding)));
.find()
.map(|body: Node| body.into_block().padded(Sides::splat(padding)));
Ok(Value::inline( Ok(Value::inline(
ShapeNode { kind, fill, stroke, child } ShapeNode { kind, fill, stroke, child }

View File

@ -6,16 +6,14 @@ use super::prelude::*;
pub fn box_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { pub fn box_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let width = args.named("width")?; let width = args.named("width")?;
let height = args.named("height")?; let height = args.named("height")?;
let body: Node = args.find().unwrap_or_default(); let body: PackedNode = args.find().unwrap_or_default();
Ok(Value::inline( Ok(Value::inline(body.sized(Spec::new(width, height))))
body.into_block().sized(Spec::new(width, height)),
))
} }
/// `block`: Place content into the flow. /// `block`: Place content into the flow.
pub fn block(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { pub fn block(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let body: Node = args.find().unwrap_or_default(); let body: PackedNode = args.find().unwrap_or_default();
Ok(Value::block(body.into_block())) Ok(Value::block(body))
} }
/// A node that sizes its child. /// A node that sizes its child.

View File

@ -1,33 +1,15 @@
//! Side-by-side layout of nodes along an axis. //! Side-by-side layout of nodes along an axis.
use std::fmt::{self, Debug, Formatter};
use super::prelude::*; use super::prelude::*;
use super::{AlignNode, SpacingKind, SpacingNode}; use super::{AlignNode, SpacingKind, SpacingNode};
/// `stack`: Stack children along an axis. /// `stack`: Stack children along an axis.
pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let dir = args.named("dir")?.unwrap_or(Dir::TTB); Ok(Value::block(StackNode {
let spacing = args.named("spacing")?; dir: args.named("dir")?.unwrap_or(Dir::TTB),
spacing: args.named("spacing")?,
let mut children = vec![]; children: args.all().collect(),
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 }))
} }
/// A node that stacks its children. /// A node that stacks its children.
@ -35,6 +17,8 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
pub struct StackNode { pub struct StackNode {
/// The stacking direction. /// The stacking direction.
pub dir: Dir, pub dir: Dir,
/// The spacing between non-spacing children.
pub spacing: Option<SpacingKind>,
/// The children to be stacked. /// The children to be stacked.
pub children: Vec<StackChild>, pub children: Vec<StackChild>,
} }
@ -45,7 +29,7 @@ impl Layout for StackNode {
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
regions: &Regions, regions: &Regions,
) -> Vec<Constrained<Rc<Frame>>> { ) -> Vec<Constrained<Rc<Frame>>> {
StackLayouter::new(self, regions).layout(ctx) StackLayouter::new(self, regions.clone()).layout(ctx)
} }
} }
@ -58,9 +42,8 @@ pub enum StackChild {
Node(PackedNode), Node(PackedNode),
} }
impl StackChild { impl From<SpacingKind> for StackChild {
/// Create a spacing node from a spacing kind. fn from(kind: SpacingKind) -> Self {
pub fn spacing(kind: SpacingKind) -> Self {
Self::Spacing(SpacingNode { kind, styles: Styles::new() }) Self::Spacing(SpacingNode { kind, styles: Styles::new() })
} }
} }
@ -77,23 +60,27 @@ impl Debug for StackChild {
castable! { castable! {
StackChild, StackChild,
Expected: "linear, fractional or template", Expected: "linear, fractional or template",
Value::Length(v) => Self::spacing(SpacingKind::Linear(v.into())), Value::Length(v) => SpacingKind::Linear(v.into()).into(),
Value::Relative(v) => Self::spacing(SpacingKind::Linear(v.into())), Value::Relative(v) => SpacingKind::Linear(v.into()).into(),
Value::Linear(v) => Self::spacing(SpacingKind::Linear(v)), Value::Linear(v) => SpacingKind::Linear(v).into(),
Value::Fractional(v) => Self::spacing(SpacingKind::Fractional(v)), Value::Fractional(v) => SpacingKind::Fractional(v).into(),
Value::Node(v) => Self::Node(v.into_block()), Value::Node(v) => Self::Node(v.into_block()),
} }
/// Performs stack layout. /// Performs stack layout.
struct StackLayouter<'a> { struct StackLayouter<'a> {
/// The stack node to layout. /// The flow node to layout.
stack: &'a StackNode, children: &'a [StackChild],
/// The axis of the block direction. /// The stacking direction.
dir: Dir,
/// The axis of the stacking direction.
axis: SpecAxis, axis: SpecAxis,
/// Whether the stack should expand to fill the region. /// The spacing between non-spacing children.
expand: Spec<bool>, spacing: Option<SpacingKind>,
/// The regions to layout children into. /// The regions to layout children into.
regions: Regions, regions: Regions,
/// Whether the stack should expand to fill the region.
expand: Spec<bool>,
/// The full size of `regions.current` that was available before we started /// The full size of `regions.current` that was available before we started
/// subtracting. /// subtracting.
full: Size, full: Size,
@ -119,21 +106,23 @@ enum StackItem {
impl<'a> StackLayouter<'a> { impl<'a> StackLayouter<'a> {
/// Create a new stack layouter. /// Create a new stack layouter.
fn new(stack: &'a StackNode, regions: &Regions) -> Self { fn new(stack: &'a StackNode, mut regions: Regions) -> Self {
let axis = stack.dir.axis(); let dir = stack.dir;
let axis = dir.axis();
let expand = regions.expand; let expand = regions.expand;
let full = regions.current; let full = regions.current;
// Disable expansion along the block axis for children. // Disable expansion along the block axis for children.
let mut regions = regions.clone();
regions.expand.set(axis, false); regions.expand.set(axis, false);
Self { Self {
stack, children: &stack.children,
dir,
axis, axis,
spacing: stack.spacing,
regions,
expand, expand,
full, full,
regions,
used: Gen::zero(), used: Gen::zero(),
fr: Fractional::zero(), fr: Fractional::zero(),
items: vec![], items: vec![],
@ -143,21 +132,26 @@ impl<'a> StackLayouter<'a> {
/// Layout all children. /// Layout all children.
fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> { fn layout(mut self, ctx: &mut LayoutContext) -> Vec<Constrained<Rc<Frame>>> {
for child in &self.stack.children { // Spacing to insert before the next node.
let mut deferred = None;
for child in self.children {
match child { match child {
StackChild::Spacing(node) => match node.kind { StackChild::Spacing(node) => {
SpacingKind::Linear(v) => self.layout_absolute(v), self.layout_spacing(node.kind);
SpacingKind::Fractional(v) => { deferred = None;
self.items.push(StackItem::Fractional(v));
self.fr += v;
} }
},
StackChild::Node(node) => { StackChild::Node(node) => {
if let Some(kind) = deferred {
self.layout_spacing(kind);
}
if self.regions.is_full() { if self.regions.is_full() {
self.finish_region(); self.finish_region();
} }
self.layout_node(ctx, node); self.layout_node(ctx, node);
deferred = self.spacing;
} }
} }
} }
@ -166,6 +160,17 @@ impl<'a> StackLayouter<'a> {
self.finished 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. /// Layout absolute spacing.
fn layout_absolute(&mut self, amount: Linear) { fn layout_absolute(&mut self, amount: Linear) {
// Resolve the linear, limiting it to the remaining available space. // Resolve the linear, limiting it to the remaining available space.
@ -183,7 +188,7 @@ impl<'a> StackLayouter<'a> {
let align = node let align = node
.downcast::<AlignNode>() .downcast::<AlignNode>()
.and_then(|node| node.aligns.get(self.axis)) .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 frames = node.layout(ctx, &self.regions);
let len = frames.len(); let len = frames.len();
@ -218,7 +223,7 @@ impl<'a> StackLayouter<'a> {
let mut output = Frame::new(size); let mut output = Frame::new(size);
let mut cursor = Length::zero(); 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. // Place all frames.
for item in self.items.drain(..) { for item in self.items.drain(..) {
@ -230,7 +235,7 @@ impl<'a> StackLayouter<'a> {
cursor += v.resolve(self.fr, remaining); cursor += v.resolve(self.fr, remaining);
} }
StackItem::Frame(frame, align) => { StackItem::Frame(frame, align) => {
if self.stack.dir.is_positive() { if self.dir.is_positive() {
ruler = ruler.max(align); ruler = ruler.max(align);
} else { } else {
ruler = ruler.min(align); ruler = ruler.min(align);
@ -240,7 +245,7 @@ impl<'a> StackLayouter<'a> {
let parent = size.get(self.axis); let parent = size.get(self.axis);
let child = frame.size.get(self.axis); let child = frame.size.get(self.axis);
let block = ruler.resolve(parent - self.used.main) let block = ruler.resolve(parent - self.used.main)
+ if self.stack.dir.is_positive() { + if self.dir.is_positive() {
cursor cursor
} else { } else {
self.used.main - child - cursor self.used.main - child - cursor

View File

@ -173,13 +173,6 @@ pub enum FontFamily {
Named(NamedFamily), 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 { impl Debug for FontFamily {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
@ -193,13 +186,13 @@ impl Debug for FontFamily {
dynamic! { dynamic! {
FontFamily: "font family", FontFamily: "font family",
Value::Str(string) => Self::named(&string), Value::Str(string) => Self::Named(NamedFamily::new(&string)),
} }
castable! { castable! {
Vec<FontFamily>, Vec<FontFamily>,
Expected: "string, generic family or array thereof", 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) => { Value::Array(values) => {
values.into_iter().filter_map(|v| v.cast().ok()).collect() values.into_iter().filter_map(|v| v.cast().ok()).collect()
}, },

View File

@ -28,15 +28,13 @@ pub fn rotate(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
} }
fn transform_impl(args: &mut Args, transform: Transform) -> TypResult<Value> { fn transform_impl(args: &mut Args, transform: Transform) -> TypResult<Value> {
let body: Node = args.expect("body")?; let body: PackedNode = args.expect("body")?;
let origin = args let origin = args
.named("origin")? .named("origin")?
.unwrap_or(Spec::splat(None)) .unwrap_or(Spec::splat(None))
.unwrap_or(Align::CENTER_HORIZON); .unwrap_or(Align::CENTER_HORIZON);
Ok(Value::inline( Ok(Value::inline(body.transformed(transform, origin)))
body.into_block().transformed(transform, origin),
))
} }
/// A node that transforms its child without affecting layout. /// A node that transforms its child without affecting layout.

View File

@ -275,13 +275,13 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult {
Some(NodeKind::Error(_, _)) => { Some(NodeKind::Error(_, _)) => {
p.eat(); p.eat();
Err(()) Err(ParseError)
} }
// Nothing. // Nothing.
_ => { _ => {
p.expected("expression"); p.expected("expression");
Err(()) Err(ParseError)
} }
} }
} }
@ -428,7 +428,7 @@ fn item(p: &mut Parser) -> ParseResult<NodeKind> {
marker.end(p, error); marker.end(p, error);
p.eat(); p.eat();
expr(p).ok(); 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 => {} Some(NodeKind::LeftBracket) if brackets => {}
_ => { _ => {
p.expected("argument list"); p.expected("argument list");
return Err(()); return Err(ParseError);
} }
} }
@ -689,7 +689,7 @@ fn ident(p: &mut Parser) -> ParseResult {
} }
_ => { _ => {
p.expected("identifier"); p.expected("identifier");
Err(()) Err(ParseError)
} }
} }
} }
@ -701,7 +701,7 @@ fn body(p: &mut Parser) -> ParseResult {
Some(NodeKind::LeftBrace) => block(p), Some(NodeKind::LeftBrace) => block(p),
_ => { _ => {
p.expected_at("body"); p.expected_at("body");
return Err(()); return Err(ParseError);
} }
} }
Ok(()) Ok(())

View File

@ -1,13 +1,10 @@
use std::fmt::{self, Display, Formatter};
use std::mem; use std::mem;
use super::{TokenMode, Tokens}; use super::{TokenMode, Tokens};
use crate::syntax::{ErrorPos, Green, GreenData, GreenNode, NodeKind}; use crate::syntax::{ErrorPos, Green, GreenData, GreenNode, NodeKind};
use crate::util::EcoString; 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<T = ()> = Result<T, ()>;
/// A convenient token-based parser. /// A convenient token-based parser.
pub struct Parser<'s> { pub struct Parser<'s> {
/// An iterator over the source tokens. /// An iterator over the source tokens.
@ -121,7 +118,7 @@ impl<'s> Parser<'s> {
if !eaten { if !eaten {
self.expected_at(t.as_str()); 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. /// 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`. /// A group for import items, ended by a semicolon, line break or `from`.
Imports, Imports,
} }
/// Allows parser methods to use the try operator. Never returned top-level
/// because the parser recovers from all errors.
pub type ParseResult<T = ()> = Result<T, ParseError>;
/// 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 {}

View File

@ -21,6 +21,7 @@ pub trait Pretty {
} }
/// A buffer into which items can be pretty printed. /// A buffer into which items can be pretty printed.
#[derive(Default)]
pub struct Printer { pub struct Printer {
buf: String, buf: String,
} }
@ -28,7 +29,7 @@ pub struct Printer {
impl Printer { impl Printer {
/// Create a new pretty printer. /// Create a new pretty printer.
pub fn new() -> Self { pub fn new() -> Self {
Self { buf: String::new() } Self::default()
} }
/// Push a character into the buffer. /// Push a character into the buffer.