diff --git a/Cargo.toml b/Cargo.toml index 402419deb..f08f9056f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" authors = ["The Typst Project Developers"] edition = "2021" +[workspace] +members = ["macros"] + [dependencies] # Workspace typst-macros = { path = "./macros" } @@ -68,11 +71,8 @@ walkdir = { version = "2", optional = true } [dev-dependencies] iai = { git = "https://github.com/reknih/iai" } -walkdir = "2" elsa = "1.7" - -[workspace] -members = ["macros"] +walkdir = "2" [features] cli = [ @@ -103,5 +103,4 @@ harness = false [[bench]] name = "oneshot" -path = "benches/oneshot.rs" harness = false diff --git a/src/export/pdf/mod.rs b/src/export/pdf/mod.rs index a87f5c6be..2f6f255d8 100644 --- a/src/export/pdf/mod.rs +++ b/src/export/pdf/mod.rs @@ -15,10 +15,9 @@ use pdf_writer::{Finish, Name, PdfWriter, Ref, TextStr}; use self::outline::{Heading, HeadingNode}; use self::page::Page; use crate::font::Font; -use crate::frame::Frame; +use crate::frame::{Frame, Lang}; use crate::geom::{Dir, Em, Length}; use crate::image::Image; -use crate::library::text::Lang; /// Export a collection of frames into a PDF file. /// diff --git a/src/frame.rs b/src/frame.rs index c7827a9ce..a367935c4 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -6,10 +6,9 @@ use std::sync::Arc; use crate::font::Font; use crate::geom::{ - Align, Em, Length, Numeric, Paint, Point, Shape, Size, Spec, Transform, + Align, Dir, Em, Length, Numeric, Paint, Point, Shape, Size, Spec, Transform, }; use crate::image::Image; -use crate::library::text::Lang; use crate::model::{Dict, Value}; use crate::util::EcoString; @@ -397,6 +396,64 @@ pub struct Glyph { pub c: char, } +/// A code for a natural language. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct Lang([u8; 3], u8); + +impl Lang { + /// The code for the english language. + pub const ENGLISH: Self = Self(*b"en ", 2); + + /// Construct a language from a two- or three-byte ISO 639-1/2/3 code. + pub fn from_str(iso: &str) -> Option { + let len = iso.len(); + if matches!(len, 2 ..= 3) && iso.is_ascii() { + let mut bytes = [b' '; 3]; + bytes[.. len].copy_from_slice(iso.as_bytes()); + bytes.make_ascii_lowercase(); + Some(Self(bytes, len as u8)) + } else { + None + } + } + + /// Return the language code as an all lowercase string slice. + pub fn as_str(&self) -> &str { + std::str::from_utf8(&self.0[.. usize::from(self.1)]).unwrap_or_default() + } + + /// The default direction for the language. + pub fn dir(self) -> Dir { + match self.as_str() { + "ar" | "dv" | "fa" | "he" | "ks" | "pa" | "ps" | "sd" | "ug" | "ur" + | "yi" => Dir::RTL, + _ => Dir::LTR, + } + } +} + +/// A code for a region somewhere in the world. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct Region([u8; 2]); + +impl Region { + /// Construct a region from its two-byte ISO 3166-1 alpha-2 code. + pub fn from_str(iso: &str) -> Option { + if iso.is_ascii() { + let mut bytes: [u8; 2] = iso.as_bytes().try_into().ok()?; + bytes.make_ascii_uppercase(); + Some(Self(bytes)) + } else { + None + } + } + + /// Return the region code as an all uppercase string slice. + pub fn as_str(&self) -> &str { + std::str::from_utf8(&self.0).unwrap_or_default() + } +} + /// A link destination. #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum Destination { diff --git a/src/lib.rs b/src/lib.rs index 25f59aae1..caed7592b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,6 +46,7 @@ pub mod image; pub mod library; pub mod syntax; +use std::num::NonZeroUsize; use std::path::{Path, PathBuf}; use comemo::{Prehashed, Track}; @@ -54,9 +55,9 @@ use crate::diag::{FileResult, SourceResult}; use crate::font::{Font, FontBook}; use crate::frame::Frame; use crate::model::StyleMap; -use crate::model::{Route, Scope}; +use crate::model::{Content, Route, Scope}; use crate::syntax::{Source, SourceId}; -use crate::util::Buffer; +use crate::util::{Buffer, EcoString}; /// Typeset a source file into a collection of layouted frames. /// @@ -105,6 +106,10 @@ pub struct Config { /// /// Default: Typst's standard library. pub std: Scope, + /// Defines which standard library items fulfill which syntactical roles. + /// + /// Default: Typst's standard library's role map. + pub roles: RoleMap, /// The default properties for page size, font selection and so on. /// /// Default: Empty style map. @@ -115,8 +120,23 @@ impl Default for Config { fn default() -> Self { Self { root: PathBuf::new(), - std: library::new(), + std: library::scope(), styles: StyleMap::new(), + roles: library::roles(), } } } + +/// Definition of certain standard library items the language is aware of. +#[derive(Debug, Clone, Hash)] +pub struct RoleMap { + strong: fn(Content) -> Content, + emph: fn(Content) -> Content, + raw: fn(EcoString, Option, bool) -> Content, + link: fn(EcoString) -> Content, + ref_: fn(EcoString) -> Content, + heading: fn(NonZeroUsize, Content) -> Content, + list_item: fn(Content) -> Content, + enum_item: fn(Option, Content) -> Content, + desc_item: fn(Content, Content) -> Content, +} diff --git a/src/library/graphics/mod.rs b/src/library/graphics/mod.rs index e9a6188f3..34182121c 100644 --- a/src/library/graphics/mod.rs +++ b/src/library/graphics/mod.rs @@ -4,10 +4,8 @@ mod hide; mod image; mod line; mod shape; -mod transform; pub use self::image::*; pub use hide::*; pub use line::*; pub use shape::*; -pub use transform::*; diff --git a/src/library/layout/mod.rs b/src/library/layout/mod.rs index 588b15aa1..02276f228 100644 --- a/src/library/layout/mod.rs +++ b/src/library/layout/mod.rs @@ -10,6 +10,7 @@ mod page; mod place; mod spacing; mod stack; +mod transform; pub use align::*; pub use columns::*; @@ -21,3 +22,4 @@ pub use page::*; pub use place::*; pub use spacing::*; pub use stack::*; +pub use transform::*; diff --git a/src/library/layout/page.rs b/src/library/layout/page.rs index 9cbbcca5f..7d13163da 100644 --- a/src/library/layout/page.rs +++ b/src/library/layout/page.rs @@ -188,7 +188,7 @@ impl Marginal { Self::Content(content) => Some(content.clone()), Self::Func(func, span) => { let args = Args::new(*span, [Value::Int(page as i64)]); - Some(func.call_detached(world, args)?.display()) + Some(func.call_detached(world, args)?.display(world)) } }) } diff --git a/src/library/graphics/transform.rs b/src/library/layout/transform.rs similarity index 100% rename from src/library/graphics/transform.rs rename to src/library/layout/transform.rs diff --git a/src/library/math/mod.rs b/src/library/math/mod.rs index 7b5fdf522..7d0fecb42 100644 --- a/src/library/math/mod.rs +++ b/src/library/math/mod.rs @@ -15,7 +15,7 @@ use rex::render::{Backend, Cursor, Renderer}; use crate::font::Font; use crate::library::layout::BlockSpacing; use crate::library::prelude::*; -use crate::library::text::{variant, FontFamily, Lang, TextNode}; +use crate::library::text::{variant, FontFamily, TextNode}; /// A piece of a mathematical formula. #[derive(Debug, Clone, Hash)] diff --git a/src/library/mod.rs b/src/library/mod.rs index b42ec0713..cf9acdac0 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -14,7 +14,7 @@ pub mod utility; use prelude::*; /// Construct a scope containing all standard library definitions. -pub fn new() -> Scope { +pub fn scope() -> Scope { let mut std = Scope::new(); // Text. @@ -58,6 +58,9 @@ pub fn new() -> Scope { std.def_node::("columns"); std.def_node::("colbreak"); std.def_node::("place"); + std.def_node::("move"); + std.def_node::("scale"); + std.def_node::("rotate"); // Graphics. std.def_node::("image"); @@ -66,9 +69,6 @@ pub fn new() -> Scope { std.def_node::("square"); std.def_node::("ellipse"); std.def_node::("circle"); - std.def_node::("move"); - std.def_node::("scale"); - std.def_node::("rotate"); std.def_node::("hide"); // Math. @@ -142,3 +142,117 @@ pub fn new() -> Scope { std } + +/// Construct the standard role map. +pub fn roles() -> RoleMap { + RoleMap { + strong: |body| Content::show(text::StrongNode(body)), + emph: |body| Content::show(text::EmphNode(body)), + raw: |text, lang, block| { + let node = Content::show(text::RawNode { text, block }); + match lang { + Some(_) => node.styled(text::RawNode::LANG, lang), + None => node, + } + }, + link: |url| Content::show(text::LinkNode::from_url(url)), + ref_: |target| Content::show(structure::RefNode(target)), + heading: |level, body| Content::show(structure::HeadingNode { level, body }), + list_item: |body| Content::Item(structure::ListItem::List(Box::new(body))), + enum_item: |number, body| { + Content::Item(structure::ListItem::Enum(number, Box::new(body))) + }, + desc_item: |term, body| { + Content::Item(structure::ListItem::Desc(Box::new(structure::DescItem { + term, + body, + }))) + }, + } +} + +/// Additional methods on content. +pub trait ContentExt { + /// Make this content strong. + fn strong(self) -> Self; + + /// Make this content emphasized. + fn emph(self) -> Self; + + /// Underline this content. + fn underlined(self) -> Self; +} + +impl ContentExt for Content { + fn strong(self) -> Self { + Self::show(text::StrongNode(self)) + } + + fn emph(self) -> Self { + Self::show(text::EmphNode(self)) + } + + fn underlined(self) -> Self { + Self::show(text::DecoNode::<{ text::UNDERLINE }>(self)) + } +} + +/// Additional methods for the style chain. +pub trait StyleMapExt { + /// Set a font family composed of a preferred family and existing families + /// from a style chain. + fn set_family(&mut self, preferred: text::FontFamily, existing: StyleChain); +} + +impl StyleMapExt for StyleMap { + fn set_family(&mut self, preferred: text::FontFamily, existing: StyleChain) { + self.set( + text::TextNode::FAMILY, + std::iter::once(preferred) + .chain(existing.get(text::TextNode::FAMILY).iter().cloned()) + .collect(), + ); + } +} + +/// Additional methods for layout nodes. +pub trait LayoutNodeExt { + /// Set alignments for this node. + fn aligned(self, aligns: Spec>) -> Self; + + /// Pad this node at the sides. + fn padded(self, padding: Sides>) -> Self; + + /// Transform this node's contents without affecting layout. + fn moved(self, delta: Spec>) -> Self; +} + +impl LayoutNodeExt for LayoutNode { + fn aligned(self, aligns: Spec>) -> Self { + if aligns.any(Option::is_some) { + layout::AlignNode { aligns, child: self }.pack() + } else { + self + } + } + + fn padded(self, padding: Sides>) -> Self { + if !padding.left.is_zero() + || !padding.top.is_zero() + || !padding.right.is_zero() + || !padding.bottom.is_zero() + { + layout::PadNode { padding, child: self }.pack() + } else { + self + } + } + + fn moved(self, delta: Spec>) -> Self { + if delta.any(|r| !r.is_zero()) { + layout::MoveNode { delta, child: self }.pack() + } else { + self + } + } +} diff --git a/src/library/prelude.rs b/src/library/prelude.rs index 7a4284f16..1d98e2a92 100644 --- a/src/library/prelude.rs +++ b/src/library/prelude.rs @@ -9,6 +9,7 @@ pub use std::sync::Arc; pub use comemo::Tracked; pub use typst_macros::node; +pub use super::{ContentExt, LayoutNodeExt, StyleMapExt}; pub use crate::diag::{ with_alternative, At, FileError, FileResult, SourceError, SourceResult, StrResult, }; @@ -24,4 +25,4 @@ pub use crate::model::{ }; pub use crate::syntax::{Span, Spanned}; pub use crate::util::EcoString; -pub use crate::World; +pub use crate::{RoleMap, World}; diff --git a/src/library/structure/list.rs b/src/library/structure/list.rs index 7a43e5db1..9d78238d7 100644 --- a/src/library/structure/list.rs +++ b/src/library/structure/list.rs @@ -328,7 +328,7 @@ impl Label { 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() + func.call_detached(world, args)?.display(world) } }) } diff --git a/src/library/text/lang.rs b/src/library/text/lang.rs deleted file mode 100644 index f2193f050..000000000 --- a/src/library/text/lang.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::geom::Dir; -use crate::model::Value; - -/// A code for a natural language. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct Lang([u8; 3], u8); - -impl Lang { - /// The code for the english language. - pub const ENGLISH: Self = Self(*b"en ", 2); - - /// Construct a language from a two- or three-byte ISO 639-1/2/3 code. - pub fn from_str(iso: &str) -> Option { - let len = iso.len(); - if matches!(len, 2 ..= 3) && iso.is_ascii() { - let mut bytes = [b' '; 3]; - bytes[.. len].copy_from_slice(iso.as_bytes()); - bytes.make_ascii_lowercase(); - Some(Self(bytes, len as u8)) - } else { - None - } - } - - /// Return the language code as an all lowercase string slice. - pub fn as_str(&self) -> &str { - std::str::from_utf8(&self.0[.. usize::from(self.1)]).unwrap_or_default() - } - - /// The default direction for the language. - pub fn dir(self) -> Dir { - match self.as_str() { - "ar" | "dv" | "fa" | "he" | "ks" | "pa" | "ps" | "sd" | "ug" | "ur" - | "yi" => Dir::RTL, - _ => Dir::LTR, - } - } -} - -castable! { - Lang, - Expected: "string", - Value::Str(string) => Self::from_str(&string) - .ok_or("expected two or three letter language code (ISO 639-1/2/3)")?, -} - -/// A code for a region somewhere in the world. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct Region([u8; 2]); - -impl Region { - /// Construct a region from its two-byte ISO 3166-1 alpha-2 code. - pub fn from_str(iso: &str) -> Option { - if iso.is_ascii() { - let mut bytes: [u8; 2] = iso.as_bytes().try_into().ok()?; - bytes.make_ascii_uppercase(); - Some(Self(bytes)) - } else { - None - } - } - - /// Return the region code as an all uppercase string slice. - pub fn as_str(&self) -> &str { - std::str::from_utf8(&self.0).unwrap_or_default() - } -} - -castable! { - Region, - Expected: "string", - Value::Str(string) => Self::from_str(&string) - .ok_or("expected two letter region code (ISO 3166-1 alpha-2)")?, -} diff --git a/src/library/text/mod.rs b/src/library/text/mod.rs index c9bf2e57a..299357de7 100644 --- a/src/library/text/mod.rs +++ b/src/library/text/mod.rs @@ -1,7 +1,6 @@ //! Text handling and paragraph layout. mod deco; -mod lang; mod link; mod par; mod quotes; @@ -11,7 +10,6 @@ mod shaping; mod shift; pub use deco::*; -pub use lang::*; pub use link::*; pub use par::*; pub use quotes::*; @@ -290,6 +288,20 @@ castable! { }), } +castable! { + Lang, + Expected: "string", + Value::Str(string) => Self::from_str(&string) + .ok_or("expected two or three letter language code (ISO 639-1/2/3)")?, +} + +castable! { + Region, + Expected: "string", + Value::Str(string) => Self::from_str(&string) + .ok_or("expected two letter region code (ISO 3166-1 alpha-2)")?, +} + /// The direction of text and inline objects in their line. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub struct HorizontalDir(pub Dir); diff --git a/src/model/collapse.rs b/src/model/collapse.rs index 258f577eb..5a0d6531c 100644 --- a/src/model/collapse.rs +++ b/src/model/collapse.rs @@ -114,64 +114,3 @@ impl<'a, T> Default for CollapsingBuilder<'a, T> { Self::new() } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::library::layout::FlowChild; - use crate::library::prelude::*; - - #[track_caller] - fn test(builder: CollapsingBuilder, expected: &[T]) - where - T: Debug + PartialEq, - { - let result = builder.finish().0; - let items: Vec<_> = result.items().collect(); - let expected: Vec<_> = expected.iter().collect(); - assert_eq!(items, expected); - } - - fn node() -> FlowChild { - FlowChild::Node(Content::Text("Hi".into()).pack()) - } - - fn abs(pt: f64) -> FlowChild { - FlowChild::Spacing(Length::pt(pt).into()) - } - - #[test] - fn test_collapsing_weak() { - let mut builder = CollapsingBuilder::new(); - let styles = StyleChain::default(); - builder.weak(FlowChild::Colbreak, styles, 0); - builder.supportive(node(), styles); - builder.weak(abs(10.0), styles, 0); - builder.ignorant(FlowChild::Colbreak, styles); - builder.weak(abs(20.0), styles, 0); - builder.supportive(node(), styles); - builder.weak(abs(10.0), styles, 0); - builder.weak(abs(20.0), styles, 1); - builder.supportive(node(), styles); - test(builder, &[ - node(), - FlowChild::Colbreak, - abs(20.0), - node(), - abs(10.0), - node(), - ]); - } - - #[test] - fn test_collapsing_destructive() { - let mut builder = CollapsingBuilder::new(); - let styles = StyleChain::default(); - builder.supportive(node(), styles); - builder.weak(abs(10.0), styles, 0); - builder.destructive(FlowChild::Colbreak, styles); - builder.weak(abs(20.0), styles, 0); - builder.supportive(node(), styles); - test(builder, &[node(), FlowChild::Colbreak, node()]); - } -} diff --git a/src/model/content.rs b/src/model/content.rs index 5f0536c3a..428865f33 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -1,23 +1,24 @@ -use std::fmt::Debug; +use std::fmt::{self, Debug, Formatter}; use std::hash::Hash; use std::iter::Sum; use std::mem; use std::ops::{Add, AddAssign}; +use std::sync::Arc; use comemo::Tracked; use typed_arena::Arena; use super::{ - Barrier, CollapsingBuilder, Interruption, Key, Layout, LayoutNode, Property, Show, - ShowNode, StyleEntry, StyleMap, StyleVecBuilder, Target, + Barrier, CollapsingBuilder, Dict, Interruption, Key, Layout, LayoutNode, Property, + Regions, Selector, Show, ShowNode, StyleChain, StyleEntry, StyleMap, StyleVecBuilder, + Target, }; -use crate::diag::StrResult; +use crate::diag::{SourceResult, StrResult}; +use crate::frame::{Frame, Role}; +use crate::geom::{Length, Numeric}; use crate::library::layout::{FlowChild, FlowNode, PageNode, PlaceNode, Spacing}; -use crate::library::prelude::*; use crate::library::structure::{DocNode, ListItem, ListNode, DESC, ENUM, LIST}; -use crate::library::text::{ - DecoNode, EmphNode, ParChild, ParNode, StrongNode, UNDERLINE, -}; +use crate::library::text::{ParChild, ParNode}; use crate::util::EcoString; use crate::World; @@ -173,21 +174,6 @@ impl Content { self.clone().styled_with_entry(StyleEntry::Unguard(sel)) } - /// Make this content strong. - pub fn strong(self) -> Self { - Self::show(StrongNode(self)) - } - - /// Make this content emphasized. - pub fn emph(self) -> Self { - Self::show(EmphNode(self)) - } - - /// Underline this content. - pub fn underlined(self) -> Self { - Self::show(DecoNode::(self)) - } - /// Add weak vertical spacing above and below the node. pub fn spaced(self, above: Option, below: Option) -> Self { if above.is_none() && below.is_none() { diff --git a/src/model/eval.rs b/src/model/eval.rs index aa5f0378a..6658e244e 100644 --- a/src/model/eval.rs +++ b/src/model/eval.rs @@ -141,7 +141,7 @@ fn eval_markup( ast::MarkupNode::Expr(ast::Expr::Wrap(wrap)) => { let tail = eval_markup(vm, nodes)?; vm.scopes.top.define(wrap.binding().take(), tail); - wrap.body().eval(vm)?.display() + wrap.body().eval(vm)?.display(vm.world) } _ => node.eval(vm)?, @@ -181,7 +181,7 @@ impl Eval for ast::MarkupNode { Self::Desc(v) => v.eval(vm), Self::Label(v) => v.eval(vm), Self::Ref(v) => v.eval(vm), - Self::Expr(v) => v.eval(vm).map(Value::display), + Self::Expr(v) => v.eval(vm).map(|value| value.display(vm.world)), } } } @@ -242,9 +242,7 @@ impl Eval for ast::Strong { type Output = Content; fn eval(&self, vm: &mut Vm) -> SourceResult { - Ok(Content::show(library::text::StrongNode( - self.body().eval(vm)?, - ))) + Ok((vm.roles().strong)(self.body().eval(vm)?)) } } @@ -252,34 +250,80 @@ impl Eval for ast::Emph { type Output = Content; fn eval(&self, vm: &mut Vm) -> SourceResult { - Ok(Content::show(library::text::EmphNode( - self.body().eval(vm)?, - ))) - } -} - -impl Eval for ast::Link { - type Output = Content; - - fn eval(&self, _: &mut Vm) -> SourceResult { - Ok(Content::show(library::text::LinkNode::from_url( - self.url().clone(), - ))) + Ok((vm.roles().emph)(self.body().eval(vm)?)) } } impl Eval for ast::Raw { type Output = Content; + fn eval(&self, vm: &mut Vm) -> SourceResult { + let text = self.text().clone(); + let lang = self.lang().cloned(); + let block = self.block(); + Ok((vm.roles().raw)(text, lang, block)) + } +} + +impl Eval for ast::Link { + type Output = Content; + + fn eval(&self, vm: &mut Vm) -> SourceResult { + Ok((vm.roles().link)(self.url().clone())) + } +} + +impl Eval for ast::Label { + type Output = Content; + fn eval(&self, _: &mut Vm) -> SourceResult { - let content = Content::show(library::text::RawNode { - text: self.text().clone(), - block: self.block(), - }); - Ok(match self.lang() { - Some(_) => content.styled(library::text::RawNode::LANG, self.lang().cloned()), - None => content, - }) + Ok(Content::Empty) + } +} + +impl Eval for ast::Ref { + type Output = Content; + + fn eval(&self, vm: &mut Vm) -> SourceResult { + Ok((vm.roles().ref_)(self.get().clone())) + } +} + +impl Eval for ast::Heading { + type Output = Content; + + fn eval(&self, vm: &mut Vm) -> SourceResult { + let level = self.level(); + let body = self.body().eval(vm)?; + Ok((vm.roles().heading)(level, body)) + } +} + +impl Eval for ast::ListItem { + type Output = Content; + + fn eval(&self, vm: &mut Vm) -> SourceResult { + Ok((vm.roles().list_item)(self.body().eval(vm)?)) + } +} + +impl Eval for ast::EnumItem { + type Output = Content; + + fn eval(&self, vm: &mut Vm) -> SourceResult { + let number = self.number(); + let body = self.body().eval(vm)?; + Ok((vm.roles().enum_item)(number, body)) + } +} + +impl Eval for ast::DescItem { + type Output = Content; + + fn eval(&self, vm: &mut Vm) -> SourceResult { + let term = self.term().eval(vm)?; + let body = self.body().eval(vm)?; + Ok((vm.roles().desc_item)(term, body)) } } @@ -318,7 +362,7 @@ impl Eval for ast::MathNode { ), node.span(), ), - Self::Expr(expr) => match expr.eval(vm)?.display() { + Self::Expr(expr) => match expr.eval(vm)?.display(vm.world) { Content::Text(text) => library::math::MathNode::Atom(text), _ => bail!(expr.span(), "expected text"), }, @@ -369,68 +413,6 @@ impl Eval for ast::Align { } } -impl Eval for ast::Heading { - type Output = Content; - - fn eval(&self, vm: &mut Vm) -> SourceResult { - Ok(Content::show(library::structure::HeadingNode { - body: self.body().eval(vm)?, - level: self.level(), - })) - } -} - -impl Eval for ast::ListItem { - type Output = Content; - - fn eval(&self, vm: &mut Vm) -> SourceResult { - let body = Box::new(self.body().eval(vm)?); - Ok(Content::Item(library::structure::ListItem::List(body))) - } -} - -impl Eval for ast::EnumItem { - type Output = Content; - - fn eval(&self, vm: &mut Vm) -> SourceResult { - let number = self.number(); - let body = Box::new(self.body().eval(vm)?); - Ok(Content::Item(library::structure::ListItem::Enum( - number, body, - ))) - } -} - -impl Eval for ast::DescItem { - type Output = Content; - - fn eval(&self, vm: &mut Vm) -> SourceResult { - let term = self.term().eval(vm)?; - let body = self.body().eval(vm)?; - Ok(Content::Item(library::structure::ListItem::Desc(Box::new( - library::structure::DescItem { term, body }, - )))) - } -} - -impl Eval for ast::Label { - type Output = Content; - - fn eval(&self, _: &mut Vm) -> SourceResult { - Ok(Content::Empty) - } -} - -impl Eval for ast::Ref { - type Output = Content; - - fn eval(&self, _: &mut Vm) -> SourceResult { - Ok(Content::show(library::structure::RefNode( - self.get().clone(), - ))) - } -} - impl Eval for ast::Expr { type Output = Value; @@ -530,7 +512,7 @@ fn eval_code( break; } - let tail = eval_code(vm, exprs)?.display(); + let tail = eval_code(vm, exprs)?.display(vm.world); Value::Content(tail.styled_with_map(styles)) } ast::Expr::Show(show) => { @@ -540,7 +522,7 @@ fn eval_code( break; } - let tail = eval_code(vm, exprs)?.display(); + let tail = eval_code(vm, exprs)?.display(vm.world); Value::Content(tail.styled_with_entry(entry)) } ast::Expr::Wrap(wrap) => { diff --git a/src/model/layout.rs b/src/model/layout.rs index 09888ba5d..5248157b0 100644 --- a/src/model/layout.rs +++ b/src/model/layout.rs @@ -8,19 +8,13 @@ use std::sync::Arc; use comemo::{Prehashed, Tracked}; use super::{Barrier, NodeId, Resolve, StyleChain, StyleEntry}; -use super::{Builder, Content, RawAlign, RawLength, Scratch}; +use super::{Builder, Content, RawLength, Scratch}; use crate::diag::SourceResult; use crate::frame::{Element, Frame}; -use crate::geom::{ - Align, Geometry, Length, Paint, Point, Relative, Sides, Size, Spec, Stroke, -}; -use crate::library::graphics::MoveNode; -use crate::library::layout::{AlignNode, PadNode}; +use crate::geom::{Align, Geometry, Length, Paint, Point, Relative, Size, Spec, Stroke}; use crate::World; /// Layout content into a collection of pages. -/// -/// Relayouts until all pinned locations are converged. #[comemo::memoize] pub fn layout(world: Tracked, content: &Content) -> SourceResult> { let styles = StyleChain::with_root(&world.config().styles); @@ -196,37 +190,6 @@ impl LayoutNode { pub fn stroked(self, stroke: Stroke) -> Self { StrokeNode { stroke, child: self }.pack() } - - /// Set alignments for this node. - pub fn aligned(self, aligns: Spec>) -> 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>) -> 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>) -> Self { - if delta.any(|r| !r.is_zero()) { - MoveNode { delta, child: self }.pack() - } else { - self - } - } } impl Layout for LayoutNode { diff --git a/src/model/recipe.rs b/src/model/recipe.rs index 05ef07a63..46dad6d1b 100644 --- a/src/model/recipe.rs +++ b/src/model/recipe.rs @@ -89,7 +89,7 @@ impl Recipe { Args::new(self.func.span, [arg()]) }; - Ok(self.func.v.call_detached(world, args)?.display()) + Ok(self.func.v.call_detached(world, args)?.display(world)) } /// What kind of structure the property interrupts. diff --git a/src/model/styles.rs b/src/model/styles.rs index 76199ca11..d21f3a9f7 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -8,7 +8,6 @@ use comemo::Tracked; use super::{Barrier, Content, Key, Property, Recipe, Selector, Show, Target}; use crate::diag::SourceResult; use crate::frame::Role; -use crate::library::text::{FontFamily, TextNode}; use crate::util::ReadableTypeId; use crate::World; @@ -55,17 +54,6 @@ impl StyleMap { } } - /// 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, - iter::once(preferred) - .chain(existing.get(TextNode::FAMILY).iter().cloned()) - .collect(), - ); - } - /// Whether the map contains a style property for the given key. pub fn contains<'a, K: Key<'a>>(&self, _: K) -> bool { self.0 diff --git a/src/model/value.rs b/src/model/value.rs index 4075ce9cb..e0d5edf3d 100644 --- a/src/model/value.rs +++ b/src/model/value.rs @@ -4,13 +4,14 @@ use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::sync::Arc; +use comemo::Tracked; use siphasher::sip128::{Hasher128, SipHasher}; use super::{ops, Args, Array, Cast, Content, Dict, Func, Layout, RawLength, Str}; use crate::diag::StrResult; use crate::geom::{Angle, Color, Em, Fraction, Length, Ratio, Relative, RgbaColor}; -use crate::library::text::RawNode; use crate::util::EcoString; +use crate::World; /// A computational value. #[derive(Clone)] @@ -113,7 +114,7 @@ impl Value { } /// Return the display representation of the value. - pub fn display(self) -> Content { + pub fn display(self, world: Tracked) -> Content { match self { Value::None => Content::new(), Value::Int(v) => Content::Text(format_eco!("{}", v)), @@ -123,8 +124,7 @@ impl Value { // For values which can't be shown "naturally", we return the raw // representation with typst code syntax highlighting. - v => Content::show(RawNode { text: v.repr().into(), block: false }) - .styled(RawNode::LANG, Some("typc".into())), + v => (world.config().roles.raw)(v.repr().into(), Some("typc".into()), false), } } } diff --git a/src/model/vm.rs b/src/model/vm.rs index a1b1ba81c..829693bcd 100644 --- a/src/model/vm.rs +++ b/src/model/vm.rs @@ -6,7 +6,7 @@ use super::{Route, Scopes, Value}; use crate::diag::{SourceError, StrResult}; use crate::syntax::{SourceId, Span}; use crate::util::PathExt; -use crate::World; +use crate::{RoleMap, World}; /// A virtual machine. pub struct Vm<'a> { @@ -54,6 +54,11 @@ impl<'a> Vm<'a> { return Err("cannot access file system from here".into()); } + + /// The role map. + pub fn roles(&self) -> &RoleMap { + &self.world.config().roles + } } /// A control flow event that occurred during evaluation. diff --git a/tests/typeset.rs b/tests/typeset.rs index 235fcab37..26a0c8b01 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -159,7 +159,7 @@ fn config() -> Config { styles.set(TextNode::SIZE, TextSize(Length::pt(10.0).into())); // Hook up helpers into the global scope. - let mut std = typst::library::new(); + let mut std = typst::library::scope(); std.define("conifer", RgbaColor::new(0x9f, 0xEB, 0x52, 0xFF)); std.define("forest", RgbaColor::new(0x43, 0xA1, 0x27, 0xFF)); std.def_fn("test", move |_, args| { @@ -182,7 +182,12 @@ fn config() -> Config { Ok(Value::None) }); - Config { root: PathBuf::new(), std, styles } + Config { + root: PathBuf::new(), + roles: typst::library::roles(), + std, + styles, + } } /// A world that provides access to the tests environment.