summaryrefslogtreecommitdiff
path: root/library/src/meta/reference.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/src/meta/reference.rs')
-rw-r--r--library/src/meta/reference.rs122
1 files changed, 49 insertions, 73 deletions
diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs
index 328e6098..c538b696 100644
--- a/library/src/meta/reference.rs
+++ b/library/src/meta/reference.rs
@@ -95,6 +95,9 @@ pub struct RefElem {
/// For references to headings or figures, this is added before the
/// referenced number. For citations, this can be used to add a page number.
///
+ /// If a function is specified, it is passed the referenced element and
+ /// should return content.
+ ///
/// ```example
/// #set heading(numbering: "1.")
/// #set ref(supplement: it => {
@@ -149,43 +152,57 @@ impl Show for RefElem {
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!(self.span(), "label occurs in the document and its bibliography");
+ bail!(span, "label occurs in the document and its bibliography");
}
- return Ok(self.to_citation(vt, styles)?.pack().spanned(self.span()));
+ return Ok(self.to_citation(vt, styles)?.pack().spanned(span));
}
- let elem = elem.at(self.span())?;
- if !elem.can::<dyn Refable>() {
- if elem.can::<dyn Figurable>() {
- bail!(
- self.span(),
- "cannot reference {} directly, try putting it into a figure",
- elem.func().name()
- );
- } else {
- bail!(self.span(), "cannot reference {}", elem.func().name());
- }
- }
+ 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 numbering = refable
+ .numbering()
+ .ok_or_else(|| {
+ eco_format!("cannot reference {} without numbering", 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 | Smart::Custom(None) => None,
+ Smart::Auto => refable.supplement(),
+ Smart::Custom(None) => Content::empty(),
Smart::Custom(Some(supplement)) => {
- Some(supplement.resolve(vt, [(*elem).clone().into()])?)
+ supplement.resolve(vt, [(*elem).clone().into()])?
}
};
- let lang = TextElem::lang_in(styles);
- let region = TextElem::region_in(styles);
- let reference = elem
- .with::<dyn Refable>()
- .expect("element should be refable")
- .reference(vt, supplement, lang, region)?;
+ let mut content = numbers;
+ if !supplement.is_empty() {
+ content = supplement + TextElem::packed("\u{a0}") + content;
+ }
- Ok(reference.linked(Destination::Location(elem.location().unwrap())))
+ Ok(content.linked(Destination::Location(elem.location().unwrap())))
}
}
@@ -217,19 +234,10 @@ impl Supplement {
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,
- }
+ Ok(match self {
+ Supplement::Content(content) => content.clone(),
+ Supplement::Func(func) => func.call_vt(vt, args)?.display(),
+ })
}
}
@@ -247,46 +255,14 @@ cast_to_value! {
}
/// Marks an element as being able to be referenced. This is used to implement
-/// the `@ref` element. It is expected to build the [`Content`] that gets linked
-/// by the [`RefElem`].
+/// the `@ref` element.
pub trait Refable {
- /// Tries to build a reference content for this element.
- ///
- /// # Arguments
- /// - `vt` - The virtual typesetter.
- /// - `supplement` - The supplement of the reference.
- /// - `lang`: The language of the reference.
- /// - `region`: The region of the reference.
- fn reference(
- &self,
- vt: &mut Vt,
- supplement: Option<Content>,
- lang: Lang,
- region: Option<Region>,
- ) -> 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,
- lang: Lang,
- region: Option<Region>,
- ) -> SourceResult<Option<Content>> {
- self.reference(vt, None, lang, region).map(Some)
- }
+ /// The supplement, if not overriden by the reference.
+ fn supplement(&self) -> Content;
- /// Returns the level of this element.
- /// This is used to determine the level of the outline.
- /// By default this returns `0`.
- fn level(&self) -> usize {
- 0
- }
+ /// Returns the counter of this element.
+ fn counter(&self) -> Counter;
/// Returns the numbering of this element.
fn numbering(&self) -> Option<Numbering>;
-
- /// Returns the counter of this element.
- fn counter(&self) -> Counter;
}