mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Remove Tracer
(#4365)
This commit is contained in:
parent
f91cad7d78
commit
a68a241570
@ -8,8 +8,7 @@ use codespan_reporting::term;
|
||||
use ecow::{eco_format, eco_vec, EcoString, EcoVec};
|
||||
use parking_lot::RwLock;
|
||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||
use typst::diag::{bail, FileError, Severity, SourceDiagnostic, StrResult};
|
||||
use typst::eval::Tracer;
|
||||
use typst::diag::{bail, FileError, Severity, SourceDiagnostic, StrResult, Warned};
|
||||
use typst::foundations::{Datetime, Smart};
|
||||
use typst::layout::{Frame, PageRanges};
|
||||
use typst::model::Document;
|
||||
@ -112,11 +111,9 @@ pub fn compile_once(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut tracer = Tracer::new();
|
||||
let result = typst::compile(world, &mut tracer);
|
||||
let warnings = tracer.warnings();
|
||||
let Warned { output, warnings } = typst::compile(world);
|
||||
|
||||
match result {
|
||||
match output {
|
||||
// Export the PDF / PNG.
|
||||
Ok(document) => {
|
||||
export(world, &document, command, watching)?;
|
||||
|
@ -1,8 +1,8 @@
|
||||
use comemo::Track;
|
||||
use ecow::{eco_format, EcoString};
|
||||
use serde::Serialize;
|
||||
use typst::diag::{bail, HintedStrResult, StrResult};
|
||||
use typst::eval::{eval_string, EvalMode, Tracer};
|
||||
use typst::diag::{bail, HintedStrResult, StrResult, Warned};
|
||||
use typst::eval::{eval_string, EvalMode};
|
||||
use typst::foundations::{Content, IntoValue, LocatableSelector, Scope};
|
||||
use typst::model::Document;
|
||||
use typst::syntax::Span;
|
||||
@ -21,11 +21,9 @@ pub fn query(command: &QueryCommand) -> HintedStrResult<()> {
|
||||
world.reset();
|
||||
world.source(world.main()).map_err(|err| err.to_string())?;
|
||||
|
||||
let mut tracer = Tracer::new();
|
||||
let result = typst::compile(&world, &mut tracer);
|
||||
let warnings = tracer.warnings();
|
||||
let Warned { output, warnings } = typst::compile(&world);
|
||||
|
||||
match result {
|
||||
match output {
|
||||
// Retrieve and print query results.
|
||||
Ok(document) => {
|
||||
let data = retrieve(&world, command, &document)?;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use comemo::Track;
|
||||
use ecow::{eco_vec, EcoString, EcoVec};
|
||||
use typst::engine::{Engine, Route};
|
||||
use typst::eval::{Tracer, Vm};
|
||||
use typst::engine::{Engine, Route, Sink, Traced};
|
||||
use typst::eval::Vm;
|
||||
use typst::foundations::{Context, Label, Scopes, Styles, Value};
|
||||
use typst::introspection::Introspector;
|
||||
use typst::model::{BibliographyElem, Document};
|
||||
@ -38,10 +38,7 @@ pub fn analyze_expr(
|
||||
}
|
||||
}
|
||||
|
||||
let mut tracer = Tracer::new();
|
||||
tracer.inspect(node.span());
|
||||
typst::compile(world, &mut tracer).ok();
|
||||
return tracer.values();
|
||||
return typst::trace(world, node.span());
|
||||
}
|
||||
};
|
||||
|
||||
@ -59,12 +56,14 @@ pub fn analyze_import(world: &dyn World, source: &LinkedNode) -> Option<Value> {
|
||||
}
|
||||
|
||||
let introspector = Introspector::default();
|
||||
let mut tracer = Tracer::new();
|
||||
let traced = Traced::default();
|
||||
let mut sink = Sink::new();
|
||||
let engine = Engine {
|
||||
world: world.track(),
|
||||
route: Route::default(),
|
||||
introspector: introspector.track(),
|
||||
tracer: tracer.track_mut(),
|
||||
traced: traced.track(),
|
||||
sink: sink.track_mut(),
|
||||
route: Route::default(),
|
||||
};
|
||||
|
||||
let context = Context::none();
|
||||
|
@ -1406,7 +1406,6 @@ impl<'a> CompletionContext<'a> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use typst::eval::Tracer;
|
||||
|
||||
use super::autocomplete;
|
||||
use crate::tests::TestWorld;
|
||||
@ -1414,7 +1413,7 @@ mod tests {
|
||||
#[track_caller]
|
||||
fn test(text: &str, cursor: usize, contains: &[&str], excludes: &[&str]) {
|
||||
let world = TestWorld::new(text);
|
||||
let doc = typst::compile(&world, &mut Tracer::new()).ok();
|
||||
let doc = typst::compile(&world).output.ok();
|
||||
let (_, completions) =
|
||||
autocomplete(&world, doc.as_ref(), &world.main, cursor, true)
|
||||
.unwrap_or_default();
|
||||
|
@ -170,7 +170,6 @@ fn is_in_rect(pos: Point, size: Size, click: Point) -> bool {
|
||||
mod tests {
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
use typst::eval::Tracer;
|
||||
use typst::layout::{Abs, Point, Position};
|
||||
|
||||
use super::{jump_from_click, jump_from_cursor, Jump};
|
||||
@ -200,14 +199,14 @@ mod tests {
|
||||
#[track_caller]
|
||||
fn test_click(text: &str, click: Point, expected: Option<Jump>) {
|
||||
let world = TestWorld::new(text);
|
||||
let doc = typst::compile(&world, &mut Tracer::new()).unwrap();
|
||||
let doc = typst::compile(&world).output.unwrap();
|
||||
assert_eq!(jump_from_click(&world, &doc, &doc.pages[0].frame, click), expected);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn test_cursor(text: &str, cursor: usize, expected: Option<Position>) {
|
||||
let world = TestWorld::new(text);
|
||||
let doc = typst::compile(&world, &mut Tracer::new()).unwrap();
|
||||
let doc = typst::compile(&world).output.unwrap();
|
||||
let pos = jump_from_cursor(&doc, &world.main, cursor);
|
||||
assert_eq!(pos.is_some(), expected.is_some());
|
||||
if let (Some(pos), Some(expected)) = (pos, expected) {
|
||||
|
@ -2,7 +2,8 @@ use std::fmt::Write;
|
||||
|
||||
use ecow::{eco_format, EcoString};
|
||||
use if_chain::if_chain;
|
||||
use typst::eval::{CapturesVisitor, Tracer};
|
||||
use typst::engine::Sink;
|
||||
use typst::eval::CapturesVisitor;
|
||||
use typst::foundations::{repr, Capturer, CastInfo, Repr, Value};
|
||||
use typst::layout::Length;
|
||||
use typst::model::Document;
|
||||
@ -79,7 +80,7 @@ fn expr_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option<Tooltip> {
|
||||
let mut last = None;
|
||||
let mut pieces: Vec<EcoString> = vec![];
|
||||
let mut iter = values.iter();
|
||||
for (value, _) in (&mut iter).take(Tracer::MAX_VALUES - 1) {
|
||||
for (value, _) in (&mut iter).take(Sink::MAX_VALUES - 1) {
|
||||
if let Some((prev, count)) = &mut last {
|
||||
if *prev == value {
|
||||
*count += 1;
|
||||
@ -253,7 +254,6 @@ fn font_tooltip(world: &dyn World, leaf: &LinkedNode) -> Option<Tooltip> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use typst::eval::Tracer;
|
||||
use typst::syntax::Side;
|
||||
|
||||
use super::{tooltip, Tooltip};
|
||||
@ -270,7 +270,7 @@ mod tests {
|
||||
#[track_caller]
|
||||
fn test(text: &str, cursor: usize, side: Side, expected: Option<Tooltip>) {
|
||||
let world = TestWorld::new(text);
|
||||
let doc = typst::compile(&world, &mut Tracer::new()).ok();
|
||||
let doc = typst::compile(&world).output.ok();
|
||||
assert_eq!(tooltip(&world, doc.as_ref(), &world.main, cursor, side), expected);
|
||||
}
|
||||
|
||||
|
@ -138,6 +138,15 @@ pub use {
|
||||
/// A result that can carry multiple source errors.
|
||||
pub type SourceResult<T> = Result<T, EcoVec<SourceDiagnostic>>;
|
||||
|
||||
/// An output alongside warnings generated while producing it.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Warned<T> {
|
||||
/// The produced output.
|
||||
pub output: T,
|
||||
/// Warnings generated while producing the output.
|
||||
pub warnings: EcoVec<SourceDiagnostic>,
|
||||
}
|
||||
|
||||
/// An error or warning in a source file.
|
||||
///
|
||||
/// The contained spans will only be detached if any of the input source files
|
||||
|
@ -1,13 +1,15 @@
|
||||
//! Definition of the central compilation context.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use comemo::{Track, Tracked, TrackedMut, Validate};
|
||||
use ecow::EcoVec;
|
||||
|
||||
use crate::diag::SourceResult;
|
||||
use crate::eval::Tracer;
|
||||
use crate::diag::{SourceDiagnostic, SourceResult};
|
||||
use crate::foundations::{Styles, Value};
|
||||
use crate::introspection::Introspector;
|
||||
use crate::syntax::FileId;
|
||||
use crate::syntax::{FileId, Span};
|
||||
use crate::World;
|
||||
|
||||
/// Holds all data needed during compilation.
|
||||
@ -16,18 +18,20 @@ pub struct Engine<'a> {
|
||||
pub world: Tracked<'a, dyn World + 'a>,
|
||||
/// Provides access to information about the document.
|
||||
pub introspector: Tracked<'a, Introspector>,
|
||||
/// May hold a span that is currently under inspection.
|
||||
pub traced: Tracked<'a, Traced>,
|
||||
/// A pure sink for warnings, delayed errors, and spans under inspection.
|
||||
pub sink: TrackedMut<'a, Sink>,
|
||||
/// The route the engine took during compilation. This is used to detect
|
||||
/// cyclic imports and excessive nesting.
|
||||
pub route: Route<'a>,
|
||||
/// The tracer for inspection of the values an expression produces.
|
||||
pub tracer: TrackedMut<'a, Tracer>,
|
||||
}
|
||||
|
||||
impl Engine<'_> {
|
||||
/// Performs 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
|
||||
pub fn delay<F, T>(&mut self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&mut Self) -> SourceResult<T>,
|
||||
T: Default,
|
||||
@ -35,13 +39,112 @@ impl Engine<'_> {
|
||||
match f(self) {
|
||||
Ok(value) => value,
|
||||
Err(errors) => {
|
||||
self.tracer.delay(errors);
|
||||
self.sink.delay(errors);
|
||||
T::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// May hold a span that is currently under inspection.
|
||||
#[derive(Default)]
|
||||
pub struct Traced(Option<Span>);
|
||||
|
||||
impl Traced {
|
||||
/// Wraps a to-be-traced `Span`.
|
||||
///
|
||||
/// Call `Traced::default()` to trace nothing.
|
||||
pub fn new(traced: Span) -> Self {
|
||||
Self(Some(traced))
|
||||
}
|
||||
}
|
||||
|
||||
#[comemo::track]
|
||||
impl Traced {
|
||||
/// Returns the traced span _if_ it is part of the given source file or
|
||||
/// `None` otherwise.
|
||||
///
|
||||
/// We hide the span if it isn't in the given file so that only results for
|
||||
/// the file with the traced span are invalidated.
|
||||
pub fn get(&self, id: FileId) -> Option<Span> {
|
||||
if self.0.and_then(Span::id) == Some(id) {
|
||||
self.0
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A push-only sink for delayed errors, warnings, and traced values.
|
||||
///
|
||||
/// All tracked methods of this type are of the form `(&mut self, ..) -> ()`, so
|
||||
/// in principle they do not need validation (though that optimization is not
|
||||
/// yet implemented in comemo).
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Sink {
|
||||
/// Delayed errors: Those are errors that we can ignore until the last
|
||||
/// iteration. For instance, show rules may throw during earlier iterations
|
||||
/// because the introspector is not yet ready. We first ignore that and
|
||||
/// proceed with empty content and only if the error remains by the end
|
||||
/// of the last iteration, we promote it.
|
||||
delayed: EcoVec<SourceDiagnostic>,
|
||||
/// Warnings emitted during iteration.
|
||||
warnings: EcoVec<SourceDiagnostic>,
|
||||
/// Hashes of all warning's spans and messages for warning deduplication.
|
||||
warnings_set: HashSet<u128>,
|
||||
/// A sequence of traced values for a span.
|
||||
values: EcoVec<(Value, Option<Styles>)>,
|
||||
}
|
||||
|
||||
impl Sink {
|
||||
/// The maximum number of traced values.
|
||||
pub const MAX_VALUES: usize = 10;
|
||||
|
||||
/// Create a new empty sink.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Get the stored delayed errors.
|
||||
pub fn delayed(&mut self) -> EcoVec<SourceDiagnostic> {
|
||||
std::mem::take(&mut self.delayed)
|
||||
}
|
||||
|
||||
/// Get the stored warnings.
|
||||
pub fn warnings(self) -> EcoVec<SourceDiagnostic> {
|
||||
self.warnings
|
||||
}
|
||||
|
||||
/// Get the values for the traced span.
|
||||
pub fn values(self) -> EcoVec<(Value, Option<Styles>)> {
|
||||
self.values
|
||||
}
|
||||
}
|
||||
|
||||
#[comemo::track]
|
||||
impl Sink {
|
||||
/// Push delayed errors.
|
||||
pub fn delay(&mut self, errors: EcoVec<SourceDiagnostic>) {
|
||||
self.delayed.extend(errors);
|
||||
}
|
||||
|
||||
/// Add a warning.
|
||||
pub fn warn(&mut self, warning: SourceDiagnostic) {
|
||||
// Check if warning is a duplicate.
|
||||
let hash = crate::utils::hash128(&(&warning.span, &warning.message));
|
||||
if self.warnings_set.insert(hash) {
|
||||
self.warnings.push(warning);
|
||||
}
|
||||
}
|
||||
|
||||
/// Trace a value and optionally styles for the traced span.
|
||||
pub fn value(&mut self, value: Value, styles: Option<Styles>) {
|
||||
if self.values.len() < Self::MAX_VALUES {
|
||||
self.values.push((value, styles));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The route the engine took during compilation. This is used to detect
|
||||
/// cyclic imports and excessive nesting.
|
||||
pub struct Route<'a> {
|
||||
|
@ -2,8 +2,8 @@ use comemo::{Tracked, TrackedMut};
|
||||
use ecow::{eco_format, 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::engine::{Engine, Sink, Traced};
|
||||
use crate::eval::{Access, Eval, FlowEvent, Route, Vm};
|
||||
use crate::foundations::{
|
||||
call_method_mut, is_mutating_method, Arg, Args, Bytes, Capturer, Closure, Content,
|
||||
Context, Func, IntoValue, NativeElement, Scope, Scopes, Value,
|
||||
@ -275,8 +275,9 @@ pub(crate) fn call_closure(
|
||||
closure: &LazyHash<Closure>,
|
||||
world: Tracked<dyn World + '_>,
|
||||
introspector: Tracked<Introspector>,
|
||||
traced: Tracked<Traced>,
|
||||
sink: TrackedMut<Sink>,
|
||||
route: Tracked<Route>,
|
||||
tracer: TrackedMut<Tracer>,
|
||||
context: Tracked<Context>,
|
||||
mut args: Args,
|
||||
) -> SourceResult<Value> {
|
||||
@ -294,8 +295,9 @@ pub(crate) fn call_closure(
|
||||
let engine = Engine {
|
||||
world,
|
||||
introspector,
|
||||
traced,
|
||||
sink,
|
||||
route: Route::extend(route),
|
||||
tracer,
|
||||
};
|
||||
|
||||
// Prepare VM.
|
||||
|
@ -35,7 +35,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.engine.tracer.warn(warning!(
|
||||
vm.engine.sink.warn(warning!(
|
||||
new_name.span(),
|
||||
"unnecessary import rename to same name",
|
||||
));
|
||||
@ -110,7 +110,7 @@ impl Eval for ast::ModuleImport<'_> {
|
||||
if renamed_item.original_name().as_str()
|
||||
== renamed_item.new_name().as_str()
|
||||
{
|
||||
vm.engine.tracer.warn(warning!(
|
||||
vm.engine.sink.warn(warning!(
|
||||
renamed_item.new_name().span(),
|
||||
"unnecessary import rename to same name",
|
||||
));
|
||||
@ -185,8 +185,9 @@ fn import_package(vm: &mut Vm, spec: PackageSpec, span: Span) -> SourceResult<Mo
|
||||
let point = || Tracepoint::Import;
|
||||
Ok(eval(
|
||||
vm.world(),
|
||||
vm.engine.traced,
|
||||
TrackedMut::reborrow_mut(&mut vm.engine.sink),
|
||||
vm.engine.route.track(),
|
||||
TrackedMut::reborrow_mut(&mut vm.engine.tracer),
|
||||
&source,
|
||||
)
|
||||
.trace(vm.world(), point, span)?
|
||||
@ -209,8 +210,9 @@ fn import_file(vm: &mut Vm, path: &str, span: Span) -> SourceResult<Module> {
|
||||
let point = || Tracepoint::Import;
|
||||
eval(
|
||||
world,
|
||||
vm.engine.traced,
|
||||
TrackedMut::reborrow_mut(&mut vm.engine.sink),
|
||||
vm.engine.route.track(),
|
||||
TrackedMut::reborrow_mut(&mut vm.engine.tracer),
|
||||
&source,
|
||||
)
|
||||
.trace(world, point, span)
|
||||
|
@ -134,7 +134,7 @@ impl Eval for ast::Strong<'_> {
|
||||
let body = self.body();
|
||||
if body.exprs().next().is_none() {
|
||||
vm.engine
|
||||
.tracer
|
||||
.sink
|
||||
.warn(warning!(
|
||||
self.span(), "no text within stars";
|
||||
hint: "using multiple consecutive stars (e.g. **) has no additional effect",
|
||||
@ -152,7 +152,7 @@ impl Eval for ast::Emph<'_> {
|
||||
let body = self.body();
|
||||
if body.exprs().next().is_none() {
|
||||
vm.engine
|
||||
.tracer
|
||||
.sink
|
||||
.warn(warning!(
|
||||
self.span(), "no text within underscores";
|
||||
hint: "using multiple consecutive underscores (e.g. __) has no additional effect"
|
||||
|
@ -11,12 +11,10 @@ mod import;
|
||||
mod markup;
|
||||
mod math;
|
||||
mod rules;
|
||||
mod tracer;
|
||||
mod vm;
|
||||
|
||||
pub use self::call::*;
|
||||
pub use self::import::*;
|
||||
pub use self::tracer::*;
|
||||
pub use self::vm::*;
|
||||
|
||||
pub(crate) use self::access::*;
|
||||
@ -26,7 +24,7 @@ pub(crate) use self::flow::*;
|
||||
use comemo::{Track, Tracked, TrackedMut};
|
||||
|
||||
use crate::diag::{bail, SourceResult};
|
||||
use crate::engine::{Engine, Route};
|
||||
use crate::engine::{Engine, Route, Sink, Traced};
|
||||
use crate::foundations::{Cast, Context, Module, NativeElement, Scope, Scopes, Value};
|
||||
use crate::introspection::Introspector;
|
||||
use crate::math::EquationElem;
|
||||
@ -38,8 +36,9 @@ use crate::World;
|
||||
#[typst_macros::time(name = "eval", span = source.root().span())]
|
||||
pub fn eval(
|
||||
world: Tracked<dyn World + '_>,
|
||||
traced: Tracked<Traced>,
|
||||
sink: TrackedMut<Sink>,
|
||||
route: Tracked<Route>,
|
||||
tracer: TrackedMut<Tracer>,
|
||||
source: &Source,
|
||||
) -> SourceResult<Module> {
|
||||
// Prevent cyclic evaluation.
|
||||
@ -52,9 +51,10 @@ pub fn eval(
|
||||
let introspector = Introspector::default();
|
||||
let engine = Engine {
|
||||
world,
|
||||
route: Route::extend(route).with_id(id),
|
||||
introspector: introspector.track(),
|
||||
tracer,
|
||||
traced,
|
||||
sink,
|
||||
route: Route::extend(route).with_id(id),
|
||||
};
|
||||
|
||||
// Prepare VM.
|
||||
@ -115,13 +115,15 @@ pub fn eval_string(
|
||||
}
|
||||
|
||||
// Prepare the engine.
|
||||
let mut tracer = Tracer::new();
|
||||
let mut sink = Sink::new();
|
||||
let introspector = Introspector::default();
|
||||
let traced = Traced::default();
|
||||
let engine = Engine {
|
||||
world,
|
||||
introspector: introspector.track(),
|
||||
traced: traced.track(),
|
||||
sink: sink.track_mut(),
|
||||
route: Route::default(),
|
||||
tracer: tracer.track_mut(),
|
||||
};
|
||||
|
||||
// Prepare VM.
|
||||
|
@ -1,82 +0,0 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use ecow::EcoVec;
|
||||
|
||||
use crate::diag::SourceDiagnostic;
|
||||
use crate::foundations::{Styles, Value};
|
||||
use crate::syntax::{FileId, Span};
|
||||
use crate::utils::hash128;
|
||||
|
||||
/// Traces warnings and which values existed for an expression at a span.
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Tracer {
|
||||
inspected: Option<Span>,
|
||||
warnings: EcoVec<SourceDiagnostic>,
|
||||
warnings_set: HashSet<u128>,
|
||||
delayed: EcoVec<SourceDiagnostic>,
|
||||
values: EcoVec<(Value, Option<Styles>)>,
|
||||
}
|
||||
|
||||
impl Tracer {
|
||||
/// The maximum number of inspected values.
|
||||
pub const MAX_VALUES: usize = 10;
|
||||
|
||||
/// Create a new tracer.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Get the stored delayed errors.
|
||||
pub fn delayed(&mut self) -> EcoVec<SourceDiagnostic> {
|
||||
std::mem::take(&mut self.delayed)
|
||||
}
|
||||
|
||||
/// Get the stored warnings.
|
||||
pub fn warnings(self) -> EcoVec<SourceDiagnostic> {
|
||||
self.warnings
|
||||
}
|
||||
|
||||
/// Mark a span as inspected. All values observed for this span can be
|
||||
/// retrieved via `values` later.
|
||||
pub fn inspect(&mut self, span: Span) {
|
||||
self.inspected = Some(span);
|
||||
}
|
||||
|
||||
/// Get the values for the inspected span.
|
||||
pub fn values(self) -> EcoVec<(Value, Option<Styles>)> {
|
||||
self.values
|
||||
}
|
||||
}
|
||||
|
||||
#[comemo::track]
|
||||
impl Tracer {
|
||||
/// Push delayed errors.
|
||||
pub fn delay(&mut self, errors: EcoVec<SourceDiagnostic>) {
|
||||
self.delayed.extend(errors);
|
||||
}
|
||||
|
||||
/// Add a warning.
|
||||
pub fn warn(&mut self, warning: SourceDiagnostic) {
|
||||
// Check if warning is a duplicate.
|
||||
let hash = hash128(&(&warning.span, &warning.message));
|
||||
if self.warnings_set.insert(hash) {
|
||||
self.warnings.push(warning);
|
||||
}
|
||||
}
|
||||
|
||||
/// The inspected span if it is part of the given source file.
|
||||
pub fn inspected(&self, id: FileId) -> Option<Span> {
|
||||
if self.inspected.and_then(Span::id) == Some(id) {
|
||||
self.inspected
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Trace a value for the span.
|
||||
pub fn value(&mut self, value: Value, styles: Option<Styles>) {
|
||||
if self.values.len() < Self::MAX_VALUES {
|
||||
self.values.push((value, styles));
|
||||
}
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ impl<'a> Vm<'a> {
|
||||
scopes: Scopes<'a>,
|
||||
target: Span,
|
||||
) -> Self {
|
||||
let inspected = target.id().and_then(|id| engine.tracer.inspected(id));
|
||||
let inspected = target.id().and_then(|id| engine.traced.get(id));
|
||||
Self { engine, context, flow: None, scopes, inspected }
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ impl<'a> Vm<'a> {
|
||||
#[cold]
|
||||
pub fn trace(&mut self, value: Value) {
|
||||
self.engine
|
||||
.tracer
|
||||
.sink
|
||||
.value(value.clone(), self.context.styles().ok().map(|s| s.to_map()));
|
||||
}
|
||||
}
|
||||
|
@ -297,8 +297,9 @@ impl Func {
|
||||
closure,
|
||||
engine.world,
|
||||
engine.introspector,
|
||||
engine.traced,
|
||||
TrackedMut::reborrow_mut(&mut engine.sink),
|
||||
engine.route.track(),
|
||||
TrackedMut::reborrow_mut(&mut engine.tracer),
|
||||
context,
|
||||
args,
|
||||
),
|
||||
|
@ -6,8 +6,7 @@ use ecow::{eco_format, eco_vec, EcoString, EcoVec};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::diag::{bail, At, HintedStrResult, SourceResult};
|
||||
use crate::engine::{Engine, Route};
|
||||
use crate::eval::Tracer;
|
||||
use crate::engine::{Engine, Route, Sink, Traced};
|
||||
use crate::foundations::{
|
||||
cast, elem, func, scope, select_where, ty, Args, Array, Construct, Content, Context,
|
||||
Element, Func, IntoValue, Label, LocatableSelector, NativeElement, Packed, Repr,
|
||||
@ -281,8 +280,9 @@ impl Counter {
|
||||
self.sequence_impl(
|
||||
engine.world,
|
||||
engine.introspector,
|
||||
engine.traced,
|
||||
TrackedMut::reborrow_mut(&mut engine.sink),
|
||||
engine.route.track(),
|
||||
TrackedMut::reborrow_mut(&mut engine.tracer),
|
||||
)
|
||||
}
|
||||
|
||||
@ -292,14 +292,16 @@ impl Counter {
|
||||
&self,
|
||||
world: Tracked<dyn World + '_>,
|
||||
introspector: Tracked<Introspector>,
|
||||
traced: Tracked<Traced>,
|
||||
sink: TrackedMut<Sink>,
|
||||
route: Tracked<Route>,
|
||||
tracer: TrackedMut<Tracer>,
|
||||
) -> SourceResult<EcoVec<(CounterState, NonZeroUsize)>> {
|
||||
let mut engine = Engine {
|
||||
world,
|
||||
introspector,
|
||||
traced,
|
||||
sink,
|
||||
route: Route::extend(route).unnested(),
|
||||
tracer,
|
||||
};
|
||||
|
||||
let mut state = CounterState::init(&self.0);
|
||||
|
@ -2,8 +2,7 @@ use comemo::{Track, Tracked, TrackedMut};
|
||||
use ecow::{eco_format, eco_vec, EcoString, EcoVec};
|
||||
|
||||
use crate::diag::{bail, At, SourceResult};
|
||||
use crate::engine::{Engine, Route};
|
||||
use crate::eval::Tracer;
|
||||
use crate::engine::{Engine, Route, Sink, Traced};
|
||||
use crate::foundations::{
|
||||
cast, elem, func, scope, select_where, ty, Args, Construct, Content, Context, Func,
|
||||
LocatableSelector, NativeElement, Packed, Repr, Selector, Show, Str, StyleChain,
|
||||
@ -214,8 +213,9 @@ impl State {
|
||||
self.sequence_impl(
|
||||
engine.world,
|
||||
engine.introspector,
|
||||
engine.traced,
|
||||
TrackedMut::reborrow_mut(&mut engine.sink),
|
||||
engine.route.track(),
|
||||
TrackedMut::reborrow_mut(&mut engine.tracer),
|
||||
)
|
||||
}
|
||||
|
||||
@ -225,14 +225,16 @@ impl State {
|
||||
&self,
|
||||
world: Tracked<dyn World + '_>,
|
||||
introspector: Tracked<Introspector>,
|
||||
traced: Tracked<Traced>,
|
||||
sink: TrackedMut<Sink>,
|
||||
route: Tracked<Route>,
|
||||
tracer: TrackedMut<Tracer>,
|
||||
) -> SourceResult<EcoVec<Value>> {
|
||||
let mut engine = Engine {
|
||||
world,
|
||||
introspector,
|
||||
traced,
|
||||
sink,
|
||||
route: Route::extend(route).unnested(),
|
||||
tracer,
|
||||
};
|
||||
let mut state = self.init.clone();
|
||||
let mut stops = eco_vec![state.clone()];
|
||||
|
@ -11,8 +11,7 @@ use self::shaping::{
|
||||
END_PUNCT_PAT,
|
||||
};
|
||||
use crate::diag::{bail, SourceResult};
|
||||
use crate::engine::{Engine, Route};
|
||||
use crate::eval::Tracer;
|
||||
use crate::engine::{Engine, Route, Sink, Traced};
|
||||
use crate::foundations::{Packed, Resolve, Smart, StyleChain};
|
||||
use crate::introspection::{Introspector, Locator, LocatorLink, Tag, TagElem};
|
||||
use crate::layout::{
|
||||
@ -45,8 +44,9 @@ pub(crate) fn layout_inline(
|
||||
children: &StyleVec,
|
||||
world: Tracked<dyn World + '_>,
|
||||
introspector: Tracked<Introspector>,
|
||||
traced: Tracked<Traced>,
|
||||
sink: TrackedMut<Sink>,
|
||||
route: Tracked<Route>,
|
||||
tracer: TrackedMut<Tracer>,
|
||||
locator: Tracked<Locator>,
|
||||
styles: StyleChain,
|
||||
consecutive: bool,
|
||||
@ -58,8 +58,9 @@ pub(crate) fn layout_inline(
|
||||
let mut engine = Engine {
|
||||
world,
|
||||
introspector,
|
||||
traced,
|
||||
sink,
|
||||
route: Route::extend(route),
|
||||
tracer,
|
||||
};
|
||||
|
||||
// Collect all text into one string for BiDi analysis.
|
||||
@ -83,8 +84,9 @@ pub(crate) fn layout_inline(
|
||||
children,
|
||||
engine.world,
|
||||
engine.introspector,
|
||||
engine.traced,
|
||||
TrackedMut::reborrow_mut(&mut engine.sink),
|
||||
engine.route.track(),
|
||||
TrackedMut::reborrow_mut(&mut engine.tracer),
|
||||
locator.track(),
|
||||
styles,
|
||||
consecutive,
|
||||
|
@ -72,8 +72,7 @@ pub(crate) use self::inline::*;
|
||||
use comemo::{Track, Tracked, TrackedMut};
|
||||
|
||||
use crate::diag::{bail, SourceResult};
|
||||
use crate::engine::{Engine, Route};
|
||||
use crate::eval::Tracer;
|
||||
use crate::engine::{Engine, Route, Sink, Traced};
|
||||
use crate::foundations::{category, Category, Content, Scope, StyleChain};
|
||||
use crate::introspection::{Introspector, Locator, LocatorLink};
|
||||
use crate::model::Document;
|
||||
@ -137,16 +136,18 @@ impl Content {
|
||||
content: &Content,
|
||||
world: Tracked<dyn World + '_>,
|
||||
introspector: Tracked<Introspector>,
|
||||
traced: Tracked<Traced>,
|
||||
sink: TrackedMut<Sink>,
|
||||
route: Tracked<Route>,
|
||||
tracer: TrackedMut<Tracer>,
|
||||
styles: StyleChain,
|
||||
) -> SourceResult<Document> {
|
||||
let mut locator = Locator::root().split();
|
||||
let mut engine = Engine {
|
||||
world,
|
||||
introspector,
|
||||
traced,
|
||||
sink,
|
||||
route: Route::extend(route).unnested(),
|
||||
tracer,
|
||||
};
|
||||
let arenas = Arenas::default();
|
||||
let (document, styles) =
|
||||
@ -158,8 +159,9 @@ impl Content {
|
||||
self,
|
||||
engine.world,
|
||||
engine.introspector,
|
||||
engine.traced,
|
||||
TrackedMut::reborrow_mut(&mut engine.sink),
|
||||
engine.route.track(),
|
||||
TrackedMut::reborrow_mut(&mut engine.tracer),
|
||||
styles,
|
||||
)
|
||||
}
|
||||
@ -178,8 +180,9 @@ impl Content {
|
||||
content: &Content,
|
||||
world: Tracked<dyn World + '_>,
|
||||
introspector: Tracked<Introspector>,
|
||||
traced: Tracked<Traced>,
|
||||
sink: TrackedMut<Sink>,
|
||||
route: Tracked<Route>,
|
||||
tracer: TrackedMut<Tracer>,
|
||||
locator: Tracked<Locator>,
|
||||
styles: StyleChain,
|
||||
regions: Regions,
|
||||
@ -189,8 +192,9 @@ impl Content {
|
||||
let mut engine = Engine {
|
||||
world,
|
||||
introspector,
|
||||
traced,
|
||||
sink,
|
||||
route: Route::extend(route),
|
||||
tracer,
|
||||
};
|
||||
|
||||
if !engine.route.within(Route::MAX_LAYOUT_DEPTH) {
|
||||
@ -218,8 +222,9 @@ impl Content {
|
||||
self,
|
||||
engine.world,
|
||||
engine.introspector,
|
||||
engine.traced,
|
||||
TrackedMut::reborrow_mut(&mut engine.sink),
|
||||
engine.route.track(),
|
||||
TrackedMut::reborrow_mut(&mut engine.tracer),
|
||||
locator.track(),
|
||||
styles,
|
||||
regions,
|
||||
|
@ -63,11 +63,10 @@ use comemo::{Track, Tracked, Validate};
|
||||
use ecow::{EcoString, EcoVec};
|
||||
use typst_timing::{timed, TimingScope};
|
||||
|
||||
use crate::diag::{warning, FileResult, SourceDiagnostic, SourceResult};
|
||||
use crate::engine::{Engine, Route};
|
||||
use crate::eval::Tracer;
|
||||
use crate::diag::{warning, FileResult, SourceDiagnostic, SourceResult, Warned};
|
||||
use crate::engine::{Engine, Route, Sink, Traced};
|
||||
use crate::foundations::{
|
||||
Array, Bytes, Content, Datetime, Dict, Module, Scope, StyleChain, Styles, Value,
|
||||
Array, Bytes, Datetime, Dict, Module, Scope, StyleChain, Styles, Value,
|
||||
};
|
||||
use crate::introspection::Introspector;
|
||||
use crate::layout::{Alignment, Dir};
|
||||
@ -78,62 +77,68 @@ use crate::text::{Font, FontBook};
|
||||
use crate::utils::LazyHash;
|
||||
use crate::visualize::Color;
|
||||
|
||||
/// Compile a source file into a fully layouted document.
|
||||
/// Compile sources into a fully layouted document.
|
||||
///
|
||||
/// - Returns `Ok(document)` if there were no fatal errors.
|
||||
/// - Returns `Err(errors)` if there were fatal errors.
|
||||
///
|
||||
/// Requires a mutable reference to a tracer. Such a tracer can be created with
|
||||
/// `Tracer::new()`. Independently of whether compilation succeeded, calling
|
||||
/// `tracer.warnings()` after compilation will return all compiler warnings.
|
||||
#[typst_macros::time(name = "compile")]
|
||||
pub fn compile(world: &dyn World, tracer: &mut Tracer) -> SourceResult<Document> {
|
||||
// Call `track` on the world just once to keep comemo's ID stable.
|
||||
let world = world.track();
|
||||
#[typst_macros::time]
|
||||
pub fn compile(world: &dyn World) -> Warned<SourceResult<Document>> {
|
||||
let mut sink = Sink::new();
|
||||
let output = compile_inner(world.track(), Traced::default().track(), &mut sink)
|
||||
.map_err(deduplicate);
|
||||
Warned { output, warnings: sink.warnings() }
|
||||
}
|
||||
|
||||
// Try to evaluate the source file into a module.
|
||||
let module = crate::eval::eval(
|
||||
world,
|
||||
Route::default().track(),
|
||||
tracer.track_mut(),
|
||||
&world.main(),
|
||||
)
|
||||
.map_err(deduplicate)?;
|
||||
|
||||
// Typeset the module's content, relayouting until convergence.
|
||||
typeset(world, tracer, &module.content()).map_err(deduplicate)
|
||||
/// Compiles sources and returns all values and styles observed at the given
|
||||
/// `span` during compilation.
|
||||
#[typst_macros::time]
|
||||
pub fn trace(world: &dyn World, span: Span) -> EcoVec<(Value, Option<Styles>)> {
|
||||
let mut sink = Sink::new();
|
||||
let traced = Traced::new(span);
|
||||
compile_inner(world.track(), traced.track(), &mut sink).ok();
|
||||
sink.values()
|
||||
}
|
||||
|
||||
/// Relayout until introspection converges.
|
||||
fn typeset(
|
||||
fn compile_inner(
|
||||
world: Tracked<dyn World + '_>,
|
||||
tracer: &mut Tracer,
|
||||
content: &Content,
|
||||
traced: Tracked<Traced>,
|
||||
sink: &mut Sink,
|
||||
) -> SourceResult<Document> {
|
||||
// The name of the iterations for timing scopes.
|
||||
const ITER_NAMES: &[&str] =
|
||||
&["typeset (1)", "typeset (2)", "typeset (3)", "typeset (4)", "typeset (5)"];
|
||||
|
||||
let library = world.library();
|
||||
let styles = StyleChain::new(&library.styles);
|
||||
|
||||
// First evaluate the main source file into a module.
|
||||
let content = crate::eval::eval(
|
||||
world,
|
||||
traced,
|
||||
sink.track_mut(),
|
||||
Route::default().track(),
|
||||
&world.main(),
|
||||
)?
|
||||
.content();
|
||||
|
||||
let mut iter = 0;
|
||||
let mut document = Document::default();
|
||||
|
||||
// Relayout until all introspections stabilize.
|
||||
// If that doesn't happen within five attempts, we give up.
|
||||
loop {
|
||||
// The name of the iterations for timing scopes.
|
||||
const ITER_NAMES: &[&str] =
|
||||
&["layout (1)", "layout (2)", "layout (3)", "layout (4)", "layout (5)"];
|
||||
let _scope = TimingScope::new(ITER_NAMES[iter], None);
|
||||
|
||||
// Clear delayed errors.
|
||||
tracer.delayed();
|
||||
sink.delayed();
|
||||
|
||||
let constraint = <Introspector as Validate>::Constraint::new();
|
||||
let mut engine = Engine {
|
||||
world,
|
||||
route: Route::default(),
|
||||
tracer: tracer.track_mut(),
|
||||
introspector: document.introspector.track_with(&constraint),
|
||||
traced,
|
||||
sink: sink.track_mut(),
|
||||
route: Route::default(),
|
||||
};
|
||||
|
||||
// Layout!
|
||||
@ -146,7 +151,7 @@ fn typeset(
|
||||
}
|
||||
|
||||
if iter >= 5 {
|
||||
tracer.warn(warning!(
|
||||
sink.warn(warning!(
|
||||
Span::detached(), "layout did not converge within 5 attempts";
|
||||
hint: "check if any states or queries are updating themselves"
|
||||
));
|
||||
@ -155,7 +160,7 @@ fn typeset(
|
||||
}
|
||||
|
||||
// Promote delayed errors.
|
||||
let delayed = tracer.delayed();
|
||||
let delayed = sink.delayed();
|
||||
if !delayed.is_empty() {
|
||||
return Err(delayed);
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ pub fn process(
|
||||
//
|
||||
// This way, we can ignore errors that only occur in earlier
|
||||
// iterations and also show more useful errors at once.
|
||||
engine.delayed(|engine| show(engine, target, step, styles.chain(&map)))
|
||||
engine.delay(|engine| show(engine, target, step, styles.chain(&map)))
|
||||
}
|
||||
None => target,
|
||||
};
|
||||
|
@ -132,7 +132,7 @@ pub struct TextElem {
|
||||
let book = engine.world.book();
|
||||
for family in &font_list.v {
|
||||
if !book.contains_family(family.as_str()) {
|
||||
engine.tracer.warn(warning!(
|
||||
engine.sink.warn(warning!(
|
||||
font_list.span,
|
||||
"unknown font family: {}",
|
||||
family.as_str(),
|
||||
|
@ -7,7 +7,6 @@ use pulldown_cmark as md;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typed_arena::Arena;
|
||||
use typst::diag::{FileResult, StrResult};
|
||||
use typst::eval::Tracer;
|
||||
use typst::foundations::{Bytes, Datetime};
|
||||
use typst::layout::{Abs, Point, Size};
|
||||
use typst::syntax::{FileId, Source, VirtualPath};
|
||||
@ -411,8 +410,7 @@ fn code_block(resolver: &dyn Resolver, lang: &str, text: &str) -> Html {
|
||||
let source = Source::new(id, compile);
|
||||
let world = DocWorld(source);
|
||||
|
||||
let mut tracer = Tracer::new();
|
||||
let mut document = match typst::compile(&world, &mut tracer) {
|
||||
let mut document = match typst::compile(&world).output {
|
||||
Ok(doc) => doc,
|
||||
Err(err) => {
|
||||
let msg = &err[0].message;
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use typst::diag::{FileError, FileResult};
|
||||
use typst::eval::Tracer;
|
||||
use typst::foundations::{Bytes, Datetime};
|
||||
use typst::syntax::{FileId, Source};
|
||||
use typst::text::{Font, FontBook};
|
||||
@ -63,8 +62,7 @@ impl World for FuzzWorld {
|
||||
|
||||
fuzz_target!(|text: &str| {
|
||||
let world = FuzzWorld::new(text);
|
||||
let mut tracer = Tracer::new();
|
||||
if let Ok(document) = typst::compile(&world, &mut tracer) {
|
||||
if let Ok(document) = typst::compile(&world).output {
|
||||
if let Some(page) = document.pages.first() {
|
||||
std::hint::black_box(typst_render::render(&page.frame, 1.0, Color::WHITE));
|
||||
}
|
||||
|
@ -4,8 +4,7 @@ use std::path::Path;
|
||||
|
||||
use ecow::eco_vec;
|
||||
use tiny_skia as sk;
|
||||
use typst::diag::SourceDiagnostic;
|
||||
use typst::eval::Tracer;
|
||||
use typst::diag::{SourceDiagnostic, Warned};
|
||||
use typst::foundations::Smart;
|
||||
use typst::layout::{Abs, Frame, FrameItem, Page, Transform};
|
||||
use typst::model::Document;
|
||||
@ -80,13 +79,12 @@ impl<'a> Runner<'a> {
|
||||
log!(into: self.result.infos, "tree: {:#?}", self.test.source.root());
|
||||
}
|
||||
|
||||
let mut tracer = Tracer::new();
|
||||
let (doc, errors) = match typst::compile(&self.world, &mut tracer) {
|
||||
let Warned { output, warnings } = typst::compile(&self.world);
|
||||
let (doc, errors) = match output {
|
||||
Ok(doc) => (Some(doc), eco_vec![]),
|
||||
Err(errors) => (None, errors),
|
||||
};
|
||||
|
||||
let warnings = tracer.warnings();
|
||||
if doc.is_none() && errors.is_empty() {
|
||||
log!(self, "no document, but also no errors");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user