diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-11-18 15:33:06 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-11-19 22:40:42 +0100 |
| commit | 1937d746abf19a5c1142db546912dbed0e6711fb (patch) | |
| tree | 287a7462720698347361be5d34d61cf7bf3dce45 /src | |
| parent | 9b8c1dc19fcda399f00e486460b4cc521d1a460b (diff) | |
Show everything!
Diffstat (limited to 'src')
| -rw-r--r-- | src/model/content.rs | 29 | ||||
| -rw-r--r-- | src/model/styles.rs | 157 |
2 files changed, 63 insertions, 123 deletions
diff --git a/src/model/content.rs b/src/model/content.rs index 3cae99b2..15ea33cb 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -20,7 +20,7 @@ use crate::World; /// - anything written between square brackets in Typst /// - any constructor function #[derive(Clone, Hash)] -pub struct Content(Arc<dyn Bounds>); +pub struct Content(Arc<dyn Bounds>, Vec<RecipeId>); impl Content { /// Create empty content. @@ -112,16 +112,16 @@ impl Content { } /// Style this content with a style entry. - pub fn styled_with_entry(mut self, entry: Style) -> Self { + pub fn styled_with_entry(mut self, style: Style) -> Self { if let Some(styled) = self.try_downcast_mut::<StyledNode>() { - styled.map.apply(entry); + styled.map.apply(style); self } else if let Some(styled) = self.downcast::<StyledNode>() { let mut map = styled.map.clone(); - map.apply(entry); + map.apply(style); StyledNode { sub: styled.sub.clone(), map }.pack() } else { - StyledNode { sub: self, map: entry.into() }.pack() + StyledNode { sub: self, map: style.into() }.pack() } } @@ -139,9 +139,20 @@ impl Content { StyledNode { sub: self, map: styles }.pack() } - /// Reenable a specific show rule recipe. - pub fn unguard(&self, id: RecipeId) -> Self { - self.clone().styled_with_entry(Style::Unguard(id)) + /// 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) } } @@ -241,7 +252,7 @@ pub trait Node: 'static { where Self: Debug + Hash + Sync + Send + Sized + 'static, { - Content(Arc::new(self)) + Content(Arc::new(self), vec![]) } /// Construct a node from the arguments. diff --git a/src/model/styles.rs b/src/model/styles.rs index ff9905f6..ced11e6a 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -31,13 +31,6 @@ impl StyleMap { self.0.is_empty() } - /// Create a style map from a single property-value pair. - pub fn with<'a, K: Key<'a>>(key: K, value: K::Value) -> Self { - let mut styles = Self::new(); - styles.set(key, value); - styles - } - /// Set an inner value for a style property. /// /// If the property needs folding and the value is already contained in the @@ -75,12 +68,12 @@ impl StyleMap { } } - /// Set an outer style property. + /// Set an outer style. /// /// Like [`chain`](Self::chain) or [`apply_map`](Self::apply_map), but with /// only a entry. - pub fn apply(&mut self, entry: Style) { - self.0.insert(0, entry); + pub fn apply(&mut self, style: Style) { + self.0.insert(0, style); } /// Apply styles from `tail` in-place. The resulting style map is equivalent @@ -135,10 +128,6 @@ pub enum Style { Recipe(Recipe), /// A barrier for scoped styles. Barrier(NodeId), - /// Guards against recursive show rules. - Guard(RecipeId), - /// Allows recursive show rules again. - Unguard(RecipeId), } impl Style { @@ -190,8 +179,6 @@ impl Debug for Style { Self::Property(property) => property.fmt(f)?, Self::Recipe(recipe) => recipe.fmt(f)?, Self::Barrier(id) => write!(f, "Barrier for {id:?}")?, - Self::Guard(sel) => write!(f, "Guard against {sel:?}")?, - Self::Unguard(sel) => write!(f, "Unguard against {sel:?}")?, } f.write_str("]") } @@ -338,14 +325,10 @@ impl Debug for KeyId { } } -/// A node that can be realized given some styles. +/// A built-in show rule for a node. #[capability] pub trait Show: 'static + Sync + Send { - /// Unguard nested content against a specific recipe. - fn unguard_parts(&self, id: RecipeId) -> Content; - - /// The base recipe for this node that is executed if there is no - /// user-defined show rule. + /// Execute the base recipe for this node. fn show( &self, world: Tracked<dyn World>, @@ -356,9 +339,9 @@ pub trait Show: 'static + Sync + Send { /// Post-process a node after it was realized. #[capability] pub trait Finalize: 'static + Sync + Send { - /// Finalize this node given the realization of a base or user recipe. 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. + /// 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. fn finalize( &self, world: Tracked<dyn World>, @@ -400,7 +383,7 @@ impl Recipe { } self.transform.apply(world, self.span, || { - Value::Content(target.to::<dyn Show>().unwrap().unguard_parts(sel)) + Value::Content(target.clone().guard(sel)) })? } @@ -420,7 +403,7 @@ impl Recipe { } let transformed = self.transform.apply(world, self.span, || { - Value::Content(make(mat.as_str().into())) + Value::Content(make(mat.as_str().into()).guard(sel)) })?; result.push(transformed); @@ -441,7 +424,7 @@ impl Recipe { None => return Ok(None), }; - Ok(Some(content.styled_with_entry(Style::Guard(sel)))) + Ok(Some(content)) } /// Whether this recipe is for the given node. @@ -566,87 +549,41 @@ impl<'a> StyleChain<'a> { K::get(self, self.values(key)) } - /// Whether the style chain has a matching recipe for the content. - pub fn applicable(self, target: &Content) -> bool { - // Find out how many recipes there any and whether any of them match. - let mut n = 0; - let mut any = true; - for recipe in self.entries().filter_map(Style::recipe) { - n += 1; - any |= recipe.applicable(target); - } - - // Find an applicable recipe. - if any { - for recipe in self.entries().filter_map(Style::recipe) { - if recipe.applicable(target) { - let sel = RecipeId::Nth(n); - if !self.guarded(sel) { - return true; - } - } - n -= 1; - } - } - - false - } - /// Apply show recipes in this style chain to a target. - pub fn apply( + pub fn show( self, world: Tracked<dyn World>, target: &Content, ) -> SourceResult<Option<Content>> { - // Find out how many recipes there any and whether any of them match. - let mut n = 0; - let mut any = true; - for recipe in self.entries().filter_map(Style::recipe) { - n += 1; - any |= recipe.applicable(target); - } + // Find out how many recipes there are. + let mut n = self.entries().filter_map(Style::recipe).count(); // Find an applicable recipe. let mut realized = None; - let mut guarded = false; - if any { - for recipe in self.entries().filter_map(Style::recipe) { - if recipe.applicable(target) { - let sel = RecipeId::Nth(n); - if self.guarded(sel) { - guarded = true; - } else if let Some(content) = recipe.apply(world, sel, target)? { - realized = Some(content); - break; - } + for recipe in self.entries().filter_map(Style::recipe) { + let sel = RecipeId::Nth(n); + if recipe.applicable(target) && !target.guarded(sel) { + if let Some(content) = recipe.apply(world, sel, target)? { + realized = Some(content); + break; } - n -= 1; } + n -= 1; } - if let Some(showable) = target.to::<dyn Show>() { - // Realize if there was no matching recipe. - if realized.is_none() { - let sel = RecipeId::Base(target.id()); - if self.guarded(sel) { - guarded = true; - } else { - let content = showable - .unguard_parts(sel) - .to::<dyn Show>() - .unwrap() - .show(world, self)?; - - realized = Some(content.styled_with_entry(Style::Guard(sel))); - } + // 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::<dyn Show>() { + realized = Some(showable.show(world, self)?); } + } - // Finalize only if guarding didn't stop any recipe. - if !guarded { - if let Some(node) = target.to::<dyn Finalize>() { - if let Some(content) = realized { - realized = Some(node.finalize(world, self, content)?); - } + // Finalize only if this is the first application for this node. + if let Some(node) = target.to::<dyn Finalize>() { + if target.pristine() { + if let Some(content) = realized { + realized = Some(node.finalize(world, self, content)?); } } } @@ -654,14 +591,17 @@ impl<'a> StyleChain<'a> { Ok(realized) } - /// Whether the recipe identified by the selector is guarded. - fn guarded(self, sel: RecipeId) -> bool { - for entry in self.entries() { - match *entry { - Style::Guard(s) if s == sel => return true, - Style::Unguard(s) if s == sel => return false, - _ => {} + /// Whether the style chain has a matching recipe for the content. + pub fn applicable(self, target: &Content) -> bool { + // Find out how many recipes there are. + let mut n = self.entries().filter_map(Style::recipe).count(); + + // 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)) { + return true; } + n -= 1; } false @@ -689,7 +629,6 @@ impl<'a> StyleChain<'a> { entries: self.entries(), key: PhantomData, barriers: 0, - guarded: false, } } @@ -727,7 +666,6 @@ struct Values<'a, K> { entries: Entries<'a>, key: PhantomData<K>, barriers: usize, - guarded: bool, } impl<'a, K: Key<'a>> Iterator for Values<'a, K> { @@ -738,13 +676,7 @@ impl<'a, K: Key<'a>> Iterator for Values<'a, K> { match entry { Style::Property(property) => { if let Some(value) = property.downcast::<K>() { - if !property.scoped() - || if self.guarded { - self.barriers == 1 - } else { - self.barriers <= 1 - } - { + if !property.scoped() || self.barriers <= 1 { return Some(value); } } @@ -752,9 +684,6 @@ impl<'a, K: Key<'a>> Iterator for Values<'a, K> { Style::Barrier(id) => { self.barriers += (*id == K::node()) as usize; } - Style::Guard(RecipeId::Base(id)) => { - self.guarded |= *id == K::node(); - } _ => {} } } |
