Split up styled and sequence template

This commit is contained in:
Laurenz 2022-02-02 16:18:10 +01:00
parent a7b403fd74
commit d3ccd55d4b
3 changed files with 62 additions and 57 deletions

View File

@ -183,17 +183,18 @@ fn eval_markup(
nodes: &mut impl Iterator<Item = MarkupNode>, nodes: &mut impl Iterator<Item = MarkupNode>,
) -> TypResult<Template> { ) -> TypResult<Template> {
let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default()); let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default());
let mut styles = StyleMap::new();
while let Some(node) = nodes.next() { while let Some(node) = nodes.next() {
let template = match node { seq.push(match node {
MarkupNode::Expr(Expr::Set(set)) => { MarkupNode::Expr(Expr::Set(set)) => {
let class = set.class(); let class = set.class();
let class = class.eval(ctx)?.cast::<Class>().at(class.span())?; let class = class.eval(ctx)?.cast::<Class>().at(class.span())?;
let mut args = set.args().eval(ctx)?; let mut args = set.args().eval(ctx)?;
let mut styles = StyleMap::new();
class.set(&mut args, &mut styles)?; class.set(&mut args, &mut styles)?;
args.finish()?; args.finish()?;
continue; let tail = eval_markup(ctx, nodes)?;
tail.styled_with_map(styles)
} }
MarkupNode::Expr(Expr::Show(show)) => { MarkupNode::Expr(Expr::Show(show)) => {
return Err("show rules are not yet implemented").at(show.span()); return Err("show rules are not yet implemented").at(show.span());
@ -204,12 +205,14 @@ fn eval_markup(
wrap.body().eval(ctx)?.show() wrap.body().eval(ctx)?.show()
} }
_ => node.eval(ctx)?, _ => node.eval(ctx)?,
}; });
seq.push(Styled::new(template, styles.clone()));
} }
if seq.len() == 1 {
Ok(seq.into_iter().next().unwrap())
} else {
Ok(Template::Sequence(seq)) Ok(Template::Sequence(seq))
}
} }
impl Eval for MarkupNode { impl Eval for MarkupNode {
@ -265,7 +268,7 @@ impl Eval for RawNode {
impl RawNode { impl RawNode {
/// Styled template for a code block, with optional syntax highlighting. /// Styled template for a code block, with optional syntax highlighting.
pub fn highlighted(&self) -> Template { pub fn highlighted(&self) -> Template {
let mut seq: Vec<Styled<Template>> = vec![]; let mut seq: Vec<Template> = vec![];
let syntax = if let Some(syntax) = self let syntax = if let Some(syntax) = self
.lang .lang
@ -294,7 +297,7 @@ impl RawNode {
let mut highlighter = HighlightLines::new(syntax, &THEME); let mut highlighter = HighlightLines::new(syntax, &THEME);
for (i, line) in self.text.lines().enumerate() { for (i, line) in self.text.lines().enumerate() {
if i != 0 { if i != 0 {
seq.push(Styled::bare(Template::Linebreak)); seq.push(Template::Linebreak);
} }
for (style, piece) in highlighter.highlight(line, &SYNTAXES) { for (style, piece) in highlighter.highlight(line, &SYNTAXES) {
@ -322,7 +325,7 @@ impl RawNode {
} }
/// Style a piece of text with a syntect style. /// Style a piece of text with a syntect style.
fn style_piece(piece: &str, foreground: Paint, style: SynStyle) -> Styled<Template> { fn style_piece(piece: &str, foreground: Paint, style: SynStyle) -> Template {
let mut styles = StyleMap::new(); let mut styles = StyleMap::new();
let paint = style.foreground.into(); let paint = style.foreground.into();
@ -342,7 +345,7 @@ fn style_piece(piece: &str, foreground: Paint, style: SynStyle) -> Styled<Templa
styles.set(TextNode::LINES, vec![DecoLine::Underline.into()]); styles.set(TextNode::LINES, vec![DecoLine::Underline.into()]);
} }
Styled::new(Template::Text(piece.into()), styles) Template::Text(piece.into()).styled_with_map(styles)
} }
impl Eval for MathNode { impl Eval for MathNode {

View File

@ -20,9 +20,25 @@ use crate::util::EcoString;
/// - anything written between square brackets in Typst /// - anything written between square brackets in Typst
/// - any class constructor /// - any class constructor
/// ///
/// When you write `[Hi] + [you]` in Typst, this type's [`Add`] implementation /// This enum has two notable variants:
/// is invoked. There, multiple templates are combined into a single ///
/// [`Sequence`](Self::Sequence) template. /// 1. A `Styled` template attaches a style map to a template. This map affects
/// the whole subtemplate. For example, a single bold word could be
/// represented as a `Styled(Text("Hello"), [TextNode::STRONG: true])`
/// template.
///
/// 2. A `Sequence` template simply combines multiple templates and will be
/// layouted as a [flow](FlowNode). So, when you write `[Hi] + [you]` in
/// Typst, this type's [`Add`] implementation is invoked and the two
/// templates are combined into a single [`Sequence`](Self::Sequence)
/// template.
///
/// A sequence may contain nested sequences (meaning this variant effectively
/// allows nodes to form trees). All nested sequences can equivalently be
/// represented as a single flat sequence, but allowing nesting doesn't hurt
/// since we can just recurse into the nested sequences during packing. Also,
/// in theory, this allows better complexity when adding (large) sequence
/// nodes (just like for a text rope).
#[derive(Debug, PartialEq, Clone, Hash)] #[derive(Debug, PartialEq, Clone, Hash)]
pub enum Template { pub enum Template {
/// A word space. /// A word space.
@ -45,21 +61,10 @@ pub enum Template {
Block(PackedNode), Block(PackedNode),
/// A page node. /// A page node.
Page(PageNode), Page(PageNode),
/// Multiple nodes with attached styles. /// A template with attached styles.
/// Styled(Box<Self>, StyleMap),
/// For example, the Typst template `[Hi *you!*]` would result in the /// A sequence of multiple subtemplates.
/// sequence: Sequence(Vec<Self>),
/// - `Text("Hi")` with empty style map,
/// - `Space` with empty style map,
/// - `Text("you!")` with `TextNode::STRONG` set to `true`.
///
/// A sequence may contain nested sequences (meaning this variant
/// effectively allows nodes to form trees). All nested sequences can
/// equivalently be represented as a single flat sequence, but allowing
/// nesting doesn't hurt since we can just recurse into the nested sequences
/// during packing. Also, in theory, this allows better complexity when
/// adding (large) sequence nodes (just like for a text rope).
Sequence(Vec<Styled<Self>>),
} }
impl Template { impl Template {
@ -86,30 +91,24 @@ impl Template {
/// Style this template with a single property. /// Style this template with a single property.
pub fn styled<P: Property>(mut self, key: P, value: P::Value) -> Self { pub fn styled<P: Property>(mut self, key: P, value: P::Value) -> Self {
if let Self::Sequence(vec) = &mut self { if let Self::Styled(_, map) = &mut self {
if let [styled] = vec.as_mut_slice() { map.set(key, value);
styled.map.set(key, value); self
return self; } else {
}
}
self.styled_with_map(StyleMap::with(key, value)) self.styled_with_map(StyleMap::with(key, value))
} }
}
/// Style this template with a full style map. /// Style this template with a full style map.
pub fn styled_with_map(mut self, styles: StyleMap) -> Self { pub fn styled_with_map(mut self, styles: StyleMap) -> Self {
if styles.is_empty() { if styles.is_empty() {
return self; self
} else if let Self::Styled(_, map) = &mut self {
map.apply(&styles);
self
} else {
Self::Styled(Box::new(self), styles)
} }
if let Self::Sequence(vec) = &mut self {
if let [styled] = vec.as_mut_slice() {
styled.map.apply(&styles);
return self;
}
}
Self::Sequence(vec![Styled::new(self, styles)])
} }
/// Style this template in monospace. /// Style this template in monospace.
@ -140,7 +139,7 @@ impl Template {
let count = usize::try_from(n) let count = usize::try_from(n)
.map_err(|_| format!("cannot repeat this template {} times", n))?; .map_err(|_| format!("cannot repeat this template {} times", n))?;
Ok(Self::Sequence(vec![Styled::bare(self.clone()); count])) Ok(Self::Sequence(vec![self.clone(); count]))
} }
} }
@ -160,15 +159,15 @@ impl Add for Template {
left left
} }
(Self::Sequence(mut left), right) => { (Self::Sequence(mut left), right) => {
left.push(Styled::bare(right)); left.push(right);
left left
} }
(left, Self::Sequence(mut right)) => { (left, Self::Sequence(mut right)) => {
right.insert(0, Styled::bare(left)); right.insert(0, left);
right right
} }
(left, right) => { (left, right) => {
vec![Styled::bare(left), Styled::bare(right)] vec![left, right]
} }
}) })
} }
@ -182,7 +181,7 @@ impl AddAssign for Template {
impl Sum for Template { impl Sum for Template {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
Self::Sequence(iter.map(|n| Styled::bare(n)).collect()) Self::Sequence(iter.collect())
} }
} }
@ -289,12 +288,15 @@ impl Packer {
self.push_block(Styled::new(page.0, styles)); self.push_block(Styled::new(page.0, styles));
} }
} }
Template::Sequence(list) => { Template::Styled(template, mut map) => {
map.apply(&styles);
self.walk(*template, map);
}
Template::Sequence(seq) => {
// For a list of templates, we apply the list's styles to each // For a list of templates, we apply the list's styles to each
// templates individually. // templates individually.
for Styled { item, mut map } in list { for item in seq {
map.apply(&styles); self.walk(item, styles.clone());
self.walk(item, map);
} }
} }
} }

View File

@ -130,13 +130,13 @@
#test(test == test, true) #test(test == test, true)
#test((() => {}) == (() => {}), false) #test((() => {}) == (() => {}), false)
// Templates compare by shallow equality. // Templates compare by some kind of equality.
#let t = [a] #let t = [a]
#test(t == t, true) #test(t == t, true)
#test([] == [], true) #test([] == [], true)
#test([a] == [a], true) #test([a] == [a], true)
#test([[a]] == [a], true)
#test([] == [a], false) #test([] == [a], false)
#test([[a]] == [a], false)
#test(box[] == box[], false) #test(box[] == box[], false)
--- ---