summaryrefslogtreecommitdiff
path: root/src/model/styles.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/model/styles.rs')
-rw-r--r--src/model/styles.rs750
1 files changed, 0 insertions, 750 deletions
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()))
- }
-}