summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-01-30 10:43:08 +0100
committerGitHub <noreply@github.com>2024-01-30 09:43:08 +0000
commitf14288cacf7331a422cb447d60d529797def3950 (patch)
tree6776d31396e2f848b9053bb8420f96ac7e66ba4b
parenta3e1c70e9e20b0438c233223eeead4f06a371c2c (diff)
Refactor folding (#3294)
-rw-r--r--crates/typst-macros/src/elem.rs58
-rw-r--r--crates/typst/src/foundations/auto.rs18
-rw-r--r--crates/typst/src/foundations/dict.rs1
-rw-r--r--crates/typst/src/foundations/styles.rs116
-rw-r--r--crates/typst/src/layout/align.rs4
-rw-r--r--crates/typst/src/layout/axes.rs31
-rw-r--r--crates/typst/src/layout/container.rs32
-rw-r--r--crates/typst/src/layout/corners.rs36
-rw-r--r--crates/typst/src/layout/grid/layout.rs4
-rw-r--r--crates/typst/src/layout/grid/mod.rs10
-rw-r--r--crates/typst/src/layout/length.rs8
-rw-r--r--crates/typst/src/layout/page.rs17
-rw-r--r--crates/typst/src/layout/rel.rs19
-rw-r--r--crates/typst/src/layout/sides.rs36
-rw-r--r--crates/typst/src/math/cancel.rs2
-rw-r--r--crates/typst/src/math/matrix.rs72
-rw-r--r--crates/typst/src/model/bibliography.rs7
-rw-r--r--crates/typst/src/model/emph.rs2
-rw-r--r--crates/typst/src/model/enum.rs30
-rw-r--r--crates/typst/src/model/list.rs23
-rw-r--r--crates/typst/src/model/table.rs7
-rw-r--r--crates/typst/src/text/deco.rs39
-rw-r--r--crates/typst/src/text/mod.rs64
-rw-r--r--crates/typst/src/text/raw.rs7
-rw-r--r--crates/typst/src/visualize/shape.rs41
-rw-r--r--crates/typst/src/visualize/stroke.rs28
26 files changed, 303 insertions, 409 deletions
diff --git a/crates/typst-macros/src/elem.rs b/crates/typst-macros/src/elem.rs
index 722131b7..91147b8d 100644
--- a/crates/typst-macros/src/elem.rs
+++ b/crates/typst-macros/src/elem.rs
@@ -106,7 +106,9 @@ impl Elem {
/// This includes:
/// - Fields that are not synthesized.
fn construct_fields(&self) -> impl Iterator<Item = &Field> + Clone {
- self.real_fields().filter(|field| !field.synthesized)
+ self.real_fields().filter(|field| {
+ !field.synthesized && (!field.internal || field.parse.is_some())
+ })
}
}
@@ -263,11 +265,6 @@ fn parse_field(field: &syn::Field) -> Result<Field> {
field.output = parse_quote! { <#output as #foundations::Resolve>::Output };
}
- if field.fold {
- let output = &field.output;
- field.output = parse_quote! { <#output as #foundations::Fold>::Output };
- }
-
validate_attrs(&attrs)?;
Ok(field)
@@ -555,32 +552,42 @@ fn create_style_chain_access(
let elem = &element.ident;
let Field { ty, default, enum_ident, .. } = field;
- let getter = match (field.fold, field.resolve, field.borrowed) {
- (false, false, false) => quote! { get },
- (false, false, true) => quote! { get_borrowed },
- (false, true, _) => quote! { get_resolve },
- (true, false, _) => quote! { get_fold },
- (true, true, _) => quote! { get_resolve_fold },
+
+ let getter = match (field.fold, field.borrowed) {
+ (false, false) => quote! { get },
+ (false, true) => quote! { get_ref },
+ (true, _) => quote! { get_folded },
};
- let default = default
- .clone()
- .unwrap_or_else(|| parse_quote! { ::std::default::Default::default() });
- let (init, default) = field.fold.then(|| (None, quote! { || #default })).unwrap_or_else(|| (
- Some(quote! {
+ let mut default = match default {
+ Some(default) => quote! { #default },
+ None => quote! { ::std::default::Default::default() },
+ };
+
+ let mut init = None;
+ if field.borrowed {
+ init = Some(quote! {
static DEFAULT: ::once_cell::sync::Lazy<#ty> = ::once_cell::sync::Lazy::new(|| #default);
- }),
- quote! { &DEFAULT },
- ));
+ });
+ default = quote! { &DEFAULT };
+ }
- quote! {
- #init
+ let mut value = quote! {
styles.#getter::<#ty>(
<Self as #foundations::NativeElement>::elem(),
<#elem as #foundations::Fields>::Enum::#enum_ident as u8,
#inherent,
- #default,
+ || #default,
)
+ };
+
+ if field.resolve {
+ value = quote! { #foundations::Resolve::resolve(#value, styles) };
+ }
+
+ quote! {
+ #init
+ #value
}
}
@@ -995,21 +1002,18 @@ fn create_param_info(field: &Field) -> TokenStream {
variadic,
required,
default,
- fold,
ty,
- output,
..
} = field;
let named = !positional;
let settable = field.settable();
- let default_ty = if *fold { &output } else { &ty };
let default = quote_option(&settable.then(|| {
let default = default
.clone()
.unwrap_or_else(|| parse_quote! { ::std::default::Default::default() });
quote! {
|| {
- let typed: #default_ty = #default;
+ let typed: #ty = #default;
#foundations::IntoValue::into_value(typed)
}
}
diff --git a/crates/typst/src/foundations/auto.rs b/crates/typst/src/foundations/auto.rs
index fcd72999..05f776d1 100644
--- a/crates/typst/src/foundations/auto.rs
+++ b/crates/typst/src/foundations/auto.rs
@@ -239,14 +239,14 @@ impl<T: Resolve> Resolve for Smart<T> {
}
}
-impl<T> Fold for Smart<T>
-where
- T: Fold,
- T::Output: Default,
-{
- type Output = Smart<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.map(|inner| inner.fold(outer.unwrap_or_default()))
+impl<T: Fold> Fold for Smart<T> {
+ fn fold(self, outer: Self) -> Self {
+ use Smart::Custom;
+ match (self, outer) {
+ (Custom(inner), Custom(outer)) => Custom(inner.fold(outer)),
+ // An explicit `auto` should be respected, thus we don't do
+ // `inner.or(outer)`.
+ (inner, _) => inner,
+ }
}
}
diff --git a/crates/typst/src/foundations/dict.rs b/crates/typst/src/foundations/dict.rs
index 291b8f60..da82889d 100644
--- a/crates/typst/src/foundations/dict.rs
+++ b/crates/typst/src/foundations/dict.rs
@@ -138,7 +138,6 @@ impl Dict {
};
let mut msg = String::from(match unexpected.len() {
- 0 => unreachable!(),
1 => "unexpected key ",
_ => "unexpected keys ",
});
diff --git a/crates/typst/src/foundations/styles.rs b/crates/typst/src/foundations/styles.rs
index f44a320f..83279a7b 100644
--- a/crates/typst/src/foundations/styles.rs
+++ b/crates/typst/src/foundations/styles.rs
@@ -6,7 +6,6 @@ use std::{iter, mem, ptr};
use comemo::Prehashed;
use ecow::{eco_vec, EcoString, EcoVec};
-use once_cell::sync::Lazy;
use smallvec::SmallVec;
use crate::diag::{SourceResult, Trace, Tracepoint};
@@ -457,54 +456,47 @@ impl<'a> StyleChain<'a> {
Chainable::chain(local, self)
}
- /// Cast the first value for the given property in the chain,
- /// returning a borrowed value if possible.
- pub fn get_borrowed<T: Clone>(
- self,
- func: Element,
- id: u8,
- inherent: Option<&'a T>,
- default: &'static Lazy<T>,
- ) -> &'a T {
- self.properties::<T>(func, id, inherent)
- .next()
- .unwrap_or_else(|| default)
- }
-
/// Cast the first value for the given property in the chain.
- pub fn get<T: Clone>(
+ pub fn get<T: Clone + 'static>(
self,
func: Element,
id: u8,
inherent: Option<&T>,
- default: &'static Lazy<T>,
+ default: impl Fn() -> T,
) -> T {
- self.get_borrowed(func, id, inherent, default).clone()
+ self.properties::<T>(func, id, inherent)
+ .next()
+ .cloned()
+ .unwrap_or_else(default)
}
- /// Cast the first value for the given property in the chain.
- pub fn get_resolve<T: Clone + Resolve>(
+ /// Cast the first value for the given property in the chain,
+ /// returning a borrowed value.
+ pub fn get_ref<T: 'static>(
self,
func: Element,
id: u8,
- inherent: Option<&T>,
- default: &'static Lazy<T>,
- ) -> T::Output {
- self.get(func, id, inherent, default).resolve(self)
+ inherent: Option<&'a T>,
+ default: impl Fn() -> &'a T,
+ ) -> &'a T {
+ self.properties::<T>(func, id, inherent)
+ .next()
+ .unwrap_or_else(default)
}
- /// Cast the first value for the given property in the chain.
- pub fn get_fold<T: Clone + Fold + 'static>(
+ /// Cast the first value for the given property in the chain, taking
+ /// `Fold` implementations into account.
+ pub fn get_folded<T: Fold + Clone + 'static>(
self,
func: Element,
id: u8,
inherent: Option<&T>,
- default: impl Fn() -> T::Output,
- ) -> T::Output {
+ default: impl Fn() -> T,
+ ) -> T {
fn next<T: Fold>(
mut values: impl Iterator<Item = T>,
- default: &impl Fn() -> T::Output,
- ) -> T::Output {
+ default: &impl Fn() -> T,
+ ) -> T {
values
.next()
.map(|value| value.fold(next(values, default)))
@@ -513,36 +505,6 @@ impl<'a> StyleChain<'a> {
next(self.properties::<T>(func, id, inherent).cloned(), &default)
}
- /// Cast the first value for the given property in the chain.
- pub fn get_resolve_fold<T>(
- self,
- func: Element,
- id: u8,
- inherent: Option<&T>,
- default: impl Fn() -> <T::Output as Fold>::Output,
- ) -> <T::Output as Fold>::Output
- where
- T: Resolve + Clone + 'static,
- T::Output: Fold,
- {
- fn next<T>(
- mut values: impl Iterator<Item = T>,
- styles: StyleChain,
- default: &impl Fn() -> <T::Output as Fold>::Output,
- ) -> <T::Output as Fold>::Output
- where
- T: Resolve + 'static,
- T::Output: Fold,
- {
- values
- .next()
- .map(|value| value.resolve(styles).fold(next(values, styles, default)))
- .unwrap_or_else(default)
- }
-
- next(self.properties::<T>(func, id, inherent).cloned(), self, &default)
- }
-
/// Iterate over all style recipes in the chain.
pub fn recipes(self) -> impl Iterator<Item = &'a Recipe> {
self.entries().filter_map(Style::recipe)
@@ -925,38 +887,36 @@ impl<T: Resolve> Resolve for Option<T> {
/// #rect()
/// ```
pub trait Fold {
- /// The type of the folded output.
- type Output;
-
/// Fold this inner value with an outer folded value.
- fn fold(self, outer: Self::Output) -> Self::Output;
+ fn fold(self, outer: Self) -> Self;
}
-impl<T> Fold for Option<T>
-where
- T: Fold,
- T::Output: Default,
-{
- type Output = Option<T::Output>;
+impl Fold for bool {
+ fn fold(self, _: Self) -> Self {
+ self
+ }
+}
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.map(|inner| inner.fold(outer.unwrap_or_default()))
+impl<T: Fold> Fold for Option<T> {
+ fn fold(self, outer: Self) -> Self {
+ match (self, outer) {
+ (Some(inner), Some(outer)) => Some(inner.fold(outer)),
+ // An explicit `None` should be respected, thus we don't do
+ // `inner.or(outer)`.
+ (inner, _) => inner,
+ }
}
}
impl<T> Fold for Vec<T> {
- type Output = Vec<T>;
-
- fn fold(mut self, outer: Self::Output) -> Self::Output {
+ fn fold(mut self, outer: Self) -> Self {
self.extend(outer);
self
}
}
impl<T, const N: usize> Fold for SmallVec<[T; N]> {
- type Output = SmallVec<[T; N]>;
-
- fn fold(mut self, outer: Self::Output) -> Self::Output {
+ fn fold(mut self, outer: Self) -> Self {
self.extend(outer);
self
}
diff --git a/crates/typst/src/layout/align.rs b/crates/typst/src/layout/align.rs
index e6b56f18..934bfa19 100644
--- a/crates/typst/src/layout/align.rs
+++ b/crates/typst/src/layout/align.rs
@@ -215,9 +215,7 @@ impl Repr for Alignment {
}
impl Fold for Alignment {
- type Output = Self;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
+ fn fold(self, outer: Self) -> Self {
match (self, outer) {
(Self::H(x), Self::V(y) | Self::Both(_, y)) => Self::Both(x, y),
(Self::V(y), Self::H(x) | Self::Both(x, _)) => Self::Both(x, y),
diff --git a/crates/typst/src/layout/axes.rs b/crates/typst/src/layout/axes.rs
index e5c47edd..82cb4aee 100644
--- a/crates/typst/src/layout/axes.rs
+++ b/crates/typst/src/layout/axes.rs
@@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Formatter};
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, Not};
use crate::diag::bail;
-use crate::foundations::{array, cast, Array, Fold, Resolve, Smart, StyleChain};
+use crate::foundations::{array, cast, Array, Resolve, Smart, StyleChain};
use crate::layout::{Abs, Dir, Length, Ratio, Rel};
use crate::util::Get;
@@ -84,14 +84,6 @@ impl<T> Axes<T> {
{
f(&self.x) && f(&self.y)
}
-
- /// Filter the individual fields with a mask.
- pub fn filter(self, mask: Axes<bool>) -> Axes<Option<T>> {
- Axes {
- x: if mask.x { Some(self.x) } else { None },
- y: if mask.y { Some(self.y) } else { None },
- }
- }
}
impl<T: Default> Axes<T> {
@@ -198,16 +190,6 @@ cast! {
"vertical" => Self::Y,
}
-impl<T> Axes<Option<T>> {
- /// Unwrap the individual fields.
- pub fn unwrap_or(self, other: Axes<T>) -> Axes<T> {
- Axes {
- x: self.x.unwrap_or(other.x),
- y: self.y.unwrap_or(other.y),
- }
- }
-}
-
impl<T> Axes<Smart<T>> {
/// Unwrap the individual fields.
pub fn unwrap_or(self, other: Axes<T>) -> Axes<T> {
@@ -325,14 +307,3 @@ impl<T: Resolve> Resolve for Axes<T> {
self.map(|v| v.resolve(styles))
}
}
-
-impl<T: Fold> Fold for Axes<Option<T>> {
- type Output = Axes<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip_map(outer, |inner, outer| match inner {
- Some(value) => value.fold(outer),
- None => outer,
- })
- }
-}
diff --git a/crates/typst/src/layout/container.rs b/crates/typst/src/layout/container.rs
index 7a673a4a..c4de09fe 100644
--- a/crates/typst/src/layout/container.rs
+++ b/crates/typst/src/layout/container.rs
@@ -133,7 +133,7 @@ impl Packed<BoxElem> {
// Apply inset.
let mut body = self.body(styles).unwrap_or_default();
- let inset = self.inset(styles);
+ let inset = self.inset(styles).unwrap_or_default();
if inset.iter().any(|v| !v.is_zero()) {
body = body.padded(inset.map(|side| side.map(Length::from)));
}
@@ -154,20 +154,24 @@ impl Packed<BoxElem> {
// Prepare fill and stroke.
let fill = self.fill(styles);
- let stroke = self.stroke(styles).map(|s| s.map(Stroke::unwrap_or_default));
+ let stroke = self
+ .stroke(styles)
+ .unwrap_or_default()
+ .map(|s| s.map(Stroke::unwrap_or_default));
// Clip the contents
if self.clip(styles) {
- let outset = self.outset(styles).relative_to(frame.size());
+ let outset =
+ self.outset(styles).unwrap_or_default().relative_to(frame.size());
let size = frame.size() + outset.sum_by_axis();
- let radius = self.radius(styles);
+ let radius = self.radius(styles).unwrap_or_default();
frame.clip(clip_rect(size, radius, &stroke));
}
// Add fill and/or stroke.
if fill.is_some() || stroke.iter().any(Option::is_some) {
- let outset = self.outset(styles);
- let radius = self.radius(styles);
+ let outset = self.outset(styles).unwrap_or_default();
+ let radius = self.radius(styles).unwrap_or_default();
frame.fill_and_stroke(fill, stroke, outset, radius, self.span());
}
@@ -350,7 +354,7 @@ impl LayoutMultiple for Packed<BlockElem> {
) -> SourceResult<Fragment> {
// Apply inset.
let mut body = self.body(styles).unwrap_or_default();
- let inset = self.inset(styles);
+ let inset = self.inset(styles).unwrap_or_default();
if inset.iter().any(|v| !v.is_zero()) {
body = body.clone().padded(inset.map(|side| side.map(Length::from)));
}
@@ -418,14 +422,18 @@ impl LayoutMultiple for Packed<BlockElem> {
// Prepare fill and stroke.
let fill = self.fill(styles);
- let stroke = self.stroke(styles).map(|s| s.map(Stroke::unwrap_or_default));
+ let stroke = self
+ .stroke(styles)
+ .unwrap_or_default()
+ .map(|s| s.map(Stroke::unwrap_or_default));
// Clip the contents
if self.clip(styles) {
for frame in frames.iter_mut() {
- let outset = self.outset(styles).relative_to(frame.size());
+ let outset =
+ self.outset(styles).unwrap_or_default().relative_to(frame.size());
let size = frame.size() + outset.sum_by_axis();
- let radius = self.radius(styles);
+ let radius = self.radius(styles).unwrap_or_default();
frame.clip(clip_rect(size, radius, &stroke));
}
}
@@ -437,8 +445,8 @@ impl LayoutMultiple for Packed<BlockElem> {
skip = first.is_empty() && rest.iter().any(|frame| !frame.is_empty());
}
- let outset = self.outset(styles);
- let radius = self.radius(styles);
+ let outset = self.outset(styles).unwrap_or_default();
+ let radius = self.radius(styles).unwrap_or_default();
for frame in frames.iter_mut().skip(skip as usize) {
frame.fill_and_stroke(
fill.clone(),
diff --git a/crates/typst/src/layout/corners.rs b/crates/typst/src/layout/corners.rs
index e674b04d..85fd0503 100644
--- a/crates/typst/src/layout/corners.rs
+++ b/crates/typst/src/layout/corners.rs
@@ -80,6 +80,16 @@ impl<T> Corners<T> {
}
}
+impl<T> Corners<Option<T>> {
+ /// Unwrap-or-default the individual corners.
+ pub fn unwrap_or_default(self) -> Corners<T>
+ where
+ T: Default,
+ {
+ self.map(Option::unwrap_or_default)
+ }
+}
+
impl<T> Get<Corner> for Corners<T> {
type Component = T;
@@ -133,20 +143,21 @@ impl<T: Reflect> Reflect for Corners<Option<T>> {
}
}
-impl<T> IntoValue for Corners<T>
+impl<T> IntoValue for Corners<Option<T>>
where
T: PartialEq + IntoValue,
{
fn into_value(self) -> Value {
if self.is_uniform() {
- return self.top_left.into_value();
+ if let Some(top_left) = self.top_left {
+ return top_left.into_value();
+ }
}
let mut dict = Dict::new();
- let mut handle = |key: &str, component: T| {
- let value = component.into_value();
- if value != Value::None {
- dict.insert(key.into(), value);
+ let mut handle = |key: &str, component: Option<T>| {
+ if let Some(c) = component {
+ dict.insert(key.into(), c.into_value());
}
};
@@ -228,12 +239,13 @@ impl<T: Resolve> Resolve for Corners<T> {
}
impl<T: Fold> Fold for Corners<Option<T>> {
- type Output = Corners<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer).map(|(inner, outer)| match inner {
- Some(value) => value.fold(outer),
- None => outer,
+ fn fold(self, outer: Self) -> Self {
+ self.zip(outer).map(|(inner, outer)| match (inner, outer) {
+ (Some(inner), Some(outer)) => Some(inner.fold(outer)),
+ // Usually, folding an inner `None` with an `outer` preferres the
+ // explicit `None`. However, here `None` means unspecified and thus
+ // we want `outer`.
+ (inner, outer) => inner.or(outer),
})
}
}
diff --git a/crates/typst/src/layout/grid/layout.rs b/crates/typst/src/layout/grid/layout.rs
index f9957fcb..c17bbda5 100644
--- a/crates/typst/src/layout/grid/layout.rs
+++ b/crates/typst/src/layout/grid/layout.rs
@@ -151,7 +151,7 @@ pub trait ResolvableCell {
y: usize,
fill: &Option<Paint>,
align: Smart<Alignment>,
- inset: Sides<Rel<Length>>,
+ inset: Sides<Option<Rel<Length>>>,
styles: StyleChain,
) -> Cell;
@@ -204,7 +204,7 @@ impl CellGrid {
cells: &[T],
fill: &Celled<Option<Paint>>,
align: &Celled<Smart<Alignment>>,
- inset: Sides<Rel<Length>>,
+ inset: Sides<Option<Rel<Length>>>,
engine: &mut Engine,
styles: StyleChain,
span: Span,
diff --git a/crates/typst/src/layout/grid/mod.rs b/crates/typst/src/layout/grid/mod.rs
index 544d43a8..ee2eeecf 100644
--- a/crates/typst/src/layout/grid/mod.rs
+++ b/crates/typst/src/layout/grid/mod.rs
@@ -13,8 +13,8 @@ use crate::foundations::{
cast, elem, scope, Array, Content, Fold, Packed, Show, Smart, StyleChain, Value,
};
use crate::layout::{
- Abs, AlignElem, Alignment, Axes, Fragment, LayoutMultiple, Length, Regions, Rel,
- Sides, Sizing,
+ AlignElem, Alignment, Axes, Fragment, LayoutMultiple, Length, Regions, Rel, Sides,
+ Sizing,
};
use crate::syntax::Span;
use crate::util::NonZeroExt;
@@ -264,7 +264,6 @@ pub struct GridElem {
/// )
/// ```
#[fold]
- #[default(Sides::splat(Abs::pt(0.0).into()))]
pub inset: Sides<Option<Rel<Length>>>,
/// The contents of the grid cells.
@@ -462,7 +461,7 @@ impl ResolvableCell for Packed<GridCell> {
y: usize,
fill: &Option<Paint>,
align: Smart<Alignment>,
- inset: Sides<Rel<Length>>,
+ inset: Sides<Option<Rel<Length>>>,
styles: StyleChain,
) -> Cell {
let cell = &mut *self;
@@ -481,9 +480,8 @@ impl ResolvableCell for Packed<GridCell> {
Smart::Auto => cell.align(styles),
});
cell.push_inset(Smart::Custom(
- cell.inset(styles).map_or(inset, |inner| inner.fold(inset)).map(Some),
+ cell.inset(styles).map_or(inset, |inner| inner.fold(inset)),
));
-
Cell { body: self.pack(), fill, colspan }
}
diff --git a/crates/typst/src/layout/length.rs b/crates/typst/src/layout/length.rs
index 94e85248..7365ca19 100644
--- a/crates/typst/src/layout/length.rs
+++ b/crates/typst/src/layout/length.rs
@@ -5,7 +5,7 @@ use std::ops::{Add, Div, Mul, Neg};
use ecow::{eco_format, EcoString};
use crate::diag::{At, Hint, SourceResult};
-use crate::foundations::{func, scope, ty, Repr, Resolve, StyleChain, Styles};
+use crate::foundations::{func, scope, ty, Fold, Repr, Resolve, StyleChain, Styles};
use crate::layout::{Abs, Em};
use crate::syntax::Span;
use crate::util::Numeric;
@@ -274,3 +274,9 @@ impl Resolve for Length {
self.abs + self.em.resolve(styles)
}
}
+
+impl Fold for Length {
+ fn fold(self, _: Self) -> Self {
+ self
+ }
+}
diff --git a/crates/typst/src/layout/page.rs b/crates/typst/src/layout/page.rs
index 7e94984b..bb9097a6 100644
--- a/crates/typst/src/layout/page.rs
+++ b/crates/typst/src/layout/page.rs
@@ -549,18 +549,11 @@ impl Margin {
}
impl Fold for Margin {
- type Output = Margin;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- let sides =
- self.sides
- .zip(outer.sides)
- .map(|(inner, outer)| match (inner, outer) {
- (Some(value), Some(outer)) => Some(value.fold(outer)),
- _ => inner.or(outer),
- });
- let two_sided = self.two_sided.or(outer.two_sided);
- Margin { sides, two_sided }
+ fn fold(self, outer: Self) -> Self {
+ Margin {
+ sides: self.sides.fold(outer.sides),
+ two_sided: self.two_sided.fold(outer.two_sided),
+ }
}
}
diff --git a/crates/typst/src/layout/rel.rs b/crates/typst/src/layout/rel.rs
index 8f937102..df58df86 100644
--- a/crates/typst/src/layout/rel.rs
+++ b/crates/typst/src/layout/rel.rs
@@ -259,19 +259,12 @@ where
}
}
-impl Fold for Rel<Abs> {
- type Output = Self;
-
- fn fold(self, _: Self::Output) -> Self::Output {
- self
- }
-}
-
-impl Fold for Rel<Length> {
- type Output = Self;
-
- fn fold(self, _: Self::Output) -> Self::Output {
- self
+impl<T> Fold for Rel<T>
+where
+ T: Numeric + Fold,
+{
+ fn fold(self, outer: Self) -> Self {
+ Self { rel: self.rel, abs: self.abs.fold(outer.abs) }
}
}
diff --git a/crates/typst/src/layout/sides.rs b/crates/typst/src/layout/sides.rs
index 937919c4..bb35ffb3 100644
--- a/crates/typst/src/layout/sides.rs
+++ b/crates/typst/src/layout/sides.rs
@@ -94,6 +94,16 @@ impl<T: Add> Sides<T> {
}
}
+impl<T> Sides<Option<T>> {
+ /// Unwrap-or-default the individual sides.
+ pub fn unwrap_or_default(self) -> Sides<T>
+ where
+ T: Default,
+ {
+ self.map(Option::unwrap_or_default)
+ }
+}
+
impl Sides<Rel<Abs>> {
/// Evaluate the sides relative to the given `size`.
pub fn relative_to(self, size: Size) -> Sides<Abs> {
@@ -159,20 +169,21 @@ impl<T: Reflect> Reflect for Sides<Option<T>> {
}
}
-impl<T> IntoValue for Sides<T>
+impl<T> IntoValue for Sides<Option<T>>
where
T: PartialEq + IntoValue,
{
fn into_value(self) -> Value {
if self.is_uniform() {
- return self.left.into_value();
+ if let Some(left) = self.left {
+ return left.into_value();
+ }
}
let mut dict = Dict::new();
- let mut handle = |key: &str, component: T| {
- let value = component.into_value();
- if value != Value::None {
- dict.insert(key.into(), value);
+ let mut handle = |key: &str, component: Option<T>| {
+ if let Some(c) = component {
+ dict.insert(key.into(), c.into_value());
}
};
@@ -233,12 +244,13 @@ impl<T: Resolve> Resolve for Sides<T> {
}
impl<T: Fold> Fold for Sides<Option<T>> {
- type Output = Sides<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.zip(outer).map(|(inner, outer)| match inner {
- Some(value) => value.fold(outer),
- None => outer,
+ fn fold(self, outer: Self) -> Self {
+ self.zip(outer).map(|(inner, outer)| match (inner, outer) {
+ (Some(inner), Some(outer)) => Some(inner.fold(outer)),
+ // Usually, folding an inner `None` with an `outer` preferres the
+ // explicit `None`. However, here `None` means unspecified and thus
+ // we want `outer`.
+ (inner, outer) => inner.or(outer),
})
}
}
diff --git a/crates/typst/src/math/cancel.rs b/crates/typst/src/math/cancel.rs
index 039ef752..695ff567 100644
--- a/crates/typst/src/math/cancel.rs
+++ b/crates/typst/src/math/cancel.rs
@@ -97,7 +97,7 @@ pub struct CancelElem {
#[fold]
#[default(Stroke {
// Default stroke has 0.5pt for better visuals.
- thickness: Smart::Custom(Abs::pt(0.5)),
+ thickness: Smart::Custom(Abs::pt(0.5).into()),
..Default::default()
})]
pub stroke: Stroke,
diff --git a/crates/typst/src/math/matrix.rs b/crates/typst/src/math/matrix.rs
index 616cc738..97b01491 100644
--- a/crates/typst/src/math/matrix.rs
+++ b/crates/typst/src/math/matrix.rs
@@ -425,11 +425,10 @@ fn layout_mat_body(
};
let (hline, vline, stroke) = match augment {
- Some(v) => {
- // need to get stroke here for ownership
- let stroke = v.stroke_or(default_stroke);
-
- (v.hline, v.vline, stroke)
+ Some(augment) => {
+ // We need to get stroke here for ownership.
+ let stroke = augment.stroke.unwrap_or_default().unwrap_or(default_stroke);
+ (augment.hline, augment.vline, stroke)
}
_ => (AugmentOffsets::default(), AugmentOffsets::default(), default_stroke),
};
@@ -593,11 +592,19 @@ pub struct Augment<T: Numeric = Length> {
pub stroke: Smart<Stroke<T>>,
}
-impl Augment<Abs> {
- fn stroke_or(&self, fallback: FixedStroke) -> FixedStroke {
- match &self.stroke {
- Smart::Custom(v) => v.clone().unwrap_or(fallback),
- Smart::Auto => fallback,
+impl<T: Numeric + Fold> Fold for Augment<T> {
+ fn fold(self, outer: Self) -> Self {
+ Self {
+ stroke: match (self.stroke, outer.stroke) {
+ (Smart::Custom(inner), Smart::Custom(outer)) => {
+ Smart::Custom(inner.fold(outer))
+ }
+ // Usually, folding an inner `auto` with an `outer` preferres
+ // the explicit `auto`. However, here `auto` means unspecified
+ // and thus we want `outer`.
+ (inner, outer) => inner.or(outer),
+ },
+ ..self
}
}
}
@@ -614,21 +621,6 @@ impl Resolve for Augment {
}
}
-impl Fold for Augment<Abs> {
- type Output = Augment<Abs>;
-
- fn fold(mut self, outer: Self::Output) -> Self::Output {
- // Special case for handling `auto` strokes in subsequent `Augment`.
- if self.stroke.is_auto() && outer.stroke.is_custom() {
- self.stroke = outer.stroke;
- } else {
- self.stroke = self.stroke.fold(outer.stroke);
- }
-
- self
- }
-}
-
cast! {
Augment,
self => {
@@ -637,13 +629,11 @@ cast! {
return self.vline.0[0].into_value();
}
- let d = dict! {
- "hline" => self.hline.into_value(),
- "vline" => self.vline.into_value(),
- "stroke" => self.stroke.into_value()
- };
-
- d.into_value()
+ dict! {
+ "hline" => self.hline,
+ "vline" => self.vline,
+ "stroke" => self.stroke,
+ }.into_value()
},
v: isize => Augment {
hline: AugmentOffsets::default(),
@@ -651,15 +641,15 @@ cast! {
stroke: Smart::Auto,
},
mut dict: Dict => {
- // need the transpose for the defaults to work
- let hline = dict.take("hline").ok().map(AugmentOffsets::from_value)
- .transpose().unwrap_or_default().unwrap_or_default();
- let vline = dict.take("vline").ok().map(AugmentOffsets::from_value)
- .transpose().unwrap_or_default().unwrap_or_default();
-
- let stroke = dict.take("stroke").ok().map(Stroke::from_value)
- .transpose()?.map(Smart::Custom).unwrap_or(Smart::Auto);
-
+ let mut take = |key| dict.take(key).ok().map(AugmentOffsets::from_value).transpose();
+ let hline = take("hline")?.unwrap_or_default();
+ let vline = take("vline")?.unwrap_or_default();
+ let stroke = dict.take("stroke")
+ .ok()
+ .map(Stroke::from_value)
+ .transpose()?
+ .map(Smart::Custom)
+ .unwrap_or(Smart::Auto);
Augment { hline, vline, stroke }
},
}
diff --git a/crates/typst/src/model/bibliography.rs b/crates/typst/src/model/bibliography.rs
index 94681fe1..6e20e98a 100644
--- a/crates/typst/src/model/bibliography.rs
+++ b/crates/typst/src/model/bibliography.rs
@@ -323,7 +323,6 @@ impl LocalName for Packed<BibliographyElem> {
}
/// A loaded bibliography.
-#[ty]
#[derive(Clone, PartialEq)]
pub struct Bibliography {
map: Arc<IndexMap<PicoStr, hayagriva::Entry>>,
@@ -422,12 +421,6 @@ impl Hash for Bibliography {
}
}
-impl Repr for Bibliography {
- fn repr(&self) -> EcoString {
- "..".into()
- }
-}
-
/// Format a BibLaTeX loading error.
fn format_biblatex_error(path: &str, src: &str, errors: Vec<BibLaTeXError>) -> EcoString {
let Some(error) = errors.first() else {
diff --git a/crates/typst/src/model/emph.rs b/crates/typst/src/model/emph.rs
index b67643de..a4c916ed 100644
--- a/crates/typst/src/model/emph.rs
+++ b/crates/typst/src/model/emph.rs
@@ -36,6 +36,6 @@ pub struct EmphElem {
impl Show for Packed<EmphElem> {
#[typst_macros::time(name = "emph", span = self.span())]
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
- Ok(self.body().clone().styled(TextElem::set_emph(ItalicToggle)))
+ Ok(self.body().clone().styled(TextElem::set_emph(ItalicToggle(true))))
}
}
diff --git a/crates/typst/src/model/enum.rs b/crates/typst/src/model/enum.rs
index 622501db..0c1a31c5 100644
--- a/crates/typst/src/model/enum.rs
+++ b/crates/typst/src/model/enum.rs
@@ -1,10 +1,10 @@
use std::str::FromStr;
+use smallvec::{smallvec, SmallVec};
+
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
-use crate::foundations::{
- cast, elem, scope, Array, Content, Fold, Packed, Smart, StyleChain,
-};
+use crate::foundations::{cast, elem, scope, Array, Content, Packed, Smart, StyleChain};
use crate::layout::{
Alignment, Axes, BlockElem, Cell, CellGrid, Em, Fragment, GridLayouter, HAlignment,
LayoutMultiple, Length, Regions, Sizing, Spacing, VAlignment,
@@ -199,7 +199,7 @@ pub struct EnumElem {
/// The numbers of parent items.
#[internal]
#[fold]
- parents: Parent,
+ parents: SmallVec<[usize; 4]>,
}
#[scope]
@@ -229,6 +229,8 @@ impl LayoutMultiple for Packed<EnumElem> {
let mut cells = vec![];
let mut number = self.start(styles);
let mut parents = self.parents(styles);
+ parents.reverse();
+
let full = self.full(styles);
// Horizontally align based on the given respective parameter.
@@ -263,7 +265,7 @@ impl LayoutMultiple for Packed<EnumElem> {
cells.push(Cell::from(resolved));
cells.push(Cell::from(Content::empty()));
cells.push(Cell::from(
- item.body().clone().styled(EnumElem::set_parents(Parent(number))),
+ item.body().clone().styled(EnumElem::set_parents(smallvec![number])),
));
number = number.saturating_add(1);
}
@@ -309,21 +311,3 @@ cast! {
},
v: Content => v.unpack::<Self>().unwrap_or_else(Self::new),
}
-
-#[derive(Debug, Clone, Copy, PartialEq, Hash)]
-struct Parent(usize);
-
-cast! {
- Parent,
- self => self.0.into_value(),
- v: usize => Self(v),
-}
-
-impl Fold for Parent {
- type Output = Vec<usize>;
-
- fn fold(self, mut outer: Self::Output) -> Self::Output {
- outer.push(self.0);
- outer
- }
-}
diff --git a/crates/typst/src/model/list.rs b/crates/typst/src/model/list.rs
index 71a586b9..c849fa65 100644
--- a/crates/typst/src/model/list.rs
+++ b/crates/typst/src/model/list.rs
@@ -150,7 +150,7 @@ impl LayoutMultiple for Packed<ListElem> {
.unwrap_or_else(|| *BlockElem::below_in(styles).amount())
};
- let depth = self.depth(styles);
+ let Depth(depth) = self.depth(styles);
let marker = self
.marker(styles)
.resolve(engine, depth)?
@@ -162,8 +162,9 @@ impl LayoutMultiple for Packed<ListElem> {
cells.push(Cell::from(Content::empty()));
cells.push(Cell::from(marker.clone()));
cells.push(Cell::from(Content::empty()));
- cells
- .push(Cell::from(item.body().clone().styled(ListElem::set_depth(Depth))));
+ cells.push(Cell::from(
+ item.body().clone().styled(ListElem::set_depth(Depth(1))),
+ ));
}
let stroke = None;
@@ -235,19 +236,11 @@ cast! {
v: Func => Self::Func(v),
}
-#[derive(Debug, Clone, Copy, PartialEq, Hash)]
-struct Depth;
-
-cast! {
- Depth,
- self => Value::None,
- _: Value => Self,
-}
+#[derive(Debug, Default, Clone, Copy, PartialEq, Hash)]
+struct Depth(usize);
impl Fold for Depth {
- type Output = usize;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- outer + 1
+ fn fold(self, outer: Self) -> Self {
+ Self(outer.0 + self.0)
}
}
diff --git a/crates/typst/src/model/table.rs b/crates/typst/src/model/table.rs
index ecda0527..256459c7 100644
--- a/crates/typst/src/model/table.rs
+++ b/crates/typst/src/model/table.rs
@@ -194,7 +194,7 @@ pub struct TableElem {
/// )
/// ```
#[fold]
- #[default(Sides::splat(Abs::pt(5.0).into()))]
+ #[default(Sides::splat(Some(Abs::pt(5.0).into())))]
pub inset: Sides<Option<Rel<Length>>>,
/// The contents of the table cells.
@@ -378,7 +378,7 @@ impl ResolvableCell for Packed<TableCell> {
y: usize,
fill: &Option<Paint>,
align: Smart<Alignment>,
- inset: Sides<Rel<Length>>,
+ inset: Sides<Option<Rel<Length>>>,
styles: StyleChain,
) -> Cell {
let cell = &mut *self;
@@ -397,9 +397,8 @@ impl ResolvableCell for Packed<TableCell> {
Smart::Auto => cell.align(styles),
});
cell.push_inset(Smart::Custom(
- cell.inset(styles).map_or(inset, |inner| inner.fold(inset)).map(Some),
+ cell.inset(styles).map_or(inset, |inner| inner.fold(inset)),
));
-
Cell { body: self.pack(), fill, colspan }
}
diff --git a/crates/typst/src/text/deco.rs b/crates/typst/src/text/deco.rs
index 3872334d..7873f97b 100644
--- a/crates/typst/src/text/deco.rs
+++ b/crates/typst/src/text/deco.rs
@@ -1,13 +1,10 @@
use kurbo::{BezPath, Line, ParamCurve};
+use smallvec::smallvec;
use ttf_parser::{GlyphId, OutlineBuilder};
-use ecow::{eco_format, EcoString};
-
use crate::diag::SourceResult;
use crate::engine::Engine;
-use crate::foundations::{
- elem, ty, Content, Fold, Packed, Repr, Show, Smart, StyleChain,
-};
+use crate::foundations::{elem, Content, Packed, Show, Smart, StyleChain};
use crate::layout::{Abs, Em, Frame, FrameItem, Length, Point, Size};
use crate::syntax::Span;
use crate::text::{
@@ -89,7 +86,7 @@ pub struct UnderlineElem {
impl Show for Packed<UnderlineElem> {
#[typst_macros::time(name = "underline", span = self.span())]
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body().clone().styled(TextElem::set_deco(Decoration {
+ Ok(self.body().clone().styled(TextElem::set_deco(smallvec![Decoration {
line: DecoLine::Underline {
stroke: self.stroke(styles).unwrap_or_default(),
offset: self.offset(styles),
@@ -97,7 +94,7 @@ impl Show for Packed<UnderlineElem> {
background: self.background(styles),
},
extent: self.extent(styles),
- })))
+ }])))
}
}
@@ -181,7 +178,7 @@ pub struct OverlineElem {
impl Show for Packed<OverlineElem> {
#[typst_macros::time(name = "overline", span = self.span())]
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body().clone().styled(TextElem::set_deco(Decoration {
+ Ok(self.body().clone().styled(TextElem::set_deco(smallvec![Decoration {
line: DecoLine::Overline {
stroke: self.stroke(styles).unwrap_or_default(),
offset: self.offset(styles),
@@ -189,7 +186,7 @@ impl Show for Packed<OverlineElem> {
background: self.background(styles),
},
extent: self.extent(styles),
- })))
+ }])))
}
}
@@ -258,7 +255,7 @@ pub struct StrikeElem {
impl Show for Packed<StrikeElem> {
#[typst_macros::time(name = "strike", span = self.span())]
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body().clone().styled(TextElem::set_deco(Decoration {
+ Ok(self.body().clone().styled(TextElem::set_deco(smallvec![Decoration {
// Note that we do not support evade option for strikethrough.
line: DecoLine::Strikethrough {
stroke: self.stroke(styles).unwrap_or_default(),
@@ -266,7 +263,7 @@ impl Show for Packed<StrikeElem> {
background: self.background(styles),
},
extent: self.extent(styles),
- })))
+ }])))
}
}
@@ -328,14 +325,14 @@ pub struct HighlightElem {
impl Show for Packed<HighlightElem> {
#[typst_macros::time(name = "highlight", span = self.span())]
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body().clone().styled(TextElem::set_deco(Decoration {
+ Ok(self.body().clone().styled(TextElem::set_deco(smallvec![Decoration {
line: DecoLine::Highlight {
fill: self.fill(styles),
top_edge: self.top_edge(styles),
bottom_edge: self.bottom_edge(styles),
},
extent: self.extent(styles),
- })))
+ }])))
}
}
@@ -343,28 +340,12 @@ impl Show for Packed<HighlightElem> {
///
/// Can be positioned over, under, or on top of text, or highlight the text with
/// a background.
-#[ty]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Decoration {
line: DecoLine,
extent: Abs,
}
-impl Fold for Decoration {
- type Output = Vec<Self>;
-
- fn fold(self, mut outer: Self::Output) -> Self::Output {
- outer.insert(0, self);
- outer
- }
-}
-
-impl Repr for Decoration {
- fn repr(&self) -> EcoString {
- eco_format!("{self:?}")
- }
-}
-
/// A kind of decorative line.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
enum DecoLine {
diff --git a/crates/typst/src/text/mod.rs b/crates/typst/src/text/mod.rs
index 23a685d8..eab01066 100644
--- a/crates/typst/src/text/mod.rs
+++ b/crates/typst/src/text/mod.rs
@@ -32,6 +32,7 @@ use std::fmt::{self, Debug, Formatter};
use ecow::{eco_format, EcoString};
use rustybuzz::{Feature, Tag};
+use smallvec::SmallVec;
use ttf_parser::Rect;
use crate::diag::{bail, SourceResult, StrResult};
@@ -39,8 +40,9 @@ use crate::engine::Engine;
use crate::foundations::Packed;
use crate::foundations::{
cast, category, elem, Args, Array, Cast, Category, Construct, Content, Dict, Fold,
- NativeElement, Never, PlainText, Repr, Resolve, Scope, Set, Smart, StyleChain, Value,
+ NativeElement, Never, PlainText, Repr, Resolve, Scope, Set, Smart, StyleChain,
};
+use crate::layout::Em;
use crate::layout::{Abs, Axis, Dir, Length, Rel};
use crate::model::ParElem;
use crate::syntax::Spanned;
@@ -214,7 +216,8 @@ pub struct TextElem {
/// ```
#[parse(args.named_or_find("size")?)]
#[fold]
- #[default(Abs::pt(11.0))]
+ #[default(TextSize(Abs::pt(11.0).into()))]
+ #[resolve]
#[ghost]
pub size: TextSize,
@@ -628,7 +631,7 @@ pub struct TextElem {
/// Whether the font style should be inverted.
#[internal]
#[fold]
- #[default(false)]
+ #[default(ItalicToggle(false))]
#[ghost]
pub emph: ItalicToggle,
@@ -636,7 +639,7 @@ pub struct TextElem {
#[internal]
#[fold]
#[ghost]
- pub deco: Decoration,
+ pub deco: SmallVec<[Decoration; 1]>,
/// A case transformation that should be applied to the text.
#[internal]
@@ -763,12 +766,12 @@ pub(crate) fn variant(styles: StyleChain) -> FontVariant {
TextElem::stretch_in(styles),
);
- let delta = TextElem::delta_in(styles);
+ let WeightDelta(delta) = TextElem::delta_in(styles);
variant.weight = variant
.weight
.thicken(delta.clamp(i16::MIN as i64, i16::MAX as i64) as i16);
- if TextElem::emph_in(styles) {
+ if TextElem::emph_in(styles).0 {
variant.style = match variant.style {
FontStyle::Normal => FontStyle::Italic,
FontStyle::Italic => FontStyle::Normal,
@@ -784,10 +787,20 @@ pub(crate) fn variant(styles: StyleChain) -> FontVariant {
pub struct TextSize(pub Length);
impl Fold for TextSize {
+ fn fold(self, outer: Self) -> Self {
+ // Multiply the two linear functions.
+ Self(Length {
+ em: Em::new(self.0.em.get() * outer.0.em.get()),
+ abs: self.0.em.get() * outer.0.abs + self.0.abs,
+ })
+ }
+}
+
+impl Resolve for TextSize {
type Output = Abs;
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.0.em.at(outer) + self.0.abs
+ fn resolve(self, styles: StyleChain) -> Self::Output {
+ self.0.resolve(styles)
}
}
@@ -1056,11 +1069,8 @@ cast! {
}
impl Fold for FontFeatures {
- type Output = Self;
-
- fn fold(mut self, outer: Self::Output) -> Self::Output {
- self.0.extend(outer.0);
- self
+ fn fold(self, outer: Self) -> Self {
+ Self(self.0.fold(outer.0))
}
}
@@ -1133,36 +1143,20 @@ pub(crate) fn features(styles: StyleChain) -> Vec<Feature> {
/// A toggle that turns on and off alternatingly if folded.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct ItalicToggle;
-
-cast! {
- ItalicToggle,
- self => Value::None,
- _: Value => Self,
-}
+pub struct ItalicToggle(pub bool);
impl Fold for ItalicToggle {
- type Output = bool;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- !outer
+ fn fold(self, outer: Self) -> Self {
+ Self(self.0 ^ outer.0)
}
}
/// A delta that is summed up when folded.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct WeightDelta(pub i64);
-cast! {
- WeightDelta,
- self => self.0.into_value(),
- v: i64 => Self(v),
-}
-
impl Fold for WeightDelta {
- type Output = i64;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- outer + self.0
+ fn fold(self, outer: Self) -> Self {
+ Self(outer.0 + self.0)
}
}
diff --git a/crates/typst/src/text/raw.rs b/crates/typst/src/text/raw.rs
index b032f666..28fcf891 100644
--- a/crates/typst/src/text/raw.rs
+++ b/crates/typst/src/text/raw.rs
@@ -658,11 +658,8 @@ cast! {
}
impl Fold for SyntaxPaths {
- type Output = Self;
-
- fn fold(mut self, outer: Self::Output) -> Self::Output {
- self.0.extend(outer.0);
- self
+ fn fold(self, outer: Self) -> Self {
+ Self(self.0.fold(outer.0))
}
}
diff --git a/crates/typst/src/visualize/shape.rs b/crates/typst/src/visualize/shape.rs
index 89de3398..de1eac16 100644
--- a/crates/typst/src/visualize/shape.rs
+++ b/crates/typst/src/visualize/shape.rs
@@ -114,7 +114,7 @@ pub struct RectElem {
/// See the [box's documentation]($box.outset) for more details.
#[resolve]
#[fold]
- #[default(Sides::splat(Abs::pt(5.0).into()))]
+ #[default(Sides::splat(Some(Abs::pt(5.0).into())))]
pub inset: Sides<Option<Rel<Length>>>,
/// How much to expand the rectangle's size without affecting the layout.
@@ -219,7 +219,7 @@ pub struct SquareElem {
/// [box's documentation]($box.inset) for more details.
#[resolve]
#[fold]
- #[default(Sides::splat(Abs::pt(5.0).into()))]
+ #[default(Sides::splat(Some(Abs::pt(5.0).into())))]
pub inset: Sides<Option<Rel<Length>>>,
/// How much to expand the square's size without affecting the layout. See
@@ -298,7 +298,7 @@ pub struct EllipseElem {
/// [box's documentation]($box.inset) for more details.
#[resolve]
#[fold]
- #[default(Sides::splat(Abs::pt(5.0).into()))]
+ #[default(Sides::splat(Some(Abs::pt(5.0).into())))]
pub inset: Sides<Option<Rel<Length>>>,
/// How much to expand the ellipse's size without affecting the layout. See
@@ -331,10 +331,10 @@ impl LayoutSingle for Packed<EllipseElem> {
&self.body(styles),
Axes::new(self.width(styles), self.height(styles)),
self.fill(styles),
- self.stroke(styles).map(Sides::splat),
+ self.stroke(styles).map(|s| Sides::splat(Some(s))),
self.inset(styles),
self.outset(styles),
- Corners::splat(Rel::zero()),
+ Corners::splat(None),
self.span(),
)
}
@@ -403,7 +403,7 @@ pub struct CircleElem {
/// [box's documentation]($box.inset) for more details.
#[resolve]
#[fold]
- #[default(Sides::splat(Abs::pt(5.0).into()))]
+ #[default(Sides::splat(Some(Abs::pt(5.0).into())))]
pub inset: Sides<Option<Rel<Length>>>,
/// How much to expand the circle's size without affecting the layout. See
@@ -434,10 +434,10 @@ impl LayoutSingle for Packed<CircleElem> {
&self.body(styles),
Axes::new(self.width(styles), self.height(styles)),
self.fill(styles),
- self.stroke(styles).map(Sides::splat),
+ self.stroke(styles).map(|s| Sides::splat(Some(s))),
self.inset(styles),
self.outset(styles),
- Corners::splat(Rel::zero()),
+ Corners::splat(None),
self.span(),
)
}
@@ -453,18 +453,21 @@ fn layout(
body: &Option<Content>,
sizing: Axes<Smart<Rel<Length>>>,
fill: Option<Paint>,
- stroke: Smart<Sides<Option<Stroke<Abs>>>>,
- mut inset: Sides<Rel<Abs>>,
- outset: Sides<Rel<Abs>>,
- radius: Corners<Rel<Abs>>,
+ stroke: Smart<Sides<Option<Option<Stroke<Abs>>>>>,
+ inset: Sides<Option<Rel<Abs>>>,
+ outset: Sides<Option<Rel<Abs>>>,
+ radius: Corners<Option<Rel<Abs>>>,
span: Span,
) -> SourceResult<Frame> {
let resolved = sizing
.zip_map(regions.base(), |s, r| s.map(|v| v.resolve(styles).relative_to(r)));
let mut frame;
+ let mut inset = inset.unwrap_or_default();
+
if let Some(child) = body {
let region = resolved.unwrap_or(regions.base());
+
if kind.is_round() {
inset = inset.map(|side| side + Ratio::new(0.5 - SQRT_2 / 4.0));
}
@@ -507,19 +510,27 @@ fn layout(
let stroke = match stroke {
Smart::Auto if fill.is_none() => Sides::splat(Some(FixedStroke::default())),
Smart::Auto => Sides::splat(None),
- Smart::Custom(strokes) => strokes.map(|s| s.map(Stroke::unwrap_or_default)),
+ Smart::Custom(strokes) => {
+ strokes.unwrap_or_default().map(|s| s.map(Stroke::unwrap_or_default))
+ }
};
// Add fill and/or stroke.
if fill.is_some() || stroke.iter().any(Option::is_some) {
if kind.is_round() {
- let outset = outset.relative_to(frame.size());
+ let outset = outset.unwrap_or_default().relative_to(frame.size());
let size = frame.size() + outset.sum_by_axis();
let pos = Point::new(-outset.left, -outset.top);
let shape = ellipse(size, fill, stroke.left);
frame.prepend(pos, FrameItem::Shape(shape, span));
} else {
- frame.fill_and_stroke(fill, stroke, outset, radius, span);
+ frame.fill_and_stroke(
+ fill,
+ stroke,
+ outset.unwrap_or_default(),
+ radius.unwrap_or_default(),
+ span,
+ );
}
}
diff --git a/crates/typst/src/visualize/stroke.rs b/crates/typst/src/visualize/stroke.rs
index 1d43a7b3..d060ad09 100644
--- a/crates/typst/src/visualize/stroke.rs
+++ b/crates/typst/src/visualize/stroke.rs
@@ -330,6 +330,19 @@ impl<T: Numeric + Repr> Repr for Stroke<T> {
}
}
+impl<T: Numeric + Fold> Fold for Stroke<T> {
+ fn fold(self, outer: Self) -> Self {
+ Self {
+ paint: self.paint.or(outer.paint),
+ thickness: self.thickness.or(outer.thickness),
+ cap: self.cap.or(outer.cap),
+ join: self.join.or(outer.join),
+ dash: self.dash.or(outer.dash),
+ miter_limit: self.miter_limit.or(outer.miter_limit),
+ }
+ }
+}
+
impl Resolve for Stroke {
type Output = Stroke<Abs>;
@@ -345,21 +358,6 @@ impl Resolve for Stroke {
}
}
-impl Fold for Stroke<Abs> {
- type Output = Self;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- Self {
- paint: self.paint.or(outer.paint),
- thickness: self.thickness.or(outer.thickness),
- cap: self.cap.or(outer.cap),
- join: self.join.or(outer.join),
- dash: self.dash.or(outer.dash),
- miter_limit: self.miter_limit.or(outer.miter_limit),
- }
- }
-}
-
cast! {
type Stroke,
thickness: Length => Self {