Show everything!

This commit is contained in:
Laurenz 2022-11-18 15:33:06 +01:00
parent 9b8c1dc19f
commit 1937d746ab
19 changed files with 95 additions and 251 deletions

View File

@ -32,7 +32,7 @@ use typst::diag::SourceResult;
use typst::frame::Frame;
use typst::geom::*;
use typst::model::{
capability, Content, Node, SequenceNode, Show, Style, StyleChain, StyleVecBuilder,
capability, Content, Node, SequenceNode, Style, StyleChain, StyleVecBuilder,
StyledNode,
};
use typst::World;
@ -87,7 +87,7 @@ impl LayoutBlock for Content {
regions: &Regions,
styles: StyleChain,
) -> SourceResult<Vec<Frame>> {
if !self.has::<dyn Show>() || !styles.applicable(self) {
if !styles.applicable(self) {
if let Some(node) = self.to::<dyn LayoutBlock>() {
let barrier = Style::Barrier(self.id());
let styles = barrier.chain(&styles);
@ -126,7 +126,7 @@ impl LayoutInline for Content {
assert!(regions.backlog.is_empty());
assert!(regions.last.is_none());
if !self.has::<dyn Show>() || !styles.applicable(self) {
if !styles.applicable(self) {
if let Some(node) = self.to::<dyn LayoutInline>() {
let barrier = Style::Barrier(self.id());
let styles = barrier.chain(&styles);
@ -312,16 +312,17 @@ impl<'a> Builder<'a> {
content: &'a Content,
styles: StyleChain<'a>,
) -> SourceResult<()> {
if content.is::<TextNode>() {
if let Some(realized) = styles.apply(self.world, content)? {
let stored = self.scratch.content.alloc(realized);
return self.accept(stored, styles);
}
} else if let Some(styled) = content.downcast::<StyledNode>() {
if let Some(styled) = content.downcast::<StyledNode>() {
return self.styled(styled, styles);
} else if let Some(seq) = content.downcast::<SequenceNode>() {
}
if let Some(seq) = content.downcast::<SequenceNode>() {
return self.sequence(seq, styles);
} else if content.has::<dyn Show>() && self.show(content, styles)? {
}
if let Some(realized) = styles.show(self.world, content)? {
let stored = self.scratch.content.alloc(realized);
self.accept(stored, styles)?;
return Ok(());
}
@ -361,17 +362,6 @@ impl<'a> Builder<'a> {
Ok(())
}
fn show(&mut self, content: &Content, styles: StyleChain<'a>) -> SourceResult<bool> {
let Some(realized) = styles.apply(self.world, content)? else {
return Ok(false);
};
let stored = self.scratch.content.alloc(realized);
self.accept(stored, styles)?;
Ok(true)
}
fn styled(
&mut self,
styled: &'a StyledNode,

View File

@ -28,15 +28,16 @@ impl MathNode {
}
impl Show for MathNode {
fn unguard_parts(&self, _: RecipeId) -> Content {
self.clone().pack()
}
fn show(&self, _: Tracked<dyn World>, styles: StyleChain) -> SourceResult<Content> {
let mut map = StyleMap::new();
map.set_family(FontFamily::new("NewComputerModernMath"), styles);
let mut realized = self.clone().pack().styled_with_map(map);
let mut realized = self
.clone()
.pack()
.guard(RecipeId::Base(NodeId::of::<Self>()))
.styled_with_map(map);
if self.display {
realized = realized.aligned(Axes::with_x(Some(Align::Center.into())))
}

View File

@ -16,8 +16,8 @@ pub use typst::geom::*;
#[doc(no_inline)]
pub use typst::model::{
array, capability, castable, dict, dynamic, format_str, node, Args, Array, Cast,
Content, Dict, Finalize, Fold, Func, Node, RecipeId, Resolve, Show, Smart, Str,
StyleChain, StyleMap, StyleVec, Value, Vm,
Content, Dict, Finalize, Fold, Func, Node, NodeId, RecipeId, Resolve, Show, Smart,
Str, StyleChain, StyleMap, StyleVec, Value, Vm,
};
#[doc(no_inline)]
pub use typst::syntax::{Span, Spanned};

View File

@ -34,10 +34,6 @@ impl HeadingNode {
}
impl Show for HeadingNode {
fn unguard_parts(&self, id: RecipeId) -> Content {
Self { body: self.body.unguard(id), ..*self }.pack()
}
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
Ok(BlockNode(self.body.clone()).pack())
}

View File

@ -6,7 +6,7 @@ use crate::prelude::*;
use crate::text::{ParNode, SpaceNode, TextNode};
/// An unordered (bulleted) or ordered (numbered) list.
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Hash)]
pub struct ListNode<const L: ListKind = LIST> {
/// If true, the items are separated by leading instead of list spacing.
pub tight: bool,
@ -20,7 +20,7 @@ pub type EnumNode = ListNode<ENUM>;
/// A description list.
pub type DescNode = ListNode<DESC>;
#[node(Show, LayoutBlock)]
#[node(LayoutBlock)]
impl<const L: ListKind> ListNode<L> {
/// How the list is labelled.
#[property(referenced)]
@ -77,20 +77,6 @@ impl<const L: ListKind> ListNode<L> {
}
}
impl<const L: ListKind> Show for ListNode<L> {
fn unguard_parts(&self, id: RecipeId) -> Content {
Self {
items: self.items.map(|item| item.unguard(id)),
..*self
}
.pack()
}
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
Ok(self.clone().pack())
}
}
impl<const L: ListKind> LayoutBlock for ListNode<L> {
fn layout_block(
&self,
@ -178,17 +164,6 @@ impl ListItem {
}
}
fn unguard(&self, sel: RecipeId) -> Self {
match self {
Self::List(body) => Self::List(Box::new(body.unguard(sel))),
Self::Enum(number, body) => Self::Enum(*number, Box::new(body.unguard(sel))),
Self::Desc(item) => Self::Desc(Box::new(DescItem {
term: item.term.unguard(sel),
body: item.body.unguard(sel),
})),
}
}
/// Encode the item into a value.
fn encode(&self) -> Value {
match self {

View File

@ -20,10 +20,6 @@ impl RefNode {
}
impl Show for RefNode {
fn unguard_parts(&self, _: RecipeId) -> Content {
Self(self.0.clone()).pack()
}
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
Ok(TextNode::packed(format_eco!("@{}", self.0)))
}

View File

@ -2,7 +2,7 @@ use crate::layout::{GridNode, TrackSizing, TrackSizings};
use crate::prelude::*;
/// A table of items.
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Hash)]
pub struct TableNode {
/// Defines sizing for content rows and columns.
pub tracks: Axes<Vec<TrackSizing>>,
@ -12,7 +12,7 @@ pub struct TableNode {
pub cells: Vec<Content>,
}
#[node(Show, LayoutBlock)]
#[node(LayoutBlock)]
impl TableNode {
/// How to fill the cells.
#[property(referenced)]
@ -50,21 +50,6 @@ impl TableNode {
}
}
impl Show for TableNode {
fn unguard_parts(&self, id: RecipeId) -> Content {
Self {
tracks: self.tracks.clone(),
gutter: self.gutter.clone(),
cells: self.cells.iter().map(|cell| cell.unguard(id)).collect(),
}
.pack()
}
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
Ok(self.clone().pack())
}
}
impl LayoutBlock for TableNode {
fn layout_block(
&self,

View File

@ -47,10 +47,6 @@ impl<const L: DecoLine> DecoNode<L> {
}
impl<const L: DecoLine> Show for DecoNode<L> {
fn unguard_parts(&self, id: RecipeId) -> Content {
Self(self.0.unguard(id)).pack()
}
fn show(&self, _: Tracked<dyn World>, styles: StyleChain) -> SourceResult<Content> {
Ok(self.0.clone().styled(
TextNode::DECO,

View File

@ -50,14 +50,6 @@ impl LinkNode {
}
impl Show for LinkNode {
fn unguard_parts(&self, id: RecipeId) -> Content {
Self {
dest: self.dest.clone(),
body: self.body.unguard(id),
}
.pack()
}
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
Ok(self.body.clone())
}

View File

@ -528,10 +528,6 @@ impl StrongNode {
}
impl Show for StrongNode {
fn unguard_parts(&self, id: RecipeId) -> Content {
Self(self.0.unguard(id)).pack()
}
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
Ok(self.0.clone().styled(TextNode::BOLD, Toggle))
}
@ -556,10 +552,6 @@ impl EmphNode {
}
impl Show for EmphNode {
fn unguard_parts(&self, id: RecipeId) -> Content {
Self(self.0.unguard(id)).pack()
}
fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> SourceResult<Content> {
Ok(self.0.clone().styled(TextNode::ITALIC, Toggle))
}

View File

@ -43,10 +43,6 @@ impl RawNode {
}
impl Show for RawNode {
fn unguard_parts(&self, _: RecipeId) -> Content {
Self { text: self.text.clone(), ..*self }.pack()
}
fn show(&self, _: Tracked<dyn World>, styles: StyleChain) -> SourceResult<Content> {
let lang = styles.get(Self::LANG).as_ref().map(|s| s.to_lowercase());
let foreground = THEME

View File

@ -43,10 +43,6 @@ impl<const S: ShiftKind> ShiftNode<S> {
}
impl<const S: ShiftKind> Show for ShiftNode<S> {
fn unguard_parts(&self, _: RecipeId) -> Content {
Self(self.0.clone()).pack()
}
fn show(
&self,
world: Tracked<dyn World>,

View File

@ -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.

View File

@ -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;
}
// 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)?);
}
}
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)));
}
}
// 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();
}
_ => {}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -13,17 +13,6 @@ Die Zeitung Der Spiegel existiert.
TeX, LaTeX, LuaTeX and LuaLaTeX!
---
// Test out-of-order guarding.
#show "Good": [Typst!]
#show "Typst": [Fun!]
#show "Fun": [Good!]
#show enum: []
Good \
Fun \
Typst \
---
// Test that replacements happen exactly once.
#show "A": [BB]

View File

@ -1,19 +1,19 @@
// Test sub- and superscipt shifts.
---
#table(columns: 3,
[Typo.], [Fallb.], [Synth],
[x#super[1]], [x#super[5n]], [x#super[2 #square(width: 6pt)]],
[x#sub[1]], [x#sub[5n]], [x#sub[2 #square(width: 6pt)]],
#table(
columns: 3,
[Typo.], [Fallb.], [Synth],
[x#super[1]], [x#super[5n]], [x#super[2 #square(width: 6pt)]],
[x#sub[1]], [x#sub[5n]], [x#sub[2 #square(width: 6pt)]],
)
---
#set super(typographic: false, baseline: -0.25em, size: 0.7em)
n#super[1], n#sub[2], ... n#super[N]
n#super[1], n#sub[2], ... n#super[N]
---
#set underline(stroke: 0.5pt, offset: 0.15em)
#underline[The claim#super[\[4\]]] has been disputed. \
The claim#super[#underline[\[4\]]] has been disputed. \
It really has been#super(box(text(baseline: 0pt, underline[\[4\]]))) \