diff options
Diffstat (limited to 'src/model')
| -rw-r--r-- | src/model/content.rs | 33 | ||||
| -rw-r--r-- | src/model/realize.rs | 20 | ||||
| -rw-r--r-- | src/model/typeset.rs | 31 |
3 files changed, 69 insertions, 15 deletions
diff --git a/src/model/content.rs b/src/model/content.rs index 17fa786b..be737331 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -10,7 +10,7 @@ use once_cell::sync::Lazy; use super::{node, Guard, Recipe, Style, StyleMap}; use crate::diag::{SourceResult, StrResult}; -use crate::eval::{cast_from_value, Args, FuncInfo, Str, Value, Vm}; +use crate::eval::{cast_from_value, Args, Cast, FuncInfo, Str, Value, Vm}; use crate::syntax::Span; use crate::util::pretty_array_like; use crate::World; @@ -27,7 +27,7 @@ pub struct Content { /// Modifiers that can be attached to content. #[derive(Debug, Clone, PartialEq, Hash)] enum Modifier { - Prepared, + Synthesized, Guard(Guard), } @@ -59,6 +59,12 @@ impl Content { self.id } + /// Whether the content is empty. + pub fn is_empty(&self) -> bool { + self.to::<SequenceNode>() + .map_or(false, |seq| seq.children().is_empty()) + } + /// Whether the contained node is of type `T`. pub fn is<T>(&self) -> bool where @@ -112,6 +118,21 @@ impl Content { .map(|(_, value)| value) } + /// Access a field on the content as a specified type. + #[track_caller] + pub fn cast_field<T: Cast>(&self, name: &str) -> Option<T> { + match self.field(name) { + Some(value) => Some(value.clone().cast().unwrap()), + None => None, + } + } + + /// Expect a field on the content to exist as a specified type. + #[track_caller] + pub fn expect_field<T: Cast>(&self, name: &str) -> T { + self.cast_field(name).unwrap() + } + /// List all fields on the content. pub fn fields(&self) -> &[(EcoString, Value)] { &self.fields @@ -209,14 +230,14 @@ impl Content { } /// Mark this content as prepared. - pub fn prepared(mut self) -> Self { - self.modifiers.push(Modifier::Prepared); + pub fn synthesized(mut self) -> Self { + self.modifiers.push(Modifier::Synthesized); self } /// Whether this node was prepared. - pub fn is_prepared(&self) -> bool { - self.modifiers.contains(&Modifier::Prepared) + pub fn is_synthesized(&self) -> bool { + self.modifiers.contains(&Modifier::Synthesized) } /// Whether no show rule was executed for this node so far. diff --git a/src/model/realize.rs b/src/model/realize.rs index c4c67a4f..4685a605 100644 --- a/src/model/realize.rs +++ b/src/model/realize.rs @@ -3,7 +3,7 @@ use crate::diag::SourceResult; /// 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 Prepare>() && !target.is_prepared() { + if target.can::<dyn Synthesize>() && !target.is_synthesized() { return true; } @@ -34,6 +34,18 @@ pub fn realize( // Find out how many recipes there are. let mut n = styles.recipes().count(); + // 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), + )); + } + // Find an applicable recipe. let mut realized = None; for recipe in styles.recipes() { @@ -132,10 +144,10 @@ fn try_apply( } } -/// Preparations before execution of any show rule. -pub trait Prepare { +/// Synthesize fields on a node. This happens before execution of any show rule. +pub trait Synthesize { /// Prepare the node for show rule application. - fn prepare(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content>; + fn synthesize(&self, vt: &mut Vt, styles: StyleChain) -> Content; } /// The base recipe for a node. diff --git a/src/model/typeset.rs b/src/model/typeset.rs index 6361e6ce..377c7c76 100644 --- a/src/model/typeset.rs +++ b/src/model/typeset.rs @@ -77,6 +77,11 @@ impl<'a> Vt<'a> { self.provider.identify(hash128(key)) } + /// Whether things are locatable already. + pub fn locatable(&self) -> bool { + self.introspector.init() + } + /// Locate all metadata matches for the given selector. pub fn locate(&self, selector: Selector) -> Vec<(StableId, &Content)> { self.introspector.locate(selector) @@ -115,6 +120,7 @@ impl StabilityProvider { /// Provides access to information about the document. #[doc(hidden)] pub struct Introspector { + init: bool, nodes: Vec<(StableId, Content)>, queries: RefCell<Vec<(Selector, u128)>>, } @@ -122,7 +128,11 @@ pub struct Introspector { impl Introspector { /// Create a new introspector. fn new() -> Self { - Self { nodes: vec![], queries: RefCell::new(vec![]) } + Self { + init: false, + nodes: vec![], + queries: RefCell::new(vec![]), + } } /// Update the information given new frames and return whether we can stop @@ -135,14 +145,20 @@ impl Introspector { self.extract(frame, page, Transform::identity()); } + let was_init = std::mem::replace(&mut self.init, true); let queries = std::mem::take(&mut self.queries).into_inner(); - for (selector, hash) in queries { - let nodes = self.locate_impl(&selector); - if hash128(&nodes) != hash { + + for (selector, hash) in &queries { + let nodes = self.locate_impl(selector); + if hash128(&nodes) != *hash { return false; } } + if !was_init && !queries.is_empty() { + return false; + } + true } @@ -161,7 +177,7 @@ impl Introspector { let pos = pos.transform(ts); let mut node = content.clone(); let loc = Location { page, pos }; - node.push_field("loc", loc); + node.push_field("location", loc); self.nodes.push((id, node)); } } @@ -173,6 +189,11 @@ impl Introspector { #[comemo::track] impl Introspector { + /// Whether this introspector is not yet initialized. + fn init(&self) -> bool { + self.init + } + /// Locate all metadata matches for the given selector. fn locate(&self, selector: Selector) -> Vec<(StableId, &Content)> { let nodes = self.locate_impl(&selector); |
