summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-04-24 15:42:56 +0200
committerLaurenz <laurmaedje@gmail.com>2022-04-24 15:47:42 +0200
commit8fbb11fc05b3313bf102c1f23693290661d00863 (patch)
treea198db77338f1cc1304fe50f55020b08e22bca60 /src/eval
parente4ee14e54fb87961096856c7ea105435b7cc3c45 (diff)
Extract `model` module
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/collapse.rs100
-rw-r--r--src/eval/content.rs604
-rw-r--r--src/eval/func.rs3
-rw-r--r--src/eval/layout.rs382
-rw-r--r--src/eval/mod.rs13
-rw-r--r--src/eval/module.rs3
-rw-r--r--src/eval/ops.rs9
-rw-r--r--src/eval/raw.rs3
-rw-r--r--src/eval/show.rs111
-rw-r--r--src/eval/styles.rs829
-rw-r--r--src/eval/value.rs3
11 files changed, 15 insertions, 2045 deletions
diff --git a/src/eval/collapse.rs b/src/eval/collapse.rs
deleted file mode 100644
index 5e7a25f8..00000000
--- a/src/eval/collapse.rs
+++ /dev/null
@@ -1,100 +0,0 @@
-use super::{StyleChain, StyleVec, StyleVecBuilder};
-
-/// A wrapper around a [`StyleVecBuilder`] that allows to collapse items.
-pub struct CollapsingBuilder<'a, T> {
- builder: StyleVecBuilder<'a, T>,
- staged: Vec<(T, StyleChain<'a>, Option<u8>)>,
- last: Last,
-}
-
-/// What the last non-ignorant item was.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-enum Last {
- Weak,
- Destructive,
- Supportive,
-}
-
-impl<'a, T> CollapsingBuilder<'a, T> {
- /// Create a new style-vec builder.
- pub fn new() -> Self {
- Self {
- builder: StyleVecBuilder::new(),
- staged: vec![],
- last: Last::Destructive,
- }
- }
-
- /// Can only exist when there is at least one supportive item to its left
- /// and to its right, with no destructive items or weak items in between to
- /// its left and no destructive items in between to its right. There may be
- /// ignorant items in between in both directions.
- pub fn weak(&mut self, item: T, strength: u8, styles: StyleChain<'a>) {
- if self.last != Last::Destructive {
- if self.last == Last::Weak {
- if let Some(i) = self
- .staged
- .iter()
- .position(|(.., prev)| prev.map_or(false, |p| p < strength))
- {
- self.staged.remove(i);
- } else {
- return;
- }
- }
-
- self.staged.push((item, styles, Some(strength)));
- self.last = Last::Weak;
- }
- }
-
- /// Forces nearby weak items to collapse.
- pub fn destructive(&mut self, item: T, styles: StyleChain<'a>) {
- self.flush(false);
- self.push(item, styles);
- self.last = Last::Destructive;
- }
-
- /// Allows nearby weak items to exist.
- pub fn supportive(&mut self, item: T, styles: StyleChain<'a>) {
- self.flush(true);
- self.push(item, styles);
- self.last = Last::Supportive;
- }
-
- /// Has no influence on other items.
- pub fn ignorant(&mut self, item: T, styles: StyleChain<'a>) {
- self.staged.push((item, styles, None));
- }
-
- /// Iterate over the contained items.
- pub fn items(&self) -> impl DoubleEndedIterator<Item = &T> {
- self.builder.items().chain(self.staged.iter().map(|(item, ..)| item))
- }
-
- /// Return the finish style vec and the common prefix chain.
- pub fn finish(mut self) -> (StyleVec<T>, StyleChain<'a>) {
- self.flush(false);
- self.builder.finish()
- }
-
- /// Push the staged items, filtering out weak items if `supportive` is false.
- fn flush(&mut self, supportive: bool) {
- for (item, styles, strength) in self.staged.drain(..) {
- if supportive || strength.is_none() {
- self.builder.push(item, styles);
- }
- }
- }
-
- /// Push a new item into the style vector.
- fn push(&mut self, item: T, styles: StyleChain<'a>) {
- self.builder.push(item, styles);
- }
-}
-
-impl<'a, T> Default for CollapsingBuilder<'a, T> {
- fn default() -> Self {
- Self::new()
- }
-}
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)
- }
-}
diff --git a/src/eval/func.rs b/src/eval/func.rs
index fcd19326..b2ecfe93 100644
--- a/src/eval/func.rs
+++ b/src/eval/func.rs
@@ -2,8 +2,9 @@ use std::fmt::{self, Debug, Formatter, Write};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
-use super::{Args, Content, Control, Eval, Scope, Scopes, StyleMap, Value};
+use super::{Args, Control, Eval, Scope, Scopes, Value};
use crate::diag::{StrResult, TypResult};
+use crate::model::{Content, StyleMap};
use crate::syntax::ast::Expr;
use crate::syntax::Span;
use crate::util::EcoString;
diff --git a/src/eval/layout.rs b/src/eval/layout.rs
deleted file mode 100644
index 117c269a..00000000
--- a/src/eval/layout.rs
+++ /dev/null
@@ -1,382 +0,0 @@
-//! Layouting infrastructure.
-
-use std::any::Any;
-use std::fmt::{self, Debug, Formatter};
-use std::hash::Hash;
-use std::sync::Arc;
-
-use super::{Barrier, RawAlign, RawLength, Resolve, StyleChain};
-use crate::diag::TypResult;
-use crate::frame::{Element, Frame, Geometry};
-use crate::geom::{Align, Length, Paint, Point, Relative, Sides, Size, Spec, Stroke};
-use crate::library::graphics::MoveNode;
-use crate::library::layout::{AlignNode, PadNode};
-use crate::util::Prehashed;
-use crate::Context;
-
-/// A node that can be layouted into a sequence of regions.
-///
-/// Layout return one frame per used region alongside constraints that define
-/// whether the result is reusable in other regions.
-pub trait Layout: 'static {
- /// Layout this node into the given regions, producing constrained frames.
- fn layout(
- &self,
- ctx: &mut Context,
- regions: &Regions,
- styles: StyleChain,
- ) -> TypResult<Vec<Arc<Frame>>>;
-
- /// Convert to a packed node.
- fn pack(self) -> LayoutNode
- where
- Self: Debug + Hash + Sized + Sync + Send + 'static,
- {
- LayoutNode::new(self)
- }
-}
-
-/// A sequence of regions to layout into.
-#[derive(Debug, Clone, Hash)]
-pub struct Regions {
- /// The (remaining) size of the first region.
- pub first: Size,
- /// The base size for relative sizing.
- pub base: Size,
- /// The height of followup regions. The width is the same for all regions.
- pub backlog: Vec<Length>,
- /// The height of the final region that is repeated once the backlog is
- /// drained. The width is the same for all regions.
- pub last: Option<Length>,
- /// Whether nodes should expand to fill the regions instead of shrinking to
- /// fit the content.
- pub expand: Spec<bool>,
-}
-
-impl Regions {
- /// Create a new region sequence with exactly one region.
- pub fn one(size: Size, base: Size, expand: Spec<bool>) -> Self {
- Self {
- first: size,
- base,
- backlog: vec![],
- last: None,
- expand,
- }
- }
-
- /// Create a new sequence of same-size regions that repeats indefinitely.
- pub fn repeat(size: Size, base: Size, expand: Spec<bool>) -> Self {
- Self {
- first: size,
- base,
- backlog: vec![],
- last: Some(size.y),
- expand,
- }
- }
-
- /// Create new regions where all sizes are mapped with `f`.
- ///
- /// Note that since all regions must have the same width, the width returned
- /// by `f` is ignored for the backlog and the final region.
- pub fn map<F>(&self, mut f: F) -> Self
- where
- F: FnMut(Size) -> Size,
- {
- let x = self.first.x;
- Self {
- first: f(self.first),
- base: f(self.base),
- backlog: self.backlog.iter().map(|&y| f(Size::new(x, y)).y).collect(),
- last: self.last.map(|y| f(Size::new(x, y)).y),
- expand: self.expand,
- }
- }
-
- /// Whether the first region is full and a region break is called for.
- pub fn is_full(&self) -> bool {
- Length::zero().fits(self.first.y) && !self.in_last()
- }
-
- /// Whether the first region is the last usable region.
- ///
- /// If this is true, calling `next()` will have no effect.
- pub fn in_last(&self) -> bool {
- self.backlog.len() == 0 && self.last.map_or(true, |height| self.first.y == height)
- }
-
- /// Advance to the next region if there is any.
- pub fn next(&mut self) {
- if let Some(height) = (!self.backlog.is_empty())
- .then(|| self.backlog.remove(0))
- .or(self.last)
- {
- self.first.y = height;
- self.base.y = height;
- }
- }
-
- /// An iterator that returns the sizes of the first and all following
- /// regions, equivalently to what would be produced by calling
- /// [`next()`](Self::next) repeatedly until all regions are exhausted.
- /// This iterater may be infinite.
- pub fn iter(&self) -> impl Iterator<Item = Size> + '_ {
- let first = std::iter::once(self.first);
- let backlog = self.backlog.iter();
- let last = self.last.iter().cycle();
- first.chain(backlog.chain(last).map(|&h| Size::new(self.first.x, h)))
- }
-}
-
-/// A type-erased layouting node with a precomputed hash.
-#[derive(Clone, Hash)]
-pub struct LayoutNode(Arc<Prehashed<dyn Bounds>>);
-
-impl LayoutNode {
- /// Pack any layoutable node.
- pub fn new<T>(node: T) -> Self
- where
- T: Layout + Debug + Hash + Sync + Send + 'static,
- {
- Self(Arc::new(Prehashed::new(node)))
- }
-
- /// Check whether the contained node is a specific layout node.
- pub fn is<T: 'static>(&self) -> bool {
- (**self.0).as_any().is::<T>()
- }
-
- /// A barrier for the node.
- pub fn barrier(&self) -> Barrier {
- (**self.0).barrier()
- }
-
- /// Try to downcast to a specific layout node.
- pub fn downcast<T>(&self) -> Option<&T>
- where
- T: Layout + Debug + Hash + 'static,
- {
- (**self.0).as_any().downcast_ref()
- }
-
- /// Force a size for this node.
- pub fn sized(self, sizing: Spec<Option<Relative<RawLength>>>) -> Self {
- if sizing.any(Option::is_some) {
- SizedNode { sizing, child: self }.pack()
- } else {
- self
- }
- }
-
- /// Fill the frames resulting from a node.
- pub fn filled(self, fill: Paint) -> Self {
- FillNode { fill, child: self }.pack()
- }
-
- /// Stroke the frames resulting from a node.
- pub fn stroked(self, stroke: Stroke) -> Self {
- StrokeNode { stroke, child: self }.pack()
- }
-
- /// Set alignments for this node.
- pub fn aligned(self, aligns: Spec<Option<RawAlign>>) -> Self {
- if aligns.any(Option::is_some) {
- AlignNode { aligns, child: self }.pack()
- } else {
- self
- }
- }
-
- /// Pad this node at the sides.
- pub fn padded(self, padding: Sides<Relative<RawLength>>) -> Self {
- if !padding.left.is_zero()
- || !padding.top.is_zero()
- || !padding.right.is_zero()
- || !padding.bottom.is_zero()
- {
- PadNode { padding, child: self }.pack()
- } else {
- self
- }
- }
-
- /// Transform this node's contents without affecting layout.
- pub fn moved(self, delta: Spec<Relative<RawLength>>) -> Self {
- if delta.any(|r| !r.is_zero()) {
- MoveNode { delta, child: self }.pack()
- } else {
- self
- }
- }
-}
-
-impl Layout for LayoutNode {
- fn layout(
- &self,
- ctx: &mut Context,
- regions: &Regions,
- styles: StyleChain,
- ) -> TypResult<Vec<Arc<Frame>>> {
- ctx.query((self, regions, styles), |ctx, (node, regions, styles)| {
- node.0.layout(ctx, regions, node.barrier().chain(&styles))
- })
- .clone()
- }
-
- fn pack(self) -> LayoutNode {
- self
- }
-}
-
-impl Default for LayoutNode {
- fn default() -> Self {
- EmptyNode.pack()
- }
-}
-
-impl Debug for LayoutNode {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.0.fmt(f)
- }
-}
-
-impl PartialEq for LayoutNode {
- fn eq(&self, other: &Self) -> bool {
- self.0.eq(&other.0)
- }
-}
-
-trait Bounds: Layout + Debug + Sync + Send + 'static {
- fn as_any(&self) -> &dyn Any;
- fn barrier(&self) -> Barrier;
-}
-
-impl<T> Bounds for T
-where
- T: Layout + Debug + Hash + Sync + Send + 'static,
-{
- fn as_any(&self) -> &dyn Any {
- self
- }
-
- fn barrier(&self) -> Barrier {
- Barrier::new::<T>()
- }
-}
-
-/// A layout node that produces an empty frame.
-///
-/// The packed version of this is returned by [`PackedNode::default`].
-#[derive(Debug, Hash)]
-struct EmptyNode;
-
-impl Layout for EmptyNode {
- fn layout(
- &self,
- _: &mut Context,
- regions: &Regions,
- _: StyleChain,
- ) -> TypResult<Vec<Arc<Frame>>> {
- Ok(vec![Arc::new(Frame::new(
- regions.expand.select(regions.first, Size::zero()),
- ))])
- }
-}
-
-/// Fix the size of a node.
-#[derive(Debug, Hash)]
-struct SizedNode {
- /// How to size the node horizontally and vertically.
- sizing: Spec<Option<Relative<RawLength>>>,
- /// The node to be sized.
- child: LayoutNode,
-}
-
-impl Layout for SizedNode {
- fn layout(
- &self,
- ctx: &mut Context,
- regions: &Regions,
- styles: StyleChain,
- ) -> TypResult<Vec<Arc<Frame>>> {
- // The "pod" is the region into which the child will be layouted.
- let pod = {
- // Resolve the sizing to a concrete size.
- let size = self
- .sizing
- .resolve(styles)
- .zip(regions.base)
- .map(|(s, b)| s.map(|v| v.relative_to(b)))
- .unwrap_or(regions.first);
-
- // Select the appropriate base and expansion for the child depending
- // on whether it is automatically or relatively sized.
- let is_auto = self.sizing.map_is_none();
- let base = is_auto.select(regions.base, size);
- let expand = regions.expand | !is_auto;
-
- Regions::one(size, base, expand)
- };
-
- // Layout the child.
- let mut frames = self.child.layout(ctx, &pod, styles)?;
-
- // Ensure frame size matches regions size if expansion is on.
- let frame = &mut frames[0];
- let target = regions.expand.select(regions.first, frame.size);
- Arc::make_mut(frame).resize(target, Align::LEFT_TOP);
-
- Ok(frames)
- }
-}
-
-/// Fill the frames resulting from a node.
-#[derive(Debug, Hash)]
-struct FillNode {
- /// How to fill the frames resulting from the `child`.
- fill: Paint,
- /// The node to fill.
- child: LayoutNode,
-}
-
-impl Layout for FillNode {
- fn layout(
- &self,
- ctx: &mut Context,
- regions: &Regions,
- styles: StyleChain,
- ) -> TypResult<Vec<Arc<Frame>>> {
- let mut frames = self.child.layout(ctx, regions, styles)?;
- for frame in &mut frames {
- let shape = Geometry::Rect(frame.size).filled(self.fill);
- Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape));
- }
- Ok(frames)
- }
-}
-
-/// Stroke the frames resulting from a node.
-#[derive(Debug, Hash)]
-struct StrokeNode {
- /// How to stroke the frames resulting from the `child`.
- stroke: Stroke,
- /// The node to stroke.
- child: LayoutNode,
-}
-
-impl Layout for StrokeNode {
- fn layout(
- &self,
- ctx: &mut Context,
- regions: &Regions,
- styles: StyleChain,
- ) -> TypResult<Vec<Arc<Frame>>> {
- let mut frames = self.child.layout(ctx, regions, styles)?;
- for frame in &mut frames {
- let shape = Geometry::Rect(frame.size).stroked(self.stroke);
- Arc::make_mut(frame).prepend(Point::zero(), Element::Shape(shape));
- }
- Ok(frames)
- }
-}
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index f77efe47..2839baff 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -6,38 +6,28 @@ mod array;
mod dict;
#[macro_use]
mod value;
-#[macro_use]
-mod styles;
+
mod args;
mod capture;
-mod collapse;
-mod content;
mod control;
mod func;
-mod layout;
pub mod methods;
mod module;
pub mod ops;
mod raw;
mod scope;
-mod show;
mod str;
pub use self::str::*;
pub use args::*;
pub use array::*;
pub use capture::*;
-pub use collapse::*;
-pub use content::*;
pub use control::*;
pub use dict::*;
pub use func::*;
-pub use layout::*;
pub use module::*;
pub use raw::*;
pub use scope::*;
-pub use show::*;
-pub use styles::*;
pub use value::*;
use std::collections::BTreeMap;
@@ -48,6 +38,7 @@ use unicode_segmentation::UnicodeSegmentation;
use crate::diag::{At, StrResult, Trace, Tracepoint, TypResult};
use crate::geom::{Angle, Em, Fraction, Length, Ratio};
use crate::library;
+use crate::model::{Content, StyleMap};
use crate::syntax::ast::*;
use crate::syntax::{Span, Spanned};
use crate::util::EcoString;
diff --git a/src/eval/module.rs b/src/eval/module.rs
index 4460caec..d2b4c520 100644
--- a/src/eval/module.rs
+++ b/src/eval/module.rs
@@ -1,4 +1,5 @@
-use super::{Content, Scope};
+use super::Scope;
+use crate::model::Content;
use crate::source::{SourceId, SourceStore};
/// An evaluated module, ready for importing or layouting.
diff --git a/src/eval/ops.rs b/src/eval/ops.rs
index 70352039..832bd354 100644
--- a/src/eval/ops.rs
+++ b/src/eval/ops.rs
@@ -5,6 +5,7 @@ use std::cmp::Ordering;
use super::{Dynamic, RawAlign, RawStroke, Smart, StrExt, Value};
use crate::diag::StrResult;
use crate::geom::{Numeric, Spec, SpecAxis};
+use crate::model;
use Value::*;
/// Bail with a type mismatch error.
@@ -20,8 +21,8 @@ pub fn join(lhs: Value, rhs: Value) -> StrResult<Value> {
(a, None) => a,
(None, b) => b,
(Str(a), Str(b)) => Str(a + b),
- (Str(a), Content(b)) => Content(super::Content::Text(a) + b),
- (Content(a), Str(b)) => Content(a + super::Content::Text(b)),
+ (Str(a), Content(b)) => Content(model::Content::Text(a) + b),
+ (Content(a), Str(b)) => Content(a + model::Content::Text(b)),
(Content(a), Content(b)) => Content(a + b),
(Array(a), Array(b)) => Array(a + b),
(Dict(a), Dict(b)) => Dict(a + b),
@@ -86,8 +87,8 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult<Value> {
(Content(a), None) => Content(a),
(None, Content(b)) => Content(b),
(Content(a), Content(b)) => Content(a + b),
- (Content(a), Str(b)) => Content(a + super::Content::Text(b)),
- (Str(a), Content(b)) => Content(super::Content::Text(a) + b),
+ (Content(a), Str(b)) => Content(a + model::Content::Text(b)),
+ (Str(a), Content(b)) => Content(model::Content::Text(a) + b),
(Array(a), Array(b)) => Array(a + b),
(Dict(a), Dict(b)) => Dict(a + b),
diff --git a/src/eval/raw.rs b/src/eval/raw.rs
index 6792c491..2d82fca8 100644
--- a/src/eval/raw.rs
+++ b/src/eval/raw.rs
@@ -2,11 +2,12 @@ use std::cmp::Ordering;
use std::fmt::{self, Debug, Formatter};
use std::ops::{Add, Div, Mul, Neg};
-use super::{Fold, Resolve, Smart, StyleChain, Value};
+use super::{Smart, Value};
use crate::geom::{
Align, Em, Get, Length, Numeric, Paint, Relative, Spec, SpecAxis, Stroke,
};
use crate::library::text::TextNode;
+use crate::model::{Fold, Resolve, StyleChain};
/// The unresolved alignment representation.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
diff --git a/src/eval/show.rs b/src/eval/show.rs
deleted file mode 100644
index c374c2df..00000000
--- a/src/eval/show.rs
+++ /dev/null
@@ -1,111 +0,0 @@
-use std::any::{Any, TypeId};
-use std::fmt::{self, Debug, Formatter};
-use std::hash::Hash;
-use std::sync::Arc;
-
-use super::{Content, Dict, StyleChain};
-use crate::diag::TypResult;
-use crate::util::Prehashed;
-use crate::Context;
-
-/// A node that can be realized given some styles.
-pub trait Show: 'static {
- /// Encode this node into a dictionary.
- fn encode(&self) -> Dict;
-
- /// The base recipe for this node that is executed if there is no
- /// user-defined show rule.
- 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,
- ctx: &mut Context,
- styles: StyleChain,
- realized: Content,
- ) -> TypResult<Content> {
- Ok(realized)
- }
-
- /// Convert to a packed show node.
- fn pack(self) -> ShowNode
- where
- Self: Debug + Hash + Sized + Sync + Send + 'static,
- {
- ShowNode::new(self)
- }
-}
-
-/// A type-erased showable node with a precomputed hash.
-#[derive(Clone, Hash)]
-pub struct ShowNode(Arc<Prehashed<dyn Bounds>>);
-
-impl ShowNode {
- /// Pack any showable node.
- pub fn new<T>(node: T) -> Self
- where
- T: Show + Debug + Hash + Sync + Send + 'static,
- {
- Self(Arc::new(Prehashed::new(node)))
- }
-
- /// The type id of this node.
- pub fn id(&self) -> TypeId {
- self.0.as_any().type_id()
- }
-}
-
-impl Show for ShowNode {
- fn encode(&self) -> Dict {
- self.0.encode()
- }
-
- fn realize(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
- self.0.realize(ctx, styles)
- }
-
- fn finalize(
- &self,
- ctx: &mut Context,
- styles: StyleChain,
- realized: Content,
- ) -> TypResult<Content> {
- self.0.finalize(ctx, styles, realized)
- }
-
- fn pack(self) -> ShowNode {
- self
- }
-}
-
-impl Debug for ShowNode {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.0.fmt(f)
- }
-}
-
-impl PartialEq for ShowNode {
- fn eq(&self, other: &Self) -> bool {
- self.0.eq(&other.0)
- }
-}
-
-trait Bounds: Show + Debug + Sync + Send + 'static {
- fn as_any(&self) -> &dyn Any;
-}
-
-impl<T> Bounds for T
-where
- T: Show + Debug + Hash + Sync + Send + 'static,
-{
- fn as_any(&self) -> &dyn Any {
- self
- }
-}
diff --git a/src/eval/styles.rs b/src/eval/styles.rs
deleted file mode 100644
index b666c85d..00000000
--- a/src/eval/styles.rs
+++ /dev/null
@@ -1,829 +0,0 @@
-use std::any::{Any, TypeId};
-use std::fmt::{self, Debug, Formatter};
-use std::hash::Hash;
-use std::marker::PhantomData;
-use std::sync::Arc;
-
-use super::{Args, Content, Func, Layout, Node, Show, ShowNode, Smart, Span, Value};
-use crate::diag::{At, TypResult};
-use crate::geom::{Numeric, Relative, Sides, Spec};
-use crate::library::layout::PageNode;
-use crate::library::text::{FontFamily, ParNode, TextNode};
-use crate::util::Prehashed;
-use crate::Context;
-
-/// A map of style properties.
-#[derive(Default, Clone, PartialEq, Hash)]
-pub struct StyleMap(Vec<Entry>);
-
-impl StyleMap {
- /// Create a new, empty style map.
- pub fn new() -> Self {
- Self::default()
- }
-
- /// Whether this map contains no styles.
- pub fn is_empty(&self) -> bool {
- self.0.is_empty()
- }
-
- /// Create a style map from a single property-value pair.
- pub fn with<'a, K: Key<'a>>(key: K, value: K::Value) -> Self {
- let mut styles = Self::new();
- styles.set(key, value);
- styles
- }
-
- /// Set an inner value for a style property.
- ///
- /// If the property needs folding and the value is already contained in the
- /// style map, `self` contributes the outer values and `value` is the inner
- /// one.
- pub fn set<'a, K: Key<'a>>(&mut self, key: K, value: K::Value) {
- self.0.push(Entry::Property(Property::new(key, value)));
- }
-
- /// Set an inner value for a style property if it is `Some(_)`.
- pub fn set_opt<'a, K: Key<'a>>(&mut self, key: K, value: Option<K::Value>) {
- if let Some(value) = value {
- self.set(key, value);
- }
- }
-
- /// Set a font family composed of a preferred family and existing families
- /// from a style chain.
- pub fn set_family(&mut self, preferred: FontFamily, existing: StyleChain) {
- self.set(
- TextNode::FAMILY,
- std::iter::once(preferred)
- .chain(existing.get(TextNode::FAMILY).iter().cloned())
- .collect(),
- );
- }
-
- /// Set a show rule recipe for a node.
- pub fn set_recipe<T: Node>(&mut self, func: Func, span: Span) {
- self.0.push(Entry::Recipe(Recipe::new::<T>(func, span)));
- }
-
- /// Whether the map contains a style property for the given key.
- pub fn contains<'a, K: Key<'a>>(&self, _: K) -> bool {
- self.0
- .iter()
- .filter_map(|entry| entry.property())
- .any(|property| property.key == TypeId::of::<K>())
- }
-
- /// Make `self` the first link of the `tail` chain.
- ///
- /// The resulting style chain contains styles from `self` as well as
- /// `tail`. The ones from `self` take precedence over the ones from
- /// `tail`. For folded properties `self` contributes the inner value.
- pub fn chain<'a>(&'a self, tail: &'a StyleChain<'a>) -> StyleChain<'a> {
- if self.is_empty() {
- *tail
- } else {
- StyleChain { head: &self.0, tail: Some(tail) }
- }
- }
-
- /// Set an outer value for a style property.
- ///
- /// If the property needs folding and the value is already contained in the
- /// style map, `self` contributes the inner values and `value` is the outer
- /// one.
- ///
- /// Like [`chain`](Self::chain) or [`apply_map`](Self::apply_map), but with
- /// only a single property.
- pub fn apply<'a, K: Key<'a>>(&mut self, key: K, value: K::Value) {
- self.0.insert(0, Entry::Property(Property::new(key, value)));
- }
-
- /// Apply styles from `tail` in-place. The resulting style map is equivalent
- /// to the style chain created by `self.chain(StyleChain::new(tail))`.
- ///
- /// This is useful over `chain` when you want to combine two maps, but you
- /// still need an owned map without a lifetime.
- pub fn apply_map(&mut self, tail: &Self) {
- self.0.splice(0 .. 0, tail.0.iter().cloned());
- }
-
- /// Mark all contained properties as _scoped_. This means that they only
- /// apply to the first descendant node (of their type) in the hierarchy and
- /// not its children, too. This is used by [constructors](Node::construct).
- pub fn scoped(mut self) -> Self {
- for entry in &mut self.0 {
- if let Entry::Property(property) = entry {
- property.scoped = true;
- }
- }
- self
- }
-
- /// The highest-level kind of of structure the map interrupts.
- pub fn interruption(&self) -> Option<Interruption> {
- self.0
- .iter()
- .filter_map(|entry| entry.property())
- .filter_map(|property| property.interruption())
- .max()
- }
-}
-
-impl Debug for StyleMap {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- for entry in self.0.iter().rev() {
- writeln!(f, "{:?}", entry)?;
- }
- Ok(())
- }
-}
-
-/// An entry for a single style property, recipe or barrier.
-#[derive(Clone, PartialEq, Hash)]
-enum Entry {
- /// A style property originating from a set rule or constructor.
- Property(Property),
- /// A barrier for scoped styles.
- Barrier(TypeId, &'static str),
- /// A show rule recipe.
- Recipe(Recipe),
-}
-
-impl Entry {
- /// If this is a property, return it.
- fn property(&self) -> Option<&Property> {
- match self {
- Self::Property(property) => Some(property),
- _ => None,
- }
- }
-
- /// If this is a recipe, return it.
- fn recipe(&self) -> Option<&Recipe> {
- match self {
- Self::Recipe(recipe) => Some(recipe),
- _ => None,
- }
- }
-}
-
-impl Debug for Entry {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.write_str("#[")?;
- match self {
- Self::Property(property) => property.fmt(f)?,
- Self::Recipe(recipe) => recipe.fmt(f)?,
- Self::Barrier(_, name) => write!(f, "Barrier for {name}")?,
- }
- f.write_str("]")
- }
-}
-
-/// A style property originating from a set rule or constructor.
-#[derive(Clone, Hash)]
-struct Property {
- /// The type id of the property's [key](Key).
- key: TypeId,
- /// The type id of the node the property belongs to.
- node: TypeId,
- /// The name of the property.
- name: &'static str,
- /// The property's value.
- value: Arc<Prehashed<dyn Bounds>>,
- /// Whether the property should only affects the first node down the
- /// hierarchy. Used by constructors.
- scoped: bool,
-}
-
-impl Property {
- /// Create a new property from a key-value pair.
- fn new<'a, K: Key<'a>>(_: K, value: K::Value) -> Self {
- Self {
- key: TypeId::of::<K>(),
- node: K::node(),
- name: K::NAME,
- value: Arc::new(Prehashed::new(value)),
- scoped: false,
- }
- }
-
- /// What kind of structure the property interrupts.
- fn interruption(&self) -> Option<Interruption> {
- if self.is_of::<PageNode>() {
- Some(Interruption::Page)
- } else if self.is_of::<ParNode>() {
- Some(Interruption::Par)
- } else {
- None
- }
- }
-
- /// Access the property's value if it is of the given key.
- fn downcast<'a, K: Key<'a>>(&'a self) -> Option<&'a K::Value> {
- if self.key == TypeId::of::<K>() {
- (**self.value).as_any().downcast_ref()
- } else {
- None
- }
- }
-
- /// Whether this property belongs to the node `T`.
- fn is_of<T: Node>(&self) -> bool {
- self.node == TypeId::of::<T>()
- }
-}
-
-impl Debug for Property {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "{} = {:?}", self.name, self.value)?;
- if self.scoped {
- write!(f, " [scoped]")?;
- }
- Ok(())
- }
-}
-
-impl PartialEq for Property {
- fn eq(&self, other: &Self) -> bool {
- self.key == other.key
- && self.value.eq(&other.value)
- && self.scoped == other.scoped
- }
-}
-
-trait Bounds: Debug + Sync + Send + 'static {
- fn as_any(&self) -> &dyn Any;
-}
-
-impl<T> Bounds for T
-where
- T: Debug + Sync + Send + 'static,
-{
- fn as_any(&self) -> &dyn Any {
- self
- }
-}
-
-/// Style property keys.
-///
-/// This trait is not intended to be implemented manually, but rather through
-/// the `#[node]` proc-macro.
-pub trait Key<'a>: Copy + 'static {
- /// The unfolded type which this property is stored as in a style map. For
- /// example, this is [`Toggle`](crate::geom::Length) for the
- /// [`STRONG`](TextNode::STRONG) property.
- type Value: Debug + Clone + Hash + Sync + Send + 'static;
-
- /// The folded type of value that is returned when reading this property
- /// from a style chain. For example, this is [`bool`] for the
- /// [`STRONG`](TextNode::STRONG) property. For non-copy, non-folding
- /// properties this is a reference type.
- type Output;
-
- /// The name of the property, used for debug printing.
- const NAME: &'static str;
-
- /// The type id of the node this property belongs to.
- fn node() -> TypeId;
-
- /// Compute an output value from a sequence of values belong to this key,
- /// folding if necessary.
- fn get(
- chain: StyleChain<'a>,
- values: impl Iterator<Item = &'a Self::Value>,
- ) -> Self::Output;
-}
-
-/// A property that is resolved with other properties from the style chain.
-pub trait Resolve {
- /// The type of the resolved output.
- type Output;
-
- /// Resolve the value using the style chain.
- fn resolve(self, styles: StyleChain) -> Self::Output;
-}
-
-impl<T: Resolve> Resolve for Option<T> {
- type Output = Option<T::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|v| v.resolve(styles))
- }
-}
-
-impl<T: Resolve> Resolve for Smart<T> {
- type Output = Smart<T::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|v| v.resolve(styles))
- }
-}
-
-impl<T: Resolve> Resolve for Spec<T> {
- type Output = Spec<T::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|v| v.resolve(styles))
- }
-}
-
-impl<T: Resolve> Resolve for Sides<T> {
- type Output = Sides<T::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- Sides {
- left: self.left.resolve(styles),
- right: self.right.resolve(styles),
- top: self.top.resolve(styles),
- bottom: self.bottom.resolve(styles),
- }
- }
-}
-
-impl<T> Resolve for Relative<T>
-where
- T: Resolve + Numeric,
- <T as Resolve>::Output: Numeric,
-{
- type Output = Relative<<T as Resolve>::Output>;
-
- fn resolve(self, styles: StyleChain) -> Self::Output {
- self.map(|abs| abs.resolve(styles))
- }
-}
-
-/// A property that is folded to determine its final value.
-pub trait Fold {
- /// The type of the folded output.
- type Output;
-
- /// Fold this inner value with an outer folded value.
- fn fold(self, outer: Self::Output) -> Self::Output;
-}
-
-impl<T> Fold for Option<T>
-where
- T: Fold,
- T::Output: Default,
-{
- type Output = Option<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.map(|inner| inner.fold(outer.unwrap_or_default()))
- }
-}
-
-impl<T> Fold for Smart<T>
-where
- T: Fold,
- T::Output: Default,
-{
- type Output = Smart<T::Output>;
-
- fn fold(self, outer: Self::Output) -> Self::Output {
- self.map(|inner| inner.fold(outer.unwrap_or_default()))
- }
-}
-
-/// A show rule recipe.
-#[derive(Clone, PartialEq, Hash)]
-struct Recipe {
- /// The affected node.
- node: TypeId,
- /// The name of the affected node.
- name: &'static str,
- /// The function that defines the recipe.
- func: Func,
- /// The span to report all erros with.
- span: Span,
-}
-
-impl Recipe {
- /// Create a new recipe for the node `T`.
- fn new<T: Node>(func: Func, span: Span) -> Self {
- Self {
- node: TypeId::of::<T>(),
- name: std::any::type_name::<T>(),
- func,
- span,
- }
- }
-}
-
-impl Debug for Recipe {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "Recipe for {} from {:?}", self.name, self.span)
- }
-}
-
-/// A style chain barrier.
-///
-/// Barriers interact with [scoped](StyleMap::scoped) styles: A scoped style
-/// can still be read through a single barrier (the one of the node it
-/// _should_ apply to), but a second barrier will make it invisible.
-#[derive(Clone, PartialEq, Hash)]
-pub struct Barrier(Entry);
-
-impl Barrier {
- /// Create a new barrier for the layout node `T`.
- pub fn new<T: Layout>() -> Self {
- Self(Entry::Barrier(
- TypeId::of::<T>(),
- std::any::type_name::<T>(),
- ))
- }
-
- /// Make this barrier the first link of the `tail` chain.
- pub fn chain<'a>(&'a self, tail: &'a StyleChain) -> StyleChain<'a> {
- // We have to store a full `Entry` enum inside the barrier because
- // otherwise the `slice::from_ref` trick below won't work.
- // Unfortunately, that also means we have to somehow extract the id
- // here.
- let id = match self.0 {
- Entry::Barrier(id, _) => id,
- _ => unreachable!(),
- };
-
- if tail
- .entries()
- .filter_map(Entry::property)
- .any(|p| p.scoped && p.node == id)
- {
- StyleChain {
- head: std::slice::from_ref(&self.0),
- tail: Some(tail),
- }
- } else {
- *tail
- }
- }
-}
-
-impl Debug for Barrier {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.0.fmt(f)
- }
-}
-
-/// Determines whether a style could interrupt some composable structure.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
-pub enum Interruption {
- /// The style forces a paragraph break.
- Par,
- /// The style forces a page break.
- Page,
-}
-
-/// A chain of style maps, similar to a linked list.
-///
-/// A style chain allows to combine properties from multiple style maps in a
-/// node hierarchy in a non-allocating way. Rather than eagerly merging the
-/// maps, each access walks the hierarchy from the innermost to the outermost
-/// map, trying to find a match and then folding it with matches further up the
-/// chain.
-#[derive(Default, Clone, Copy, Hash)]
-pub struct StyleChain<'a> {
- /// The first link of this chain.
- head: &'a [Entry],
- /// The remaining links in the chain.
- tail: Option<&'a Self>,
-}
-
-impl<'a> StyleChain<'a> {
- /// Create a new, empty style chain.
- pub fn new() -> Self {
- Self::default()
- }
-
- /// Start a new style chain with a root map.
- pub fn with_root(root: &'a StyleMap) -> Self {
- Self { head: &root.0, tail: None }
- }
-
- /// Get the output value of a style property.
- ///
- /// Returns the property's default value if no map in the chain contains an
- /// entry for it. Also takes care of resolving and folding and returns
- /// references where applicable.
- pub fn get<K: Key<'a>>(self, key: K) -> K::Output {
- K::get(self, self.values(key))
- }
-
- /// Realize a node with a user recipe.
- pub fn realize(
- self,
- ctx: &mut Context,
- node: &ShowNode,
- ) -> TypResult<Option<Content>> {
- let id = node.id();
- if let Some(recipe) = self
- .entries()
- .filter_map(Entry::recipe)
- .find(|recipe| recipe.node == id)
- {
- let dict = node.encode();
- let args = Args::from_values(recipe.span, [Value::Dict(dict)]);
- Ok(Some(recipe.func.call(ctx, args)?.cast().at(recipe.span)?))
- } else {
- Ok(None)
- }
- }
-}
-
-impl<'a> StyleChain<'a> {
- /// Return the chain, but without the trailing scoped property for the given
- /// `node`. This is a 90% hack fix for show node constructor scoping.
- pub(super) fn unscoped(mut self, node: TypeId) -> Self {
- while self
- .head
- .last()
- .and_then(Entry::property)
- .map_or(false, |p| p.scoped && p.node == node)
- {
- let len = self.head.len();
- self.head = &self.head[.. len - 1]
- }
- self
- }
-
- /// Remove the last link from the chain.
- fn pop(&mut self) {
- *self = self.tail.copied().unwrap_or_default();
- }
-
- /// Build a style map from the suffix (all links beyond the `len`) of the
- /// chain.
- fn suffix(self, len: usize) -> StyleMap {
- let mut suffix = StyleMap::new();
- let take = self.links().count().saturating_sub(len);
- for link in self.links().take(take) {
- suffix.0.splice(0 .. 0, link.iter().cloned());
- }
- suffix
- }
-
- /// Iterate over all values for the given property in the chain.
- fn values<K: Key<'a>>(self, _: K) -> Values<'a, K> {
- Values {
- entries: self.entries(),
- depth: 0,
- key: PhantomData,
- }
- }
-
- /// Iterate over the entries of the chain.
- fn entries(self) -> Entries<'a> {
- Entries {
- inner: [].as_slice().iter(),
- links: self.links(),
- }
- }
-
- /// Iterate over the links of the chain.
- fn links(self) -> Links<'a> {
- Links(Some(self))
- }
-}
-
-impl Debug for StyleChain<'_> {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- for entry in self.entries() {
- writeln!(f, "{:?}", entry)?;
- }
- Ok(())
- }
-}
-
-impl PartialEq for StyleChain<'_> {
- fn eq(&self, other: &Self) -> bool {
- let as_ptr = |s| s as *const _;
- self.head.as_ptr() == other.head.as_ptr()
- && self.head.len() == other.head.len()
- && self.tail.map(as_ptr) == other.tail.map(as_ptr)
- }
-}
-
-/// An iterator over the values in a style chain.
-struct Values<'a, K> {
- entries: Entries<'a>,
- depth: usize,
- key: PhantomData<K>,
-}
-
-impl<'a, K: Key<'a>> Iterator for Values<'a, K> {
- type Item = &'a K::Value;
-
- fn next(&mut self) -> Option<Self::Item> {
- while let Some(entry) = self.entries.next() {
- match entry {
- Entry::Property(property) => {
- if let Some(value) = property.downcast::<K>() {
- if !property.scoped || self.depth <= 1 {
- return Some(value);
- }
- }
- }
- Entry::Barrier(id, _) => {
- self.depth += (*id == K::node()) as usize;
- }
- Entry::Recipe(_) => {}
- }
- }
-
- None
- }
-}
-
-/// An iterator over the entries in a style chain.
-struct Entries<'a> {
- inner: std::slice::Iter<'a, Entry>,
- links: Links<'a>,
-}
-
-impl<'a> Iterator for Entries<'a> {
- type Item = &'a Entry;
-
- fn next(&mut self) -> Option<Self::Item> {
- loop {
- if let Some(entry) = self.inner.next_back() {
- return Some(entry);
- }
-
- match self.links.next() {
- Some(next) => self.inner = next.iter(),
- None => return None,
- }
- }
- }
-}
-
-/// An iterator over the links of a style chain.
-struct Links<'a>(Option<StyleChain<'a>>);
-
-impl<'a> Iterator for Links<'a> {
- type Item = &'a [Entry];
-
- fn next(&mut self) -> Option<Self::Item> {
- let StyleChain { head, tail } = self.0?;
- self.0 = tail.copied();
- Some(head)
- }
-}
-
-/// A sequence of items with associated styles.
-#[derive(Hash)]
-pub struct StyleVec<T> {
- items: Vec<T>,
- maps: Vec<(StyleMap, usize)>,
-}
-
-impl<T> StyleVec<T> {
- /// Whether there are any items in the sequence.
- pub fn is_empty(&self) -> bool {
- self.items.is_empty()
- }
-
- /// Number of items in the sequence.
- pub fn len(&self) -> usize {
- self.items.len()
- }
-
- /// Iterate over the contained maps. Note that zipping this with `items()`
- /// does not yield the same result as calling `iter()` because this method
- /// only returns maps once that are shared by consecutive items. This method
- /// is designed for use cases where you want to check, for example, whether
- /// any of the maps fulfills a specific property.
- pub fn maps(&self) -> impl Iterator<Item = &StyleMap> {
- self.maps.iter().map(|(map, _)| map)
- }
-
- /// Iterate over the contained items.
- pub fn items(&self) -> std::slice::Iter<'_, T> {
- self.items.iter()
- }
-
- /// Iterate over the contained items and associated style maps.
- pub fn iter(&self) -> impl Iterator<Item = (&T, &StyleMap)> + '_ {
- let styles = self
- .maps
- .iter()
- .flat_map(|(map, count)| std::iter::repeat(map).take(*count));
- self.items().zip(styles)
- }
-
- /// Insert an element in the front. The element will share the style of the
- /// current first element.
- ///
- /// This method has no effect if the vector is empty.
- pub fn push_front(&mut self, item: T) {
- if !self.maps.is_empty() {
- self.items.insert(0, item);
- self.maps[0].1 += 1;
- }
- }
-}
-
-impl<T> Default for StyleVec<T> {
- fn default() -> Self {
- Self { items: vec![], maps: vec![] }
- }
-}
-
-impl<T: Debug> Debug for StyleVec<T> {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- f.debug_list()
- .entries(self.iter().map(|(item, map)| {
- crate::util::debug(|f| {
- map.fmt(f)?;
- item.fmt(f)
- })
- }))
- .finish()
- }
-}
-
-/// Assists in the construction of a [`StyleVec`].
-pub struct StyleVecBuilder<'a, T> {
- items: Vec<T>,
- chains: Vec<(StyleChain<'a>, usize)>,
-}
-
-impl<'a, T> StyleVecBuilder<'a, T> {
- /// Create a new style-vec builder.
- pub fn new() -> Self {
- Self { items: vec![], chains: vec![] }
- }
-
- /// Push a new item into the style vector.
- pub fn push(&mut self, item: T, styles: StyleChain<'a>) {
- self.items.push(item);
-
- if let Some((prev, count)) = self.chains.last_mut() {
- if *prev == styles {
- *count += 1;
- return;
- }
- }
-
- self.chains.push((styles, 1));
- }
-
- /// Access the last item mutably and its chain by value.
- pub fn last_mut(&mut self) -> Option<(&mut T, StyleChain<'a>)> {
- let item = self.items.last_mut()?;
- let chain = self.chains.last()?.0;
- Some((item, chain))
- }
-
- /// Iterate over the contained items.
- pub fn items(&self) -> std::slice::Iter<'_, T> {
- self.items.iter()
- }
-
- /// Finish building, returning a pair of two things:
- /// - a style vector of items with the non-shared styles
- /// - a shared prefix chain of styles that apply to all items
- pub fn finish(self) -> (StyleVec<T>, StyleChain<'a>) {
- let mut iter = self.chains.iter();
- let mut trunk = match iter.next() {
- Some(&(chain, _)) => chain,
- None => return Default::default(),
- };
-
- let mut shared = trunk.links().count();
- for &(mut chain, _) in iter {
- let len = chain.links().count();
- if len < shared {
- for _ in 0 .. shared - len {
- trunk.pop();
- }
- shared = len;
- } else if len > shared {
- for _ in 0 .. len - shared {
- chain.pop();
- }
- }
-
- while shared > 0 && chain != trunk {
- trunk.pop();
- chain.pop();
- shared -= 1;
- }
- }
-
- let maps = self
- .chains
- .into_iter()
- .map(|(chain, count)| (chain.suffix(shared), count))
- .collect();
-
- (StyleVec { items: self.items, maps }, trunk)
- }
-}
-
-impl<'a, T> Default for StyleVecBuilder<'a, T> {
- fn default() -> Self {
- Self::new()
- }
-}
diff --git a/src/eval/value.rs b/src/eval/value.rs
index 2ef26059..31287fa8 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -5,12 +5,13 @@ use std::hash::{Hash, Hasher};
use std::num::NonZeroUsize;
use std::sync::Arc;
-use super::{ops, Args, Array, Content, Dict, Func, Layout, LayoutNode, RawLength};
+use super::{ops, Args, Array, Dict, Func, RawLength};
use crate::diag::{with_alternative, StrResult};
use crate::geom::{
Angle, Color, Dir, Em, Fraction, Length, Paint, Ratio, Relative, RgbaColor,
};
use crate::library::text::RawNode;
+use crate::model::{Content, Layout, LayoutNode};
use crate::syntax::Spanned;
use crate::util::EcoString;