summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-01-15 16:53:02 +0100
committerLaurenz <laurmaedje@gmail.com>2021-01-15 16:53:02 +0100
commitd763f0f5a6a700352ee8926c15c8e58624f705c9 (patch)
treed287edfdab9793a796404516c7313689e4e69964
parent0f0416054f263b80ccec1a463ce4ab20913bdf71 (diff)
Split state and scopes, less ref-counting 🔀
-rw-r--r--bench/src/bench.rs31
-rw-r--r--src/env.rs5
-rw-r--r--src/eval/call.rs15
-rw-r--r--src/eval/context.rs14
-rw-r--r--src/eval/mod.rs22
-rw-r--r--src/eval/scope.rs41
-rw-r--r--src/eval/state.rs37
-rw-r--r--src/font.rs18
-rw-r--r--src/layout/mod.rs10
-rw-r--r--src/layout/text.rs3
-rw-r--r--src/lib.rs15
-rw-r--r--src/library/insert.rs6
-rw-r--r--src/library/mod.rs7
-rw-r--r--src/library/style.rs5
-rw-r--r--src/main.rs20
-rw-r--r--tests/typeset.rs32
16 files changed, 168 insertions, 113 deletions
diff --git a/bench/src/bench.rs b/bench/src/bench.rs
index 58ecf9fd..e1ae838a 100644
--- a/bench/src/bench.rs
+++ b/bench/src/bench.rs
@@ -1,14 +1,12 @@
-use std::cell::RefCell;
-use std::rc::Rc;
-
use criterion::{criterion_group, criterion_main, Criterion};
-use fontdock::fs::{FsIndex, FsSource};
+use fontdock::fs::FsIndex;
use typst::env::{Env, ResourceLoader};
use typst::eval::{eval, State};
use typst::export::pdf;
-use typst::font::FontLoader;
+use typst::font::FsIndexExt;
use typst::layout::layout;
+use typst::library;
use typst::parse::parse;
use typst::typeset;
@@ -25,25 +23,24 @@ fn benchmarks(c: &mut Criterion) {
let mut index = FsIndex::new();
index.search_dir(FONT_DIR);
- let (files, descriptors) = index.into_vecs();
- let env = Rc::new(RefCell::new(Env {
- fonts: FontLoader::new(Box::new(FsSource::new(files)), descriptors),
+ let mut env = Env {
+ fonts: index.into_dynamic_loader(),
resources: ResourceLoader::new(),
- }));
+ };
- // Prepare intermediate results and run warm.
+ let scope = library::new();
let state = State::default();
+
+ // Prepare intermediate results and run warm.
let syntax_tree = parse(COMA).output;
- let layout_tree = eval(&syntax_tree, Rc::clone(&env), state.clone()).output;
- let frames = layout(&layout_tree, Rc::clone(&env));
+ let layout_tree = eval(&syntax_tree, &mut env, &scope, state.clone()).output;
+ let frames = layout(&layout_tree, &mut env);
// Bench!
bench!("parse-coma": parse(COMA));
- bench!("eval-coma": eval(&syntax_tree, Rc::clone(&env), state.clone()));
- bench!("layout-coma": layout(&layout_tree, Rc::clone(&env)));
- bench!("typeset-coma": typeset(COMA, Rc::clone(&env), state.clone()));
-
- let env = env.borrow();
+ bench!("eval-coma": eval(&syntax_tree, &mut env, &scope, state.clone()));
+ bench!("layout-coma": layout(&layout_tree, &mut env));
+ bench!("typeset-coma": typeset(COMA, &mut env, &scope, state.clone()));
bench!("export-pdf-coma": pdf::export(&frames, &env));
}
diff --git a/src/env.rs b/src/env.rs
index 58c44a5f..5746dd2f 100644
--- a/src/env.rs
+++ b/src/env.rs
@@ -1,22 +1,17 @@
//! Environment interactions.
use std::any::Any;
-use std::cell::RefCell;
use std::collections::{hash_map::Entry, HashMap};
use std::fmt::{self, Debug, Formatter};
use std::fs;
use std::io::Cursor;
use std::path::{Path, PathBuf};
-use std::rc::Rc;
use image::io::Reader as ImageReader;
use image::{DynamicImage, GenericImageView, ImageFormat};
use crate::font::FontLoader;
-/// A reference-counted shared environment.
-pub type SharedEnv = Rc<RefCell<Env>>;
-
/// Encapsulates all environment dependencies (fonts, resources).
#[derive(Debug)]
pub struct Env {
diff --git a/src/eval/call.rs b/src/eval/call.rs
index f47ee847..8e75f17c 100644
--- a/src/eval/call.rs
+++ b/src/eval/call.rs
@@ -8,7 +8,7 @@ impl Eval for Spanned<&ExprCall> {
let name = &self.v.name.v;
let span = self.v.name.span;
- if let Some(value) = ctx.state.scope.get(name) {
+ if let Some(value) = ctx.scopes.get(name) {
if let Value::Func(func) = value {
let func = func.clone();
ctx.deco(Deco::Resolved.with_span(span));
@@ -90,10 +90,10 @@ impl Args {
}
/// Filter out and remove all convertible positional arguments.
- pub fn filter<'a, T>(
+ pub fn filter<'a, 'b: 'a, T>(
&'a mut self,
- ctx: &'a mut EvalContext,
- ) -> impl Iterator<Item = T> + 'a
+ ctx: &'a mut EvalContext<'b>,
+ ) -> impl Iterator<Item = T> + Captures<'a> + Captures<'b>
where
T: Cast<Spanned<Value>>,
{
@@ -130,6 +130,13 @@ impl Args {
}
}
+// This is a workaround because `-> impl Trait + 'a + 'b` does not work.
+//
+// See also: https://github.com/rust-lang/rust/issues/49431
+#[doc(hidden)]
+pub trait Captures<'a> {}
+impl<'a, T: ?Sized> Captures<'a> for T {}
+
/// Cast the value into `T`, generating an error if the conversion fails.
fn cast<T>(ctx: &mut EvalContext, value: Spanned<Value>) -> Option<T>
where
diff --git a/src/eval/context.rs b/src/eval/context.rs
index 1e09aaaf..a998bbdc 100644
--- a/src/eval/context.rs
+++ b/src/eval/context.rs
@@ -6,7 +6,6 @@ use fontdock::FontStyle;
use super::*;
use crate::diag::Diag;
use crate::diag::{Deco, Feedback, Pass};
-use crate::env::SharedEnv;
use crate::geom::{ChildAlign, Dir, Gen, LayoutDirs, Length, Linear, Sides, Size};
use crate::layout::{
Expansion, Node, NodePad, NodePages, NodePar, NodeSpacing, NodeStack, NodeText, Tree,
@@ -14,9 +13,11 @@ use crate::layout::{
/// The context for evaluation.
#[derive(Debug)]
-pub struct EvalContext {
+pub struct EvalContext<'a> {
/// The environment from which resources are gathered.
- pub env: SharedEnv,
+ pub env: &'a mut Env,
+ /// The active scopes.
+ pub scopes: Scopes<'a>,
/// The active evaluation state.
pub state: State,
/// The accumulated feedback.
@@ -34,11 +35,12 @@ pub struct EvalContext {
inner: Vec<Node>,
}
-impl EvalContext {
- /// Create a new evaluation context with a base state.
- pub fn new(env: SharedEnv, state: State) -> Self {
+impl<'a> EvalContext<'a> {
+ /// Create a new evaluation context with a base state and scope.
+ pub fn new(env: &'a mut Env, scope: &'a Scope, state: State) -> Self {
Self {
env,
+ scopes: Scopes::new(scope),
state,
groups: vec![],
inner: vec![],
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 9abc1074..efc77f69 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -18,17 +18,23 @@ use std::rc::Rc;
use crate::color::Color;
use crate::diag::Pass;
-use crate::env::SharedEnv;
+use crate::env::Env;
use crate::geom::{Angle, Length, Relative, Spec};
use crate::layout::{self, Expansion, NodeSpacing, NodeStack};
use crate::syntax::*;
/// Evaluate a syntax tree into a layout tree.
///
-/// The given `state` is the base state that may be updated over the course of
-/// evaluation.
-pub fn eval(tree: &Tree, env: SharedEnv, state: State) -> Pass<layout::Tree> {
- let mut ctx = EvalContext::new(env, state);
+/// The `state` is the base state that may be updated over the course of
+/// evaluation. The `scope` similarly consists of the base definitions that are
+/// present from the beginning (typically, the standard library).
+pub fn eval(
+ tree: &Tree,
+ env: &mut Env,
+ scope: &Scope,
+ state: State,
+) -> Pass<layout::Tree> {
+ let mut ctx = EvalContext::new(env, scope, state);
ctx.start_page_group(Softness::Hard);
tree.eval(&mut ctx);
ctx.end_page_group(|s| s == Softness::Hard);
@@ -118,7 +124,7 @@ impl Eval for Spanned<&NodeRaw> {
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
let prev = Rc::clone(&ctx.state.font.families);
- let families = Rc::make_mut(&mut ctx.state.font.families);
+ let families = ctx.state.font.families_mut();
families.list.insert(0, "monospace".to_string());
families.flatten();
@@ -151,7 +157,7 @@ impl Eval for Spanned<&Expr> {
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
match self.v {
Expr::None => Value::None,
- Expr::Ident(v) => match ctx.state.scope.get(v) {
+ Expr::Ident(v) => match ctx.scopes.get(v) {
Some(value) => value.clone(),
None => {
ctx.diag(error!(self.span, "unknown variable"));
@@ -179,7 +185,7 @@ impl Eval for Spanned<&Expr> {
Some(expr) => expr.as_ref().eval(ctx),
None => Value::None,
};
- Rc::make_mut(&mut ctx.state.scope).set(v.pat.v.as_str(), value);
+ ctx.scopes.define(v.pat.v.as_str(), value);
Value::None
}
}
diff --git a/src/eval/scope.rs b/src/eval/scope.rs
index dd7cc1da..62ee7e40 100644
--- a/src/eval/scope.rs
+++ b/src/eval/scope.rs
@@ -1,27 +1,58 @@
use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter};
+use std::iter;
use super::Value;
-/// A map from identifiers to values.
+/// A hierarchy of scopes.
+#[derive(Debug, Clone, PartialEq)]
+pub struct Scopes<'a> {
+ /// The active scope.
+ top: Scope,
+ /// The stack of lower scopes.
+ scopes: Vec<Scope>,
+ /// The base scope.
+ base: &'a Scope,
+}
+
+impl<'a> Scopes<'a> {
+ /// Create a new hierarchy of scopes.
+ pub fn new(base: &'a Scope) -> Self {
+ Self { top: Scope::new(), scopes: vec![], base }
+ }
+
+ /// Look up the value of a variable in the scopes.
+ pub fn get(&self, var: &str) -> Option<&Value> {
+ iter::once(&self.top)
+ .chain(&self.scopes)
+ .chain(iter::once(self.base))
+ .find_map(|scope| scope.get(var))
+ }
+
+ /// Define a variable in the active scope.
+ pub fn define(&mut self, var: impl Into<String>, value: impl Into<Value>) {
+ self.top.set(var, value);
+ }
+}
+
+/// A map from variable names to values.
#[derive(Default, Clone, PartialEq)]
pub struct Scope {
values: HashMap<String, Value>,
}
impl Scope {
- // Create a new empty scope with a fallback function that is invoked when no
- // match is found.
+ // Create a new empty scope.
pub fn new() -> Self {
Self::default()
}
- /// Return the value of the given variable.
+ /// Look up the value of a variable.
pub fn get(&self, var: &str) -> Option<&Value> {
self.values.get(var)
}
- /// Store the value for the given variable.
+ /// Store the value for a variable.
pub fn set(&mut self, var: impl Into<String>, value: impl Into<Value>) {
self.values.insert(var.into(), value.into());
}
diff --git a/src/eval/state.rs b/src/eval/state.rs
index ce6bd009..a88dfd07 100644
--- a/src/eval/state.rs
+++ b/src/eval/state.rs
@@ -2,7 +2,6 @@ use std::rc::Rc;
use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight};
-use super::Scope;
use crate::geom::{
Align, ChildAlign, Dir, LayoutDirs, Length, Linear, Relative, Sides, Size, Spec,
};
@@ -12,14 +11,12 @@ use crate::paper::{Paper, PaperClass, PAPER_A4};
/// The evaluation state.
#[derive(Debug, Clone, PartialEq)]
pub struct State {
- /// The scope that contains variable definitions.
- pub scope: Rc<Scope>,
/// The current page state.
- pub page: StatePage,
+ pub page: PageSettings,
/// The current paragraph state.
- pub par: StatePar,
+ pub par: ParSettings,
/// The current font state.
- pub font: StateFont,
+ pub font: FontSettings,
/// The current directions.
pub dirs: LayoutDirs,
/// The current alignments.
@@ -29,10 +26,9 @@ pub struct State {
impl Default for State {
fn default() -> Self {
Self {
- scope: Rc::new(crate::library::_std()),
- page: StatePage::default(),
- par: StatePar::default(),
- font: StateFont::default(),
+ page: PageSettings::default(),
+ par: ParSettings::default(),
+ font: FontSettings::default(),
dirs: LayoutDirs::new(Dir::TTB, Dir::LTR),
align: ChildAlign::new(Align::Start, Align::Start),
}
@@ -41,7 +37,7 @@ impl Default for State {
/// Defines page properties.
#[derive(Debug, Copy, Clone, PartialEq)]
-pub struct StatePage {
+pub struct PageSettings {
/// The class of this page.
pub class: PaperClass,
/// The width and height of the page.
@@ -53,7 +49,7 @@ pub struct StatePage {
pub margins: Sides<Option<Linear>>,
}
-impl StatePage {
+impl PageSettings {
/// The default page style for the given paper.
pub fn new(paper: Paper) -> Self {
Self {
@@ -76,7 +72,7 @@ impl StatePage {
}
}
-impl Default for StatePage {
+impl Default for PageSettings {
fn default() -> Self {
Self::new(PAPER_A4)
}
@@ -84,7 +80,7 @@ impl Default for StatePage {
/// Defines paragraph properties.
#[derive(Debug, Copy, Clone, PartialEq)]
-pub struct StatePar {
+pub struct ParSettings {
/// The spacing between words (dependent on scaled font size).
pub word_spacing: Linear,
/// The spacing between lines (dependent on scaled font size).
@@ -93,7 +89,7 @@ pub struct StatePar {
pub par_spacing: Linear,
}
-impl Default for StatePar {
+impl Default for ParSettings {
fn default() -> Self {
Self {
word_spacing: Relative::new(0.25).into(),
@@ -105,7 +101,7 @@ impl Default for StatePar {
/// Defines font properties.
#[derive(Debug, Clone, PartialEq)]
-pub struct StateFont {
+pub struct FontSettings {
/// A tree of font family names and generic class names.
pub families: Rc<FallbackTree>,
/// The selected font variant.
@@ -122,14 +118,19 @@ pub struct StateFont {
pub emph: bool,
}
-impl StateFont {
+impl FontSettings {
+ /// Access the `families` mutably.
+ pub fn families_mut(&mut self) -> &mut FallbackTree {
+ Rc::make_mut(&mut self.families)
+ }
+
/// The absolute font size.
pub fn font_size(&self) -> Length {
self.scale.resolve(self.size)
}
}
-impl Default for StateFont {
+impl Default for FontSettings {
fn default() -> Self {
Self {
/// The default tree of font fallbacks.
diff --git a/src/font.rs b/src/font.rs
index 40ec5918..01ea48c0 100644
--- a/src/font.rs
+++ b/src/font.rs
@@ -3,6 +3,9 @@
use fontdock::{ContainsChar, FaceFromVec, FontSource};
use ttf_parser::Face;
+#[cfg(feature = "fs")]
+use fontdock::fs::{FsIndex, FsSource};
+
/// A font loader that is backed by a dynamic source.
pub type FontLoader = fontdock::FontLoader<Box<dyn FontSource<Face = FaceBuf>>>;
@@ -47,3 +50,18 @@ impl ContainsChar for FaceBuf {
self.get().glyph_index(c).is_some()
}
}
+
+#[cfg(feature = "fs")]
+pub trait FsIndexExt {
+ /// Create a font loader backed by a boxed [`FsSource`] which serves all
+ /// indexed font faces.
+ fn into_dynamic_loader(self) -> FontLoader;
+}
+
+#[cfg(feature = "fs")]
+impl FsIndexExt for FsIndex {
+ fn into_dynamic_loader(self) -> FontLoader {
+ let (files, descriptors) = self.into_vecs();
+ FontLoader::new(Box::new(FsSource::new(files)), descriptors)
+ }
+}
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 714bac4b..44960de7 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -8,7 +8,7 @@ mod spacing;
mod stack;
mod text;
-use crate::env::{ResourceId, SharedEnv};
+use crate::env::{Env, ResourceId};
use crate::geom::*;
use crate::shaping::Shaped;
@@ -21,7 +21,7 @@ pub use stack::*;
pub use text::*;
/// Layout a tree into a collection of frames.
-pub fn layout(tree: &Tree, env: SharedEnv) -> Vec<Frame> {
+pub fn layout(tree: &Tree, env: &mut Env) -> Vec<Frame> {
tree.layout(&mut LayoutContext { env })
}
@@ -65,10 +65,10 @@ pub trait Layout {
}
/// The context for layouting.
-#[derive(Debug, Clone)]
-pub struct LayoutContext {
+#[derive(Debug)]
+pub struct LayoutContext<'a> {
/// The environment from which fonts are gathered.
- pub env: SharedEnv,
+ pub env: &'a mut Env,
}
/// A collection of areas to layout into.
diff --git a/src/layout/text.rs b/src/layout/text.rs
index cfd83372..ee85ee17 100644
--- a/src/layout/text.rs
+++ b/src/layout/text.rs
@@ -25,13 +25,12 @@ pub struct NodeText {
impl Layout for NodeText {
fn layout(&self, ctx: &mut LayoutContext, _: &Areas) -> Layouted {
- let mut env = ctx.env.borrow_mut();
Layouted::Frame(
shaping::shape(
&self.text,
self.dir,
self.font_size,
- &mut env.fonts,
+ &mut ctx.env.fonts,
&self.families,
self.variant,
),
diff --git a/src/lib.rs b/src/lib.rs
index 05cc0569..39ca47a3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -42,18 +42,21 @@ pub mod pretty;
pub mod shaping;
pub mod syntax;
-use std::rc::Rc;
-
use crate::diag::{Feedback, Pass};
-use crate::env::SharedEnv;
-use crate::eval::State;
+use crate::env::Env;
+use crate::eval::{Scope, State};
use crate::layout::Frame;
/// Process _Typst_ source code directly into a collection of frames.
-pub fn typeset(src: &str, env: SharedEnv, state: State) -> Pass<Vec<Frame>> {
+pub fn typeset(
+ src: &str,
+ env: &mut Env,
+ scope: &Scope,
+ state: State,
+) -> Pass<Vec<Frame>> {
let Pass { output: syntax_tree, feedback: f1 } = parse::parse(src);
let Pass { output: layout_tree, feedback: f2 } =
- eval::eval(&syntax_tree, Rc::clone(&env), state);
+ eval::eval(&syntax_tree, env, scope, state);
let frames = layout::layout(&layout_tree, env);
Pass::new(frames, Feedback::join(f1, f2))
}
diff --git a/src/library/insert.rs b/src/library/insert.rs
index 51cbbf52..58e8a11c 100644
--- a/src/library/insert.rs
+++ b/src/library/insert.rs
@@ -16,12 +16,9 @@ pub fn image(ctx: &mut EvalContext, args: &mut Args) -> Value {
let height = args.get(ctx, "height");
if let Some(path) = path {
- let mut env = ctx.env.borrow_mut();
- let loaded = env.resources.load(path.v, ImageResource::parse);
-
+ let loaded = ctx.env.resources.load(path.v, ImageResource::parse);
if let Some((res, img)) = loaded {
let dimensions = img.buf.dimensions();
- drop(env);
ctx.push(NodeImage {
res,
dimensions,
@@ -30,7 +27,6 @@ pub fn image(ctx: &mut EvalContext, args: &mut Args) -> Value {
align: ctx.state.align,
});
} else {
- drop(env);
ctx.diag(error!(path.span, "failed to load image"));
}
}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index f9047fc6..5c4e774c 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -1,4 +1,7 @@
//! The standard library.
+//!
+//! Call [`new`] to obtain a [`Scope`] containing all standard library
+//! definitions.
mod extend;
mod insert;
@@ -15,8 +18,8 @@ use fontdock::{FontStretch, FontStyle, FontWeight};
use crate::eval::{Scope, ValueAny, ValueFunc};
use crate::geom::Dir;
-/// The scope containing the standard library.
-pub fn _std() -> Scope {
+/// Construct a scope containing all standard library definitions.
+pub fn new() -> Scope {
let mut std = Scope::new();
macro_rules! set {
(func: $name:expr, $func:expr) => {
diff --git a/src/library/style.rs b/src/library/style.rs
index 7b2772cc..2e348440 100644
--- a/src/library/style.rs
+++ b/src/library/style.rs
@@ -1,5 +1,4 @@
use std::fmt::{self, Display, Formatter};
-use std::rc::Rc;
use fontdock::{FontStretch, FontStyle, FontWeight};
@@ -69,7 +68,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> Value {
let list: Vec<_> = args.filter::<FontFamily>(ctx).map(|f| f.to_string()).collect();
if !list.is_empty() {
- let families = Rc::make_mut(&mut ctx.state.font.families);
+ let families = ctx.state.font.families_mut();
families.list = list;
families.flatten();
}
@@ -89,7 +88,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> Value {
for variant in FontFamily::VARIANTS {
if let Some(FontFamilies(list)) = args.get(ctx, variant.as_str()) {
let strings = list.into_iter().map(|f| f.to_string()).collect();
- let families = Rc::make_mut(&mut ctx.state.font.families);
+ let families = ctx.state.font.families_mut();
families.update_class_list(variant.to_string(), strings);
families.flatten();
}
diff --git a/src/main.rs b/src/main.rs
index 4746bc0c..07eb673a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,16 +1,15 @@
-use std::cell::RefCell;
use std::fs;
use std::path::{Path, PathBuf};
-use std::rc::Rc;
use anyhow::{anyhow, bail, Context};
-use fontdock::fs::{FsIndex, FsSource};
+use fontdock::fs::FsIndex;
use typst::diag::{Feedback, Pass};
use typst::env::{Env, ResourceLoader};
use typst::eval::State;
use typst::export::pdf;
-use typst::font::FontLoader;
+use typst::font::FsIndexExt;
+use typst::library;
use typst::parse::LineMap;
use typst::typeset;
@@ -41,17 +40,18 @@ fn main() -> anyhow::Result<()> {
index.search_dir("fonts");
index.search_system();
- let (files, descriptors) = index.into_vecs();
- let env = Rc::new(RefCell::new(Env {
- fonts: FontLoader::new(Box::new(FsSource::new(files)), descriptors),
+ let mut env = Env {
+ fonts: index.into_dynamic_loader(),
resources: ResourceLoader::new(),
- }));
+ };
+ let scope = library::new();
let state = State::default();
+
let Pass {
output: frames,
feedback: Feedback { mut diags, .. },
- } = typeset(&src, Rc::clone(&env), state);
+ } = typeset(&src, &mut env, &scope, state);
if !diags.is_empty() {
diags.sort();
@@ -72,7 +72,7 @@ fn main() -> anyhow::Result<()> {
}
}
- let pdf_data = pdf::export(&frames, &env.borrow());
+ let pdf_data = pdf::export(&frames, &env);
fs::write(&dest_path, pdf_data).context("Failed to write PDF file.")?;
Ok(())
diff --git a/tests/typeset.rs b/tests/typeset.rs
index 980e3a6c..5777950f 100644
--- a/tests/typeset.rs
+++ b/tests/typeset.rs
@@ -1,11 +1,9 @@
-use std::cell::RefCell;
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::path::Path;
-use std::rc::Rc;
-use fontdock::fs::{FsIndex, FsSource};
+use fontdock::fs::FsIndex;
use image::{GenericImageView, Rgba};
use tiny_skia::{
Canvas, Color, ColorU8, FillRule, FilterQuality, Paint, PathBuilder, Pattern, Pixmap,
@@ -15,12 +13,13 @@ use ttf_parser::OutlineBuilder;
use walkdir::WalkDir;
use typst::diag::{Diag, Feedback, Level, Pass};
-use typst::env::{Env, ImageResource, ResourceLoader, SharedEnv};
+use typst::env::{Env, ImageResource, ResourceLoader};
use typst::eval::{Args, EvalContext, Scope, State, Value, ValueFunc};
use typst::export::pdf;
-use typst::font::FontLoader;
+use typst::font::FsIndexExt;
use typst::geom::{Length, Point, Sides, Size, Spec};
use typst::layout::{Element, Expansion, Frame, Image};
+use typst::library;
use typst::parse::{LineMap, Scanner};
use typst::pretty::{Pretty, Printer};
use typst::shaping::Shaped;
@@ -61,11 +60,10 @@ fn main() {
let mut index = FsIndex::new();
index.search_dir(FONT_DIR);
- let (files, descriptors) = index.into_vecs();
- let env = Rc::new(RefCell::new(Env {
- fonts: FontLoader::new(Box::new(FsSource::new(files)), descriptors),
+ let mut env = Env {
+ fonts: index.into_dynamic_loader(),
resources: ResourceLoader::new(),
- }));
+ };
let playground = Path::new("playground.typ");
if playground.exists() && filtered.is_empty() {
@@ -74,7 +72,7 @@ fn main() {
Path::new("playground.png"),
Path::new("playground.pdf"),
None,
- &env,
+ &mut env,
);
}
@@ -84,7 +82,7 @@ fn main() {
let png_path = Path::new(PNG_DIR).join(&relative).with_extension("png");
let pdf_path = Path::new(PDF_DIR).join(&relative).with_extension("pdf");
let ref_path = Path::new(REF_DIR).join(&relative).with_extension("png");
- ok &= test(&src_path, &png_path, &pdf_path, Some(&ref_path), &env);
+ ok &= test(&src_path, &png_path, &pdf_path, Some(&ref_path), &mut env);
}
if !ok {
@@ -127,7 +125,7 @@ fn test(
png_path: &Path,
pdf_path: &Path,
ref_path: Option<&Path>,
- env: &SharedEnv,
+ env: &mut Env,
) -> bool {
let name = src_path.strip_prefix(TYP_DIR).unwrap_or(src_path);
println!("Testing {}", name.display());
@@ -143,7 +141,6 @@ fn test(
frames.extend(part_frames);
}
- let env = env.borrow();
if !frames.is_empty() {
let pdf_data = pdf::export(&frames, &env);
fs::create_dir_all(&pdf_path.parent().unwrap()).unwrap();
@@ -173,23 +170,24 @@ fn test(
ok
}
-fn test_part(i: usize, src: &str, env: &SharedEnv) -> (bool, Vec<Frame>) {
+fn test_part(i: usize, src: &str, env: &mut Env) -> (bool, Vec<Frame>) {
let map = LineMap::new(src);
let (compare_ref, ref_diags) = parse_metadata(src, &map);
- let mut state = State::default();
+ let mut scope = library::new();
+ register_helpers(&mut scope);
// We want to have "unbounded" pages, so we allow them to be infinitely
// large and fit them to match their content.
+ let mut state = State::default();
state.page.size = Size::new(Length::pt(120.0), Length::raw(f64::INFINITY));
state.page.expand = Spec::new(Expansion::Fill, Expansion::Fit);
state.page.margins = Sides::uniform(Some(Length::pt(10.0).into()));
- register_helpers(Rc::make_mut(&mut state.scope));
let Pass {
output: mut frames,
feedback: Feedback { mut diags, .. },
- } = typeset(&src, Rc::clone(env), state);
+ } = typeset(&src, env, &scope, state);
if !compare_ref {
frames.clear();