diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-07-02 19:59:52 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-07-02 20:07:43 +0200 |
| commit | ebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch) | |
| tree | 2bbc24ddb4124c4bb14dec0e536129d4de37b056 /src/model | |
| parent | 3ab19185093d7709f824b95b979060ce125389d8 (diff) | |
Move everything into `crates/` directory
Diffstat (limited to 'src/model')
| -rw-r--r-- | src/model/content.rs | 614 | ||||
| -rw-r--r-- | src/model/element.rs | 134 | ||||
| -rw-r--r-- | src/model/introspect.rs | 352 | ||||
| -rw-r--r-- | src/model/label.rs | 16 | ||||
| -rw-r--r-- | src/model/mod.rs | 148 | ||||
| -rw-r--r-- | src/model/realize.rs | 228 | ||||
| -rw-r--r-- | src/model/selector.rs | 296 | ||||
| -rw-r--r-- | src/model/styles.rs | 750 |
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(®ex::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())) - } -} |
