Split show into realize and finalize

This commit is contained in:
Laurenz 2022-04-24 14:39:53 +02:00
parent 04fb8b288a
commit 50e4002a2a
10 changed files with 165 additions and 170 deletions

View File

@ -104,6 +104,23 @@ impl Content {
Self::Show(node.pack()) Self::Show(node.pack())
} }
/// Create a new sequence nodes from multiples nodes.
pub fn sequence(seq: Vec<Self>) -> Self {
if seq.len() == 1 {
seq.into_iter().next().unwrap()
} else {
Self::Sequence(Arc::new(seq))
}
}
/// Repeat this content `n` times.
pub fn repeat(&self, n: i64) -> StrResult<Self> {
let count = usize::try_from(n)
.map_err(|_| format!("cannot repeat this content {} times", n))?;
Ok(Self::sequence(vec![self.clone(); count]))
}
/// Style this content with a single style property. /// Style this content with a single style property.
pub fn styled<'k, K: Key<'k>>(mut self, key: K, value: K::Value) -> Self { pub fn styled<'k, K: Key<'k>>(mut self, key: K, value: K::Value) -> Self {
if let Self::Styled(styled) = &mut self { if let Self::Styled(styled) = &mut self {
@ -137,21 +154,24 @@ impl Content {
Self::show(DecoNode::<UNDERLINE>(self)) Self::show(DecoNode::<UNDERLINE>(self))
} }
/// Create a new sequence nodes from multiples nodes. /// Return a node that is spaced apart at top and bottom.
pub fn sequence(seq: Vec<Self>) -> Self { pub fn spaced(self, above: Length, below: Length) -> Self {
if seq.len() == 1 { if above.is_zero() && below.is_zero() {
seq.into_iter().next().unwrap() return self;
} else {
Self::Sequence(Arc::new(seq))
} }
}
/// Repeat this content `n` times. let mut seq = vec![];
pub fn repeat(&self, n: i64) -> StrResult<Self> { if !above.is_zero() {
let count = usize::try_from(n) seq.push(Content::Vertical(above.into()));
.map_err(|_| format!("cannot repeat this content {} times", n))?; }
Ok(Self::sequence(vec![self.clone(); count])) seq.push(self);
if !below.is_zero() {
seq.push(Content::Vertical(below.into()));
}
Self::sequence(seq)
} }
/// Layout this content into a collection of pages. /// Layout this content into a collection of pages.
@ -454,8 +474,11 @@ impl<'a> Builder<'a> {
} }
Content::Show(node) => { Content::Show(node) => {
let id = node.id(); let id = node.id();
let realized = styles.realize(ctx, node)?; let realized = match styles.realize(ctx, node)? {
let content = node.show(ctx, styles, realized)?; Some(content) => content,
None => node.realize(ctx, styles)?,
};
let content = node.finalize(ctx, styles, realized)?;
let stored = self.tpa.alloc(content); let stored = self.tpa.alloc(content);
self.process(ctx, stored, styles.unscoped(id))?; self.process(ctx, stored, styles.unscoped(id))?;
} }

View File

@ -13,14 +13,26 @@ pub trait Show: 'static {
/// Encode this node into a dictionary. /// Encode this node into a dictionary.
fn encode(&self) -> Dict; fn encode(&self) -> Dict;
/// Show this node in the given styles and optionally given the realization /// The base recipe for this node that is executed if there is no
/// of a show rule. /// user-defined show rule.
fn show( fn realize(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content>;
/// 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:
/// - Application of general settable properties
/// - Attaching things like semantics to a heading
///
/// Defaults to just the realized content.
#[allow(unused_variables)]
fn finalize(
&self, &self,
ctx: &mut Context, ctx: &mut Context,
styles: StyleChain, styles: StyleChain,
realized: Option<Content>, realized: Content,
) -> TypResult<Content>; ) -> TypResult<Content> {
Ok(realized)
}
/// Convert to a packed show node. /// Convert to a packed show node.
fn pack(self) -> ShowNode fn pack(self) -> ShowNode
@ -55,13 +67,17 @@ impl Show for ShowNode {
self.0.encode() self.0.encode()
} }
fn show( fn realize(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
self.0.realize(ctx, styles)
}
fn finalize(
&self, &self,
ctx: &mut Context, ctx: &mut Context,
styles: StyleChain, styles: StyleChain,
realized: Option<Content>, realized: Content,
) -> TypResult<Content> { ) -> TypResult<Content> {
self.0.show(ctx, styles, realized) self.0.finalize(ctx, styles, realized)
} }
fn pack(self) -> ShowNode { fn pack(self) -> ShowNode {

View File

@ -35,26 +35,27 @@ impl Show for MathNode {
} }
} }
fn show( fn realize(&self, _: &mut Context, _: StyleChain) -> TypResult<Content> {
Ok(Content::Text(self.formula.trim().into()))
}
fn finalize(
&self, &self,
_: &mut Context, _: &mut Context,
styles: StyleChain, styles: StyleChain,
realized: Option<Content>, mut realized: Content,
) -> TypResult<Content> { ) -> TypResult<Content> {
let mut content =
realized.unwrap_or_else(|| Content::Text(self.formula.trim().into()));
let mut map = StyleMap::new(); let mut map = StyleMap::new();
if let Smart::Custom(family) = styles.get(Self::FAMILY) { if let Smart::Custom(family) = styles.get(Self::FAMILY) {
map.set_family(family.clone(), styles); map.set_family(family.clone(), styles);
} }
content = content.styled_with_map(map); realized = realized.styled_with_map(map);
if self.display { if self.display {
content = Content::Block(content.pack()); realized = Content::block(realized);
} }
Ok(content) Ok(realized)
} }
} }

View File

@ -63,11 +63,15 @@ impl Show for HeadingNode {
} }
} }
fn show( fn realize(&self, _: &mut Context, _: StyleChain) -> TypResult<Content> {
Ok(self.body.clone())
}
fn finalize(
&self, &self,
ctx: &mut Context, ctx: &mut Context,
styles: StyleChain, styles: StyleChain,
realized: Option<Content>, mut realized: Content,
) -> TypResult<Content> { ) -> TypResult<Content> {
macro_rules! resolve { macro_rules! resolve {
($key:expr) => { ($key:expr) => {
@ -75,8 +79,6 @@ impl Show for HeadingNode {
}; };
} }
let mut body = realized.unwrap_or_else(|| self.body.clone());
let mut map = StyleMap::new(); let mut map = StyleMap::new();
map.set(TextNode::SIZE, resolve!(Self::SIZE)); map.set(TextNode::SIZE, resolve!(Self::SIZE));
@ -96,30 +98,22 @@ impl Show for HeadingNode {
map.set(TextNode::EMPH, Toggle); map.set(TextNode::EMPH, Toggle);
} }
let mut seq = vec![];
if resolve!(Self::UNDERLINE) { if resolve!(Self::UNDERLINE) {
body = body.underlined(); realized = realized.underlined();
} }
let above = resolve!(Self::ABOVE); realized = realized.styled_with_map(map);
if !above.is_zero() {
seq.push(Content::Vertical(above.resolve(styles).into()));
}
seq.push(body);
let below = resolve!(Self::BELOW);
if !below.is_zero() {
seq.push(Content::Vertical(below.resolve(styles).into()));
}
let mut content = Content::sequence(seq).styled_with_map(map);
if resolve!(Self::BLOCK) { if resolve!(Self::BLOCK) {
content = Content::block(content); realized = Content::block(realized);
} }
Ok(content) realized = realized.spaced(
resolve!(Self::ABOVE).resolve(styles),
resolve!(Self::BELOW).resolve(styles),
);
Ok(realized)
} }
} }

View File

@ -79,66 +79,51 @@ impl<const L: ListKind> Show for ListNode<L> {
} }
} }
fn show( fn realize(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
&self, let mut cells = vec![];
ctx: &mut Context, let mut number = self.start;
styles: StyleChain,
realized: Option<Content>, let label = styles.get(Self::LABEL);
) -> TypResult<Content> {
let content = if let Some(content) = realized { for item in &self.items {
content number = item.number.unwrap_or(number);
cells.push(LayoutNode::default());
cells.push(label.resolve(ctx, L, number)?.pack());
cells.push(LayoutNode::default());
cells.push((*item.body).clone().pack());
number += 1;
}
let leading = styles.get(ParNode::LEADING);
let spacing = if self.tight {
styles.get(Self::SPACING)
} else { } else {
let mut cells = vec![]; styles.get(ParNode::SPACING)
let mut number = self.start;
let label = styles.get(Self::LABEL);
for item in &self.items {
number = item.number.unwrap_or(number);
cells.push(LayoutNode::default());
cells.push(label.resolve(ctx, L, number)?.pack());
cells.push(LayoutNode::default());
cells.push((*item.body).clone().pack());
number += 1;
}
let leading = styles.get(ParNode::LEADING);
let spacing = if self.tight {
styles.get(Self::SPACING)
} else {
styles.get(ParNode::SPACING)
};
let gutter = leading + spacing;
let indent = styles.get(Self::INDENT);
let body_indent = styles.get(Self::BODY_INDENT);
Content::block(GridNode {
tracks: Spec::with_x(vec![
TrackSizing::Relative(indent.into()),
TrackSizing::Auto,
TrackSizing::Relative(body_indent.into()),
TrackSizing::Auto,
]),
gutter: Spec::with_y(vec![TrackSizing::Relative(gutter.into())]),
cells,
})
}; };
let mut seq = vec![]; let gutter = leading + spacing;
let above = styles.get(Self::ABOVE); let indent = styles.get(Self::INDENT);
if !above.is_zero() { let body_indent = styles.get(Self::BODY_INDENT);
seq.push(Content::Vertical(above.into()));
}
seq.push(content); Ok(Content::block(GridNode {
tracks: Spec::with_x(vec![
TrackSizing::Relative(indent.into()),
TrackSizing::Auto,
TrackSizing::Relative(body_indent.into()),
TrackSizing::Auto,
]),
gutter: Spec::with_y(vec![TrackSizing::Relative(gutter.into())]),
cells,
}))
}
let below = styles.get(Self::BELOW); fn finalize(
if !below.is_zero() { &self,
seq.push(Content::Vertical(below.into())); _: &mut Context,
} styles: StyleChain,
realized: Content,
Ok(Content::sequence(seq)) ) -> TypResult<Content> {
Ok(realized.spaced(styles.get(Self::ABOVE), styles.get(Self::BELOW)))
} }
} }

View File

@ -64,16 +64,7 @@ impl Show for TableNode {
} }
} }
fn show( fn realize(&self, _: &mut Context, styles: StyleChain) -> TypResult<Content> {
&self,
_: &mut Context,
styles: StyleChain,
realized: Option<Content>,
) -> TypResult<Content> {
if let Some(content) = realized {
return Ok(content);
}
let primary = styles.get(Self::PRIMARY); let primary = styles.get(Self::PRIMARY);
let secondary = styles.get(Self::SECONDARY); let secondary = styles.get(Self::SECONDARY);
let stroke = styles.get(Self::STROKE).map(RawStroke::unwrap_or_default); let stroke = styles.get(Self::STROKE).map(RawStroke::unwrap_or_default);

View File

@ -47,20 +47,13 @@ impl<const L: DecoLine> Show for DecoNode<L> {
dict! { "body" => Value::Content(self.0.clone()) } dict! { "body" => Value::Content(self.0.clone()) }
} }
fn show( fn realize(&self, _: &mut Context, styles: StyleChain) -> TypResult<Content> {
&self, Ok(self.0.clone().styled(TextNode::DECO, Decoration {
_: &mut Context, line: L,
styles: StyleChain, stroke: styles.get(Self::STROKE).unwrap_or_default(),
realized: Option<Content>, offset: styles.get(Self::OFFSET),
) -> TypResult<Content> { extent: styles.get(Self::EXTENT),
Ok(realized.unwrap_or_else(|| { evade: styles.get(Self::EVADE),
self.0.clone().styled(TextNode::DECO, Decoration {
line: L,
stroke: styles.get(Self::STROKE).unwrap_or_default(),
offset: styles.get(Self::OFFSET),
extent: styles.get(Self::EXTENT),
evade: styles.get(Self::EVADE),
})
})) }))
} }
} }

View File

@ -38,13 +38,8 @@ impl Show for LinkNode {
} }
} }
fn show( fn realize(&self, _: &mut Context, _: StyleChain) -> TypResult<Content> {
&self, Ok(self.body.clone().unwrap_or_else(|| {
_: &mut Context,
styles: StyleChain,
realized: Option<Content>,
) -> TypResult<Content> {
let mut body = realized.or_else(|| self.body.clone()).unwrap_or_else(|| {
let url = &self.url; let url = &self.url;
let mut text = url.as_str(); let mut text = url.as_str();
for prefix in ["mailto:", "tel:"] { for prefix in ["mailto:", "tel:"] {
@ -52,8 +47,15 @@ impl Show for LinkNode {
} }
let shorter = text.len() < url.len(); let shorter = text.len() < url.len();
Content::Text(if shorter { text.into() } else { url.clone() }) Content::Text(if shorter { text.into() } else { url.clone() })
}); }))
}
fn finalize(
&self,
_: &mut Context,
styles: StyleChain,
mut realized: Content,
) -> TypResult<Content> {
let mut map = StyleMap::new(); let mut map = StyleMap::new();
map.set(TextNode::LINK, Some(self.url.clone())); map.set(TextNode::LINK, Some(self.url.clone()));
@ -62,9 +64,9 @@ impl Show for LinkNode {
} }
if styles.get(Self::UNDERLINE) { if styles.get(Self::UNDERLINE) {
body = body.underlined(); realized = realized.underlined();
} }
Ok(body.styled_with_map(map)) Ok(realized.styled_with_map(map))
} }
} }

View File

@ -475,13 +475,8 @@ impl Show for StrongNode {
dict! { "body" => Value::Content(self.0.clone()) } dict! { "body" => Value::Content(self.0.clone()) }
} }
fn show( fn realize(&self, _: &mut Context, _: StyleChain) -> TypResult<Content> {
&self, Ok(self.0.clone().styled(TextNode::STRONG, Toggle))
_: &mut Context,
_: StyleChain,
realized: Option<Content>,
) -> TypResult<Content> {
Ok(realized.unwrap_or_else(|| self.0.clone().styled(TextNode::STRONG, Toggle)))
} }
} }
@ -501,12 +496,7 @@ impl Show for EmphNode {
dict! { "body" => Value::Content(self.0.clone()) } dict! { "body" => Value::Content(self.0.clone()) }
} }
fn show( fn realize(&self, _: &mut Context, _: StyleChain) -> TypResult<Content> {
&self, Ok(self.0.clone().styled(TextNode::EMPH, Toggle))
_: &mut Context,
_: StyleChain,
realized: Option<Content>,
) -> TypResult<Content> {
Ok(realized.unwrap_or_else(|| self.0.clone().styled(TextNode::EMPH, Toggle)))
} }
} }

View File

@ -50,12 +50,7 @@ impl Show for RawNode {
} }
} }
fn show( fn realize(&self, _: &mut Context, styles: StyleChain) -> TypResult<Content> {
&self,
_: &mut Context,
styles: StyleChain,
realized: Option<Content>,
) -> TypResult<Content> {
let lang = styles.get(Self::LANG).as_ref(); let lang = styles.get(Self::LANG).as_ref();
let foreground = THEME let foreground = THEME
.settings .settings
@ -64,9 +59,7 @@ impl Show for RawNode {
.unwrap_or(Color::BLACK) .unwrap_or(Color::BLACK)
.into(); .into();
let mut content = if let Some(content) = realized { if matches!(
content
} else if matches!(
lang.map(|s| s.to_lowercase()).as_deref(), lang.map(|s| s.to_lowercase()).as_deref(),
Some("typ" | "typst") Some("typ" | "typst")
) { ) {
@ -79,7 +72,7 @@ impl Show for RawNode {
seq.push(styled(&self.text[range], foreground, style)); seq.push(styled(&self.text[range], foreground, style));
}); });
Content::sequence(seq) Ok(Content::sequence(seq))
} else if let Some(syntax) = } else if let Some(syntax) =
lang.and_then(|token| SYNTAXES.find_syntax_by_token(&token)) lang.and_then(|token| SYNTAXES.find_syntax_by_token(&token))
{ {
@ -95,11 +88,18 @@ impl Show for RawNode {
} }
} }
Content::sequence(seq) Ok(Content::sequence(seq))
} else { } else {
Content::Text(self.text.clone()) Ok(Content::Text(self.text.clone()))
}; }
}
fn finalize(
&self,
_: &mut Context,
styles: StyleChain,
mut realized: Content,
) -> TypResult<Content> {
let mut map = StyleMap::new(); let mut map = StyleMap::new();
map.set(TextNode::OVERHANG, false); map.set(TextNode::OVERHANG, false);
map.set(TextNode::HYPHENATE, Smart::Custom(Hyphenate(false))); map.set(TextNode::HYPHENATE, Smart::Custom(Hyphenate(false)));
@ -109,13 +109,13 @@ impl Show for RawNode {
map.set_family(family.clone(), styles); map.set_family(family.clone(), styles);
} }
content = content.styled_with_map(map); realized = realized.styled_with_map(map);
if self.block { if self.block {
content = Content::Block(content.pack()); realized = Content::block(realized);
} }
Ok(content) Ok(realized)
} }
} }