use std::fmt::Debug; use std::hash::Hash; use std::iter::Sum; use std::ops::{Add, AddAssign}; use typed_arena::Arena; use super::{ CollapsingBuilder, Interruption, Layout, LayoutNode, Property, Show, ShowNode, StyleMap, StyleVecBuilder, }; use crate::diag::StrResult; use crate::library::prelude::*; use crate::library::{ DecoNode, FlowChild, FlowNode, ListItem, ListKind, ListNode, PageNode, ParChild, ParNode, PlaceNode, SpacingKind, TextNode, ORDERED, UNDERLINE, UNORDERED, }; use crate::util::EcoString; /// Composable representation of styled content. /// /// This results from: /// - anything written between square brackets in Typst /// - any class constructor /// /// 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 combines multiple arbitrary templates and is the /// representation of a "flow" of content. So, when you write `[Hi] + [you]` /// in Typst, this type's [`Add`] implementation is invoked and the two /// [`Text`](Self::Text) templates are combined into a single /// [`Sequence`](Self::Sequence) template. /// /// A sequence may contain nested sequences (meaning this variant effectively /// allows templates 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. Also, in theory, /// this allows better complexity when adding large sequence nodes just like /// for something like a text rope. #[derive(PartialEq, Clone, Hash)] pub enum Template { /// A word space. Space, /// A line break. Linebreak, /// Horizontal spacing. Horizontal(SpacingKind), /// Plain text. Text(EcoString), /// An inline-level node. Inline(LayoutNode), /// A paragraph break. Parbreak, /// A column break. Colbreak, /// Vertical spacing. Vertical(SpacingKind), /// A block-level node. Block(LayoutNode), /// An item in an unordered list. List(ListItem), /// An item in an ordered list. Enum(ListItem), /// A page break. Pagebreak, /// A page node. Page(PageNode), /// A node that can be realized with styles. Show(ShowNode), /// A template with attached styles. Styled(Arc<(Self, StyleMap)>), /// A sequence of multiple subtemplates. Sequence(Arc>), } impl Template { /// Create an empty template. pub fn new() -> Self { Self::sequence(vec![]) } /// Create a template from an inline-level node. pub fn inline(node: T) -> Self where T: Layout + Debug + Hash + Sync + Send + 'static, { Self::Inline(node.pack()) } /// Create a template from a block-level node. pub fn block(node: T) -> Self where T: Layout + Debug + Hash + Sync + Send + 'static, { Self::Block(node.pack()) } /// Create a template from a showable node. pub fn show(node: T) -> Self where T: Show + Debug + Hash + Sync + Send + 'static, { Self::Show(node.pack()) } /// Style this template with a single property. pub fn styled(mut self, key: P, value: P::Value) -> Self { if let Self::Styled(styled) = &mut self { if let Some((_, map)) = Arc::get_mut(styled) { if !map.has_scoped() { map.set(key, value); } return self; } } 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::Styled(styled) = &mut self { if let Some((_, map)) = Arc::get_mut(styled) { if !styles.has_scoped() && !map.has_scoped() { map.apply(&styles); return self; } } } Self::Styled(Arc::new((self, styles))) } /// Style this template in monospace. pub fn monospaced(self) -> Self { self.styled(TextNode::MONOSPACED, true) } /// Underline this template. pub fn underlined(self) -> Self { Self::show(DecoNode::(self)) } /// Create a new sequence template. pub fn sequence(seq: Vec) -> Self { if seq.len() == 1 { seq.into_iter().next().unwrap() } else { Self::Sequence(Arc::new(seq)) } } /// Repeat this template `n` times. pub fn repeat(&self, n: i64) -> StrResult { let count = usize::try_from(n) .map_err(|_| format!("cannot repeat this template {} times", n))?; Ok(Self::sequence(vec![self.clone(); count])) } /// Layout this template into a collection of pages. pub fn layout(&self, ctx: &mut Context) -> TypResult>> { let sya = Arena::new(); let tpa = Arena::new(); let styles = ctx.styles.clone(); let styles = StyleChain::new(&styles); let mut builder = Builder::new(&sya, &tpa, true); builder.process(ctx, self, styles)?; builder.finish(ctx, styles)?; let mut frames = vec![]; let (pages, shared) = builder.pages.unwrap().finish(); for (page, map) in pages.iter() { let number = 1 + frames.len(); frames.extend(page.layout(ctx, number, map.chain(&shared))?); } Ok(frames) } } impl Default for Template { fn default() -> Self { Self::new() } } impl Add for Template { type Output = Self; fn add(self, rhs: Self) -> Self::Output { Self::Sequence(match (self, rhs) { (Self::Sequence(mut lhs), Self::Sequence(rhs)) => { let mutable = Arc::make_mut(&mut lhs); match Arc::try_unwrap(rhs) { Ok(vec) => mutable.extend(vec), Err(rc) => mutable.extend(rc.iter().cloned()), } lhs } (Self::Sequence(mut lhs), rhs) => { Arc::make_mut(&mut lhs).push(rhs); lhs } (lhs, Self::Sequence(mut rhs)) => { Arc::make_mut(&mut rhs).insert(0, lhs); rhs } (lhs, rhs) => Arc::new(vec![lhs, rhs]), }) } } impl AddAssign for Template { fn add_assign(&mut self, rhs: Self) { *self = std::mem::take(self) + rhs; } } impl Sum for Template { fn sum>(iter: I) -> Self { Self::sequence(iter.collect()) } } impl Layout for Template { fn layout( &self, ctx: &mut Context, regions: &Regions, styles: StyleChain, ) -> TypResult>> { let sya = Arena::new(); let tpa = Arena::new(); let mut builder = Builder::new(&sya, &tpa, false); builder.process(ctx, self, styles)?; builder.finish(ctx, styles)?; let (flow, shared) = builder.flow.finish(); FlowNode(flow).layout(ctx, regions, shared) } fn pack(self) -> LayoutNode { match self { Template::Block(node) => node, other => LayoutNode::new(other), } } } /// Builds a flow or page nodes from a template. struct Builder<'a> { /// An arena where intermediate style chains are stored. sya: &'a Arena>, /// An arena where intermediate templates are stored. tpa: &'a Arena