Delayed errors

Fixes #785. Thanks to @Dherse for the idea!
This commit is contained in:
Laurenz 2023-06-12 15:40:43 +02:00
parent 19bf1f5894
commit 378ebe5f56
14 changed files with 315 additions and 204 deletions

View File

@ -50,6 +50,7 @@ use std::mem;
use typed_arena::Arena; use typed_arena::Arena;
use typst::diag::SourceResult; use typst::diag::SourceResult;
use typst::eval::Tracer; use typst::eval::Tracer;
use typst::model::DelayedErrors;
use typst::model::{applicable, realize, StyleVecBuilder}; use typst::model::{applicable, realize, StyleVecBuilder};
use crate::math::{EquationElem, LayoutMath}; use crate::math::{EquationElem, LayoutMath};
@ -116,13 +117,20 @@ impl LayoutRoot for Content {
fn cached( fn cached(
content: &Content, content: &Content,
world: Tracked<dyn World + '_>, world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>,
locator: Tracked<Locator>,
introspector: Tracked<Introspector>, introspector: Tracked<Introspector>,
locator: Tracked<Locator>,
delayed: TrackedMut<DelayedErrors>,
tracer: TrackedMut<Tracer>,
styles: StyleChain, styles: StyleChain,
) -> SourceResult<Document> { ) -> SourceResult<Document> {
let mut locator = Locator::chained(locator); 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 scratch = Scratch::default();
let (realized, styles) = realize_root(&mut vt, &scratch, content, styles)?; let (realized, styles) = realize_root(&mut vt, &scratch, content, styles)?;
realized realized
@ -132,13 +140,13 @@ impl LayoutRoot for Content {
} }
tracing::info!("Starting layout"); tracing::info!("Starting layout");
cached( cached(
self, self,
vt.world, vt.world,
TrackedMut::reborrow_mut(&mut vt.tracer),
vt.locator.track(),
vt.introspector, vt.introspector,
vt.locator.track(),
TrackedMut::reborrow_mut(&mut vt.delayed),
TrackedMut::reborrow_mut(&mut vt.tracer),
styles, styles,
) )
} }
@ -168,9 +176,10 @@ pub trait Layout {
let mut locator = Locator::chained(vt.locator.track()); let mut locator = Locator::chained(vt.locator.track());
let mut vt = Vt { let mut vt = Vt {
world: vt.world, world: vt.world,
tracer: TrackedMut::reborrow_mut(&mut vt.tracer),
locator: &mut locator,
introspector: vt.introspector, 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) self.layout(&mut vt, styles, regions)
} }
@ -184,18 +193,26 @@ impl Layout for Content {
styles: StyleChain, styles: StyleChain,
regions: Regions, regions: Regions,
) -> SourceResult<Fragment> { ) -> SourceResult<Fragment> {
#[allow(clippy::too_many_arguments)]
#[comemo::memoize] #[comemo::memoize]
fn cached( fn cached(
content: &Content, content: &Content,
world: Tracked<dyn World + '_>, world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>,
locator: Tracked<Locator>,
introspector: Tracked<Introspector>, introspector: Tracked<Introspector>,
locator: Tracked<Locator>,
delayed: TrackedMut<DelayedErrors>,
tracer: TrackedMut<Tracer>,
styles: StyleChain, styles: StyleChain,
regions: Regions, regions: Regions,
) -> SourceResult<Fragment> { ) -> SourceResult<Fragment> {
let mut locator = Locator::chained(locator); 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 scratch = Scratch::default();
let (realized, styles) = realize_block(&mut vt, &scratch, content, styles)?; let (realized, styles) = realize_block(&mut vt, &scratch, content, styles)?;
realized realized
@ -209,9 +226,10 @@ impl Layout for Content {
let fragment = cached( let fragment = cached(
self, self,
vt.world, vt.world,
TrackedMut::reborrow_mut(&mut vt.tracer),
vt.locator.track(),
vt.introspector, vt.introspector,
vt.locator.track(),
TrackedMut::reborrow_mut(&mut vt.delayed),
TrackedMut::reborrow_mut(&mut vt.tracer),
styles, styles,
regions, regions,
)?; )?;

View File

@ -5,6 +5,7 @@ use icu_provider_blob::BlobDataProvider;
use icu_segmenter::{LineBreakIteratorUtf8, LineSegmenter}; use icu_segmenter::{LineBreakIteratorUtf8, LineSegmenter};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use typst::eval::Tracer; use typst::eval::Tracer;
use typst::model::DelayedErrors;
use unicode_bidi::{BidiInfo, Level as BidiLevel}; use unicode_bidi::{BidiInfo, Level as BidiLevel};
use unicode_script::{Script, UnicodeScript}; use unicode_script::{Script, UnicodeScript};
@ -148,16 +149,23 @@ impl ParElem {
fn cached( fn cached(
par: &ParElem, par: &ParElem,
world: Tracked<dyn World + '_>, world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>,
locator: Tracked<Locator>,
introspector: Tracked<Introspector>, introspector: Tracked<Introspector>,
locator: Tracked<Locator>,
delayed: TrackedMut<DelayedErrors>,
tracer: TrackedMut<Tracer>,
styles: StyleChain, styles: StyleChain,
consecutive: bool, consecutive: bool,
region: Size, region: Size,
expand: bool, expand: bool,
) -> SourceResult<Fragment> { ) -> SourceResult<Fragment> {
let mut locator = Locator::chained(locator); 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(); let children = par.children();
// Collect all text into one string for BiDi analysis. // Collect all text into one string for BiDi analysis.
@ -178,9 +186,10 @@ impl ParElem {
let fragment = cached( let fragment = cached(
self, self,
vt.world, vt.world,
TrackedMut::reborrow_mut(&mut vt.tracer),
vt.locator.track(),
vt.introspector, vt.introspector,
vt.locator.track(),
TrackedMut::reborrow_mut(&mut vt.delayed),
TrackedMut::reborrow_mut(&mut vt.tracer),
styles, styles,
consecutive, consecutive,
region, region,

View File

@ -96,11 +96,11 @@ impl BibliographyElem {
pub fn find(introspector: Tracked<Introspector>) -> StrResult<Self> { pub fn find(introspector: Tracked<Introspector>) -> StrResult<Self> {
let mut iter = introspector.query(&Self::func().select()).into_iter(); let mut iter = introspector.query(&Self::func().select()).into_iter();
let Some(elem) = iter.next() else { 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() { if iter.next().is_some() {
Err("multiple bibliographies are not supported")?; bail!("multiple bibliographies are not supported");
} }
Ok(elem.to::<Self>().unwrap().clone()) Ok(elem.to::<Self>().unwrap().clone())
@ -162,42 +162,40 @@ impl Show for BibliographyElem {
seq.push(HeadingElem::new(title).with_level(NonZeroUsize::ONE).pack()); seq.push(HeadingElem::new(title).with_level(NonZeroUsize::ONE).pack());
} }
if !vt.introspector.init() { Ok(vt.delayed(|vt| {
return Ok(Content::sequence(seq)); 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(); seq.push(VElem::new(row_gutter).with_weakness(3).pack());
if works.references.iter().any(|(prefix, _)| prefix.is_some()) { seq.push(
let mut cells = vec![]; GridElem::new(cells)
for (prefix, reference) in &works.references { .with_columns(TrackSizings(vec![Sizing::Auto; 2]))
cells.push(prefix.clone().unwrap_or_default()); .with_column_gutter(TrackSizings(vec![COLUMN_GUTTER.into()]))
cells.push(reference.clone()); .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()); Ok(Content::sequence(seq))
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))
} }
} }
@ -357,19 +355,17 @@ impl Synthesize for CiteElem {
impl Show for CiteElem { impl Show for CiteElem {
#[tracing::instrument(name = "CiteElem::show", skip(self, vt))] #[tracing::instrument(name = "CiteElem::show", skip(self, vt))]
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> { fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
if !vt.introspector.init() { Ok(vt.delayed(|vt| {
return Ok(Content::empty()); let works = Works::new(vt).at(self.span())?;
} let location = self.0.location().unwrap();
works
let works = Works::new(vt).at(self.span())?; .citations
let location = self.0.location().unwrap(); .get(&location)
works .cloned()
.citations .flatten()
.get(&location) .ok_or("bibliography does not contain this key")
.cloned() .at(self.span())
.flatten() }))
.ok_or("bibliography does not contain this key")
.at(self.span())
} }
} }

View File

@ -73,12 +73,10 @@ struct LocateElem {
impl Show for LocateElem { impl Show for LocateElem {
#[tracing::instrument(name = "LocateElem::show", skip(self, vt))] #[tracing::instrument(name = "LocateElem::show", skip(self, vt))]
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> { fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
if !vt.introspector.init() { Ok(vt.delayed(|vt| {
return Ok(Content::empty()); let location = self.0.location().unwrap();
} Ok(self.func().call_vt(vt, [location])?.display())
}))
let location = self.0.location().unwrap();
Ok(self.func().call_vt(vt, [location])?.display())
} }
} }

View File

@ -4,6 +4,7 @@ use std::str::FromStr;
use ecow::{eco_vec, EcoVec}; use ecow::{eco_vec, EcoVec};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use typst::eval::Tracer; use typst::eval::Tracer;
use typst::model::DelayedErrors;
use super::{FigureElem, HeadingElem, Numbering, NumberingPattern}; use super::{FigureElem, HeadingElem, Numbering, NumberingPattern};
use crate::layout::PageElem; use crate::layout::PageElem;
@ -397,9 +398,10 @@ impl Counter {
) -> SourceResult<EcoVec<(CounterState, NonZeroUsize)>> { ) -> SourceResult<EcoVec<(CounterState, NonZeroUsize)>> {
self.sequence_impl( self.sequence_impl(
vt.world, vt.world,
TrackedMut::reborrow_mut(&mut vt.tracer),
vt.locator.track(),
vt.introspector, 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( fn sequence_impl(
&self, &self,
world: Tracked<dyn World + '_>, world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>,
locator: Tracked<Locator>,
introspector: Tracked<Introspector>, introspector: Tracked<Introspector>,
locator: Tracked<Locator>,
delayed: TrackedMut<DelayedErrors>,
tracer: TrackedMut<Tracer>,
) -> SourceResult<EcoVec<(CounterState, NonZeroUsize)>> { ) -> SourceResult<EcoVec<(CounterState, NonZeroUsize)>> {
let mut locator = Locator::chained(locator); 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 { let mut state = CounterState(match &self.0 {
// special case, because pages always start at one. // special case, because pages always start at one.
CounterKey::Page => smallvec![1], CounterKey::Page => smallvec![1],
@ -618,37 +627,36 @@ struct DisplayElem {
impl Show for DisplayElem { impl Show for DisplayElem {
#[tracing::instrument(name = "DisplayElem::show", skip_all)] #[tracing::instrument(name = "DisplayElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> { fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
if !vt.introspector.init() { Ok(vt.delayed(|vt| {
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;
};
let location = self.0.location().unwrap(); if func == HeadingElem::func() {
let counter = self.counter(); HeadingElem::numbering_in(styles)
let numbering = self } else if func == FigureElem::func() {
.numbering() FigureElem::numbering_in(styles)
.or_else(|| { } else if func == EquationElem::func() {
let CounterKey::Selector(Selector::Elem(func, _)) = counter.0 else { EquationElem::numbering_in(styles)
return None; } 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() { state.display(vt, &numbering)
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)
} }
} }

View File

@ -91,21 +91,18 @@ impl Show for LinkElem {
#[tracing::instrument(name = "LinkElem::show", skip(self, vt))] #[tracing::instrument(name = "LinkElem::show", skip(self, vt))]
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> { fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
let body = self.body(); let body = self.body();
let dest = match self.dest() { let linked = match self.dest() {
LinkTarget::Dest(dest) => dest, LinkTarget::Dest(dest) => body.linked(dest),
LinkTarget::Label(label) => { LinkTarget::Label(label) => vt
if !vt.introspector.init() { .delayed(|vt| {
return Ok(body); let elem = vt.introspector.query_label(&label).at(self.span())?;
} let dest = Destination::Location(elem.location().unwrap());
Ok(Some(body.clone().linked(dest)))
let elem = vt.introspector.query_label(&label).at(self.span())?; })
Destination::Location(elem.location().unwrap()) .unwrap_or(body),
}
}; };
Ok(body Ok(linked.styled(TextElem::set_hyphenate(Hyphenate(Smart::Custom(false)))))
.linked(dest)
.styled(TextElem::set_hyphenate(Hyphenate(Smart::Custom(false)))))
} }
} }

View File

@ -132,7 +132,7 @@ impl Synthesize for RefElem {
self.push_element(None); self.push_element(None);
let target = self.target(); 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) { if let Ok(elem) = vt.introspector.query_label(&target) {
self.push_element(Some(elem.into_inner())); self.push_element(Some(elem.into_inner()));
return Ok(()); return Ok(());
@ -146,63 +146,65 @@ impl Synthesize for RefElem {
impl Show for RefElem { impl Show for RefElem {
#[tracing::instrument(name = "RefElem::show", skip_all)] #[tracing::instrument(name = "RefElem::show", skip_all)]
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> { fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
if !vt.introspector.init() { Ok(vt.delayed(|vt| {
return Ok(Content::empty()); let target = self.target();
} let elem = vt.introspector.query_label(&self.target());
let span = self.span();
let target = self.target(); if BibliographyElem::has(vt, &target.0) {
let elem = vt.introspector.query_label(&self.target()); if elem.is_ok() {
let span = self.span(); bail!(span, "label occurs in the document and its bibliography");
}
if BibliographyElem::has(vt, &target.0) { return Ok(self.to_citation(vt, styles)?.pack().spanned(span));
if elem.is_ok() {
bail!(span, "label occurs in the document and its bibliography");
} }
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 numbering = refable
let refable = elem .numbering()
.with::<dyn Refable>() .ok_or_else(|| {
.ok_or_else(|| {
if elem.can::<dyn Figurable>() {
eco_format!( 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() 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 let mut content = numbers;
.numbering() if !supplement.is_empty() {
.ok_or_else(|| { content = supplement + TextElem::packed("\u{a0}") + content;
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; Ok(content.linked(Destination::Location(elem.location().unwrap())))
if !supplement.is_empty() { }))
content = supplement + TextElem::packed("\u{a0}") + content;
}
Ok(content.linked(Destination::Location(elem.location().unwrap())))
} }
} }

View File

@ -2,6 +2,7 @@ use std::fmt::{self, Debug, Formatter, Write};
use ecow::{eco_vec, EcoVec}; use ecow::{eco_vec, EcoVec};
use typst::eval::Tracer; use typst::eval::Tracer;
use typst::model::DelayedErrors;
use crate::prelude::*; use crate::prelude::*;
@ -306,9 +307,10 @@ impl State {
fn sequence(&self, vt: &mut Vt) -> SourceResult<EcoVec<Value>> { fn sequence(&self, vt: &mut Vt) -> SourceResult<EcoVec<Value>> {
self.sequence_impl( self.sequence_impl(
vt.world, vt.world,
TrackedMut::reborrow_mut(&mut vt.tracer),
vt.locator.track(),
vt.introspector, 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( fn sequence_impl(
&self, &self,
world: Tracked<dyn World + '_>, world: Tracked<dyn World + '_>,
tracer: TrackedMut<Tracer>,
locator: Tracked<Locator>,
introspector: Tracked<Introspector>, introspector: Tracked<Introspector>,
locator: Tracked<Locator>,
delayed: TrackedMut<DelayedErrors>,
tracer: TrackedMut<Tracer>,
) -> SourceResult<EcoVec<Value>> { ) -> SourceResult<EcoVec<Value>> {
let mut locator = Locator::chained(locator); 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 state = self.init.clone();
let mut stops = eco_vec![state.clone()]; let mut stops = eco_vec![state.clone()];
@ -397,16 +406,14 @@ struct DisplayElem {
impl Show for DisplayElem { impl Show for DisplayElem {
#[tracing::instrument(name = "DisplayElem::show", skip(self, vt))] #[tracing::instrument(name = "DisplayElem::show", skip(self, vt))]
fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> { fn show(&self, vt: &mut Vt, _: StyleChain) -> SourceResult<Content> {
if !vt.introspector.init() { Ok(vt.delayed(|vt| {
return Ok(Content::empty()); let location = self.0.location().unwrap();
} let value = self.state().at(vt, location)?;
Ok(match self.func() {
let location = self.0.location().unwrap(); Some(func) => func.call_vt(vt, [value])?.display(),
let value = self.state().at(vt, location)?; None => value.display(),
Ok(match self.func() { })
Some(func) => func.call_vt(vt, [value])?.display(), }))
None => value.display(),
})
} }
} }

View File

@ -11,7 +11,7 @@ use super::{
Value, Vm, Value, Vm,
}; };
use crate::diag::{bail, SourceResult, StrResult}; 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::ast::{self, AstNode, Expr, Ident};
use crate::syntax::{SourceId, Span, SyntaxNode}; use crate::syntax::{SourceId, Span, SyntaxNode};
use crate::World; use crate::World;
@ -102,9 +102,10 @@ impl Func {
self, self,
vm.world(), vm.world(),
route, route,
TrackedMut::reborrow_mut(&mut vm.vt.tracer),
vm.vt.locator.track(),
vm.vt.introspector, vm.vt.introspector,
vm.vt.locator.track(),
TrackedMut::reborrow_mut(&mut vm.vt.delayed),
TrackedMut::reborrow_mut(&mut vm.vt.tracer),
vm.depth + 1, vm.depth + 1,
args, args,
) )
@ -129,9 +130,10 @@ impl Func {
let mut locator = Locator::chained(vt.locator.track()); let mut locator = Locator::chained(vt.locator.track());
let vt = Vt { let vt = Vt {
world: vt.world, world: vt.world,
tracer: TrackedMut::reborrow_mut(&mut vt.tracer),
locator: &mut locator,
introspector: vt.introspector, 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 mut vm = Vm::new(vt, route.track(), id, scopes);
let args = Args::new(self.span(), args); let args = Args::new(self.span(), args);
@ -326,9 +328,10 @@ impl Closure {
this: &Func, this: &Func,
world: Tracked<dyn World + '_>, world: Tracked<dyn World + '_>,
route: Tracked<Route>, route: Tracked<Route>,
tracer: TrackedMut<Tracer>,
locator: Tracked<Locator>,
introspector: Tracked<Introspector>, introspector: Tracked<Introspector>,
locator: Tracked<Locator>,
delayed: TrackedMut<DelayedErrors>,
tracer: TrackedMut<Tracer>,
depth: usize, depth: usize,
mut args: Args, mut args: Args,
) -> SourceResult<Value> { ) -> SourceResult<Value> {
@ -344,7 +347,13 @@ impl Closure {
// Prepare VT. // Prepare VT.
let mut locator = Locator::chained(locator); 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. // Prepare VM.
let mut vm = Vm::new(vt, route, closure.location, scopes); let mut vm = Vm::new(vt, route, closure.location, scopes);

View File

@ -62,9 +62,6 @@ use ecow::{EcoString, EcoVec};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use self::func::{CapturesVisitor, Closure}; use self::func::{CapturesVisitor, Closure};
use crate::diag::{
bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint,
};
use crate::model::{ use crate::model::{
Content, Introspector, Label, Locator, Recipe, ShowableSelector, Styles, Transform, Content, Introspector, Label, Locator, Recipe, ShowableSelector, Styles, Transform,
Unlabellable, Vt, Unlabellable, Vt,
@ -75,6 +72,10 @@ use crate::syntax::{
}; };
use crate::util::PathExt; use crate::util::PathExt;
use crate::World; use crate::World;
use crate::{
diag::{bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint},
model::DelayedErrors,
};
const MAX_ITERATIONS: usize = 10_000; const MAX_ITERATIONS: usize = 10_000;
const MAX_CALL_DEPTH: usize = 64; const MAX_CALL_DEPTH: usize = 64;
@ -102,11 +103,13 @@ pub fn eval(
// Prepare VT. // Prepare VT.
let mut locator = Locator::default(); let mut locator = Locator::default();
let introspector = Introspector::default(); let introspector = Introspector::default();
let mut delayed = DelayedErrors::default();
let vt = Vt { let vt = Vt {
world, world,
tracer,
locator: &mut locator,
introspector: introspector.track(), introspector: introspector.track(),
locator: &mut locator,
delayed: delayed.track_mut(),
tracer,
}; };
// Prepare VM. // Prepare VM.
@ -151,12 +154,14 @@ pub fn eval_string(
// Prepare VT. // Prepare VT.
let mut tracer = Tracer::default(); let mut tracer = Tracer::default();
let mut locator = Locator::default(); let mut locator = Locator::default();
let mut delayed = DelayedErrors::default();
let introspector = Introspector::default(); let introspector = Introspector::default();
let vt = Vt { let vt = Vt {
world, world,
tracer: tracer.track_mut(),
locator: &mut locator,
introspector: introspector.track(), introspector: introspector.track(),
locator: &mut locator,
delayed: delayed.track_mut(),
tracer: tracer.track_mut(),
}; };
// Prepare VM. // Prepare VM.

View File

@ -241,11 +241,6 @@ impl Introspector {
#[comemo::track] #[comemo::track]
impl Introspector { impl Introspector {
/// Whether this introspector is not yet initialized.
pub fn init(&self) -> bool {
self.pages > 0
}
/// Query for all matching elements. /// Query for all matching elements.
pub fn query(&self, selector: &Selector) -> EcoVec<Prehashed<Content>> { pub fn query(&self, selector: &Selector) -> EcoVec<Prehashed<Content>> {
let hash = crate::util::hash128(selector); let hash = crate::util::hash128(selector);

View File

@ -28,7 +28,7 @@ use std::mem::ManuallyDrop;
use comemo::{Track, Tracked, TrackedMut, Validate}; use comemo::{Track, Tracked, TrackedMut, Validate};
use crate::diag::SourceResult; use crate::diag::{SourceError, SourceResult};
use crate::doc::Document; use crate::doc::Document;
use crate::eval::Tracer; use crate::eval::Tracer;
use crate::World; use crate::World;
@ -46,8 +46,9 @@ pub fn typeset(
let library = world.library(); let library = world.library();
let styles = StyleChain::new(&library.styles); let styles = StyleChain::new(&library.styles);
let mut document;
let mut iter = 0; let mut iter = 0;
let mut document;
let mut delayed;
// We need `ManuallyDrop` until this lands in stable: // We need `ManuallyDrop` until this lands in stable:
// https://github.com/rust-lang/rust/issues/70919 // https://github.com/rust-lang/rust/issues/70919
@ -58,6 +59,8 @@ pub fn typeset(
loop { loop {
tracing::info!("Layout iteration {iter}"); tracing::info!("Layout iteration {iter}");
delayed = DelayedErrors::default();
let constraint = <Introspector as Validate>::Constraint::new(); let constraint = <Introspector as Validate>::Constraint::new();
let mut locator = Locator::new(); let mut locator = Locator::new();
let mut vt = Vt { let mut vt = Vt {
@ -65,6 +68,7 @@ pub fn typeset(
tracer: TrackedMut::reborrow_mut(&mut tracer), tracer: TrackedMut::reborrow_mut(&mut tracer),
locator: &mut locator, locator: &mut locator,
introspector: introspector.track_with(&constraint), introspector: introspector.track_with(&constraint),
delayed: delayed.track_mut(),
}; };
// Layout! // Layout!
@ -86,6 +90,11 @@ pub fn typeset(
// Drop the introspector. // Drop the introspector.
ManuallyDrop::into_inner(introspector); ManuallyDrop::into_inner(introspector);
// Promote delayed errors.
if !delayed.0.is_empty() {
return Err(Box::new(delayed.0));
}
Ok(document) Ok(document)
} }
@ -95,10 +104,45 @@ pub fn typeset(
pub struct Vt<'a> { pub struct Vt<'a> {
/// The compilation environment. /// The compilation environment.
pub world: Tracked<'a, dyn World + 'a>, 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. /// Provides access to information about the document.
pub introspector: Tracked<'a, Introspector>, 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);
}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -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")