summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/model/content.rs57
-rw-r--r--src/model/realize.rs42
-rw-r--r--src/model/typeset.rs15
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()))
}
}