diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs index d934c458e..41490eb8f 100644 --- a/library/src/layout/mod.rs +++ b/library/src/layout/mod.rs @@ -50,6 +50,7 @@ use std::mem; use typed_arena::Arena; use typst::diag::SourceResult; use typst::eval::Tracer; +use typst::model::DelayedErrors; use typst::model::{applicable, realize, StyleVecBuilder}; use crate::math::{EquationElem, LayoutMath}; @@ -116,13 +117,20 @@ impl LayoutRoot for Content { fn cached( content: &Content, world: Tracked, - tracer: TrackedMut, - locator: Tracked, introspector: Tracked, + locator: Tracked, + delayed: TrackedMut, + tracer: TrackedMut, styles: StyleChain, ) -> SourceResult { let mut locator = Locator::chained(locator); - let mut vt = Vt { world, tracer, locator: &mut locator, introspector }; + let mut vt = Vt { + world, + introspector, + locator: &mut locator, + delayed, + tracer, + }; let scratch = Scratch::default(); let (realized, styles) = realize_root(&mut vt, &scratch, content, styles)?; realized @@ -132,13 +140,13 @@ impl LayoutRoot for Content { } tracing::info!("Starting layout"); - cached( self, vt.world, - TrackedMut::reborrow_mut(&mut vt.tracer), - vt.locator.track(), vt.introspector, + vt.locator.track(), + TrackedMut::reborrow_mut(&mut vt.delayed), + TrackedMut::reborrow_mut(&mut vt.tracer), styles, ) } @@ -168,9 +176,10 @@ pub trait Layout { let mut locator = Locator::chained(vt.locator.track()); let mut vt = Vt { world: vt.world, - tracer: TrackedMut::reborrow_mut(&mut vt.tracer), - locator: &mut locator, introspector: vt.introspector, + locator: &mut locator, + tracer: TrackedMut::reborrow_mut(&mut vt.tracer), + delayed: TrackedMut::reborrow_mut(&mut vt.delayed), }; self.layout(&mut vt, styles, regions) } @@ -184,18 +193,26 @@ impl Layout for Content { styles: StyleChain, regions: Regions, ) -> SourceResult { + #[allow(clippy::too_many_arguments)] #[comemo::memoize] fn cached( content: &Content, world: Tracked, - tracer: TrackedMut, - locator: Tracked, introspector: Tracked, + locator: Tracked, + delayed: TrackedMut, + tracer: TrackedMut, styles: StyleChain, regions: Regions, ) -> SourceResult { let mut locator = Locator::chained(locator); - let mut vt = Vt { world, tracer, locator: &mut locator, introspector }; + let mut vt = Vt { + world, + introspector, + locator: &mut locator, + delayed, + tracer, + }; let scratch = Scratch::default(); let (realized, styles) = realize_block(&mut vt, &scratch, content, styles)?; realized @@ -209,9 +226,10 @@ impl Layout for Content { let fragment = cached( self, vt.world, - TrackedMut::reborrow_mut(&mut vt.tracer), - vt.locator.track(), vt.introspector, + vt.locator.track(), + TrackedMut::reborrow_mut(&mut vt.delayed), + TrackedMut::reborrow_mut(&mut vt.tracer), styles, regions, )?; diff --git a/library/src/layout/par.rs b/library/src/layout/par.rs index fa53f0317..2d0944ceb 100644 --- a/library/src/layout/par.rs +++ b/library/src/layout/par.rs @@ -5,6 +5,7 @@ use icu_provider_blob::BlobDataProvider; use icu_segmenter::{LineBreakIteratorUtf8, LineSegmenter}; use once_cell::sync::Lazy; use typst::eval::Tracer; +use typst::model::DelayedErrors; use unicode_bidi::{BidiInfo, Level as BidiLevel}; use unicode_script::{Script, UnicodeScript}; @@ -148,16 +149,23 @@ impl ParElem { fn cached( par: &ParElem, world: Tracked, - tracer: TrackedMut, - locator: Tracked, introspector: Tracked, + locator: Tracked, + delayed: TrackedMut, + tracer: TrackedMut, styles: StyleChain, consecutive: bool, region: Size, expand: bool, ) -> SourceResult { let mut locator = Locator::chained(locator); - let mut vt = Vt { world, tracer, locator: &mut locator, introspector }; + let mut vt = Vt { + world, + introspector, + locator: &mut locator, + delayed, + tracer, + }; let children = par.children(); // Collect all text into one string for BiDi analysis. @@ -178,9 +186,10 @@ impl ParElem { let fragment = cached( self, vt.world, - TrackedMut::reborrow_mut(&mut vt.tracer), - vt.locator.track(), vt.introspector, + vt.locator.track(), + TrackedMut::reborrow_mut(&mut vt.delayed), + TrackedMut::reborrow_mut(&mut vt.tracer), styles, consecutive, region, diff --git a/library/src/meta/bibliography.rs b/library/src/meta/bibliography.rs index 737530b8b..f020f730a 100644 --- a/library/src/meta/bibliography.rs +++ b/library/src/meta/bibliography.rs @@ -96,11 +96,11 @@ impl BibliographyElem { pub fn find(introspector: Tracked) -> StrResult { let mut iter = introspector.query(&Self::func().select()).into_iter(); let Some(elem) = iter.next() else { - return Err("the document does not contain a bibliography".into()); + bail!("the document does not contain a bibliography"); }; if iter.next().is_some() { - Err("multiple bibliographies are not supported")?; + bail!("multiple bibliographies are not supported"); } Ok(elem.to::().unwrap().clone()) @@ -162,42 +162,40 @@ impl Show for BibliographyElem { seq.push(HeadingElem::new(title).with_level(NonZeroUsize::ONE).pack()); } - if !vt.introspector.init() { - return Ok(Content::sequence(seq)); - } + Ok(vt.delayed(|vt| { + let works = Works::new(vt).at(self.span())?; - let works = Works::new(vt).at(self.span())?; + let row_gutter = BlockElem::below_in(styles).amount(); + if works.references.iter().any(|(prefix, _)| prefix.is_some()) { + let mut cells = vec![]; + for (prefix, reference) in &works.references { + cells.push(prefix.clone().unwrap_or_default()); + cells.push(reference.clone()); + } - let row_gutter = BlockElem::below_in(styles).amount(); - if works.references.iter().any(|(prefix, _)| prefix.is_some()) { - let mut cells = vec![]; - for (prefix, reference) in &works.references { - cells.push(prefix.clone().unwrap_or_default()); - cells.push(reference.clone()); + seq.push(VElem::new(row_gutter).with_weakness(3).pack()); + seq.push( + GridElem::new(cells) + .with_columns(TrackSizings(vec![Sizing::Auto; 2])) + .with_column_gutter(TrackSizings(vec![COLUMN_GUTTER.into()])) + .with_row_gutter(TrackSizings(vec![row_gutter.into()])) + .pack(), + ); + } else { + let mut entries = vec![]; + for (_, reference) in &works.references { + entries.push(VElem::new(row_gutter).with_weakness(3).pack()); + entries.push(reference.clone()); + } + + seq.push( + Content::sequence(entries) + .styled(ParElem::set_hanging_indent(INDENT.into())), + ); } - seq.push(VElem::new(row_gutter).with_weakness(3).pack()); - seq.push( - GridElem::new(cells) - .with_columns(TrackSizings(vec![Sizing::Auto; 2])) - .with_column_gutter(TrackSizings(vec![COLUMN_GUTTER.into()])) - .with_row_gutter(TrackSizings(vec![row_gutter.into()])) - .pack(), - ); - } else { - let mut entries = vec![]; - for (_, reference) in &works.references { - entries.push(VElem::new(row_gutter).with_weakness(3).pack()); - entries.push(reference.clone()); - } - - seq.push( - Content::sequence(entries) - .styled(ParElem::set_hanging_indent(INDENT.into())), - ); - } - - Ok(Content::sequence(seq)) + Ok(Content::sequence(seq)) + })) } } @@ -357,19 +355,17 @@ impl Synthesize for CiteElem { impl Show for CiteElem { #[tracing::instrument(name = "CiteElem::show", skip(self, vt))] fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult { - if !vt.introspector.init() { - return Ok(Content::empty()); - } - - let works = Works::new(vt).at(self.span())?; - let location = self.0.location().unwrap(); - works - .citations - .get(&location) - .cloned() - .flatten() - .ok_or("bibliography does not contain this key") - .at(self.span()) + Ok(vt.delayed(|vt| { + let works = Works::new(vt).at(self.span())?; + let location = self.0.location().unwrap(); + works + .citations + .get(&location) + .cloned() + .flatten() + .ok_or("bibliography does not contain this key") + .at(self.span()) + })) } } diff --git a/library/src/meta/context.rs b/library/src/meta/context.rs index d599c63e3..2ed359ee4 100644 --- a/library/src/meta/context.rs +++ b/library/src/meta/context.rs @@ -73,12 +73,10 @@ struct LocateElem { impl Show for LocateElem { #[tracing::instrument(name = "LocateElem::show", skip(self, vt))] fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult { - if !vt.introspector.init() { - return Ok(Content::empty()); - } - - let location = self.0.location().unwrap(); - Ok(self.func().call_vt(vt, [location])?.display()) + Ok(vt.delayed(|vt| { + let location = self.0.location().unwrap(); + Ok(self.func().call_vt(vt, [location])?.display()) + })) } } diff --git a/library/src/meta/counter.rs b/library/src/meta/counter.rs index ef4646ab2..65522c403 100644 --- a/library/src/meta/counter.rs +++ b/library/src/meta/counter.rs @@ -4,6 +4,7 @@ use std::str::FromStr; use ecow::{eco_vec, EcoVec}; use smallvec::{smallvec, SmallVec}; use typst::eval::Tracer; +use typst::model::DelayedErrors; use super::{FigureElem, HeadingElem, Numbering, NumberingPattern}; use crate::layout::PageElem; @@ -397,9 +398,10 @@ impl Counter { ) -> SourceResult> { self.sequence_impl( vt.world, - TrackedMut::reborrow_mut(&mut vt.tracer), - vt.locator.track(), vt.introspector, + vt.locator.track(), + TrackedMut::reborrow_mut(&mut vt.delayed), + TrackedMut::reborrow_mut(&mut vt.tracer), ) } @@ -408,12 +410,19 @@ impl Counter { fn sequence_impl( &self, world: Tracked, - tracer: TrackedMut, - locator: Tracked, introspector: Tracked, + locator: Tracked, + delayed: TrackedMut, + tracer: TrackedMut, ) -> SourceResult> { let mut locator = Locator::chained(locator); - let mut vt = Vt { world, tracer, locator: &mut locator, introspector }; + let mut vt = Vt { + world, + introspector, + locator: &mut locator, + delayed, + tracer, + }; let mut state = CounterState(match &self.0 { // special case, because pages always start at one. CounterKey::Page => smallvec![1], @@ -618,37 +627,36 @@ struct DisplayElem { impl Show for DisplayElem { #[tracing::instrument(name = "DisplayElem::show", skip_all)] fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult { - if !vt.introspector.init() { - return Ok(Content::empty()); - } + Ok(vt.delayed(|vt| { + let location = self.0.location().unwrap(); + let counter = self.counter(); + let numbering = self + .numbering() + .or_else(|| { + let CounterKey::Selector(Selector::Elem(func, _)) = counter.0 else { + return None; + }; - let location = self.0.location().unwrap(); - let counter = self.counter(); - let numbering = self - .numbering() - .or_else(|| { - let CounterKey::Selector(Selector::Elem(func, _)) = counter.0 else { - return None; + if func == HeadingElem::func() { + HeadingElem::numbering_in(styles) + } else if func == FigureElem::func() { + FigureElem::numbering_in(styles) + } else if func == EquationElem::func() { + EquationElem::numbering_in(styles) + } else { + None + } + }) + .unwrap_or_else(|| NumberingPattern::from_str("1.1").unwrap().into()); + + let state = if self.both() { + counter.both(vt, location)? + } else { + counter.at(vt, location)? }; - if func == HeadingElem::func() { - HeadingElem::numbering_in(styles) - } else if func == FigureElem::func() { - FigureElem::numbering_in(styles) - } else if func == EquationElem::func() { - EquationElem::numbering_in(styles) - } else { - None - } - }) - .unwrap_or_else(|| NumberingPattern::from_str("1.1").unwrap().into()); - - let state = if self.both() { - counter.both(vt, location)? - } else { - counter.at(vt, location)? - }; - state.display(vt, &numbering) + state.display(vt, &numbering) + })) } } diff --git a/library/src/meta/link.rs b/library/src/meta/link.rs index 43f6a34d6..c3d8718c9 100644 --- a/library/src/meta/link.rs +++ b/library/src/meta/link.rs @@ -91,21 +91,18 @@ impl Show for LinkElem { #[tracing::instrument(name = "LinkElem::show", skip(self, vt))] fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult { let body = self.body(); - let dest = match self.dest() { - LinkTarget::Dest(dest) => dest, - LinkTarget::Label(label) => { - if !vt.introspector.init() { - return Ok(body); - } - - let elem = vt.introspector.query_label(&label).at(self.span())?; - Destination::Location(elem.location().unwrap()) - } + let linked = match self.dest() { + LinkTarget::Dest(dest) => body.linked(dest), + LinkTarget::Label(label) => vt + .delayed(|vt| { + let elem = vt.introspector.query_label(&label).at(self.span())?; + let dest = Destination::Location(elem.location().unwrap()); + Ok(Some(body.clone().linked(dest))) + }) + .unwrap_or(body), }; - Ok(body - .linked(dest) - .styled(TextElem::set_hyphenate(Hyphenate(Smart::Custom(false))))) + Ok(linked.styled(TextElem::set_hyphenate(Hyphenate(Smart::Custom(false))))) } } diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs index 5a295e335..96358ffa8 100644 --- a/library/src/meta/reference.rs +++ b/library/src/meta/reference.rs @@ -132,7 +132,7 @@ impl Synthesize for RefElem { self.push_element(None); let target = self.target(); - if vt.introspector.init() && !BibliographyElem::has(vt, &target.0) { + if !BibliographyElem::has(vt, &target.0) { if let Ok(elem) = vt.introspector.query_label(&target) { self.push_element(Some(elem.into_inner())); return Ok(()); @@ -146,63 +146,65 @@ impl Synthesize for RefElem { impl Show for RefElem { #[tracing::instrument(name = "RefElem::show", skip_all)] fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult { - if !vt.introspector.init() { - return Ok(Content::empty()); - } + Ok(vt.delayed(|vt| { + let target = self.target(); + let elem = vt.introspector.query_label(&self.target()); + let span = self.span(); - let target = self.target(); - let elem = vt.introspector.query_label(&self.target()); - let span = self.span(); + if BibliographyElem::has(vt, &target.0) { + if elem.is_ok() { + bail!(span, "label occurs in the document and its bibliography"); + } - if BibliographyElem::has(vt, &target.0) { - if elem.is_ok() { - bail!(span, "label occurs in the document and its bibliography"); + return Ok(self.to_citation(vt, styles)?.pack().spanned(span)); } - return Ok(self.to_citation(vt, styles)?.pack().spanned(span)); - } + let elem = elem.at(span)?; + let refable = elem + .with::() + .ok_or_else(|| { + if elem.can::() { + eco_format!( + "cannot reference {} directly, try putting it into a figure", + elem.func().name() + ) + } else { + eco_format!("cannot reference {}", elem.func().name()) + } + }) + .at(span)?; - let elem = elem.at(span)?; - let refable = elem - .with::() - .ok_or_else(|| { - if elem.can::() { + let numbering = refable + .numbering() + .ok_or_else(|| { eco_format!( - "cannot reference {} directly, try putting it into a figure", + "cannot reference {0} without numbering \ + - did you mean to use `#set {0}(numbering: \"1.\")`?", elem.func().name() ) - } else { - eco_format!("cannot reference {}", elem.func().name()) + }) + .at(span)?; + + let numbers = refable + .counter() + .at(vt, elem.location().unwrap())? + .display(vt, &numbering.trimmed())?; + + let supplement = match self.supplement(styles) { + Smart::Auto => refable.supplement(), + Smart::Custom(None) => Content::empty(), + Smart::Custom(Some(supplement)) => { + supplement.resolve(vt, [(*elem).clone()])? } - }) - .at(span)?; + }; - let numbering = refable - .numbering() - .ok_or_else(|| { - eco_format!("cannot reference {0} without numbering - did you mean to use `#set {0}(numbering: \"1.\")`?", elem.func().name()) - }) - .at(span)?; - - let numbers = refable - .counter() - .at(vt, elem.location().unwrap())? - .display(vt, &numbering.trimmed())?; - - let supplement = match self.supplement(styles) { - Smart::Auto => refable.supplement(), - Smart::Custom(None) => Content::empty(), - Smart::Custom(Some(supplement)) => { - supplement.resolve(vt, [(*elem).clone()])? + let mut content = numbers; + if !supplement.is_empty() { + content = supplement + TextElem::packed("\u{a0}") + content; } - }; - let mut content = numbers; - if !supplement.is_empty() { - content = supplement + TextElem::packed("\u{a0}") + content; - } - - Ok(content.linked(Destination::Location(elem.location().unwrap()))) + Ok(content.linked(Destination::Location(elem.location().unwrap()))) + })) } } diff --git a/library/src/meta/state.rs b/library/src/meta/state.rs index 231852e30..a8e37901b 100644 --- a/library/src/meta/state.rs +++ b/library/src/meta/state.rs @@ -2,6 +2,7 @@ use std::fmt::{self, Debug, Formatter, Write}; use ecow::{eco_vec, EcoVec}; use typst::eval::Tracer; +use typst::model::DelayedErrors; use crate::prelude::*; @@ -306,9 +307,10 @@ impl State { fn sequence(&self, vt: &mut Vt) -> SourceResult> { self.sequence_impl( vt.world, - TrackedMut::reborrow_mut(&mut vt.tracer), - vt.locator.track(), vt.introspector, + vt.locator.track(), + TrackedMut::reborrow_mut(&mut vt.delayed), + TrackedMut::reborrow_mut(&mut vt.tracer), ) } @@ -317,12 +319,19 @@ impl State { fn sequence_impl( &self, world: Tracked, - tracer: TrackedMut, - locator: Tracked, introspector: Tracked, + locator: Tracked, + delayed: TrackedMut, + tracer: TrackedMut, ) -> SourceResult> { let mut locator = Locator::chained(locator); - let mut vt = Vt { world, tracer, locator: &mut locator, introspector }; + let mut vt = Vt { + world, + introspector, + locator: &mut locator, + delayed, + tracer, + }; let mut state = self.init.clone(); let mut stops = eco_vec![state.clone()]; @@ -397,16 +406,14 @@ struct DisplayElem { impl Show for DisplayElem { #[tracing::instrument(name = "DisplayElem::show", skip(self, vt))] fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult { - if !vt.introspector.init() { - return Ok(Content::empty()); - } - - let location = self.0.location().unwrap(); - let value = self.state().at(vt, location)?; - Ok(match self.func() { - Some(func) => func.call_vt(vt, [value])?.display(), - None => value.display(), - }) + Ok(vt.delayed(|vt| { + let location = self.0.location().unwrap(); + let value = self.state().at(vt, location)?; + Ok(match self.func() { + Some(func) => func.call_vt(vt, [value])?.display(), + None => value.display(), + }) + })) } } diff --git a/src/eval/func.rs b/src/eval/func.rs index 1745dfb1a..28d4a40df 100644 --- a/src/eval/func.rs +++ b/src/eval/func.rs @@ -11,7 +11,7 @@ use super::{ Value, Vm, }; use crate::diag::{bail, SourceResult, StrResult}; -use crate::model::{ElemFunc, Introspector, Locator, Vt}; +use crate::model::{DelayedErrors, ElemFunc, Introspector, Locator, Vt}; use crate::syntax::ast::{self, AstNode, Expr, Ident}; use crate::syntax::{SourceId, Span, SyntaxNode}; use crate::World; @@ -102,9 +102,10 @@ impl Func { self, vm.world(), route, - TrackedMut::reborrow_mut(&mut vm.vt.tracer), - vm.vt.locator.track(), vm.vt.introspector, + vm.vt.locator.track(), + TrackedMut::reborrow_mut(&mut vm.vt.delayed), + TrackedMut::reborrow_mut(&mut vm.vt.tracer), vm.depth + 1, args, ) @@ -129,9 +130,10 @@ impl Func { let mut locator = Locator::chained(vt.locator.track()); let vt = Vt { world: vt.world, - tracer: TrackedMut::reborrow_mut(&mut vt.tracer), - locator: &mut locator, introspector: vt.introspector, + locator: &mut locator, + delayed: TrackedMut::reborrow_mut(&mut vt.delayed), + tracer: TrackedMut::reborrow_mut(&mut vt.tracer), }; let mut vm = Vm::new(vt, route.track(), id, scopes); let args = Args::new(self.span(), args); @@ -326,9 +328,10 @@ impl Closure { this: &Func, world: Tracked, route: Tracked, - tracer: TrackedMut, - locator: Tracked, introspector: Tracked, + locator: Tracked, + delayed: TrackedMut, + tracer: TrackedMut, depth: usize, mut args: Args, ) -> SourceResult { @@ -344,7 +347,13 @@ impl Closure { // Prepare VT. let mut locator = Locator::chained(locator); - let vt = Vt { world, tracer, locator: &mut locator, introspector }; + let vt = Vt { + world, + introspector, + locator: &mut locator, + delayed, + tracer, + }; // Prepare VM. let mut vm = Vm::new(vt, route, closure.location, scopes); diff --git a/src/eval/mod.rs b/src/eval/mod.rs index e60162186..fe11606ae 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -62,9 +62,6 @@ use ecow::{EcoString, EcoVec}; use unicode_segmentation::UnicodeSegmentation; use self::func::{CapturesVisitor, Closure}; -use crate::diag::{ - bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint, -}; use crate::model::{ Content, Introspector, Label, Locator, Recipe, ShowableSelector, Styles, Transform, Unlabellable, Vt, @@ -75,6 +72,10 @@ use crate::syntax::{ }; use crate::util::PathExt; use crate::World; +use crate::{ + diag::{bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint}, + model::DelayedErrors, +}; const MAX_ITERATIONS: usize = 10_000; const MAX_CALL_DEPTH: usize = 64; @@ -102,11 +103,13 @@ pub fn eval( // Prepare VT. let mut locator = Locator::default(); let introspector = Introspector::default(); + let mut delayed = DelayedErrors::default(); let vt = Vt { world, - tracer, - locator: &mut locator, introspector: introspector.track(), + locator: &mut locator, + delayed: delayed.track_mut(), + tracer, }; // Prepare VM. @@ -151,12 +154,14 @@ pub fn eval_string( // Prepare VT. let mut tracer = Tracer::default(); let mut locator = Locator::default(); + let mut delayed = DelayedErrors::default(); let introspector = Introspector::default(); let vt = Vt { world, - tracer: tracer.track_mut(), - locator: &mut locator, introspector: introspector.track(), + locator: &mut locator, + delayed: delayed.track_mut(), + tracer: tracer.track_mut(), }; // Prepare VM. diff --git a/src/model/introspect.rs b/src/model/introspect.rs index f00f89f50..b150fabff 100644 --- a/src/model/introspect.rs +++ b/src/model/introspect.rs @@ -241,11 +241,6 @@ impl Introspector { #[comemo::track] impl Introspector { - /// Whether this introspector is not yet initialized. - pub fn init(&self) -> bool { - self.pages > 0 - } - /// Query for all matching elements. pub fn query(&self, selector: &Selector) -> EcoVec> { let hash = crate::util::hash128(selector); diff --git a/src/model/mod.rs b/src/model/mod.rs index 632b691fe..ee9402361 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -28,7 +28,7 @@ use std::mem::ManuallyDrop; use comemo::{Track, Tracked, TrackedMut, Validate}; -use crate::diag::SourceResult; +use crate::diag::{SourceError, SourceResult}; use crate::doc::Document; use crate::eval::Tracer; use crate::World; @@ -46,8 +46,9 @@ pub fn typeset( let library = world.library(); let styles = StyleChain::new(&library.styles); - let mut document; let mut iter = 0; + let mut document; + let mut delayed; // We need `ManuallyDrop` until this lands in stable: // https://github.com/rust-lang/rust/issues/70919 @@ -58,6 +59,8 @@ pub fn typeset( loop { tracing::info!("Layout iteration {iter}"); + delayed = DelayedErrors::default(); + let constraint = ::Constraint::new(); let mut locator = Locator::new(); let mut vt = Vt { @@ -65,6 +68,7 @@ pub fn typeset( tracer: TrackedMut::reborrow_mut(&mut tracer), locator: &mut locator, introspector: introspector.track_with(&constraint), + delayed: delayed.track_mut(), }; // Layout! @@ -86,6 +90,11 @@ pub fn typeset( // Drop the introspector. ManuallyDrop::into_inner(introspector); + // Promote delayed errors. + if !delayed.0.is_empty() { + return Err(Box::new(delayed.0)); + } + Ok(document) } @@ -95,10 +104,45 @@ pub fn typeset( pub struct Vt<'a> { /// The compilation environment. pub world: Tracked<'a, dyn World + 'a>, - /// The tracer for inspection of the values an expression produces. - pub tracer: TrackedMut<'a, Tracer>, - /// Provides stable identities to elements. - pub locator: &'a mut Locator<'a>, /// Provides access to information about the document. pub introspector: Tracked<'a, Introspector>, + /// Provides stable identities to elements. + pub locator: &'a mut Locator<'a>, + /// Delayed errors that do not immediately terminate execution. + pub delayed: TrackedMut<'a, DelayedErrors>, + /// 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(&mut self, f: F) -> T + where + F: FnOnce(&mut Self) -> SourceResult, + T: Default, + { + match f(self) { + Ok(value) => value, + Err(errors) => { + for error in *errors { + self.delayed.push(error); + } + T::default() + } + } + } +} + +/// Holds delayed errors. +#[derive(Default, Clone)] +pub struct DelayedErrors(Vec); + +#[comemo::track] +impl DelayedErrors { + /// Push a delayed error. + fn push(&mut self, error: SourceError) { + self.0.push(error); + } } diff --git a/tests/ref/bugs/cite-locate.png b/tests/ref/bugs/cite-locate.png new file mode 100644 index 000000000..396726f3b Binary files /dev/null and b/tests/ref/bugs/cite-locate.png differ diff --git a/tests/typ/bugs/cite-locate.typ b/tests/typ/bugs/cite-locate.typ new file mode 100644 index 000000000..c3786c5fc --- /dev/null +++ b/tests/typ/bugs/cite-locate.typ @@ -0,0 +1,23 @@ +// Test citation in other introspection. + +--- +#set page(width: 180pt) +#set heading(numbering: "1") + +#outline( + title: [List of Figures], + target: figure.where(kind: image), +) + +#pagebreak() + += Introduction +#figure( + rect[-- PIRATE --], + caption: [A pirate @arrgh in @intro], +) + +#locate(loc => [Citation @distress on page #loc.page()]) + +#pagebreak() +#bibliography("/works.bib", style: "chicago-notes")