diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-11-22 20:12:37 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-11-22 20:12:37 +0100 |
| commit | 5992f11b4c33d82fa245205980094accb57a8c76 (patch) | |
| tree | b0043ba4b4cabab2fa8f63ec80f37c9d247e134a /src/model/content.rs | |
| parent | b476de87b7cea1405bf3c051ff8e0ac7c473dbae (diff) | |
Reorganize content type
Diffstat (limited to 'src/model/content.rs')
| -rw-r--r-- | src/model/content.rs | 366 |
1 files changed, 185 insertions, 181 deletions
diff --git a/src/model/content.rs b/src/model/content.rs index d72e4e19..9d78020c 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -9,19 +9,22 @@ use comemo::Tracked; use siphasher::sip128::{Hasher128, SipHasher}; use typst_macros::node; -use super::{Args, Key, Property, Recipe, RecipeId, Style, StyleMap, Value, Vm}; +use super::{ + Args, Key, Property, Recipe, RecipeId, Style, StyleMap, Unlabellable, Value, Vm, +}; use crate::diag::{SourceResult, StrResult}; use crate::syntax::Span; use crate::util::{EcoString, ReadableTypeId}; use crate::World; /// Composable representation of styled content. -/// -/// This results from: -/// - anything written between square brackets in Typst -/// - any constructor function #[derive(Clone, Hash)] -pub struct Content(Arc<dyn Bounds>, Vec<RecipeId>, Option<Span>, Option<EcoString>); +pub struct Content { + obj: Arc<dyn Bounds>, + guards: Vec<RecipeId>, + span: Option<Span>, + label: Option<EcoString>, +} impl Content { /// Create empty content. @@ -37,99 +40,38 @@ impl Content { } } - /// Whether the content is empty. - pub fn is_empty(&self) -> bool { - self.downcast::<SequenceNode>().map_or(false, |seq| seq.0.is_empty()) - } - - /// The node's human-readable name. - pub fn name(&self) -> &'static str { - (*self.0).name() - } - - /// The id of the contained node. - pub fn id(&self) -> NodeId { - (*self.0).id() - } - - /// Whether the contained node is of type `T`. - pub fn is<T: 'static>(&self) -> bool { - (*self.0).as_any().is::<T>() - } - - /// Cast to `T` if the contained node is of type `T`. - pub fn downcast<T: 'static>(&self) -> Option<&T> { - (*self.0).as_any().downcast_ref::<T>() - } - - /// Try to cast to a mutable instance of `T`. - fn try_downcast_mut<T: 'static>(&mut self) -> Option<&mut T> { - Arc::get_mut(&mut self.0)?.as_any_mut().downcast_mut::<T>() - } - - /// Access a field on this content. - pub fn field(&self, name: &str) -> Option<Value> { - if name == "label" { - return Some(match &self.3 { - Some(label) => Value::Str(label.clone().into()), - None => Value::None, - }); + /// Attach a span to the content. + pub fn spanned(mut self, span: Span) -> Self { + if let Some(styled) = self.to_mut::<StyledNode>() { + styled.sub.span = Some(span); + } else if let Some(styled) = self.to::<StyledNode>() { + self = StyledNode { + sub: styled.sub.clone().spanned(span), + map: styled.map.clone(), + } + .pack(); } - - self.0.field(name) - } - - /// Whether this content has the given capability. - pub fn has<C>(&self) -> bool - where - C: Capability + ?Sized, - { - self.0.vtable(TypeId::of::<C>()).is_some() - } - - /// Cast to a trait object if this content has the given capability. - pub fn to<C>(&self) -> Option<&C> - where - C: Capability + ?Sized, - { - let node: &dyn Bounds = &*self.0; - let vtable = node.vtable(TypeId::of::<C>())?; - let data = node as *const dyn Bounds as *const (); - Some(unsafe { &*crate::util::fat::from_raw_parts(data, vtable) }) + self.span = Some(span); + self } - /// Repeat this content `n` times. - pub fn repeat(&self, n: i64) -> StrResult<Self> { - let count = usize::try_from(n) - .map_err(|_| format!("cannot repeat this content {} times", n))?; - - Ok(Self::sequence(vec![self.clone(); count])) + /// Attach a label to the content. + pub fn labelled(mut self, label: EcoString) -> Self { + self.label = Some(label); + self } /// Style this content with a single style property. - pub fn styled<'k, K: Key<'k>>(self, key: K, value: K::Value) -> Self { + pub fn styled<K: Key>(self, key: K, value: K::Value) -> Self { self.styled_with_entry(Style::Property(Property::new(key, value))) } - /// Style this content with a recipe, eagerly applying it if possible. - pub fn styled_with_recipe( - self, - world: Tracked<dyn World>, - recipe: Recipe, - ) -> SourceResult<Self> { - if recipe.selector.is_none() { - recipe.transform.apply(world, recipe.span, self) - } else { - Ok(self.styled_with_entry(Style::Recipe(recipe))) - } - } - /// Style this content with a style entry. pub fn styled_with_entry(mut self, style: Style) -> Self { - if let Some(styled) = self.try_downcast_mut::<StyledNode>() { + if let Some(styled) = self.to_mut::<StyledNode>() { styled.map.apply(style); self - } else if let Some(styled) = self.downcast::<StyledNode>() { + } else if let Some(styled) = self.to::<StyledNode>() { let mut map = styled.map.clone(); map.apply(style); StyledNode { sub: styled.sub.clone(), map }.pack() @@ -144,7 +86,7 @@ impl Content { return self; } - if let Some(styled) = self.try_downcast_mut::<StyledNode>() { + if let Some(styled) = self.to_mut::<StyledNode>() { styled.map.apply_map(&styles); return self; } @@ -152,86 +94,140 @@ impl Content { StyledNode { sub: self, map: styles }.pack() } - /// Disable a show rule recipe. - pub fn guard(mut self, id: RecipeId) -> Self { - self.1.push(id); - self + /// Style this content with a recipe, eagerly applying it if possible. + pub fn styled_with_recipe( + self, + world: Tracked<dyn World>, + recipe: Recipe, + ) -> SourceResult<Self> { + if recipe.selector.is_none() { + recipe.transform.apply(world, recipe.span, self) + } else { + Ok(self.styled_with_entry(Style::Recipe(recipe))) + } } - /// Whether no show rule was executed for this node so far. - pub fn pristine(&self) -> bool { - self.1.is_empty() + /// Repeat this content `n` times. + pub fn repeat(&self, n: i64) -> StrResult<Self> { + let count = usize::try_from(n) + .map_err(|_| format!("cannot repeat this content {} times", n))?; + + Ok(Self::sequence(vec![self.clone(); count])) } +} - /// Check whether a show rule recipe is disabled. - pub fn guarded(&self, id: RecipeId) -> bool { - self.1.contains(&id) +impl Content { + /// The id of the contained node. + pub fn id(&self) -> NodeId { + (*self.obj).id() + } + + /// The node's human-readable name. + pub fn name(&self) -> &'static str { + (*self.obj).name() } /// The node's span. pub fn span(&self) -> Option<Span> { - self.2 + self.span } - /// Set the content's span. - pub fn set_span(&mut self, span: Span) { - if let Some(styled) = self.try_downcast_mut::<StyledNode>() { - styled.sub.2 = Some(span); - } else if let Some(styled) = self.downcast::<StyledNode>() { - *self = StyledNode { - sub: styled.sub.clone().spanned(span), - map: styled.map.clone(), - } - .pack(); + /// The content's label. + pub fn label(&self) -> Option<&EcoString> { + self.label.as_ref() + } + + /// Access a field on this content. + pub fn field(&self, name: &str) -> Option<Value> { + if name == "label" { + return Some(match &self.label { + Some(label) => Value::Str(label.clone().into()), + None => Value::None, + }); } - self.2 = Some(span); + + self.obj.field(name) } - /// Attach a span to the content. - pub fn spanned(mut self, span: Span) -> Self { - self.set_span(span); - self + /// Whether the contained node is of type `T`. + pub fn is<T: 'static>(&self) -> bool { + (*self.obj).as_any().is::<T>() } - /// The content's label. - pub fn label(&self) -> Option<&EcoString> { - self.3.as_ref() + /// Cast to `T` if the contained node is of type `T`. + pub fn to<T: 'static>(&self) -> Option<&T> { + (*self.obj).as_any().downcast_ref::<T>() } - /// Set the content's label. - pub fn set_label(&mut self, label: EcoString) { - self.3 = Some(label); + /// Whether this content has the given capability. + pub fn has<C>(&self) -> bool + where + C: Capability + ?Sized, + { + self.obj.vtable(TypeId::of::<C>()).is_some() } - /// Attacha label to the content. - pub fn labelled(mut self, label: EcoString) -> Self { - self.set_label(label); + /// Cast to a trait object if this content has the given capability. + pub fn with<C>(&self) -> Option<&C> + where + C: Capability + ?Sized, + { + let node: &dyn Bounds = &*self.obj; + let vtable = node.vtable(TypeId::of::<C>())?; + let data = node as *const dyn Bounds as *const (); + Some(unsafe { &*crate::util::fat::from_raw_parts(data, vtable) }) + } + + /// Try to cast to a mutable instance of `T`. + fn to_mut<T: 'static>(&mut self) -> Option<&mut T> { + Arc::get_mut(&mut self.obj)?.as_any_mut().downcast_mut::<T>() + } + + /// Disable a show rule recipe. + #[doc(hidden)] + pub fn guarded(mut self, id: RecipeId) -> Self { + self.guards.push(id); self } - /// Copy the metadata from other content. - pub fn copy_meta(&mut self, from: &Content) { - self.1 = from.1.clone(); - self.2 = from.2; - self.3 = from.3.clone(); + /// Whether a label can be attached to the content. + pub(super) fn labellable(&self) -> bool { + !self.has::<dyn Unlabellable>() } -} -impl Default for Content { - fn default() -> Self { - Self::empty() + /// Whether no show rule was executed for this node so far. + pub(super) fn is_pristine(&self) -> bool { + self.guards.is_empty() + } + + /// Check whether a show rule recipe is disabled. + pub(super) fn is_guarded(&self, id: RecipeId) -> bool { + self.guards.contains(&id) + } + + /// Copy the metadata from other content. + pub(super) fn copy_meta(&mut self, from: &Content) { + self.guards = from.guards.clone(); + self.span = from.span; + self.label = from.label.clone(); } } impl Debug for Content { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.0.fmt(f) + self.obj.fmt(f) + } +} + +impl Default for Content { + fn default() -> Self { + Self::empty() } } impl PartialEq for Content { fn eq(&self, other: &Self) -> bool { - (*self.0).hash128() == (*other.0).hash128() + (*self.obj).hash128() == (*other.obj).hash128() } } @@ -240,10 +236,10 @@ impl Add for Content { fn add(self, mut rhs: Self) -> Self::Output { let mut lhs = self; - if let Some(lhs_mut) = lhs.try_downcast_mut::<SequenceNode>() { - if let Some(rhs_mut) = rhs.try_downcast_mut::<SequenceNode>() { + if let Some(lhs_mut) = lhs.to_mut::<SequenceNode>() { + if let Some(rhs_mut) = rhs.to_mut::<SequenceNode>() { lhs_mut.0.append(&mut rhs_mut.0); - } else if let Some(rhs) = rhs.downcast::<SequenceNode>() { + } else if let Some(rhs) = rhs.to::<SequenceNode>() { lhs_mut.0.extend(rhs.0.iter().cloned()); } else { lhs_mut.0.push(rhs); @@ -251,7 +247,7 @@ impl Add for Content { return lhs; } - let seq = match (lhs.downcast::<SequenceNode>(), rhs.downcast::<SequenceNode>()) { + let seq = match (lhs.to::<SequenceNode>(), rhs.to::<SequenceNode>()) { (Some(lhs), Some(rhs)) => lhs.0.iter().chain(&rhs.0).cloned().collect(), (Some(lhs), None) => lhs.0.iter().cloned().chain(iter::once(rhs)).collect(), (None, Some(rhs)) => iter::once(lhs).chain(rhs.0.iter().cloned()).collect(), @@ -306,14 +302,54 @@ impl Hash for dyn Bounds { } } +/// A node with applied styles. +#[derive(Clone, Hash)] +pub struct StyledNode { + /// The styled content. + pub sub: Content, + /// The styles. + pub map: StyleMap, +} + +#[node] +impl StyledNode {} + +impl Debug for StyledNode { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.map.fmt(f)?; + self.sub.fmt(f) + } +} + +/// A sequence of nodes. +/// +/// Combines other arbitrary content. So, when you write `[Hi] + [you]` in +/// Typst, the two text nodes are combined into a single sequence node. +#[derive(Clone, Hash)] +pub struct SequenceNode(pub Vec<Content>); + +#[node] +impl SequenceNode {} + +impl Debug for SequenceNode { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_list().entries(self.0.iter()).finish() + } +} + /// A constructable, stylable content node. -pub trait Node: 'static { - /// Pack into type-erased content. +pub trait Node: 'static + Capable { + /// Pack a node into type-erased content. fn pack(self) -> Content where - Self: Debug + Hash + Sync + Send + Sized + 'static, + Self: Node + Debug + Hash + Sync + Send + Sized + 'static, { - Content(Arc::new(self), vec![], None, None) + Content { + obj: Arc::new(self), + guards: vec![], + span: None, + label: None, + } } /// A unique identifier of the node type. @@ -340,17 +376,8 @@ pub trait Node: 'static { /// Access a field on this node. fn field(&self, name: &str) -> Option<Value>; - - /// Extract the pointer of the vtable of the trait object with the - /// given type `id` if this node implements that trait. - fn vtable(&self, id: TypeId) -> Option<*const ()>; } -/// A capability a node can have. -/// -/// This is implemented by trait objects. -pub trait Capability: 'static + Send + Sync {} - /// A unique identifier for a node. #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct NodeId(ReadableTypeId); @@ -368,37 +395,14 @@ impl Debug for NodeId { } } -/// A node with applied styles. -#[derive(Clone, Hash)] -pub struct StyledNode { - /// The styled content. - pub sub: Content, - /// The styles. - pub map: StyleMap, -} - -#[node] -impl StyledNode {} - -impl Debug for StyledNode { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.map.fmt(f)?; - self.sub.fmt(f) - } -} - -/// A sequence of nodes. +/// A capability a node can have. /// -/// Combines other arbitrary content. So, when you write `[Hi] + [you]` in -/// Typst, the two text nodes are combined into a single sequence node. -#[derive(Clone, Hash)] -pub struct SequenceNode(pub Vec<Content>); - -#[node] -impl SequenceNode {} +/// This is implemented by trait objects. +pub trait Capability: 'static {} -impl Debug for SequenceNode { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_list().entries(self.0.iter()).finish() - } +/// Dynamically access a trait implementation at runtime. +pub unsafe trait Capable { + /// Return the vtable pointer of the trait object with given type `id` + /// if `self` implements the trait. + fn vtable(&self, of: TypeId) -> Option<*const ()>; } |
