diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-03-13 21:40:57 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-03-13 21:41:33 +0100 |
| commit | 724e9b140cc0a87208aa9c4914b1b8aeddf25c30 (patch) | |
| tree | 632984e85eb21c5a5a04c397a74725df6f7f8a28 /src/model | |
| parent | 880b1847bd4170ce80be5781c2163ba085cdcaff (diff) | |
Locatability and synthesis improvements
Diffstat (limited to 'src/model')
| -rw-r--r-- | src/model/content.rs | 57 | ||||
| -rw-r--r-- | src/model/realize.rs | 42 | ||||
| -rw-r--r-- | src/model/typeset.rs | 15 |
3 files changed, 84 insertions, 30 deletions
diff --git a/src/model/content.rs b/src/model/content.rs index 071a5862..58b80487 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -8,7 +8,7 @@ use comemo::Tracked; use ecow::{eco_format, EcoString, EcoVec}; use once_cell::sync::Lazy; -use super::{node, Guard, Recipe, Style, StyleMap}; +use super::{node, Guard, Locatable, Recipe, StableId, Style, StyleMap, Synthesize}; use crate::diag::{SourceResult, StrResult}; use crate::eval::{ cast_from_value, cast_to_value, Args, Cast, Func, FuncInfo, Str, Value, Vm, @@ -29,8 +29,9 @@ pub struct Content { /// Modifiers that can be attached to content. #[derive(Debug, Clone, PartialEq, Hash)] enum Modifier { - Synthesized, + Prepared, Guard(Guard), + Id(StableId), } impl Content { @@ -101,6 +102,16 @@ impl Content { Some(unsafe { &*crate::util::fat::from_raw_parts(data, vtable) }) } + /// Cast to a trait object if this content has the given capability. + pub fn with_mut<C>(&mut self) -> Option<&mut C> + where + C: ?Sized + 'static, + { + let vtable = (self.id.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 node's span. pub fn span(&self) -> Span { self.span @@ -233,17 +244,6 @@ impl Content { self } - /// Mark this content as prepared. - pub fn synthesized(mut self) -> Self { - self.modifiers.push(Modifier::Synthesized); - self - } - - /// Whether this node was prepared. - pub fn is_synthesized(&self) -> bool { - self.modifiers.contains(&Modifier::Synthesized) - } - /// Whether no show rule was executed for this node so far. pub(super) fn is_pristine(&self) -> bool { !self @@ -257,6 +257,37 @@ impl Content { self.modifiers.contains(&Modifier::Guard(id)) } + /// Whether this node was prepared. + pub fn is_prepared(&self) -> bool { + self.modifiers.contains(&Modifier::Prepared) + } + + /// Whether the node 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() + } + + /// Mark this content as prepared. + pub fn mark_prepared(&mut self) { + self.modifiers.push(Modifier::Prepared); + } + + /// Attach a stable id to this content. + pub fn set_stable_id(&mut self, id: StableId) { + self.modifiers.push(Modifier::Id(id)); + } + + /// This content's stable identifier. + pub fn stable_id(&self) -> Option<StableId> { + self.modifiers.iter().find_map(|modifier| match modifier { + Modifier::Id(id) => Some(*id), + _ => None, + }) + } + /// Copy the modifiers from another piece of content. pub(super) fn copy_modifiers(&mut self, from: &Content) { self.span = from.span; diff --git a/src/model/realize.rs b/src/model/realize.rs index 4685a605..3ead8f7d 100644 --- a/src/model/realize.rs +++ b/src/model/realize.rs @@ -1,9 +1,10 @@ use super::{Content, NodeId, Recipe, Selector, StyleChain, Vt}; use crate::diag::SourceResult; +use crate::doc::{Meta, MetaNode}; /// Whether the target is affected by show rules in the given style chain. pub fn applicable(target: &Content, styles: StyleChain) -> bool { - if target.can::<dyn Synthesize>() && !target.is_synthesized() { + if target.needs_preparation() { return true; } @@ -31,21 +32,31 @@ pub fn realize( target: &Content, styles: StyleChain, ) -> SourceResult<Option<Content>> { - // Find out how many recipes there are. - let mut n = styles.recipes().count(); + // Pre-process. + if target.needs_preparation() { + let mut node = target.clone(); + if target.can::<dyn Locatable>() || target.label().is_some() { + let id = vt.identify(target); + node.set_stable_id(id); + } + + if let Some(node) = node.with_mut::<dyn Synthesize>() { + node.synthesize(vt, styles); + } + + node.mark_prepared(); - // Synthesize if not already happened for this node. - if target.can::<dyn Synthesize>() && !target.is_synthesized() { - return Ok(Some( - target - .clone() - .synthesized() - .with::<dyn Synthesize>() - .unwrap() - .synthesize(vt, styles), - )); + if let Some(id) = node.stable_id() { + let meta = Meta::Node(id, node.clone()); + return Ok(Some(node.styled(MetaNode::set_data(vec![meta])))); + } + + return Ok(Some(node)); } + // 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() { @@ -144,10 +155,13 @@ fn try_apply( } } +/// Makes this node locatable through `vt.locate`. +pub trait Locatable {} + /// Synthesize fields on a node. This happens before execution of any show rule. pub trait Synthesize { /// Prepare the node for show rule application. - fn synthesize(&self, vt: &mut Vt, styles: StyleChain) -> Content; + fn synthesize(&mut self, vt: &Vt, styles: StyleChain); } /// The base recipe for a node. diff --git a/src/model/typeset.rs b/src/model/typeset.rs index 377c7c76..8719ea0c 100644 --- a/src/model/typeset.rs +++ b/src/model/typeset.rs @@ -5,7 +5,7 @@ use std::num::NonZeroUsize; use comemo::{Track, Tracked, TrackedMut}; -use super::{Content, Selector, StyleChain}; +use super::{Content, Node, Selector, StyleChain}; use crate::diag::SourceResult; use crate::doc::{Document, Element, Frame, Location, Meta}; use crate::geom::Transform; @@ -83,8 +83,17 @@ impl<'a> Vt<'a> { } /// Locate all metadata matches for the given selector. - pub fn locate(&self, selector: Selector) -> Vec<(StableId, &Content)> { - self.introspector.locate(selector) + pub fn locate( + &self, + selector: Selector, + ) -> impl Iterator<Item = (StableId, &Content)> { + self.introspector.locate(selector).into_iter() + } + + /// Locate all metadata matches for the given node. + pub fn locate_node<T: Node>(&self) -> impl Iterator<Item = (StableId, &T)> { + self.locate(Selector::node::<T>()) + .map(|(id, content)| (id, content.to::<T>().unwrap())) } } |
