diff options
| author | Sébastien d'Herbais de Thun <sebastien.d.herbais@gmail.com> | 2023-04-04 19:21:25 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-04-04 19:21:25 +0200 |
| commit | f347ed4314e32383dc09ff234180e8ea6fef7b8b (patch) | |
| tree | dba4296812a131e52da4eb0079fd0d240860d832 /library/src/meta/reference.rs | |
| parent | 5b0297464efc131beb7be84fa7a02b9a8670b5dd (diff) | |
Improved figure numbering, labelling and referencing (#491)
Diffstat (limited to 'library/src/meta/reference.rs')
| -rw-r--r-- | library/src/meta/reference.rs | 114 |
1 files changed, 82 insertions, 32 deletions
diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs index 24687845..81fd88b9 100644 --- a/library/src/meta/reference.rs +++ b/library/src/meta/reference.rs @@ -1,6 +1,5 @@ -use super::{BibliographyElem, CiteElem, Counter, LocalName, Numbering}; +use super::{BibliographyElem, CiteElem, Counter, Numbering}; use crate::prelude::*; -use crate::text::TextElem; /// A reference to a label or bibliography. /// @@ -83,9 +82,10 @@ pub struct RefElem { } impl Synthesize for RefElem { - fn synthesize(&mut self, styles: StyleChain) { - let citation = self.to_citation(styles); + fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()> { + let citation = self.to_citation(vt, styles)?; self.push_citation(Some(citation)); + Ok(()) } } @@ -103,55 +103,42 @@ impl Show for RefElem { bail!(self.span(), "label occurs in the document and its bibliography"); } - return Ok(self.to_citation(styles).pack()); + return Ok(self.to_citation(vt, styles)?.pack()); } let elem = elem.at(self.span())?; - if !elem.can::<dyn Locatable>() { + if !elem.can::<dyn Refable>() { bail!(self.span(), "cannot reference {}", elem.func().name()); } - let supplement = self.supplement(styles); - let mut supplement = match supplement { - Smart::Auto => elem - .with::<dyn LocalName>() - .map(|elem| elem.local_name(TextElem::lang_in(styles))) - .map(TextElem::packed) - .unwrap_or_default(), - Smart::Custom(None) => Content::empty(), - Smart::Custom(Some(Supplement::Content(content))) => content, - Smart::Custom(Some(Supplement::Func(func))) => { - func.call_vt(vt, [elem.clone().into()])?.display() + let supplement = match self.supplement(styles) { + Smart::Auto | Smart::Custom(None) => None, + Smart::Custom(Some(supplement)) => { + Some(supplement.resolve(vt, [elem.clone().into()])?) } }; - if !supplement.is_empty() { - supplement += TextElem::packed('\u{a0}'); - } - - let Some(numbering) = elem.cast_field::<Numbering>("numbering") else { - bail!(self.span(), "only numbered elements can be referenced"); - }; + let reference = elem + .with::<dyn Refable>() + .expect("element should be refable") + .reference(vt, styles, supplement)?; - let numbers = Counter::of(elem.func()) - .at(vt, elem.location().unwrap())? - .display(vt, &numbering.trimmed())?; - - Ok((supplement + numbers).linked(Destination::Location(elem.location().unwrap()))) + Ok(reference.linked(Destination::Location(elem.location().unwrap()))) } } impl RefElem { /// Turn the reference into a citation. - pub fn to_citation(&self, styles: StyleChain) -> CiteElem { + pub fn to_citation(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<CiteElem> { let mut elem = CiteElem::new(vec![self.target().0]); elem.0.set_location(self.0.location().unwrap()); - elem.synthesize(styles); + elem.synthesize(vt, styles)?; elem.push_supplement(match self.supplement(styles) { Smart::Custom(Some(Supplement::Content(content))) => Some(content), _ => None, }); - elem + + Ok(elem) } } @@ -161,6 +148,29 @@ pub enum Supplement { Func(Func), } +impl Supplement { + /// Tries to resolve the supplement into its content. + pub fn resolve( + &self, + vt: &mut Vt, + args: impl IntoIterator<Item = Value>, + ) -> SourceResult<Content> { + match self { + Supplement::Content(content) => Ok(content.clone()), + Supplement::Func(func) => func.call_vt(vt, args).map(|v| v.display()), + } + } + + /// Tries to get the content of the supplement. + /// Returns `None` if the supplement is a function. + pub fn as_content(self) -> Option<Content> { + match self { + Supplement::Content(content) => Some(content), + _ => None, + } + } +} + cast_from_value! { Supplement, v: Content => Self::Content(v), @@ -173,3 +183,43 @@ cast_to_value! { Supplement::Func(v) => v.into(), } } + +/// Marks an element as being able to be referenced. +/// This is used to implement the `@ref` macro. +/// It is expected to build the [`Content`] that gets linked +/// by the [`RefElement`]. +pub trait Refable { + /// Tries to build a reference content for this element. + /// + /// # Arguments + /// - `vt` - The virtual typesetter. + /// - `styles` - The styles of the reference. + /// - `location` - The location where the reference is being created. + /// - `supplement` - The supplement of the reference. + fn reference( + &self, + vt: &mut Vt, + styles: StyleChain, + supplement: Option<Content>, + ) -> SourceResult<Content>; + + /// Tries to build an outline element for this element. + /// If this returns `None`, the outline will not include this element. + /// By default this just calls [`Refable::reference`]. + fn outline(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Option<Content>> { + self.reference(vt, styles, None).map(Some) + } + + /// Returns the level of this element. + /// This is used to determine the level of the outline. + /// By default this returns `0`. + fn level(&self, _styles: StyleChain) -> usize { + 0 + } + + /// Returns the numbering of this element. + fn numbering(&self, styles: StyleChain) -> Option<Numbering>; + + /// Returns the counter of this element. + fn counter(&self, styles: StyleChain) -> Counter; +} |
