mirror of
https://github.com/typst/typst
synced 2025-05-17 02:25:27 +08:00
parent
19bf1f5894
commit
378ebe5f56
@ -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,
|
||||||
)?;
|
)?;
|
||||||
|
@ -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,
|
||||||
|
@ -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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)))))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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())))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
BIN
tests/ref/bugs/cite-locate.png
Normal file
BIN
tests/ref/bugs/cite-locate.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
23
tests/typ/bugs/cite-locate.typ
Normal file
23
tests/typ/bugs/cite-locate.typ
Normal 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")
|
Loading…
x
Reference in New Issue
Block a user