summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/eval/mod.rs25
-rw-r--r--src/eval/template.rs90
-rw-r--r--tests/typ/code/ops.typ4
3 files changed, 62 insertions, 57 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index fce8bed4..33ee23ca 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -183,17 +183,18 @@ fn eval_markup(
nodes: &mut impl Iterator<Item = MarkupNode>,
) -> TypResult<Template> {
let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default());
- let mut styles = StyleMap::new();
while let Some(node) = nodes.next() {
- let template = match node {
+ seq.push(match node {
MarkupNode::Expr(Expr::Set(set)) => {
let class = set.class();
let class = class.eval(ctx)?.cast::<Class>().at(class.span())?;
let mut args = set.args().eval(ctx)?;
+ let mut styles = StyleMap::new();
class.set(&mut args, &mut styles)?;
args.finish()?;
- continue;
+ let tail = eval_markup(ctx, nodes)?;
+ tail.styled_with_map(styles)
}
MarkupNode::Expr(Expr::Show(show)) => {
return Err("show rules are not yet implemented").at(show.span());
@@ -204,12 +205,14 @@ fn eval_markup(
wrap.body().eval(ctx)?.show()
}
_ => node.eval(ctx)?,
- };
-
- seq.push(Styled::new(template, styles.clone()));
+ });
}
- Ok(Template::Sequence(seq))
+ if seq.len() == 1 {
+ Ok(seq.into_iter().next().unwrap())
+ } else {
+ Ok(Template::Sequence(seq))
+ }
}
impl Eval for MarkupNode {
@@ -265,7 +268,7 @@ impl Eval for RawNode {
impl RawNode {
/// Styled template for a code block, with optional syntax highlighting.
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
.lang
@@ -294,7 +297,7 @@ impl RawNode {
let mut highlighter = HighlightLines::new(syntax, &THEME);
for (i, line) in self.text.lines().enumerate() {
if i != 0 {
- seq.push(Styled::bare(Template::Linebreak));
+ seq.push(Template::Linebreak);
}
for (style, piece) in highlighter.highlight(line, &SYNTAXES) {
@@ -322,7 +325,7 @@ impl RawNode {
}
/// 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 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()]);
}
- Styled::new(Template::Text(piece.into()), styles)
+ Template::Text(piece.into()).styled_with_map(styles)
}
impl Eval for MathNode {
diff --git a/src/eval/template.rs b/src/eval/template.rs
index 8a49f84d..c1a2b44c 100644
--- a/src/eval/template.rs
+++ b/src/eval/template.rs
@@ -20,9 +20,25 @@ use crate::util::EcoString;
/// - anything written between square brackets in Typst
/// - any class constructor
///
-/// When you write `[Hi] + [you]` in Typst, this type's [`Add`] implementation
-/// is invoked. There, multiple templates are combined into a single
-/// [`Sequence`](Self::Sequence) template.
+/// This enum has two notable variants:
+///
+/// 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)]
pub enum Template {
/// A word space.
@@ -45,21 +61,10 @@ pub enum Template {
Block(PackedNode),
/// A page node.
Page(PageNode),
- /// Multiple nodes with attached styles.
- ///
- /// For example, the Typst template `[Hi *you!*]` would result in the
- /// sequence:
- /// - `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>>),
+ /// A template with attached styles.
+ Styled(Box<Self>, StyleMap),
+ /// A sequence of multiple subtemplates.
+ Sequence(Vec<Self>),
}
impl Template {
@@ -86,30 +91,24 @@ impl Template {
/// Style this template with a single property.
pub fn styled<P: Property>(mut self, key: P, value: P::Value) -> Self {
- if let Self::Sequence(vec) = &mut self {
- if let [styled] = vec.as_mut_slice() {
- styled.map.set(key, value);
- return self;
- }
+ if let Self::Styled(_, map) = &mut self {
+ map.set(key, value);
+ 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.
pub fn styled_with_map(mut self, styles: StyleMap) -> Self {
if styles.is_empty() {
- return self;
- }
-
- if let Self::Sequence(vec) = &mut self {
- if let [styled] = vec.as_mut_slice() {
- styled.map.apply(&styles);
- return self;
- }
+ self
+ } else if let Self::Styled(_, map) = &mut self {
+ map.apply(&styles);
+ self
+ } else {
+ Self::Styled(Box::new(self), styles)
}
-
- Self::Sequence(vec![Styled::new(self, styles)])
}
/// Style this template in monospace.
@@ -140,7 +139,7 @@ impl Template {
let count = usize::try_from(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
}
(Self::Sequence(mut left), right) => {
- left.push(Styled::bare(right));
+ left.push(right);
left
}
(left, Self::Sequence(mut right)) => {
- right.insert(0, Styled::bare(left));
+ right.insert(0, 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 {
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));
}
}
- 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
// templates individually.
- for Styled { item, mut map } in list {
- map.apply(&styles);
- self.walk(item, map);
+ for item in seq {
+ self.walk(item, styles.clone());
}
}
}
diff --git a/tests/typ/code/ops.typ b/tests/typ/code/ops.typ
index e3e2e855..0ad35a27 100644
--- a/tests/typ/code/ops.typ
+++ b/tests/typ/code/ops.typ
@@ -130,13 +130,13 @@
#test(test == test, true)
#test((() => {}) == (() => {}), false)
-// Templates compare by shallow equality.
+// Templates compare by some kind of equality.
#let t = [a]
#test(t == t, true)
#test([] == [], true)
#test([a] == [a], true)
+#test([[a]] == [a], true)
#test([] == [a], false)
-#test([[a]] == [a], false)
#test(box[] == box[], false)
---