summaryrefslogtreecommitdiff
path: root/library/src/meta/reference.rs
diff options
context:
space:
mode:
authorSébastien d'Herbais de Thun <sebastien.d.herbais@gmail.com>2023-04-04 19:21:25 +0200
committerGitHub <noreply@github.com>2023-04-04 19:21:25 +0200
commitf347ed4314e32383dc09ff234180e8ea6fef7b8b (patch)
treedba4296812a131e52da4eb0079fd0d240860d832 /library/src/meta/reference.rs
parent5b0297464efc131beb7be84fa7a02b9a8670b5dd (diff)
Improved figure numbering, labelling and referencing (#491)
Diffstat (limited to 'library/src/meta/reference.rs')
-rw-r--r--library/src/meta/reference.rs114
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;
+}