From 37a7afddfaffd44cb9bc013c9506599267e08983 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Thu, 3 Nov 2022 11:44:53 +0100 Subject: Split crates --- src/library/structure/doc.rs | 29 --- src/library/structure/heading.rs | 176 ------------------ src/library/structure/list.rs | 371 ------------------------------------- src/library/structure/mod.rs | 13 -- src/library/structure/reference.rs | 29 --- src/library/structure/table.rs | 160 ---------------- 6 files changed, 778 deletions(-) delete mode 100644 src/library/structure/doc.rs delete mode 100644 src/library/structure/heading.rs delete mode 100644 src/library/structure/list.rs delete mode 100644 src/library/structure/mod.rs delete mode 100644 src/library/structure/reference.rs delete mode 100644 src/library/structure/table.rs (limited to 'src/library/structure') diff --git a/src/library/structure/doc.rs b/src/library/structure/doc.rs deleted file mode 100644 index c3af3f1c..00000000 --- a/src/library/structure/doc.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::library::layout::PageNode; -use crate::library::prelude::*; - -/// A sequence of page runs. -#[derive(Hash)] -pub struct DocNode(pub StyleVec); - -impl DocNode { - /// Layout the document into a sequence of frames, one per page. - pub fn layout( - &self, - world: Tracked, - styles: StyleChain, - ) -> SourceResult> { - let mut frames = vec![]; - for (page, map) in self.0.iter() { - let number = 1 + frames.len(); - frames.extend(page.layout(world, number, map.chain(&styles))?); - } - Ok(frames) - } -} - -impl Debug for DocNode { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.write_str("Doc ")?; - self.0.fmt(f) - } -} diff --git a/src/library/structure/heading.rs b/src/library/structure/heading.rs deleted file mode 100644 index 5b056c30..00000000 --- a/src/library/structure/heading.rs +++ /dev/null @@ -1,176 +0,0 @@ -use crate::library::layout::{BlockNode, BlockSpacing}; -use crate::library::prelude::*; -use crate::library::text::{FontFamily, TextNode, TextSize}; - -/// A section heading. -#[derive(Debug, Hash)] -pub struct HeadingNode { - /// The logical nesting depth of the section, starting from one. In the - /// default style, this controls the text size of the heading. - pub level: NonZeroUsize, - /// The heading's contents. - pub body: Content, -} - -#[node(Show)] -impl HeadingNode { - /// The heading's font family. Just the normal text family if `auto`. - #[property(referenced)] - pub const FAMILY: Leveled> = Leveled::Value(Smart::Auto); - /// The color of text in the heading. Just the normal text color if `auto`. - #[property(referenced)] - pub const FILL: Leveled> = Leveled::Value(Smart::Auto); - /// The size of text in the heading. - #[property(referenced)] - pub const SIZE: Leveled = Leveled::Mapping(|level| { - let size = match level.get() { - 1 => 1.4, - 2 => 1.2, - _ => 1.0, - }; - TextSize(Em::new(size).into()) - }); - - /// Whether text in the heading is strengthend. - #[property(referenced)] - pub const STRONG: Leveled = Leveled::Value(true); - /// Whether text in the heading is emphasized. - #[property(referenced)] - pub const EMPH: Leveled = Leveled::Value(false); - /// Whether the heading is underlined. - #[property(referenced)] - pub const UNDERLINE: Leveled = Leveled::Value(false); - - /// The spacing above the heading. - #[property(referenced, shorthand(around))] - pub const ABOVE: Leveled> = Leveled::Mapping(|level| { - let ratio = match level.get() { - 1 => 1.5, - _ => 1.2, - }; - Some(Ratio::new(ratio).into()) - }); - /// The spacing below the heading. - #[property(referenced, shorthand(around))] - pub const BELOW: Leveled> = - Leveled::Value(Some(Ratio::new(0.55).into())); - - /// Whether the heading appears in the outline. - pub const OUTLINED: bool = true; - /// Whether the heading is numbered. - pub const NUMBERED: bool = true; - - fn construct(_: &mut Vm, args: &mut Args) -> SourceResult { - Ok(Self { - body: args.expect("body")?, - level: args.named("level")?.unwrap_or(NonZeroUsize::new(1).unwrap()), - } - .pack()) - } -} - -impl Show for HeadingNode { - fn unguard_parts(&self, sel: Selector) -> Content { - Self { body: self.body.unguard(sel), ..*self }.pack() - } - - fn field(&self, name: &str) -> Option { - match name { - "level" => Some(Value::Int(self.level.get() as i64)), - "body" => Some(Value::Content(self.body.clone())), - _ => None, - } - } - - fn realize(&self, _: Tracked, _: StyleChain) -> SourceResult { - Ok(BlockNode(self.body.clone()).pack()) - } - - fn finalize( - &self, - world: Tracked, - styles: StyleChain, - mut realized: Content, - ) -> SourceResult { - macro_rules! resolve { - ($key:expr) => { - styles.get($key).resolve(world, self.level)? - }; - } - - let mut map = StyleMap::new(); - map.set(TextNode::SIZE, resolve!(Self::SIZE)); - - if let Smart::Custom(family) = resolve!(Self::FAMILY) { - map.set_family(family, styles); - } - - if let Smart::Custom(fill) = resolve!(Self::FILL) { - map.set(TextNode::FILL, fill); - } - - if resolve!(Self::STRONG) { - realized = realized.strong(); - } - - if resolve!(Self::EMPH) { - realized = realized.emph(); - } - - if resolve!(Self::UNDERLINE) { - realized = realized.underlined(); - } - - realized = realized.styled_with_map(map); - realized = realized.spaced( - resolve!(Self::ABOVE).resolve(styles), - resolve!(Self::BELOW).resolve(styles), - ); - - Ok(realized) - } -} - -/// Either the value or a closure mapping to the value. -#[derive(Debug, Clone, PartialEq, Hash)] -pub enum Leveled { - /// A bare value. - Value(T), - /// A simple mapping from a heading level to a value. - Mapping(fn(NonZeroUsize) -> T), - /// A closure mapping from a heading level to a value. - Func(Func, Span), -} - -impl Leveled { - /// Resolve the value based on the level. - pub fn resolve( - &self, - world: Tracked, - level: NonZeroUsize, - ) -> SourceResult { - Ok(match self { - Self::Value(value) => value.clone(), - Self::Mapping(mapping) => mapping(level), - Self::Func(func, span) => { - let args = Args::new(*span, [Value::Int(level.get() as i64)]); - func.call_detached(world, args)?.cast().at(*span)? - } - }) - } -} - -impl Cast> for Leveled { - fn is(value: &Spanned) -> bool { - matches!(&value.v, Value::Func(_)) || T::is(&value.v) - } - - fn cast(value: Spanned) -> StrResult { - match value.v { - Value::Func(v) => Ok(Self::Func(v, value.span)), - v => T::cast(v) - .map(Self::Value) - .map_err(|msg| with_alternative(msg, "function")), - } - } -} diff --git a/src/library/structure/list.rs b/src/library/structure/list.rs deleted file mode 100644 index f061c5f8..00000000 --- a/src/library/structure/list.rs +++ /dev/null @@ -1,371 +0,0 @@ -use unscanny::Scanner; - -use crate::library::layout::{BlockSpacing, GridNode, HNode, TrackSizing}; -use crate::library::prelude::*; -use crate::library::text::{ParNode, SpaceNode}; -use crate::library::utility::Numbering; - -/// An unordered (bulleted) or ordered (numbered) list. -#[derive(Debug, Hash)] -pub struct ListNode { - /// If true, the items are separated by leading instead of list spacing. - pub tight: bool, - /// If true, the spacing above the list is leading instead of above spacing. - pub attached: bool, - /// The individual bulleted or numbered items. - pub items: StyleVec, -} - -/// An ordered list. -pub type EnumNode = ListNode; - -/// A description list. -pub type DescNode = ListNode; - -#[node(Show)] -impl ListNode { - /// How the list is labelled. - #[property(referenced)] - pub const LABEL: Label = Label::Default; - /// The indentation of each item's label. - #[property(resolve)] - pub const INDENT: Length = Length::zero(); - /// The space between the label and the body of each item. - #[property(resolve)] - pub const BODY_INDENT: Length = Em::new(match L { - LIST | ENUM => 0.5, - DESC | _ => 1.0, - }) - .into(); - - /// The spacing above the list. - #[property(resolve, shorthand(around))] - pub const ABOVE: Option = Some(Ratio::one().into()); - /// The spacing below the list. - #[property(resolve, shorthand(around))] - pub const BELOW: Option = Some(Ratio::one().into()); - /// The spacing between the items of a wide (non-tight) list. - #[property(resolve)] - pub const SPACING: BlockSpacing = Ratio::one().into(); - - fn construct(_: &mut Vm, args: &mut Args) -> SourceResult { - let items = match L { - LIST => args - .all()? - .into_iter() - .map(|body| ListItem::List(Box::new(body))) - .collect(), - ENUM => { - let mut number: usize = args.named("start")?.unwrap_or(1); - args.all()? - .into_iter() - .map(|body| { - let item = ListItem::Enum(Some(number), Box::new(body)); - number += 1; - item - }) - .collect() - } - DESC | _ => args - .all()? - .into_iter() - .map(|item| ListItem::Desc(Box::new(item))) - .collect(), - }; - - Ok(Self { - tight: args.named("tight")?.unwrap_or(true), - attached: args.named("attached")?.unwrap_or(false), - items, - } - .pack()) - } -} - -impl Show for ListNode { - fn unguard_parts(&self, sel: Selector) -> Content { - Self { - items: self.items.map(|item| item.unguard(sel)), - ..*self - } - .pack() - } - - fn field(&self, name: &str) -> Option { - match name { - "tight" => Some(Value::Bool(self.tight)), - "attached" => Some(Value::Bool(self.attached)), - "items" => Some(Value::Array( - self.items.items().map(|item| item.encode()).collect(), - )), - _ => None, - } - } - - fn realize( - &self, - world: Tracked, - styles: StyleChain, - ) -> SourceResult { - let mut cells = vec![]; - let mut number = 1; - - let label = styles.get(Self::LABEL); - let indent = styles.get(Self::INDENT); - let body_indent = styles.get(Self::BODY_INDENT); - let gutter = if self.tight { - styles.get(ParNode::LEADING) - } else { - styles.get(Self::SPACING) - }; - - for (item, map) in self.items.iter() { - if let &ListItem::Enum(Some(n), _) = item { - number = n; - } - - cells.push(Content::empty()); - - let label = if L == LIST || L == ENUM { - label.resolve(world, L, number)?.styled_with_map(map.clone()) - } else { - Content::empty() - }; - - cells.push(label); - cells.push(Content::empty()); - - let body = match &item { - ListItem::List(body) => body.as_ref().clone(), - ListItem::Enum(_, body) => body.as_ref().clone(), - ListItem::Desc(item) => Content::sequence(vec![ - HNode { - amount: (-body_indent).into(), - weak: false, - } - .pack(), - (item.term.clone() + TextNode(':'.into()).pack()).strong(), - SpaceNode.pack(), - item.body.clone(), - ]), - }; - - cells.push(body.styled_with_map(map.clone())); - number += 1; - } - - Ok(GridNode { - tracks: Axes::with_x(vec![ - TrackSizing::Relative(indent.into()), - TrackSizing::Auto, - TrackSizing::Relative(body_indent.into()), - TrackSizing::Auto, - ]), - gutter: Axes::with_y(vec![TrackSizing::Relative(gutter.into())]), - cells, - } - .pack()) - } - - fn finalize( - &self, - _: Tracked, - styles: StyleChain, - realized: Content, - ) -> SourceResult { - let mut above = styles.get(Self::ABOVE); - let mut below = styles.get(Self::BELOW); - - if self.attached { - if above.is_some() { - above = Some(styles.get(ParNode::LEADING)); - } - if below.is_some() { - below = Some(styles.get(ParNode::SPACING)); - } - } - - Ok(realized.spaced(above, below)) - } -} - -/// An item in a list. -#[derive(Clone, PartialEq, Hash)] -pub enum ListItem { - /// An item of an unordered list. - List(Box), - /// An item of an ordered list. - Enum(Option, Box), - /// An item of a description list. - Desc(Box), -} - -impl ListItem { - /// What kind of item this is. - pub fn kind(&self) -> ListKind { - match self { - Self::List(_) => LIST, - Self::Enum { .. } => ENUM, - Self::Desc { .. } => DESC, - } - } - - fn unguard(&self, sel: Selector) -> Self { - match self { - Self::List(body) => Self::List(Box::new(body.unguard(sel))), - Self::Enum(number, body) => Self::Enum(*number, Box::new(body.unguard(sel))), - Self::Desc(item) => Self::Desc(Box::new(DescItem { - term: item.term.unguard(sel), - body: item.body.unguard(sel), - })), - } - } - - /// Encode the item into a value. - fn encode(&self) -> Value { - match self { - Self::List(body) => Value::Content(body.as_ref().clone()), - Self::Enum(number, body) => Value::Dict(dict! { - "number" => match *number { - Some(n) => Value::Int(n as i64), - None => Value::None, - }, - "body" => Value::Content(body.as_ref().clone()), - }), - Self::Desc(item) => Value::Dict(dict! { - "term" => Value::Content(item.term.clone()), - "body" => Value::Content(item.body.clone()), - }), - } - } -} - -impl Debug for ListItem { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::List(body) => write!(f, "- {body:?}"), - Self::Enum(number, body) => match number { - Some(n) => write!(f, "{n}. {body:?}"), - None => write!(f, "+ {body:?}"), - }, - Self::Desc(item) => item.fmt(f), - } - } -} - -#[node] -impl ListItem {} - -/// A description list item. -#[derive(Clone, PartialEq, Hash)] -pub struct DescItem { - /// The term described by the list item. - pub term: Content, - /// The description of the term. - pub body: Content, -} - -castable! { - DescItem, - Expected: "dictionary with `term` and `body` keys", - Value::Dict(dict) => { - let term: Content = dict.get("term")?.clone().cast()?; - let body: Content = dict.get("body")?.clone().cast()?; - Self { term, body } - }, -} - -impl Debug for DescItem { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "/ {:?}: {:?}", self.term, self.body) - } -} - -/// How to label a list. -pub type ListKind = usize; - -/// An unordered list. -pub const LIST: ListKind = 0; - -/// An ordered list. -pub const ENUM: ListKind = 1; - -/// A description list. -pub const DESC: ListKind = 2; - -/// How to label a list or enumeration. -#[derive(Debug, Clone, PartialEq, Hash)] -pub enum Label { - /// The default labelling. - Default, - /// A pattern with prefix, numbering, lower / upper case and suffix. - Pattern(EcoString, Numbering, bool, EcoString), - /// Bare content. - Content(Content), - /// A closure mapping from an item number to a value. - Func(Func, Span), -} - -impl Label { - /// Resolve the value based on the level. - pub fn resolve( - &self, - world: Tracked, - kind: ListKind, - number: usize, - ) -> SourceResult { - Ok(match self { - Self::Default => match kind { - LIST => TextNode('•'.into()).pack(), - ENUM => TextNode(format_eco!("{}.", number)).pack(), - DESC | _ => panic!("description lists don't have a label"), - }, - Self::Pattern(prefix, numbering, upper, suffix) => { - let fmt = numbering.apply(number); - let mid = if *upper { fmt.to_uppercase() } else { fmt.to_lowercase() }; - TextNode(format_eco!("{}{}{}", prefix, mid, suffix)).pack() - } - Self::Content(content) => content.clone(), - Self::Func(func, span) => { - let args = Args::new(*span, [Value::Int(number as i64)]); - func.call_detached(world, args)?.display(world) - } - }) - } -} - -impl Cast> for Label { - fn is(value: &Spanned) -> bool { - matches!(&value.v, Value::Content(_) | Value::Func(_)) - } - - fn cast(value: Spanned) -> StrResult { - match value.v { - Value::None => Ok(Self::Content(Content::empty())), - Value::Str(pattern) => { - let mut s = Scanner::new(&pattern); - let mut prefix; - let numbering = loop { - prefix = s.before(); - match s.eat().map(|c| c.to_ascii_lowercase()) { - Some('1') => break Numbering::Arabic, - Some('a') => break Numbering::Letter, - Some('i') => break Numbering::Roman, - Some('*') => break Numbering::Symbol, - Some(_) => {} - None => Err("invalid pattern")?, - } - }; - let upper = s.scout(-1).map_or(false, char::is_uppercase); - let suffix = s.after().into(); - Ok(Self::Pattern(prefix.into(), numbering, upper, suffix)) - } - Value::Content(v) => Ok(Self::Content(v)), - Value::Func(v) => Ok(Self::Func(v, value.span)), - v => Err(format!( - "expected string, content or function, found {}", - v.type_name(), - )), - } - } -} diff --git a/src/library/structure/mod.rs b/src/library/structure/mod.rs deleted file mode 100644 index 088d1e6c..00000000 --- a/src/library/structure/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Document structuring. - -mod doc; -mod heading; -mod list; -mod reference; -mod table; - -pub use doc::*; -pub use heading::*; -pub use list::*; -pub use reference::*; -pub use table::*; diff --git a/src/library/structure/reference.rs b/src/library/structure/reference.rs deleted file mode 100644 index b4e8b047..00000000 --- a/src/library/structure/reference.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::library::prelude::*; - -/// A reference to a label. -#[derive(Debug, Hash)] -pub struct RefNode(pub EcoString); - -#[node(Show)] -impl RefNode { - fn construct(_: &mut Vm, args: &mut Args) -> SourceResult { - Ok(Self(args.expect("label")?).pack()) - } -} - -impl Show for RefNode { - fn unguard_parts(&self, _: Selector) -> Content { - Self(self.0.clone()).pack() - } - - fn field(&self, name: &str) -> Option { - match name { - "label" => Some(Value::Str(self.0.clone().into())), - _ => None, - } - } - - fn realize(&self, _: Tracked, _: StyleChain) -> SourceResult { - Ok(TextNode(format_eco!("@{}", self.0)).pack()) - } -} diff --git a/src/library/structure/table.rs b/src/library/structure/table.rs deleted file mode 100644 index 8a4eb302..00000000 --- a/src/library/structure/table.rs +++ /dev/null @@ -1,160 +0,0 @@ -use crate::library::layout::{BlockSpacing, GridNode, TrackSizing}; -use crate::library::prelude::*; - -/// A table of items. -#[derive(Debug, Hash)] -pub struct TableNode { - /// Defines sizing for content rows and columns. - pub tracks: Axes>, - /// Defines sizing of gutter rows and columns between content. - pub gutter: Axes>, - /// The content to be arranged in the table. - pub cells: Vec, -} - -#[node(Show)] -impl TableNode { - /// How to fill the cells. - #[property(referenced)] - pub const FILL: Celled> = Celled::Value(None); - /// How to stroke the cells. - #[property(resolve, fold)] - pub const STROKE: Option = Some(RawStroke::default()); - /// How much to pad the cells's content. - pub const PADDING: Rel = Abs::pt(5.0).into(); - - /// The spacing above the table. - #[property(resolve, shorthand(around))] - pub const ABOVE: Option = Some(Ratio::one().into()); - /// The spacing below the table. - #[property(resolve, shorthand(around))] - pub const BELOW: Option = Some(Ratio::one().into()); - - fn construct(_: &mut Vm, args: &mut Args) -> SourceResult { - let columns = args.named("columns")?.unwrap_or_default(); - let rows = args.named("rows")?.unwrap_or_default(); - let base_gutter: Vec = args.named("gutter")?.unwrap_or_default(); - let column_gutter = args.named("column-gutter")?; - let row_gutter = args.named("row-gutter")?; - Ok(Self { - tracks: Axes::new(columns, rows), - gutter: Axes::new( - column_gutter.unwrap_or_else(|| base_gutter.clone()), - row_gutter.unwrap_or(base_gutter), - ), - cells: args.all()?, - } - .pack()) - } -} - -impl Show for TableNode { - fn unguard_parts(&self, sel: Selector) -> Content { - Self { - tracks: self.tracks.clone(), - gutter: self.gutter.clone(), - cells: self.cells.iter().map(|cell| cell.unguard(sel)).collect(), - } - .pack() - } - - fn field(&self, name: &str) -> Option { - match name { - "cells" => Some(Value::Array( - self.cells.iter().cloned().map(Value::Content).collect(), - )), - _ => None, - } - } - - fn realize( - &self, - world: Tracked, - styles: StyleChain, - ) -> SourceResult { - let fill = styles.get(Self::FILL); - let stroke = styles.get(Self::STROKE).map(RawStroke::unwrap_or_default); - let padding = styles.get(Self::PADDING); - - let cols = self.tracks.x.len().max(1); - let cells = self - .cells - .iter() - .cloned() - .enumerate() - .map(|(i, child)| { - let mut child = child.padded(Sides::splat(padding)); - - if let Some(stroke) = stroke { - child = child.stroked(stroke); - } - - let x = i % cols; - let y = i / cols; - if let Some(fill) = fill.resolve(world, x, y)? { - child = child.filled(fill); - } - - Ok(child) - }) - .collect::>()?; - - Ok(GridNode { - tracks: self.tracks.clone(), - gutter: self.gutter.clone(), - cells, - } - .pack()) - } - - fn finalize( - &self, - _: Tracked, - styles: StyleChain, - realized: Content, - ) -> SourceResult { - Ok(realized.spaced(styles.get(Self::ABOVE), styles.get(Self::BELOW))) - } -} - -/// A value that can be configured per cell. -#[derive(Debug, Clone, PartialEq, Hash)] -pub enum Celled { - /// A bare value, the same for all cells. - Value(T), - /// A closure mapping from cell coordinates to a value. - Func(Func, Span), -} - -impl Celled { - /// Resolve the value based on the cell position. - pub fn resolve( - &self, - world: Tracked, - x: usize, - y: usize, - ) -> SourceResult { - Ok(match self { - Self::Value(value) => value.clone(), - Self::Func(func, span) => { - let args = Args::new(*span, [Value::Int(x as i64), Value::Int(y as i64)]); - func.call_detached(world, args)?.cast().at(*span)? - } - }) - } -} - -impl Cast> for Celled { - fn is(value: &Spanned) -> bool { - matches!(&value.v, Value::Func(_)) || T::is(&value.v) - } - - fn cast(value: Spanned) -> StrResult { - match value.v { - Value::Func(v) => Ok(Self::Func(v, value.span)), - v => T::cast(v) - .map(Self::Value) - .map_err(|msg| with_alternative(msg, "function")), - } - } -} -- cgit v1.2.3