mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Fs builder methods + tidy up
This commit is contained in:
parent
9488b1b850
commit
b0e5212973
@ -4,6 +4,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
|
use typst::diag::Pass;
|
||||||
use typst::eval::{eval, Module};
|
use typst::eval::{eval, Module};
|
||||||
use typst::exec::exec;
|
use typst::exec::exec;
|
||||||
use typst::export::pdf;
|
use typst::export::pdf;
|
||||||
@ -18,18 +19,13 @@ const TYP_DIR: &str = "../tests/typ";
|
|||||||
const CASES: &[&str] = &["coma.typ", "text/basic.typ"];
|
const CASES: &[&str] = &["coma.typ", "text/basic.typ"];
|
||||||
|
|
||||||
fn benchmarks(c: &mut Criterion) {
|
fn benchmarks(c: &mut Criterion) {
|
||||||
let loader = {
|
let loader = FsLoader::new().with_path(FONT_DIR).wrap();
|
||||||
let mut loader = FsLoader::new();
|
|
||||||
loader.search_path(FONT_DIR);
|
|
||||||
Rc::new(loader)
|
|
||||||
};
|
|
||||||
|
|
||||||
let ctx = Rc::new(RefCell::new(Context::new(loader.clone())));
|
let ctx = Rc::new(RefCell::new(Context::new(loader.clone())));
|
||||||
|
|
||||||
for case in CASES {
|
for case in CASES {
|
||||||
let path = Path::new(TYP_DIR).join(case);
|
let path = Path::new(TYP_DIR).join(case);
|
||||||
let name = path.file_stem().unwrap().to_string_lossy();
|
let name = path.file_stem().unwrap().to_string_lossy();
|
||||||
let src_id = loader.resolve_path(&path).unwrap();
|
let src_id = loader.resolve(&path).unwrap();
|
||||||
let src = std::fs::read_to_string(&path).unwrap();
|
let src = std::fs::read_to_string(&path).unwrap();
|
||||||
let case = Case::new(src_id, src, ctx.clone());
|
let case = Case::new(src_id, src, ctx.clone());
|
||||||
|
|
||||||
@ -115,21 +111,24 @@ impl Case {
|
|||||||
parse(&self.src).output
|
parse(&self.src).output
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval(&self) -> Module {
|
fn eval(&self) -> Pass<Module> {
|
||||||
let mut borrowed = self.ctx.borrow_mut();
|
eval(
|
||||||
eval(&mut borrowed, self.src_id, Rc::clone(&self.ast)).output
|
&mut self.ctx.borrow_mut(),
|
||||||
|
self.src_id,
|
||||||
|
Rc::clone(&self.ast),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(&self) -> LayoutTree {
|
fn exec(&self) -> Pass<LayoutTree> {
|
||||||
exec(&mut self.ctx.borrow_mut(), &self.module.template).output
|
exec(&mut self.ctx.borrow_mut(), &self.module.template)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&self) -> Vec<Rc<Frame>> {
|
fn layout(&self) -> Vec<Rc<Frame>> {
|
||||||
layout(&mut self.ctx.borrow_mut(), &self.tree)
|
layout(&mut self.ctx.borrow_mut(), &self.tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn typeset(&self) -> Vec<Rc<Frame>> {
|
fn typeset(&self) -> Pass<Vec<Rc<Frame>>> {
|
||||||
self.ctx.borrow_mut().typeset(self.src_id, &self.src).output
|
self.ctx.borrow_mut().typeset(self.src_id, &self.src)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pdf(&self) -> Vec<u8> {
|
fn pdf(&self) -> Vec<u8> {
|
||||||
|
@ -36,8 +36,8 @@ use crate::syntax::*;
|
|||||||
use crate::Context;
|
use crate::Context;
|
||||||
|
|
||||||
/// Evaluate a parsed source file into a module.
|
/// Evaluate a parsed source file into a module.
|
||||||
pub fn eval(ctx: &mut Context, location: FileId, ast: Rc<SyntaxTree>) -> Pass<Module> {
|
pub fn eval(ctx: &mut Context, file: FileId, ast: Rc<SyntaxTree>) -> Pass<Module> {
|
||||||
let mut ctx = EvalContext::new(ctx, location);
|
let mut ctx = EvalContext::new(ctx, file);
|
||||||
let template = ast.eval(&mut ctx);
|
let template = ast.eval(&mut ctx);
|
||||||
let module = Module { scope: ctx.scopes.top, template };
|
let module = Module { scope: ctx.scopes.top, template };
|
||||||
Pass::new(module, ctx.diags)
|
Pass::new(module, ctx.diags)
|
||||||
@ -70,13 +70,13 @@ pub struct EvalContext<'a> {
|
|||||||
|
|
||||||
impl<'a> EvalContext<'a> {
|
impl<'a> EvalContext<'a> {
|
||||||
/// Create a new evaluation context.
|
/// Create a new evaluation context.
|
||||||
pub fn new(ctx: &'a mut Context, location: FileId) -> Self {
|
pub fn new(ctx: &'a mut Context, file: FileId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
loader: ctx.loader.as_ref(),
|
loader: ctx.loader.as_ref(),
|
||||||
images: &mut ctx.images,
|
images: &mut ctx.images,
|
||||||
scopes: Scopes::new(Some(&ctx.std)),
|
scopes: Scopes::new(Some(&ctx.std)),
|
||||||
diags: DiagSet::new(),
|
diags: DiagSet::new(),
|
||||||
route: vec![location],
|
route: vec![file],
|
||||||
modules: HashMap::new(),
|
modules: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,8 +96,8 @@ impl Context {
|
|||||||
|
|
||||||
/// Typeset a source file into a collection of layouted frames.
|
/// Typeset a source file into a collection of layouted frames.
|
||||||
///
|
///
|
||||||
/// The `file` is the file id of the source file and is used to resolve
|
/// The `file` identifies the source file and is used to resolve relative
|
||||||
/// relative paths (for importing and image loading).
|
/// paths (for importing and image loading).
|
||||||
///
|
///
|
||||||
/// Returns a vector of frames representing individual pages alongside
|
/// Returns a vector of frames representing individual pages alongside
|
||||||
/// diagnostic information (errors and warnings).
|
/// diagnostic information (errors and warnings).
|
||||||
@ -125,15 +125,14 @@ pub struct ContextBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ContextBuilder {
|
impl ContextBuilder {
|
||||||
/// The scope containing definitions that are available everywhere,
|
/// The scope containing definitions that are available everywhere
|
||||||
/// (the standard library).
|
/// (the standard library).
|
||||||
pub fn std(mut self, std: Scope) -> Self {
|
pub fn std(mut self, std: Scope) -> Self {
|
||||||
self.std = Some(std);
|
self.std = Some(std);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `state` defining initial properties for page size, font selection
|
/// The initial properties for page size, font selection and so on.
|
||||||
/// and so on.
|
|
||||||
pub fn state(mut self, state: State) -> Self {
|
pub fn state(mut self, state: State) -> Self {
|
||||||
self.state = Some(state);
|
self.state = Some(state);
|
||||||
self
|
self
|
||||||
|
@ -3,6 +3,7 @@ use std::collections::HashMap;
|
|||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use memmap2::Mmap;
|
use memmap2::Mmap;
|
||||||
use same_file::Handle;
|
use same_file::Handle;
|
||||||
@ -28,58 +29,27 @@ impl FsLoader {
|
|||||||
Self { faces: vec![], paths: RefCell::default() }
|
Self { faces: vec![], paths: RefCell::default() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve a file id for a path.
|
/// Builder-style variant of `search_system`.
|
||||||
pub fn resolve_path(&self, path: &Path) -> io::Result<FileId> {
|
pub fn with_system(mut self) -> Self {
|
||||||
let file = File::open(path)?;
|
self.search_system();
|
||||||
let meta = file.metadata()?;
|
self
|
||||||
if meta.is_file() {
|
|
||||||
let handle = Handle::from_file(file)?;
|
|
||||||
let id = FileId(fxhash::hash64(&handle));
|
|
||||||
self.paths.borrow_mut().insert(id, path.normalize());
|
|
||||||
Ok(id)
|
|
||||||
} else {
|
|
||||||
Err(io::Error::new(io::ErrorKind::Other, "not a file"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for fonts in the operating system's font directories.
|
/// Builder-style variant of `search_path`.
|
||||||
#[cfg(all(unix, not(target_os = "macos")))]
|
pub fn with_path(mut self, dir: impl AsRef<Path>) -> Self {
|
||||||
pub fn search_system(&mut self) {
|
|
||||||
self.search_path("/usr/share/fonts");
|
|
||||||
self.search_path("/usr/local/share/fonts");
|
|
||||||
|
|
||||||
if let Some(dir) = dirs::font_dir() {
|
|
||||||
self.search_path(dir);
|
self.search_path(dir);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builder-style method to wrap the loader in an [`Rc`] to make it usable
|
||||||
|
/// with the [`Context`](crate::Context).
|
||||||
|
pub fn wrap(self) -> Rc<Self> {
|
||||||
|
Rc::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for fonts in the operating system's font directories.
|
/// Search for fonts in the operating system's font directories.
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
pub fn search_system(&mut self) {
|
pub fn search_system(&mut self) {
|
||||||
self.search_path("/Library/Fonts");
|
self.search_system_impl();
|
||||||
self.search_path("/Network/Library/Fonts");
|
|
||||||
self.search_path("/System/Library/Fonts");
|
|
||||||
|
|
||||||
if let Some(dir) = dirs::font_dir() {
|
|
||||||
self.search_path(dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Search for fonts in the operating system's font directories.
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub fn search_system(&mut self) {
|
|
||||||
let windir =
|
|
||||||
std::env::var("WINDIR").unwrap_or_else(|_| "C:\\Windows".to_string());
|
|
||||||
|
|
||||||
self.search_path(Path::new(&windir).join("Fonts"));
|
|
||||||
|
|
||||||
if let Some(roaming) = dirs::config_dir() {
|
|
||||||
self.search_path(roaming.join("Microsoft\\Windows\\Fonts"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(local) = dirs::cache_dir() {
|
|
||||||
self.search_path(local.join("Microsoft\\Windows\\Fonts"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for all fonts at a path.
|
/// Search for all fonts at a path.
|
||||||
@ -108,6 +78,57 @@ impl FsLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve a file id for a path.
|
||||||
|
pub fn resolve(&self, path: &Path) -> io::Result<FileId> {
|
||||||
|
let file = File::open(path)?;
|
||||||
|
let meta = file.metadata()?;
|
||||||
|
if meta.is_file() {
|
||||||
|
let handle = Handle::from_file(file)?;
|
||||||
|
let id = FileId(fxhash::hash64(&handle));
|
||||||
|
self.paths.borrow_mut().insert(id, path.normalize());
|
||||||
|
Ok(id)
|
||||||
|
} else {
|
||||||
|
Err(io::Error::new(io::ErrorKind::Other, "not a file"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(unix, not(target_os = "macos")))]
|
||||||
|
fn search_system_impl(&mut self) {
|
||||||
|
self.search_path("/usr/share/fonts");
|
||||||
|
self.search_path("/usr/local/share/fonts");
|
||||||
|
|
||||||
|
if let Some(dir) = dirs::font_dir() {
|
||||||
|
self.search_path(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
fn search_system_impl(&mut self) {
|
||||||
|
self.search_path("/Library/Fonts");
|
||||||
|
self.search_path("/Network/Library/Fonts");
|
||||||
|
self.search_path("/System/Library/Fonts");
|
||||||
|
|
||||||
|
if let Some(dir) = dirs::font_dir() {
|
||||||
|
self.search_path(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn search_system_impl(&mut self) {
|
||||||
|
let windir =
|
||||||
|
std::env::var("WINDIR").unwrap_or_else(|_| "C:\\Windows".to_string());
|
||||||
|
|
||||||
|
self.search_path(Path::new(&windir).join("Fonts"));
|
||||||
|
|
||||||
|
if let Some(roaming) = dirs::config_dir() {
|
||||||
|
self.search_path(roaming.join("Microsoft\\Windows\\Fonts"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(local) = dirs::cache_dir() {
|
||||||
|
self.search_path(local.join("Microsoft\\Windows\\Fonts"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Index the font faces in the file at the given path.
|
/// Index the font faces in the file at the given path.
|
||||||
///
|
///
|
||||||
/// The file may form a font collection and contain multiple font faces,
|
/// The file may form a font collection and contain multiple font faces,
|
||||||
@ -154,7 +175,7 @@ impl FsLoader {
|
|||||||
stretch: FontStretch::from_number(face.width().to_number()),
|
stretch: FontStretch::from_number(face.width().to_number()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let file = self.resolve_path(path)?;
|
let file = self.resolve(path)?;
|
||||||
self.faces.push(FaceInfo { file, index, family, variant });
|
self.faces.push(FaceInfo { file, index, family, variant });
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -182,11 +203,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_index_font_dir() {
|
fn test_index_font_dir() {
|
||||||
let mut loader = FsLoader::new();
|
let map = FsLoader::new().with_path("fonts").paths.into_inner();
|
||||||
loader.search_path("fonts");
|
let mut paths: Vec<_> = map.into_iter().map(|p| p.1).collect();
|
||||||
|
|
||||||
let map = loader.paths.borrow();
|
|
||||||
let mut paths: Vec<_> = map.values().collect();
|
|
||||||
paths.sort();
|
paths.sort();
|
||||||
|
|
||||||
assert_eq!(paths, [
|
assert_eq!(paths, [
|
||||||
|
12
src/main.rs
12
src/main.rs
@ -1,6 +1,5 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Context};
|
use anyhow::{anyhow, bail, Context};
|
||||||
use same_file::is_same_file;
|
use same_file::is_same_file;
|
||||||
@ -30,17 +29,18 @@ fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a loader for fonts and files.
|
// Create a loader for fonts and files.
|
||||||
let mut loader = typst::loading::FsLoader::new();
|
let loader = typst::loading::FsLoader::new()
|
||||||
loader.search_path("fonts");
|
.with_path("fonts")
|
||||||
loader.search_system();
|
.with_system()
|
||||||
|
.wrap();
|
||||||
|
|
||||||
// Resolve the file id of the source file and read the file.
|
// Resolve the file id of the source file and read the file.
|
||||||
let src_id = loader.resolve_path(src_path).context("source file not found")?;
|
let src_id = loader.resolve(src_path).context("source file not found")?;
|
||||||
let src = fs::read_to_string(&src_path)
|
let src = fs::read_to_string(&src_path)
|
||||||
.map_err(|_| anyhow!("failed to read source file"))?;
|
.map_err(|_| anyhow!("failed to read source file"))?;
|
||||||
|
|
||||||
// Typeset.
|
// Typeset.
|
||||||
let mut ctx = typst::Context::new(Rc::new(loader));
|
let mut ctx = typst::Context::new(loader);
|
||||||
let pass = ctx.typeset(src_id, &src);
|
let pass = ctx.typeset(src_id, &src);
|
||||||
|
|
||||||
// Print diagnostics.
|
// Print diagnostics.
|
||||||
|
103
tests/typeset.rs
103
tests/typeset.rs
@ -63,15 +63,16 @@ fn main() {
|
|||||||
state.page.size = Size::new(Length::pt(120.0), Length::inf());
|
state.page.size = Size::new(Length::pt(120.0), Length::inf());
|
||||||
state.page.margins = Sides::splat(Some(Length::pt(10.0).into()));
|
state.page.margins = Sides::splat(Some(Length::pt(10.0).into()));
|
||||||
|
|
||||||
// Create a file system loader.
|
// We hook up some extra test helpers into the global scope.
|
||||||
let loader = {
|
let mut std = typst::library::new();
|
||||||
let mut loader = typst::loading::FsLoader::new();
|
let panics = Rc::new(RefCell::new(vec![]));
|
||||||
loader.search_path(FONT_DIR);
|
register_helpers(&mut std, Rc::clone(&panics));
|
||||||
Rc::new(loader)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut ctx = typst::Context::builder().state(state).build(loader.clone());
|
// Create loader and context.
|
||||||
|
let loader = FsLoader::new().with_path(FONT_DIR).wrap();
|
||||||
|
let mut ctx = Context::builder().std(std).state(state).build(loader.clone());
|
||||||
|
|
||||||
|
// Run all the tests.
|
||||||
let mut ok = true;
|
let mut ok = true;
|
||||||
for src_path in filtered {
|
for src_path in filtered {
|
||||||
let path = src_path.strip_prefix(TYP_DIR).unwrap();
|
let path = src_path.strip_prefix(TYP_DIR).unwrap();
|
||||||
@ -81,8 +82,9 @@ fn main() {
|
|||||||
args.pdf.then(|| Path::new(PDF_DIR).join(path).with_extension("pdf"));
|
args.pdf.then(|| Path::new(PDF_DIR).join(path).with_extension("pdf"));
|
||||||
|
|
||||||
ok &= test(
|
ok &= test(
|
||||||
loader.as_ref(),
|
|
||||||
&mut ctx,
|
&mut ctx,
|
||||||
|
loader.as_ref(),
|
||||||
|
&panics,
|
||||||
&src_path,
|
&src_path,
|
||||||
&png_path,
|
&png_path,
|
||||||
&ref_path,
|
&ref_path,
|
||||||
@ -128,15 +130,38 @@ impl Args {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Panics = Rc<RefCell<Vec<Panic>>>;
|
||||||
|
|
||||||
struct Panic {
|
struct Panic {
|
||||||
pos: Pos,
|
pos: Pos,
|
||||||
lhs: Option<Value>,
|
lhs: Option<Value>,
|
||||||
rhs: Option<Value>,
|
rhs: Option<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn register_helpers(scope: &mut Scope, panics: Rc<RefCell<Vec<Panic>>>) {
|
||||||
|
scope.def_const("error", Value::Error);
|
||||||
|
scope.def_func("args", |_, args| {
|
||||||
|
let repr = typst::pretty::pretty(args);
|
||||||
|
args.items.clear();
|
||||||
|
Value::template(move |ctx| {
|
||||||
|
ctx.set_monospace();
|
||||||
|
ctx.push_text(&repr);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
scope.def_func("test", move |ctx, args| {
|
||||||
|
let lhs = args.expect::<Value>(ctx, "left-hand side");
|
||||||
|
let rhs = args.expect::<Value>(ctx, "right-hand side");
|
||||||
|
if lhs != rhs {
|
||||||
|
panics.borrow_mut().push(Panic { pos: args.span.start, lhs, rhs });
|
||||||
|
}
|
||||||
|
Value::None
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn test(
|
fn test(
|
||||||
loader: &FsLoader,
|
|
||||||
ctx: &mut Context,
|
ctx: &mut Context,
|
||||||
|
loader: &FsLoader,
|
||||||
|
panics: &Panics,
|
||||||
src_path: &Path,
|
src_path: &Path,
|
||||||
png_path: &Path,
|
png_path: &Path,
|
||||||
ref_path: &Path,
|
ref_path: &Path,
|
||||||
@ -146,7 +171,7 @@ fn test(
|
|||||||
println!("Testing {}", name.display());
|
println!("Testing {}", name.display());
|
||||||
|
|
||||||
let src = fs::read_to_string(src_path).unwrap();
|
let src = fs::read_to_string(src_path).unwrap();
|
||||||
let src_id = loader.resolve_path(src_path).unwrap();
|
let src_id = loader.resolve(src_path).unwrap();
|
||||||
|
|
||||||
let mut ok = true;
|
let mut ok = true;
|
||||||
let mut frames = vec![];
|
let mut frames = vec![];
|
||||||
@ -170,7 +195,7 @@ fn test(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let (part_ok, compare_here, part_frames) =
|
let (part_ok, compare_here, part_frames) =
|
||||||
test_part(ctx, src_id, part, i, compare_ref, lines);
|
test_part(ctx, panics, src_id, part, i, compare_ref, lines);
|
||||||
ok &= part_ok;
|
ok &= part_ok;
|
||||||
compare_ever |= compare_here;
|
compare_ever |= compare_here;
|
||||||
frames.extend(part_frames);
|
frames.extend(part_frames);
|
||||||
@ -210,6 +235,7 @@ fn test(
|
|||||||
|
|
||||||
fn test_part(
|
fn test_part(
|
||||||
ctx: &mut Context,
|
ctx: &mut Context,
|
||||||
|
panics: &Panics,
|
||||||
src_id: FileId,
|
src_id: FileId,
|
||||||
src: &str,
|
src: &str,
|
||||||
i: usize,
|
i: usize,
|
||||||
@ -220,23 +246,17 @@ fn test_part(
|
|||||||
let (local_compare_ref, ref_diags) = parse_metadata(src, &map);
|
let (local_compare_ref, ref_diags) = parse_metadata(src, &map);
|
||||||
let compare_ref = local_compare_ref.unwrap_or(compare_ref);
|
let compare_ref = local_compare_ref.unwrap_or(compare_ref);
|
||||||
|
|
||||||
// We hook up some extra test helpers into the global scope.
|
let ast = parse(src);
|
||||||
let mut scope = typst::library::new();
|
let module = eval(ctx, src_id, Rc::new(ast.output));
|
||||||
let panics = Rc::new(RefCell::new(vec![]));
|
let tree = exec(ctx, &module.output.template);
|
||||||
register_helpers(&mut scope, Rc::clone(&panics));
|
let mut frames = layout(ctx, &tree.output);
|
||||||
|
|
||||||
let parsed = parse(src);
|
let mut diags = ast.diags;
|
||||||
let evaluated = eval(ctx, src_id, Rc::new(parsed.output));
|
diags.extend(module.diags);
|
||||||
let executed = exec(ctx, &evaluated.output.template);
|
diags.extend(tree.diags);
|
||||||
let mut layouted = layout(ctx, &executed.output);
|
|
||||||
|
|
||||||
let mut diags = parsed.diags;
|
|
||||||
diags.extend(evaluated.diags);
|
|
||||||
diags.extend(executed.diags);
|
|
||||||
|
|
||||||
let mut ok = true;
|
let mut ok = true;
|
||||||
|
for panic in panics.borrow().iter() {
|
||||||
for panic in &*panics.borrow() {
|
|
||||||
let line = map.location(panic.pos).unwrap().line;
|
let line = map.location(panic.pos).unwrap().line;
|
||||||
println!(" Assertion failed in line {} ❌", lines + line);
|
println!(" Assertion failed in line {} ❌", lines + line);
|
||||||
if let (Some(lhs), Some(rhs)) = (&panic.lhs, &panic.rhs) {
|
if let (Some(lhs), Some(rhs)) = (&panic.lhs, &panic.rhs) {
|
||||||
@ -248,6 +268,8 @@ fn test_part(
|
|||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
panics.borrow_mut().clear();
|
||||||
|
|
||||||
if diags != ref_diags {
|
if diags != ref_diags {
|
||||||
println!(" Subtest {} does not match expected diagnostics. ❌", i);
|
println!(" Subtest {} does not match expected diagnostics. ❌", i);
|
||||||
ok = false;
|
ok = false;
|
||||||
@ -279,8 +301,7 @@ fn test_part(
|
|||||||
|
|
||||||
ctx.layouts.turnaround();
|
ctx.layouts.turnaround();
|
||||||
|
|
||||||
let cached_result = layout(ctx, &executed.output);
|
let cached = layout(ctx, &tree.output);
|
||||||
|
|
||||||
let misses = ctx
|
let misses = ctx
|
||||||
.layouts
|
.layouts
|
||||||
.frames
|
.frames
|
||||||
@ -297,7 +318,7 @@ fn test_part(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if cached_result != layouted {
|
if cached != frames {
|
||||||
ok = false;
|
ok = false;
|
||||||
println!(
|
println!(
|
||||||
" Recompilation of subtest {} differs from clean pass ❌",
|
" Recompilation of subtest {} differs from clean pass ❌",
|
||||||
@ -311,10 +332,10 @@ fn test_part(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !compare_ref {
|
if !compare_ref {
|
||||||
layouted.clear();
|
frames.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
(ok, compare_ref, layouted)
|
(ok, compare_ref, frames)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_metadata(src: &str, map: &LineMap) -> (Option<bool>, DiagSet) {
|
fn parse_metadata(src: &str, map: &LineMap) -> (Option<bool>, DiagSet) {
|
||||||
@ -364,28 +385,6 @@ fn parse_metadata(src: &str, map: &LineMap) -> (Option<bool>, DiagSet) {
|
|||||||
(compare_ref, diags)
|
(compare_ref, diags)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_helpers(scope: &mut Scope, panics: Rc<RefCell<Vec<Panic>>>) {
|
|
||||||
scope.def_const("error", Value::Error);
|
|
||||||
|
|
||||||
scope.def_func("args", |_, args| {
|
|
||||||
let repr = typst::pretty::pretty(args);
|
|
||||||
args.items.clear();
|
|
||||||
Value::template(move |ctx| {
|
|
||||||
ctx.set_monospace();
|
|
||||||
ctx.push_text(&repr);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
scope.def_func("test", move |ctx, args| {
|
|
||||||
let lhs = args.expect::<Value>(ctx, "left-hand side");
|
|
||||||
let rhs = args.expect::<Value>(ctx, "right-hand side");
|
|
||||||
if lhs != rhs {
|
|
||||||
panics.borrow_mut().push(Panic { pos: args.span.start, lhs, rhs });
|
|
||||||
}
|
|
||||||
Value::None
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_diag(diag: &Diag, map: &LineMap, lines: u32) {
|
fn print_diag(diag: &Diag, map: &LineMap, lines: u32) {
|
||||||
let mut start = map.location(diag.span.start).unwrap();
|
let mut start = map.location(diag.span.start).unwrap();
|
||||||
let mut end = map.location(diag.span.end).unwrap();
|
let mut end = map.location(diag.span.end).unwrap();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user