summaryrefslogtreecommitdiff
path: root/src/model
diff options
context:
space:
mode:
Diffstat (limited to 'src/model')
-rw-r--r--src/model/content.rs614
-rw-r--r--src/model/element.rs134
-rw-r--r--src/model/introspect.rs352
-rw-r--r--src/model/label.rs16
-rw-r--r--src/model/mod.rs148
-rw-r--r--src/model/realize.rs228
-rw-r--r--src/model/selector.rs296
-rw-r--r--src/model/styles.rs750
8 files changed, 0 insertions, 2538 deletions
diff --git a/src/model/content.rs b/src/model/content.rs
deleted file mode 100644
index 015f8b76..00000000
--- a/src/model/content.rs
+++ /dev/null
@@ -1,614 +0,0 @@
-use std::any::TypeId;
-use std::fmt::{self, Debug, Formatter, Write};
-use std::iter::Sum;
-use std::ops::{Add, AddAssign};
-
-use comemo::Prehashed;
-use ecow::{eco_format, EcoString, EcoVec};
-
-use super::{
- element, Behave, Behaviour, ElemFunc, Element, Fold, Guard, Label, Locatable,
- Location, Recipe, Selector, Style, Styles, Synthesize,
-};
-use crate::diag::{SourceResult, StrResult};
-use crate::doc::Meta;
-use crate::eval::{Dict, FromValue, IntoValue, Str, Value, Vm};
-use crate::syntax::Span;
-use crate::util::pretty_array_like;
-
-/// Composable representation of styled content.
-#[derive(Clone, Hash)]
-#[allow(clippy::derived_hash_with_manual_eq)]
-pub struct Content {
- func: ElemFunc,
- attrs: EcoVec<Attr>,
-}
-
-/// Attributes that can be attached to content.
-#[derive(Debug, Clone, PartialEq, Hash)]
-enum Attr {
- Span(Span),
- Field(EcoString),
- Value(Prehashed<Value>),
- Child(Prehashed<Content>),
- Styles(Styles),
- Prepared,
- Guard(Guard),
- Location(Location),
-}
-
-impl Content {
- /// Create an empty element.
- pub fn new(func: ElemFunc) -> Self {
- Self { func, attrs: EcoVec::new() }
- }
-
- /// Create empty content.
- pub fn empty() -> Self {
- Self::new(SequenceElem::func())
- }
-
- /// Create a new sequence element from multiples elements.
- pub fn sequence(iter: impl IntoIterator<Item = Self>) -> Self {
- let mut iter = iter.into_iter();
- let Some(first) = iter.next() else { return Self::empty() };
- let Some(second) = iter.next() else { return first };
- let mut content = Content::empty();
- content.attrs.push(Attr::Child(Prehashed::new(first)));
- content.attrs.push(Attr::Child(Prehashed::new(second)));
- content
- .attrs
- .extend(iter.map(|child| Attr::Child(Prehashed::new(child))));
- content
- }
-
- /// The element function of the contained content.
- pub fn func(&self) -> ElemFunc {
- self.func
- }
-
- /// Whether the content is an empty sequence.
- pub fn is_empty(&self) -> bool {
- self.is::<SequenceElem>() && self.attrs.is_empty()
- }
-
- /// Whether the contained element is of type `T`.
- pub fn is<T: Element>(&self) -> bool {
- self.func == T::func()
- }
-
- /// Cast to `T` if the contained element is of type `T`.
- pub fn to<T: Element>(&self) -> Option<&T> {
- T::unpack(self)
- }
-
- /// Access the children if this is a sequence.
- pub fn to_sequence(&self) -> Option<impl Iterator<Item = &Self>> {
- if !self.is::<SequenceElem>() {
- return None;
- }
- Some(self.attrs.iter().filter_map(Attr::child))
- }
-
- /// Access the child and styles.
- pub fn to_styled(&self) -> Option<(&Content, &Styles)> {
- if !self.is::<StyledElem>() {
- return None;
- }
- let child = self.attrs.iter().find_map(Attr::child)?;
- let styles = self.attrs.iter().find_map(Attr::styles)?;
- Some((child, styles))
- }
-
- /// Whether the contained element has the given capability.
- pub fn can<C>(&self) -> bool
- where
- C: ?Sized + 'static,
- {
- (self.func.0.vtable)(TypeId::of::<C>()).is_some()
- }
-
- /// Whether the contained element has the given capability.
- /// Where the capability is given by a `TypeId`.
- pub fn can_type_id(&self, type_id: TypeId) -> bool {
- (self.func.0.vtable)(type_id).is_some()
- }
-
- /// Cast to a trait object if the contained element has the given
- /// capability.
- pub fn with<C>(&self) -> Option<&C>
- where
- C: ?Sized + 'static,
- {
- let vtable = (self.func.0.vtable)(TypeId::of::<C>())?;
- let data = self as *const Self as *const ();
- Some(unsafe { &*crate::util::fat::from_raw_parts(data, vtable) })
- }
-
- /// Cast to a mutable trait object if the contained element has the given
- /// capability.
- pub fn with_mut<C>(&mut self) -> Option<&mut C>
- where
- C: ?Sized + 'static,
- {
- let vtable = (self.func.0.vtable)(TypeId::of::<C>())?;
- let data = self as *mut Self as *mut ();
- Some(unsafe { &mut *crate::util::fat::from_raw_parts_mut(data, vtable) })
- }
-
- /// The content's span.
- pub fn span(&self) -> Span {
- self.attrs.iter().find_map(Attr::span).unwrap_or(Span::detached())
- }
-
- /// Attach a span to the content if it doesn't already have one.
- pub fn spanned(mut self, span: Span) -> Self {
- if self.span().is_detached() {
- self.attrs.push(Attr::Span(span));
- }
- self
- }
-
- /// Attach a field to the content.
- pub fn with_field(
- mut self,
- name: impl Into<EcoString>,
- value: impl IntoValue,
- ) -> Self {
- self.push_field(name, value);
- self
- }
-
- /// Attach a field to the content.
- pub fn push_field(&mut self, name: impl Into<EcoString>, value: impl IntoValue) {
- let name = name.into();
- if let Some(i) = self.attrs.iter().position(|attr| match attr {
- Attr::Field(field) => *field == name,
- _ => false,
- }) {
- self.attrs.make_mut()[i + 1] =
- Attr::Value(Prehashed::new(value.into_value()));
- } else {
- self.attrs.push(Attr::Field(name));
- self.attrs.push(Attr::Value(Prehashed::new(value.into_value())));
- }
- }
-
- /// Access a field on the content.
- pub fn field(&self, name: &str) -> Option<Value> {
- if let (Some(iter), "children") = (self.to_sequence(), name) {
- Some(Value::Array(iter.cloned().map(Value::Content).collect()))
- } else if let (Some((child, _)), "child") = (self.to_styled(), name) {
- Some(Value::Content(child.clone()))
- } else {
- self.field_ref(name).cloned()
- }
- }
-
- /// Access a field on the content by reference.
- ///
- /// Does not include synthesized fields for sequence and styled elements.
- pub fn field_ref(&self, name: &str) -> Option<&Value> {
- self.fields_ref()
- .find(|&(field, _)| field == name)
- .map(|(_, value)| value)
- }
-
- /// Iter over all fields on the content.
- ///
- /// Does not include synthesized fields for sequence and styled elements.
- pub fn fields(&self) -> impl Iterator<Item = (&EcoString, Value)> {
- static CHILD: EcoString = EcoString::inline("child");
- static CHILDREN: EcoString = EcoString::inline("children");
-
- let option = if let Some(iter) = self.to_sequence() {
- Some((&CHILDREN, Value::Array(iter.cloned().map(Value::Content).collect())))
- } else if let Some((child, _)) = self.to_styled() {
- Some((&CHILD, Value::Content(child.clone())))
- } else {
- None
- };
-
- self.fields_ref()
- .map(|(name, value)| (name, value.clone()))
- .chain(option)
- }
-
- /// Iter over all fields on the content.
- ///
- /// Does not include synthesized fields for sequence and styled elements.
- pub fn fields_ref(&self) -> impl Iterator<Item = (&EcoString, &Value)> {
- let mut iter = self.attrs.iter();
- std::iter::from_fn(move || {
- let field = iter.find_map(Attr::field)?;
- let value = iter.next()?.value()?;
- Some((field, value))
- })
- }
-
- /// Try to access a field on the content as a specified type.
- pub fn cast_field<T: FromValue>(&self, name: &str) -> Option<T> {
- match self.field(name) {
- Some(value) => value.cast().ok(),
- None => None,
- }
- }
-
- /// Expect a field on the content to exist as a specified type.
- #[track_caller]
- pub fn expect_field<T: FromValue>(&self, name: &str) -> T {
- self.field(name).unwrap().cast().unwrap()
- }
-
- /// Whether the content has the specified field.
- pub fn has(&self, field: &str) -> bool {
- self.field(field).is_some()
- }
-
- /// Borrow the value of the given field.
- pub fn at(&self, field: &str, default: Option<Value>) -> StrResult<Value> {
- self.field(field)
- .or(default)
- .ok_or_else(|| missing_field_no_default(field))
- }
-
- /// Return the fields of the content as a dict.
- pub fn dict(&self) -> Dict {
- self.fields()
- .map(|(key, value)| (key.to_owned().into(), value))
- .collect()
- }
-
- /// The content's label.
- pub fn label(&self) -> Option<&Label> {
- match self.field_ref("label")? {
- Value::Label(label) => Some(label),
- _ => None,
- }
- }
-
- /// Attach a label to the content.
- pub fn labelled(self, label: Label) -> Self {
- self.with_field("label", label)
- }
-
- /// Style this content with a style entry.
- pub fn styled(mut self, style: impl Into<Style>) -> Self {
- if self.is::<StyledElem>() {
- let prev =
- self.attrs.make_mut().iter_mut().find_map(Attr::styles_mut).unwrap();
- prev.apply_one(style.into());
- self
- } else {
- self.styled_with_map(style.into().into())
- }
- }
-
- /// Style this content with a full style map.
- pub fn styled_with_map(mut self, styles: Styles) -> Self {
- if styles.is_empty() {
- return self;
- }
-
- if self.is::<StyledElem>() {
- let prev =
- self.attrs.make_mut().iter_mut().find_map(Attr::styles_mut).unwrap();
- prev.apply(styles);
- self
- } else {
- let mut content = Content::new(StyledElem::func());
- content.attrs.push(Attr::Child(Prehashed::new(self)));
- content.attrs.push(Attr::Styles(styles));
- content
- }
- }
-
- /// Style this content with a recipe, eagerly applying it if possible.
- pub fn styled_with_recipe(self, vm: &mut Vm, recipe: Recipe) -> SourceResult<Self> {
- if recipe.selector.is_none() {
- recipe.apply_vm(vm, self)
- } else {
- Ok(self.styled(recipe))
- }
- }
-
- /// Repeat this content `count` times.
- pub fn repeat(&self, count: usize) -> Self {
- Self::sequence(vec![self.clone(); count])
- }
-
- /// Disable a show rule recipe.
- pub fn guarded(mut self, guard: Guard) -> Self {
- self.attrs.push(Attr::Guard(guard));
- self
- }
-
- /// Check whether a show rule recipe is disabled.
- pub fn is_guarded(&self, guard: Guard) -> bool {
- self.attrs.contains(&Attr::Guard(guard))
- }
-
- /// Whether no show rule was executed for this content so far.
- pub fn is_pristine(&self) -> bool {
- !self.attrs.iter().any(|modifier| matches!(modifier, Attr::Guard(_)))
- }
-
- /// Whether this content has already been prepared.
- pub fn is_prepared(&self) -> bool {
- self.attrs.contains(&Attr::Prepared)
- }
-
- /// Mark this content as prepared.
- pub fn mark_prepared(&mut self) {
- self.attrs.push(Attr::Prepared);
- }
-
- /// Whether the content needs to be realized specially.
- pub fn needs_preparation(&self) -> bool {
- (self.can::<dyn Locatable>()
- || self.can::<dyn Synthesize>()
- || self.label().is_some())
- && !self.is_prepared()
- }
-
- /// This content's location in the document flow.
- pub fn location(&self) -> Option<Location> {
- self.attrs.iter().find_map(|modifier| match modifier {
- Attr::Location(location) => Some(*location),
- _ => None,
- })
- }
-
- /// Attach a location to this content.
- pub fn set_location(&mut self, location: Location) {
- self.attrs.push(Attr::Location(location));
- }
-
- /// Queries the content tree for all elements that match the given selector.
- ///
- /// Elements produced in `show` rules will not be included in the results.
- #[tracing::instrument(skip_all)]
- pub fn query(&self, selector: Selector) -> Vec<&Content> {
- let mut results = Vec::new();
- self.traverse(&mut |element| {
- if selector.matches(element) {
- results.push(element);
- }
- });
- results
- }
-
- /// Queries the content tree for the first element that match the given
- /// selector.
- ///
- /// Elements produced in `show` rules will not be included in the results.
- #[tracing::instrument(skip_all)]
- pub fn query_first(&self, selector: Selector) -> Option<&Content> {
- let mut result = None;
- self.traverse(&mut |element| {
- if result.is_none() && selector.matches(element) {
- result = Some(element);
- }
- });
- result
- }
-
- /// Extracts the plain text of this content.
- pub fn plain_text(&self) -> EcoString {
- let mut text = EcoString::new();
- self.traverse(&mut |element| {
- if let Some(textable) = element.with::<dyn PlainText>() {
- textable.plain_text(&mut text);
- }
- });
- text
- }
-
- /// Traverse this content.
- fn traverse<'a, F>(&'a self, f: &mut F)
- where
- F: FnMut(&'a Content),
- {
- f(self);
-
- for attr in &self.attrs {
- match attr {
- Attr::Child(child) => child.traverse(f),
- Attr::Value(value) => walk_value(value, f),
- _ => {}
- }
- }
-
- /// Walks a given value to find any content that matches the selector.
- fn walk_value<'a, F>(value: &'a Value, f: &mut F)
- where
- F: FnMut(&'a Content),
- {
- match value {
- Value::Content(content) => content.traverse(f),
- Value::Array(array) => {
- for value in array {
- walk_value(value, f);
- }
- }
- _ => {}
- }
- }
- }
-}
-
-impl Debug for Content {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- let name = self.func.name();
- if let Some(text) = item!(text_str)(self) {
- f.write_char('[')?;
- f.write_str(&text)?;
- f.write_char(']')?;
- return Ok(());
- } else if name == "space" {
- return f.write_str("[ ]");
- }
-
- let mut pieces: Vec<_> = self
- .fields()
- .map(|(name, value)| eco_format!("{name}: {value:?}"))
- .collect();
-
- if self.is::<StyledElem>() {
- pieces.push(EcoString::from(".."));
- }
-
- f.write_str(name)?;
- f.write_str(&pretty_array_like(&pieces, false))
- }
-}
-
-impl Default for Content {
- fn default() -> Self {
- Self::empty()
- }
-}
-
-impl PartialEq for Content {
- fn eq(&self, other: &Self) -> bool {
- if let (Some(left), Some(right)) = (self.to_sequence(), other.to_sequence()) {
- left.eq(right)
- } else if let (Some(left), Some(right)) = (self.to_styled(), other.to_styled()) {
- left == right
- } else {
- self.func == other.func && self.fields_ref().eq(other.fields_ref())
- }
- }
-}
-
-impl Add for Content {
- type Output = Self;
-
- fn add(self, mut rhs: Self) -> Self::Output {
- let mut lhs = self;
- match (lhs.is::<SequenceElem>(), rhs.is::<SequenceElem>()) {
- (true, true) => {
- lhs.attrs.extend(rhs.attrs);
- lhs
- }
- (true, false) => {
- lhs.attrs.push(Attr::Child(Prehashed::new(rhs)));
- lhs
- }
- (false, true) => {
- rhs.attrs.insert(0, Attr::Child(Prehashed::new(lhs)));
- rhs
- }
- (false, false) => Self::sequence([lhs, rhs]),
- }
- }
-}
-
-impl AddAssign for Content {
- fn add_assign(&mut self, rhs: Self) {
- *self = std::mem::take(self) + rhs;
- }
-}
-
-impl Sum for Content {
- fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
- Self::sequence(iter)
- }
-}
-
-impl Attr {
- fn child(&self) -> Option<&Content> {
- match self {
- Self::Child(child) => Some(child),
- _ => None,
- }
- }
-
- fn styles(&self) -> Option<&Styles> {
- match self {
- Self::Styles(styles) => Some(styles),
- _ => None,
- }
- }
-
- fn styles_mut(&mut self) -> Option<&mut Styles> {
- match self {
- Self::Styles(styles) => Some(styles),
- _ => None,
- }
- }
-
- fn field(&self) -> Option<&EcoString> {
- match self {
- Self::Field(field) => Some(field),
- _ => None,
- }
- }
-
- fn value(&self) -> Option<&Value> {
- match self {
- Self::Value(value) => Some(value),
- _ => None,
- }
- }
-
- fn span(&self) -> Option<Span> {
- match self {
- Self::Span(span) => Some(*span),
- _ => None,
- }
- }
-}
-
-/// Display: Sequence
-/// Category: special
-#[element]
-struct SequenceElem {}
-
-/// Display: Sequence
-/// Category: special
-#[element]
-struct StyledElem {}
-
-/// Hosts metadata and ensures metadata is produced even for empty elements.
-///
-/// Display: Meta
-/// Category: special
-#[element(Behave)]
-pub struct MetaElem {
- /// Metadata that should be attached to all elements affected by this style
- /// property.
- #[fold]
- pub data: Vec<Meta>,
-}
-
-impl Behave for MetaElem {
- fn behaviour(&self) -> Behaviour {
- Behaviour::Ignorant
- }
-}
-
-impl Fold for Vec<Meta> {
- type Output = Self;
-
- fn fold(mut self, outer: Self::Output) -> Self::Output {
- self.extend(outer);
- self
- }
-}
-
-/// Tries to extract the plain-text representation of the element.
-pub trait PlainText {
- /// Write this element's plain text into the given buffer.
- fn plain_text(&self, text: &mut EcoString);
-}
-
-/// The missing key access error message when no default value was given.
-#[cold]
-fn missing_field_no_default(key: &str) -> EcoString {
- eco_format!(
- "content does not contain field {:?} and \
- no default value was specified",
- Str::from(key)
- )
-}
diff --git a/src/model/element.rs b/src/model/element.rs
deleted file mode 100644
index c673ee41..00000000
--- a/src/model/element.rs
+++ /dev/null
@@ -1,134 +0,0 @@
-use std::any::TypeId;
-use std::fmt::{self, Debug, Formatter};
-use std::hash::{Hash, Hasher};
-
-use once_cell::sync::Lazy;
-
-use super::{Content, Selector, Styles};
-use crate::diag::SourceResult;
-use crate::eval::{cast, Args, Dict, Func, FuncInfo, Value, Vm};
-
-/// A document element.
-pub trait Element: Construct + Set + Sized + 'static {
- /// Pack the element into type-erased content.
- fn pack(self) -> Content;
-
- /// Extract this element from type-erased content.
- fn unpack(content: &Content) -> Option<&Self>;
-
- /// The element's function.
- fn func() -> ElemFunc;
-}
-
-/// An element's constructor function.
-pub trait Construct {
- /// Construct an element from the arguments.
- ///
- /// This is passed only the arguments that remain after execution of the
- /// element's set rule.
- fn construct(vm: &mut Vm, args: &mut Args) -> SourceResult<Content>;
-}
-
-/// An element's set rule.
-pub trait Set {
- /// Parse relevant arguments into style properties for this element.
- fn set(args: &mut Args) -> SourceResult<Styles>;
-}
-
-/// An element's function.
-#[derive(Copy, Clone)]
-pub struct ElemFunc(pub(super) &'static NativeElemFunc);
-
-impl ElemFunc {
- /// The function's name.
- pub fn name(self) -> &'static str {
- self.0.name
- }
-
- /// Apply the given arguments to the function.
- pub fn with(self, args: Args) -> Func {
- Func::from(self).with(args)
- }
-
- /// Extract details about the function.
- pub fn info(&self) -> &'static FuncInfo {
- &self.0.info
- }
-
- /// Construct an element.
- pub fn construct(self, vm: &mut Vm, args: &mut Args) -> SourceResult<Content> {
- (self.0.construct)(vm, args)
- }
-
- /// Whether the contained element has the given capability.
- pub fn can<C>(&self) -> bool
- where
- C: ?Sized + 'static,
- {
- (self.0.vtable)(TypeId::of::<C>()).is_some()
- }
-
- /// Create a selector for elements of this function.
- pub fn select(self) -> Selector {
- Selector::Elem(self, None)
- }
-
- /// Create a selector for elements of this function, filtering for those
- /// whose [fields](super::Content::field) match the given arguments.
- pub fn where_(self, fields: Dict) -> Selector {
- Selector::Elem(self, Some(fields))
- }
-
- /// Execute the set rule for the element and return the resulting style map.
- pub fn set(self, mut args: Args) -> SourceResult<Styles> {
- let styles = (self.0.set)(&mut args)?;
- args.finish()?;
- Ok(styles)
- }
-}
-
-impl Debug for ElemFunc {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.pad(self.name())
- }
-}
-
-impl Eq for ElemFunc {}
-
-impl PartialEq for ElemFunc {
- fn eq(&self, other: &Self) -> bool {
- std::ptr::eq(self.0, other.0)
- }
-}
-
-impl Hash for ElemFunc {
- fn hash<H: Hasher>(&self, state: &mut H) {
- state.write_usize(self.0 as *const _ as usize);
- }
-}
-
-cast! {
- ElemFunc,
- self => Value::Func(self.into()),
- v: Func => v.element().ok_or("expected element function")?,
-}
-
-impl From<&'static NativeElemFunc> for ElemFunc {
- fn from(native: &'static NativeElemFunc) -> Self {
- Self(native)
- }
-}
-
-/// An element function backed by a Rust type.
-pub struct NativeElemFunc {
- /// The element's name.
- pub name: &'static str,
- /// The element's vtable for capability dispatch.
- pub vtable: fn(of: TypeId) -> Option<*const ()>,
- /// The element's constructor.
- pub construct: fn(&mut Vm, &mut Args) -> SourceResult<Content>,
- /// The element's set rule.
- pub set: fn(&mut Args) -> SourceResult<Styles>,
- /// Details about the function.
- pub info: Lazy<FuncInfo>,
-}
diff --git a/src/model/introspect.rs b/src/model/introspect.rs
deleted file mode 100644
index 42c1a9e1..00000000
--- a/src/model/introspect.rs
+++ /dev/null
@@ -1,352 +0,0 @@
-use std::cell::RefCell;
-use std::collections::HashMap;
-use std::fmt::{self, Debug, Formatter};
-use std::hash::Hash;
-use std::num::NonZeroUsize;
-
-use comemo::{Prehashed, Track, Tracked, Validate};
-use ecow::EcoVec;
-use indexmap::IndexMap;
-
-use super::{Content, Selector};
-use crate::diag::{bail, StrResult};
-use crate::doc::{Frame, FrameItem, Meta, Position};
-use crate::eval::{cast, Value};
-use crate::geom::{Point, Transform};
-use crate::model::Label;
-use crate::util::NonZeroExt;
-
-/// Identifies the location of an element in the document.
-///
-/// This struct is created by [`Locator::locate`].
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub struct Location {
- /// The hash of the element.
- hash: u128,
- /// An unique number among elements with the same hash. This is the reason
- /// we need a `Locator` everywhere.
- disambiguator: usize,
- /// A synthetic location created from another one. This is used for example
- /// in bibliography management to create individual linkable locations for
- /// reference entries from the bibliography's location.
- variant: usize,
-}
-
-impl Location {
- /// Produce a variant of this location.
- pub fn variant(mut self, n: usize) -> Self {
- self.variant = n;
- self
- }
-}
-
-impl Debug for Location {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.pad("..")
- }
-}
-
-cast! {
- type Location: "location",
-}
-
-/// Provides locations for elements in the document.
-///
-/// A [`Location`] consists of an element's hash plus a disambiguator. Just the
-/// hash is not enough because we can have multiple equal elements with the same
-/// hash (not a hash collision, just equal elements!). Between these, we
-/// disambiguate with an increasing number. In principle, the disambiguator
-/// could just be counted up. However, counting is an impure operation and as
-/// such we can't count across a memoization boundary. [^1]
-///
-/// Instead, we only mutate within a single "layout run" and combine the results
-/// with disambiguators from an outer tracked locator. Thus, the locators form a
-/// "tracked chain". When a layout run ends, its mutations are discarded and, on
-/// the other side of the memoization boundary, we
-/// [reconstruct](Self::visit_frame) them from the resulting [frames](Frame).
-///
-/// [^1]: Well, we could with [`TrackedMut`](comemo::TrackedMut), but the
-/// overhead is quite high, especially since we need to save & undo the counting
-/// when only measuring.
-#[derive(Default)]
-pub struct Locator<'a> {
- /// Maps from a hash to the maximum number we've seen for this hash. This
- /// number becomes the `disambiguator`.
- hashes: RefCell<HashMap<u128, usize>>,
- /// An outer `Locator`, from which we can get disambiguator for hashes
- /// outside of the current "layout run".
- ///
- /// We need to override the constraint's lifetime here so that `Tracked` is
- /// covariant over the constraint. If it becomes invariant, we're in for a
- /// world of lifetime pain.
- outer: Option<Tracked<'a, Self, <Locator<'static> as Validate>::Constraint>>,
-}
-
-impl<'a> Locator<'a> {
- /// Create a new locator.
- pub fn new() -> Self {
- Self::default()
- }
-
- /// Create a new chained locator.
- pub fn chained(outer: Tracked<'a, Self>) -> Self {
- Self { outer: Some(outer), ..Default::default() }
- }
-
- /// Start tracking this locator.
- ///
- /// In comparison to [`Track::track`], this method skips this chain link
- /// if it does not contribute anything.
- pub fn track(&self) -> Tracked<'_, Self> {
- match self.outer {
- Some(outer) if self.hashes.borrow().is_empty() => outer,
- _ => Track::track(self),
- }
- }
-
- /// Produce a stable identifier for this call site.
- pub fn locate(&mut self, hash: u128) -> Location {
- // Get the current disambiguator for this hash.
- let disambiguator = self.disambiguator_impl(hash);
-
- // Bump the next disambiguator up by one.
- self.hashes.borrow_mut().insert(hash, disambiguator + 1);
-
- // Create the location in its default variant.
- Location { hash, disambiguator, variant: 0 }
- }
-
- /// Advance past a frame.
- pub fn visit_frame(&mut self, frame: &Frame) {
- for (_, item) in frame.items() {
- match item {
- FrameItem::Group(group) => self.visit_frame(&group.frame),
- FrameItem::Meta(Meta::Elem(elem), _) => {
- let mut hashes = self.hashes.borrow_mut();
- let loc = elem.location().unwrap();
- let entry = hashes.entry(loc.hash).or_default();
-
- // Next disambiguator needs to be at least one larger than
- // the maximum we've seen so far.
- *entry = (*entry).max(loc.disambiguator + 1);
- }
- _ => {}
- }
- }
- }
-
- /// Advance past a number of frames.
- pub fn visit_frames<'b>(&mut self, frames: impl IntoIterator<Item = &'b Frame>) {
- for frame in frames {
- self.visit_frame(frame);
- }
- }
-
- /// The current disambiguator for the given hash.
- fn disambiguator_impl(&self, hash: u128) -> usize {
- *self
- .hashes
- .borrow_mut()
- .entry(hash)
- .or_insert_with(|| self.outer.map_or(0, |outer| outer.disambiguator(hash)))
- }
-}
-
-#[comemo::track]
-impl<'a> Locator<'a> {
- /// The current disambiguator for the hash.
- fn disambiguator(&self, hash: u128) -> usize {
- self.disambiguator_impl(hash)
- }
-}
-
-/// Can be queried for elements and their positions.
-pub struct Introspector {
- /// The number of pages in the document.
- pages: usize,
- /// All introspectable elements.
- elems: IndexMap<Location, (Prehashed<Content>, Position)>,
- /// The page numberings, indexed by page number minus 1.
- page_numberings: Vec<Value>,
- /// Caches queries done on the introspector. This is important because
- /// even if all top-level queries are distinct, they often have shared
- /// subqueries. Example: Individual counter queries with `before` that
- /// all depend on a global counter query.
- queries: RefCell<HashMap<u128, EcoVec<Prehashed<Content>>>>,
-}
-
-impl Introspector {
- /// Create a new introspector.
- #[tracing::instrument(skip(frames))]
- pub fn new(frames: &[Frame]) -> Self {
- let mut introspector = Self {
- pages: frames.len(),
- elems: IndexMap::new(),
- page_numberings: vec![],
- queries: RefCell::default(),
- };
- for (i, frame) in frames.iter().enumerate() {
- let page = NonZeroUsize::new(1 + i).unwrap();
- introspector.extract(frame, page, Transform::identity());
- }
- introspector
- }
-
- /// Extract metadata from a frame.
- #[tracing::instrument(skip_all)]
- fn extract(&mut self, frame: &Frame, page: NonZeroUsize, ts: Transform) {
- for (pos, item) in frame.items() {
- match item {
- FrameItem::Group(group) => {
- let ts = ts
- .pre_concat(Transform::translate(pos.x, pos.y))
- .pre_concat(group.transform);
- self.extract(&group.frame, page, ts);
- }
- FrameItem::Meta(Meta::Elem(content), _)
- if !self.elems.contains_key(&content.location().unwrap()) =>
- {
- let pos = pos.transform(ts);
- let ret = self.elems.insert(
- content.location().unwrap(),
- (Prehashed::new(content.clone()), Position { page, point: pos }),
- );
- assert!(ret.is_none(), "duplicate locations");
- }
- FrameItem::Meta(Meta::PageNumbering(numbering), _) => {
- self.page_numberings.push(numbering.clone());
- }
- _ => {}
- }
- }
- }
-
- /// Iterate over all locatable elements.
- pub fn all(&self) -> impl Iterator<Item = &Prehashed<Content>> + '_ {
- self.elems.values().map(|(c, _)| c)
- }
-
- /// Get an element by its location.
- fn get(&self, location: &Location) -> Option<&Prehashed<Content>> {
- self.elems.get(location).map(|(elem, _)| elem)
- }
-
- /// Get the index of this element among all.
- fn index(&self, elem: &Content) -> usize {
- self.elems
- .get_index_of(&elem.location().unwrap())
- .unwrap_or(usize::MAX)
- }
-}
-
-#[comemo::track]
-impl Introspector {
- /// Query for all matching elements.
- pub fn query(&self, selector: &Selector) -> EcoVec<Prehashed<Content>> {
- let hash = crate::util::hash128(selector);
- if let Some(output) = self.queries.borrow().get(&hash) {
- return output.clone();
- }
-
- let output = match selector {
- Selector::Elem(..)
- | Selector::Label(_)
- | Selector::Regex(_)
- | Selector::Can(_)
- | Selector::Or(_)
- | Selector::And(_) => {
- self.all().filter(|elem| selector.matches(elem)).cloned().collect()
- }
-
- Selector::Location(location) => {
- self.get(location).cloned().into_iter().collect()
- }
- Selector::Before { selector, end, inclusive } => {
- let mut list = self.query(selector);
- if let Some(end) = self.query_first(end) {
- // Determine which elements are before `end`.
- let split = match list
- .binary_search_by_key(&self.index(&end), |elem| self.index(elem))
- {
- // Element itself is contained.
- Ok(i) => i + *inclusive as usize,
- // Element itself is not contained.
- Err(i) => i,
- };
- list = list[..split].into();
- }
- list
- }
- Selector::After { selector, start, inclusive } => {
- let mut list = self.query(selector);
- if let Some(start) = self.query_first(start) {
- // Determine which elements are after `start`.
- let split = match list
- .binary_search_by_key(&self.index(&start), |elem| {
- self.index(elem)
- }) {
- // Element itself is contained.
- Ok(i) => i + !*inclusive as usize,
- // Element itself is not contained.
- Err(i) => i,
- };
- list = list[split..].into();
- }
- list
- }
- };
-
- self.queries.borrow_mut().insert(hash, output.clone());
- output
- }
-
- /// Query for the first element that matches the selector.
- pub fn query_first(&self, selector: &Selector) -> Option<Prehashed<Content>> {
- match selector {
- Selector::Location(location) => self.get(location).cloned(),
- _ => self.query(selector).first().cloned(),
- }
- }
-
- /// Query for a unique element with the label.
- pub fn query_label(&self, label: &Label) -> StrResult<Prehashed<Content>> {
- let mut found = None;
- for elem in self.all().filter(|elem| elem.label() == Some(label)) {
- if found.is_some() {
- bail!("label occurs multiple times in the document");
- }
- found = Some(elem.clone());
- }
- found.ok_or_else(|| "label does not exist in the document".into())
- }
-
- /// The total number pages.
- pub fn pages(&self) -> NonZeroUsize {
- NonZeroUsize::new(self.pages).unwrap_or(NonZeroUsize::ONE)
- }
-
- /// Gets the page numbering for the given location, if any.
- pub fn page_numbering(&self, location: Location) -> Value {
- let page = self.page(location);
- self.page_numberings.get(page.get() - 1).cloned().unwrap_or_default()
- }
-
- /// Find the page number for the given location.
- pub fn page(&self, location: Location) -> NonZeroUsize {
- self.position(location).page
- }
-
- /// Find the position for the given location.
- pub fn position(&self, location: Location) -> Position {
- self.elems
- .get(&location)
- .map(|(_, loc)| *loc)
- .unwrap_or(Position { page: NonZeroUsize::ONE, point: Point::zero() })
- }
-}
-
-impl Default for Introspector {
- fn default() -> Self {
- Self::new(&[])
- }
-}
diff --git a/src/model/label.rs b/src/model/label.rs
deleted file mode 100644
index ef8f3edd..00000000
--- a/src/model/label.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-use std::fmt::{self, Debug, Formatter};
-
-use ecow::EcoString;
-
-/// A label for an element.
-#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub struct Label(pub EcoString);
-
-impl Debug for Label {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "<{}>", self.0)
- }
-}
-
-/// Indicates that an element cannot be labelled.
-pub trait Unlabellable {}
diff --git a/src/model/mod.rs b/src/model/mod.rs
deleted file mode 100644
index ee940236..00000000
--- a/src/model/mod.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-//! The document model.
-
-mod content;
-mod element;
-mod introspect;
-mod label;
-mod realize;
-mod selector;
-mod styles;
-
-#[doc(inline)]
-pub use typst_macros::element;
-
-pub use self::content::{Content, MetaElem, PlainText};
-pub use self::element::{Construct, ElemFunc, Element, NativeElemFunc, Set};
-pub use self::introspect::{Introspector, Location, Locator};
-pub use self::label::{Label, Unlabellable};
-pub use self::realize::{
- applicable, realize, Behave, Behaviour, Finalize, Guard, Locatable, Show, Synthesize,
-};
-pub use self::selector::{LocatableSelector, Selector, ShowableSelector};
-pub use self::styles::{
- Fold, Property, Recipe, Resolve, Style, StyleChain, StyleVec, StyleVecBuilder,
- Styles, Transform,
-};
-
-use std::mem::ManuallyDrop;
-
-use comemo::{Track, Tracked, TrackedMut, Validate};
-
-use crate::diag::{SourceError, SourceResult};
-use crate::doc::Document;
-use crate::eval::Tracer;
-use crate::World;
-
-/// Typeset content into a fully layouted document.
-#[comemo::memoize]
-#[tracing::instrument(skip(world, tracer, content))]
-pub fn typeset(
- world: Tracked<dyn World + '_>,
- mut tracer: TrackedMut<Tracer>,
- content: &Content,
-) -> SourceResult<Document> {
- tracing::info!("Starting typesetting");
-
- let library = world.library();
- let styles = StyleChain::new(&library.styles);
-
- let mut iter = 0;
- let mut document;
- let mut delayed;
-
- // We need `ManuallyDrop` until this lands in stable:
- // https://github.com/rust-lang/rust/issues/70919
- let mut introspector = ManuallyDrop::new(Introspector::new(&[]));
-
- // Relayout until all introspections stabilize.
- // If that doesn't happen within five attempts, we give up.
- loop {
- tracing::info!("Layout iteration {iter}");
-
- delayed = DelayedErrors::default();
-
- let constraint = <Introspector as Validate>::Constraint::new();
- let mut locator = Locator::new();
- let mut vt = Vt {
- world,
- tracer: TrackedMut::reborrow_mut(&mut tracer),
- locator: &mut locator,
- introspector: introspector.track_with(&constraint),
- delayed: delayed.track_mut(),
- };
-
- // Layout!
- let result = (library.items.layout)(&mut vt, content, styles)?;
-
- // Drop the old introspector.
- ManuallyDrop::into_inner(introspector);
-
- // Only now assign the document and construct the new introspector.
- document = result;
- introspector = ManuallyDrop::new(Introspector::new(&document.pages));
- iter += 1;
-
- if iter >= 5 || introspector.validate(&constraint) {
- break;
- }
- }
-
- // Drop the introspector.
- ManuallyDrop::into_inner(introspector);
-
- // Promote delayed errors.
- if !delayed.0.is_empty() {
- return Err(Box::new(delayed.0));
- }
-
- Ok(document)
-}
-
-/// A virtual typesetter.
-///
-/// Holds the state needed to [typeset] content.
-pub struct Vt<'a> {
- /// The compilation environment.
- pub world: Tracked<'a, dyn World + 'a>,
- /// Provides access to information about the document.
- pub introspector: Tracked<'a, Introspector>,
- /// Provides stable identities to elements.
- pub locator: &'a mut Locator<'a>,
- /// Delayed errors that do not immediately terminate execution.
- pub delayed: TrackedMut<'a, DelayedErrors>,
- /// The tracer for inspection of the values an expression produces.
- pub tracer: TrackedMut<'a, Tracer>,
-}
-
-impl Vt<'_> {
- /// Perform a fallible operation that does not immediately terminate further
- /// execution. Instead it produces a delayed error that is only promoted to
- /// a fatal one if it remains at the end of the introspection loop.
- pub fn delayed<F, T>(&mut self, f: F) -> T
- where
- F: FnOnce(&mut Self) -> SourceResult<T>,
- T: Default,
- {
- match f(self) {
- Ok(value) => value,
- Err(errors) => {
- for error in *errors {
- self.delayed.push(error);
- }
- T::default()
- }
- }
- }
-}
-
-/// Holds delayed errors.
-#[derive(Default, Clone)]
-pub struct DelayedErrors(Vec<SourceError>);
-
-#[comemo::track]
-impl DelayedErrors {
- /// Push a delayed error.
- fn push(&mut self, error: SourceError) {
- self.0.push(error);
- }
-}
diff --git a/src/model/realize.rs b/src/model/realize.rs
deleted file mode 100644
index 01c46b81..00000000
--- a/src/model/realize.rs
+++ /dev/null
@@ -1,228 +0,0 @@
-use super::{Content, ElemFunc, Element, MetaElem, Recipe, Selector, StyleChain, Vt};
-use crate::diag::SourceResult;
-use crate::doc::Meta;
-use crate::util::hash128;
-
-/// Whether the target is affected by show rules in the given style chain.
-pub fn applicable(target: &Content, styles: StyleChain) -> bool {
- if target.needs_preparation() {
- return true;
- }
-
- if target.can::<dyn Show>() && target.is_pristine() {
- return true;
- }
-
- // Find out how many recipes there are.
- let mut n = styles.recipes().count();
-
- // Find out whether any recipe matches and is unguarded.
- for recipe in styles.recipes() {
- if recipe.applicable(target) && !target.is_guarded(Guard::Nth(n)) {
- return true;
- }
- n -= 1;
- }
-
- false
-}
-
-/// Apply the show rules in the given style chain to a target.
-pub fn realize(
- vt: &mut Vt,
- target: &Content,
- styles: StyleChain,
-) -> SourceResult<Option<Content>> {
- // Pre-process.
- if target.needs_preparation() {
- let mut elem = target.clone();
- if target.can::<dyn Locatable>() || target.label().is_some() {
- let location = vt.locator.locate(hash128(target));
- elem.set_location(location);
- }
-
- if let Some(elem) = elem.with_mut::<dyn Synthesize>() {
- elem.synthesize(vt, styles)?;
- }
-
- elem.mark_prepared();
-
- if elem.location().is_some() {
- let span = elem.span();
- let meta = Meta::Elem(elem.clone());
- return Ok(Some(
- (elem + MetaElem::new().pack().spanned(span))
- .styled(MetaElem::set_data(vec![meta])),
- ));
- }
-
- return Ok(Some(elem));
- }
-
- // Find out how many recipes there are.
- let mut n = styles.recipes().count();
-
- // Find an applicable recipe.
- let mut realized = None;
- for recipe in styles.recipes() {
- let guard = Guard::Nth(n);
- if recipe.applicable(target) && !target.is_guarded(guard) {
- if let Some(content) = try_apply(vt, target, recipe, guard)? {
- realized = Some(content);
- break;
- }
- }
- n -= 1;
- }
-
- // Realize if there was no matching recipe.
- if let Some(showable) = target.with::<dyn Show>() {
- let guard = Guard::Base(target.func());
- if realized.is_none() && !target.is_guarded(guard) {
- realized = Some(showable.show(vt, styles)?);
- }
- }
-
- // Finalize only if this is the first application for this element.
- if let Some(elem) = target.with::<dyn Finalize>() {
- if target.is_pristine() {
- if let Some(already) = realized {
- realized = Some(elem.finalize(already, styles));
- }
- }
- }
-
- Ok(realized)
-}
-
-/// Try to apply a recipe to the target.
-fn try_apply(
- vt: &mut Vt,
- target: &Content,
- recipe: &Recipe,
- guard: Guard,
-) -> SourceResult<Option<Content>> {
- match &recipe.selector {
- Some(Selector::Elem(element, _)) => {
- if target.func() != *element {
- return Ok(None);
- }
-
- recipe.apply_vt(vt, target.clone().guarded(guard)).map(Some)
- }
-
- Some(Selector::Label(label)) => {
- if target.label() != Some(label) {
- return Ok(None);
- }
-
- recipe.apply_vt(vt, target.clone().guarded(guard)).map(Some)
- }
-
- Some(Selector::Regex(regex)) => {
- let Some(text) = item!(text_str)(target) else {
- return Ok(None);
- };
-
- let make = |s: &str| target.clone().with_field("text", s);
- let mut result = vec![];
- let mut cursor = 0;
-
- for m in regex.find_iter(&text) {
- let start = m.start();
- if cursor < start {
- result.push(make(&text[cursor..start]));
- }
-
- let piece = make(m.as_str()).guarded(guard);
- let transformed = recipe.apply_vt(vt, piece)?;
- result.push(transformed);
- cursor = m.end();
- }
-
- if result.is_empty() {
- return Ok(None);
- }
-
- if cursor < text.len() {
- result.push(make(&text[cursor..]));
- }
-
- Ok(Some(Content::sequence(result)))
- }
-
- // Not supported here.
- Some(
- Selector::Or(_)
- | Selector::And(_)
- | Selector::Location(_)
- | Selector::Can(_)
- | Selector::Before { .. }
- | Selector::After { .. },
- ) => Ok(None),
-
- None => Ok(None),
- }
-}
-
-/// Makes this element locatable through `vt.locate`.
-pub trait Locatable {}
-
-/// Synthesize fields on an element. This happens before execution of any show
-/// rule.
-pub trait Synthesize {
- /// Prepare the element for show rule application.
- fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()>;
-}
-
-/// The base recipe for an element.
-pub trait Show {
- /// Execute the base recipe for this element.
- fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content>;
-}
-
-/// Post-process an element after it was realized.
-pub trait Finalize {
- /// Finalize the fully realized form of the element. Use this for effects
- /// that should work even in the face of a user-defined show rule.
- fn finalize(&self, realized: Content, styles: StyleChain) -> Content;
-}
-
-/// How the element interacts with other elements.
-pub trait Behave {
- /// The element's interaction behaviour.
- fn behaviour(&self) -> Behaviour;
-
- /// Whether this weak element is larger than a previous one and thus picked
- /// as the maximum when the levels are the same.
- #[allow(unused_variables)]
- fn larger(&self, prev: &Content) -> bool {
- false
- }
-}
-
-/// How an element interacts with other elements in a stream.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub enum Behaviour {
- /// A weak element which only survives when a supportive element is before
- /// and after it. Furthermore, per consecutive run of weak elements, only
- /// one survives: The one with the lowest weakness level (or the larger one
- /// if there is a tie).
- Weak(usize),
- /// An element that enables adjacent weak elements to exist. The default.
- Supportive,
- /// An element that destroys adjacent weak elements.
- Destructive,
- /// An element that does not interact at all with other elements, having the
- /// same effect as if it didn't exist.
- Ignorant,
-}
-
-/// Guards content against being affected by the same show rule multiple times.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Guard {
- /// The nth recipe from the top of the chain.
- Nth(usize),
- /// The [base recipe](Show) for a kind of element.
- Base(ElemFunc),
-}
diff --git a/src/model/selector.rs b/src/model/selector.rs
deleted file mode 100644
index 9723ee4f..00000000
--- a/src/model/selector.rs
+++ /dev/null
@@ -1,296 +0,0 @@
-use std::any::{Any, TypeId};
-use std::fmt::{self, Debug, Formatter, Write};
-use std::sync::Arc;
-
-use ecow::{eco_format, EcoString, EcoVec};
-
-use super::{Content, ElemFunc, Label, Location};
-use crate::diag::{bail, StrResult};
-use crate::eval::{
- cast, CastInfo, Dict, FromValue, Func, IntoValue, Reflect, Regex, Value,
-};
-use crate::model::Locatable;
-use crate::util::pretty_array_like;
-
-/// A selector in a show rule.
-#[derive(Clone, PartialEq, Hash)]
-pub enum Selector {
- /// Matches a specific type of element.
- ///
- /// If there is a dictionary, only elements with the fields from the
- /// dictionary match.
- Elem(ElemFunc, Option<Dict>),
- /// Matches the element at the specified location.
- Location(Location),
- /// Matches elements with a specific label.
- Label(Label),
- /// Matches text elements through a regular expression.
- Regex(Regex),
- /// Matches elements with a specific capability.
- Can(TypeId),
- /// Matches if any of the subselectors match.
- Or(EcoVec<Self>),
- /// Matches if all of the subselectors match.
- And(EcoVec<Self>),
- /// Matches all matches of `selector` before `end`.
- Before { selector: Arc<Self>, end: Arc<Self>, inclusive: bool },
- /// Matches all matches of `selector` after `start`.
- After { selector: Arc<Self>, start: Arc<Self>, inclusive: bool },
-}
-
-impl Selector {
- /// Define a simple text selector.
- pub fn text(text: &str) -> Self {
- Self::Regex(Regex::new(&regex::escape(text)).unwrap())
- }
-
- /// Define a simple [`Selector::Can`] selector.
- pub fn can<T: ?Sized + Any>() -> Self {
- Self::Can(TypeId::of::<T>())
- }
-
- /// Transforms this selector and an iterator of other selectors into a
- /// [`Selector::Or`] selector.
- pub fn and(self, others: impl IntoIterator<Item = Self>) -> Self {
- Self::And(others.into_iter().chain(Some(self)).collect())
- }
-
- /// Transforms this selector and an iterator of other selectors into a
- /// [`Selector::And`] selector.
- pub fn or(self, others: impl IntoIterator<Item = Self>) -> Self {
- Self::Or(others.into_iter().chain(Some(self)).collect())
- }
-
- /// Transforms this selector into a [`Selector::Before`] selector.
- pub fn before(self, location: impl Into<Self>, inclusive: bool) -> Self {
- Self::Before {
- selector: Arc::new(self),
- end: Arc::new(location.into()),
- inclusive,
- }
- }
-
- /// Transforms this selector into a [`Selector::After`] selector.
- pub fn after(self, location: impl Into<Self>, inclusive: bool) -> Self {
- Self::After {
- selector: Arc::new(self),
- start: Arc::new(location.into()),
- inclusive,
- }
- }
-
- /// Whether the selector matches for the target.
- pub fn matches(&self, target: &Content) -> bool {
- match self {
- Self::Elem(element, dict) => {
- target.func() == *element
- && dict
- .iter()
- .flat_map(|dict| dict.iter())
- .all(|(name, value)| target.field_ref(name) == Some(value))
- }
- Self::Label(label) => target.label() == Some(label),
- Self::Regex(regex) => {
- target.func() == item!(text_func)
- && item!(text_str)(target).map_or(false, |text| regex.is_match(&text))
- }
- Self::Can(cap) => target.can_type_id(*cap),
- Self::Or(selectors) => selectors.iter().any(move |sel| sel.matches(target)),
- Self::And(selectors) => selectors.iter().all(move |sel| sel.matches(target)),
- Self::Location(location) => target.location() == Some(*location),
- // Not supported here.
- Self::Before { .. } | Self::After { .. } => false,
- }
- }
-}
-
-impl From<Location> for Selector {
- fn from(value: Location) -> Self {
- Self::Location(value)
- }
-}
-
-impl Debug for Selector {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match self {
- Self::Elem(elem, dict) => {
- f.write_str(elem.name())?;
- if let Some(dict) = dict {
- f.write_str(".where")?;
- dict.fmt(f)?;
- }
- Ok(())
- }
- Self::Label(label) => label.fmt(f),
- Self::Regex(regex) => regex.fmt(f),
- Self::Can(cap) => cap.fmt(f),
- Self::Or(selectors) | Self::And(selectors) => {
- f.write_str(if matches!(self, Self::Or(_)) { "or" } else { "and" })?;
- let pieces: Vec<_> =
- selectors.iter().map(|sel| eco_format!("{sel:?}")).collect();
- f.write_str(&pretty_array_like(&pieces, false))
- }
- Self::Location(loc) => loc.fmt(f),
- Self::Before { selector, end: split, inclusive }
- | Self::After { selector, start: split, inclusive } => {
- selector.fmt(f)?;
-
- if matches!(self, Self::Before { .. }) {
- f.write_str(".before(")?;
- } else {
- f.write_str(".after(")?;
- }
-
- split.fmt(f)?;
- if !*inclusive {
- f.write_str(", inclusive: false")?;
- }
- f.write_char(')')
- }
- }
- }
-}
-
-cast! {
- type Selector: "selector",
- func: Func => func
- .element()
- .ok_or("only element functions can be used as selectors")?
- .select(),
- label: Label => Self::Label(label),
- text: EcoString => Self::text(&text),
- regex: Regex => Self::Regex(regex),
- location: Location => Self::Location(location),
-}
-
-/// A selector that can be used with `query`.
-///
-/// Hopefully, this is made obsolete by a more powerful query mechanism in the
-/// future.
-#[derive(Clone, PartialEq, Hash)]
-pub struct LocatableSelector(pub Selector);
-
-impl Reflect for LocatableSelector {
- fn describe() -> CastInfo {
- CastInfo::Union(vec![
- CastInfo::Type("function"),
- CastInfo::Type("label"),
- CastInfo::Type("selector"),
- ])
- }
-
- fn castable(value: &Value) -> bool {
- matches!(value.type_name(), "function" | "label" | "selector")
- }
-}
-
-impl IntoValue for LocatableSelector {
- fn into_value(self) -> Value {
- self.0.into_value()
- }
-}
-
-impl FromValue for LocatableSelector {
- fn from_value(value: Value) -> StrResult<Self> {
- fn validate(selector: &Selector) -> StrResult<()> {
- match selector {
- Selector::Elem(elem, _) => {
- if !elem.can::<dyn Locatable>() {
- Err(eco_format!("{} is not locatable", elem.name()))?
- }
- }
- Selector::Location(_) => {}
- Selector::Label(_) => {}
- Selector::Regex(_) => bail!("text is not locatable"),
- Selector::Can(_) => bail!("capability is not locatable"),
- Selector::Or(list) | Selector::And(list) => {
- for selector in list {
- validate(selector)?;
- }
- }
- Selector::Before { selector, end: split, .. }
- | Selector::After { selector, start: split, .. } => {
- for selector in [selector, split] {
- validate(selector)?;
- }
- }
- }
- Ok(())
- }
-
- if !Self::castable(&value) {
- return Err(Self::error(&value));
- }
-
- let selector = Selector::from_value(value)?;
- validate(&selector)?;
- Ok(Self(selector))
- }
-}
-
-/// A selector that can be used with show rules.
-///
-/// Hopefully, this is made obsolete by a more powerful showing mechanism in the
-/// future.
-#[derive(Clone, PartialEq, Hash)]
-pub struct ShowableSelector(pub Selector);
-
-impl Reflect for ShowableSelector {
- fn describe() -> CastInfo {
- CastInfo::Union(vec![
- CastInfo::Type("function"),
- CastInfo::Type("label"),
- CastInfo::Type("string"),
- CastInfo::Type("regular expression"),
- CastInfo::Type("symbol"),
- CastInfo::Type("selector"),
- ])
- }
-
- fn castable(value: &Value) -> bool {
- matches!(
- value.type_name(),
- "symbol"
- | "string"
- | "label"
- | "function"
- | "regular expression"
- | "selector"
- )
- }
-}
-
-impl IntoValue for ShowableSelector {
- fn into_value(self) -> Value {
- self.0.into_value()
- }
-}
-
-impl FromValue for ShowableSelector {
- fn from_value(value: Value) -> StrResult<Self> {
- fn validate(selector: &Selector) -> StrResult<()> {
- match selector {
- Selector::Elem(_, _) => {}
- Selector::Label(_) => {}
- Selector::Regex(_) => {}
- Selector::Or(_)
- | Selector::And(_)
- | Selector::Location(_)
- | Selector::Can(_)
- | Selector::Before { .. }
- | Selector::After { .. } => {
- bail!("this selector cannot be used with show")
- }
- }
- Ok(())
- }
-
- if !Self::castable(&value) {
- return Err(Self::error(&value));
- }
-
- let selector = Selector::from_value(value)?;
- validate(&selector)?;
- Ok(Self(selector))
- }
-}
diff --git a/src/model/styles.rs b/src/model/styles.rs
deleted file mode 100644
index 23748a3f..00000000
--- a/src/model/styles.rs
+++ /dev/null
@@ -1,750 +0,0 @@
-use std::fmt::{self, Debug, Formatter, Write};
-use std::iter;
-use std::mem;
-use std::ptr;
-
-use comemo::Prehashed;
-use ecow::{eco_vec, EcoString, EcoVec};
-
-use super::{Content, ElemFunc, Element, Selector, Vt};
-use crate::diag::{SourceResult, Trace, Tracepoint};
-use crate::eval::{cast, Args, FromValue, Func, IntoValue, Value, Vm};
-use crate::syntax::Span;
-
-/// A list of style properties.
-#[derive(Default, PartialEq, Clone, Hash)]
-pub struct Styles(EcoVec<Prehashed<Style>>);
-
-impl Styles {
- /// Create a new, empty style list.
- pub fn new() -> Self {
- Self::default()
- }
-
- /// Whether this contains no styles.
- pub fn is_empty(&self) -> bool {
- self.0.is_empty()
- }
-
- /// Set an inner value for a style property.
- ///
- /// If the property needs folding and the value is already contained in the
- /// style map, `self` contributes the outer values and `value` is the inner
- /// one.
- pub fn set(&mut self, style: impl Into<Style>) {
- self.0.push(Prehashed::new(style.into()));
- }
-
- /// Remove the style that was last set.
- pub fn unset(&mut self) {
- self.0.pop();
- }
-
- /// Apply outer styles. Like [`chain`](StyleChain::chain), but in-place.
- pub fn apply(&mut self, mut outer: Self) {
- outer.0.extend(mem::take(self).0.into_iter());
- *self = outer;
- }
-
- /// Apply one outer styles.
- pub fn apply_one(&mut self, outer: Style) {
- self.0.insert(0, Prehashed::new(outer));
- }
-
- /// Apply a slice of outer styles.
- pub fn apply_slice(&mut self, outer: &[Prehashed<Style>]) {
- self.0 = outer.iter().cloned().chain(mem::take(self).0.into_iter()).collect();
- }
-
- /// Add an origin span to all contained properties.
- pub fn spanned(mut self, span: Span) -> Self {
- for entry in self.0.make_mut() {
- entry.update(|entry| {
- if let Style::Property(property) = entry {
- property.span = Some(span);
- }
- });
- }
- self
- }
-
- /// Returns `Some(_)` with an optional span if this list contains
- /// styles for the given element.
- pub fn interruption<T: Element>(&self) -> Option<Option<Span>> {
- let func = T::func();
- self.0.iter().find_map(|entry| match &**entry {
- Style::Property(property) => property.is_of(func).then_some(property.span),
- Style::Recipe(recipe) => recipe.is_of(func).then_some(Some(recipe.span)),
- })
- }
-}
-
-impl From<Style> for Styles {
- fn from(entry: Style) -> Self {
- Self(eco_vec![Prehashed::new(entry)])
- }
-}
-
-impl Debug for Styles {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.pad("..")
- }
-}
-
-/// A single style property or recipe.
-#[derive(Clone, PartialEq, Hash)]
-pub enum Style {
- /// A style property originating from a set rule or constructor.
- Property(Property),
- /// A show rule recipe.
- Recipe(Recipe),
-}
-
-impl Style {
- /// If this is a property, return it.
- pub fn property(&self) -> Option<&Property> {
- match self {
- Self::Property(property) => Some(property),
- _ => None,
- }
- }
-
- /// If this is a recipe, return it.
- pub fn recipe(&self) -> Option<&Recipe> {
- match self {
- Self::Recipe(recipe) => Some(recipe),
- _ => None,
- }
- }
-}
-
-impl Debug for Style {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match self {
- Self::Property(property) => property.fmt(f),
- Self::Recipe(recipe) => recipe.fmt(f),
- }
- }
-}
-
-impl From<Property> for Style {
- fn from(property: Property) -> Self {
- Self::Property(property)
- }
-}
-
-impl From<Recipe> for Style {
- fn from(recipe: Recipe) -> Self {
- Self::Recipe(recipe)
- }
-}
-
-/// A style property originating from a set rule or constructor.
-#[derive(Clone, PartialEq, Hash)]
-pub struct Property {
- /// The element the property belongs to.
- element: ElemFunc,
- /// The property's name.
- name: EcoString,
- /// The property's value.
- value: Value,
- /// The span of the set rule the property stems from.
- span: Option<Span>,
-}
-
-impl Property {
- /// Create a new property from a key-value pair.
- pub fn new(
- element: ElemFunc,
- name: impl Into<EcoString>,
- value: impl IntoValue,
- ) -> Self {
- Self {
- element,
- name: name.into(),
- value: value.into_value(),
- span: None,
- }
- }
-
- /// Whether this property is the given one.
- pub fn is(&self, element: ElemFunc, name: &str) -> bool {
- self.element == element && self.name == name
- }
-
- /// Whether this property belongs to the given element.
- pub fn is_of(&self, element: ElemFunc) -> bool {
- self.element == element
- }
-}
-
-impl Debug for Property {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "set {}({}: {:?})", self.element.name(), self.name, self.value)?;
- Ok(())
- }
-}
-
-/// A show rule recipe.
-#[derive(Clone, PartialEq, Hash)]
-pub struct Recipe {
- /// The span errors are reported with.
- pub span: Span,
- /// Determines whether the recipe applies to an element.
- pub selector: Option<Selector>,
- /// The transformation to perform on the match.
- pub transform: Transform,
-}
-
-impl Recipe {
- /// Whether this recipe is for the given type of element.
- pub fn is_of(&self, element: ElemFunc) -> bool {
- match self.selector {
- Some(Selector::Elem(own, _)) => own == element,
- _ => false,
- }
- }
-
- /// Whether the recipe is applicable to the target.
- pub fn applicable(&self, target: &Content) -> bool {
- self.selector
- .as_ref()
- .map_or(false, |selector| selector.matches(target))
- }
-
- /// Apply the recipe to the given content.
- pub fn apply_vm(&self, vm: &mut Vm, content: Content) -> SourceResult<Content> {
- match &self.transform {
- Transform::Content(content) => Ok(content.clone()),
- Transform::Func(func) => {
- let args = Args::new(self.span, [Value::Content(content.clone())]);
- let mut result = func.call_vm(vm, args);
- // For selector-less show rules, a tracepoint makes no sense.
- if self.selector.is_some() {
- let point = || Tracepoint::Show(content.func().name().into());
- result = result.trace(vm.world(), point, content.span());
- }
- Ok(result?.display())
- }
- Transform::Style(styles) => Ok(content.styled_with_map(styles.clone())),
- }
- }
-
- /// Apply the recipe to the given content.
- pub fn apply_vt(&self, vt: &mut Vt, content: Content) -> SourceResult<Content> {
- match &self.transform {
- Transform::Content(content) => Ok(content.clone()),
- Transform::Func(func) => {
- let mut result = func.call_vt(vt, [Value::Content(content.clone())]);
- if self.selector.is_some() {
- let point = || Tracepoint::Show(content.func().name().into());
- result = result.trace(vt.world, point, content.span());
- }
- Ok(result?.display())
- }
- Transform::Style(styles) => Ok(content.styled_with_map(styles.clone())),
- }
- }
-}
-
-impl Debug for Recipe {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.write_str("show")?;
- if let Some(selector) = &self.selector {
- f.write_char(' ')?;
- selector.fmt(f)?;
- }
- f.write_str(": ")?;
- self.transform.fmt(f)
- }
-}
-
-/// A show rule transformation that can be applied to a match.
-#[derive(Clone, PartialEq, Hash)]
-pub enum Transform {
- /// Replacement content.
- Content(Content),
- /// A function to apply to the match.
- Func(Func),
- /// Apply styles to the content.
- Style(Styles),
-}
-
-impl Debug for Transform {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match self {
- Self::Content(content) => content.fmt(f),
- Self::Func(func) => func.fmt(f),
- Self::Style(styles) => styles.fmt(f),
- }
- }
-}
-
-cast! {
- Transform,
- content: Content => Self::Content(content),
- func: Func => Self::Func(func),
-}
-
-/// A chain of styles, similar to a linked list.
-///
-/// A style chain allows to combine properties from multiple style lists in a
-/// element hierarchy in a non-allocating way. Rather than eagerly merging the
-/// lists, each access walks the hierarchy from the innermost to the outermost
-/// map, trying to find a match and then folding it with matches further up the
-/// chain.
-#[derive(Default, Clone, Copy, Hash)]
-pub struct StyleChain<'a> {
- /// The first link of this chain.
- head: &'a [Prehashed<Style>],
- /// The remaining links in the chain.
- tail: Option<&'a Self>,
-}
-
-impl<'a> StyleChain<'a> {
- /// Start a new style chain with root styles.
- pub fn new(root: &'a Styles) -> Self {
- Self { head: &root.0, tail: None }
- }
-
- /// Make the given style list the first link of this chain.
- ///
- /// The resulting style chain contains styles from `local` as well as
- /// `self`. The ones from `local` take precedence over the ones from
- /// `self`. For folded properties `local` contributes the inner value.
- pub fn chain<'b>(&'b self, local: &'b Styles) -> StyleChain<'b> {
- if local.is_empty() {
- *self
- } else {
- StyleChain { head: &local.0, tail: Some(self) }
- }
- }
-
- /// Cast the first value for the given property in the chain.
- pub fn get<T: FromValue>(
- self,
- func: ElemFunc,
- name: &'a str,
- inherent: Option<Value>,
- default: impl Fn() -> T,
- ) -> T {
- self.properties::<T>(func, name, inherent)
- .next()
- .unwrap_or_else(default)
- }
-
- /// Cast the first value for the given property in the chain.
- pub fn get_resolve<T: FromValue + Resolve>(
- self,
- func: ElemFunc,
- name: &'a str,
- inherent: Option<Value>,
- default: impl Fn() -> T,
- ) -> T::Output {
- self.get(func, name, inherent, default).resolve(self)
- }
-
- /// Cast the first value for the given property in the chain.
- pub fn get_fold<T: FromValue + Fold>(
- self,
- func: ElemFunc,
- name: &'a str,
- inherent: Option<Value>,
- default: impl Fn() -> T::Output,
- ) -> T::Output {
- fn next<T: Fold>(
- mut values: impl Iterator<Item = T>,
- _styles: StyleChain,
- default: &impl Fn() -> T::Output,
- ) -> T::Output {
- values
- .next()
- .map(|value| value.fold(next(values, _styles, default)))
- .unwrap_or_else(default)
- }
- next(self.properties::<T>(func, name, inherent), self, &default)
- }
-
- /// Cast the first value for the given property in the chain.
- pub fn get_resolve_fold<T>(
- self,
- func: ElemFunc,
- name: &'a str,
- inherent: Option<Value>,
- default: impl Fn() -> <T::Output as Fold>::Output,
- ) -> <T::Output as Fold>::Output
- where
- T: FromValue + Resolve,
- 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,
- T::Output: Fold,
- {
- values
- .next()
- .map(|value| value.resolve(styles).fold(next(values, styles, default)))
- .unwrap_or_else(default)
- }
- next(self.properties::<T>(func, name, inherent), 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)
- }
-
- /// Iterate over all values for the given property in the chain.
- pub fn properties<T: FromValue + 'a>(
- self,
- func: ElemFunc,
- name: &'a str,
- inherent: Option<Value>,
- ) -> impl Iterator<Item = T> + '_ {
- inherent
- .into_iter()
- .chain(
- self.entries()
- .filter_map(Style::property)
- .filter(move |property| property.is(func, name))
- .map(|property| property.value.clone()),
- )
- .map(move |value| {
- value.cast().unwrap_or_else(|err| {
- panic!("{} (for {}.{})", err, func.name(), name)
- })
- })
- }
-
- /// Convert to a style map.
- pub fn to_map(self) -> Styles {
- let mut suffix = Styles::new();
- for link in self.links() {
- suffix.apply_slice(link);
- }
- suffix
- }
-
- /// Iterate over the entries of the chain.
- fn entries(self) -> Entries<'a> {
- Entries { inner: [].as_slice().iter(), links: self.links() }
- }
-
- /// Iterate over the links of the chain.
- fn links(self) -> Links<'a> {
- Links(Some(self))
- }
-
- /// Build owned styles from the suffix (all links beyond the `len`) of the
- /// chain.
- fn suffix(self, len: usize) -> Styles {
- let mut suffix = Styles::new();
- let take = self.links().count().saturating_sub(len);
- for link in self.links().take(take) {
- suffix.apply_slice(link);
- }
- suffix
- }
-
- /// Remove the last link from the chain.
- fn pop(&mut self) {
- *self = self.tail.copied().unwrap_or_default();
- }
-}
-
-impl Debug for StyleChain<'_> {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- for entry in self.entries().collect::<Vec<_>>().into_iter().rev() {
- writeln!(f, "{:?}", entry)?;
- }
- Ok(())
- }
-}
-
-impl PartialEq for StyleChain<'_> {
- fn eq(&self, other: &Self) -> bool {
- ptr::eq(self.head, other.head)
- && match (self.tail, other.tail) {
- (Some(a), Some(b)) => ptr::eq(a, b),
- (None, None) => true,
- _ => false,
- }
- }
-}
-
-/// An iterator over the entries in a style chain.
-struct Entries<'a> {
- inner: std::slice::Iter<'a, Prehashed<Style>>,
- links: Links<'a>,
-}
-
-impl<'a> Iterator for Entries<'a> {
- type Item = &'a Style;
-
- fn next(&mut self) -> Option<Self::Item> {
- loop {
- if let Some(entry) = self.inner.next_back() {
- return Some(entry);
- }
-
- match self.links.next() {
- Some(next) => self.inner = next.iter(),
- None => return None,
- }
- }
- }
-}
-
-/// An iterator over the links of a style chain.
-struct Links<'a>(Option<StyleChain<'a>>);
-
-impl<'a> Iterator for Links<'a> {
- type Item = &'a [Prehashed<Style>];
-
- fn next(&mut self) -> Option<Self::Item> {
- let StyleChain { head, tail } = self.0?;
- self.0 = tail.copied();
- Some(head)
- }
-}
-
-/// A sequence of items with associated styles.
-#[derive(Clone, Hash)]
-pub struct StyleVec<T> {
- items: Vec<T>,
- styles: Vec<(Styles, usize)>,
-}
-
-impl<T> StyleVec<T> {
- /// Whether there are any items in the sequence.
- pub fn is_empty(&self) -> bool {
- self.items.is_empty()
- }
-
- /// Number of items in the sequence.
- pub fn len(&self) -> usize {
- self.items.len()
- }
-
- /// Insert an item in the front. The item will share the style of the
- /// current first item.
- ///
- /// This method has no effect if the vector is empty.
- pub fn push_front(&mut self, item: T) {
- if !self.styles.is_empty() {
- self.items.insert(0, item);
- self.styles[0].1 += 1;
- }
- }
-
- /// Map the contained items.
- pub fn map<F, U>(&self, f: F) -> StyleVec<U>
- where
- F: FnMut(&T) -> U,
- {
- StyleVec {
- items: self.items.iter().map(f).collect(),
- styles: self.styles.clone(),
- }
- }
-
- /// Iterate over references to the contained items and associated styles.
- pub fn iter(&self) -> impl Iterator<Item = (&T, &Styles)> + '_ {
- self.items().zip(
- self.styles
- .iter()
- .flat_map(|(map, count)| iter::repeat(map).take(*count)),
- )
- }
-
- /// Iterate over the contained items.
- pub fn items(&self) -> std::slice::Iter<'_, T> {
- self.items.iter()
- }
-
- /// Iterate over the contained style lists. Note that zipping this with
- /// `items()` does not yield the same result as calling `iter()` because
- /// this method only returns lists once that are shared by consecutive
- /// items. This method is designed for use cases where you want to check,
- /// for example, whether any of the lists fulfills a specific property.
- pub fn styles(&self) -> impl Iterator<Item = &Styles> {
- self.styles.iter().map(|(map, _)| map)
- }
-}
-
-impl StyleVec<Content> {
- pub fn to_vec(self) -> Vec<Content> {
- self.items
- .into_iter()
- .zip(
- self.styles
- .iter()
- .flat_map(|(map, count)| iter::repeat(map).take(*count)),
- )
- .map(|(content, styles)| content.styled_with_map(styles.clone()))
- .collect()
- }
-}
-
-impl<T> Default for StyleVec<T> {
- fn default() -> Self {
- Self { items: vec![], styles: vec![] }
- }
-}
-
-impl<T> FromIterator<T> for StyleVec<T> {
- fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
- let items: Vec<_> = iter.into_iter().collect();
- let styles = vec![(Styles::new(), items.len())];
- Self { items, styles }
- }
-}
-
-impl<T: Debug> Debug for StyleVec<T> {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- f.debug_list()
- .entries(self.iter().map(|(item, styles)| {
- crate::util::debug(|f| {
- styles.fmt(f)?;
- item.fmt(f)
- })
- }))
- .finish()
- }
-}
-
-/// Assists in the construction of a [`StyleVec`].
-#[derive(Debug)]
-pub struct StyleVecBuilder<'a, T> {
- items: Vec<T>,
- chains: Vec<(StyleChain<'a>, usize)>,
-}
-
-impl<'a, T> StyleVecBuilder<'a, T> {
- /// Create a new style-vec builder.
- pub fn new() -> Self {
- Self { items: vec![], chains: vec![] }
- }
-
- /// Whether the builder is empty.
- pub fn is_empty(&self) -> bool {
- self.items.is_empty()
- }
-
- /// Push a new item into the style vector.
- pub fn push(&mut self, item: T, styles: StyleChain<'a>) {
- self.items.push(item);
-
- if let Some((prev, count)) = self.chains.last_mut() {
- if *prev == styles {
- *count += 1;
- return;
- }
- }
-
- self.chains.push((styles, 1));
- }
-
- /// Iterate over the contained items.
- pub fn elems(&self) -> std::slice::Iter<'_, T> {
- self.items.iter()
- }
-
- /// Finish building, returning a pair of two things:
- /// - a style vector of items with the non-shared styles
- /// - a shared prefix chain of styles that apply to all items
- pub fn finish(self) -> (StyleVec<T>, StyleChain<'a>) {
- let mut iter = self.chains.iter();
- let mut trunk = match iter.next() {
- Some(&(chain, _)) => chain,
- None => return Default::default(),
- };
-
- let mut shared = trunk.links().count();
- for &(mut chain, _) in iter {
- let len = chain.links().count();
- if len < shared {
- for _ in 0..shared - len {
- trunk.pop();
- }
- shared = len;
- } else if len > shared {
- for _ in 0..len - shared {
- chain.pop();
- }
- }
-
- while shared > 0 && chain != trunk {
- trunk.pop();
- chain.pop();
- shared -= 1;
- }
- }
-
- let styles = self
- .chains
- .into_iter()
- .map(|(chain, count)| (chain.suffix(shared), count))
- .collect();
-
- (StyleVec { items: self.items, styles }, trunk)
- }
-}
-
-impl<'a, T> Default for StyleVecBuilder<'a, T> {
- fn default() -> Self {
- Self::new()
- }
-}
-
-/// A property that is resolved with other properties from the style chain.
-pub trait Resolve {
- /// The type of the resolved output.
- type Output;
-
- /// Resolve the value using the style chain.
- fn resolve(self, styles: StyleChain) -> Self::Output;
-}
-
-impl<T: Resolve> Resolve for Option<T> {
- type Output = Option<T::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|v| v.resolve(styles))
- }
-}
-
-/// A property that is folded to determine its final value.
-///
-/// In the example below, the chain of stroke values is folded into a single
-/// value: `4pt + red`.
-///
-/// ```example
-/// #set rect(stroke: red)
-/// #set rect(stroke: 4pt)
-/// #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;
-}
-
-impl<T> Fold for Option<T>
-where
- T: Fold,
- T::Output: Default,
-{
- type Output = Option<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.map(|inner| inner.fold(outer.unwrap_or_default()))
- }
-}