mirror of
https://github.com/typst/typst
synced 2025-05-15 09:35:28 +08:00
Reduce dependencies from compiler on library
This commit is contained in:
parent
e218226655
commit
c0e972b91a
@ -4,6 +4,9 @@ version = "0.1.0"
|
|||||||
authors = ["The Typst Project Developers"]
|
authors = ["The Typst Project Developers"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = ["macros"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Workspace
|
# Workspace
|
||||||
typst-macros = { path = "./macros" }
|
typst-macros = { path = "./macros" }
|
||||||
@ -68,11 +71,8 @@ walkdir = { version = "2", optional = true }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
iai = { git = "https://github.com/reknih/iai" }
|
iai = { git = "https://github.com/reknih/iai" }
|
||||||
walkdir = "2"
|
|
||||||
elsa = "1.7"
|
elsa = "1.7"
|
||||||
|
walkdir = "2"
|
||||||
[workspace]
|
|
||||||
members = ["macros"]
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
cli = [
|
cli = [
|
||||||
@ -103,5 +103,4 @@ harness = false
|
|||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "oneshot"
|
name = "oneshot"
|
||||||
path = "benches/oneshot.rs"
|
|
||||||
harness = false
|
harness = false
|
||||||
|
@ -15,10 +15,9 @@ use pdf_writer::{Finish, Name, PdfWriter, Ref, TextStr};
|
|||||||
use self::outline::{Heading, HeadingNode};
|
use self::outline::{Heading, HeadingNode};
|
||||||
use self::page::Page;
|
use self::page::Page;
|
||||||
use crate::font::Font;
|
use crate::font::Font;
|
||||||
use crate::frame::Frame;
|
use crate::frame::{Frame, Lang};
|
||||||
use crate::geom::{Dir, Em, Length};
|
use crate::geom::{Dir, Em, Length};
|
||||||
use crate::image::Image;
|
use crate::image::Image;
|
||||||
use crate::library::text::Lang;
|
|
||||||
|
|
||||||
/// Export a collection of frames into a PDF file.
|
/// Export a collection of frames into a PDF file.
|
||||||
///
|
///
|
||||||
|
61
src/frame.rs
61
src/frame.rs
@ -6,10 +6,9 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use crate::font::Font;
|
use crate::font::Font;
|
||||||
use crate::geom::{
|
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::image::Image;
|
||||||
use crate::library::text::Lang;
|
|
||||||
use crate::model::{Dict, Value};
|
use crate::model::{Dict, Value};
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
|
||||||
@ -397,6 +396,64 @@ pub struct Glyph {
|
|||||||
pub c: char,
|
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<Self> {
|
||||||
|
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<Self> {
|
||||||
|
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.
|
/// A link destination.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum Destination {
|
pub enum Destination {
|
||||||
|
26
src/lib.rs
26
src/lib.rs
@ -46,6 +46,7 @@ pub mod image;
|
|||||||
pub mod library;
|
pub mod library;
|
||||||
pub mod syntax;
|
pub mod syntax;
|
||||||
|
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use comemo::{Prehashed, Track};
|
use comemo::{Prehashed, Track};
|
||||||
@ -54,9 +55,9 @@ use crate::diag::{FileResult, SourceResult};
|
|||||||
use crate::font::{Font, FontBook};
|
use crate::font::{Font, FontBook};
|
||||||
use crate::frame::Frame;
|
use crate::frame::Frame;
|
||||||
use crate::model::StyleMap;
|
use crate::model::StyleMap;
|
||||||
use crate::model::{Route, Scope};
|
use crate::model::{Content, Route, Scope};
|
||||||
use crate::syntax::{Source, SourceId};
|
use crate::syntax::{Source, SourceId};
|
||||||
use crate::util::Buffer;
|
use crate::util::{Buffer, EcoString};
|
||||||
|
|
||||||
/// Typeset a source file into a collection of layouted frames.
|
/// Typeset a source file into a collection of layouted frames.
|
||||||
///
|
///
|
||||||
@ -105,6 +106,10 @@ pub struct Config {
|
|||||||
///
|
///
|
||||||
/// Default: Typst's standard library.
|
/// Default: Typst's standard library.
|
||||||
pub std: Scope,
|
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.
|
/// The default properties for page size, font selection and so on.
|
||||||
///
|
///
|
||||||
/// Default: Empty style map.
|
/// Default: Empty style map.
|
||||||
@ -115,8 +120,23 @@ impl Default for Config {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
root: PathBuf::new(),
|
root: PathBuf::new(),
|
||||||
std: library::new(),
|
std: library::scope(),
|
||||||
styles: StyleMap::new(),
|
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<EcoString>, bool) -> Content,
|
||||||
|
link: fn(EcoString) -> Content,
|
||||||
|
ref_: fn(EcoString) -> Content,
|
||||||
|
heading: fn(NonZeroUsize, Content) -> Content,
|
||||||
|
list_item: fn(Content) -> Content,
|
||||||
|
enum_item: fn(Option<usize>, Content) -> Content,
|
||||||
|
desc_item: fn(Content, Content) -> Content,
|
||||||
|
}
|
||||||
|
@ -4,10 +4,8 @@ mod hide;
|
|||||||
mod image;
|
mod image;
|
||||||
mod line;
|
mod line;
|
||||||
mod shape;
|
mod shape;
|
||||||
mod transform;
|
|
||||||
|
|
||||||
pub use self::image::*;
|
pub use self::image::*;
|
||||||
pub use hide::*;
|
pub use hide::*;
|
||||||
pub use line::*;
|
pub use line::*;
|
||||||
pub use shape::*;
|
pub use shape::*;
|
||||||
pub use transform::*;
|
|
||||||
|
@ -10,6 +10,7 @@ mod page;
|
|||||||
mod place;
|
mod place;
|
||||||
mod spacing;
|
mod spacing;
|
||||||
mod stack;
|
mod stack;
|
||||||
|
mod transform;
|
||||||
|
|
||||||
pub use align::*;
|
pub use align::*;
|
||||||
pub use columns::*;
|
pub use columns::*;
|
||||||
@ -21,3 +22,4 @@ pub use page::*;
|
|||||||
pub use place::*;
|
pub use place::*;
|
||||||
pub use spacing::*;
|
pub use spacing::*;
|
||||||
pub use stack::*;
|
pub use stack::*;
|
||||||
|
pub use transform::*;
|
||||||
|
@ -188,7 +188,7 @@ impl Marginal {
|
|||||||
Self::Content(content) => Some(content.clone()),
|
Self::Content(content) => Some(content.clone()),
|
||||||
Self::Func(func, span) => {
|
Self::Func(func, span) => {
|
||||||
let args = Args::new(*span, [Value::Int(page as i64)]);
|
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))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ use rex::render::{Backend, Cursor, Renderer};
|
|||||||
use crate::font::Font;
|
use crate::font::Font;
|
||||||
use crate::library::layout::BlockSpacing;
|
use crate::library::layout::BlockSpacing;
|
||||||
use crate::library::prelude::*;
|
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.
|
/// A piece of a mathematical formula.
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
@ -14,7 +14,7 @@ pub mod utility;
|
|||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
|
||||||
/// Construct a scope containing all standard library definitions.
|
/// Construct a scope containing all standard library definitions.
|
||||||
pub fn new() -> Scope {
|
pub fn scope() -> Scope {
|
||||||
let mut std = Scope::new();
|
let mut std = Scope::new();
|
||||||
|
|
||||||
// Text.
|
// Text.
|
||||||
@ -58,6 +58,9 @@ pub fn new() -> Scope {
|
|||||||
std.def_node::<layout::ColumnsNode>("columns");
|
std.def_node::<layout::ColumnsNode>("columns");
|
||||||
std.def_node::<layout::ColbreakNode>("colbreak");
|
std.def_node::<layout::ColbreakNode>("colbreak");
|
||||||
std.def_node::<layout::PlaceNode>("place");
|
std.def_node::<layout::PlaceNode>("place");
|
||||||
|
std.def_node::<layout::MoveNode>("move");
|
||||||
|
std.def_node::<layout::ScaleNode>("scale");
|
||||||
|
std.def_node::<layout::RotateNode>("rotate");
|
||||||
|
|
||||||
// Graphics.
|
// Graphics.
|
||||||
std.def_node::<graphics::ImageNode>("image");
|
std.def_node::<graphics::ImageNode>("image");
|
||||||
@ -66,9 +69,6 @@ pub fn new() -> Scope {
|
|||||||
std.def_node::<graphics::SquareNode>("square");
|
std.def_node::<graphics::SquareNode>("square");
|
||||||
std.def_node::<graphics::EllipseNode>("ellipse");
|
std.def_node::<graphics::EllipseNode>("ellipse");
|
||||||
std.def_node::<graphics::CircleNode>("circle");
|
std.def_node::<graphics::CircleNode>("circle");
|
||||||
std.def_node::<graphics::MoveNode>("move");
|
|
||||||
std.def_node::<graphics::ScaleNode>("scale");
|
|
||||||
std.def_node::<graphics::RotateNode>("rotate");
|
|
||||||
std.def_node::<graphics::HideNode>("hide");
|
std.def_node::<graphics::HideNode>("hide");
|
||||||
|
|
||||||
// Math.
|
// Math.
|
||||||
@ -142,3 +142,117 @@ pub fn new() -> Scope {
|
|||||||
|
|
||||||
std
|
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<Option<RawAlign>>) -> Self;
|
||||||
|
|
||||||
|
/// Pad this node at the sides.
|
||||||
|
fn padded(self, padding: Sides<Relative<RawLength>>) -> Self;
|
||||||
|
|
||||||
|
/// Transform this node's contents without affecting layout.
|
||||||
|
fn moved(self, delta: Spec<Relative<RawLength>>) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayoutNodeExt for LayoutNode {
|
||||||
|
fn aligned(self, aligns: Spec<Option<RawAlign>>) -> Self {
|
||||||
|
if aligns.any(Option::is_some) {
|
||||||
|
layout::AlignNode { aligns, child: self }.pack()
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
layout::PadNode { padding, child: self }.pack()
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn moved(self, delta: Spec<Relative<RawLength>>) -> Self {
|
||||||
|
if delta.any(|r| !r.is_zero()) {
|
||||||
|
layout::MoveNode { delta, child: self }.pack()
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,6 +9,7 @@ pub use std::sync::Arc;
|
|||||||
pub use comemo::Tracked;
|
pub use comemo::Tracked;
|
||||||
pub use typst_macros::node;
|
pub use typst_macros::node;
|
||||||
|
|
||||||
|
pub use super::{ContentExt, LayoutNodeExt, StyleMapExt};
|
||||||
pub use crate::diag::{
|
pub use crate::diag::{
|
||||||
with_alternative, At, FileError, FileResult, SourceError, SourceResult, StrResult,
|
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::syntax::{Span, Spanned};
|
||||||
pub use crate::util::EcoString;
|
pub use crate::util::EcoString;
|
||||||
pub use crate::World;
|
pub use crate::{RoleMap, World};
|
||||||
|
@ -328,7 +328,7 @@ impl Label {
|
|||||||
Self::Content(content) => content.clone(),
|
Self::Content(content) => content.clone(),
|
||||||
Self::Func(func, span) => {
|
Self::Func(func, span) => {
|
||||||
let args = Args::new(*span, [Value::Int(number as i64)]);
|
let args = Args::new(*span, [Value::Int(number as i64)]);
|
||||||
func.call_detached(world, args)?.display()
|
func.call_detached(world, args)?.display(world)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -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<Self> {
|
|
||||||
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<Self> {
|
|
||||||
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)")?,
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
//! Text handling and paragraph layout.
|
//! Text handling and paragraph layout.
|
||||||
|
|
||||||
mod deco;
|
mod deco;
|
||||||
mod lang;
|
|
||||||
mod link;
|
mod link;
|
||||||
mod par;
|
mod par;
|
||||||
mod quotes;
|
mod quotes;
|
||||||
@ -11,7 +10,6 @@ mod shaping;
|
|||||||
mod shift;
|
mod shift;
|
||||||
|
|
||||||
pub use deco::*;
|
pub use deco::*;
|
||||||
pub use lang::*;
|
|
||||||
pub use link::*;
|
pub use link::*;
|
||||||
pub use par::*;
|
pub use par::*;
|
||||||
pub use quotes::*;
|
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.
|
/// The direction of text and inline objects in their line.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct HorizontalDir(pub Dir);
|
pub struct HorizontalDir(pub Dir);
|
||||||
|
@ -114,64 +114,3 @@ impl<'a, T> Default for CollapsingBuilder<'a, T> {
|
|||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::library::layout::FlowChild;
|
|
||||||
use crate::library::prelude::*;
|
|
||||||
|
|
||||||
#[track_caller]
|
|
||||||
fn test<T>(builder: CollapsingBuilder<T>, 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()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,23 +1,24 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::iter::Sum;
|
use std::iter::Sum;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::{Add, AddAssign};
|
use std::ops::{Add, AddAssign};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use comemo::Tracked;
|
use comemo::Tracked;
|
||||||
use typed_arena::Arena;
|
use typed_arena::Arena;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Barrier, CollapsingBuilder, Interruption, Key, Layout, LayoutNode, Property, Show,
|
Barrier, CollapsingBuilder, Dict, Interruption, Key, Layout, LayoutNode, Property,
|
||||||
ShowNode, StyleEntry, StyleMap, StyleVecBuilder, Target,
|
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::layout::{FlowChild, FlowNode, PageNode, PlaceNode, Spacing};
|
||||||
use crate::library::prelude::*;
|
|
||||||
use crate::library::structure::{DocNode, ListItem, ListNode, DESC, ENUM, LIST};
|
use crate::library::structure::{DocNode, ListItem, ListNode, DESC, ENUM, LIST};
|
||||||
use crate::library::text::{
|
use crate::library::text::{ParChild, ParNode};
|
||||||
DecoNode, EmphNode, ParChild, ParNode, StrongNode, UNDERLINE,
|
|
||||||
};
|
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
@ -173,21 +174,6 @@ impl Content {
|
|||||||
self.clone().styled_with_entry(StyleEntry::Unguard(sel))
|
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::<UNDERLINE>(self))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add weak vertical spacing above and below the node.
|
/// Add weak vertical spacing above and below the node.
|
||||||
pub fn spaced(self, above: Option<Length>, below: Option<Length>) -> Self {
|
pub fn spaced(self, above: Option<Length>, below: Option<Length>) -> Self {
|
||||||
if above.is_none() && below.is_none() {
|
if above.is_none() && below.is_none() {
|
||||||
|
@ -141,7 +141,7 @@ fn eval_markup(
|
|||||||
ast::MarkupNode::Expr(ast::Expr::Wrap(wrap)) => {
|
ast::MarkupNode::Expr(ast::Expr::Wrap(wrap)) => {
|
||||||
let tail = eval_markup(vm, nodes)?;
|
let tail = eval_markup(vm, nodes)?;
|
||||||
vm.scopes.top.define(wrap.binding().take(), tail);
|
vm.scopes.top.define(wrap.binding().take(), tail);
|
||||||
wrap.body().eval(vm)?.display()
|
wrap.body().eval(vm)?.display(vm.world)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => node.eval(vm)?,
|
_ => node.eval(vm)?,
|
||||||
@ -181,7 +181,7 @@ impl Eval for ast::MarkupNode {
|
|||||||
Self::Desc(v) => v.eval(vm),
|
Self::Desc(v) => v.eval(vm),
|
||||||
Self::Label(v) => v.eval(vm),
|
Self::Label(v) => v.eval(vm),
|
||||||
Self::Ref(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;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Ok(Content::show(library::text::StrongNode(
|
Ok((vm.roles().strong)(self.body().eval(vm)?))
|
||||||
self.body().eval(vm)?,
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,34 +250,80 @@ impl Eval for ast::Emph {
|
|||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
Ok(Content::show(library::text::EmphNode(
|
Ok((vm.roles().emph)(self.body().eval(vm)?))
|
||||||
self.body().eval(vm)?,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eval for ast::Link {
|
|
||||||
type Output = Content;
|
|
||||||
|
|
||||||
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
|
||||||
Ok(Content::show(library::text::LinkNode::from_url(
|
|
||||||
self.url().clone(),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ast::Raw {
|
impl Eval for ast::Raw {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
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<Self::Output> {
|
||||||
|
Ok((vm.roles().link)(self.url().clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eval for ast::Label {
|
||||||
|
type Output = Content;
|
||||||
|
|
||||||
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let content = Content::show(library::text::RawNode {
|
Ok(Content::Empty)
|
||||||
text: self.text().clone(),
|
}
|
||||||
block: self.block(),
|
}
|
||||||
});
|
|
||||||
Ok(match self.lang() {
|
impl Eval for ast::Ref {
|
||||||
Some(_) => content.styled(library::text::RawNode::LANG, self.lang().cloned()),
|
type Output = Content;
|
||||||
None => content,
|
|
||||||
})
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
Ok((vm.roles().ref_)(self.get().clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eval for ast::Heading {
|
||||||
|
type Output = Content;
|
||||||
|
|
||||||
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
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<Self::Output> {
|
||||||
|
Ok((vm.roles().list_item)(self.body().eval(vm)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eval for ast::EnumItem {
|
||||||
|
type Output = Content;
|
||||||
|
|
||||||
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
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<Self::Output> {
|
||||||
|
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(),
|
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),
|
Content::Text(text) => library::math::MathNode::Atom(text),
|
||||||
_ => bail!(expr.span(), "expected 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<Self::Output> {
|
|
||||||
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<Self::Output> {
|
|
||||||
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<Self::Output> {
|
|
||||||
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<Self::Output> {
|
|
||||||
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<Self::Output> {
|
|
||||||
Ok(Content::Empty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eval for ast::Ref {
|
|
||||||
type Output = Content;
|
|
||||||
|
|
||||||
fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
|
|
||||||
Ok(Content::show(library::structure::RefNode(
|
|
||||||
self.get().clone(),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eval for ast::Expr {
|
impl Eval for ast::Expr {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
@ -530,7 +512,7 @@ fn eval_code(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tail = eval_code(vm, exprs)?.display();
|
let tail = eval_code(vm, exprs)?.display(vm.world);
|
||||||
Value::Content(tail.styled_with_map(styles))
|
Value::Content(tail.styled_with_map(styles))
|
||||||
}
|
}
|
||||||
ast::Expr::Show(show) => {
|
ast::Expr::Show(show) => {
|
||||||
@ -540,7 +522,7 @@ fn eval_code(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tail = eval_code(vm, exprs)?.display();
|
let tail = eval_code(vm, exprs)?.display(vm.world);
|
||||||
Value::Content(tail.styled_with_entry(entry))
|
Value::Content(tail.styled_with_entry(entry))
|
||||||
}
|
}
|
||||||
ast::Expr::Wrap(wrap) => {
|
ast::Expr::Wrap(wrap) => {
|
||||||
|
@ -8,19 +8,13 @@ use std::sync::Arc;
|
|||||||
use comemo::{Prehashed, Tracked};
|
use comemo::{Prehashed, Tracked};
|
||||||
|
|
||||||
use super::{Barrier, NodeId, Resolve, StyleChain, StyleEntry};
|
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::diag::SourceResult;
|
||||||
use crate::frame::{Element, Frame};
|
use crate::frame::{Element, Frame};
|
||||||
use crate::geom::{
|
use crate::geom::{Align, Geometry, Length, Paint, Point, Relative, Size, Spec, Stroke};
|
||||||
Align, Geometry, Length, Paint, Point, Relative, Sides, Size, Spec, Stroke,
|
|
||||||
};
|
|
||||||
use crate::library::graphics::MoveNode;
|
|
||||||
use crate::library::layout::{AlignNode, PadNode};
|
|
||||||
use crate::World;
|
use crate::World;
|
||||||
|
|
||||||
/// Layout content into a collection of pages.
|
/// Layout content into a collection of pages.
|
||||||
///
|
|
||||||
/// Relayouts until all pinned locations are converged.
|
|
||||||
#[comemo::memoize]
|
#[comemo::memoize]
|
||||||
pub fn layout(world: Tracked<dyn World>, content: &Content) -> SourceResult<Vec<Frame>> {
|
pub fn layout(world: Tracked<dyn World>, content: &Content) -> SourceResult<Vec<Frame>> {
|
||||||
let styles = StyleChain::with_root(&world.config().styles);
|
let styles = StyleChain::with_root(&world.config().styles);
|
||||||
@ -196,37 +190,6 @@ impl LayoutNode {
|
|||||||
pub fn stroked(self, stroke: Stroke) -> Self {
|
pub fn stroked(self, stroke: Stroke) -> Self {
|
||||||
StrokeNode { stroke, child: self }.pack()
|
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 {
|
impl Layout for LayoutNode {
|
||||||
|
@ -89,7 +89,7 @@ impl Recipe {
|
|||||||
Args::new(self.func.span, [arg()])
|
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.
|
/// What kind of structure the property interrupts.
|
||||||
|
@ -8,7 +8,6 @@ use comemo::Tracked;
|
|||||||
use super::{Barrier, Content, Key, Property, Recipe, Selector, Show, Target};
|
use super::{Barrier, Content, Key, Property, Recipe, Selector, Show, Target};
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::frame::Role;
|
use crate::frame::Role;
|
||||||
use crate::library::text::{FontFamily, TextNode};
|
|
||||||
use crate::util::ReadableTypeId;
|
use crate::util::ReadableTypeId;
|
||||||
use crate::World;
|
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.
|
/// Whether the map contains a style property for the given key.
|
||||||
pub fn contains<'a, K: Key<'a>>(&self, _: K) -> bool {
|
pub fn contains<'a, K: Key<'a>>(&self, _: K) -> bool {
|
||||||
self.0
|
self.0
|
||||||
|
@ -4,13 +4,14 @@ use std::fmt::{self, Debug, Formatter};
|
|||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use comemo::Tracked;
|
||||||
use siphasher::sip128::{Hasher128, SipHasher};
|
use siphasher::sip128::{Hasher128, SipHasher};
|
||||||
|
|
||||||
use super::{ops, Args, Array, Cast, Content, Dict, Func, Layout, RawLength, Str};
|
use super::{ops, Args, Array, Cast, Content, Dict, Func, Layout, RawLength, Str};
|
||||||
use crate::diag::StrResult;
|
use crate::diag::StrResult;
|
||||||
use crate::geom::{Angle, Color, Em, Fraction, Length, Ratio, Relative, RgbaColor};
|
use crate::geom::{Angle, Color, Em, Fraction, Length, Ratio, Relative, RgbaColor};
|
||||||
use crate::library::text::RawNode;
|
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
|
use crate::World;
|
||||||
|
|
||||||
/// A computational value.
|
/// A computational value.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -113,7 +114,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the display representation of the value.
|
/// Return the display representation of the value.
|
||||||
pub fn display(self) -> Content {
|
pub fn display(self, world: Tracked<dyn World>) -> Content {
|
||||||
match self {
|
match self {
|
||||||
Value::None => Content::new(),
|
Value::None => Content::new(),
|
||||||
Value::Int(v) => Content::Text(format_eco!("{}", v)),
|
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
|
// For values which can't be shown "naturally", we return the raw
|
||||||
// representation with typst code syntax highlighting.
|
// representation with typst code syntax highlighting.
|
||||||
v => Content::show(RawNode { text: v.repr().into(), block: false })
|
v => (world.config().roles.raw)(v.repr().into(), Some("typc".into()), false),
|
||||||
.styled(RawNode::LANG, Some("typc".into())),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use super::{Route, Scopes, Value};
|
|||||||
use crate::diag::{SourceError, StrResult};
|
use crate::diag::{SourceError, StrResult};
|
||||||
use crate::syntax::{SourceId, Span};
|
use crate::syntax::{SourceId, Span};
|
||||||
use crate::util::PathExt;
|
use crate::util::PathExt;
|
||||||
use crate::World;
|
use crate::{RoleMap, World};
|
||||||
|
|
||||||
/// A virtual machine.
|
/// A virtual machine.
|
||||||
pub struct Vm<'a> {
|
pub struct Vm<'a> {
|
||||||
@ -54,6 +54,11 @@ impl<'a> Vm<'a> {
|
|||||||
|
|
||||||
return Err("cannot access file system from here".into());
|
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.
|
/// A control flow event that occurred during evaluation.
|
||||||
|
@ -159,7 +159,7 @@ fn config() -> Config {
|
|||||||
styles.set(TextNode::SIZE, TextSize(Length::pt(10.0).into()));
|
styles.set(TextNode::SIZE, TextSize(Length::pt(10.0).into()));
|
||||||
|
|
||||||
// Hook up helpers into the global scope.
|
// 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("conifer", RgbaColor::new(0x9f, 0xEB, 0x52, 0xFF));
|
||||||
std.define("forest", RgbaColor::new(0x43, 0xA1, 0x27, 0xFF));
|
std.define("forest", RgbaColor::new(0x43, 0xA1, 0x27, 0xFF));
|
||||||
std.def_fn("test", move |_, args| {
|
std.def_fn("test", move |_, args| {
|
||||||
@ -182,7 +182,12 @@ fn config() -> Config {
|
|||||||
Ok(Value::None)
|
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.
|
/// A world that provides access to the tests environment.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user