diff --git a/library/src/core/behave.rs b/library/src/core/behave.rs index 8558facae..ec8fade91 100644 --- a/library/src/core/behave.rs +++ b/library/src/core/behave.rs @@ -4,7 +4,7 @@ use typst::model::{capability, Content, StyleChain, StyleVec, StyleVecBuilder}; /// How a node interacts with other nodes. #[capability] -pub trait Behave: 'static + Send + Sync { +pub trait Behave { /// The node's interaction behaviour. fn behaviour(&self) -> Behaviour; @@ -62,13 +62,13 @@ impl<'a> BehavedBuilder<'a> { /// Push an item into the sequence. pub fn push(&mut self, item: Content, styles: StyleChain<'a>) { let interaction = item - .to::() + .with::() .map_or(Behaviour::Supportive, Behave::behaviour); match interaction { Behaviour::Weak(level) => { if matches!(self.last, Behaviour::Weak(_)) { - let item = item.to::().unwrap(); + let item = item.with::().unwrap(); let i = self.staged.iter().position(|prev| { let Behaviour::Weak(prev_level) = prev.1 else { return false }; level < prev_level diff --git a/library/src/layout/flow.rs b/library/src/layout/flow.rs index b69f4f4e9..4508023a7 100644 --- a/library/src/layout/flow.rs +++ b/library/src/layout/flow.rs @@ -25,7 +25,7 @@ impl LayoutBlock for FlowNode { for (child, map) in self.0.iter() { let styles = map.chain(&styles); - if let Some(&node) = child.downcast::() { + if let Some(&node) = child.to::() { layouter.layout_spacing(node.amount, styles); } else if child.has::() { layouter.layout_block(world, child, styles)?; @@ -134,7 +134,7 @@ impl FlowLayouter { // Placed nodes that are out of flow produce placed items which aren't // aligned later. - if let Some(placed) = block.downcast::() { + if let Some(placed) = block.to::() { if placed.out_of_flow() { let frame = block.layout_block(world, &self.regions, styles)?.remove(0); self.items.push(FlowItem::Placed(frame)); @@ -149,7 +149,7 @@ impl FlowLayouter { styles.get(ParNode::ALIGN), // Vertical align node alignment is respected by the flow. block - .downcast::() + .to::() .and_then(|aligned| aligned.aligns.y) .map(|align| align.resolve(styles)) .unwrap_or(Align::Top), diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs index 7b11177b9..b05da901b 100644 --- a/library/src/layout/mod.rs +++ b/library/src/layout/mod.rs @@ -32,8 +32,7 @@ use typst::diag::SourceResult; use typst::frame::Frame; use typst::geom::*; use typst::model::{ - capability, Content, Node, SequenceNode, Style, StyleChain, StyleVecBuilder, - StyledNode, + capability, Content, SequenceNode, Style, StyleChain, StyleVecBuilder, StyledNode, }; use typst::World; @@ -48,7 +47,7 @@ use crate::text::{ /// Root-level layout. #[capability] -pub trait LayoutRoot: 'static + Sync + Send { +pub trait LayoutRoot { /// Layout into one frame per page. fn layout_root(&self, world: Tracked) -> SourceResult>; } @@ -69,7 +68,7 @@ impl LayoutRoot for Content { /// Block-level layout. #[capability] -pub trait LayoutBlock: 'static + Sync + Send { +pub trait LayoutBlock { /// Layout into one frame per region. fn layout_block( &self, @@ -88,7 +87,7 @@ impl LayoutBlock for Content { styles: StyleChain, ) -> SourceResult> { if !styles.applicable(self) { - if let Some(node) = self.to::() { + if let Some(node) = self.with::() { let barrier = Style::Barrier(self.id()); let styles = barrier.chain(&styles); return node.layout_block(world, regions, styles); @@ -105,7 +104,7 @@ impl LayoutBlock for Content { /// Inline-level layout. #[capability] -pub trait LayoutInline: 'static + Sync + Send { +pub trait LayoutInline { /// Layout into a single frame. fn layout_inline( &self, @@ -127,13 +126,13 @@ impl LayoutInline for Content { assert!(regions.last.is_none()); if !styles.applicable(self) { - if let Some(node) = self.to::() { + if let Some(node) = self.with::() { let barrier = Style::Barrier(self.id()); let styles = barrier.chain(&styles); return node.layout_inline(world, regions, styles); } - if let Some(node) = self.to::() { + if let Some(node) = self.with::() { let barrier = Style::Barrier(self.id()); let styles = barrier.chain(&styles); return Ok(node.layout_block(world, regions, styles)?.remove(0)); @@ -312,11 +311,11 @@ impl<'a> Builder<'a> { content: &'a Content, styles: StyleChain<'a>, ) -> SourceResult<()> { - if let Some(styled) = content.downcast::() { + if let Some(styled) = content.to::() { return self.styled(styled, styles); } - if let Some(seq) = content.downcast::() { + if let Some(seq) = content.to::() { return self.sequence(seq, styles); } @@ -347,7 +346,7 @@ impl<'a> Builder<'a> { } let keep = content - .downcast::() + .to::() .map_or(false, |pagebreak| !pagebreak.weak); self.interrupt(Interruption::Page, styles, keep)?; @@ -457,12 +456,12 @@ struct DocBuilder<'a> { impl<'a> DocBuilder<'a> { fn accept(&mut self, content: &Content, styles: StyleChain<'a>) -> bool { - if let Some(pagebreak) = content.downcast::() { + if let Some(pagebreak) = content.to::() { self.keep_next = !pagebreak.weak; return true; } - if let Some(page) = content.downcast::() { + if let Some(page) = content.to::() { self.pages.push(page.clone(), styles); self.keep_next = false; return true; @@ -498,11 +497,11 @@ impl<'a> FlowBuilder<'a> { } if content.has::() { - let is_tight_list = if let Some(node) = content.downcast::() { + let is_tight_list = if let Some(node) = content.to::() { node.tight - } else if let Some(node) = content.downcast::() { + } else if let Some(node) = content.to::() { node.tight - } else if let Some(node) = content.downcast::() { + } else if let Some(node) = content.to::() { node.tight } else { false @@ -576,7 +575,7 @@ impl<'a> ListBuilder<'a> { return true; } - if let Some(item) = content.downcast::() { + if let Some(item) = content.to::() { if self .items .items() diff --git a/library/src/layout/place.rs b/library/src/layout/place.rs index 42f7ff7d3..a4f05a6c7 100644 --- a/library/src/layout/place.rs +++ b/library/src/layout/place.rs @@ -49,9 +49,7 @@ impl PlaceNode { /// origin. Instead of relative to the parent's current flow/cursor /// position. pub fn out_of_flow(&self) -> bool { - self.0 - .downcast::() - .map_or(false, |node| node.aligns.y.is_some()) + self.0.to::().map_or(false, |node| node.aligns.y.is_some()) } } diff --git a/library/src/layout/spacing.rs b/library/src/layout/spacing.rs index 74bd2f0f0..b42f90ef8 100644 --- a/library/src/layout/spacing.rs +++ b/library/src/layout/spacing.rs @@ -44,7 +44,7 @@ impl Behave for HNode { } fn larger(&self, prev: &Content) -> bool { - let Some(prev) = prev.downcast::() else { return false }; + let Some(prev) = prev.to::() else { return false }; self.amount > prev.amount } } @@ -110,7 +110,7 @@ impl Behave for VNode { } fn larger(&self, prev: &Content) -> bool { - let Some(prev) = prev.downcast::() else { return false }; + let Some(prev) = prev.to::() else { return false }; self.amount > prev.amount } } diff --git a/library/src/layout/stack.rs b/library/src/layout/stack.rs index 02129e1f0..12a1c384a 100644 --- a/library/src/layout/stack.rs +++ b/library/src/layout/stack.rs @@ -182,11 +182,11 @@ impl<'a> StackLayouter<'a> { // Block-axis alignment of the `AlignNode` is respected // by the stack node. let align = block - .downcast::() + .to::() .and_then(|node| node.aligns.get(self.axis)) .map(|align| align.resolve(styles)) .unwrap_or_else(|| { - if let Some(styled) = block.downcast::() { + if let Some(styled) = block.to::() { let map = &styled.map; if map.contains(ParNode::ALIGN) { return StyleChain::with_root(map).get(ParNode::ALIGN); diff --git a/library/src/lib.rs b/library/src/lib.rs index ca10837fc..41c841f2b 100644 --- a/library/src/lib.rs +++ b/library/src/lib.rs @@ -161,7 +161,7 @@ pub fn items() -> LangItems { linebreak: |justify| text::LinebreakNode { justify }.pack(), text: |text| text::TextNode(text).pack(), text_id: NodeId::of::(), - text_str: |content| Some(&content.downcast::()?.0), + text_str: |content| Some(&content.to::()?.0), smart_quote: |double| text::SmartQuoteNode { double }.pack(), parbreak: || text::ParbreakNode.pack(), strong: |body| text::StrongNode(body).pack(), diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs index fb53b52de..1ea03d70a 100644 --- a/library/src/math/mod.rs +++ b/library/src/math/mod.rs @@ -35,7 +35,7 @@ impl Show for MathNode { let mut realized = self .clone() .pack() - .guard(RecipeId::Base(NodeId::of::())) + .guarded(RecipeId::Base(NodeId::of::())) .styled_with_map(map); if self.display { diff --git a/library/src/math/tex.rs b/library/src/math/tex.rs index d7c5493bc..ffde719be 100644 --- a/library/src/math/tex.rs +++ b/library/src/math/tex.rs @@ -10,7 +10,7 @@ use crate::text::{families, variant, LinebreakNode, SpaceNode, TextNode}; /// Turn a math node into TeX math code. #[capability] -pub trait Texify: 'static + Sync + Send { +pub trait Texify { /// Perform the conversion. fn texify(&self) -> EcoString; } @@ -25,7 +25,7 @@ impl Texify for Content { return r"\\".into(); } - if let Some(node) = self.to::() { + if let Some(node) = self.with::() { return node.texify(); } diff --git a/library/src/text/par.rs b/library/src/text/par.rs index 37d5e3965..196878f83 100644 --- a/library/src/text/par.rs +++ b/library/src/text/par.rs @@ -420,7 +420,7 @@ fn collect<'a>( let segment = if child.is::() { full.push(' '); Segment::Text(1) - } else if let Some(node) = child.downcast::() { + } else if let Some(node) = child.to::() { let prev = full.len(); if let Some(case) = styles.get(TextNode::CASE) { full.push_str(&case.apply(&node.0)); @@ -428,18 +428,18 @@ fn collect<'a>( full.push_str(&node.0); } Segment::Text(full.len() - prev) - } else if let Some(node) = child.downcast::() { + } else if let Some(node) = child.to::() { let c = if node.justify { '\u{2028}' } else { '\n' }; full.push(c); Segment::Text(c.len_utf8()) - } else if let Some(node) = child.downcast::() { + } else if let Some(node) = child.to::() { let prev = full.len(); if styles.get(TextNode::SMART_QUOTES) { let lang = styles.get(TextNode::LANG); let region = styles.get(TextNode::REGION); let quotes = Quotes::from_lang(lang, region); let peeked = iter.peek().and_then(|(child, _)| { - if let Some(node) = child.downcast::() { + if let Some(node) = child.to::() { node.0.chars().next() } else if child.is::() { Some('"') @@ -455,7 +455,7 @@ fn collect<'a>( full.push(if node.double { '"' } else { '\'' }); } Segment::Text(full.len() - prev) - } else if let Some(&node) = child.downcast::() { + } else if let Some(&node) = child.to::() { full.push(SPACING_REPLACE); Segment::Spacing(node.amount) } else if child.has::() { @@ -523,7 +523,7 @@ fn prepare<'a>( } }, Segment::Inline(inline) => { - if let Some(repeat) = inline.downcast::() { + if let Some(repeat) = inline.to::() { items.push(Item::Repeat(repeat, styles)); } else { let size = Size::new(regions.first.x, regions.base.y); @@ -606,11 +606,11 @@ fn is_compatible(a: Script, b: Script) -> bool { /// Get a style property, but only if it is the same for all children of the /// paragraph. -fn shared_get<'a, K: Key<'a>>( +fn shared_get<'a, K: Key>( styles: StyleChain<'a>, children: &StyleVec, key: K, -) -> Option { +) -> Option> { children .styles() .all(|map| !map.contains(key)) diff --git a/library/src/text/shift.rs b/library/src/text/shift.rs index 32f110c66..e63211c65 100644 --- a/library/src/text/shift.rs +++ b/library/src/text/shift.rs @@ -69,13 +69,11 @@ impl Show for ShiftNode { /// Find and transform the text contained in `content` to the given script kind /// if and only if it only consists of `Text`, `Space`, and `Empty` leaf nodes. fn search_text(content: &Content, mode: ShiftKind) -> Option { - if content.is_empty() { - Some(EcoString::new()) - } else if content.is::() { + if content.is::() { Some(' '.into()) - } else if let Some(text) = content.downcast::() { + } else if let Some(text) = content.to::() { convert_script(&text.0, mode) - } else if let Some(seq) = content.downcast::() { + } else if let Some(seq) = content.to::() { let mut full = EcoString::new(); for item in seq.0.iter() { match search_text(item, mode) { diff --git a/macros/src/node.rs b/macros/src/node.rs index b92dabcc4..45e91b2ef 100644 --- a/macros/src/node.rs +++ b/macros/src/node.rs @@ -227,6 +227,9 @@ fn create(node: &Node) -> Result { #construct_func #set_func #field_method + } + + unsafe impl<#params> ::typst::model::Capable for #self_ty { #vtable_method } }; @@ -411,9 +414,9 @@ fn create_property_module(node: &Node, property: &Property) -> (syn::Type, syn:: fn clone(&self) -> Self { *self } } - impl<'a, #params> ::typst::model::Key<'a> for #key { + impl<#params> ::typst::model::Key for #key { type Value = #value_ty; - type Output = #output_ty; + type Output<'a> = #output_ty; #name_const #node_func #get_method @@ -492,10 +495,10 @@ fn create_property_get_method(property: &Property) -> syn::ImplItemMethod { }; parse_quote! { - fn get( + fn get<'a>( chain: ::typst::model::StyleChain<'a>, mut values: impl ::std::iter::Iterator, - ) -> Self::Output { + ) -> Self::Output<'a> { #value } } diff --git a/src/model/content.rs b/src/model/content.rs index d72e4e19a..9d78020c4 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, Vec, Option, Option); +pub struct Content { + obj: Arc, + guards: Vec, + span: Option, + label: Option, +} impl Content { /// Create empty content. @@ -37,80 +40,60 @@ impl Content { } } - /// Whether the content is empty. - pub fn is_empty(&self) -> bool { - self.downcast::().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(&self) -> bool { - (*self.0).as_any().is::() - } - - /// Cast to `T` if the contained node is of type `T`. - pub fn downcast(&self) -> Option<&T> { - (*self.0).as_any().downcast_ref::() - } - - /// Try to cast to a mutable instance of `T`. - fn try_downcast_mut(&mut self) -> Option<&mut T> { - Arc::get_mut(&mut self.0)?.as_any_mut().downcast_mut::() - } - - /// Access a field on this content. - pub fn field(&self, name: &str) -> Option { - 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::() { + styled.sub.span = Some(span); + } else if let Some(styled) = self.to::() { + self = StyledNode { + sub: styled.sub.clone().spanned(span), + map: styled.map.clone(), + } + .pack(); } - - self.0.field(name) + self.span = Some(span); + self } - /// Whether this content has the given capability. - pub fn has(&self) -> bool - where - C: Capability + ?Sized, - { - self.0.vtable(TypeId::of::()).is_some() - } - - /// Cast to a trait object if this content has the given capability. - pub fn to(&self) -> Option<&C> - where - C: Capability + ?Sized, - { - let node: &dyn Bounds = &*self.0; - let vtable = node.vtable(TypeId::of::())?; - let data = node as *const dyn Bounds as *const (); - Some(unsafe { &*crate::util::fat::from_raw_parts(data, vtable) }) - } - - /// Repeat this content `n` times. - pub fn repeat(&self, n: i64) -> StrResult { - 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(self, key: K, value: K::Value) -> Self { self.styled_with_entry(Style::Property(Property::new(key, value))) } + /// Style this content with a style entry. + pub fn styled_with_entry(mut self, style: Style) -> Self { + if let Some(styled) = self.to_mut::() { + styled.map.apply(style); + self + } else if let Some(styled) = self.to::() { + let mut map = styled.map.clone(); + map.apply(style); + StyledNode { sub: styled.sub.clone(), map }.pack() + } else { + StyledNode { sub: self, map: style.into() }.pack() + } + } + + /// Style this content with a full style map. + pub fn styled_with_map(mut self, styles: StyleMap) -> Self { + if styles.is_empty() { + return self; + } + + if let Some(styled) = self.to_mut::() { + styled.map.apply_map(&styles); + return self; + } + + StyledNode { sub: self, map: styles }.pack() + } + /// Style this content with a recipe, eagerly applying it if possible. pub fn styled_with_recipe( self, @@ -124,96 +107,115 @@ impl Content { } } - /// 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::() { - styled.map.apply(style); - self - } else if let Some(styled) = self.downcast::() { - let mut map = styled.map.clone(); - map.apply(style); - StyledNode { sub: styled.sub.clone(), map }.pack() - } else { - StyledNode { sub: self, map: style.into() }.pack() - } + /// Repeat this content `n` times. + pub fn repeat(&self, n: i64) -> StrResult { + let count = usize::try_from(n) + .map_err(|_| format!("cannot repeat this content {} times", n))?; + + Ok(Self::sequence(vec![self.clone(); count])) + } +} + +impl Content { + /// The id of the contained node. + pub fn id(&self) -> NodeId { + (*self.obj).id() } - /// Style this content with a full style map. - pub fn styled_with_map(mut self, styles: StyleMap) -> Self { - if styles.is_empty() { - return self; - } - - if let Some(styled) = self.try_downcast_mut::() { - styled.map.apply_map(&styles); - return self; - } - - StyledNode { sub: self, map: styles }.pack() - } - - /// Disable a show rule recipe. - pub fn guard(mut self, id: RecipeId) -> Self { - self.1.push(id); - self - } - - /// Whether no show rule was executed for this node so far. - pub fn pristine(&self) -> bool { - self.1.is_empty() - } - - /// Check whether a show rule recipe is disabled. - pub fn guarded(&self, id: RecipeId) -> bool { - self.1.contains(&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 { - self.2 - } - - /// Set the content's span. - pub fn set_span(&mut self, span: Span) { - if let Some(styled) = self.try_downcast_mut::() { - styled.sub.2 = Some(span); - } else if let Some(styled) = self.downcast::() { - *self = StyledNode { - sub: styled.sub.clone().spanned(span), - map: styled.map.clone(), - } - .pack(); - } - self.2 = Some(span); - } - - /// Attach a span to the content. - pub fn spanned(mut self, span: Span) -> Self { - self.set_span(span); - self + self.span } /// The content's label. pub fn label(&self) -> Option<&EcoString> { - self.3.as_ref() + self.label.as_ref() } - /// Set the content's label. - pub fn set_label(&mut self, label: EcoString) { - self.3 = Some(label); + /// Access a field on this content. + pub fn field(&self, name: &str) -> Option { + if name == "label" { + return Some(match &self.label { + Some(label) => Value::Str(label.clone().into()), + None => Value::None, + }); + } + + self.obj.field(name) } - /// Attacha label to the content. - pub fn labelled(mut self, label: EcoString) -> Self { - self.set_label(label); + /// Whether the contained node is of type `T`. + pub fn is(&self) -> bool { + (*self.obj).as_any().is::() + } + + /// Cast to `T` if the contained node is of type `T`. + pub fn to(&self) -> Option<&T> { + (*self.obj).as_any().downcast_ref::() + } + + /// Whether this content has the given capability. + pub fn has(&self) -> bool + where + C: Capability + ?Sized, + { + self.obj.vtable(TypeId::of::()).is_some() + } + + /// Cast to a trait object if this content has the given capability. + pub fn with(&self) -> Option<&C> + where + C: Capability + ?Sized, + { + let node: &dyn Bounds = &*self.obj; + let vtable = node.vtable(TypeId::of::())?; + 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(&mut self) -> Option<&mut T> { + Arc::get_mut(&mut self.obj)?.as_any_mut().downcast_mut::() + } + + /// Disable a show rule recipe. + #[doc(hidden)] + pub fn guarded(mut self, id: RecipeId) -> Self { + self.guards.push(id); self } + /// Whether a label can be attached to the content. + pub(super) fn labellable(&self) -> bool { + !self.has::() + } + + /// 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 fn copy_meta(&mut self, from: &Content) { - self.1 = from.1.clone(); - self.2 = from.2; - self.3 = from.3.clone(); + 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.obj.fmt(f) } } @@ -223,15 +225,9 @@ impl Default for Content { } } -impl Debug for Content { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - 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::() { - if let Some(rhs_mut) = rhs.try_downcast_mut::() { + if let Some(lhs_mut) = lhs.to_mut::() { + if let Some(rhs_mut) = rhs.to_mut::() { lhs_mut.0.append(&mut rhs_mut.0); - } else if let Some(rhs) = rhs.downcast::() { + } else if let Some(rhs) = rhs.to::() { 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::(), rhs.downcast::()) { + let seq = match (lhs.to::(), rhs.to::()) { (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,68 +302,6 @@ impl Hash for dyn Bounds { } } -/// A constructable, stylable content node. -pub trait Node: 'static { - /// Pack into type-erased content. - fn pack(self) -> Content - where - Self: Debug + Hash + Sync + Send + Sized + 'static, - { - Content(Arc::new(self), vec![], None, None) - } - - /// A unique identifier of the node type. - fn id(&self) -> NodeId; - - /// The node's name. - fn name(&self) -> &'static str; - - /// Construct a node from the arguments. - /// - /// This is passed only the arguments that remain after execution of the - /// node's set rule. - fn construct(vm: &mut Vm, args: &mut Args) -> SourceResult - where - Self: Sized; - - /// Parse relevant arguments into style properties for this node. - /// - /// When `constructor` is true, [`construct`](Self::construct) will run - /// after this invocation of `set` with the remaining arguments. - fn set(args: &mut Args, constructor: bool) -> SourceResult - where - Self: Sized; - - /// Access a field on this node. - fn field(&self, name: &str) -> Option; - - /// 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); - -impl NodeId { - /// The id of the given node. - pub fn of() -> Self { - Self(ReadableTypeId::of::()) - } -} - -impl Debug for NodeId { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - /// A node with applied styles. #[derive(Clone, Hash)] pub struct StyledNode { @@ -402,3 +336,73 @@ impl Debug for SequenceNode { f.debug_list().entries(self.0.iter()).finish() } } + +/// A constructable, stylable content node. +pub trait Node: 'static + Capable { + /// Pack a node into type-erased content. + fn pack(self) -> Content + where + Self: Node + Debug + Hash + Sync + Send + Sized + 'static, + { + Content { + obj: Arc::new(self), + guards: vec![], + span: None, + label: None, + } + } + + /// A unique identifier of the node type. + fn id(&self) -> NodeId; + + /// The node's name. + fn name(&self) -> &'static str; + + /// Construct a node from the arguments. + /// + /// This is passed only the arguments that remain after execution of the + /// node's set rule. + fn construct(vm: &mut Vm, args: &mut Args) -> SourceResult + where + Self: Sized; + + /// Parse relevant arguments into style properties for this node. + /// + /// When `constructor` is true, [`construct`](Self::construct) will run + /// after this invocation of `set` with the remaining arguments. + fn set(args: &mut Args, constructor: bool) -> SourceResult + where + Self: Sized; + + /// Access a field on this node. + fn field(&self, name: &str) -> Option; +} + +/// A unique identifier for a node. +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct NodeId(ReadableTypeId); + +impl NodeId { + /// The id of the given node. + pub fn of() -> Self { + Self(ReadableTypeId::of::()) + } +} + +impl Debug for NodeId { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +/// A capability a node can have. +/// +/// This is implemented by trait objects. +pub trait Capability: 'static {} + +/// 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 ()>; +} diff --git a/src/model/eval.rs b/src/model/eval.rs index 8078c87f1..7c0cffb59 100644 --- a/src/model/eval.rs +++ b/src/model/eval.rs @@ -1,13 +1,14 @@ //! Evaluation of markup into modules. use std::collections::BTreeMap; +use std::mem; use comemo::{Track, Tracked}; use unicode_segmentation::UnicodeSegmentation; use super::{ methods, ops, Arg, Args, Array, CapturesVisitor, Closure, Content, Dict, Flow, Func, - Recipe, Scope, Scopes, Selector, StyleMap, Transform, Unlabellable, Value, Vm, + Recipe, Scope, Scopes, Selector, StyleMap, Transform, Value, Vm, }; use crate::diag::{bail, error, At, SourceResult, StrResult, Trace, Tracepoint}; use crate::geom::{Abs, Angle, Em, Fr, Ratio}; @@ -137,10 +138,8 @@ fn eval_markup( seq.push(tail.styled_with_recipe(vm.world, recipe)?) } ast::MarkupNode::Label(label) => { - if let Some(node) = - seq.iter_mut().rev().find(|node| !node.has::()) - { - node.set_label(label.get().clone()); + if let Some(node) = seq.iter_mut().rev().find(|node| node.labellable()) { + *node = mem::take(node).labelled(label.get().clone()); } } _ => seq.push(node.eval(vm)?), @@ -1080,7 +1079,7 @@ impl Eval for ast::FuncReturn { } /// Access an expression mutably. -pub trait Access { +trait Access { /// Access the value. fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value>; } diff --git a/src/model/styles.rs b/src/model/styles.rs index 30db00bd3..96eb0ab42 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -36,19 +36,19 @@ impl StyleMap { /// 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<'a, K: Key<'a>>(&mut self, key: K, value: K::Value) { + pub fn set(&mut self, key: K, value: K::Value) { self.0.push(Style::Property(Property::new(key, value))); } /// Set an inner value for a style property if it is `Some(_)`. - pub fn set_opt<'a, K: Key<'a>>(&mut self, key: K, value: Option) { + pub fn set_opt(&mut self, key: K, value: Option) { if let Some(value) = value { self.set(key, value); } } /// Whether the map contains a style property for the given key. - pub fn contains<'a, K: Key<'a>>(&self, _: K) -> bool { + pub fn contains(&self, _: K) -> bool { self.0 .iter() .filter_map(|entry| entry.property()) @@ -188,13 +188,13 @@ impl Debug for Style { /// /// This trait is not intended to be implemented manually, but rather through /// the `#[node]` proc-macro. -pub trait Key<'a>: Copy + 'static { +pub trait Key: Copy + 'static { /// The unfolded type which this property is stored as in a style map. type Value: Debug + Clone + Hash + Sync + Send + 'static; /// The folded type of value that is returned when reading this property /// from a style chain. - type Output; + type Output<'a>; /// The name of the property, used for debug printing. const NAME: &'static str; @@ -204,10 +204,10 @@ pub trait Key<'a>: Copy + 'static { /// Compute an output value from a sequence of values belonging to this key, /// folding if necessary. - fn get( + fn get<'a>( chain: StyleChain<'a>, values: impl Iterator, - ) -> Self::Output; + ) -> Self::Output<'a>; } /// A style property originating from a set rule or constructor. @@ -229,7 +229,7 @@ pub struct Property { impl Property { /// Create a new property from a key-value pair. - pub fn new<'a, K: Key<'a>>(_: K, value: K::Value) -> Self { + pub fn new(_: K, value: K::Value) -> Self { Self { key: KeyId::of::(), node: K::node(), @@ -241,7 +241,7 @@ impl Property { } /// Whether this property has the given key. - pub fn is<'a, K: Key<'a>>(&self) -> bool { + pub fn is(&self) -> bool { self.key == KeyId::of::() } @@ -251,7 +251,7 @@ impl Property { } /// Access the property's value if it is of the given key. - pub fn downcast<'a, K: Key<'a>>(&'a self) -> Option<&'a K::Value> { + pub fn downcast(&self) -> Option<&K::Value> { if self.key == KeyId::of::() { (**self.value).as_any().downcast_ref() } else { @@ -314,7 +314,7 @@ struct KeyId(ReadableTypeId); impl KeyId { /// The id of the given key. - pub fn of<'a, T: Key<'a>>() -> Self { + pub fn of() -> Self { Self(ReadableTypeId::of::()) } } @@ -327,7 +327,7 @@ impl Debug for KeyId { /// A built-in show rule for a node. #[capability] -pub trait Show: 'static + Sync + Send { +pub trait Show { /// Execute the base recipe for this node. fn show( &self, @@ -338,7 +338,7 @@ pub trait Show: 'static + Sync + Send { /// Post-process a node after it was realized. #[capability] -pub trait Finalize: 'static + Sync + Send { +pub trait Finalize { /// Finalize the fully realized form of the node. Use this for effects that /// should work even in the face of a user-defined show rule, for example /// the linking behaviour of a link node. @@ -352,7 +352,7 @@ pub trait Finalize: 'static + Sync + Send { /// Indicates that a node cannot be labelled. #[capability] -pub trait Unlabellable: 'static + Sync + Send {} +pub trait Unlabellable {} /// A show rule recipe. #[derive(Clone, PartialEq, Hash)] @@ -386,7 +386,7 @@ impl Recipe { return Ok(None); } - self.transform.apply(world, self.span, target.clone().guard(sel))? + self.transform.apply(world, self.span, target.clone().guarded(sel))? } Some(Selector::Regex(regex)) => { @@ -412,7 +412,7 @@ impl Recipe { let transformed = self.transform.apply( world, self.span, - make(m.as_str().into()).guard(sel), + make(m.as_str().into()).guarded(sel), )?; result.push(transformed); @@ -559,7 +559,7 @@ impl<'a> StyleChain<'a> { /// Returns the property's default value if no map in the chain contains an /// entry for it. Also takes care of resolving and folding and returns /// references where applicable. - pub fn get>(self, key: K) -> K::Output { + pub fn get(self, key: K) -> K::Output<'a> { K::get(self, self.values(key)) } @@ -576,7 +576,7 @@ impl<'a> StyleChain<'a> { let mut realized = None; for recipe in self.entries().filter_map(Style::recipe) { let sel = RecipeId::Nth(n); - if recipe.applicable(target) && !target.guarded(sel) { + if recipe.applicable(target) && !target.is_guarded(sel) { if let Some(content) = recipe.apply(world, sel, target)? { realized = Some(content); break; @@ -587,15 +587,15 @@ impl<'a> StyleChain<'a> { // Realize if there was no matching recipe. let base = RecipeId::Base(target.id()); - if realized.is_none() && !target.guarded(base) { - if let Some(showable) = target.to::() { + if realized.is_none() && !target.is_guarded(base) { + if let Some(showable) = target.with::() { realized = Some(showable.show(world, self)?); } } // Finalize only if this is the first application for this node. - if let Some(node) = target.to::() { - if target.pristine() { + if let Some(node) = target.with::() { + if target.is_pristine() { if let Some(content) = realized { realized = Some(node.finalize(world, self, content)?); } @@ -612,7 +612,7 @@ impl<'a> StyleChain<'a> { // Find out whether any recipe matches and is unguarded. for recipe in self.entries().filter_map(Style::recipe) { - if recipe.applicable(target) && !target.guarded(RecipeId::Nth(n)) { + if recipe.applicable(target) && !target.is_guarded(RecipeId::Nth(n)) { return true; } n -= 1; @@ -638,7 +638,7 @@ impl<'a> StyleChain<'a> { } /// Iterate over all values for the given property in the chain. - fn values>(self, _: K) -> Values<'a, K> { + fn values(self, _: K) -> Values<'a, K> { Values { entries: self.entries(), key: PhantomData, @@ -682,7 +682,7 @@ struct Values<'a, K> { barriers: usize, } -impl<'a, K: Key<'a>> Iterator for Values<'a, K> { +impl<'a, K: Key> Iterator for Values<'a, K> { type Item = &'a K::Value; fn next(&mut self) -> Option {