diff options
Diffstat (limited to 'src/model/content.rs')
| -rw-r--r-- | src/model/content.rs | 614 |
1 files changed, 0 insertions, 614 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) - ) -} |
