summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-06-12 15:40:43 +0200
committerLaurenz <laurmaedje@gmail.com>2023-06-12 15:42:01 +0200
commit378ebe5f5601f11c3f428c17bed492012feb251e (patch)
tree4530bf009844faca40ca4fd6d0b9251fd51ef587
parent19bf1f58944a42f17903869c162a33aba22b80b0 (diff)
Delayed errors
Fixes #785. Thanks to @Dherse for the idea!
-rw-r--r--library/src/layout/mod.rs44
-rw-r--r--library/src/layout/par.rs19
-rw-r--r--library/src/meta/bibliography.rs88
-rw-r--r--library/src/meta/context.rs10
-rw-r--r--library/src/meta/counter.rs76
-rw-r--r--library/src/meta/link.rs23
-rw-r--r--library/src/meta/reference.rs92
-rw-r--r--library/src/meta/state.rs37
-rw-r--r--src/eval/func.rs25
-rw-r--r--src/eval/mod.rs19
-rw-r--r--src/model/introspect.rs5
-rw-r--r--src/model/mod.rs56
-rw-r--r--tests/ref/bugs/cite-locate.pngbin0 -> 50739 bytes
-rw-r--r--tests/typ/bugs/cite-locate.typ23
14 files changed, 314 insertions, 203 deletions
diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs
index d934c458..41490eb8 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<dyn World + '_>,
- tracer: TrackedMut<Tracer>,
- locator: Tracked<Locator>,
introspector: Tracked<Introspector>,
+ locator: Tracked<Locator>,
+ delayed: TrackedMut<DelayedErrors>,
+ tracer: TrackedMut<Tracer>,
styles: StyleChain,
) -> SourceResult<Document> {
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<Fragment> {
+ #[allow(clippy::too_many_arguments)]
#[comemo::memoize]
fn cached(
content: &Content,
world: Tracked<dyn World + '_>,
- tracer: TrackedMut<Tracer>,
- locator: Tracked<Locator>,
introspector: Tracked<Introspector>,
+ locator: Tracked<Locator>,
+ delayed: TrackedMut<DelayedErrors>,
+ tracer: TrackedMut<Tracer>,
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
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 fa53f031..2d0944ce 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<dyn World + '_>,
- tracer: TrackedMut<Tracer>,
- locator: Tracked<Locator>,
introspector: Tracked<Introspector>,
+ locator: Tracked<Locator>,
+ delayed: TrackedMut<DelayedErrors>,
+ tracer: TrackedMut<Tracer>,
styles: StyleChain,
consecutive: bool,
region: Size,
expand: bool,
) -> SourceResult<Fragment> {
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 737530b8..f020f730 100644
--- a/library/src/meta/bibliography.rs
+++ b/library/src/meta/bibliography.rs
@@ -96,11 +96,11 @@ impl BibliographyElem {
pub fn find(introspector: Tracked<Introspector>) -> StrResult<Self> {
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::<Self>().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(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(
- 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<Content> {
- 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 d599c63e..2ed359ee 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<Content> {
- 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 ef4646ab..65522c40 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<EcoVec<(CounterState, NonZeroUsize)>> {
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<dyn World + '_>,
- tracer: TrackedMut<Tracer>,
- locator: Tracked<Locator>,
introspector: Tracked<Introspector>,
+ locator: Tracked<Locator>,
+ delayed: TrackedMut<DelayedErrors>,
+ tracer: TrackedMut<Tracer>,
) -> SourceResult<EcoVec<(CounterState, NonZeroUsize)>> {
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<Content> {
- if !vt.introspector.init() {
- return Ok(Content::empty());
- }
-
- 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;
+ 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;
+ };
+
+ 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 43f6a34d..c3d8718c 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<Content> {
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 5a295e33..96358ffa 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<Content> {
- 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::<dyn Refable>()
+ .ok_or_else(|| {
+ if elem.can::<dyn Figurable>() {
+ 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::<dyn Refable>()
- .ok_or_else(|| {
- if elem.can::<dyn Figurable>() {
+ 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)?;
+ })
+ .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 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 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 231852e3..a8e37901 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<EcoVec<Value>> {
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<dyn World + '_>,
- tracer: TrackedMut<Tracer>,
- locator: Tracked<Locator>,
introspector: Tracked<Introspector>,
+ locator: Tracked<Locator>,
+ delayed: TrackedMut<DelayedErrors>,
+ tracer: TrackedMut<Tracer>,
) -> SourceResult<EcoVec<Value>> {
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<Content> {
- 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 1745dfb1..28d4a40d 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<dyn World + '_>,
route: Tracked<Route>,
- tracer: TrackedMut<Tracer>,
- locator: Tracked<Locator>,
introspector: Tracked<Introspector>,
+ locator: Tracked<Locator>,
+ delayed: TrackedMut<DelayedErrors>,
+ tracer: TrackedMut<Tracer>,
depth: usize,
mut args: Args,
) -> SourceResult<Value> {
@@ -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 e6016218..fe11606a 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 f00f89f5..b150fabf 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<Prehashed<Content>> {
let hash = crate::util::hash128(selector);
diff --git a/src/model/mod.rs b/src/model/mod.rs
index 632b691f..ee940236 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 = <Introspector as Validate>::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<F, T>(&mut self, f: F) -> T
+ where
+ F: FnOnce(&mut Self) -> SourceResult<T>,
+ 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<SourceError>);
+
+#[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 00000000..396726f3
--- /dev/null
+++ b/tests/ref/bugs/cite-locate.png
Binary files differ
diff --git a/tests/typ/bugs/cite-locate.typ b/tests/typ/bugs/cite-locate.typ
new file mode 100644
index 00000000..c3786c5f
--- /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 <intro>
+#figure(
+ rect[-- PIRATE --],
+ caption: [A pirate @arrgh in @intro],
+)
+
+#locate(loc => [Citation @distress on page #loc.page()])
+
+#pagebreak()
+#bibliography("/works.bib", style: "chicago-notes")