diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-04-24 15:42:56 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-04-24 15:47:42 +0200 |
| commit | 8fbb11fc05b3313bf102c1f23693290661d00863 (patch) | |
| tree | a198db77338f1cc1304fe50f55020b08e22bca60 /src/eval/content.rs | |
| parent | e4ee14e54fb87961096856c7ea105435b7cc3c45 (diff) | |
Extract `model` module
Diffstat (limited to 'src/eval/content.rs')
| -rw-r--r-- | src/eval/content.rs | 604 |
1 files changed, 0 insertions, 604 deletions
diff --git a/src/eval/content.rs b/src/eval/content.rs deleted file mode 100644 index 2465f0e3..00000000 --- a/src/eval/content.rs +++ /dev/null @@ -1,604 +0,0 @@ -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, Key, Layout, LayoutNode, Show, ShowNode, StyleMap, - StyleVecBuilder, -}; -use crate::diag::StrResult; -use crate::library::layout::{FlowChild, FlowNode, PageNode, PlaceNode, Spacing}; -use crate::library::prelude::*; -use crate::library::structure::{ListItem, ListKind, ListNode, ORDERED, UNORDERED}; -use crate::library::text::{DecoNode, ParChild, ParNode, UNDERLINE}; -use crate::util::EcoString; - -/// Composable representation of styled content. -/// -/// This results from: -/// - anything written between square brackets in Typst -/// - any node constructor -/// -/// Content is represented as a tree of nodes. There are two nodes of special -/// interest: -/// -/// 1. A `Styled` node attaches a style map to other content. For example, a -/// single bold word could be represented as a `Styled(Text("Hello"), -/// [TextNode::STRONG: true])` node. -/// -/// 2. A `Sequence` node content combines other arbitrary content and is the -/// representation of a "flow" of other nodes. So, when you write `[Hi] + -/// [you]` in Typst, this type's [`Add`] implementation is invoked and the -/// two [`Text`](Self::Text) nodes are combined into a single -/// [`Sequence`](Self::Sequence) node. A sequence may contain nested -/// sequences. -#[derive(PartialEq, Clone, Hash)] -pub enum Content { - /// A word space. - Space, - /// A forced line break. If soft (`true`), the preceding line can still be - /// justified, if hard (`false`) not. - Linebreak(bool), - /// Horizontal spacing. - Horizontal(Spacing), - /// Plain text. - Text(EcoString), - /// A smart quote, may be single (`false`) or double (`true`). - Quote(bool), - /// An inline-level node. - Inline(LayoutNode), - /// A paragraph break. - Parbreak, - /// A column break. - Colbreak, - /// Vertical spacing. - Vertical(Spacing), - /// 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), - /// Content with attached styles. - Styled(Arc<(Self, StyleMap)>), - /// A sequence of multiple nodes. - Sequence(Arc<Vec<Self>>), -} - -impl Content { - /// Create empty content. - pub fn new() -> Self { - Self::sequence(vec![]) - } - - /// Create content from an inline-level node. - pub fn inline<T>(node: T) -> Self - where - T: Layout + Debug + Hash + Sync + Send + 'static, - { - Self::Inline(node.pack()) - } - - /// Create content from a block-level node. - pub fn block<T>(node: T) -> Self - where - T: Layout + Debug + Hash + Sync + Send + 'static, - { - Self::Block(node.pack()) - } - - /// Create content from a showable node. - pub fn show<T>(node: T) -> Self - where - T: Show + Debug + Hash + Sync + Send + 'static, - { - 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. - pub fn styled<'k, K: Key<'k>>(mut self, key: K, value: K::Value) -> Self { - if let Self::Styled(styled) = &mut self { - if let Some((_, map)) = Arc::get_mut(styled) { - map.apply(key, value); - return self; - } - } - - Self::Styled(Arc::new((self, StyleMap::with(key, value)))) - } - - /// Style this content 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) { - map.apply_map(&styles); - return self; - } - } - - Self::Styled(Arc::new((self, styles))) - } - - /// Underline this content. - pub fn underlined(self) -> Self { - Self::show(DecoNode::<UNDERLINE>(self)) - } - - /// Return a node that is spaced apart at top and bottom. - pub fn spaced(self, above: Length, below: Length) -> Self { - if above.is_zero() && below.is_zero() { - return self; - } - - let mut seq = vec![]; - if !above.is_zero() { - seq.push(Content::Vertical(above.into())); - } - - seq.push(self); - - if !below.is_zero() { - seq.push(Content::Vertical(below.into())); - } - - Self::sequence(seq) - } - - /// Layout this content into a collection of pages. - pub fn layout(&self, ctx: &mut Context) -> TypResult<Vec<Arc<Frame>>> { - let sya = Arena::new(); - let tpa = Arena::new(); - - let styles = ctx.styles.clone(); - let styles = StyleChain::with_root(&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 Layout for Content { - fn layout( - &self, - ctx: &mut Context, - regions: &Regions, - styles: StyleChain, - ) -> TypResult<Vec<Arc<Frame>>> { - 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 { - Content::Block(node) => node, - other => LayoutNode::new(other), - } - } -} - -impl Default for Content { - fn default() -> Self { - Self::new() - } -} - -impl Debug for Content { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::Space => f.pad("Space"), - Self::Linebreak(soft) => write!(f, "Linebreak({soft})"), - Self::Horizontal(kind) => write!(f, "Horizontal({kind:?})"), - Self::Text(text) => write!(f, "Text({text:?})"), - Self::Quote(double) => write!(f, "Quote({double})"), - Self::Inline(node) => { - f.write_str("Inline(")?; - node.fmt(f)?; - f.write_str(")") - } - Self::Parbreak => f.pad("Parbreak"), - Self::Colbreak => f.pad("Colbreak"), - Self::Vertical(kind) => write!(f, "Vertical({kind:?})"), - Self::Block(node) => { - f.write_str("Block(")?; - node.fmt(f)?; - f.write_str(")") - } - Self::List(item) => { - f.write_str("- ")?; - item.body.fmt(f) - } - Self::Enum(item) => { - if let Some(number) = item.number { - write!(f, "{}", number)?; - } - f.write_str(". ")?; - item.body.fmt(f) - } - Self::Pagebreak => f.pad("Pagebreak"), - Self::Page(page) => page.fmt(f), - Self::Show(node) => { - f.write_str("Show(")?; - node.fmt(f)?; - f.write_str(")") - } - Self::Styled(styled) => { - let (sub, map) = styled.as_ref(); - map.fmt(f)?; - sub.fmt(f) - } - Self::Sequence(seq) => f.debug_list().entries(seq.iter()).finish(), - } - } -} - -impl Add for Content { - 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 Content { - fn add_assign(&mut self, rhs: Self) { - *self = std::mem::take(self) + rhs; - } -} - -impl Sum for Content { - fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { - Self::sequence(iter.collect()) - } -} - -/// Builds a flow or page nodes from content. -struct Builder<'a> { - /// An arena where intermediate style chains are stored. - sya: &'a Arena<StyleChain<'a>>, - /// An arena where intermediate content resulting from show rules is stored. - tpa: &'a Arena<Content>, - /// The already built page runs. - pages: Option<StyleVecBuilder<'a, PageNode>>, - /// The currently built list. - list: Option<ListBuilder<'a>>, - /// The currently built flow. - flow: CollapsingBuilder<'a, FlowChild>, - /// The currently built paragraph. - par: CollapsingBuilder<'a, ParChild>, - /// Whether to keep the next page even if it is empty. - keep_next: bool, -} - -/// Builds an unordered or ordered list from items. -struct ListBuilder<'a> { - styles: StyleChain<'a>, - kind: ListKind, - items: Vec<ListItem>, - tight: bool, - staged: Vec<(&'a Content, StyleChain<'a>)>, -} - -impl<'a> Builder<'a> { - /// Prepare the builder. - fn new(sya: &'a Arena<StyleChain<'a>>, tpa: &'a Arena<Content>, top: bool) -> Self { - Self { - sya, - tpa, - pages: top.then(|| StyleVecBuilder::new()), - flow: CollapsingBuilder::new(), - list: None, - par: CollapsingBuilder::new(), - keep_next: true, - } - } - - /// Process content. - fn process( - &mut self, - ctx: &mut Context, - content: &'a Content, - styles: StyleChain<'a>, - ) -> TypResult<()> { - if let Some(builder) = &mut self.list { - match content { - Content::Space => { - builder.staged.push((content, styles)); - return Ok(()); - } - Content::Parbreak => { - builder.staged.push((content, styles)); - return Ok(()); - } - Content::List(item) if builder.kind == UNORDERED => { - builder.tight &= - builder.staged.iter().all(|&(t, _)| *t != Content::Parbreak); - builder.staged.clear(); - builder.items.push(item.clone()); - return Ok(()); - } - Content::Enum(item) if builder.kind == ORDERED => { - builder.tight &= - builder.staged.iter().all(|&(t, _)| *t != Content::Parbreak); - builder.staged.clear(); - builder.items.push(item.clone()); - return Ok(()); - } - _ => self.finish_list(ctx)?, - } - } - - match content { - Content::Space => { - self.par.weak(ParChild::Text(' '.into()), 0, styles); - } - Content::Linebreak(soft) => { - let c = if *soft { '\u{2028}' } else { '\n' }; - self.par.destructive(ParChild::Text(c.into()), styles); - } - Content::Horizontal(kind) => { - let child = ParChild::Spacing(*kind); - if kind.is_fractional() { - self.par.destructive(child, styles); - } else { - self.par.ignorant(child, styles); - } - } - Content::Quote(double) => { - self.par.supportive(ParChild::Quote(*double), styles); - } - Content::Text(text) => { - self.par.supportive(ParChild::Text(text.clone()), styles); - } - Content::Inline(node) => { - self.par.supportive(ParChild::Node(node.clone()), styles); - } - Content::Parbreak => { - self.finish_par(styles); - self.flow.weak(FlowChild::Parbreak, 1, styles); - } - Content::Colbreak => { - self.finish_par(styles); - self.flow.destructive(FlowChild::Colbreak, styles); - } - Content::Vertical(kind) => { - self.finish_par(styles); - let child = FlowChild::Spacing(*kind); - if kind.is_fractional() { - self.flow.destructive(child, styles); - } else { - self.flow.ignorant(child, styles); - } - } - Content::Block(node) => { - self.finish_par(styles); - let child = FlowChild::Node(node.clone()); - if node.is::<PlaceNode>() { - self.flow.ignorant(child, styles); - } else { - self.flow.supportive(child, styles); - } - self.finish_par(styles); - } - Content::List(item) => { - self.list = Some(ListBuilder { - styles, - kind: UNORDERED, - items: vec![item.clone()], - tight: true, - staged: vec![], - }); - } - Content::Enum(item) => { - self.list = Some(ListBuilder { - styles, - kind: ORDERED, - items: vec![item.clone()], - tight: true, - staged: vec![], - }); - } - Content::Pagebreak => { - self.finish_page(ctx, true, true, styles)?; - } - Content::Page(page) => { - self.finish_page(ctx, false, false, styles)?; - if let Some(pages) = &mut self.pages { - pages.push(page.clone(), styles); - } - } - Content::Show(node) => { - let id = node.id(); - let realized = match styles.realize(ctx, node)? { - Some(content) => content, - None => node.realize(ctx, styles)?, - }; - let content = node.finalize(ctx, styles, realized)?; - let stored = self.tpa.alloc(content); - self.process(ctx, stored, styles.unscoped(id))?; - } - Content::Styled(styled) => { - let (sub, map) = styled.as_ref(); - let stored = self.sya.alloc(styles); - let styles = map.chain(stored); - - let interruption = map.interruption(); - match interruption { - Some(Interruption::Page) => { - self.finish_page(ctx, false, true, styles)? - } - Some(Interruption::Par) => self.finish_par(styles), - None => {} - } - - self.process(ctx, sub, styles)?; - - match interruption { - Some(Interruption::Page) => { - self.finish_page(ctx, true, false, styles)? - } - Some(Interruption::Par) => self.finish_par(styles), - None => {} - } - } - Content::Sequence(seq) => { - for sub in seq.iter() { - self.process(ctx, sub, styles)?; - } - } - } - - Ok(()) - } - - /// Finish the currently built paragraph. - fn finish_par(&mut self, styles: StyleChain<'a>) { - let (mut par, shared) = std::mem::take(&mut self.par).finish(); - if !par.is_empty() { - // Paragraph indent should only apply if the paragraph starts with - // text and follows directly after another paragraph. - let indent = shared.get(ParNode::INDENT); - if !indent.is_zero() - && par - .items() - .find_map(|child| match child { - ParChild::Spacing(_) => None, - ParChild::Text(_) | ParChild::Quote(_) => Some(true), - ParChild::Node(_) => Some(false), - }) - .unwrap_or_default() - && self - .flow - .items() - .rev() - .find_map(|child| match child { - FlowChild::Leading => None, - FlowChild::Parbreak => None, - FlowChild::Node(node) => Some(node.is::<ParNode>()), - FlowChild::Spacing(_) => Some(false), - FlowChild::Colbreak => Some(false), - }) - .unwrap_or_default() - { - par.push_front(ParChild::Spacing(indent.into())); - } - - let node = ParNode(par).pack(); - self.flow.supportive(FlowChild::Node(node), shared); - } - self.flow.weak(FlowChild::Leading, 0, styles); - } - - /// Finish the currently built list. - fn finish_list(&mut self, ctx: &mut Context) -> TypResult<()> { - let ListBuilder { styles, kind, items, tight, staged } = match self.list.take() { - Some(list) => list, - None => return Ok(()), - }; - - let content = match kind { - UNORDERED => Content::show(ListNode::<UNORDERED> { start: 1, tight, items }), - ORDERED | _ => Content::show(ListNode::<ORDERED> { start: 1, tight, items }), - }; - - let stored = self.tpa.alloc(content); - self.process(ctx, stored, styles)?; - for (content, styles) in staged { - self.process(ctx, content, styles)?; - } - - Ok(()) - } - - /// Finish the currently built page run. - fn finish_page( - &mut self, - ctx: &mut Context, - keep_last: bool, - keep_next: bool, - styles: StyleChain<'a>, - ) -> TypResult<()> { - self.finish_list(ctx)?; - self.finish_par(styles); - if let Some(pages) = &mut self.pages { - let (flow, shared) = std::mem::take(&mut self.flow).finish(); - if !flow.is_empty() || (keep_last && self.keep_next) { - let styles = if flow.is_empty() { styles } else { shared }; - let node = PageNode(FlowNode(flow).pack()); - pages.push(node, styles); - } - } - self.keep_next = keep_next; - Ok(()) - } - - /// Finish everything. - fn finish(&mut self, ctx: &mut Context, styles: StyleChain<'a>) -> TypResult<()> { - self.finish_page(ctx, true, false, styles) - } -} |
