Rework Vt into Engine

- Moves as much data out of the `Vm`
- Removes duplication with call_vm and call_vt flavours
- Uses tracked chain instead of fixed int for determining max nesting depth
- This means that nesting checks now generalizes to layout and realization, to detect crashing show rules and overly nested layouts
This commit is contained in:
Laurenz 2023-11-25 16:10:28 +01:00
parent 2f795b5c07
commit 85b1d1d4dd
95 changed files with 1257 additions and 1014 deletions

View File

@ -1,9 +1,10 @@
use comemo::Track;
use ecow::{eco_vec, EcoString, EcoVec};
use typst::eval::{Route, Tracer, Vm};
use typst::engine::{Engine, Route};
use typst::eval::{Tracer, Vm};
use typst::foundations::{Label, Scopes, Value};
use typst::introspection::{Introspector, Locator};
use typst::layout::{Frame, Vt};
use typst::layout::Frame;
use typst::model::BibliographyElem;
use typst::syntax::{ast, LinkedNode, Span, SyntaxKind};
use typst::World;
@ -46,7 +47,6 @@ pub fn analyze_expr(world: &dyn World, node: &LinkedNode) -> EcoVec<Value> {
/// Try to load a module from the current source file.
pub fn analyze_import(world: &dyn World, source: &LinkedNode) -> Option<Value> {
let id = source.span().id()?;
let source = analyze_expr(world, source).into_iter().next()?;
if source.scope().is_some() {
return Some(source);
@ -55,15 +55,15 @@ pub fn analyze_import(world: &dyn World, source: &LinkedNode) -> Option<Value> {
let mut locator = Locator::default();
let introspector = Introspector::default();
let mut tracer = Tracer::new();
let vt = Vt {
let engine = Engine {
world: world.track(),
route: Route::default(),
introspector: introspector.track(),
locator: &mut locator,
tracer: tracer.track_mut(),
};
let route = Route::default();
let mut vm = Vm::new(vt, route.track(), Some(id), Scopes::new(Some(world.library())));
let mut vm = Vm::new(engine, Scopes::new(Some(world.library())), Span::detached());
typst::eval::import(&mut vm, source, Span::detached(), true)
.ok()
.map(Value::Module)

View File

@ -1083,7 +1083,7 @@ fn create_construct_impl(element: &Elem) -> TokenStream {
quote! {
impl #foundations::Construct for #ident {
fn construct(
vm: &mut ::typst::eval::Vm,
engine: &mut ::typst::engine::Engine,
args: &mut #foundations::Args,
) -> ::typst::diag::SourceResult<#foundations::Content> {
#(#pre)*
@ -1115,7 +1115,7 @@ fn create_set_impl(element: &Elem) -> TokenStream {
quote! {
impl #foundations::Set for #ident {
fn set(
vm: &mut ::typst::eval::Vm,
engine: &mut ::typst::engine::Engine,
args: &mut #foundations::Args,
) -> ::typst::diag::SourceResult<#foundations::Styles> {
let mut styles = #foundations::Styles::new();

View File

@ -36,8 +36,7 @@ struct Func {
#[derive(Default)]
struct SpecialParams {
self_: Option<Param>,
vm: bool,
vt: bool,
engine: bool,
args: bool,
span: bool,
}
@ -171,8 +170,7 @@ fn parse_param(
};
match ident.to_string().as_str() {
"vm" => special.vm = true,
"vt" => special.vt = true,
"engine" => special.engine = true,
"args" => special.args = true,
"span" => special.span = true,
_ => {
@ -322,13 +320,12 @@ fn create_wrapper_closure(func: &Func) -> TokenStream {
.as_ref()
.map(bind)
.map(|tokens| quote! { #tokens, });
let vm_ = func.special.vm.then(|| quote! { vm, });
let vt_ = func.special.vt.then(|| quote! { &mut vm.vt, });
let vt_ = func.special.engine.then(|| quote! { engine, });
let args_ = func.special.args.then(|| quote! { args, });
let span_ = func.special.span.then(|| quote! { args.span, });
let forwarded = func.params.iter().filter(|param| !param.external).map(bind);
quote! {
__typst_func(#self_ #vm_ #vt_ #args_ #span_ #(#forwarded,)*)
__typst_func(#self_ #vt_ #args_ #span_ #(#forwarded,)*)
}
};
@ -336,7 +333,7 @@ fn create_wrapper_closure(func: &Func) -> TokenStream {
let ident = &func.ident;
let parent = func.parent.as_ref().map(|ty| quote! { #ty:: });
quote! {
|vm, args| {
|engine, args| {
let __typst_func = #parent #ident;
#handlers
#finish

View File

@ -2,6 +2,8 @@ use std::fmt::{self, Debug, Formatter};
use std::num::NonZeroU64;
use std::ops::Range;
use ecow::EcoString;
use crate::FileId;
/// A unique identifier for a syntax node.
@ -80,6 +82,14 @@ impl Span {
pub const fn number(self) -> u64 {
self.0.get() & ((1 << Self::BITS) - 1)
}
/// Resolve a file location relative to this span's source.
pub fn resolve_path(self, path: &str) -> Result<FileId, EcoString> {
let Some(file) = self.id() else {
return Err("cannot access file system from here".into());
};
Ok(file.join(path))
}
}
/// A value with a span locating it in the source code.

159
crates/typst/src/engine.rs Normal file
View File

@ -0,0 +1,159 @@
use std::cell::Cell;
use comemo::{Track, Tracked, TrackedMut, Validate};
use crate::diag::SourceResult;
use crate::eval::Tracer;
use crate::introspection::{Introspector, Locator};
use crate::syntax::FileId;
use crate::World;
/// The maxmium stack nesting depth.
const MAX_DEPTH: usize = 64;
/// Holds all data needed during compilation.
pub struct Engine<'a> {
/// The compilation environment.
pub world: Tracked<'a, dyn World + 'a>,
/// Provides access to information about the document.
pub introspector: Tracked<'a, Introspector>,
/// The route the engine took during compilation. This is used to detect
/// cyclic imports and too much nesting.
pub route: Route<'a>,
/// Provides stable identities to elements.
pub locator: &'a mut Locator<'a>,
/// The tracer for inspection of the values an expression produces.
pub tracer: TrackedMut<'a, Tracer>,
}
impl Engine<'_> {
/// Perform a fallible operation that does not immediately terminate further
/// execution. Instead it produces a delayed error that is only promoted to
/// a fatal one if it remains at the end of the introspection loop.
pub fn delayed<F, T>(&mut self, f: F) -> T
where
F: FnOnce(&mut Self) -> SourceResult<T>,
T: Default,
{
match f(self) {
Ok(value) => value,
Err(errors) => {
self.tracer.delay(errors);
T::default()
}
}
}
}
/// The route the engine took during compilation. This is used to detect
/// cyclic imports and too much nesting.
#[derive(Clone)]
pub struct Route<'a> {
// We need to override the constraint's lifetime here so that `Tracked` is
// covariant over the constraint. If it becomes invariant, we're in for a
// world of lifetime pain.
outer: Option<Tracked<'a, Self, <Route<'static> as Validate>::Constraint>>,
/// This is set if this route segment was inserted through the start of a
/// module evaluation.
id: Option<FileId>,
/// This is set whenever we enter a function, nested layout, or are applying
/// a show rule. The length of this segment plus the lengths of all `outer`
/// route segments make up the length of the route. If the length of the
/// route exceeds `MAX_DEPTH`, then we throw a "maximum ... depth exceeded"
/// error.
len: usize,
/// The upper bound we've established for the parent chain length. We don't
/// know the exact length (that would defeat the whole purpose because it
/// would prevent cache reuse of some computation at different,
/// non-exceeding depths).
upper: Cell<usize>,
}
impl<'a> Route<'a> {
/// Create a new, empty route.
pub fn root() -> Self {
Self { id: None, outer: None, len: 0, upper: Cell::new(0) }
}
/// Insert a new id into the route.
///
/// You must guarantee that `outer` lives longer than the resulting
/// route is ever used.
pub fn insert(outer: Tracked<'a, Self>, id: FileId) -> Self {
Route {
outer: Some(outer),
id: Some(id),
len: 0,
upper: Cell::new(usize::MAX),
}
}
/// Extend the route without another id.
pub fn extend(outer: Tracked<'a, Self>) -> Self {
Route {
outer: Some(outer),
id: None,
len: 1,
upper: Cell::new(usize::MAX),
}
}
/// Start tracking this route.
///
/// In comparison to [`Track::track`], this method skips this chain link
/// if it does not contribute anything.
pub fn track(&self) -> Tracked<'_, Self> {
match self.outer {
Some(outer) if self.id.is_none() && self.len == 0 => outer,
_ => Track::track(self),
}
}
/// Increase the nesting depth for this route segment.
pub fn increase(&mut self) {
self.len += 1;
}
/// Decrease the nesting depth for this route segment.
pub fn decrease(&mut self) {
self.len -= 1;
}
/// Check whether the nesting depth exceeds the limit.
pub fn exceeding(&self) -> bool {
!self.within(MAX_DEPTH)
}
}
#[comemo::track]
impl<'a> Route<'a> {
/// Whether the given id is part of the route.
pub fn contains(&self, id: FileId) -> bool {
self.id == Some(id) || self.outer.map_or(false, |outer| outer.contains(id))
}
/// Whether the route's depth is less than or equal to the given depth.
pub fn within(&self, depth: usize) -> bool {
if self.upper.get().saturating_add(self.len) <= depth {
return true;
}
match self.outer {
Some(_) if depth < self.len => false,
Some(outer) => {
let within = outer.within(depth - self.len);
if within && depth < self.upper.get() {
self.upper.set(depth);
}
within
}
None => true,
}
}
}
impl Default for Route<'_> {
fn default() -> Self {
Self::root()
}
}

View File

@ -31,7 +31,7 @@ impl Access for ast::Ident<'_> {
let span = self.span();
let value = vm.scopes.get_mut(&self).at(span)?;
if vm.inspected == Some(span) {
vm.vt.tracer.value(value.clone());
vm.engine.tracer.value(value.clone());
}
Ok(value)
}

View File

@ -2,13 +2,13 @@ use comemo::{Prehashed, Tracked, TrackedMut};
use ecow::EcoVec;
use crate::diag::{bail, error, At, HintedStrResult, SourceResult, Trace, Tracepoint};
use crate::engine::Engine;
use crate::eval::{Access, Eval, FlowEvent, Route, Tracer, Vm};
use crate::foundations::{
call_method_mut, is_mutating_method, Arg, Args, Bytes, Closure, Content, Func,
IntoValue, NativeElement, Scope, Scopes, Value,
};
use crate::introspection::{Introspector, Locator};
use crate::layout::Vt;
use crate::math::{Accent, AccentElem, LrElem};
use crate::symbols::Symbol;
use crate::syntax::ast::{self, AstNode};
@ -16,23 +16,19 @@ use crate::syntax::{Spanned, SyntaxNode};
use crate::text::TextElem;
use crate::World;
/// The maxmium function call depth.
const MAX_CALL_DEPTH: usize = 64;
impl Eval for ast::FuncCall<'_> {
type Output = Value;
#[tracing::instrument(name = "FuncCall::eval", skip_all)]
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
let span = self.span();
if vm.depth >= MAX_CALL_DEPTH {
bail!(span, "maximum function call depth exceeded");
}
let callee = self.callee();
let in_math = in_math(callee);
let callee_span = callee.span();
let args = self.args();
if vm.engine.route.exceeding() {
bail!(span, "maximum function call depth exceeded");
}
// Try to evaluate as a call to an associated function or field.
let (callee, mut args) = if let ast::Expr::FieldAccess(access) = callee {
@ -146,7 +142,7 @@ impl Eval for ast::FuncCall<'_> {
let callee = callee.cast::<Func>().at(callee_span)?;
let point = || Tracepoint::Call(callee.name().map(Into::into));
let f = || callee.call_vm(vm, args).trace(vm.world(), point, span);
let f = || callee.call(&mut vm.engine, args).trace(vm.world(), point, span);
// Stacker is broken on WASM.
#[cfg(target_arch = "wasm32")]
@ -229,7 +225,6 @@ impl Eval for ast::Closure<'_> {
// Define the closure.
let closure = Closure {
node: self.to_untyped().clone(),
file: vm.file,
defaults,
captured,
};
@ -246,11 +241,10 @@ pub(crate) fn call_closure(
func: &Func,
closure: &Prehashed<Closure>,
world: Tracked<dyn World + '_>,
route: Tracked<Route>,
introspector: Tracked<Introspector>,
route: Tracked<Route>,
locator: Tracked<Locator>,
tracer: TrackedMut<Tracer>,
depth: usize,
mut args: Args,
) -> SourceResult<Value> {
let node = closure.node.cast::<ast::Closure>().unwrap();
@ -260,13 +254,18 @@ pub(crate) fn call_closure(
let mut scopes = Scopes::new(None);
scopes.top = closure.captured.clone();
// Prepare VT.
// Prepare the engine.
let mut locator = Locator::chained(locator);
let vt = Vt { world, introspector, locator: &mut locator, tracer };
let engine = Engine {
world,
introspector,
route: Route::extend(route),
locator: &mut locator,
tracer,
};
// Prepare VM.
let mut vm = Vm::new(vt, route, closure.file, scopes);
vm.depth = depth;
let mut vm = Vm::new(engine, scopes, node.span());
// Provide the closure itself for recursive calls.
if let Some(name) = node.name() {
@ -279,6 +278,7 @@ pub(crate) fn call_closure(
.children()
.filter(|p| matches!(p, ast::Param::Pos(_)))
.count();
let num_pos_args = args.to_pos().len();
let sink_size = num_pos_args.checked_sub(num_pos_params);

View File

@ -40,7 +40,7 @@ fn eval_code<'a>(
}
let tail = eval_code(vm, exprs)?.display();
Value::Content(tail.styled_with_recipe(vm, recipe)?)
Value::Content(tail.styled_with_recipe(&mut vm.engine, recipe)?)
}
_ => expr.eval(vm)?,
};
@ -130,7 +130,7 @@ impl Eval for ast::Expr<'_> {
.spanned(span);
if vm.inspected == Some(span) {
vm.vt.tracer.value(v.clone());
vm.engine.tracer.value(v.clone());
}
Ok(v)

View File

@ -38,7 +38,7 @@ impl Eval for ast::ModuleImport<'_> {
if let ast::Expr::Ident(ident) = self.source() {
if ident.as_str() == new_name.as_str() {
// Warn on `import x as x`
vm.vt.tracer.warn(warning!(
vm.engine.tracer.warn(warning!(
new_name.span(),
"unnecessary import rename to same name",
));
@ -73,7 +73,7 @@ impl Eval for ast::ModuleImport<'_> {
if renamed_item.original_name().as_str()
== renamed_item.new_name().as_str()
{
vm.vt.tracer.warn(warning!(
vm.engine.tracer.warn(warning!(
renamed_item.new_name().span(),
"unnecessary import rename to same name",
));
@ -145,27 +145,37 @@ fn import_package(vm: &mut Vm, spec: PackageSpec, span: Span) -> SourceResult<Mo
let entrypoint_id = manifest_id.join(&manifest.package.entrypoint);
let source = vm.world().source(entrypoint_id).at(span)?;
let point = || Tracepoint::Import;
Ok(eval(vm.world(), vm.route, TrackedMut::reborrow_mut(&mut vm.vt.tracer), &source)
.trace(vm.world(), point, span)?
.with_name(manifest.package.name))
Ok(eval(
vm.world(),
vm.engine.route.track(),
TrackedMut::reborrow_mut(&mut vm.engine.tracer),
&source,
)
.trace(vm.world(), point, span)?
.with_name(manifest.package.name))
}
/// Import a file from a path.
fn import_file(vm: &mut Vm, path: &str, span: Span) -> SourceResult<Module> {
// Load the source file.
let world = vm.world();
let id = vm.resolve_path(path).at(span)?;
let id = span.resolve_path(path).at(span)?;
let source = world.source(id).at(span)?;
// Prevent cyclic importing.
if vm.route.contains(source.id()) {
if vm.engine.route.contains(source.id()) {
bail!(span, "cyclic import");
}
// Evaluate the file.
let point = || Tracepoint::Import;
eval(world, vm.route, TrackedMut::reborrow_mut(&mut vm.vt.tracer), &source)
.trace(world, point, span)
eval(
world,
vm.engine.route.track(),
TrackedMut::reborrow_mut(&mut vm.engine.tracer),
&source,
)
.trace(world, point, span)
}
/// A parsed package manifest.

View File

@ -43,7 +43,7 @@ fn eval_markup<'a>(
}
let tail = eval_markup(vm, exprs)?;
seq.push(tail.styled_with_recipe(vm, recipe)?)
seq.push(tail.styled_with_recipe(&mut vm.engine, recipe)?)
}
expr => match expr.eval(vm)? {
Value::Label(label) => {
@ -139,11 +139,11 @@ impl Eval for ast::Strong<'_> {
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
let body = self.body();
if body.exprs().next().is_none() {
vm.vt
.tracer
.warn(warning!(self.span(), "no text within stars").with_hint(
vm.engine.tracer.warn(
warning!(self.span(), "no text within stars").with_hint(
"using multiple consecutive stars (e.g. **) has no additional effect",
));
),
);
}
Ok(StrongElem::new(body.eval(vm)?).pack())
@ -157,7 +157,7 @@ impl Eval for ast::Emph<'_> {
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
let body = self.body();
if body.exprs().next().is_none() {
vm.vt
vm.engine
.tracer
.warn(warning!(self.span(), "no text within underscores").with_hint(
"using multiple consecutive underscores (e.g. __) has no additional effect"

View File

@ -26,9 +26,9 @@ pub(crate) use self::flow::*;
use comemo::{Track, Tracked, TrackedMut};
use crate::diag::{bail, SourceResult};
use crate::engine::{Engine, Route};
use crate::foundations::{Cast, Module, NativeElement, Scope, Scopes, Value};
use crate::introspection::{Introspector, Locator};
use crate::layout::Vt;
use crate::math::EquationElem;
use crate::syntax::{ast, parse, parse_code, parse_math, Source, Span};
use crate::World;
@ -48,22 +48,23 @@ pub fn eval(
panic!("Tried to cyclicly evaluate {:?}", id.vpath());
}
// Prepare VT.
// Prepare the engine.
let mut locator = Locator::new();
let introspector = Introspector::default();
let vt = Vt {
let engine = Engine {
world,
route: Route::insert(route, id),
introspector: introspector.track(),
locator: &mut locator,
tracer,
};
// Prepare VM.
let route = Route::insert(route, id);
let scopes = Scopes::new(Some(world.library()));
let mut vm = Vm::new(vt, route.track(), Some(id), scopes);
let root = source.root();
let scopes = Scopes::new(Some(world.library()));
let mut vm = Vm::new(engine, scopes, root.span());
// Check for well-formedness unless we are in trace mode.
let errors = root.errors();
if !errors.is_empty() && vm.inspected.is_none() {
return Err(errors.into_iter().map(Into::into).collect());
@ -108,26 +109,27 @@ pub fn eval_string(
root.synthesize(span);
// Check for well-formedness.
let errors = root.errors();
if !errors.is_empty() {
return Err(errors.into_iter().map(Into::into).collect());
}
// Prepare VT.
// Prepare the engine.
let mut tracer = Tracer::new();
let mut locator = Locator::new();
let introspector = Introspector::default();
let vt = Vt {
let engine = Engine {
world,
introspector: introspector.track(),
route: Route::default(),
locator: &mut locator,
tracer: tracer.track_mut(),
};
// Prepare VM.
let route = Route::default();
let scopes = Scopes::new(Some(world.library()));
let mut vm = Vm::new(vt, route.track(), None, scopes);
let mut vm = Vm::new(engine, scopes, root.span());
vm.scopes.scopes.push(scope);
// Evaluate the code.

View File

@ -24,7 +24,7 @@ impl Eval for ast::SetRule<'_> {
})
.at(target.span())?;
let args = self.args().eval(vm)?;
Ok(target.set(vm, args)?.spanned(self.span()))
Ok(target.set(&mut vm.engine, args)?.spanned(self.span()))
}
}

View File

@ -1,74 +1,37 @@
use comemo::{Track, Tracked, Validate};
use comemo::Tracked;
use crate::diag::{bail, StrResult};
use crate::engine::Engine;
use crate::eval::FlowEvent;
use crate::foundations::{IntoValue, Scopes};
use crate::layout::Vt;
use crate::syntax::ast::{self, AstNode};
use crate::syntax::{FileId, Span};
use crate::syntax::Span;
use crate::World;
/// A virtual machine.
///
/// Holds the state needed to [evaluate](crate::eval::eval()) Typst sources. A new
/// virtual machine is created for each module evaluation and function call.
/// Holds the state needed to [evaluate](crate::eval::eval()) Typst sources. A
/// new virtual machine is created for each module evaluation and function call.
pub struct Vm<'a> {
/// The underlying virtual typesetter.
pub(crate) vt: Vt<'a>,
/// The route of source ids the VM took to reach its current location.
pub(crate) route: Tracked<'a, Route<'a>>,
/// The id of the currently evaluated file.
pub(crate) file: Option<FileId>,
pub(crate) engine: Engine<'a>,
/// A control flow event that is currently happening.
pub(crate) flow: Option<FlowEvent>,
/// The stack of scopes.
pub(crate) scopes: Scopes<'a>,
/// The current call depth.
pub(crate) depth: usize,
/// A span that is currently under inspection.
pub(crate) inspected: Option<Span>,
}
impl<'a> Vm<'a> {
/// Create a new virtual machine.
pub fn new(
vt: Vt<'a>,
route: Tracked<'a, Route>,
file: Option<FileId>,
scopes: Scopes<'a>,
) -> Self {
let inspected = file.and_then(|id| vt.tracer.inspected(id));
Self {
vt,
route,
file,
flow: None,
scopes,
depth: 0,
inspected,
}
pub fn new(engine: Engine<'a>, scopes: Scopes<'a>, target: Span) -> Self {
let inspected = target.id().and_then(|id| engine.tracer.inspected(id));
Self { engine, flow: None, scopes, inspected }
}
/// Access the underlying world.
pub fn world(&self) -> Tracked<'a, dyn World + 'a> {
self.vt.world
}
/// The id of the currently evaluated file.
///
/// Returns `None` if the VM is in a detached context, e.g. when evaluating
/// a user-provided string.
pub fn file(&self) -> Option<FileId> {
self.file
}
/// Resolve a path relative to the currently evaluated file.
pub fn resolve_path(&self, path: &str) -> StrResult<FileId> {
let Some(file) = self.file else {
bail!("cannot access file system from here");
};
Ok(file.join(path))
self.engine.world
}
/// Define a variable in the current scope.
@ -76,52 +39,8 @@ impl<'a> Vm<'a> {
pub fn define(&mut self, var: ast::Ident, value: impl IntoValue) {
let value = value.into_value();
if self.inspected == Some(var.span()) {
self.vt.tracer.value(value.clone());
self.engine.tracer.value(value.clone());
}
self.scopes.top.define(var.get().clone(), value);
}
}
/// A route of source ids.
#[derive(Default)]
pub struct Route<'a> {
// We need to override the constraint's lifetime here so that `Tracked` is
// covariant over the constraint. If it becomes invariant, we're in for a
// world of lifetime pain.
outer: Option<Tracked<'a, Self, <Route<'static> as Validate>::Constraint>>,
id: Option<FileId>,
}
impl<'a> Route<'a> {
/// Create a new route with just one entry.
pub fn new(id: Option<FileId>) -> Self {
Self { id, outer: None }
}
/// Insert a new id into the route.
///
/// You must guarantee that `outer` lives longer than the resulting
/// route is ever used.
pub fn insert(outer: Tracked<'a, Self>, id: FileId) -> Self {
Route { outer: Some(outer), id: Some(id) }
}
/// Start tracking this locator.
///
/// In comparison to [`Track::track`], this method skips this chain link
/// if it does not contribute anything.
pub fn track(&self) -> Tracked<'_, Self> {
match self.outer {
Some(outer) if self.id.is_none() => outer,
_ => Track::track(self),
}
}
}
#[comemo::track]
impl<'a> Route<'a> {
/// Whether the given id is part of the route.
pub fn contains(&self, id: FileId) -> bool {
self.id == Some(id) || self.outer.map_or(false, |outer| outer.contains(id))
}
}

View File

@ -320,3 +320,26 @@ impl PartialEq for Arg {
self.name == other.name && self.value.v == other.value.v
}
}
/// Things that can be used as arguments.
pub trait IntoArgs {
/// Convert into arguments, attaching the `fallback` span in case `Self`
/// doesn't have a span.
fn into_args(self, fallback: Span) -> Args;
}
impl IntoArgs for Args {
fn into_args(self, _: Span) -> Args {
self
}
}
impl<I, T> IntoArgs for I
where
I: IntoIterator<Item = T>,
T: IntoValue,
{
fn into_args(self, fallback: Span) -> Args {
Args::new(fallback, self)
}
}

View File

@ -8,7 +8,8 @@ use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use crate::diag::{At, SourceResult, StrResult};
use crate::eval::{ops, Vm};
use crate::engine::Engine;
use crate::eval::ops;
use crate::foundations::{
cast, func, repr, scope, ty, Args, Bytes, CastInfo, FromValue, Func, IntoValue,
Reflect, Repr, Value, Version,
@ -297,14 +298,17 @@ impl Array {
#[func]
pub fn find(
&self,
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// The function to apply to each item. Must return a boolean.
searcher: Func,
) -> SourceResult<Option<Value>> {
for item in self.iter() {
let args = Args::new(searcher.span(), [item.clone()]);
if searcher.call_vm(vm, args)?.cast::<bool>().at(searcher.span())? {
if searcher
.call(engine, [item.clone()])?
.cast::<bool>()
.at(searcher.span())?
{
return Ok(Some(item.clone()));
}
}
@ -316,14 +320,17 @@ impl Array {
#[func]
pub fn position(
&self,
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// The function to apply to each item. Must return a boolean.
searcher: Func,
) -> SourceResult<Option<i64>> {
for (i, item) in self.iter().enumerate() {
let args = Args::new(searcher.span(), [item.clone()]);
if searcher.call_vm(vm, args)?.cast::<bool>().at(searcher.span())? {
if searcher
.call(engine, [item.clone()])?
.cast::<bool>()
.at(searcher.span())?
{
return Ok(Some(i as i64));
}
}
@ -388,15 +395,14 @@ impl Array {
#[func]
pub fn filter(
&self,
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// The function to apply to each item. Must return a boolean.
test: Func,
) -> SourceResult<Array> {
let mut kept = EcoVec::new();
for item in self.iter() {
let args = Args::new(test.span(), [item.clone()]);
if test.call_vm(vm, args)?.cast::<bool>().at(test.span())? {
if test.call(engine, [item.clone()])?.cast::<bool>().at(test.span())? {
kept.push(item.clone())
}
}
@ -408,17 +414,12 @@ impl Array {
#[func]
pub fn map(
self,
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// The function to apply to each item.
mapper: Func,
) -> SourceResult<Array> {
self.into_iter()
.map(|item| {
let args = Args::new(mapper.span(), [item]);
mapper.call_vm(vm, args)
})
.collect()
self.into_iter().map(|item| mapper.call(engine, [item])).collect()
}
/// Returns a new array with the values alongside their indices.
@ -518,8 +519,8 @@ impl Array {
#[func]
pub fn fold(
self,
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// The initial value to start with.
init: Value,
/// The folding function. Must have two parameters: One for the
@ -528,8 +529,7 @@ impl Array {
) -> SourceResult<Value> {
let mut acc = init;
for item in self {
let args = Args::new(folder.span(), [acc, item]);
acc = folder.call_vm(vm, args)?;
acc = folder.call(engine, [acc, item])?;
}
Ok(acc)
}
@ -579,14 +579,13 @@ impl Array {
#[func]
pub fn any(
self,
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// The function to apply to each item. Must return a boolean.
test: Func,
) -> SourceResult<bool> {
for item in self {
let args = Args::new(test.span(), [item]);
if test.call_vm(vm, args)?.cast::<bool>().at(test.span())? {
if test.call(engine, [item])?.cast::<bool>().at(test.span())? {
return Ok(true);
}
}
@ -598,14 +597,13 @@ impl Array {
#[func]
pub fn all(
self,
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// The function to apply to each item. Must return a boolean.
test: Func,
) -> SourceResult<bool> {
for item in self {
let args = Args::new(test.span(), [item]);
if !test.call_vm(vm, args)?.cast::<bool>().at(test.span())? {
if !test.call(engine, [item])?.cast::<bool>().at(test.span())? {
return Ok(false);
}
}
@ -714,8 +712,8 @@ impl Array {
#[func]
pub fn sorted(
self,
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// The callsite span.
span: Span,
/// If given, applies this function to the elements in the array to
@ -728,7 +726,7 @@ impl Array {
let mut key_of = |x: Value| match &key {
// NOTE: We are relying on `comemo`'s memoization of function
// evaluation to not excessively reevaluate the `key`.
Some(f) => f.call_vm(vm, Args::new(f.span(), [x])),
Some(f) => f.call(engine, [x]),
None => Ok(x),
};
vec.make_mut().sort_by(|a, b| {
@ -762,8 +760,8 @@ impl Array {
#[func(title = "Deduplicate")]
pub fn dedup(
self,
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// If given, applies this function to the elements in the array to
/// determine the keys to deduplicate by.
#[named]
@ -773,7 +771,7 @@ impl Array {
let mut key_of = |x: Value| match &key {
// NOTE: We are relying on `comemo`'s memoization of function
// evaluation to not excessively reevaluate the `key`.
Some(f) => f.call_vm(vm, Args::new(f.span(), [x])),
Some(f) => f.call(engine, [x]),
None => Ok(x),
};

View File

@ -12,6 +12,7 @@ use crate::diag::{At, SourceResult, StrResult};
use crate::foundations::{repr, Repr, Type, Value};
use crate::syntax::{Span, Spanned};
#[rustfmt::skip]
#[doc(inline)]
pub use typst_macros::{cast, Cast};

View File

@ -10,7 +10,7 @@ use serde::{Serialize, Serializer};
use smallvec::smallvec;
use crate::diag::{SourceResult, StrResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{
elem, func, scope, ty, Dict, Element, FromValue, Guard, IntoValue, Label,
NativeElement, Recipe, Repr, Selector, Str, Style, Styles, Value,
@ -299,16 +299,19 @@ impl Content {
/// Access the child and styles.
pub fn to_styled(&self) -> Option<(&Content, &Styles)> {
let styled = self.to::<StyledElem>()?;
let child = styled.child();
let styles = styled.styles();
Some((child, styles))
}
/// Style this content with a recipe, eagerly applying it if possible.
pub fn styled_with_recipe(self, vm: &mut Vm, recipe: Recipe) -> SourceResult<Self> {
pub fn styled_with_recipe(
self,
engine: &mut Engine,
recipe: Recipe,
) -> SourceResult<Self> {
if recipe.selector.is_none() {
recipe.apply_vm(vm, self)
recipe.apply(engine, self)
} else {
Ok(self.styled(recipe))
}

View File

@ -8,7 +8,7 @@ use time::macros::format_description;
use time::{format_description, Month, PrimitiveDateTime};
use crate::diag::{bail, StrResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{
cast, func, repr, scope, ty, Dict, Duration, Repr, Smart, Str, Value,
};
@ -296,16 +296,15 @@ impl Datetime {
/// ```
#[func]
pub fn today(
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// An offset to apply to the current UTC date. If set to `{auto}`, the
/// offset will be the local offset.
#[named]
#[default]
offset: Smart<i64>,
) -> StrResult<Datetime> {
Ok(vm
.vt
Ok(engine
.world
.today(offset.as_custom())
.ok_or("unable to get the current date")?)

View File

@ -10,13 +10,12 @@ use once_cell::sync::Lazy;
use smallvec::SmallVec;
use crate::diag::{SourceResult, StrResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{
cast, Args, Content, Dict, Func, Label, ParamInfo, Repr, Scope, Selector, StyleChain,
Styles, Value,
};
use crate::introspection::Location;
use crate::layout::Vt;
use crate::syntax::Span;
use crate::text::{Lang, Region};
use crate::util::Static;
@ -66,13 +65,17 @@ impl Element {
}
/// Construct an instance of this element.
pub fn construct(self, vm: &mut Vm, args: &mut Args) -> SourceResult<Content> {
(self.0.construct)(vm, args)
pub fn construct(
self,
engine: &mut Engine,
args: &mut Args,
) -> SourceResult<Content> {
(self.0.construct)(engine, args)
}
/// Execute the set rule for the element and return the resulting style map.
pub fn set(self, vm: &mut Vm, mut args: Args) -> SourceResult<Styles> {
let styles = (self.0.set)(vm, &mut args)?;
pub fn set(self, engine: &mut Engine, mut args: Args) -> SourceResult<Styles> {
let styles = (self.0.set)(engine, &mut args)?;
args.finish()?;
Ok(styles)
}
@ -275,7 +278,7 @@ pub trait Construct {
///
/// This is passed only the arguments that remain after execution of the
/// element's set rule.
fn construct(vm: &mut Vm, args: &mut Args) -> SourceResult<Content>
fn construct(engine: &mut Engine, args: &mut Args) -> SourceResult<Content>
where
Self: Sized;
}
@ -283,7 +286,7 @@ pub trait Construct {
/// An element's set rule.
pub trait Set {
/// Parse relevant arguments into style properties for this element.
fn set(vm: &mut Vm, args: &mut Args) -> SourceResult<Styles>
fn set(engine: &mut Engine, args: &mut Args) -> SourceResult<Styles>
where
Self: Sized;
}
@ -295,8 +298,8 @@ pub struct NativeElementData {
pub title: &'static str,
pub docs: &'static str,
pub keywords: &'static [&'static str],
pub construct: fn(&mut Vm, &mut Args) -> SourceResult<Content>,
pub set: fn(&mut Vm, &mut Args) -> SourceResult<Styles>,
pub construct: fn(&mut Engine, &mut Args) -> SourceResult<Content>,
pub set: fn(&mut Engine, &mut Args) -> SourceResult<Styles>,
pub vtable: fn(of: TypeId) -> Option<*const ()>,
pub field_id: fn(name: &str) -> Option<u8>,
pub field_name: fn(u8) -> Option<&'static str>,
@ -320,13 +323,14 @@ cast! {
/// rule.
pub trait Synthesize {
/// Prepare the element for show rule application.
fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()>;
fn synthesize(&mut self, engine: &mut Engine, styles: StyleChain)
-> SourceResult<()>;
}
/// The base recipe for an element.
pub trait Show {
/// Execute the base recipe for this element.
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content>;
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content>;
}
/// Post-process an element after it was realized.

View File

@ -6,14 +6,12 @@ use ecow::{eco_format, EcoString};
use once_cell::sync::Lazy;
use crate::diag::{bail, SourceResult, StrResult};
use crate::eval::{Route, Vm};
use crate::engine::Engine;
use crate::foundations::{
cast, repr, scope, ty, Args, CastInfo, Content, Element, IntoValue, Scope, Scopes,
Selector, Type, Value,
cast, repr, scope, ty, Args, CastInfo, Content, Element, IntoArgs, Scope, Selector,
Type, Value,
};
use crate::introspection::Locator;
use crate::layout::Vt;
use crate::syntax::{ast, FileId, Span, SyntaxNode};
use crate::syntax::{ast, Span, SyntaxNode};
use crate::util::Static;
#[doc(inline)]
@ -252,7 +250,13 @@ impl Func {
}
/// Call the function with the given arguments.
pub fn call_vm(&self, vm: &mut Vm, mut args: Args) -> SourceResult<Value> {
#[tracing::instrument(skip_all)]
pub fn call(&self, engine: &mut Engine, args: impl IntoArgs) -> SourceResult<Value> {
self.call_impl(engine, args.into_args(self.span))
}
/// Non-generic implementation of `call`.
fn call_impl(&self, engine: &mut Engine, mut args: Args) -> SourceResult<Value> {
let _span = tracing::info_span!(
"call",
name = self.name().unwrap_or("<anon>"),
@ -261,59 +265,32 @@ impl Func {
match &self.repr {
Repr::Native(native) => {
let value = (native.function)(vm, &mut args)?;
let value = (native.function)(engine, &mut args)?;
args.finish()?;
Ok(value)
}
Repr::Element(func) => {
let value = func.construct(vm, &mut args)?;
let value = func.construct(engine, &mut args)?;
args.finish()?;
Ok(Value::Content(value))
}
Repr::Closure(closure) => {
// Determine the route inside the closure.
let fresh = Route::new(closure.file);
let route = if vm.file.is_none() { fresh.track() } else { vm.route };
crate::eval::call_closure(
self,
closure,
vm.world(),
route,
vm.vt.introspector,
vm.vt.locator.track(),
TrackedMut::reborrow_mut(&mut vm.vt.tracer),
vm.depth + 1,
args,
)
}
Repr::Closure(closure) => crate::eval::call_closure(
self,
closure,
engine.world,
engine.introspector,
engine.route.track(),
engine.locator.track(),
TrackedMut::reborrow_mut(&mut engine.tracer),
args,
),
Repr::With(with) => {
args.items = with.1.items.iter().cloned().chain(args.items).collect();
with.0.call_vm(vm, args)
with.0.call(engine, args)
}
}
}
/// Call the function with a Vt.
#[tracing::instrument(skip_all)]
pub fn call_vt<T: IntoValue>(
&self,
vt: &mut Vt,
args: impl IntoIterator<Item = T>,
) -> SourceResult<Value> {
let route = Route::default();
let scopes = Scopes::new(None);
let mut locator = Locator::chained(vt.locator.track());
let vt = Vt {
world: vt.world,
introspector: vt.introspector,
locator: &mut locator,
tracer: TrackedMut::reborrow_mut(&mut vt.tracer),
};
let mut vm = Vm::new(vt, route.track(), None, scopes);
let args = Args::new(self.span(), args);
self.call_vm(&mut vm, args)
}
/// The function's span.
pub fn span(&self) -> Span {
self.span
@ -443,7 +420,7 @@ pub trait NativeFunc {
/// Defines a native function.
#[derive(Debug)]
pub struct NativeFuncData {
pub function: fn(&mut Vm, &mut Args) -> SourceResult<Value>,
pub function: fn(&mut Engine, &mut Args) -> SourceResult<Value>,
pub name: &'static str,
pub title: &'static str,
pub docs: &'static str,
@ -495,8 +472,6 @@ pub struct ParamInfo {
pub struct Closure {
/// The closure's syntax node. Must be castable to `ast::Closure`.
pub node: SyntaxNode,
/// The source file where the closure was defined.
pub file: Option<FileId>,
/// Default values of named parameters.
pub defaults: Vec<Value>,
/// Captured values from outer scopes.

View File

@ -60,6 +60,7 @@ pub use self::ty::*;
pub use self::value::*;
pub use self::version::*;
#[rustfmt::skip]
#[doc(hidden)]
pub use {
ecow::{eco_format, eco_vec},
@ -70,7 +71,8 @@ pub use {
use ecow::EcoString;
use crate::diag::{bail, SourceResult, StrResult};
use crate::eval::{EvalMode, Vm};
use crate::engine::Engine;
use crate::eval::EvalMode;
use crate::syntax::Spanned;
/// Foundational types and functions.
@ -251,8 +253,8 @@ impl assert {
/// ```
#[func(title = "Evaluate")]
pub fn eval(
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// A string of Typst code to evaluate.
///
/// The code in the string cannot interact with the file system.
@ -289,5 +291,5 @@ pub fn eval(
for (key, value) in dict {
scope.define(key, value);
}
crate::eval::eval_string(vm.world(), &text, span, mode, scope)
crate::eval::eval_string(engine.world, &text, span, mode, scope)
}

View File

@ -3,10 +3,10 @@ use std::hash::{Hash, Hasher};
use std::sync::{Arc, Mutex};
use ecow::{eco_format, EcoString};
use wasmi::{AsContext, AsContextMut, Caller, Engine, Linker, Module};
use wasmi::{AsContext, AsContextMut};
use crate::diag::{bail, At, SourceResult, StrResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{func, repr, scope, ty, Bytes};
use crate::syntax::Spanned;
use crate::World;
@ -152,14 +152,14 @@ impl Plugin {
/// Creates a new plugin from a WebAssembly file.
#[func(constructor)]
pub fn construct(
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// Path to a WebAssembly file.
path: Spanned<EcoString>,
) -> SourceResult<Plugin> {
let Spanned { v: path, span } = path;
let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
let id = span.resolve_path(&path).at(span)?;
let data = engine.world.file(id).at(span)?;
Plugin::new(data).at(span)
}
}
@ -168,11 +168,11 @@ impl Plugin {
/// Create a new plugin from raw WebAssembly bytes.
#[comemo::memoize]
pub fn new(bytes: Bytes) -> StrResult<Self> {
let engine = Engine::default();
let module = Module::new(&engine, bytes.as_slice())
let engine = wasmi::Engine::default();
let module = wasmi::Module::new(&engine, bytes.as_slice())
.map_err(|err| format!("failed to load WebAssembly module ({err})"))?;
let mut linker = Linker::new(&engine);
let mut linker = wasmi::Linker::new(&engine);
linker
.func_wrap(
"typst_env",
@ -323,7 +323,10 @@ impl Hash for Plugin {
}
/// Write the arguments to the plugin function into the plugin's memory.
fn wasm_minimal_protocol_write_args_to_buffer(mut caller: Caller<StoreData>, ptr: u32) {
fn wasm_minimal_protocol_write_args_to_buffer(
mut caller: wasmi::Caller<StoreData>,
ptr: u32,
) {
let memory = caller.get_export("memory").unwrap().into_memory().unwrap();
let arguments = std::mem::take(&mut caller.data_mut().args);
let mut offset = ptr as usize;
@ -342,7 +345,7 @@ fn wasm_minimal_protocol_write_args_to_buffer(mut caller: Caller<StoreData>, ptr
/// Extracts the output of the plugin function from the plugin's memory.
fn wasm_minimal_protocol_send_result_to_host(
mut caller: Caller<StoreData>,
mut caller: wasmi::Caller<StoreData>,
ptr: u32,
len: u32,
) {

View File

@ -8,10 +8,10 @@ use serde::{Deserialize, Serialize};
use unicode_segmentation::UnicodeSegmentation;
use crate::diag::{bail, At, SourceResult, StrResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{
cast, dict, func, repr, scope, ty, Args, Array, Bytes, Dict, Func, IntoValue, Label,
Repr, Type, Value, Version,
cast, dict, func, repr, scope, ty, Array, Bytes, Dict, Func, IntoValue, Label, Repr,
Type, Value, Version,
};
use crate::layout::Align;
use crate::syntax::{Span, Spanned};
@ -422,8 +422,8 @@ impl Str {
#[func]
pub fn replace(
&self,
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// The pattern to search for.
pattern: StrPattern,
/// The string to replace the matches with or a function that gets a
@ -449,8 +449,8 @@ impl Str {
match &replacement {
Replacement::Str(s) => output.push_str(s),
Replacement::Func(func) => {
let args = Args::new(func.span(), [dict.into_value()]);
let piece = func.call_vm(vm, args)?.cast::<Str>().at(func.span())?;
let piece =
func.call(engine, [dict])?.cast::<Str>().at(func.span())?;
output.push_str(&piece);
}
}

View File

@ -10,12 +10,10 @@ use once_cell::sync::Lazy;
use smallvec::SmallVec;
use crate::diag::{SourceResult, Trace, Tracepoint};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{
cast, elem, func, ty, Args, Content, Element, Func, NativeElement, Repr, Selector,
Show, Value,
cast, elem, func, ty, Content, Element, Func, NativeElement, Repr, Selector, Show,
};
use crate::layout::Vt;
use crate::syntax::Span;
use crate::text::{FontFamily, FontList, TextElem};
@ -58,8 +56,8 @@ struct StyleElem {
impl Show for StyleElem {
#[tracing::instrument(name = "StyleElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
Ok(self.func().call_vt(vt, [styles.to_map()])?.display())
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
Ok(self.func().call(engine, [styles.to_map()])?.display())
}
}
@ -356,37 +354,23 @@ impl Recipe {
}
/// Apply the recipe to the given content.
pub fn apply_vm(&self, vm: &mut Vm, content: Content) -> SourceResult<Content> {
match &self.transform {
Transformation::Content(content) => Ok(content.clone()),
pub fn apply(&self, engine: &mut Engine, content: Content) -> SourceResult<Content> {
let mut content = match &self.transform {
Transformation::Content(content) => content.clone(),
Transformation::Func(func) => {
let args = Args::new(self.span, [Value::Content(content.clone())]);
let mut result = func.call_vm(vm, args);
// For selector-less show rules, a tracepoint makes no sense.
let mut result = func.call(engine, [content.clone()]);
if self.selector.is_some() {
let point = || Tracepoint::Show(content.func().name().into());
result = result.trace(vm.world(), point, content.span());
result = result.trace(engine.world, point, content.span());
}
Ok(result?.display())
result?.display()
}
Transformation::Style(styles) => Ok(content.styled_with_map(styles.clone())),
}
}
/// Apply the recipe to the given content.
pub fn apply_vt(&self, vt: &mut Vt, content: Content) -> SourceResult<Content> {
match &self.transform {
Transformation::Content(content) => Ok(content.clone()),
Transformation::Func(func) => {
let mut result = func.call_vt(vt, [Value::Content(content.clone())]);
if self.selector.is_some() {
let point = || Tracepoint::Show(content.func().name().into());
result = result.trace(vt.world, point, content.span());
}
Ok(result?.display())
}
Transformation::Style(styles) => Ok(content.styled_with_map(styles.clone())),
Transformation::Style(styles) => content.styled_with_map(styles.clone()),
};
if content.span().is_detached() {
content = content.spanned(self.span);
}
Ok(content)
}
}

View File

@ -8,6 +8,7 @@ use crate::diag::StrResult;
use crate::foundations::{cast, func, Func, NativeFuncData, Repr, Scope, Value};
use crate::util::Static;
#[rustfmt::skip]
#[doc(inline)]
pub use typst_macros::{scope, ty};

View File

@ -6,6 +6,7 @@ use ecow::{eco_format, eco_vec, EcoString, EcoVec};
use smallvec::{smallvec, SmallVec};
use crate::diag::{At, SourceResult, StrResult};
use crate::engine::{Engine, Route};
use crate::eval::Tracer;
use crate::foundations::{
cast, elem, func, scope, select_where, ty, Array, Content, Element, Func, IntoValue,
@ -13,7 +14,7 @@ use crate::foundations::{
Value,
};
use crate::introspection::{Introspector, Locatable, Location, Locator, Meta};
use crate::layout::{Frame, FrameItem, PageElem, Vt};
use crate::layout::{Frame, FrameItem, PageElem};
use crate::math::EquationElem;
use crate::model::{FigureElem, HeadingElem, Numbering, NumberingPattern};
use crate::util::NonZeroExt;
@ -222,9 +223,13 @@ impl Counter {
}
/// Gets the current and final value of the state combined in one state.
pub fn both(&self, vt: &mut Vt, location: Location) -> SourceResult<CounterState> {
let sequence = self.sequence(vt)?;
let offset = vt
pub fn both(
&self,
engine: &mut Engine,
location: Location,
) -> SourceResult<CounterState> {
let sequence = self.sequence(engine)?;
let offset = engine
.introspector
.query(&self.selector().before(location.into(), true))
.len();
@ -232,10 +237,10 @@ impl Counter {
let (mut final_state, final_page) = sequence.last().unwrap().clone();
if self.is_page() {
let at_delta =
vt.introspector.page(location).get().saturating_sub(at_page.get());
engine.introspector.page(location).get().saturating_sub(at_page.get());
at_state.step(NonZeroUsize::ONE, at_delta);
let final_delta =
vt.introspector.pages().get().saturating_sub(final_page.get());
engine.introspector.pages().get().saturating_sub(final_page.get());
final_state.step(NonZeroUsize::ONE, final_delta);
}
Ok(CounterState(smallvec![at_state.first(), final_state.first()]))
@ -247,13 +252,14 @@ impl Counter {
/// of counter updates from quadratic to linear.
fn sequence(
&self,
vt: &mut Vt,
engine: &mut Engine,
) -> SourceResult<EcoVec<(CounterState, NonZeroUsize)>> {
self.sequence_impl(
vt.world,
vt.introspector,
vt.locator.track(),
TrackedMut::reborrow_mut(&mut vt.tracer),
engine.world,
engine.introspector,
engine.route.track(),
engine.locator.track(),
TrackedMut::reborrow_mut(&mut engine.tracer),
)
}
@ -263,11 +269,18 @@ impl Counter {
&self,
world: Tracked<dyn World + '_>,
introspector: Tracked<Introspector>,
route: Tracked<Route>,
locator: Tracked<Locator>,
tracer: TrackedMut<Tracer>,
) -> SourceResult<EcoVec<(CounterState, NonZeroUsize)>> {
let mut locator = Locator::chained(locator);
let mut vt = Vt { world, introspector, locator: &mut locator, tracer };
let mut engine = Engine {
world,
introspector,
route: Route::extend(route),
locator: &mut locator,
tracer,
};
let mut state = CounterState::init(&self.0);
let mut page = NonZeroUsize::ONE;
@ -288,7 +301,7 @@ impl Counter {
Some(countable) => countable.update(),
None => Some(CounterUpdate::Step(NonZeroUsize::ONE)),
} {
state.update(&mut vt, update)?;
state.update(&mut engine, update)?;
}
stops.push((state.clone(), page));
@ -399,21 +412,22 @@ impl Counter {
#[func]
pub fn at(
&self,
/// The virtual typesetter.
vt: &mut Vt,
/// The engine.
engine: &mut Engine,
/// The location at which the counter value should be retrieved. A
/// suitable location can be retrieved from [`locate`]($locate) or
/// [`query`]($query).
location: Location,
) -> SourceResult<CounterState> {
let sequence = self.sequence(vt)?;
let offset = vt
let sequence = self.sequence(engine)?;
let offset = engine
.introspector
.query(&self.selector().before(location.into(), true))
.len();
let (mut state, page) = sequence[offset].clone();
if self.is_page() {
let delta = vt.introspector.page(location).get().saturating_sub(page.get());
let delta =
engine.introspector.page(location).get().saturating_sub(page.get());
state.step(NonZeroUsize::ONE, delta);
}
@ -425,8 +439,8 @@ impl Counter {
#[func]
pub fn final_(
&self,
/// The virtual typesetter.
vt: &mut Vt,
/// The engine.
engine: &mut Engine,
/// Can be an arbitrary location, as its value is irrelevant for the
/// method's return value. Why is it required then? Typst has to
/// evaluate parts of your code multiple times to determine all counter
@ -438,10 +452,10 @@ impl Counter {
location: Location,
) -> SourceResult<CounterState> {
let _ = location;
let sequence = self.sequence(vt)?;
let sequence = self.sequence(engine)?;
let (mut state, page) = sequence.last().unwrap().clone();
if self.is_page() {
let delta = vt.introspector.pages().get().saturating_sub(page.get());
let delta = engine.introspector.pages().get().saturating_sub(page.get());
state.step(NonZeroUsize::ONE, delta);
}
Ok(state)
@ -544,12 +558,17 @@ impl CounterState {
}
/// Advance the counter and return the numbers for the given heading.
pub fn update(&mut self, vt: &mut Vt, update: CounterUpdate) -> SourceResult<()> {
pub fn update(
&mut self,
engine: &mut Engine,
update: CounterUpdate,
) -> SourceResult<()> {
match update {
CounterUpdate::Set(state) => *self = state,
CounterUpdate::Step(level) => self.step(level, 1),
CounterUpdate::Func(func) => {
*self = func.call_vt(vt, self.0.iter().copied())?.cast().at(func.span())?
*self =
func.call(engine, self.0.iter().copied())?.cast().at(func.span())?
}
}
Ok(())
@ -575,8 +594,12 @@ impl CounterState {
}
/// Display the counter state with a numbering.
pub fn display(&self, vt: &mut Vt, numbering: &Numbering) -> SourceResult<Content> {
Ok(numbering.apply_vt(vt, &self.0)?.display())
pub fn display(
&self,
engine: &mut Engine,
numbering: &Numbering,
) -> SourceResult<Content> {
Ok(numbering.apply(engine, &self.0)?.display())
}
}
@ -608,8 +631,8 @@ struct DisplayElem {
impl Show for DisplayElem {
#[tracing::instrument(name = "DisplayElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
Ok(vt.delayed(|vt| {
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
Ok(engine.delayed(|engine| {
let location = self.location().unwrap();
let counter = self.counter();
let numbering = self
@ -633,12 +656,12 @@ impl Show for DisplayElem {
.unwrap_or_else(|| NumberingPattern::from_str("1.1").unwrap().into());
let state = if *self.both() {
counter.both(vt, location)?
counter.both(engine, location)?
} else {
counter.at(vt, location)?
counter.at(engine, location)?
};
state.display(vt, &numbering)
state.display(engine, &numbering)
}))
}
}
@ -657,7 +680,7 @@ struct UpdateElem {
impl Show for UpdateElem {
#[tracing::instrument(name = "UpdateElem::show", skip(self))]
fn show(&self, _: &mut Vt, _: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
Ok(Content::empty())
}
}
@ -693,15 +716,15 @@ impl ManualPageCounter {
}
/// Advance past a page.
pub fn visit(&mut self, vt: &mut Vt, page: &Frame) -> SourceResult<()> {
pub fn visit(&mut self, engine: &mut Engine, page: &Frame) -> SourceResult<()> {
for (_, item) in page.items() {
match item {
FrameItem::Group(group) => self.visit(vt, &group.frame)?,
FrameItem::Group(group) => self.visit(engine, &group.frame)?,
FrameItem::Meta(Meta::Elem(elem), _) => {
let Some(elem) = elem.to::<UpdateElem>() else { continue };
if *elem.key() == CounterKey::Page {
let mut state = CounterState(smallvec![self.logical]);
state.update(vt, elem.update().clone())?;
state.update(engine, elem.update().clone())?;
self.logical = state.first();
}
}

View File

@ -1,7 +1,7 @@
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{elem, func, Content, Func, NativeElement, Show, StyleChain};
use crate::introspection::Locatable;
use crate::layout::Vt;
/// Provides access to the location of content.
///
@ -37,11 +37,11 @@ struct LocateElem {
}
impl Show for LocateElem {
#[tracing::instrument(name = "LocateElem::show", skip(self, vt))]
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
Ok(vt.delayed(|vt| {
#[tracing::instrument(name = "LocateElem::show", skip(self, engine))]
fn show(&self, engine: &mut Engine, _: StyleChain) -> SourceResult<Content> {
Ok(engine.delayed(|engine| {
let location = self.location().unwrap();
Ok(self.func().call_vt(vt, [location])?.display())
Ok(self.func().call(engine, [location])?.display())
}))
}
}

View File

@ -2,7 +2,7 @@ use std::num::NonZeroUsize;
use ecow::EcoString;
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{cast, func, scope, ty, Dict, Repr};
use crate::model::Numbering;
@ -45,8 +45,8 @@ impl Location {
/// If you want to know the value of the page counter, use
/// `{counter(page).at(loc)}` instead.
#[func]
pub fn page(self, vm: &mut Vm) -> NonZeroUsize {
vm.vt.introspector.page(self)
pub fn page(self, engine: &mut Engine) -> NonZeroUsize {
engine.introspector.page(self)
}
/// Return a dictionary with the page number and the x, y position for this
@ -56,8 +56,8 @@ impl Location {
/// If you only need the page number, use `page()` instead as it allows
/// Typst to skip unnecessary work.
#[func]
pub fn position(self, vm: &mut Vm) -> Dict {
vm.vt.introspector.position(self).into()
pub fn position(self, engine: &mut Engine) -> Dict {
engine.introspector.position(self).into()
}
/// Returns the page numbering pattern of the page at this location. This
@ -68,8 +68,8 @@ impl Location {
/// If the page numbering is set to `none` at that location, this function
/// returns `none`.
#[func]
pub fn page_numbering(self, vm: &mut Vm) -> Option<Numbering> {
vm.vt.introspector.page_numbering(self).cloned()
pub fn page_numbering(self, engine: &mut Engine) -> Option<Numbering> {
engine.introspector.page_numbering(self).cloned()
}
}
@ -83,5 +83,5 @@ cast! {
type Location,
}
/// Makes this element locatable through `vt.locate`.
/// Makes this element locatable through `engine.locate`.
pub trait Locatable {}

View File

@ -1,7 +1,7 @@
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{elem, Behave, Behaviour, Content, Show, StyleChain, Value};
use crate::introspection::Locatable;
use crate::layout::Vt;
/// Exposes a value to the query system without producing visible content.
///
@ -31,7 +31,7 @@ pub struct MetadataElem {
}
impl Show for MetadataElem {
fn show(&self, _vt: &mut Vt, _styles: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, _styles: StyleChain) -> SourceResult<Content> {
Ok(Content::empty())
}
}

View File

@ -1,4 +1,4 @@
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{func, Array, LocatableSelector, Value};
use crate::introspection::Location;
@ -129,8 +129,8 @@ use crate::introspection::Location;
/// ```
#[func]
pub fn query(
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// Can be an element function like a `heading` or `figure`, a `{<label>}`
/// or a more complex selector like `{heading.where(level: 1)}`.
///
@ -152,7 +152,7 @@ pub fn query(
location: Location,
) -> Array {
let _ = location;
let vec = vm.vt.introspector.query(&target.0);
let vec = engine.introspector.query(&target.0);
vec.into_iter()
.map(|elem| Value::Content(elem.into_inner()))
.collect()

View File

@ -2,13 +2,13 @@ use comemo::{Tracked, TrackedMut};
use ecow::{eco_format, eco_vec, EcoString, EcoVec};
use crate::diag::SourceResult;
use crate::engine::{Engine, Route};
use crate::eval::Tracer;
use crate::foundations::{
cast, elem, func, scope, select_where, ty, Content, Func, NativeElement, Repr,
Selector, Show, Str, StyleChain, Value,
};
use crate::introspection::{Introspector, Locatable, Location, Locator};
use crate::layout::Vt;
use crate::World;
/// Manages stateful parts of your document.
@ -204,12 +204,13 @@ impl State {
///
/// This has to happen just once for all states, cutting down the number
/// of state updates from quadratic to linear.
fn sequence(&self, vt: &mut Vt) -> SourceResult<EcoVec<Value>> {
fn sequence(&self, engine: &mut Engine) -> SourceResult<EcoVec<Value>> {
self.sequence_impl(
vt.world,
vt.introspector,
vt.locator.track(),
TrackedMut::reborrow_mut(&mut vt.tracer),
engine.world,
engine.introspector,
engine.route.track(),
engine.locator.track(),
TrackedMut::reborrow_mut(&mut engine.tracer),
)
}
@ -219,11 +220,18 @@ impl State {
&self,
world: Tracked<dyn World + '_>,
introspector: Tracked<Introspector>,
route: Tracked<Route>,
locator: Tracked<Locator>,
tracer: TrackedMut<Tracer>,
) -> SourceResult<EcoVec<Value>> {
let mut locator = Locator::chained(locator);
let mut vt = Vt { world, introspector, locator: &mut locator, tracer };
let mut engine = Engine {
world,
introspector,
route: Route::extend(route),
locator: &mut locator,
tracer,
};
let mut state = self.init.clone();
let mut stops = eco_vec![state.clone()];
@ -231,7 +239,7 @@ impl State {
let elem = elem.to::<UpdateElem>().unwrap();
match elem.update() {
StateUpdate::Set(value) => state = value.clone(),
StateUpdate::Func(func) => state = func.call_vt(&mut vt, [state])?,
StateUpdate::Func(func) => state = func.call(&mut engine, [state])?,
}
stops.push(state.clone());
}
@ -295,15 +303,15 @@ impl State {
#[func]
pub fn at(
&self,
/// The virtual typesetter.
vt: &mut Vt,
/// The engine.
engine: &mut Engine,
/// The location at which the state's value should be retrieved. A
/// suitable location can be retrieved from [`locate`]($locate) or
/// [`query`]($query).
location: Location,
) -> SourceResult<Value> {
let sequence = self.sequence(vt)?;
let offset = vt
let sequence = self.sequence(engine)?;
let offset = engine
.introspector
.query(&self.selector().before(location.into(), true))
.len();
@ -314,8 +322,8 @@ impl State {
#[func]
pub fn final_(
&self,
/// The virtual typesetter.
vt: &mut Vt,
/// The engine.
engine: &mut Engine,
/// Can be an arbitrary location, as its value is irrelevant for the
/// method's return value. Why is it required then? As noted before,
/// Typst has to evaluate parts of your code multiple times to determine
@ -327,7 +335,7 @@ impl State {
location: Location,
) -> SourceResult<Value> {
let _ = location;
let sequence = self.sequence(vt)?;
let sequence = self.sequence(engine)?;
Ok(sequence.last().unwrap().clone())
}
}
@ -377,13 +385,13 @@ struct DisplayElem {
}
impl Show for DisplayElem {
#[tracing::instrument(name = "DisplayElem::show", skip(self, vt))]
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
Ok(vt.delayed(|vt| {
#[tracing::instrument(name = "DisplayElem::show", skip(self, engine))]
fn show(&self, engine: &mut Engine, _: StyleChain) -> SourceResult<Content> {
Ok(engine.delayed(|engine| {
let location = self.location().unwrap();
let value = self.state().at(vt, location)?;
let value = self.state().at(engine, location)?;
Ok(match self.func() {
Some(func) => func.call_vt(vt, [value])?.display(),
Some(func) => func.call(engine, [value])?.display(),
None => value.display(),
})
}))
@ -404,7 +412,7 @@ struct UpdateElem {
impl Show for UpdateElem {
#[tracing::instrument(name = "UpdateElem::show")]
fn show(&self, _: &mut Vt, _: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
Ok(Content::empty())
}
}

View File

@ -3,10 +3,11 @@ use std::ops::Add;
use ecow::{eco_format, EcoString};
use crate::diag::{bail, SourceResult, StrResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, func, scope, ty, Content, Fold, Repr, Resolve, Show, StyleChain,
};
use crate::layout::{Abs, Axes, Axis, Dir, Side, Vt};
use crate::layout::{Abs, Axes, Axis, Dir, Side};
use crate::text::TextElem;
/// Aligns content horizontally and vertically.
@ -46,7 +47,7 @@ pub struct AlignElem {
impl Show for AlignElem {
#[tracing::instrument(name = "AlignElem::show", skip_all)]
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
Ok(self
.body()
.clone()

View File

@ -1,9 +1,10 @@
use std::num::NonZeroUsize;
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{elem, Behave, Behaviour, Content, StyleChain};
use crate::layout::{
Abs, Axes, Dir, Fragment, Frame, Layout, Length, Point, Ratio, Regions, Rel, Size, Vt,
Abs, Axes, Dir, Fragment, Frame, Layout, Length, Point, Ratio, Regions, Rel, Size,
};
use crate::text::TextElem;
use crate::util::Numeric;
@ -60,7 +61,7 @@ impl Layout for ColumnsElem {
#[tracing::instrument(name = "ColumnsElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -69,7 +70,7 @@ impl Layout for ColumnsElem {
// Separating the infinite space into infinite columns does not make
// much sense.
if !regions.size.x.is_finite() {
return body.layout(vt, styles, regions);
return body.layout(engine, styles, regions);
}
// Determine the width of the gutter and each column.
@ -94,7 +95,7 @@ impl Layout for ColumnsElem {
};
// Layout the children.
let mut frames = body.layout(vt, styles, pod)?.into_iter();
let mut frames = body.layout(engine, styles, pod)?.into_iter();
let mut finished = vec![];
let dir = TextElem::dir_in(styles);

View File

@ -1,10 +1,11 @@
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{
cast, elem, AutoValue, Content, NativeElement, Resolve, Smart, StyleChain, Value,
};
use crate::layout::{
Abs, Axes, Corners, Em, Fr, Fragment, FrameKind, Layout, Length, Ratio, Regions, Rel,
Sides, Size, Spacing, VElem, Vt,
Sides, Size, Spacing, VElem,
};
use crate::util::Numeric;
use crate::visualize::{clip_rect, Paint, Stroke};
@ -112,7 +113,7 @@ impl Layout for BoxElem {
#[tracing::instrument(name = "BoxElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -140,7 +141,7 @@ impl Layout for BoxElem {
// Select the appropriate base and expansion for the child depending
// on whether it is automatically or relatively sized.
let pod = Regions::one(size, expand);
let mut frame = body.layout(vt, styles, pod)?.into_frame();
let mut frame = body.layout(engine, styles, pod)?.into_frame();
// Enforce correct size.
*frame.size_mut() = expand.select(size, frame.size());
@ -344,7 +345,7 @@ impl Layout for BlockElem {
#[tracing::instrument(name = "BlockElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -368,7 +369,7 @@ impl Layout for BlockElem {
// Measure to ensure frames for all regions have the same width.
if sizing.x == Smart::Auto {
let pod = Regions::one(size, Axes::splat(false));
let frame = body.measure(vt, styles, pod)?.into_frame();
let frame = body.measure(engine, styles, pod)?.into_frame();
size.x = frame.width();
expand.x = true;
}
@ -403,7 +404,7 @@ impl Layout for BlockElem {
pod.last = None;
}
let mut frames = body.layout(vt, styles, pod)?.into_frames();
let mut frames = body.layout(engine, styles, pod)?.into_frames();
for (frame, &height) in frames.iter_mut().zip(&heights) {
*frame.size_mut() =
expand.select(Size::new(size.x, height), frame.size());
@ -411,7 +412,7 @@ impl Layout for BlockElem {
frames
} else {
let pod = Regions::one(size, expand);
let mut frames = body.layout(vt, styles, pod)?.into_frames();
let mut frames = body.layout(engine, styles, pod)?.into_frames();
*frames[0].size_mut() = expand.select(size, frames[0].size());
frames
};

View File

@ -1,12 +1,13 @@
use comemo::Prehashed;
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{elem, Content, NativeElement, Resolve, Smart, StyleChain};
use crate::introspection::{Meta, MetaElem};
use crate::layout::{
Abs, AlignElem, Axes, BlockElem, ColbreakElem, ColumnsElem, FixedAlign, Fr, Fragment,
Frame, FrameItem, Layout, PlaceElem, Point, Regions, Rel, Size, Spacing, VAlign,
VElem, Vt,
VElem,
};
use crate::model::{FootnoteElem, FootnoteEntry, ParElem};
use crate::util::Numeric;
@ -30,7 +31,7 @@ impl Layout for FlowElem {
#[tracing::instrument(name = "FlowElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -51,9 +52,9 @@ impl Layout for FlowElem {
}
if let Some(elem) = child.to::<VElem>() {
layouter.layout_spacing(vt, elem, styles)?;
layouter.layout_spacing(engine, elem, styles)?;
} else if let Some(elem) = child.to::<ParElem>() {
layouter.layout_par(vt, elem, styles)?;
layouter.layout_par(engine, elem, styles)?;
} else if child.is::<LineElem>()
|| child.is::<RectElem>()
|| child.is::<SquareElem>()
@ -64,7 +65,7 @@ impl Layout for FlowElem {
|| child.is::<PathElem>()
{
let layoutable = child.with::<dyn Layout>().unwrap();
layouter.layout_single(vt, layoutable, styles)?;
layouter.layout_single(engine, layoutable, styles)?;
} else if child.is::<MetaElem>() {
let mut frame = Frame::soft(Size::zero());
frame.meta(styles, true);
@ -75,20 +76,20 @@ impl Layout for FlowElem {
movable: false,
});
} else if let Some(placed) = child.to::<PlaceElem>() {
layouter.layout_placed(vt, placed, styles)?;
layouter.layout_placed(engine, placed, styles)?;
} else if child.can::<dyn Layout>() {
layouter.layout_multiple(vt, child, styles)?;
layouter.layout_multiple(engine, child, styles)?;
} else if child.is::<ColbreakElem>() {
if !layouter.regions.backlog.is_empty() || layouter.regions.last.is_some()
{
layouter.finish_region(vt)?;
layouter.finish_region(engine)?;
}
} else {
bail!(child.span(), "unexpected flow child");
}
}
layouter.finish(vt)
layouter.finish(engine)
}
}
@ -193,12 +194,12 @@ impl<'a> FlowLayouter<'a> {
#[tracing::instrument(name = "FlowLayouter::layout_spacing", skip_all)]
fn layout_spacing(
&mut self,
vt: &mut Vt,
engine: &mut Engine,
v: &VElem,
styles: StyleChain,
) -> SourceResult<()> {
self.layout_item(
vt,
engine,
match v.amount() {
Spacing::Rel(rel) => FlowItem::Absolute(
rel.resolve(styles).relative_to(self.initial.y),
@ -213,7 +214,7 @@ impl<'a> FlowLayouter<'a> {
#[tracing::instrument(name = "FlowLayouter::layout_par", skip_all)]
fn layout_par(
&mut self,
vt: &mut Vt,
engine: &mut Engine,
par: &ParElem,
styles: StyleChain,
) -> SourceResult<()> {
@ -221,7 +222,13 @@ impl<'a> FlowLayouter<'a> {
let leading = ParElem::leading_in(styles);
let consecutive = self.last_was_par;
let lines = par
.layout(vt, styles, consecutive, self.regions.base(), self.regions.expand.x)?
.layout(
engine,
styles,
consecutive,
self.regions.base(),
self.regions.expand.x,
)?
.into_frames();
let mut sticky = self.items.len();
@ -236,20 +243,20 @@ impl<'a> FlowLayouter<'a> {
if let Some(first) = lines.first() {
if !self.regions.size.y.fits(first.height()) && !self.regions.in_last() {
let carry: Vec<_> = self.items.drain(sticky..).collect();
self.finish_region(vt)?;
self.finish_region(engine)?;
for item in carry {
self.layout_item(vt, item)?;
self.layout_item(engine, item)?;
}
}
}
for (i, frame) in lines.into_iter().enumerate() {
if i > 0 {
self.layout_item(vt, FlowItem::Absolute(leading, true))?;
self.layout_item(engine, FlowItem::Absolute(leading, true))?;
}
self.layout_item(
vt,
engine,
FlowItem::Frame { frame, align, sticky: false, movable: true },
)?;
}
@ -262,15 +269,18 @@ impl<'a> FlowLayouter<'a> {
#[tracing::instrument(name = "FlowLayouter::layout_single", skip_all)]
fn layout_single(
&mut self,
vt: &mut Vt,
engine: &mut Engine,
content: &dyn Layout,
styles: StyleChain,
) -> SourceResult<()> {
let align = AlignElem::alignment_in(styles).resolve(styles);
let sticky = BlockElem::sticky_in(styles);
let pod = Regions::one(self.regions.base(), Axes::splat(false));
let frame = content.layout(vt, styles, pod)?.into_frame();
self.layout_item(vt, FlowItem::Frame { frame, align, sticky, movable: true })?;
let frame = content.layout(engine, styles, pod)?.into_frame();
self.layout_item(
engine,
FlowItem::Frame { frame, align, sticky, movable: true },
)?;
self.last_was_par = false;
Ok(())
}
@ -278,7 +288,7 @@ impl<'a> FlowLayouter<'a> {
/// Layout a placed element.
fn layout_placed(
&mut self,
vt: &mut Vt,
engine: &mut Engine,
placed: &PlaceElem,
styles: StyleChain,
) -> SourceResult<()> {
@ -290,15 +300,15 @@ impl<'a> FlowLayouter<'a> {
align.x().unwrap_or_default().resolve(styles)
});
let y_align = alignment.map(|align| align.y().map(VAlign::fix));
let frame = placed.layout(vt, styles, self.regions)?.into_frame();
let frame = placed.layout(engine, styles, self.regions)?.into_frame();
let item = FlowItem::Placed { frame, x_align, y_align, delta, float, clearance };
self.layout_item(vt, item)
self.layout_item(engine, item)
}
/// Layout into multiple regions.
fn layout_multiple(
&mut self,
vt: &mut Vt,
engine: &mut Engine,
block: &Content,
styles: StyleChain,
) -> SourceResult<()> {
@ -313,7 +323,7 @@ impl<'a> FlowLayouter<'a> {
if self.regions.is_full() {
// Skip directly if region is already full.
self.finish_region(vt)?;
self.finish_region(engine)?;
}
// How to align the block.
@ -328,7 +338,7 @@ impl<'a> FlowLayouter<'a> {
// Layout the block itself.
let sticky = BlockElem::sticky_in(styles);
let fragment = block.layout(vt, styles, self.regions)?;
let fragment = block.layout(engine, styles, self.regions)?;
for (i, frame) in fragment.into_iter().enumerate() {
// Find footnotes in the frame.
@ -337,14 +347,14 @@ impl<'a> FlowLayouter<'a> {
}
if i > 0 {
self.finish_region(vt)?;
self.finish_region(engine)?;
}
let item = FlowItem::Frame { frame, align, sticky, movable: false };
self.layout_item(vt, item)?;
self.layout_item(engine, item)?;
}
self.try_handle_footnotes(vt, notes)?;
self.try_handle_footnotes(engine, notes)?;
self.root = is_root;
self.regions.root = false;
@ -355,7 +365,11 @@ impl<'a> FlowLayouter<'a> {
/// Layout a finished frame.
#[tracing::instrument(name = "FlowLayouter::layout_item", skip_all)]
fn layout_item(&mut self, vt: &mut Vt, mut item: FlowItem) -> SourceResult<()> {
fn layout_item(
&mut self,
engine: &mut Engine,
mut item: FlowItem,
) -> SourceResult<()> {
match item {
FlowItem::Absolute(v, weak) => {
if weak
@ -372,7 +386,7 @@ impl<'a> FlowLayouter<'a> {
FlowItem::Frame { ref frame, movable, .. } => {
let height = frame.height();
if !self.regions.size.y.fits(height) && !self.regions.in_last() {
self.finish_region(vt)?;
self.finish_region(engine)?;
}
self.regions.size.y -= height;
@ -380,12 +394,12 @@ impl<'a> FlowLayouter<'a> {
let mut notes = Vec::new();
find_footnotes(&mut notes, frame);
self.items.push(item);
if !self.handle_footnotes(vt, &mut notes, true, false)? {
if !self.handle_footnotes(engine, &mut notes, true, false)? {
let item = self.items.pop();
self.finish_region(vt)?;
self.finish_region(engine)?;
self.items.extend(item);
self.regions.size.y -= height;
self.handle_footnotes(vt, &mut notes, true, true)?;
self.handle_footnotes(engine, &mut notes, true, true)?;
}
return Ok(());
}
@ -429,7 +443,7 @@ impl<'a> FlowLayouter<'a> {
if self.root {
let mut notes = vec![];
find_footnotes(&mut notes, frame);
self.try_handle_footnotes(vt, notes)?;
self.try_handle_footnotes(engine, notes)?;
}
}
FlowItem::Footnote(_) => {}
@ -440,7 +454,7 @@ impl<'a> FlowLayouter<'a> {
}
/// Finish the frame for one region.
fn finish_region(&mut self, vt: &mut Vt) -> SourceResult<()> {
fn finish_region(&mut self, engine: &mut Engine) -> SourceResult<()> {
// Trim weak spacing.
while self
.items
@ -567,23 +581,23 @@ impl<'a> FlowLayouter<'a> {
// Try to place floats.
for item in std::mem::take(&mut self.pending_floats) {
self.layout_item(vt, item)?;
self.layout_item(engine, item)?;
}
Ok(())
}
/// Finish layouting and return the resulting fragment.
fn finish(mut self, vt: &mut Vt) -> SourceResult<Fragment> {
fn finish(mut self, engine: &mut Engine) -> SourceResult<Fragment> {
if self.expand.y {
while !self.regions.backlog.is_empty() {
self.finish_region(vt)?;
self.finish_region(engine)?;
}
}
self.finish_region(vt)?;
self.finish_region(engine)?;
while !self.items.is_empty() {
self.finish_region(vt)?;
self.finish_region(engine)?;
}
Ok(Fragment::frames(self.finished))
@ -593,12 +607,12 @@ impl<'a> FlowLayouter<'a> {
impl FlowLayouter<'_> {
fn try_handle_footnotes(
&mut self,
vt: &mut Vt,
engine: &mut Engine,
mut notes: Vec<FootnoteElem>,
) -> SourceResult<()> {
if self.root && !self.handle_footnotes(vt, &mut notes, false, false)? {
self.finish_region(vt)?;
self.handle_footnotes(vt, &mut notes, false, true)?;
if self.root && !self.handle_footnotes(engine, &mut notes, false, false)? {
self.finish_region(engine)?;
self.handle_footnotes(engine, &mut notes, false, true)?;
}
Ok(())
}
@ -607,7 +621,7 @@ impl FlowLayouter<'_> {
#[tracing::instrument(skip_all)]
fn handle_footnotes(
&mut self,
vt: &mut Vt,
engine: &mut Engine,
notes: &mut Vec<FootnoteElem>,
movable: bool,
force: bool,
@ -624,14 +638,14 @@ impl FlowLayouter<'_> {
}
if !self.has_footnotes {
self.layout_footnote_separator(vt)?;
self.layout_footnote_separator(engine)?;
}
self.regions.size.y -= self.footnote_config.gap;
let checkpoint = vt.locator.clone();
let checkpoint = engine.locator.clone();
let frames = FootnoteEntry::new(notes[k].clone())
.pack()
.layout(vt, self.styles, self.regions.with_root(false))?
.layout(engine, self.styles, self.regions.with_root(false))?
.into_frames();
// If the entries didn't fit, abort (to keep footnote and entry
@ -649,8 +663,8 @@ impl FlowLayouter<'_> {
self.regions.size.y -= item.height();
}
// Undo Vt modifications.
*vt.locator = checkpoint;
// Undo locator modifications.
*engine.locator = checkpoint;
return Ok(false);
}
@ -659,8 +673,8 @@ impl FlowLayouter<'_> {
for (i, frame) in frames.into_iter().enumerate() {
find_footnotes(notes, &frame);
if i > 0 {
self.finish_region(vt)?;
self.layout_footnote_separator(vt)?;
self.finish_region(engine)?;
self.layout_footnote_separator(engine)?;
self.regions.size.y -= self.footnote_config.gap;
}
self.regions.size.y -= frame.height();
@ -682,12 +696,12 @@ impl FlowLayouter<'_> {
/// Layout and save the footnote separator, typically a line.
#[tracing::instrument(skip_all)]
fn layout_footnote_separator(&mut self, vt: &mut Vt) -> SourceResult<()> {
fn layout_footnote_separator(&mut self, engine: &mut Engine) -> SourceResult<()> {
let expand = Axes::new(self.regions.expand.x, false);
let pod = Regions::one(self.regions.base(), expand);
let separator = &self.footnote_config.separator;
let mut frame = separator.layout(vt, self.styles, pod)?.into_frame();
let mut frame = separator.layout(engine, self.styles, pod)?.into_frame();
frame.size_mut().y += self.footnote_config.clearance;
frame.translate(Point::with_y(self.footnote_config.clearance));

View File

@ -3,12 +3,13 @@ use std::num::NonZeroUsize;
use smallvec::{smallvec, SmallVec};
use crate::diag::{bail, SourceResult, StrResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, Array, Content, NativeElement, Resolve, StyleChain, Value,
};
use crate::layout::{
Abs, Axes, Dir, Fr, Fragment, Frame, Layout, Length, Point, Regions, Rel, Size,
Sizing, Vt,
Sizing,
};
use crate::syntax::Span;
use crate::text::TextElem;
@ -128,7 +129,7 @@ impl Layout for GridElem {
#[tracing::instrument(name = "GridElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -148,7 +149,7 @@ impl Layout for GridElem {
);
// Measure the columns and layout the grid row-by-row.
Ok(layouter.layout(vt)?.fragment)
Ok(layouter.layout(engine)?.fragment)
}
}
@ -311,24 +312,24 @@ impl<'a> GridLayouter<'a> {
}
/// Determines the columns sizes and then layouts the grid row-by-row.
pub fn layout(mut self, vt: &mut Vt) -> SourceResult<GridLayout> {
self.measure_columns(vt)?;
pub fn layout(mut self, engine: &mut Engine) -> SourceResult<GridLayout> {
self.measure_columns(engine)?;
for y in 0..self.rows.len() {
// Skip to next region if current one is full, but only for content
// rows, not for gutter rows.
if self.regions.is_full() && (!self.has_gutter || y % 2 == 0) {
self.finish_region(vt)?;
self.finish_region(engine)?;
}
match self.rows[y] {
Sizing::Auto => self.layout_auto_row(vt, y)?,
Sizing::Rel(v) => self.layout_relative_row(vt, v, y)?,
Sizing::Auto => self.layout_auto_row(engine, y)?,
Sizing::Rel(v) => self.layout_relative_row(engine, v, y)?,
Sizing::Fr(v) => self.lrows.push(Row::Fr(v, y)),
}
}
self.finish_region(vt)?;
self.finish_region(engine)?;
Ok(GridLayout {
fragment: Fragment::frames(self.finished),
@ -339,7 +340,7 @@ impl<'a> GridLayouter<'a> {
/// Determine all column sizes.
#[tracing::instrument(name = "GridLayouter::measure_columns", skip_all)]
fn measure_columns(&mut self, vt: &mut Vt) -> SourceResult<()> {
fn measure_columns(&mut self, engine: &mut Engine) -> SourceResult<()> {
// Sum of sizes of resolved relative tracks.
let mut rel = Abs::zero();
@ -365,7 +366,7 @@ impl<'a> GridLayouter<'a> {
let available = self.regions.size.x - rel;
if available >= Abs::zero() {
// Determine size of auto columns.
let (auto, count) = self.measure_auto_columns(vt, available)?;
let (auto, count) = self.measure_auto_columns(engine, available)?;
// If there is remaining space, distribute it to fractional columns,
// otherwise shrink auto columns.
@ -386,7 +387,7 @@ impl<'a> GridLayouter<'a> {
/// Measure the size that is available to auto columns.
fn measure_auto_columns(
&mut self,
vt: &mut Vt,
engine: &mut Engine,
available: Abs,
) -> SourceResult<(Abs, usize)> {
let mut auto = Abs::zero();
@ -413,7 +414,7 @@ impl<'a> GridLayouter<'a> {
let size = Size::new(available, height);
let pod = Regions::one(size, Axes::splat(false));
let frame = cell.measure(vt, self.styles, pod)?.into_frame();
let frame = cell.measure(engine, self.styles, pod)?.into_frame();
resolved.set_max(frame.width());
}
}
@ -474,14 +475,14 @@ impl<'a> GridLayouter<'a> {
/// Layout a row with automatic height. Such a row may break across multiple
/// regions.
fn layout_auto_row(&mut self, vt: &mut Vt, y: usize) -> SourceResult<()> {
fn layout_auto_row(&mut self, engine: &mut Engine, y: usize) -> SourceResult<()> {
// Determine the size for each region of the row. If the first region
// ends up empty for some column, skip the region and remeasure.
let mut resolved = match self.measure_auto_row(vt, y, true)? {
let mut resolved = match self.measure_auto_row(engine, y, true)? {
Some(resolved) => resolved,
None => {
self.finish_region(vt)?;
self.measure_auto_row(vt, y, false)?.unwrap()
self.finish_region(engine)?;
self.measure_auto_row(engine, y, false)?.unwrap()
}
};
@ -492,7 +493,7 @@ impl<'a> GridLayouter<'a> {
// Layout into a single region.
if let &[first] = resolved.as_slice() {
let frame = self.layout_single_row(vt, first, y)?;
let frame = self.layout_single_row(engine, first, y)?;
self.push_row(frame, y);
return Ok(());
}
@ -510,12 +511,12 @@ impl<'a> GridLayouter<'a> {
}
// Layout into multiple regions.
let fragment = self.layout_multi_row(vt, &resolved, y)?;
let fragment = self.layout_multi_row(engine, &resolved, y)?;
let len = fragment.len();
for (i, frame) in fragment.into_iter().enumerate() {
self.push_row(frame, y);
if i + 1 < len {
self.finish_region(vt)?;
self.finish_region(engine)?;
}
}
@ -526,7 +527,7 @@ impl<'a> GridLayouter<'a> {
/// if `can_skip` is false.
fn measure_auto_row(
&mut self,
vt: &mut Vt,
engine: &mut Engine,
y: usize,
can_skip: bool,
) -> SourceResult<Option<Vec<Abs>>> {
@ -537,7 +538,7 @@ impl<'a> GridLayouter<'a> {
let mut pod = self.regions;
pod.size.x = rcol;
let frames = cell.measure(vt, self.styles, pod)?.into_frames();
let frames = cell.measure(engine, self.styles, pod)?.into_frames();
// Skip the first region if one cell in it is empty. Then,
// remeasure.
@ -568,17 +569,17 @@ impl<'a> GridLayouter<'a> {
/// multiple regions, but it may force a region break.
fn layout_relative_row(
&mut self,
vt: &mut Vt,
engine: &mut Engine,
v: Rel<Length>,
y: usize,
) -> SourceResult<()> {
let resolved = v.resolve(self.styles).relative_to(self.regions.base().y);
let frame = self.layout_single_row(vt, resolved, y)?;
let frame = self.layout_single_row(engine, resolved, y)?;
// Skip to fitting region.
let height = frame.height();
while !self.regions.size.y.fits(height) && !self.regions.in_last() {
self.finish_region(vt)?;
self.finish_region(engine)?;
// Don't skip multiple regions for gutter and don't push a row.
if self.has_gutter && y % 2 == 1 {
@ -594,7 +595,7 @@ impl<'a> GridLayouter<'a> {
/// Layout a row with fixed height and return its frame.
fn layout_single_row(
&mut self,
vt: &mut Vt,
engine: &mut Engine,
height: Abs,
y: usize,
) -> SourceResult<Frame> {
@ -612,7 +613,7 @@ impl<'a> GridLayouter<'a> {
if self.rows[y] == Sizing::Auto {
pod.full = self.regions.full;
}
let frame = cell.layout(vt, self.styles, pod)?.into_frame();
let frame = cell.layout(engine, self.styles, pod)?.into_frame();
output.push_frame(pos, frame);
}
@ -625,7 +626,7 @@ impl<'a> GridLayouter<'a> {
/// Layout a row spanning multiple regions.
fn layout_multi_row(
&mut self,
vt: &mut Vt,
engine: &mut Engine,
heights: &[Abs],
y: usize,
) -> SourceResult<Fragment> {
@ -648,7 +649,7 @@ impl<'a> GridLayouter<'a> {
pod.size.x = rcol;
// Push the layouted frames into the individual output frames.
let fragment = cell.layout(vt, self.styles, pod)?;
let fragment = cell.layout(engine, self.styles, pod)?;
for (output, frame) in outputs.iter_mut().zip(fragment) {
output.push_frame(pos, frame);
}
@ -667,7 +668,7 @@ impl<'a> GridLayouter<'a> {
}
/// Finish rows for one region.
fn finish_region(&mut self, vt: &mut Vt) -> SourceResult<()> {
fn finish_region(&mut self, engine: &mut Engine) -> SourceResult<()> {
// Determine the height of existing rows in the region.
let mut used = Abs::zero();
let mut fr = Fr::zero();
@ -697,7 +698,7 @@ impl<'a> GridLayouter<'a> {
Row::Fr(v, y) => {
let remaining = self.regions.full - used;
let height = v.share(fr, remaining);
(self.layout_single_row(vt, height, y)?, y)
(self.layout_single_row(engine, height, y)?, y)
}
};

View File

@ -1,9 +1,9 @@
use smallvec::smallvec;
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{elem, Content, Show, StyleChain};
use crate::introspection::{Meta, MetaElem};
use crate::layout::Vt;
/// Hides content without affecting layout.
///
@ -26,7 +26,7 @@ pub struct HideElem {
impl Show for HideElem {
#[tracing::instrument(name = "HideElem::show", skip(self))]
fn show(&self, _: &mut Vt, _: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
Ok(self.body().clone().styled(MetaElem::set_data(smallvec![Meta::Hide])))
}
}

View File

@ -11,12 +11,13 @@ use self::shaping::{
END_PUNCT_PAT,
};
use crate::diag::{bail, SourceResult};
use crate::engine::{Engine, Route};
use crate::eval::Tracer;
use crate::foundations::{Content, Resolve, Smart, StyleChain};
use crate::introspection::{Introspector, Locator, MetaElem};
use crate::layout::{
Abs, AlignElem, Axes, BoxElem, Dir, Em, FixedAlign, Fr, Fragment, Frame, HElem,
Layout, Point, Regions, Size, Sizing, Spacing, Vt,
Layout, Point, Regions, Size, Sizing, Spacing,
};
use crate::math::EquationElem;
use crate::model::{Linebreaks, ParElem};
@ -30,7 +31,7 @@ use crate::World;
/// Layout's content inline.
pub(crate) fn layout_inline(
children: &[Prehashed<Content>],
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
consecutive: bool,
region: Size,
@ -42,6 +43,7 @@ pub(crate) fn layout_inline(
children: &[Prehashed<Content>],
world: Tracked<dyn World + '_>,
introspector: Tracked<Introspector>,
route: Tracked<Route>,
locator: Tracked<Locator>,
tracer: TrackedMut<Tracer>,
styles: StyleChain,
@ -50,7 +52,13 @@ pub(crate) fn layout_inline(
expand: bool,
) -> SourceResult<Fragment> {
let mut locator = Locator::chained(locator);
let mut vt = Vt { world, introspector, locator: &mut locator, tracer };
let mut engine = Engine {
world,
introspector,
route: Route::extend(route),
locator: &mut locator,
tracer,
};
// Collect all text into one string for BiDi analysis.
let (text, segments, spans) = collect(children, &styles, consecutive)?;
@ -58,28 +66,29 @@ pub(crate) fn layout_inline(
// Perform BiDi analysis and then prepare paragraph layout by building a
// representation on which we can do line breaking without layouting
// each and every line from scratch.
let p = prepare(&mut vt, children, &text, segments, spans, styles, region)?;
let p = prepare(&mut engine, children, &text, segments, spans, styles, region)?;
// Break the paragraph into lines.
let lines = linebreak(&vt, &p, region.x - p.hang);
let lines = linebreak(&engine, &p, region.x - p.hang);
// Stack the lines into one frame per region.
finalize(&mut vt, &p, &lines, region, expand)
finalize(&mut engine, &p, &lines, region, expand)
}
let fragment = cached(
children,
vt.world,
vt.introspector,
vt.locator.track(),
TrackedMut::reborrow_mut(&mut vt.tracer),
engine.world,
engine.introspector,
engine.route.track(),
engine.locator.track(),
TrackedMut::reborrow_mut(&mut engine.tracer),
styles,
consecutive,
region,
expand,
)?;
vt.locator.visit_frames(&fragment);
engine.locator.visit_frames(&fragment);
Ok(fragment)
}
@ -520,7 +529,7 @@ fn collect<'a>(
/// Prepare paragraph layout by shaping the whole paragraph and layouting all
/// contained inline-level content.
fn prepare<'a>(
vt: &mut Vt,
engine: &mut Engine,
children: &'a [Prehashed<Content>],
text: &'a str,
segments: Vec<(Segment<'a>, StyleChain<'a>)>,
@ -546,7 +555,7 @@ fn prepare<'a>(
let end = cursor + segment.len();
match segment {
Segment::Text(_) => {
shape_range(&mut items, vt, &bidi, cursor..end, &spans, styles);
shape_range(&mut items, engine, &bidi, cursor..end, &spans, styles);
}
Segment::Spacing(spacing) => match spacing {
Spacing::Rel(v) => {
@ -559,7 +568,7 @@ fn prepare<'a>(
},
Segment::Equation(equation) => {
let pod = Regions::one(region, Axes::splat(false));
let mut frame = equation.layout(vt, styles, pod)?.into_frame();
let mut frame = equation.layout(engine, styles, pod)?.into_frame();
frame.translate(Point::with_y(TextElem::baseline_in(styles)));
items.push(Item::Frame(frame));
}
@ -568,7 +577,7 @@ fn prepare<'a>(
items.push(Item::Fractional(v, Some((elem, styles))));
} else {
let pod = Regions::one(region, Axes::splat(false));
let mut frame = elem.layout(vt, styles, pod)?.into_frame();
let mut frame = elem.layout(engine, styles, pod)?.into_frame();
frame.translate(Point::with_y(TextElem::baseline_in(styles)));
items.push(Item::Frame(frame));
}
@ -655,7 +664,7 @@ fn add_cjk_latin_spacing(items: &mut [Item]) {
/// items for them.
fn shape_range<'a>(
items: &mut Vec<Item<'a>>,
vt: &Vt,
engine: &Engine,
bidi: &BidiInfo<'a>,
range: Range,
spans: &SpanMapper,
@ -666,8 +675,16 @@ fn shape_range<'a>(
let region = TextElem::region_in(styles);
let mut process = |range: Range, level: BidiLevel| {
let dir = if level.is_ltr() { Dir::LTR } else { Dir::RTL };
let shaped =
shape(vt, range.start, &bidi.text[range], spans, styles, dir, lang, region);
let shaped = shape(
engine,
range.start,
&bidi.text[range],
spans,
styles,
dir,
lang,
region,
);
items.push(Item::Text(shaped));
};
@ -732,7 +749,7 @@ fn shared_get<T: PartialEq>(
}
/// Find suitable linebreaks.
fn linebreak<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
fn linebreak<'a>(engine: &Engine, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
let linebreaks = p.linebreaks.unwrap_or_else(|| {
if p.justify {
Linebreaks::Optimized
@ -742,22 +759,26 @@ fn linebreak<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
});
match linebreaks {
Linebreaks::Simple => linebreak_simple(vt, p, width),
Linebreaks::Optimized => linebreak_optimized(vt, p, width),
Linebreaks::Simple => linebreak_simple(engine, p, width),
Linebreaks::Optimized => linebreak_optimized(engine, p, width),
}
}
/// Perform line breaking in simple first-fit style. This means that we build
/// lines greedily, always taking the longest possible line. This may lead to
/// very unbalanced line, but is fast and simple.
fn linebreak_simple<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
fn linebreak_simple<'a>(
engine: &Engine,
p: &'a Preparation<'a>,
width: Abs,
) -> Vec<Line<'a>> {
let mut lines = Vec::with_capacity(16);
let mut start = 0;
let mut last = None;
breakpoints(p, |end, breakpoint| {
// Compute the line and its size.
let mut attempt = line(vt, p, start..end, breakpoint);
let mut attempt = line(engine, p, start..end, breakpoint);
// If the line doesn't fit anymore, we push the last fitting attempt
// into the stack and rebuild the line from the attempt's end. The
@ -766,7 +787,7 @@ fn linebreak_simple<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line
if let Some((last_attempt, last_end)) = last.take() {
lines.push(last_attempt);
start = last_end;
attempt = line(vt, p, start..end, breakpoint);
attempt = line(engine, p, start..end, breakpoint);
}
}
@ -806,7 +827,11 @@ fn linebreak_simple<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line
/// computed and stored in dynamic programming table) is minimal. The final
/// result is simply the layout determined for the last breakpoint at the end of
/// text.
fn linebreak_optimized<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> {
fn linebreak_optimized<'a>(
engine: &Engine,
p: &'a Preparation<'a>,
width: Abs,
) -> Vec<Line<'a>> {
/// The cost of a line or paragraph layout.
type Cost = f64;
@ -829,7 +854,7 @@ fn linebreak_optimized<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<L
let mut table = vec![Entry {
pred: 0,
total: 0.0,
line: line(vt, p, 0..0, Breakpoint::Mandatory),
line: line(engine, p, 0..0, Breakpoint::Mandatory),
}];
let em = p.size;
@ -844,7 +869,7 @@ fn linebreak_optimized<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<L
// Layout the line.
let start = pred.line.end;
let attempt = line(vt, p, start..end, breakpoint);
let attempt = line(engine, p, start..end, breakpoint);
// Determine how much the line's spaces would need to be stretched
// to make it the desired width.
@ -953,7 +978,7 @@ fn linebreak_optimized<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<L
/// Create a line which spans the given range.
fn line<'a>(
vt: &Vt,
engine: &Engine,
p: &'a Preparation,
mut range: Range,
breakpoint: Breakpoint,
@ -1016,9 +1041,9 @@ fn line<'a>(
// are no other items in the line.
if hyphen || start + shaped.text.len() > range.end || maybe_adjust_last_glyph {
if hyphen || start < range.end || before.is_empty() {
let mut reshaped = shaped.reshape(vt, &p.spans, start..range.end);
let mut reshaped = shaped.reshape(engine, &p.spans, start..range.end);
if hyphen || shy {
reshaped.push_hyphen(vt, p.fallback);
reshaped.push_hyphen(engine, p.fallback);
}
if let Some(last_glyph) = reshaped.glyphs.last() {
@ -1069,7 +1094,7 @@ fn line<'a>(
if range.start + shaped.text.len() > end || maybe_adjust_first_glyph {
// If the range is empty, we don't want to push an empty text item.
if range.start < end {
let reshaped = shaped.reshape(vt, &p.spans, range.start..end);
let reshaped = shaped.reshape(engine, &p.spans, range.start..end);
width += reshaped.width;
first = Some(Item::Text(reshaped));
}
@ -1129,7 +1154,7 @@ fn line<'a>(
/// Combine layouted lines into one frame per region.
fn finalize(
vt: &mut Vt,
engine: &mut Engine,
p: &Preparation,
lines: &[Line],
region: Size,
@ -1150,7 +1175,7 @@ fn finalize(
// Stack the lines into one frame per region.
let mut frames: Vec<Frame> = lines
.iter()
.map(|line| commit(vt, p, line, width, region.y))
.map(|line| commit(engine, p, line, width, region.y))
.collect::<SourceResult<_>>()?;
// Prevent orphans.
@ -1181,7 +1206,7 @@ fn merge(first: &mut Frame, second: Frame, leading: Abs) {
/// Commit to a line and build its frame.
fn commit(
vt: &mut Vt,
engine: &mut Engine,
p: &Preparation,
line: &Line,
width: Abs,
@ -1276,7 +1301,7 @@ fn commit(
if let Some((elem, styles)) = elem {
let region = Size::new(amount, full);
let pod = Regions::one(region, Axes::new(true, false));
let mut frame = elem.layout(vt, *styles, pod)?.into_frame();
let mut frame = elem.layout(engine, *styles, pod)?.into_frame();
frame.translate(Point::with_y(TextElem::baseline_in(*styles)));
push(&mut offset, frame);
} else {
@ -1284,7 +1309,8 @@ fn commit(
}
}
Item::Text(shaped) => {
let frame = shaped.build(vt, justification_ratio, extra_justification);
let frame =
shaped.build(engine, justification_ratio, extra_justification);
push(&mut offset, frame);
}
Item::Frame(frame) | Item::Meta(frame) => {

View File

@ -9,8 +9,9 @@ use rustybuzz::{Tag, UnicodeBuffer};
use unicode_script::{Script, UnicodeScript};
use super::SpanMapper;
use crate::engine::Engine;
use crate::foundations::StyleChain;
use crate::layout::{Abs, Dir, Em, Frame, FrameItem, Point, Size, Vt};
use crate::layout::{Abs, Dir, Em, Frame, FrameItem, Point, Size};
use crate::syntax::Span;
use crate::text::{
decorate, families, features, variant, Font, FontVariant, Glyph, Lang, Region,
@ -213,11 +214,11 @@ impl<'a> ShapedText<'a> {
/// [justifiable glyph](ShapedGlyph::is_justifiable) will get.
pub fn build(
&self,
vt: &Vt,
engine: &Engine,
justification_ratio: f64,
extra_justification: Abs,
) -> Frame {
let (top, bottom) = self.measure(vt);
let (top, bottom) = self.measure(engine);
let size = Size::new(self.width, top + bottom);
let mut offset = Abs::zero();
@ -307,7 +308,7 @@ impl<'a> ShapedText<'a> {
}
/// Measure the top and bottom extent of this text.
fn measure(&self, vt: &Vt) -> (Abs, Abs) {
fn measure(&self, engine: &Engine) -> (Abs, Abs) {
let mut top = Abs::zero();
let mut bottom = Abs::zero();
@ -323,7 +324,7 @@ impl<'a> ShapedText<'a> {
if self.glyphs.is_empty() {
// When there are no glyphs, we just use the vertical metrics of the
// first available font.
let world = vt.world;
let world = engine.world;
for family in families(self.styles) {
if let Some(font) = world
.book()
@ -387,7 +388,7 @@ impl<'a> ShapedText<'a> {
/// The text `range` is relative to the whole paragraph.
pub fn reshape(
&'a self,
vt: &Vt,
engine: &Engine,
spans: &SpanMapper,
text_range: Range<usize>,
) -> ShapedText<'a> {
@ -409,7 +410,7 @@ impl<'a> ShapedText<'a> {
}
} else {
shape(
vt,
engine,
text_range.start,
text,
spans,
@ -422,8 +423,8 @@ impl<'a> ShapedText<'a> {
}
/// Push a hyphen to end of the text.
pub fn push_hyphen(&mut self, vt: &Vt, fallback: bool) {
let world = vt.world;
pub fn push_hyphen(&mut self, engine: &Engine, fallback: bool) {
let world = engine.world;
let book = world.book();
let fallback_func = if fallback {
Some(|| book.select_fallback(None, self.variant, "-"))
@ -553,7 +554,7 @@ impl Debug for ShapedText<'_> {
/// Holds shaping results and metadata common to all shaped segments.
struct ShapingContext<'a, 'v> {
vt: &'a Vt<'v>,
engine: &'a Engine<'v>,
spans: &'a SpanMapper,
glyphs: Vec<ShapedGlyph>,
used: Vec<Font>,
@ -568,7 +569,7 @@ struct ShapingContext<'a, 'v> {
/// Shape text into [`ShapedText`].
#[allow(clippy::too_many_arguments)]
pub(super) fn shape<'a>(
vt: &Vt,
engine: &Engine,
base: usize,
text: &'a str,
spans: &SpanMapper,
@ -579,7 +580,7 @@ pub(super) fn shape<'a>(
) -> ShapedText<'a> {
let size = TextElem::size_in(styles);
let mut ctx = ShapingContext {
vt,
engine,
spans,
size,
glyphs: vec![],
@ -630,7 +631,7 @@ fn shape_segment<'a>(
}
// Find the next available family.
let world = ctx.vt.world;
let world = ctx.engine.world;
let book = world.book();
let mut selection = families.find_map(|family| {
book.select(family, ctx.variant)

View File

@ -1,6 +1,7 @@
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{dict, elem, func, Content, Func, NativeElement, StyleChain};
use crate::layout::{Fragment, Layout, Regions, Size, Vt};
use crate::layout::{Fragment, Layout, Regions, Size};
/// Provides access to the current outer container's (or page's, if none) size
/// (width and height).
@ -68,7 +69,7 @@ impl Layout for LayoutElem {
#[tracing::instrument(name = "LayoutElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -77,8 +78,8 @@ impl Layout for LayoutElem {
let Size { x, y } = regions.base();
let result = self
.func()
.call_vt(vt, [dict! { "width" => x, "height" => y }])?
.call(engine, [dict! { "width" => x, "height" => y }])?
.display();
result.layout(vt, styles, regions)
result.layout(engine, styles, regions)
}
}

View File

@ -1,5 +1,5 @@
use crate::diag::SourceResult;
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{dict, func, Content, Dict, StyleChain, Styles};
use crate::layout::{Abs, Axes, Layout, Regions, Size};
@ -41,8 +41,8 @@ use crate::layout::{Abs, Axes, Layout, Regions, Size};
/// `height`, both of type [`length`]($length).
#[func]
pub fn measure(
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// The content whose size to measure.
content: Content,
/// The styles with which to layout the content.
@ -50,7 +50,7 @@ pub fn measure(
) -> SourceResult<Dict> {
let pod = Regions::one(Axes::splat(Abs::inf()), Axes::splat(false));
let styles = StyleChain::new(&styles);
let frame = content.measure(&mut vm.vt, styles, pod)?.into_frame();
let frame = content.measure(engine, styles, pod)?.into_frame();
let Size { x, y } = frame.size();
Ok(dict! { "width" => x, "height" => y })
}

View File

@ -34,7 +34,6 @@ mod size;
mod spacing;
mod stack;
mod transform;
mod vt;
pub use self::abs::*;
pub use self::align::*;
@ -67,13 +66,13 @@ pub use self::size::*;
pub use self::spacing::*;
pub use self::stack::*;
pub use self::transform::*;
pub use self::vt::*;
pub(crate) use self::inline::*;
use comemo::{Tracked, TrackedMut};
use crate::diag::SourceResult;
use crate::diag::{bail, error, SourceResult};
use crate::engine::{Engine, Route};
use crate::eval::Tracer;
use crate::foundations::{category, Category, Content, Scope, StyleChain};
use crate::introspection::{Introspector, Locator};
@ -122,7 +121,11 @@ pub fn define(global: &mut Scope) {
/// Root-level layout.
pub trait LayoutRoot {
/// Layout into one frame per page.
fn layout_root(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Document>;
fn layout_root(
&self,
engine: &mut Engine,
styles: StyleChain,
) -> SourceResult<Document>;
}
/// Layout into regions.
@ -130,7 +133,7 @@ pub trait Layout {
/// Layout into one frame per region.
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment>;
@ -142,50 +145,64 @@ pub trait Layout {
#[tracing::instrument(name = "Layout::measure", skip_all)]
fn measure(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
let mut locator = Locator::chained(vt.locator.track());
let mut vt = Vt {
world: vt.world,
introspector: vt.introspector,
let mut locator = Locator::chained(engine.locator.track());
let mut engine = Engine {
world: engine.world,
route: engine.route.clone(),
introspector: engine.introspector,
locator: &mut locator,
tracer: TrackedMut::reborrow_mut(&mut vt.tracer),
tracer: TrackedMut::reborrow_mut(&mut engine.tracer),
};
self.layout(&mut vt, styles, regions)
self.layout(&mut engine, styles, regions)
}
}
impl LayoutRoot for Content {
#[tracing::instrument(name = "Content::layout_root", skip_all)]
fn layout_root(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Document> {
fn layout_root(
&self,
engine: &mut Engine,
styles: StyleChain,
) -> SourceResult<Document> {
#[comemo::memoize]
fn cached(
content: &Content,
world: Tracked<dyn World + '_>,
introspector: Tracked<Introspector>,
route: Tracked<Route>,
locator: Tracked<Locator>,
tracer: TrackedMut<Tracer>,
styles: StyleChain,
) -> SourceResult<Document> {
let mut locator = Locator::chained(locator);
let mut vt = Vt { world, introspector, locator: &mut locator, tracer };
let mut engine = Engine {
world,
introspector,
route: Route::extend(route),
locator: &mut locator,
tracer,
};
let scratch = Scratch::default();
let (realized, styles) = realize_root(&mut vt, &scratch, content, styles)?;
let (realized, styles) =
realize_root(&mut engine, &scratch, content, styles)?;
realized
.with::<dyn LayoutRoot>()
.unwrap()
.layout_root(&mut vt, styles)
.layout_root(&mut engine, styles)
}
tracing::info!("Starting layout");
cached(
self,
vt.world,
vt.introspector,
vt.locator.track(),
TrackedMut::reborrow_mut(&mut vt.tracer),
engine.world,
engine.introspector,
engine.route.track(),
engine.locator.track(),
TrackedMut::reborrow_mut(&mut engine.tracer),
styles,
)
}
@ -195,7 +212,7 @@ impl Layout for Content {
#[tracing::instrument(name = "Content::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -205,34 +222,49 @@ impl Layout for Content {
content: &Content,
world: Tracked<dyn World + '_>,
introspector: Tracked<Introspector>,
route: Tracked<Route>,
locator: Tracked<Locator>,
tracer: TrackedMut<Tracer>,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
let mut locator = Locator::chained(locator);
let mut vt = Vt { world, introspector, locator: &mut locator, tracer };
let mut engine = Engine {
world,
introspector,
route: Route::extend(route),
locator: &mut locator,
tracer,
};
if engine.route.exceeding() {
bail!(error!(content.span(), "maximum layout depth exceeded")
.with_hint("try to reduce the amount of nesting in your layout"));
}
let scratch = Scratch::default();
let (realized, styles) = realize_block(&mut vt, &scratch, content, styles)?;
let (realized, styles) =
realize_block(&mut engine, &scratch, content, styles)?;
realized
.with::<dyn Layout>()
.unwrap()
.layout(&mut vt, styles, regions)
.layout(&mut engine, styles, regions)
}
tracing::info!("Layouting `Content`");
let fragment = cached(
self,
vt.world,
vt.introspector,
vt.locator.track(),
TrackedMut::reborrow_mut(&mut vt.tracer),
engine.world,
engine.introspector,
engine.route.track(),
engine.locator.track(),
TrackedMut::reborrow_mut(&mut engine.tracer),
styles,
regions,
)?;
vt.locator.visit_frames(&fragment);
engine.locator.visit_frames(&fragment);
Ok(fragment)
}
}

View File

@ -1,8 +1,7 @@
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{elem, Content, Resolve, StyleChain};
use crate::layout::{
Abs, Fragment, Layout, Length, Point, Regions, Rel, Sides, Size, Vt,
};
use crate::layout::{Abs, Fragment, Layout, Length, Point, Regions, Rel, Sides, Size};
/// Adds spacing around content.
///
@ -63,7 +62,7 @@ impl Layout for PadElem {
#[tracing::instrument(name = "PadElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -78,7 +77,7 @@ impl Layout for PadElem {
let mut backlog = vec![];
let padding = sides.resolve(styles);
let pod = regions.map(&mut backlog, |size| shrink(size, padding));
let mut fragment = self.body().layout(vt, styles, pod)?;
let mut fragment = self.body().layout(engine, styles, pod)?;
for frame in &mut fragment {
// Apply the padding inversely such that the grown size padded

View File

@ -4,6 +4,7 @@ use std::ptr;
use std::str::FromStr;
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, AutoValue, Cast, Content, Dict, Fold, Func, NativeElement, Resolve,
Smart, StyleChain, Value,
@ -11,7 +12,7 @@ use crate::foundations::{
use crate::introspection::{Counter, CounterKey, ManualPageCounter, Meta};
use crate::layout::{
Abs, Align, AlignElem, Axes, ColumnsElem, Dir, Fragment, Frame, HAlign, Layout,
Length, Point, Ratio, Regions, Rel, Sides, Size, VAlign, Vt,
Length, Point, Ratio, Regions, Rel, Sides, Size, VAlign,
};
use crate::model::Numbering;
@ -342,7 +343,7 @@ impl PageElem {
#[tracing::instrument(skip_all)]
pub fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
page_counter: &mut ManualPageCounter,
extend_to: Option<Parity>,
@ -393,7 +394,7 @@ impl PageElem {
regions.root = true;
// Layout the child.
let mut frames = child.layout(vt, styles, regions)?.into_frames();
let mut frames = child.layout(engine, styles, regions)?.into_frames();
// Align the child to the pagebreak's parity.
// Check for page count after adding the pending frames
@ -496,7 +497,7 @@ impl PageElem {
let sub = content
.clone()
.styled(AlignElem::set_alignment(align))
.layout(vt, styles, pod)?
.layout(engine, styles, pod)?
.into_frame();
if ptr::eq(marginal, &header) || ptr::eq(marginal, &background) {
@ -510,7 +511,7 @@ impl PageElem {
frame.fill(fill.clone());
}
page_counter.visit(vt, frame)?;
page_counter.visit(engine, frame)?;
// Add a PDF page label if there is a numbering.
if let Some(num) = numbering {
@ -675,10 +676,14 @@ pub enum Marginal {
impl Marginal {
/// Resolve the marginal based on the page number.
pub fn resolve(&self, vt: &mut Vt, page: usize) -> SourceResult<Cow<'_, Content>> {
pub fn resolve(
&self,
engine: &mut Engine,
page: usize,
) -> SourceResult<Cow<'_, Content>> {
Ok(match self {
Self::Content(content) => Cow::Borrowed(content),
Self::Func(func) => Cow::Owned(func.call_vt(vt, [page])?.display()),
Self::Func(func) => Cow::Owned(func.call(engine, [page])?.display()),
})
}
}

View File

@ -1,10 +1,9 @@
use crate::diag::{bail, At, Hint, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
elem, Behave, Behaviour, Content, NativeElement, Smart, StyleChain,
};
use crate::layout::{
Align, Axes, Em, Fragment, Layout, Length, Regions, Rel, VAlign, Vt,
};
use crate::layout::{Align, Axes, Em, Fragment, Layout, Length, Regions, Rel, VAlign};
/// Places content at an absolute position.
///
@ -91,7 +90,7 @@ impl Layout for PlaceElem {
#[tracing::instrument(name = "PlaceElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -118,7 +117,7 @@ impl Layout for PlaceElem {
.aligned(alignment.unwrap_or_else(|| Align::CENTER));
let pod = Regions::one(base, Axes::splat(false));
let frame = child.layout(vt, styles, pod)?.into_frame();
let frame = child.layout(engine, styles, pod)?.into_frame();
Ok(Fragment::frame(frame))
}
}

View File

@ -1,7 +1,8 @@
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{elem, Content, NativeElement, Resolve, StyleChain};
use crate::layout::{
Abs, AlignElem, Axes, Fragment, Frame, Layout, Point, Regions, Size, Vt,
Abs, AlignElem, Axes, Fragment, Frame, Layout, Point, Regions, Size,
};
use crate::util::Numeric;
@ -37,12 +38,12 @@ impl Layout for RepeatElem {
#[tracing::instrument(name = "RepeatElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
let pod = Regions::one(regions.size, Axes::new(false, false));
let piece = self.body().layout(vt, styles, pod)?.into_frame();
let piece = self.body().layout(engine, styles, pod)?.into_frame();
let align = AlignElem::alignment_in(styles).resolve(styles);
let fill = regions.size.x;

View File

@ -1,10 +1,11 @@
use std::fmt::{self, Debug, Formatter};
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{cast, elem, Content, Resolve, StyleChain};
use crate::layout::{
Abs, AlignElem, Axes, Axis, Dir, FixedAlign, Fr, Fragment, Frame, Layout, Point,
Regions, Size, Spacing, Vt,
Regions, Size, Spacing,
};
use crate::util::{Get, Numeric};
@ -54,7 +55,7 @@ impl Layout for StackElem {
#[tracing::instrument(name = "StackElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -75,7 +76,7 @@ impl Layout for StackElem {
layouter.layout_spacing(kind);
}
layouter.layout_block(vt, block, styles)?;
layouter.layout_block(engine, block, styles)?;
deferred = spacing;
}
}
@ -199,7 +200,7 @@ impl<'a> StackLayouter<'a> {
#[tracing::instrument(name = "StackLayouter::layout_block", skip_all)]
fn layout_block(
&mut self,
vt: &mut Vt,
engine: &mut Engine,
block: &Content,
styles: StyleChain,
) -> SourceResult<()> {
@ -217,7 +218,7 @@ impl<'a> StackLayouter<'a> {
}
.resolve(styles);
let fragment = block.layout(vt, styles, self.regions)?;
let fragment = block.layout(engine, styles, self.regions)?;
let len = fragment.len();
for (i, frame) in fragment.into_iter().enumerate() {
// Grow our size, shrink the region and save the frame for later.

View File

@ -1,8 +1,9 @@
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{elem, Content, Resolve, StyleChain};
use crate::layout::{
Abs, Align, Angle, Axes, FixedAlign, Fragment, HAlign, Layout, Length, Ratio,
Regions, Rel, VAlign, Vt,
Regions, Rel, VAlign,
};
/// Moves content without affecting layout.
@ -40,12 +41,12 @@ impl Layout for MoveElem {
#[tracing::instrument(name = "MoveElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
let pod = Regions::one(regions.base(), Axes::splat(false));
let mut frame = self.body().layout(vt, styles, pod)?.into_frame();
let mut frame = self.body().layout(engine, styles, pod)?.into_frame();
let delta = Axes::new(self.dx(styles), self.dy(styles)).resolve(styles);
let delta = delta.zip_map(regions.base(), Rel::relative_to);
frame.translate(delta.to_point());
@ -106,12 +107,12 @@ impl Layout for RotateElem {
#[tracing::instrument(name = "RotateElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
let pod = Regions::one(regions.base(), Axes::splat(false));
let mut frame = self.body().layout(vt, styles, pod)?.into_frame();
let mut frame = self.body().layout(engine, styles, pod)?.into_frame();
let Axes { x, y } = self
.origin(styles)
.resolve(styles)
@ -171,12 +172,12 @@ impl Layout for ScaleElem {
#[tracing::instrument(name = "ScaleElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
let pod = Regions::one(regions.base(), Axes::splat(false));
let mut frame = self.body().layout(vt, styles, pod)?.into_frame();
let mut frame = self.body().layout(engine, styles, pod)?.into_frame();
let Axes { x, y } = self
.origin(styles)
.resolve(styles)

View File

@ -1,39 +0,0 @@
use comemo::{Tracked, TrackedMut};
use crate::diag::SourceResult;
use crate::eval::Tracer;
use crate::introspection::{Introspector, Locator};
use crate::World;
/// A virtual typesetter.
///
/// Holds the state needed during compilation.
pub struct Vt<'a> {
/// The compilation environment.
pub world: Tracked<'a, dyn World + 'a>,
/// Provides access to information about the document.
pub introspector: Tracked<'a, Introspector>,
/// Provides stable identities to elements.
pub locator: &'a mut Locator<'a>,
/// The tracer for inspection of the values an expression produces.
pub tracer: TrackedMut<'a, Tracer>,
}
impl Vt<'_> {
/// Perform a fallible operation that does not immediately terminate further
/// execution. Instead it produces a delayed error that is only promoted to
/// a fatal one if it remains at the end of the introspection loop.
pub fn delayed<F, T>(&mut self, f: F) -> T
where
F: FnOnce(&mut Self) -> SourceResult<T>,
T: Default,
{
match f(self) {
Ok(value) => value,
Err(errors) => {
self.tracer.delay(errors);
T::default()
}
}
}
}

View File

@ -40,6 +40,7 @@ extern crate self as typst;
#[macro_use]
pub mod util;
pub mod diag;
pub mod engine;
pub mod eval;
pub mod foundations;
pub mod introspection;
@ -62,12 +63,13 @@ use comemo::{Prehashed, Track, Tracked, Validate};
use ecow::{EcoString, EcoVec};
use crate::diag::{warning, FileResult, SourceDiagnostic, SourceResult};
use crate::eval::{Route, Tracer};
use crate::engine::{Engine, Route};
use crate::eval::Tracer;
use crate::foundations::{
Array, Bytes, Content, Datetime, Module, Scope, StyleChain, Styles,
};
use crate::introspection::{Introspector, Locator};
use crate::layout::{Align, Dir, LayoutRoot, Vt};
use crate::layout::{Align, Dir, LayoutRoot};
use crate::model::Document;
use crate::syntax::{FileId, PackageSpec, Source, Span};
use crate::text::{Font, FontBook};
@ -122,15 +124,17 @@ fn typeset(
let constraint = <Introspector as Validate>::Constraint::new();
let mut locator = Locator::new();
let mut vt = Vt {
let mut engine = Engine {
world,
route: Route::default(),
tracer: tracer.track_mut(),
locator: &mut locator,
introspector: introspector.track_with(&constraint),
};
// Layout!
document = content.layout_root(&mut vt, styles)?;
document = content.layout_root(&mut engine, styles)?;
introspector = Introspector::new(&document.pages);
iter += 1;

View File

@ -1,7 +1,7 @@
use ecow::{eco_format, EcoString};
use crate::diag::{At, SourceResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{func, scope, Bytes, Value};
use crate::syntax::Spanned;
use crate::World;
@ -16,14 +16,14 @@ use crate::World;
/// whether they are whole numbers.
#[func(scope, title = "CBOR")]
pub fn cbor(
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// Path to a CBOR file.
path: Spanned<EcoString>,
) -> SourceResult<Value> {
let Spanned { v: path, span } = path;
let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
let id = span.resolve_path(&path).at(span)?;
let data = engine.world.file(id).at(span)?;
cbor::decode(Spanned::new(data, span))
}

View File

@ -1,7 +1,7 @@
use ecow::{eco_format, EcoString};
use crate::diag::{bail, At, SourceResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{cast, func, scope, Array, IntoValue, Value};
use crate::loading::Readable;
use crate::syntax::Spanned;
@ -26,8 +26,8 @@ use crate::World;
/// ```
#[func(scope, title = "CSV")]
pub fn csv(
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// Path to a CSV file.
path: Spanned<EcoString>,
/// The delimiter that separates columns in the CSV file.
@ -37,8 +37,8 @@ pub fn csv(
delimiter: Delimiter,
) -> SourceResult<Array> {
let Spanned { v: path, span } = path;
let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
let id = span.resolve_path(&path).at(span)?;
let data = engine.world.file(id).at(span)?;
self::csv::decode(Spanned::new(Readable::Bytes(data), span), delimiter)
}

View File

@ -1,7 +1,7 @@
use ecow::{eco_format, EcoString};
use crate::diag::{At, SourceResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{func, scope, Str, Value};
use crate::loading::Readable;
use crate::syntax::Spanned;
@ -46,14 +46,14 @@ use crate::World;
/// ```
#[func(scope, title = "JSON")]
pub fn json(
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// Path to a JSON file.
path: Spanned<EcoString>,
) -> SourceResult<Value> {
let Spanned { v: path, span } = path;
let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
let id = span.resolve_path(&path).at(span)?;
let data = engine.world.file(id).at(span)?;
json::decode(Spanned::new(Readable::Bytes(data), span))
}

View File

@ -1,7 +1,7 @@
use ecow::EcoString;
use crate::diag::{At, SourceResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{func, Cast};
use crate::loading::Readable;
use crate::syntax::Spanned;
@ -24,8 +24,8 @@ use crate::World;
/// ```
#[func]
pub fn read(
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// Path to a file.
path: Spanned<EcoString>,
/// The encoding to read the file with.
@ -36,8 +36,8 @@ pub fn read(
encoding: Option<Encoding>,
) -> SourceResult<Readable> {
let Spanned { v: path, span } = path;
let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
let id = span.resolve_path(&path).at(span)?;
let data = engine.world.file(id).at(span)?;
Ok(match encoding {
None => Readable::Bytes(data),
Some(Encoding::Utf8) => Readable::Str(

View File

@ -1,7 +1,7 @@
use ecow::{eco_format, EcoString};
use crate::diag::{At, SourceResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{func, scope, Str, Value};
use crate::loading::Readable;
use crate::syntax::{is_newline, Spanned};
@ -29,14 +29,14 @@ use crate::World;
/// ```
#[func(scope, title = "TOML")]
pub fn toml(
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// Path to a TOML file.
path: Spanned<EcoString>,
) -> SourceResult<Value> {
let Spanned { v: path, span } = path;
let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
let id = span.resolve_path(&path).at(span)?;
let data = engine.world.file(id).at(span)?;
toml::decode(Spanned::new(Readable::Bytes(data), span))
}

View File

@ -1,7 +1,7 @@
use ecow::EcoString;
use crate::diag::{format_xml_like_error, At, FileError, SourceResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{dict, func, scope, Array, Dict, IntoValue, Str, Value};
use crate::loading::Readable;
use crate::syntax::Spanned;
@ -57,14 +57,14 @@ use crate::World;
/// ```
#[func(scope, title = "XML")]
pub fn xml(
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// Path to an XML file.
path: Spanned<EcoString>,
) -> SourceResult<Value> {
let Spanned { v: path, span } = path;
let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
let id = span.resolve_path(&path).at(span)?;
let data = engine.world.file(id).at(span)?;
xml::decode(Spanned::new(Readable::Bytes(data), span))
}

View File

@ -1,7 +1,7 @@
use ecow::{eco_format, EcoString};
use crate::diag::{At, SourceResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{func, scope, Str, Value};
use crate::loading::Readable;
use crate::syntax::Spanned;
@ -38,14 +38,14 @@ use crate::World;
/// ```
#[func(scope, title = "YAML")]
pub fn yaml(
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// Path to a YAML file.
path: Spanned<EcoString>,
) -> SourceResult<Value> {
let Spanned { v: path, span } = path;
let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
let id = span.resolve_path(&path).at(span)?;
let data = engine.world.file(id).at(span)?;
yaml::decode(Spanned::new(Readable::Bytes(data), span))
}

View File

@ -191,7 +191,7 @@ fn draw_cancel_line(
CancelAngle::Angle(v) => *v,
// This specifies a function that takes the default angle as input.
CancelAngle::Func(func) => {
func.call_vt(ctx.vt, [default])?.cast().at(span)?
func.call(ctx.engine, [default])?.cast().at(span)?
}
},
};

View File

@ -9,8 +9,9 @@ use unicode_math_class::MathClass;
use unicode_segmentation::UnicodeSegmentation;
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{Content, NativeElement, Smart, StyleChain, Styles};
use crate::layout::{Abs, Axes, BoxElem, Em, Frame, Layout, Regions, Size, Vt};
use crate::layout::{Abs, Axes, BoxElem, Em, Frame, Layout, Regions, Size};
use crate::math::{
FrameFragment, GlyphFragment, LayoutMath, MathFragment, MathRow, MathSize, MathStyle,
MathVariant, THICK,
@ -43,7 +44,7 @@ macro_rules! percent {
/// The context for math layout.
pub struct MathContext<'a, 'b, 'v> {
pub vt: &'v mut Vt<'b>,
pub engine: &'v mut Engine<'b>,
pub regions: Regions<'static>,
pub font: &'a Font,
pub ttf: &'a ttf_parser::Face<'a>,
@ -62,7 +63,7 @@ pub struct MathContext<'a, 'b, 'v> {
impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
pub fn new(
vt: &'v mut Vt<'b>,
engine: &'v mut Engine<'b>,
styles: StyleChain<'a>,
regions: Regions,
font: &'a Font,
@ -103,7 +104,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
let variant = variant(styles);
Self {
vt,
engine,
regions: Regions::one(regions.base(), Axes::splat(false)),
font,
ttf: font.ttf(),
@ -167,13 +168,13 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
pub fn layout_box(&mut self, boxed: &BoxElem) -> SourceResult<Frame> {
Ok(boxed
.layout(self.vt, self.outer.chain(&self.local), self.regions)?
.layout(self.engine, self.outer.chain(&self.local), self.regions)?
.into_frame())
}
pub fn layout_content(&mut self, content: &Content) -> SourceResult<Frame> {
Ok(content
.layout(self.vt, self.outer.chain(&self.local), self.regions)?
.layout(self.engine, self.outer.chain(&self.local), self.regions)?
.into_frame())
}
@ -270,7 +271,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
let frame = ParElem::new(vec![Prehashed::new(elem)])
.spanned(span)
.layout(
self.vt,
self.engine,
self.outer.chain(&self.local),
false,
Size::splat(Abs::inf()),
@ -288,7 +289,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> {
}
pub fn realize(&mut self, content: &Content) -> SourceResult<Option<Content>> {
realize(self.vt, content, self.outer.chain(&self.local))
realize(self.engine, content, self.outer.chain(&self.local))
}
pub fn style(&mut self, style: MathStyle) {

View File

@ -1,6 +1,7 @@
use std::num::NonZeroUsize;
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
elem, Content, Finalize, Guard, NativeElement, Resolve, Show, Smart, StyleChain,
Synthesize,
@ -8,7 +9,7 @@ use crate::foundations::{
use crate::introspection::{Count, Counter, CounterUpdate, Locatable};
use crate::layout::{
Abs, Align, AlignElem, Axes, Dir, Em, FixedAlign, Fragment, Layout, Point, Regions,
Size, Vt,
Size,
};
use crate::math::{LayoutMath, MathContext};
use crate::model::{Numbering, Outlinable, ParElem, Refable, Supplement};
@ -88,12 +89,18 @@ pub struct EquationElem {
}
impl Synthesize for EquationElem {
fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
fn synthesize(
&mut self,
engine: &mut Engine,
styles: StyleChain,
) -> SourceResult<()> {
// Resolve the supplement.
let supplement = match self.supplement(styles) {
Smart::Auto => TextElem::packed(Self::local_name_in(styles)),
Smart::Custom(None) => Content::empty(),
Smart::Custom(Some(supplement)) => supplement.resolve(vt, [self.clone()])?,
Smart::Custom(Some(supplement)) => {
supplement.resolve(engine, [self.clone()])?
}
};
self.push_block(self.block(styles));
@ -106,7 +113,7 @@ impl Synthesize for EquationElem {
impl Show for EquationElem {
#[tracing::instrument(name = "EquationElem::show", skip_all)]
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let mut realized = self.clone().pack().guarded(Guard::Base(Self::elem()));
if self.block(styles) {
realized = AlignElem::new(realized).pack();
@ -133,7 +140,7 @@ impl Layout for EquationElem {
#[tracing::instrument(name = "EquationElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -143,7 +150,7 @@ impl Layout for EquationElem {
// Find a math font.
let variant = variant(styles);
let world = vt.world;
let world = engine.world;
let Some(font) = families(styles).find_map(|family| {
let id = world.book().select(family, variant)?;
let font = world.font(id)?;
@ -153,7 +160,7 @@ impl Layout for EquationElem {
bail!(self.span(), "current font does not support math");
};
let mut ctx = MathContext::new(vt, styles, regions, &font, block);
let mut ctx = MathContext::new(engine, styles, regions, &font, block);
let mut frame = ctx.layout_frame(self)?;
if block {
@ -161,7 +168,7 @@ impl Layout for EquationElem {
let pod = Regions::one(regions.base(), Axes::splat(false));
let counter = Counter::of(Self::elem())
.display(Some(numbering), false)
.layout(vt, styles, pod)?
.layout(engine, styles, pod)?
.into_frame();
let full_counter_width = counter.width() + NUMBER_GUTTER.resolve(styles);
@ -274,7 +281,7 @@ impl Refable for EquationElem {
}
impl Outlinable for EquationElem {
fn outline(&self, vt: &mut Vt) -> SourceResult<Option<Content>> {
fn outline(&self, engine: &mut Engine) -> SourceResult<Option<Content>> {
if !self.block(StyleChain::default()) {
return Ok(None);
}
@ -294,8 +301,8 @@ impl Outlinable for EquationElem {
let numbers = self
.counter()
.at(vt, self.location().unwrap())?
.display(vt, &numbering)?;
.at(engine, self.location().unwrap())?
.display(engine, &numbering)?;
Ok(Some(supplement + numbers))
}

View File

@ -20,7 +20,8 @@ use smallvec::{smallvec, SmallVec};
use typed_arena::Arena;
use crate::diag::{bail, error, At, FileError, SourceResult, StrResult};
use crate::eval::{eval_string, EvalMode, Vm};
use crate::engine::Engine;
use crate::eval::{eval_string, EvalMode};
use crate::foundations::{
cast, elem, ty, Args, Array, Bytes, CastInfo, Content, Finalize, FromValue,
IntoValue, Label, NativeElement, Reflect, Repr, Scope, Show, Smart, Str, StyleChain,
@ -28,7 +29,7 @@ use crate::foundations::{
};
use crate::introspection::{Introspector, Locatable, Location};
use crate::layout::{
BlockElem, Em, GridElem, HElem, PadElem, Sizing, TrackSizings, VElem, Vt,
BlockElem, Em, GridElem, HElem, PadElem, Sizing, TrackSizings, VElem,
};
use crate::model::{
CitationForm, CiteGroup, Destination, FootnoteElem, HeadingElem, LinkElem, ParElem,
@ -88,7 +89,7 @@ pub struct BibliographyElem {
/// Path(s) to Hayagriva `.yml` and/or BibLaTeX `.bib` files.
#[required]
#[parse(
let (paths, bibliography) = Bibliography::parse(vm, args)?;
let (paths, bibliography) = Bibliography::parse(engine, args)?;
paths
)]
pub path: BibliographyPaths,
@ -120,7 +121,7 @@ pub struct BibliographyElem {
/// a [CSL file](https://citationstyles.org/). Some of the styles listed
/// below appear twice, once with their full name and once with a short
/// alias.
#[parse(CslStyle::parse(vm, args)?)]
#[parse(CslStyle::parse(engine, args)?)]
#[default(CslStyle::from_name("ieee").unwrap())]
pub style: CslStyle,
@ -169,9 +170,10 @@ impl BibliographyElem {
}
/// Whether the bibliography contains the given key.
pub fn has(vt: &Vt, key: impl Into<PicoStr>) -> bool {
pub fn has(engine: &Engine, key: impl Into<PicoStr>) -> bool {
let key = key.into();
vt.introspector
engine
.introspector
.query(&Self::elem().select())
.iter()
.any(|elem| elem.to::<Self>().unwrap().bibliography().has(key))
@ -195,7 +197,7 @@ impl BibliographyElem {
}
impl Synthesize for BibliographyElem {
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
self.push_full(self.full(styles));
self.push_style(self.style(styles));
self.push_lang(TextElem::lang_in(styles));
@ -206,7 +208,7 @@ impl Synthesize for BibliographyElem {
impl Show for BibliographyElem {
#[tracing::instrument(name = "BibliographyElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
const COLUMN_GUTTER: Em = Em::new(0.65);
const INDENT: Em = Em::new(1.5);
@ -219,9 +221,9 @@ impl Show for BibliographyElem {
seq.push(HeadingElem::new(title).with_level(NonZeroUsize::ONE).pack());
}
Ok(vt.delayed(|vt| {
Ok(engine.delayed(|engine| {
let span = self.span();
let works = Works::generate(vt.world, vt.introspector).at(span)?;
let works = Works::generate(engine.world, engine.introspector).at(span)?;
let references = works
.references
.as_ref()
@ -316,7 +318,7 @@ pub struct Bibliography {
impl Bibliography {
/// Parse the bibliography argument.
fn parse(
vm: &mut Vm,
engine: &mut Engine,
args: &mut Args,
) -> SourceResult<(BibliographyPaths, Bibliography)> {
let Spanned { v: paths, span } =
@ -327,8 +329,8 @@ impl Bibliography {
.0
.iter()
.map(|path| {
let id = vm.resolve_path(path).at(span)?;
vm.world().file(id).at(span)
let id = span.resolve_path(path).at(span)?;
engine.world.file(id).at(span)
})
.collect::<SourceResult<Vec<Bytes>>>()?;
@ -438,19 +440,19 @@ pub struct CslStyle {
impl CslStyle {
/// Parse the style argument.
pub fn parse(vm: &mut Vm, args: &mut Args) -> SourceResult<Option<CslStyle>> {
pub fn parse(engine: &mut Engine, args: &mut Args) -> SourceResult<Option<CslStyle>> {
let Some(Spanned { v: string, span }) =
args.named::<Spanned<EcoString>>("style")?
else {
return Ok(None);
};
Ok(Some(Self::parse_impl(vm, &string).at(span)?))
Ok(Some(Self::parse_impl(engine, &string, span).at(span)?))
}
/// Parse the style argument with `Smart`.
pub fn parse_smart(
vm: &mut Vm,
engine: &mut Engine,
args: &mut Args,
) -> SourceResult<Option<Smart<CslStyle>>> {
let Some(Spanned { v: smart, span }) =
@ -462,13 +464,13 @@ impl CslStyle {
Ok(Some(match smart {
Smart::Auto => Smart::Auto,
Smart::Custom(string) => {
Smart::Custom(Self::parse_impl(vm, &string).at(span)?)
Smart::Custom(Self::parse_impl(engine, &string, span).at(span)?)
}
}))
}
/// Parse internally.
fn parse_impl(vm: &mut Vm, string: &str) -> StrResult<CslStyle> {
fn parse_impl(engine: &mut Engine, string: &str, span: Span) -> StrResult<CslStyle> {
let ext = Path::new(string)
.extension()
.and_then(OsStr::to_str)
@ -476,8 +478,8 @@ impl CslStyle {
.to_lowercase();
if ext == "csl" {
let id = vm.resolve_path(string)?;
let data = vm.world().file(id)?;
let id = span.resolve_path(string)?;
let data = engine.world.file(id)?;
CslStyle::from_data(&data)
} else {
CslStyle::from_name(string)

View File

@ -1,9 +1,9 @@
use crate::diag::{bail, At, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, Cast, Content, Label, NativeElement, Show, Smart, StyleChain, Synthesize,
};
use crate::introspection::Locatable;
use crate::layout::Vt;
use crate::model::bibliography::Works;
use crate::model::CslStyle;
use crate::text::{Lang, Region, TextElem};
@ -85,7 +85,7 @@ pub struct CiteElem {
///
/// When set to `{auto}`, automatically use the
/// [bibliography's style]($bibliography.style) for the citations.
#[parse(CslStyle::parse_smart(vm, args)?)]
#[parse(CslStyle::parse_smart(engine, args)?)]
pub style: Smart<CslStyle>,
/// The text language setting where the citation is.
@ -100,7 +100,7 @@ pub struct CiteElem {
}
impl Synthesize for CiteElem {
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
self.push_supplement(self.supplement(styles));
self.push_form(self.form(styles));
self.push_style(self.style(styles));
@ -143,12 +143,12 @@ pub struct CiteGroup {
}
impl Show for CiteGroup {
#[tracing::instrument(name = "CiteGroup::show", skip(self, vt))]
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
Ok(vt.delayed(|vt| {
#[tracing::instrument(name = "CiteGroup::show", skip(self, engine))]
fn show(&self, engine: &mut Engine, _: StyleChain) -> SourceResult<Content> {
Ok(engine.delayed(|engine| {
let location = self.location().unwrap();
let span = self.span();
Works::generate(vt.world, vt.introspector)
Works::generate(engine.world, engine.introspector)
.at(span)?
.citations
.get(&location)

View File

@ -2,12 +2,12 @@ use comemo::Prehashed;
use ecow::EcoString;
use crate::diag::{bail, SourceResult, StrResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{
cast, elem, Args, Array, Construct, Content, Datetime, Smart, StyleChain, Value,
};
use crate::introspection::ManualPageCounter;
use crate::layout::{Frame, LayoutRoot, PageElem, Vt};
use crate::layout::{Frame, LayoutRoot, PageElem};
/// The root element of a document and its metadata.
///
@ -56,7 +56,7 @@ pub struct DocumentElem {
}
impl Construct for DocumentElem {
fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> {
fn construct(_: &mut Engine, args: &mut Args) -> SourceResult<Content> {
bail!(args.span, "can only be used in set rules")
}
}
@ -64,7 +64,11 @@ impl Construct for DocumentElem {
impl LayoutRoot for DocumentElem {
/// Layout the document into a sequence of frames, one per page.
#[tracing::instrument(name = "DocumentElem::layout_root", skip_all)]
fn layout_root(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Document> {
fn layout_root(
&self,
engine: &mut Engine,
styles: StyleChain,
) -> SourceResult<Document> {
tracing::info!("Document layout");
let mut pages = vec![];
@ -88,7 +92,8 @@ impl LayoutRoot for DocumentElem {
.to::<PageElem>()?
.clear_to(styles)
});
let fragment = page.layout(vt, styles, &mut page_counter, extend_to)?;
let fragment =
page.layout(engine, styles, &mut page_counter, extend_to)?;
pages.extend(fragment);
} else {
bail!(child.span(), "unexpected document child");

View File

@ -1,6 +1,6 @@
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{elem, Content, Show, StyleChain};
use crate::layout::Vt;
use crate::text::{ItalicToggle, TextElem};
/// Emphasizes content by setting it in italics.
@ -35,7 +35,7 @@ pub struct EmphElem {
impl Show for EmphElem {
#[tracing::instrument(name = "EmphElem::show", skip(self))]
fn show(&self, _: &mut Vt, _: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
Ok(self.body().clone().styled(TextElem::set_emph(ItalicToggle)))
}
}

View File

@ -1,12 +1,13 @@
use std::str::FromStr;
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, Array, Content, Fold, NativeElement, Smart, StyleChain,
};
use crate::layout::{
Axes, BlockElem, Em, Fragment, GridLayouter, HAlign, Layout, Length, Regions, Sizing,
Spacing, VAlign, Vt,
Spacing, VAlign,
};
use crate::model::{Numbering, NumberingPattern, ParElem};
use crate::text::TextElem;
@ -209,7 +210,7 @@ impl Layout for EnumElem {
#[tracing::instrument(name = "EnumElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -239,7 +240,7 @@ impl Layout for EnumElem {
let resolved = if full {
parents.push(number);
let content = numbering.apply_vt(vt, &parents)?.display();
let content = numbering.apply(engine, &parents)?.display();
parents.pop();
content
} else {
@ -247,7 +248,7 @@ impl Layout for EnumElem {
Numbering::Pattern(pattern) => {
TextElem::packed(pattern.apply_kth(parents.len(), number))
}
other => other.apply_vt(vt, &[number])?.display(),
other => other.apply(engine, &[number])?.display(),
}
};
@ -277,7 +278,7 @@ impl Layout for EnumElem {
self.span(),
);
Ok(layouter.layout(vt)?.fragment)
Ok(layouter.layout(engine)?.fragment)
}
}

View File

@ -5,6 +5,7 @@ use std::str::FromStr;
use ecow::EcoString;
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, select_where, Content, Element, Finalize, NativeElement, Selector,
Show, Smart, StyleChain, Synthesize,
@ -12,7 +13,7 @@ use crate::foundations::{
use crate::introspection::{
Count, Counter, CounterKey, CounterUpdate, Locatable, Location,
};
use crate::layout::{Align, BlockElem, Em, HAlign, Length, PlaceElem, VAlign, VElem, Vt};
use crate::layout::{Align, BlockElem, Em, HAlign, Length, PlaceElem, VAlign, VElem};
use crate::model::{Numbering, NumberingPattern, Outlinable, Refable, Supplement};
use crate::syntax::Spanned;
use crate::text::{Lang, Region, TextElem};
@ -220,7 +221,11 @@ impl FigureElem {
}
impl Synthesize for FigureElem {
fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
fn synthesize(
&mut self,
engine: &mut Engine,
styles: StyleChain,
) -> SourceResult<()> {
let numbering = self.numbering(styles);
// Determine the figure's kind.
@ -264,7 +269,7 @@ impl Synthesize for FigureElem {
};
let target = descendant.unwrap_or_else(|| Cow::Borrowed(self.body()));
Some(supplement.resolve(vt, [target])?)
Some(supplement.resolve(engine, [target])?)
}
};
@ -296,7 +301,7 @@ impl Synthesize for FigureElem {
impl Show for FigureElem {
#[tracing::instrument(name = "FigureElem::show", skip_all)]
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let mut realized = self.body().clone();
// Build the caption, if any.
@ -364,7 +369,7 @@ impl Refable for FigureElem {
}
impl Outlinable for FigureElem {
fn outline(&self, vt: &mut Vt) -> SourceResult<Option<Content>> {
fn outline(&self, engine: &mut Engine) -> SourceResult<Option<Content>> {
if !self.outlined(StyleChain::default()) {
return Ok(None);
}
@ -384,7 +389,7 @@ impl Outlinable for FigureElem {
self.numbering(StyleChain::default()),
) {
let location = self.location().unwrap();
let numbers = counter.at(vt, location)?.display(vt, &numbering)?;
let numbers = counter.at(engine, location)?.display(engine, &numbering)?;
if !supplement.is_empty() {
supplement += TextElem::packed('\u{a0}');
@ -538,7 +543,7 @@ impl FigureCaption {
}
impl Synthesize for FigureCaption {
fn synthesize(&mut self, _: &mut Vt, styles: StyleChain) -> SourceResult<()> {
fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
self.push_position(self.position(styles));
self.push_separator(Smart::Custom(self.get_separator(styles)));
Ok(())
@ -547,7 +552,7 @@ impl Synthesize for FigureCaption {
impl Show for FigureCaption {
#[tracing::instrument(name = "FigureCaption::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let mut realized = self.body().clone();
if let (Some(mut supplement), Some(numbering), Some(counter), Some(location)) = (
@ -556,7 +561,7 @@ impl Show for FigureCaption {
self.counter(),
self.figure_location(),
) {
let numbers = counter.at(vt, *location)?.display(vt, numbering)?;
let numbers = counter.at(engine, *location)?.display(engine, numbering)?;
if !supplement.is_empty() {
supplement += TextElem::packed('\u{a0}');
}

View File

@ -4,12 +4,13 @@ use std::str::FromStr;
use comemo::Prehashed;
use crate::diag::{bail, error, At, SourceResult, StrResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, Content, Finalize, Label, NativeElement, Show, Smart, StyleChain,
Synthesize,
};
use crate::introspection::{Count, Counter, CounterUpdate, Locatable, Location};
use crate::layout::{Abs, Em, HElem, Length, Ratio, Vt};
use crate::layout::{Abs, Em, HElem, Length, Ratio};
use crate::model::{Destination, Numbering, NumberingPattern, ParElem};
use crate::text::{SuperElem, TextElem, TextSize};
use crate::util::NonZeroExt;
@ -107,14 +108,15 @@ impl FootnoteElem {
}
/// Returns the location of the definition of this footnote.
pub fn declaration_location(&self, vt: &Vt) -> StrResult<Location> {
pub fn declaration_location(&self, engine: &Engine) -> StrResult<Location> {
match self.body() {
FootnoteBody::Reference(label) => {
let element: Prehashed<Content> = vt.introspector.query_label(*label)?;
let element: Prehashed<Content> =
engine.introspector.query_label(*label)?;
let footnote = element
.to::<FootnoteElem>()
.ok_or("referenced element should be a footnote")?;
footnote.declaration_location(vt)
footnote.declaration_location(engine)
}
_ => Ok(self.location().unwrap()),
}
@ -122,7 +124,7 @@ impl FootnoteElem {
}
impl Synthesize for FootnoteElem {
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
self.push_numbering(self.numbering(styles).clone());
Ok(())
}
@ -130,12 +132,12 @@ impl Synthesize for FootnoteElem {
impl Show for FootnoteElem {
#[tracing::instrument(name = "FootnoteElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
Ok(vt.delayed(|vt| {
let loc = self.declaration_location(vt).at(self.span())?;
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
Ok(engine.delayed(|engine| {
let loc = self.declaration_location(engine).at(self.span())?;
let numbering = self.numbering(styles);
let counter = Counter::of(Self::elem());
let num = counter.at(vt, loc)?.display(vt, numbering)?;
let num = counter.at(engine, loc)?.display(engine, numbering)?;
let sup = SuperElem::new(num).pack();
let loc = loc.variant(1);
// Add zero-width weak spacing to make the footnote "sticky".
@ -271,7 +273,7 @@ pub struct FootnoteEntry {
}
impl Show for FootnoteEntry {
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let note = self.note();
let number_gap = Em::new(0.05);
let default = StyleChain::default();
@ -283,7 +285,7 @@ impl Show for FootnoteEntry {
))
};
let num = counter.at(vt, loc)?.display(vt, numbering)?;
let num = counter.at(engine, loc)?.display(engine, numbering)?;
let sup = SuperElem::new(num)
.pack()
.linked(Destination::Location(loc))

View File

@ -1,12 +1,13 @@
use std::num::NonZeroUsize;
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{
cast, elem, Content, Finalize, NativeElement, Show, Smart, StyleChain, Styles,
Synthesize,
};
use crate::introspection::{Count, Counter, CounterUpdate, Locatable};
use crate::layout::{BlockElem, Em, HElem, VElem, Vt};
use crate::layout::{BlockElem, Em, HElem, VElem};
use crate::model::{Numbering, Outlinable, Refable, Supplement};
use crate::text::{FontWeight, Lang, LocalName, Region, SpaceElem, TextElem, TextSize};
use crate::util::{option_eq, NonZeroExt};
@ -126,12 +127,18 @@ pub struct HeadingElem {
}
impl Synthesize for HeadingElem {
fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
fn synthesize(
&mut self,
engine: &mut Engine,
styles: StyleChain,
) -> SourceResult<()> {
// Resolve the supplement.
let supplement = match self.supplement(styles) {
Smart::Auto => TextElem::packed(Self::local_name_in(styles)),
Smart::Custom(None) => Content::empty(),
Smart::Custom(Some(supplement)) => supplement.resolve(vt, [self.clone()])?,
Smart::Custom(Some(supplement)) => {
supplement.resolve(engine, [self.clone()])?
}
};
self.push_level(self.level(styles));
@ -146,7 +153,7 @@ impl Synthesize for HeadingElem {
impl Show for HeadingElem {
#[tracing::instrument(name = "HeadingElem::show", skip_all)]
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let mut realized = self.body().clone();
if let Some(numbering) = self.numbering(styles).as_ref() {
realized = Counter::of(Self::elem())
@ -214,7 +221,7 @@ impl Refable for HeadingElem {
}
impl Outlinable for HeadingElem {
fn outline(&self, vt: &mut Vt) -> SourceResult<Option<Content>> {
fn outline(&self, engine: &mut Engine) -> SourceResult<Option<Content>> {
if !self.outlined(StyleChain::default()) {
return Ok(None);
}
@ -223,8 +230,8 @@ impl Outlinable for HeadingElem {
let default = StyleChain::default();
if let Some(numbering) = self.numbering(default).as_ref() {
let numbers = Counter::of(Self::elem())
.at(vt, self.location().unwrap())?
.display(vt, numbering)?;
.at(engine, self.location().unwrap())?
.display(engine, numbering)?;
content = numbers + SpaceElem::new().pack() + content;
};

View File

@ -1,11 +1,12 @@
use ecow::{eco_format, EcoString};
use crate::diag::{At, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, Content, Label, NativeElement, Repr, Show, Smart, StyleChain,
};
use crate::introspection::Location;
use crate::layout::{Position, Vt};
use crate::layout::Position;
use crate::text::{Hyphenate, TextElem};
/// Links to a URL or a location in the document.
@ -89,14 +90,14 @@ impl LinkElem {
}
impl Show for LinkElem {
#[tracing::instrument(name = "LinkElem::show", skip(self, vt))]
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
#[tracing::instrument(name = "LinkElem::show", skip(self, engine))]
fn show(&self, engine: &mut Engine, _: StyleChain) -> SourceResult<Content> {
let body = self.body().clone();
let linked = match self.dest() {
LinkTarget::Dest(dest) => body.linked(dest.clone()),
LinkTarget::Label(label) => vt
.delayed(|vt| {
let elem = vt.introspector.query_label(*label).at(self.span())?;
LinkTarget::Label(label) => engine
.delayed(|engine| {
let elem = engine.introspector.query_label(*label).at(self.span())?;
let dest = Destination::Location(elem.location().unwrap());
Ok(Some(body.clone().linked(dest)))
})

View File

@ -1,11 +1,12 @@
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, Array, Content, Fold, Func, NativeElement, Smart, StyleChain,
Value,
};
use crate::layout::{
Axes, BlockElem, Em, Fragment, GridLayouter, HAlign, Layout, Length, Regions, Sizing,
Spacing, VAlign, Vt,
Spacing, VAlign,
};
use crate::model::ParElem;
use crate::text::TextElem;
@ -130,7 +131,7 @@ impl Layout for ListElem {
#[tracing::instrument(name = "ListElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -146,7 +147,7 @@ impl Layout for ListElem {
let depth = self.depth(styles);
let marker = self
.marker(styles)
.resolve(vt, depth)?
.resolve(engine, depth)?
// avoid '#set align' interference with the list
.aligned(HAlign::Start + VAlign::Top);
@ -172,7 +173,7 @@ impl Layout for ListElem {
self.span(),
);
Ok(layouter.layout(vt)?.fragment)
Ok(layouter.layout(engine)?.fragment)
}
}
@ -198,12 +199,12 @@ pub enum ListMarker {
impl ListMarker {
/// Resolve the marker for the given depth.
fn resolve(&self, vt: &mut Vt, depth: usize) -> SourceResult<Content> {
fn resolve(&self, engine: &mut Engine, depth: usize) -> SourceResult<Content> {
Ok(match self {
Self::Content(list) => {
list.get(depth).or(list.last()).cloned().unwrap_or_default()
}
Self::Func(func) => func.call_vt(vt, [depth])?.display(),
Self::Func(func) => func.call(engine, [depth])?.display(),
})
}
}

View File

@ -5,9 +5,9 @@ use chinese_number::{ChineseCase, ChineseCountMethod, ChineseVariant, NumberToCh
use ecow::{eco_format, EcoString, EcoVec};
use crate::diag::SourceResult;
use crate::eval::Vm;
use crate::foundations::{cast, func, Args, Func, Str, Value};
use crate::layout::{PdfPageLabel, PdfPageLabelStyle, Vt};
use crate::engine::Engine;
use crate::foundations::{cast, func, Func, Str, Value};
use crate::layout::{PdfPageLabel, PdfPageLabelStyle};
use crate::text::Case;
/// Applies a numbering to a sequence of numbers.
@ -35,8 +35,8 @@ use crate::text::Case;
/// ```
#[func]
pub fn numbering(
/// The virtual machine.
vm: &mut Vm,
/// The engine.
engine: &mut Engine,
/// Defines how the numbering works.
///
/// **Counting symbols** are `1`, `a`, `A`, `i`, `I`, `い`, `イ`, `א`, `가`,
@ -68,7 +68,7 @@ pub fn numbering(
#[variadic]
numbers: Vec<usize>,
) -> SourceResult<Value> {
numbering.apply_vm(vm, &numbers)
numbering.apply(engine, &numbers)
}
/// How to number a sequence of things.
@ -82,21 +82,10 @@ pub enum Numbering {
impl Numbering {
/// Apply the pattern to the given numbers.
pub fn apply_vm(&self, vm: &mut Vm, numbers: &[usize]) -> SourceResult<Value> {
pub fn apply(&self, engine: &mut Engine, numbers: &[usize]) -> SourceResult<Value> {
Ok(match self {
Self::Pattern(pattern) => Value::Str(pattern.apply(numbers).into()),
Self::Func(func) => {
let args = Args::new(func.span(), numbers.iter().copied());
func.call_vm(vm, args)?
}
})
}
/// Apply the pattern to the given numbers.
pub fn apply_vt(&self, vt: &mut Vt, numbers: &[usize]) -> SourceResult<Value> {
Ok(match self {
Self::Pattern(pattern) => Value::Str(pattern.apply(numbers).into()),
Self::Func(func) => func.call_vt(vt, numbers.iter().copied())?,
Self::Func(func) => func.call(engine, numbers.iter().copied())?,
})
}

View File

@ -2,12 +2,13 @@ use std::num::NonZeroUsize;
use std::str::FromStr;
use crate::diag::{bail, error, At, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, select_where, Content, Finalize, Func, LocatableSelector,
NativeElement, Show, Smart, StyleChain,
};
use crate::introspection::{Counter, CounterKey, Locatable};
use crate::layout::{BoxElem, Fr, HElem, HideElem, Length, Rel, RepeatElem, Spacing, Vt};
use crate::layout::{BoxElem, Fr, HElem, HideElem, Length, Rel, RepeatElem, Spacing};
use crate::model::{Destination, HeadingElem, NumberingPattern, ParbreakElem, Refable};
use crate::syntax::Span;
use crate::text::{Lang, LinebreakElem, LocalName, Region, SpaceElem, TextElem};
@ -186,7 +187,7 @@ impl OutlineElem {
impl Show for OutlineElem {
#[tracing::instrument(name = "OutlineElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let mut seq = vec![ParbreakElem::new().pack()];
// Build the outline title.
if let Some(title) = self.title(styles) {
@ -201,11 +202,11 @@ impl Show for OutlineElem {
let depth = self.depth(styles).unwrap_or(NonZeroUsize::new(usize::MAX).unwrap());
let mut ancestors: Vec<&Content> = vec![];
let elems = vt.introspector.query(&self.target(styles).0);
let elems = engine.introspector.query(&self.target(styles).0);
for elem in &elems {
let Some(entry) = OutlineEntry::from_outlinable(
vt,
engine,
self.span(),
elem.clone().into_inner(),
self.fill(styles),
@ -229,7 +230,7 @@ impl Show for OutlineElem {
ancestors.pop();
}
OutlineIndent::apply(indent, vt, &ancestors, &mut seq, self.span())?;
OutlineIndent::apply(indent, engine, &ancestors, &mut seq, self.span())?;
// Add the overridable outline entry, followed by a line break.
seq.push(entry.pack());
@ -292,7 +293,7 @@ impl LocalName for OutlineElem {
/// `#outline()` element.
pub trait Outlinable: Refable {
/// Produce an outline item for this element.
fn outline(&self, vt: &mut Vt) -> SourceResult<Option<Content>>;
fn outline(&self, engine: &mut Engine) -> SourceResult<Option<Content>>;
/// Returns the nesting level of this element.
fn level(&self) -> NonZeroUsize {
@ -311,7 +312,7 @@ pub enum OutlineIndent {
impl OutlineIndent {
fn apply(
indent: &Option<Smart<Self>>,
vt: &mut Vt,
engine: &mut Engine,
ancestors: &Vec<&Content>,
seq: &mut Vec<Content>,
span: Span,
@ -330,8 +331,8 @@ impl OutlineIndent {
if let Some(numbering) = ancestor_outlinable.numbering() {
let numbers = ancestor_outlinable
.counter()
.at(vt, ancestor.location().unwrap())?
.display(vt, &numbering)?;
.at(engine, ancestor.location().unwrap())?
.display(engine, &numbering)?;
hidden += numbers + SpaceElem::new().pack();
};
@ -355,7 +356,7 @@ impl OutlineIndent {
Some(Smart::Custom(OutlineIndent::Func(func))) => {
let depth = ancestors.len();
let LengthOrContent(content) =
func.call_vt(vt, [depth])?.cast().at(span)?;
func.call(engine, [depth])?.cast().at(span)?;
if !content.is_empty() {
seq.push(content);
}
@ -455,7 +456,7 @@ impl OutlineEntry {
/// be outlined (e.g. heading with 'outlined: false'), does not generate an
/// entry instance (returns `Ok(None)`).
fn from_outlinable(
vt: &mut Vt,
engine: &mut Engine,
span: Span,
elem: Content,
fill: Option<Content>,
@ -464,27 +465,27 @@ impl OutlineEntry {
bail!(span, "cannot outline {}", elem.func().name());
};
let Some(body) = outlinable.outline(vt)? else {
let Some(body) = outlinable.outline(engine)? else {
return Ok(None);
};
let location = elem.location().unwrap();
let page_numbering = vt
let page_numbering = engine
.introspector
.page_numbering(location)
.cloned()
.unwrap_or_else(|| NumberingPattern::from_str("1").unwrap().into());
let page = Counter::new(CounterKey::Page)
.at(vt, location)?
.display(vt, &page_numbering)?;
.at(engine, location)?
.display(engine, &page_numbering)?;
Ok(Some(Self::new(outlinable.level(), elem, body, fill, page)))
}
}
impl Show for OutlineEntry {
fn show(&self, _vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
let mut seq = vec![];
let elem = self.element();

View File

@ -1,12 +1,12 @@
use comemo::Prehashed;
use crate::diag::SourceResult;
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{
elem, Args, Cast, Construct, Content, NativeElement, Set, Smart, StyleChain,
Unlabellable,
};
use crate::layout::{Em, Fragment, Length, Size, Vt};
use crate::layout::{Em, Fragment, Length, Size};
/// Arranges text, spacing and inline-level elements into a paragraph.
///
@ -108,11 +108,11 @@ pub struct ParElem {
}
impl Construct for ParElem {
fn construct(vm: &mut Vm, args: &mut Args) -> SourceResult<Content> {
fn construct(engine: &mut Engine, args: &mut Args) -> SourceResult<Content> {
// The paragraph constructor is special: It doesn't create a paragraph
// element. Instead, it just ensures that the passed content lives in a
// separate paragraph and styles it.
let styles = Self::set(vm, args)?;
let styles = Self::set(engine, args)?;
let body = args.expect::<Content>("body")?;
Ok(Content::sequence([
ParbreakElem::new().pack(),
@ -127,7 +127,7 @@ impl ParElem {
#[tracing::instrument(name = "ParElement::layout", skip_all)]
pub fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
consecutive: bool,
region: Size,
@ -135,7 +135,7 @@ impl ParElem {
) -> SourceResult<Fragment> {
crate::layout::layout_inline(
self.children(),
vt,
engine,
styles,
consecutive,
region,

View File

@ -1,9 +1,10 @@
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{
cast, elem, Content, Finalize, Label, NativeElement, Show, Smart, StyleChain,
Synthesize,
};
use crate::layout::{Align, BlockElem, Em, HElem, PadElem, Spacing, VElem, Vt};
use crate::layout::{Align, BlockElem, Em, HElem, PadElem, Spacing, VElem};
use crate::model::{CitationForm, CiteElem};
use crate::text::{SmartQuoteElem, SpaceElem, TextElem};
@ -145,7 +146,7 @@ cast! {
}
impl Synthesize for QuoteElem {
fn synthesize(&mut self, _: &mut Vt, styles: StyleChain) -> SourceResult<()> {
fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
self.push_block(self.block(styles));
self.push_quotes(self.quotes(styles));
Ok(())
@ -153,7 +154,7 @@ impl Synthesize for QuoteElem {
}
impl Show for QuoteElem {
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let mut realized = self.body().clone();
let block = self.block(styles);

View File

@ -1,12 +1,12 @@
use ecow::eco_format;
use crate::diag::{bail, At, Hint, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, Content, Func, IntoValue, Label, NativeElement, Show, Smart, StyleChain,
Synthesize,
};
use crate::introspection::{Counter, Locatable};
use crate::layout::Vt;
use crate::math::EquationElem;
use crate::model::{
BibliographyElem, CiteElem, Destination, Figurable, FootnoteElem, Numbering,
@ -137,14 +137,18 @@ pub struct RefElem {
}
impl Synthesize for RefElem {
fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
let citation = self.to_citation(vt, styles)?;
fn synthesize(
&mut self,
engine: &mut Engine,
styles: StyleChain,
) -> SourceResult<()> {
let citation = self.to_citation(engine, styles)?;
self.push_citation(Some(citation));
self.push_element(None);
let target = *self.target();
if !BibliographyElem::has(vt, target) {
if let Ok(elem) = vt.introspector.query_label(target) {
if !BibliographyElem::has(engine, target) {
if let Ok(elem) = engine.introspector.query_label(target) {
self.push_element(Some(elem.into_inner()));
return Ok(());
}
@ -156,18 +160,18 @@ impl Synthesize for RefElem {
impl Show for RefElem {
#[tracing::instrument(name = "RefElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
Ok(vt.delayed(|vt| {
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
Ok(engine.delayed(|engine| {
let target = *self.target();
let elem = vt.introspector.query_label(target);
let elem = engine.introspector.query_label(target);
let span = self.span();
if BibliographyElem::has(vt, target) {
if BibliographyElem::has(engine, target) {
if elem.is_ok() {
bail!(span, "label occurs in the document and its bibliography");
}
return Ok(self.to_citation(vt, styles)?.spanned(span).pack());
return Ok(self.to_citation(engine, styles)?.spanned(span).pack());
}
let elem = elem.at(span)?;
@ -211,14 +215,14 @@ impl Show for RefElem {
let numbers = refable
.counter()
.at(vt, elem.location().unwrap())?
.display(vt, &numbering.trimmed())?;
.at(engine, elem.location().unwrap())?
.display(engine, &numbering.trimmed())?;
let supplement = match self.supplement(styles).as_ref() {
Smart::Auto => refable.supplement(),
Smart::Custom(None) => Content::empty(),
Smart::Custom(Some(supplement)) => {
supplement.resolve(vt, [(*elem).clone()])?
supplement.resolve(engine, [(*elem).clone()])?
}
};
@ -234,10 +238,14 @@ impl Show for RefElem {
impl RefElem {
/// Turn the reference into a citation.
pub fn to_citation(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<CiteElem> {
pub fn to_citation(
&self,
engine: &mut Engine,
styles: StyleChain,
) -> SourceResult<CiteElem> {
let mut elem = CiteElem::new(*self.target());
elem.set_location(self.location().unwrap());
elem.synthesize(vt, styles)?;
elem.synthesize(engine, styles)?;
elem.push_supplement(match self.supplement(styles).clone() {
Smart::Custom(Some(Supplement::Content(content))) => Some(content),
_ => None,
@ -258,12 +266,12 @@ impl Supplement {
/// Tries to resolve the supplement into its content.
pub fn resolve<T: IntoValue>(
&self,
vt: &mut Vt,
engine: &mut Engine,
args: impl IntoIterator<Item = T>,
) -> SourceResult<Content> {
Ok(match self {
Supplement::Content(content) => content.clone(),
Supplement::Func(func) => func.call_vt(vt, args)?.display(),
Supplement::Func(func) => func.call(engine, args)?.display(),
})
}
}

View File

@ -1,6 +1,6 @@
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{elem, Content, Show, StyleChain};
use crate::layout::Vt;
use crate::text::{TextElem, WeightDelta};
/// Strongly emphasizes content by increasing the font weight.
@ -39,7 +39,7 @@ pub struct StrongElem {
impl Show for StrongElem {
#[tracing::instrument(name = "StrongElem::show", skip_all)]
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
Ok(self
.body()
.clone()

View File

@ -1,11 +1,12 @@
use crate::diag::{At, SourceResult, StrResult};
use crate::engine::Engine;
use crate::foundations::{
elem, Array, CastInfo, Content, FromValue, Func, IntoValue, NativeElement, Reflect,
Smart, StyleChain, Value,
};
use crate::layout::{
Abs, Align, AlignElem, Axes, Fragment, FrameItem, GridLayouter, Layout, Length,
Point, Regions, Rel, Sides, Size, TrackSizings, Vt,
Point, Regions, Rel, Sides, Size, TrackSizings,
};
use crate::model::Figurable;
use crate::text::{Lang, LocalName, Region};
@ -158,7 +159,7 @@ impl Layout for TableElem {
#[tracing::instrument(name = "TableElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -181,7 +182,7 @@ impl Layout for TableElem {
let x = i % cols;
let y = i / cols;
if let Smart::Custom(alignment) = align.resolve(vt, x, y)? {
if let Smart::Custom(alignment) = align.resolve(engine, x, y)? {
child = child.styled(AlignElem::set_alignment(alignment));
}
@ -197,7 +198,7 @@ impl Layout for TableElem {
GridLayouter::new(tracks, gutter, &cells, regions, styles, self.span());
// Measure the columns and layout the grid row-by-row.
let mut layout = layouter.layout(vt)?;
let mut layout = layouter.layout(engine)?;
// Add lines and backgrounds.
for (frame, rows) in layout.fragment.iter_mut().zip(&layout.rows) {
@ -236,7 +237,7 @@ impl Layout for TableElem {
for (x, &col) in layout.cols.iter().enumerate() {
let mut dy = Abs::zero();
for row in rows {
if let Some(fill) = fill.resolve(vt, x, row.y)? {
if let Some(fill) = fill.resolve(engine, x, row.y)? {
let pos = Point::new(dx, dy);
let size = Size::new(col, row.height);
let rect = Geometry::Rect(size).filled(fill);
@ -275,10 +276,10 @@ pub enum Celled<T> {
impl<T: Default + Clone + FromValue> Celled<T> {
/// Resolve the value based on the cell position.
pub fn resolve(&self, vt: &mut Vt, x: usize, y: usize) -> SourceResult<T> {
pub fn resolve(&self, engine: &mut Engine, x: usize, y: usize) -> SourceResult<T> {
Ok(match self {
Self::Value(value) => value.clone(),
Self::Func(func) => func.call_vt(vt, [x, y])?.cast().at(func.span())?,
Self::Func(func) => func.call(engine, [x, y])?.cast().at(func.span())?,
Self::Array(array) => x
.checked_rem(array.len())
.and_then(|i| array.get(i))

View File

@ -1,9 +1,10 @@
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, Array, Content, NativeElement, Smart, StyleChain,
};
use crate::layout::{
BlockElem, Em, Fragment, HElem, Layout, Length, Regions, Spacing, VElem, Vt,
BlockElem, Em, Fragment, HElem, Layout, Length, Regions, Spacing, VElem,
};
use crate::model::ParElem;
use crate::util::Numeric;
@ -110,7 +111,7 @@ impl Layout for TermsElem {
#[tracing::instrument(name = "TermsElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -139,7 +140,7 @@ impl Layout for TermsElem {
Content::sequence(seq)
.styled(ParElem::set_hanging_indent(hanging_indent + indent))
.layout(vt, styles, regions)
.layout(engine, styles, regions)
}
}

View File

@ -10,7 +10,8 @@ use std::mem;
use smallvec::smallvec;
use typed_arena::Arena;
use crate::diag::{bail, SourceResult};
use crate::diag::{bail, error, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
Content, Finalize, Guard, NativeElement, Recipe, Selector, Show, StyleChain,
StyleVecBuilder, Styles, Synthesize,
@ -18,7 +19,7 @@ use crate::foundations::{
use crate::introspection::{Locatable, Meta, MetaElem};
use crate::layout::{
AlignElem, BlockElem, BoxElem, ColbreakElem, FlowElem, HElem, Layout, LayoutRoot,
PageElem, PagebreakElem, Parity, PlaceElem, VElem, Vt,
PageElem, PagebreakElem, Parity, PlaceElem, VElem,
};
use crate::math::{EquationElem, LayoutMath};
use crate::model::{
@ -33,6 +34,58 @@ use crate::visualize::{
SquareElem,
};
/// Realize into an element that is capable of root-level layout.
#[tracing::instrument(skip_all)]
pub fn realize_root<'a>(
engine: &mut Engine,
scratch: &'a Scratch<'a>,
content: &'a Content,
styles: StyleChain<'a>,
) -> SourceResult<(Cow<'a, Content>, StyleChain<'a>)> {
if content.can::<dyn LayoutRoot>() && !applicable(content, styles) {
return Ok((Cow::Borrowed(content), styles));
}
let mut builder = Builder::new(engine, scratch, true);
builder.accept(content, styles)?;
builder.interrupt_page(Some(styles), true)?;
let (pages, shared) = builder.doc.unwrap().pages.finish();
Ok((Cow::Owned(DocumentElem::new(pages.to_vec()).pack()), shared))
}
/// Realize into an element that is capable of block-level layout.
#[tracing::instrument(skip_all)]
pub fn realize_block<'a>(
engine: &mut Engine,
scratch: &'a Scratch<'a>,
content: &'a Content,
styles: StyleChain<'a>,
) -> SourceResult<(Cow<'a, Content>, StyleChain<'a>)> {
// These elements implement `Layout` but still require a flow for
// proper layout.
if content.can::<dyn Layout>()
&& !content.is::<BoxElem>()
&& !content.is::<LineElem>()
&& !content.is::<RectElem>()
&& !content.is::<SquareElem>()
&& !content.is::<EllipseElem>()
&& !content.is::<CircleElem>()
&& !content.is::<ImageElem>()
&& !content.is::<PolygonElem>()
&& !content.is::<PathElem>()
&& !content.is::<PlaceElem>()
&& !applicable(content, styles)
{
return Ok((Cow::Borrowed(content), styles));
}
let mut builder = Builder::new(engine, scratch, false);
builder.accept(content, styles)?;
builder.interrupt_par()?;
let (children, shared) = builder.flow.0.finish();
Ok((Cow::Owned(FlowElem::new(children.to_vec()).pack()), shared))
}
/// Whether the target is affected by show rules in the given style chain.
pub fn applicable(target: &Content, styles: StyleChain) -> bool {
if target.needs_preparation() {
@ -59,7 +112,7 @@ pub fn applicable(target: &Content, styles: StyleChain) -> bool {
/// Apply the show rules in the given style chain to a target.
pub fn realize(
vt: &mut Vt,
engine: &mut Engine,
target: &Content,
styles: StyleChain,
) -> SourceResult<Option<Content>> {
@ -67,12 +120,12 @@ pub fn realize(
if target.needs_preparation() {
let mut elem = target.clone();
if target.can::<dyn Locatable>() || target.label().is_some() {
let location = vt.locator.locate(hash128(target));
let location = engine.locator.locate(hash128(target));
elem.set_location(location);
}
if let Some(elem) = elem.with_mut::<dyn Synthesize>() {
elem.synthesize(vt, styles)?;
elem.synthesize(engine, styles)?;
}
elem.mark_prepared();
@ -97,7 +150,7 @@ pub fn realize(
for recipe in styles.recipes() {
let guard = Guard::Nth(n);
if recipe.applicable(target) && !target.is_guarded(guard) {
if let Some(content) = try_apply(vt, target, recipe, guard)? {
if let Some(content) = try_apply(engine, target, recipe, guard)? {
realized = Some(content);
break;
}
@ -109,7 +162,7 @@ pub fn realize(
if let Some(showable) = target.with::<dyn Show>() {
let guard = Guard::Base(target.func());
if realized.is_none() && !target.is_guarded(guard) {
realized = Some(showable.show(vt, styles)?);
realized = Some(showable.show(engine, styles)?);
}
}
@ -127,7 +180,7 @@ pub fn realize(
/// Try to apply a recipe to the target.
fn try_apply(
vt: &mut Vt,
engine: &mut Engine,
target: &Content,
recipe: &Recipe,
guard: Guard,
@ -138,7 +191,7 @@ fn try_apply(
return Ok(None);
}
recipe.apply_vt(vt, target.clone().guarded(guard)).map(Some)
recipe.apply(engine, target.clone().guarded(guard)).map(Some)
}
Some(Selector::Label(label)) => {
@ -146,7 +199,7 @@ fn try_apply(
return Ok(None);
}
recipe.apply_vt(vt, target.clone().guarded(guard)).map(Some)
recipe.apply(engine, target.clone().guarded(guard)).map(Some)
}
Some(Selector::Regex(regex)) => {
@ -172,7 +225,7 @@ fn try_apply(
}
let piece = make(m.as_str()).guarded(guard);
let transformed = recipe.apply_vt(vt, piece)?;
let transformed = recipe.apply(engine, piece)?;
result.push(transformed);
cursor = m.end();
}
@ -202,62 +255,10 @@ fn try_apply(
}
}
/// Realize into an element that is capable of root-level layout.
#[tracing::instrument(skip_all)]
pub fn realize_root<'a>(
vt: &mut Vt,
scratch: &'a Scratch<'a>,
content: &'a Content,
styles: StyleChain<'a>,
) -> SourceResult<(Cow<'a, Content>, StyleChain<'a>)> {
if content.can::<dyn LayoutRoot>() && !applicable(content, styles) {
return Ok((Cow::Borrowed(content), styles));
}
let mut builder = Builder::new(vt, scratch, true);
builder.accept(content, styles)?;
builder.interrupt_page(Some(styles), true)?;
let (pages, shared) = builder.doc.unwrap().pages.finish();
Ok((Cow::Owned(DocumentElem::new(pages.to_vec()).pack()), shared))
}
/// Realize into an element that is capable of block-level layout.
#[tracing::instrument(skip_all)]
pub fn realize_block<'a>(
vt: &mut Vt,
scratch: &'a Scratch<'a>,
content: &'a Content,
styles: StyleChain<'a>,
) -> SourceResult<(Cow<'a, Content>, StyleChain<'a>)> {
// These elements implement `Layout` but still require a flow for
// proper layout.
if content.can::<dyn Layout>()
&& !content.is::<BoxElem>()
&& !content.is::<LineElem>()
&& !content.is::<RectElem>()
&& !content.is::<SquareElem>()
&& !content.is::<EllipseElem>()
&& !content.is::<CircleElem>()
&& !content.is::<ImageElem>()
&& !content.is::<PolygonElem>()
&& !content.is::<PathElem>()
&& !content.is::<PlaceElem>()
&& !applicable(content, styles)
{
return Ok((Cow::Borrowed(content), styles));
}
let mut builder = Builder::new(vt, scratch, false);
builder.accept(content, styles)?;
builder.interrupt_par()?;
let (children, shared) = builder.flow.0.finish();
Ok((Cow::Owned(FlowElem::new(children.to_vec()).pack()), shared))
}
/// Builds a document or a flow element from content.
struct Builder<'a, 'v, 't> {
/// The virtual typesetter.
vt: &'v mut Vt<'t>,
/// The engine.
engine: &'v mut Engine<'t>,
/// Scratch arenas for building.
scratch: &'a Scratch<'a>,
/// The current document building state.
@ -282,9 +283,9 @@ pub struct Scratch<'a> {
}
impl<'a, 'v, 't> Builder<'a, 'v, 't> {
fn new(vt: &'v mut Vt<'t>, scratch: &'a Scratch<'a>, top: bool) -> Self {
fn new(engine: &'v mut Engine<'t>, scratch: &'a Scratch<'a>, top: bool) -> Self {
Self {
vt,
engine,
scratch,
doc: top.then(DocBuilder::default),
flow: FlowBuilder::default(),
@ -304,9 +305,19 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
self.scratch.content.alloc(EquationElem::new(content.clone()).pack());
}
if let Some(realized) = realize(self.vt, content, styles)? {
if let Some(realized) = realize(self.engine, content, styles)? {
self.engine.route.increase();
if self.engine.route.exceeding() {
bail!(error!(content.span(), "maximum show rule depth exceeded")
.with_hint("check whether the show rule matches its own output")
.with_hint(
"this is a current compiler limitation that will be resolved in the future"
));
}
let stored = self.scratch.content.alloc(realized);
return self.accept(stored, styles);
let v = self.accept(stored, styles);
self.engine.route.decrease();
return v;
}
if let Some((elem, local)) = content.to_styled() {

View File

@ -4,8 +4,9 @@ use ttf_parser::{GlyphId, OutlineBuilder};
use ecow::{eco_format, EcoString};
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{cast, elem, ty, Content, Fold, Repr, Show, Smart, StyleChain};
use crate::layout::{Abs, Em, Frame, FrameItem, Length, Point, Size, Vt};
use crate::layout::{Abs, Em, Frame, FrameItem, Length, Point, Size};
use crate::syntax::Span;
use crate::text::{
BottomEdge, BottomEdgeMetric, TextElem, TextItem, TopEdge, TopEdgeMetric,
@ -85,7 +86,7 @@ pub struct UnderlineElem {
impl Show for UnderlineElem {
#[tracing::instrument(name = "UnderlineElem::show", skip_all)]
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
Ok(self.body().clone().styled(TextElem::set_deco(Decoration {
line: DecoLine::Underline {
stroke: self.stroke(styles).unwrap_or_default(),
@ -177,7 +178,7 @@ pub struct OverlineElem {
impl Show for OverlineElem {
#[tracing::instrument(name = "OverlineElem::show", skip_all)]
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
Ok(self.body().clone().styled(TextElem::set_deco(Decoration {
line: DecoLine::Overline {
stroke: self.stroke(styles).unwrap_or_default(),
@ -254,7 +255,7 @@ pub struct StrikeElem {
impl Show for StrikeElem {
#[tracing::instrument(name = "StrikeElem::show", skip_all)]
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
Ok(self.body().clone().styled(TextElem::set_deco(Decoration {
// Note that we do not support evade option for strikethrough.
line: DecoLine::Strikethrough {
@ -324,7 +325,7 @@ pub struct HighlightElem {
impl Show for HighlightElem {
#[tracing::instrument(name = "HighlightElem::show", skip_all)]
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
Ok(self.body().clone().styled(TextElem::set_deco(Decoration {
line: DecoLine::Highlight {
fill: self.fill(styles),

View File

@ -35,7 +35,7 @@ use rustybuzz::{Feature, Tag};
use ttf_parser::Rect;
use crate::diag::{bail, error, SourceResult, StrResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{
cast, category, elem, Args, Array, Cast, Category, Construct, Content, Dict, Fold,
NativeElement, Never, PlainText, Repr, Resolve, Scope, Set, Smart, StyleChain, Value,
@ -656,11 +656,11 @@ impl Repr for TextElem {
}
impl Construct for TextElem {
fn construct(vm: &mut Vm, args: &mut Args) -> SourceResult<Content> {
fn construct(engine: &mut Engine, args: &mut Args) -> SourceResult<Content> {
// The text constructor is special: It doesn't create a text element.
// Instead, it leaves the passed argument structurally unchanged, but
// styles all text in it.
let styles = Self::set(vm, args)?;
let styles = Self::set(engine, args)?;
let body = args.expect::<Content>("body")?;
Ok(body.styled_with_map(styles))
}

View File

@ -10,12 +10,12 @@ use syntect::parsing::{SyntaxDefinition, SyntaxSet, SyntaxSetBuilder};
use unicode_segmentation::UnicodeSegmentation;
use crate::diag::{At, FileError, SourceResult, StrResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{
cast, elem, scope, Args, Array, Bytes, Content, Finalize, Fold, NativeElement,
PlainText, Show, Smart, StyleChain, Styles, Synthesize, Value,
};
use crate::layout::{BlockElem, Em, HAlign, Vt};
use crate::layout::{BlockElem, Em, HAlign};
use crate::model::Figurable;
use crate::syntax::{split_newlines, LinkedNode, Spanned};
use crate::text::{
@ -191,7 +191,7 @@ pub struct RawElem {
/// ```
/// ````
#[parse(
let (syntaxes, syntaxes_data) = parse_syntaxes(vm, args)?;
let (syntaxes, syntaxes_data) = parse_syntaxes(engine, args)?;
syntaxes
)]
#[fold]
@ -229,7 +229,7 @@ pub struct RawElem {
/// ```
/// ````
#[parse(
let (theme_path, theme_data) = parse_theme(vm, args)?;
let (theme_path, theme_data) = parse_theme(engine, args)?;
theme_path.map(Some)
)]
#[borrowed]
@ -288,7 +288,7 @@ impl RawElem {
}
impl Synthesize for RawElem {
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
fn synthesize(&mut self, _: &mut Engine, styles: StyleChain) -> SourceResult<()> {
self.push_lang(self.lang(styles).clone());
let mut text = self.text().clone();
@ -393,7 +393,7 @@ impl Synthesize for RawElem {
impl Show for RawElem {
#[tracing::instrument(name = "RawElem::show", skip_all)]
fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let mut lines = EcoVec::with_capacity((2 * self.lines().len()).saturating_sub(1));
for (i, line) in self.lines().iter().enumerate() {
if i != 0 {
@ -495,7 +495,7 @@ pub struct RawLine {
}
impl Show for RawLine {
fn show(&self, _vt: &mut Vt, _styles: StyleChain) -> SourceResult<Content> {
fn show(&self, _: &mut Engine, _styles: StyleChain) -> SourceResult<Content> {
Ok(self.body().clone())
}
}
@ -676,7 +676,7 @@ fn load_syntaxes(paths: &SyntaxPaths, bytes: &[Bytes]) -> StrResult<Arc<SyntaxSe
/// Function to parse the syntaxes argument.
/// Much nicer than having it be part of the `element` macro.
fn parse_syntaxes(
vm: &mut Vm,
engine: &mut Engine,
args: &mut Args,
) -> SourceResult<(Option<SyntaxPaths>, Option<Vec<Bytes>>)> {
let Some(Spanned { v: paths, span }) =
@ -690,8 +690,8 @@ fn parse_syntaxes(
.0
.iter()
.map(|path| {
let id = vm.resolve_path(path).at(span)?;
vm.world().file(id).at(span)
let id = span.resolve_path(path).at(span)?;
engine.world.file(id).at(span)
})
.collect::<SourceResult<Vec<Bytes>>>()?;
@ -713,7 +713,7 @@ fn load_theme(path: &str, bytes: &Bytes) -> StrResult<Arc<synt::Theme>> {
/// Function to parse the theme argument.
/// Much nicer than having it be part of the `element` macro.
fn parse_theme(
vm: &mut Vm,
engine: &mut Engine,
args: &mut Args,
) -> SourceResult<(Option<EcoString>, Option<Bytes>)> {
let Some(Spanned { v: path, span }) = args.named::<Spanned<EcoString>>("theme")?
@ -722,8 +722,8 @@ fn parse_theme(
};
// Load theme file.
let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
let id = span.resolve_path(&path).at(span)?;
let data = engine.world.file(id).at(span)?;
// Check that parsing works.
let _ = load_theme(&path, &data).at(span)?;

View File

@ -1,8 +1,9 @@
use ecow::EcoString;
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{elem, Content, Show, StyleChain};
use crate::layout::{Em, Length, Vt};
use crate::layout::{Em, Length};
use crate::text::{variant, SpaceElem, TextElem, TextSize};
use crate::World;
@ -48,12 +49,12 @@ pub struct SubElem {
impl Show for SubElem {
#[tracing::instrument(name = "SubElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let body = self.body().clone();
let mut transformed = None;
if self.typographic(styles) {
if let Some(text) = search_text(&body, true) {
if is_shapable(vt, &text, styles) {
if is_shapable(engine, &text, styles) {
transformed = Some(TextElem::packed(text));
}
}
@ -108,12 +109,12 @@ pub struct SuperElem {
impl Show for SuperElem {
#[tracing::instrument(name = "SuperElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
let body = self.body().clone();
let mut transformed = None;
if self.typographic(styles) {
if let Some(text) = search_text(&body, false) {
if is_shapable(vt, &text, styles) {
if is_shapable(engine, &text, styles) {
transformed = Some(TextElem::packed(text));
}
}
@ -149,8 +150,8 @@ fn search_text(content: &Content, sub: bool) -> Option<EcoString> {
/// Checks whether the first retrievable family contains all code points of the
/// given string.
fn is_shapable(vt: &Vt, text: &str, styles: StyleChain) -> bool {
let world = vt.world;
fn is_shapable(engine: &Engine, text: &str, styles: StyleChain) -> bool {
let world = engine.world;
for family in TextElem::font_in(styles) {
if let Some(font) = world
.book()

View File

@ -14,13 +14,14 @@ use comemo::{Prehashed, Tracked};
use ecow::EcoString;
use crate::diag::{bail, At, SourceResult, StrResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, func, scope, Bytes, Cast, Content, NativeElement, Resolve, Smart,
StyleChain,
};
use crate::layout::{
Abs, Axes, FixedAlign, Fragment, Frame, FrameItem, Layout, Length, Point, Regions,
Rel, Size, Vt,
Rel, Size,
};
use crate::loading::Readable;
use crate::model::Figurable;
@ -57,8 +58,8 @@ pub struct ImageElem {
#[parse(
let Spanned { v: path, span } =
args.expect::<Spanned<EcoString>>("path to image file")?;
let id = vm.resolve_path(&path).at(span)?;
let data = vm.world().file(id).at(span)?;
let id = span.resolve_path(&path).at(span)?;
let data = engine.world.file(id).at(span)?;
path
)]
#[borrowed]
@ -145,7 +146,7 @@ impl Layout for ImageElem {
#[tracing::instrument(name = "ImageElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
@ -181,7 +182,7 @@ impl Layout for ImageElem {
data.clone().into(),
format,
self.alt(styles),
vt.world,
engine.world,
&families(styles).map(|s| s.into()).collect::<Vec<_>>(),
)
.at(self.span())?;

View File

@ -1,7 +1,8 @@
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{elem, NativeElement, StyleChain};
use crate::layout::{
Abs, Angle, Axes, Fragment, Frame, FrameItem, Layout, Length, Regions, Rel, Size, Vt,
Abs, Angle, Axes, Fragment, Frame, FrameItem, Layout, Length, Regions, Rel, Size,
};
use crate::util::Numeric;
use crate::visualize::{Geometry, Stroke};
@ -61,7 +62,7 @@ impl Layout for LineElem {
#[tracing::instrument(name = "LineElem::layout", skip_all)]
fn layout(
&self,
_: &mut Vt,
_: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {

View File

@ -1,11 +1,12 @@
use kurbo::{CubicBez, ParamCurveExtrema};
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
array, cast, elem, Array, NativeElement, Reflect, Resolve, Smart, StyleChain,
};
use crate::layout::{
Abs, Axes, Fragment, Frame, FrameItem, Layout, Length, Point, Regions, Rel, Size, Vt,
Abs, Axes, Fragment, Frame, FrameItem, Layout, Length, Point, Regions, Rel, Size,
};
use crate::visualize::{FixedStroke, Geometry, Paint, Shape, Stroke};
@ -73,7 +74,7 @@ impl Layout for PathElem {
#[tracing::instrument(name = "PathElem::layout", skip_all)]
fn layout(
&self,
_: &mut Vt,
_: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {

View File

@ -5,7 +5,7 @@ use comemo::Prehashed;
use ecow::{eco_format, EcoString};
use crate::diag::{bail, error, SourceResult};
use crate::eval::Vm;
use crate::engine::Engine;
use crate::foundations::{func, scope, ty, Content, Repr, Smart, StyleChain};
use crate::layout::{Abs, Axes, Em, Frame, Layout, Length, Regions, Size};
use crate::syntax::{Span, Spanned};
@ -120,7 +120,7 @@ impl Pattern {
/// ```
#[func(constructor)]
pub fn construct(
vm: &mut Vm,
engine: &mut Engine,
/// The bounding box of each cell of the pattern.
#[named]
#[default(Spanned::new(Smart::Auto, Span::detached()))]
@ -173,11 +173,11 @@ impl Pattern {
let region = size.unwrap_or_else(|| Axes::splat(Abs::inf()));
// Layout the pattern.
let world = vm.vt.world;
let world = engine.world;
let library = world.library();
let styles = StyleChain::new(&library.styles);
let pod = Regions::one(region, Axes::splat(false));
let mut frame = body.layout(&mut vm.vt, styles, pod)?.into_frame();
let mut frame = body.layout(engine, styles, pod)?.into_frame();
// Check that the frame is non-zero.
if size.is_auto() && frame.size().is_zero() {

View File

@ -1,11 +1,12 @@
use std::f64::consts::PI;
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{
elem, func, scope, Content, NativeElement, Resolve, Smart, StyleChain,
};
use crate::layout::{
Axes, Em, Fragment, Frame, FrameItem, Layout, Length, Point, Regions, Rel, Vt,
Axes, Em, Fragment, Frame, FrameItem, Layout, Length, Point, Regions, Rel,
};
use crate::util::Numeric;
use crate::visualize::{FixedStroke, Geometry, Paint, Path, Shape, Stroke};
@ -125,7 +126,7 @@ impl Layout for PolygonElem {
#[tracing::instrument(name = "PolygonElem::layout", skip_all)]
fn layout(
&self,
_: &mut Vt,
_: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {

View File

@ -1,10 +1,11 @@
use std::f64::consts::SQRT_2;
use crate::diag::SourceResult;
use crate::engine::Engine;
use crate::foundations::{elem, Content, NativeElement, Resolve, Smart, StyleChain};
use crate::layout::{
Abs, Axes, Corner, Corners, Fragment, Frame, FrameItem, Layout, Length, Point, Ratio,
Regions, Rel, Sides, Size, Vt,
Regions, Rel, Sides, Size,
};
use crate::syntax::Span;
use crate::util::Get;
@ -134,12 +135,12 @@ impl Layout for RectElem {
#[tracing::instrument(name = "RectElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
layout(
vt,
engine,
styles,
regions,
ShapeKind::Rect,
@ -240,12 +241,12 @@ impl Layout for SquareElem {
#[tracing::instrument(name = "SquareElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
layout(
vt,
engine,
styles,
regions,
ShapeKind::Square,
@ -318,12 +319,12 @@ impl Layout for EllipseElem {
#[tracing::instrument(name = "EllipseElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
layout(
vt,
engine,
styles,
regions,
ShapeKind::Ellipse,
@ -421,12 +422,12 @@ impl Layout for CircleElem {
#[tracing::instrument(name = "CircleElem::layout", skip_all)]
fn layout(
&self,
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
layout(
vt,
engine,
styles,
regions,
ShapeKind::Circle,
@ -446,7 +447,7 @@ impl Layout for CircleElem {
#[tracing::instrument(name = "shape::layout", skip_all)]
#[allow(clippy::too_many_arguments)]
fn layout(
vt: &mut Vt,
engine: &mut Engine,
styles: StyleChain,
regions: Regions,
kind: ShapeKind,
@ -473,7 +474,7 @@ fn layout(
let child = child.clone().padded(inset.map(|side| side.map(Length::from)));
let expand = sizing.as_ref().map(Smart::is_custom);
let pod = Regions::one(region, expand);
frame = child.layout(vt, styles, pod)?.into_frame();
frame = child.layout(engine, styles, pod)?.into_frame();
// Enforce correct size.
*frame.size_mut() = expand.select(region, frame.size());
@ -484,7 +485,7 @@ fn layout(
frame.set_size(Size::splat(frame.size().max_by_side()));
let length = frame.size().max_by_side().min(region.min_by_side());
let pod = Regions::one(Size::splat(length), Axes::splat(true));
frame = child.layout(vt, styles, pod)?.into_frame();
frame = child.layout(engine, styles, pod)?.into_frame();
}
// Enforce correct size again.

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -57,7 +57,7 @@ fn bench_edit(iai: &mut Iai) {
fn bench_eval(iai: &mut Iai) {
let world = BenchWorld::new();
let route = typst::eval::Route::default();
let route = typst::engine::Route::default();
let mut tracer = typst::eval::Tracer::new();
iai.run(|| {
typst::eval::eval(world.track(), route.track(), tracer.track_mut(), &world.source)

View File

@ -535,7 +535,7 @@ fn test_part(
if world.print.model {
let world = (world as &dyn World).track();
let route = typst::eval::Route::default();
let route = typst::engine::Route::default();
let mut tracer = typst::eval::Tracer::new();
let module =

View File

@ -31,12 +31,41 @@
#let f = () => f
#test(type(f()), int)
---
// Test redefinition.
#let f(x) = "hello"
#let f(x) = if x != none { f(none) } else { "world" }
#test(f(1), "world")
---
// Error: 15-21 maximum function call depth exceeded
#let rec(n) = rec(n) + 1
#rec(1)
---
#let f(x) = "hello"
#let f(x) = if x != none { f(none) } else { "world" }
#test(f(1), "world")
// Test cyclic imports during layout.
// Error: 2-38 maximum layout depth exceeded
// Hint: 2-38 try to reduce the amount of nesting in your layout
#layout(_ => include "recursion.typ")
---
// Test recursive show rules.
// Error: 22-25 maximum show rule depth exceeded
// Hint: 22-25 check whether the show rule matches its own output
// Hint: 22-25 this is a current compiler limitation that will be resolved in the future
#show math.equation: $x$
$ x $
---
// Error: 18-21 maximum show rule depth exceeded
// Hint: 18-21 check whether the show rule matches its own output
// Hint: 18-21 this is a current compiler limitation that will be resolved in the future
#show "hey": box[hey]
hey
---
// Error: 14-19 maximum show rule depth exceeded
// Hint: 14-19 check whether the show rule matches its own output
// Hint: 14-19 this is a current compiler limitation that will be resolved in the future
#show "hey": "hey"
hey

View File

@ -0,0 +1,18 @@
// Test file loading in eval.
---
// Test absolute path.
#eval("image(\"/files/tiger.jpg\", width: 50%)")
---
#show raw: it => eval(it.text, mode: "markup")
```
#show emph: image("/files/tiger.jpg", width: 50%)
_Tiger!_
```
---
// Test relative path.
// Ref: false
#test(eval(`"HELLO" in read("./eval-path.typ")`.text), true)

View File

@ -102,31 +102,6 @@ Blue #move(dy: -0.15em)[🌊]
// Error: 7-17 cannot continue outside of loop
#eval("continue")
---
// Error: 7-32 cannot access file system from here
#eval("include \"../coma.typ\"")
---
// Error: 7-30 cannot access file system from here
#eval("image(\"/tiger.jpg\")")
---
// Error: 23-30 cannot access file system from here
#show raw: it => eval(it.text)
```
image("/tiger.jpg")
```
---
// Error: 23-42 cannot access file system from here
#show raw: it => eval("[" + it.text + "]")
```
#show emph: _ => image("/giraffe.jpg")
_No relative giraffe!_
```
---
// Error: 7-12 expected semicolon or line break
#eval("1 2")