mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Methods for page and location on queried content
This commit is contained in:
parent
af7fe4d760
commit
59903270dc
@ -13,7 +13,7 @@ use super::{
|
|||||||
Vm,
|
Vm,
|
||||||
};
|
};
|
||||||
use crate::diag::{bail, SourceResult, StrResult};
|
use crate::diag::{bail, SourceResult, StrResult};
|
||||||
use crate::model::{NodeId, Selector, StyleMap, Vt};
|
use crate::model::{Introspector, NodeId, Selector, StabilityProvider, StyleMap, Vt};
|
||||||
use crate::syntax::ast::{self, AstNode, Expr};
|
use crate::syntax::ast::{self, AstNode, Expr};
|
||||||
use crate::syntax::{SourceId, Span, SyntaxNode};
|
use crate::syntax::{SourceId, Span, SyntaxNode};
|
||||||
use crate::util::hash128;
|
use crate::util::hash128;
|
||||||
@ -102,9 +102,11 @@ impl Func {
|
|||||||
|
|
||||||
Closure::call(
|
Closure::call(
|
||||||
self,
|
self,
|
||||||
vm.world,
|
vm.world(),
|
||||||
route,
|
route,
|
||||||
TrackedMut::reborrow_mut(&mut vm.tracer),
|
TrackedMut::reborrow_mut(&mut vm.vt.tracer),
|
||||||
|
TrackedMut::reborrow_mut(&mut vm.vt.provider),
|
||||||
|
vm.vt.introspector,
|
||||||
vm.depth + 1,
|
vm.depth + 1,
|
||||||
args,
|
args,
|
||||||
)
|
)
|
||||||
@ -125,13 +127,7 @@ impl Func {
|
|||||||
let route = Route::default();
|
let route = Route::default();
|
||||||
let id = SourceId::detached();
|
let id = SourceId::detached();
|
||||||
let scopes = Scopes::new(None);
|
let scopes = Scopes::new(None);
|
||||||
let mut vm = Vm::new(
|
let mut vm = Vm::new(vt.reborrow_mut(), route.track(), id, scopes);
|
||||||
vt.world,
|
|
||||||
route.track(),
|
|
||||||
TrackedMut::reborrow_mut(&mut vt.tracer),
|
|
||||||
id,
|
|
||||||
scopes,
|
|
||||||
);
|
|
||||||
let args = Args::new(self.span(), args);
|
let args = Args::new(self.span(), args);
|
||||||
self.call_vm(&mut vm, args)
|
self.call_vm(&mut vm, args)
|
||||||
}
|
}
|
||||||
@ -318,6 +314,8 @@ impl Closure {
|
|||||||
world: Tracked<dyn World>,
|
world: Tracked<dyn World>,
|
||||||
route: Tracked<Route>,
|
route: Tracked<Route>,
|
||||||
tracer: TrackedMut<Tracer>,
|
tracer: TrackedMut<Tracer>,
|
||||||
|
provider: TrackedMut<StabilityProvider>,
|
||||||
|
introspector: Tracked<Introspector>,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
mut args: Args,
|
mut args: Args,
|
||||||
) -> SourceResult<Value> {
|
) -> SourceResult<Value> {
|
||||||
@ -358,7 +356,8 @@ impl Closure {
|
|||||||
args.finish()?;
|
args.finish()?;
|
||||||
|
|
||||||
// Evaluate the body.
|
// Evaluate the body.
|
||||||
let mut sub = Vm::new(world, route, tracer, closure.location, scopes);
|
let vt = Vt { world, tracer, provider, introspector };
|
||||||
|
let mut sub = Vm::new(vt, route, closure.location, scopes);
|
||||||
sub.depth = depth;
|
sub.depth = depth;
|
||||||
|
|
||||||
// Handle control flow.
|
// Handle control flow.
|
||||||
|
@ -73,6 +73,8 @@ pub fn call(
|
|||||||
"func" => Value::Func(content.id().into()),
|
"func" => Value::Func(content.id().into()),
|
||||||
"has" => Value::Bool(content.has(&args.expect::<EcoString>("field")?)),
|
"has" => Value::Bool(content.has(&args.expect::<EcoString>("field")?)),
|
||||||
"at" => content.at(&args.expect::<EcoString>("field")?).at(span)?.clone(),
|
"at" => content.at(&args.expect::<EcoString>("field")?).at(span)?.clone(),
|
||||||
|
"page" => content.page(&vm.vt).at(span)?.into(),
|
||||||
|
"location" => content.location(&vm.vt).at(span)?.into(),
|
||||||
_ => return missing(),
|
_ => return missing(),
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -249,7 +251,13 @@ pub fn methods_on(type_name: &str) -> &[(&'static str, bool)] {
|
|||||||
("starts-with", true),
|
("starts-with", true),
|
||||||
("trim", true),
|
("trim", true),
|
||||||
],
|
],
|
||||||
"content" => &[("func", false), ("has", true), ("at", true)],
|
"content" => &[
|
||||||
|
("func", false),
|
||||||
|
("has", true),
|
||||||
|
("at", true),
|
||||||
|
("page", false),
|
||||||
|
("location", false),
|
||||||
|
],
|
||||||
"array" => &[
|
"array" => &[
|
||||||
("all", true),
|
("all", true),
|
||||||
("any", true),
|
("any", true),
|
||||||
|
@ -47,7 +47,10 @@ use unicode_segmentation::UnicodeSegmentation;
|
|||||||
use crate::diag::{
|
use crate::diag::{
|
||||||
bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint,
|
bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint,
|
||||||
};
|
};
|
||||||
|
use crate::model::Introspector;
|
||||||
|
use crate::model::StabilityProvider;
|
||||||
use crate::model::Unlabellable;
|
use crate::model::Unlabellable;
|
||||||
|
use crate::model::Vt;
|
||||||
use crate::model::{Content, Label, Recipe, Selector, StyleMap, Transform};
|
use crate::model::{Content, Label, Recipe, Selector, StyleMap, Transform};
|
||||||
use crate::syntax::ast::AstNode;
|
use crate::syntax::ast::AstNode;
|
||||||
use crate::syntax::{
|
use crate::syntax::{
|
||||||
@ -81,7 +84,15 @@ pub fn eval(
|
|||||||
// Evaluate the module.
|
// Evaluate the module.
|
||||||
let route = unsafe { Route::insert(route, id) };
|
let route = unsafe { Route::insert(route, id) };
|
||||||
let scopes = Scopes::new(Some(library));
|
let scopes = Scopes::new(Some(library));
|
||||||
let mut vm = Vm::new(world, route.track(), tracer, id, scopes);
|
let mut provider = StabilityProvider::new();
|
||||||
|
let introspector = Introspector::new(&[]);
|
||||||
|
let vt = Vt {
|
||||||
|
world,
|
||||||
|
tracer,
|
||||||
|
provider: provider.track_mut(),
|
||||||
|
introspector: introspector.track(),
|
||||||
|
};
|
||||||
|
let mut vm = Vm::new(vt, route.track(), id, scopes);
|
||||||
let root = match source.root().cast::<ast::Markup>() {
|
let root = match source.root().cast::<ast::Markup>() {
|
||||||
Some(markup) if vm.traced.is_some() => markup,
|
Some(markup) if vm.traced.is_some() => markup,
|
||||||
_ => source.ast()?,
|
_ => source.ast()?,
|
||||||
@ -121,7 +132,15 @@ pub fn eval_code_str(
|
|||||||
let scopes = Scopes::new(Some(library));
|
let scopes = Scopes::new(Some(library));
|
||||||
let route = Route::default();
|
let route = Route::default();
|
||||||
let mut tracer = Tracer::default();
|
let mut tracer = Tracer::default();
|
||||||
let mut vm = Vm::new(world, route.track(), tracer.track_mut(), id, scopes);
|
let mut provider = StabilityProvider::new();
|
||||||
|
let introspector = Introspector::new(&[]);
|
||||||
|
let vt = Vt {
|
||||||
|
world,
|
||||||
|
tracer: tracer.track_mut(),
|
||||||
|
provider: provider.track_mut(),
|
||||||
|
introspector: introspector.track(),
|
||||||
|
};
|
||||||
|
let mut vm = Vm::new(vt, route.track(), id, scopes);
|
||||||
let code = root.cast::<ast::Code>().unwrap();
|
let code = root.cast::<ast::Code>().unwrap();
|
||||||
let result = code.eval(&mut vm);
|
let result = code.eval(&mut vm);
|
||||||
|
|
||||||
@ -138,14 +157,12 @@ pub fn eval_code_str(
|
|||||||
/// Holds the state needed to [evaluate](eval) Typst sources. A new
|
/// Holds the state needed to [evaluate](eval) Typst sources. A new
|
||||||
/// virtual machine is created for each module evaluation and function call.
|
/// virtual machine is created for each module evaluation and function call.
|
||||||
pub struct Vm<'a> {
|
pub struct Vm<'a> {
|
||||||
/// The compilation environment.
|
/// The underlying virtual typesetter.
|
||||||
world: Tracked<'a, dyn World>,
|
vt: Vt<'a>,
|
||||||
/// The language items.
|
/// The language items.
|
||||||
items: LangItems,
|
items: LangItems,
|
||||||
/// The route of source ids the VM took to reach its current location.
|
/// The route of source ids the VM took to reach its current location.
|
||||||
route: Tracked<'a, Route>,
|
route: Tracked<'a, Route>,
|
||||||
/// The tracer for inspection of the values an expression produces.
|
|
||||||
tracer: TrackedMut<'a, Tracer>,
|
|
||||||
/// The current location.
|
/// The current location.
|
||||||
location: SourceId,
|
location: SourceId,
|
||||||
/// A control flow event that is currently happening.
|
/// A control flow event that is currently happening.
|
||||||
@ -161,18 +178,17 @@ pub struct Vm<'a> {
|
|||||||
impl<'a> Vm<'a> {
|
impl<'a> Vm<'a> {
|
||||||
/// Create a new virtual machine.
|
/// Create a new virtual machine.
|
||||||
fn new(
|
fn new(
|
||||||
world: Tracked<'a, dyn World>,
|
vt: Vt<'a>,
|
||||||
route: Tracked<'a, Route>,
|
route: Tracked<'a, Route>,
|
||||||
tracer: TrackedMut<'a, Tracer>,
|
|
||||||
location: SourceId,
|
location: SourceId,
|
||||||
scopes: Scopes<'a>,
|
scopes: Scopes<'a>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let traced = tracer.span(location);
|
let traced = vt.tracer.span(location);
|
||||||
|
let items = vt.world.library().items.clone();
|
||||||
Self {
|
Self {
|
||||||
world,
|
vt,
|
||||||
items: world.library().items.clone(),
|
items,
|
||||||
route,
|
route,
|
||||||
tracer,
|
|
||||||
location,
|
location,
|
||||||
flow: None,
|
flow: None,
|
||||||
scopes,
|
scopes,
|
||||||
@ -183,14 +199,14 @@ impl<'a> Vm<'a> {
|
|||||||
|
|
||||||
/// Access the underlying world.
|
/// Access the underlying world.
|
||||||
pub fn world(&self) -> Tracked<'a, dyn World> {
|
pub fn world(&self) -> Tracked<'a, dyn World> {
|
||||||
self.world
|
self.vt.world
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define a variable in the current scope.
|
/// Define a variable in the current scope.
|
||||||
pub fn define(&mut self, var: ast::Ident, value: impl Into<Value>) {
|
pub fn define(&mut self, var: ast::Ident, value: impl Into<Value>) {
|
||||||
let value = value.into();
|
let value = value.into();
|
||||||
if self.traced == Some(var.span()) {
|
if self.traced == Some(var.span()) {
|
||||||
self.tracer.trace(value.clone());
|
self.vt.tracer.trace(value.clone());
|
||||||
}
|
}
|
||||||
self.scopes.top.define(var.take(), value);
|
self.scopes.top.define(var.take(), value);
|
||||||
}
|
}
|
||||||
@ -200,10 +216,10 @@ impl<'a> Vm<'a> {
|
|||||||
pub fn locate(&self, path: &str) -> StrResult<PathBuf> {
|
pub fn locate(&self, path: &str) -> StrResult<PathBuf> {
|
||||||
if !self.location.is_detached() {
|
if !self.location.is_detached() {
|
||||||
if let Some(path) = path.strip_prefix('/') {
|
if let Some(path) = path.strip_prefix('/') {
|
||||||
return Ok(self.world.root().join(path).normalize());
|
return Ok(self.world().root().join(path).normalize());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(dir) = self.world.source(self.location).path().parent() {
|
if let Some(dir) = self.world().source(self.location).path().parent() {
|
||||||
return Ok(dir.join(path).normalize());
|
return Ok(dir.join(path).normalize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -450,7 +466,7 @@ impl Eval for ast::Expr {
|
|||||||
.spanned(span);
|
.spanned(span);
|
||||||
|
|
||||||
if vm.traced == Some(span) {
|
if vm.traced == Some(span) {
|
||||||
vm.tracer.trace(v.clone());
|
vm.vt.tracer.trace(v.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(v)
|
Ok(v)
|
||||||
@ -1004,16 +1020,22 @@ impl Eval for ast::FuncCall {
|
|||||||
let args = args.eval(vm)?;
|
let args = args.eval(vm)?;
|
||||||
let target = target.access(vm)?;
|
let target = target.access(vm)?;
|
||||||
if !matches!(target, Value::Symbol(_) | Value::Module(_)) {
|
if !matches!(target, Value::Symbol(_) | Value::Module(_)) {
|
||||||
return methods::call_mut(target, &field, args, span)
|
return methods::call_mut(target, &field, args, span).trace(
|
||||||
.trace(vm.world, point, span);
|
vm.world(),
|
||||||
|
point,
|
||||||
|
span,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
(target.field(&field).at(field_span)?, args)
|
(target.field(&field).at(field_span)?, args)
|
||||||
} else {
|
} else {
|
||||||
let target = target.eval(vm)?;
|
let target = target.eval(vm)?;
|
||||||
let args = args.eval(vm)?;
|
let args = args.eval(vm)?;
|
||||||
if !matches!(target, Value::Symbol(_) | Value::Module(_)) {
|
if !matches!(target, Value::Symbol(_) | Value::Module(_)) {
|
||||||
return methods::call(vm, target, &field, args, span)
|
return methods::call(vm, target, &field, args, span).trace(
|
||||||
.trace(vm.world, point, span);
|
vm.world(),
|
||||||
|
point,
|
||||||
|
span,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
(target.field(&field).at(field_span)?, args)
|
(target.field(&field).at(field_span)?, args)
|
||||||
}
|
}
|
||||||
@ -1052,7 +1074,7 @@ impl Eval for ast::FuncCall {
|
|||||||
|
|
||||||
let callee = callee.cast::<Func>().at(callee_span)?;
|
let callee = callee.cast::<Func>().at(callee_span)?;
|
||||||
let point = || Tracepoint::Call(callee.name().map(Into::into));
|
let point = || Tracepoint::Call(callee.name().map(Into::into));
|
||||||
callee.call_vm(vm, args).trace(vm.world, point, span)
|
callee.call_vm(vm, args).trace(vm.world(), point, span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1431,8 +1453,9 @@ fn import(vm: &mut Vm, source: Value, span: Span) -> SourceResult<Module> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Load the source file.
|
// Load the source file.
|
||||||
|
let world = vm.world();
|
||||||
let full = vm.locate(&path).at(span)?;
|
let full = vm.locate(&path).at(span)?;
|
||||||
let id = vm.world.resolve(&full).at(span)?;
|
let id = world.resolve(&full).at(span)?;
|
||||||
|
|
||||||
// Prevent cyclic importing.
|
// Prevent cyclic importing.
|
||||||
if vm.route.contains(id) {
|
if vm.route.contains(id) {
|
||||||
@ -1440,10 +1463,10 @@ fn import(vm: &mut Vm, source: Value, span: Span) -> SourceResult<Module> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the file.
|
// Evaluate the file.
|
||||||
let source = vm.world.source(id);
|
let source = world.source(id);
|
||||||
let point = || Tracepoint::Import;
|
let point = || Tracepoint::Import;
|
||||||
eval(vm.world, vm.route, TrackedMut::reborrow_mut(&mut vm.tracer), source)
|
eval(world, vm.route, TrackedMut::reborrow_mut(&mut vm.vt.tracer), source)
|
||||||
.trace(vm.world, point, span)
|
.trace(world, point, span)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eval for ast::LoopBreak {
|
impl Eval for ast::LoopBreak {
|
||||||
@ -1506,7 +1529,7 @@ impl Access for ast::Ident {
|
|||||||
let span = self.span();
|
let span = self.span();
|
||||||
let value = vm.scopes.get_mut(self).at(span)?;
|
let value = vm.scopes.get_mut(self).at(span)?;
|
||||||
if vm.traced == Some(span) {
|
if vm.traced == Some(span) {
|
||||||
vm.tracer.trace(value.clone());
|
vm.vt.tracer.trace(value.clone());
|
||||||
}
|
}
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ use std::any::TypeId;
|
|||||||
use std::fmt::{self, Debug, Formatter, Write};
|
use std::fmt::{self, Debug, Formatter, Write};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::iter::{self, Sum};
|
use std::iter::{self, Sum};
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
use std::ops::{Add, AddAssign, Deref};
|
use std::ops::{Add, AddAssign, Deref};
|
||||||
|
|
||||||
use ecow::{eco_format, EcoString, EcoVec};
|
use ecow::{eco_format, EcoString, EcoVec};
|
||||||
@ -9,10 +10,10 @@ use once_cell::sync::Lazy;
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
node, Behave, Behaviour, Fold, Guard, Locatable, Recipe, StableId, Style, StyleMap,
|
node, Behave, Behaviour, Fold, Guard, Locatable, Recipe, StableId, Style, StyleMap,
|
||||||
Synthesize,
|
Synthesize, Vt,
|
||||||
};
|
};
|
||||||
use crate::diag::{SourceResult, StrResult};
|
use crate::diag::{SourceResult, StrResult};
|
||||||
use crate::doc::Meta;
|
use crate::doc::{Location, Meta};
|
||||||
use crate::eval::{
|
use crate::eval::{
|
||||||
cast_from_value, cast_to_value, Args, Cast, Func, FuncInfo, Str, Value, Vm,
|
cast_from_value, cast_to_value, Args, Cast, Func, FuncInfo, Str, Value, Vm,
|
||||||
};
|
};
|
||||||
@ -185,6 +186,22 @@ impl Content {
|
|||||||
self.field(field).ok_or_else(|| missing_field(field))
|
self.field(field).ok_or_else(|| missing_field(field))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determine the page of this content.
|
||||||
|
pub fn page(&self, vt: &Vt) -> StrResult<NonZeroUsize> {
|
||||||
|
match self.stable_id() {
|
||||||
|
Some(id) => Ok(vt.introspector.page(id)),
|
||||||
|
None => Err("this method can only be called on queried content".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine the location of this content.
|
||||||
|
pub fn location(&self, vt: &Vt) -> StrResult<Location> {
|
||||||
|
match self.stable_id() {
|
||||||
|
Some(id) => Ok(vt.introspector.location(id)),
|
||||||
|
None => Err("this method can only be called on queried content".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The content's label.
|
/// The content's label.
|
||||||
pub fn label(&self) -> Option<&Label> {
|
pub fn label(&self) -> Option<&Label> {
|
||||||
match self.field("label")? {
|
match self.field("label")? {
|
||||||
|
@ -53,8 +53,7 @@ pub fn typeset(
|
|||||||
|
|
||||||
/// A virtual typesetter.
|
/// A virtual typesetter.
|
||||||
///
|
///
|
||||||
/// Holds the state needed to [typeset] content. This is the equivalent to the
|
/// Holds the state needed to [typeset] content.
|
||||||
/// [Vm](crate::eval::Vm) for typesetting.
|
|
||||||
pub struct Vt<'a> {
|
pub struct Vt<'a> {
|
||||||
/// The compilation environment.
|
/// The compilation environment.
|
||||||
pub world: Tracked<'a, dyn World>,
|
pub world: Tracked<'a, dyn World>,
|
||||||
@ -66,6 +65,18 @@ pub struct Vt<'a> {
|
|||||||
pub introspector: Tracked<'a, Introspector>,
|
pub introspector: Tracked<'a, Introspector>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Vt<'_> {
|
||||||
|
/// Mutably reborrow with a shorter lifetime.
|
||||||
|
pub fn reborrow_mut(&mut self) -> Vt<'_> {
|
||||||
|
Vt {
|
||||||
|
world: self.world,
|
||||||
|
tracer: TrackedMut::reborrow_mut(&mut self.tracer),
|
||||||
|
provider: TrackedMut::reborrow_mut(&mut self.provider),
|
||||||
|
introspector: self.introspector,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Provides stable identities to nodes.
|
/// Provides stable identities to nodes.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct StabilityProvider {
|
pub struct StabilityProvider {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user