summaryrefslogtreecommitdiff
path: root/library/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-04-04 23:15:03 +0200
committerLaurenz <laurmaedje@gmail.com>2023-04-04 23:15:03 +0200
commit23715e813e1888c8c317ec2f9649d0f1ad46bd8c (patch)
tree19e023514700cb9ec69285cafe658964aec4ee60 /library/src
parentf347ed4314e32383dc09ff234180e8ea6fef7b8b (diff)
Refactor and document figures
Diffstat (limited to 'library/src')
-rw-r--r--library/src/layout/table.rs3
-rw-r--r--library/src/math/mod.rs3
-rw-r--r--library/src/meta/bibliography.rs2
-rw-r--r--library/src/meta/counter.rs4
-rw-r--r--library/src/meta/figure.rs499
-rw-r--r--library/src/meta/heading.rs19
-rw-r--r--library/src/meta/outline.rs89
-rw-r--r--library/src/meta/reference.rs21
-rw-r--r--library/src/text/raw.rs1
9 files changed, 325 insertions, 316 deletions
diff --git a/library/src/layout/table.rs b/library/src/layout/table.rs
index c9a67a1b..4e21cb45 100644
--- a/library/src/layout/table.rs
+++ b/library/src/layout/table.rs
@@ -10,6 +10,9 @@ use crate::prelude::*;
/// the [grid documentation]($func/grid) for more information on how to size the
/// table tracks.
///
+/// To give a table a caption and make it [referenceable]($func/ref), put it
+/// into a [figure]($func/figure).
+///
/// ## Example
/// ```example
/// #table(
diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs
index b07fc78f..db066522 100644
--- a/library/src/math/mod.rs
+++ b/library/src/math/mod.rs
@@ -165,7 +165,6 @@ impl Synthesize for EquationElem {
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
self.push_block(self.block(styles));
self.push_numbering(self.numbering(styles));
-
Ok(())
}
}
@@ -317,7 +316,7 @@ impl Refable for EquationElem {
self.numbering(styles)
}
- fn counter(&self, _styles: StyleChain) -> Counter {
+ fn counter(&self, _: StyleChain) -> Counter {
Counter::of(Self::func())
}
}
diff --git a/library/src/meta/bibliography.rs b/library/src/meta/bibliography.rs
index 7bc2ff7b..864aa1ff 100644
--- a/library/src/meta/bibliography.rs
+++ b/library/src/meta/bibliography.rs
@@ -135,7 +135,6 @@ impl BibliographyElem {
impl Synthesize for BibliographyElem {
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
self.push_style(self.style(styles));
-
Ok(())
}
}
@@ -322,7 +321,6 @@ impl Synthesize for CiteElem {
self.push_supplement(self.supplement(styles));
self.push_brackets(self.brackets(styles));
self.push_style(self.style(styles));
-
Ok(())
}
}
diff --git a/library/src/meta/counter.rs b/library/src/meta/counter.rs
index 3868040e..5ad9b48d 100644
--- a/library/src/meta/counter.rs
+++ b/library/src/meta/counter.rs
@@ -496,9 +496,7 @@ cast_from_value! {
Self::Selector(Selector::Elem(element, None))
},
- selector: Selector => {
- Self::Selector(selector)
- }
+ selector: Selector => Self::Selector(selector),
}
impl Debug for CounterKey {
diff --git a/library/src/meta/figure.rs b/library/src/meta/figure.rs
index aae13738..2cea230e 100644
--- a/library/src/meta/figure.rs
+++ b/library/src/meta/figure.rs
@@ -1,7 +1,5 @@
use std::str::FromStr;
-use ecow::eco_vec;
-
use super::{
Count, Counter, CounterKey, CounterUpdate, LocalName, Numbering, NumberingPattern,
};
@@ -12,85 +10,63 @@ use crate::text::TextElem;
/// A figure with an optional caption.
///
-/// ## Content detection
-/// By default, the figure will attempt to automatically detect the content
-/// and use a priority list to detect which content is likely
-/// to be the most important. The priority list is as follows:
-/// - [image]($func/image) are the most important
-/// - [equations]($func/equation) are the second most important
-/// - [code]($func/raw) are the third most important
-/// - [table]($func/table) are the fourth most important.
-///
-/// There can be a variety of content within a figure and only the first element
-/// of the most important category will be used. For example, if a figure contains
-/// an image and a table, the image will be used. This behaviour can be overridden
-/// using the `kind` parameter. By setting it, you can force the figure to use a
-/// specific type of content. Note however that if the figure does not contain said
-/// element, or the `kind` is set to a string, you will need to manually specify
-/// the supplement to be able to make an outline or reference it.
+/// Automatically detects its contents to select the correct counting track.
+/// For example, figures containing images will be numbered separately from
+/// figures containing tables.
///
+/// ## Examples
+/// The example below shows a basic figure with an image:
/// ```example
-/// #figure(caption: [ Hello, world! ], kind: table)[
-/// #table(
-/// columns: (auto, 1fr),
-/// image("molecular.jpg", width: 32pt),
-/// [ A first picture ],
-/// image("molecular.jpg", width: 32pt),
-/// [ A second picture ],
-/// )
-/// ]
-/// ```
+/// @glacier shows a glacier. Glaciers
+/// are complex systems.
///
-/// If you use an element that is not supported by the figure, and set it as its `content` parameter,
-/// to be able to make an outline or reference it, you will need to manually specify the supplement
-/// and counter. Otherwise the figure will produce an error.
+/// #figure(
+/// image("glacier.jpg", width: 80%),
+/// caption: [A curious figure.],
+/// ) <glacier>
+/// ```
///
-/// ## Counting and supplement
-/// Based on the `kind` parameter or the detected content, the figure will chose
-/// the appropriate counter and supplement. These can be overridden by using the
-/// `kind` and `supplement` parameters respectively.
+/// You can also insert [tables]($func/table) into figures to give them a
+/// caption. The figure will detect this and automatically use a separate
+/// counter.
///
-/// The overriding of these values is done as follows:
/// ```example
-/// #figure(caption: [ Hello, world! ], kind: "hello", supplement: "Molecule")[
-/// #image("molecular.jpg", width: 32pt)
-/// ]
+/// #figure(
+/// table(
+/// columns: 4,
+/// [t], [1], [2], [3],
+/// [y], [0.3s], [0.4s], [0.8s],
+/// ),
+/// caption: [Timing results],
+/// )
/// ```
///
-/// The default counters are defined as follows:
-/// - for (tables)[$func/table]: `counter(figure.where(kind: table))`
-/// - for (equations)[$func/equation]: `counter(figure.where(kind: math.equation))`
-/// - for (raw text)[$func/raw]: `counter(figure.where(kind: raw))`
-/// - for (images)[$func/image]: `counter(figure.where(kind: image))`
-/// - for a custom kind: `counter(figure.where(kind: kind))`
-///
-/// These are the counters you need to use if you want to change the
-/// counting behaviour of figures.
-///
-/// ## Numbering
-/// By default, the figure will be numbered using the `1` [numbering pattern]($func/numbering).
-/// This can be overridden by using the `numbering` parameter.
+/// This behaviour can be overridden by explictly specifying the figure's
+/// `kind`. All figures of the same kind share a common counter.
///
-/// ## Outline
-/// By default, the figure will be outlined in the list of figures/tables/code. This can be disabled by
-/// setting the `outlined` parameter to `false`.
+/// ## Modifying the appearance
+/// You can completely customize the look of your figures with a [show
+/// rule]($styling/#show-rules). In the example below, we show the figure's
+/// caption above its body and display its supplement and counter after the
+/// caption.
///
-/// ## Global figure counter
-/// There is a global figure counter which can be accessed which counts all numbered figures in the document
-/// regardless of its type. This counter can be accessed using the `counter(figure)` function.
-///
-/// ## Example
/// ```example
-/// = Pipeline
-/// @lab shows the central step of
-/// our molecular testing pipeline.
+/// #show figure: it => align(center)[
+/// #it.caption |
+/// #emph[
+/// #it.supplement
+/// #it.counter.display(it.numbering)
+/// ]
+/// #v(10pt, weak: true)
+/// #it.body
+/// ]
///
/// #figure(
/// image("molecular.jpg", width: 80%),
/// caption: [
/// The molecular testing pipeline.
/// ],
-/// ) <lab>
+/// )
/// ```
///
/// Display: Figure
@@ -104,224 +80,139 @@ pub struct FigureElem {
/// The figure's caption.
pub caption: Option<Content>,
- /// The figure's supplement, if not provided, the figure will attempt to
- /// automatically detect the counter from the content.
+ /// The kind of the figure this is.
+ ///
+ /// If set to `{auto}`, the figure will try to automatically determine its
+ /// kind. All figures of the same kind share a common counter.
+ ///
+ /// Setting this to something other than `{auto}` will override the
+ /// automatic detection. This can be useful if
+ /// - you wish to create a custom figure type that is not an
+ /// [image]($func/image), a [table]($func/table) or [code]($func/raw),
+ /// - you want to force the figure to use a counter regardless of its
+ /// content.
+ ///
+ /// You can set the kind to be an element function or a string. If you set
+ /// it to an element function that is not supported by the figure, you will
+ /// need to manually specify the figure's supplement.
///
- /// ## Custom figure type
- /// If you are using a custom figure type and would like to figure to be
- /// referenced, you will need to manually specify the supplement, using either
- /// a function or a string.
+ /// The figure's automatic detection is based on a priority list to select
+ /// the element that is likely to be the most important one. If the figure's
+ /// body contains multiple valid elements, the one with the highest priority
+ /// is selected. The priority list is as follows:
+ /// - [image]($func/image) is the most important,
+ /// - [code]($func/raw) is the second most important,
+ /// - [table]($func/table) is the least important one.
///
/// ```example
- /// #figure(caption: "My custom figure", kind: "foo", supplement: "Bar")[
- /// #block[ The inside of my custom figure! ]
- /// ]
+ /// #figure(
+ /// circle(radius: 10pt),
+ /// caption: [A curious atom.],
+ /// kind: "atom",
+ /// supplement: [Atom],
+ /// )
/// ```
#[default(Smart::Auto)]
- pub supplement: Smart<Option<Supplement>>,
+ pub kind: Smart<FigureKind>,
- /// Whether the figure should appear in the list of figures/tables/code.
- /// Defaults to `true`.
- #[default(true)]
- pub outlined: bool,
+ /// The figure's supplement.
+ ///
+ /// If set to `{auto}`, the figure will try to automatically determine the
+ /// correct supplement based on the `kind` and the active [text
+ /// language]($func/text.lang). If you are using a custom figure type, you
+ /// will need to manually specify the supplement.
+ ///
+ /// This can also be set to a function that receives the figure's body to
+ /// select the supplement based on the figure's contents.
+ ///
+ /// ```example
+ /// #figure(
+ /// [The contents of my figure!],
+ /// caption: [My custom figure],
+ /// supplement: [Bar],
+ /// kind: "foo",
+ /// )
+ /// ```
+ #[default(Smart::Auto)]
+ pub supplement: Smart<Supplement>,
/// How to number the figure. Accepts a
/// [numbering pattern or function]($func/numbering).
+ ///
+ /// Defaults to `{"1"}`.
#[default(Some(NumberingPattern::from_str("1").unwrap().into()))]
pub numbering: Option<Numbering>,
- /// The type of the figure. Setting this will override the automatic detection.
- ///
- /// This can be useful if you wish to create a custom figure type that is not
- /// an [image]($func/image), a [table]($func/table) or a [code]($func/raw). Or if
- /// you want to force the figure to use a specific type regardless of its content.
- ///
- /// You can set the kind to be an element, or a string. If you set it to be
- /// a string or an element that is not supported by the figure, you will need to
- /// manually specify the supplement if you wish to number the figure.
- #[default(Smart::Auto)]
- pub kind: Smart<ContentParam>,
-
/// The vertical gap between the body and caption.
#[default(Em::new(0.65).into())]
pub gap: Length,
- /// Convenience field to get access to the figures counter, if any.
- /// If the figure is not numbered, this will be `none`.
- /// Otherwise it will be set to the counter being used by this figure.
- #[synthesized]
- #[internal]
- pub counter: Option<Counter>,
-}
-
-impl FigureElem {
- /// Determines the type of the figure by looking at the content, finding all
- /// [`Figurable`] elements and sorting them by priority then returning the highest.
- pub fn determine_type(
- &self,
- styles: StyleChain,
- require_supplement: bool,
- ) -> Option<Content> {
- let potential_elems = self.body().query(if require_supplement {
- Selector::All(eco_vec![
- Selector::can::<dyn Figurable>(),
- Selector::can::<dyn LocalName>()
- ])
- } else {
- Selector::can::<dyn Figurable>()
- });
-
- potential_elems.into_iter().max_by_key(|elem| {
- elem.with::<dyn Figurable>()
- .expect("should be figurable")
- .priority(styles)
- })
- }
-
- /// Finds the element with the given function in the figure's content.
- /// Returns `None` if no element with the given function is found.
- pub fn find_elem(&self, func: ElemFunc) -> Option<Content> {
- self.body().query(Selector::Elem(func, None)).first().cloned()
- }
-
- /// Builds the supplement and numbering of the figure.
- /// If there is no numbering, returns [`None`].
+ /// Whether the figure should appear in an [`outline`]($func/outline)
+ /// of figures.
///
- /// # Errors
- /// If a numbering is specified but the [`Self::data()`] is `None`.
- pub fn show_supplement_and_numbering(
- &self,
- vt: &mut Vt,
- styles: StyleChain,
- external_supp: Option<Content>,
- ) -> SourceResult<Option<Content>> {
- if let (Some(numbering), Some(supplement), Some(counter)) = (
- self.numbering(styles),
- self.supplement(styles)
- .as_custom()
- .and_then(|s| s.and_then(Supplement::as_content)),
- self.counter(),
- ) {
- let mut name = external_supp.unwrap_or(supplement);
-
- if !name.is_empty() {
- name += TextElem::packed("\u{a0}");
- }
-
- let number = counter
- .at(vt, self.0.location().expect("missing location"))?
- .display(vt, &numbering)?
- .spanned(self.span());
-
- Ok(Some(name + number))
- } else {
- Ok(None)
- }
- }
+ /// Defaults to `{true}`.
+ #[default(true)]
+ pub outlined: bool,
- /// Builds the caption for the figure.
- /// If there is a numbering, will also try to show the supplement and the numbering.
+ /// Convenience field to get access to the counter for this figure.
///
- /// # Errors
- /// If a numbering is specified but the [`Self::element`] is `None`.
- pub fn show_caption(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
- let Some(mut caption) = self.caption(styles) else {
- return Ok(Content::empty());
- };
-
- if let Some(sup_and_num) = self.show_supplement_and_numbering(vt, styles, None)? {
- caption = sup_and_num + TextElem::packed(": ") + caption;
- }
-
- Ok(caption)
- }
+ /// The counter only depends on the `kind`:
+ /// - For (tables)[$func/table]: `{counter(figure.where(kind: table))}`
+ /// - For (images)[$func/image]: `{counter(figure.where(kind: image))}`
+ /// - For a custom kind: `{counter(figure.where(kind: kind))}`
+ ///
+ /// These are the counters you'll need to modify if you want to skip a
+ /// number or reset the counter.
+ #[synthesized]
+ pub counter: Option<Counter>,
}
impl Synthesize for FigureElem {
fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
- self.push_numbering(self.numbering(styles));
-
- // We get the numbering or `None`.
- let numbering = self.numbering(styles);
- let supplement = self.supplement(styles);
-
- // We get the content or `None`.
- let content = match self.kind(styles) {
- Smart::Auto => match self.determine_type(styles, supplement.is_auto()) {
- Some(ty) => Some(ty),
- None => bail!(
- self.span(),
- "unable to determine figure type, use `kind` to manually specify it"
- ),
- },
- Smart::Custom(ContentParam::Elem(ty)) => self.find_elem(ty),
- Smart::Custom(ContentParam::Name(_)) => None,
+ // Determine the figure's kind.
+ let kind = match self.kind(styles) {
+ Smart::Auto => self
+ .find_figurable(styles)
+ .map(|elem| FigureKind::Elem(elem.func()))
+ .ok_or(
+ "unable to determine the figure's `kind`, please specify it manually",
+ )
+ .at(self.span())?,
+ Smart::Custom(kind) => kind,
};
- if self.kind(styles).is_auto() {
- if let Some(content) = &content {
- self.push_kind(Smart::Custom(ContentParam::Elem(content.func())));
- }
+ let content = match &kind {
+ FigureKind::Elem(func) => self.find_of_elem(*func),
+ FigureKind::Name(_) => None,
}
-
- // The list of choices is the following:
- // 1. If there is a detected content, we use the counter `counter(figure.where(kind: detected_content))`
- // 2. If there is a name/elem, we use the counter `counter(figure.where(kind: name/elem))`
- // 4. We return None.
- let counter = content
- .as_ref()
- .map(Content::func)
- .map(Value::from)
- .or_else(|| self.kind(styles).as_custom().map(Value::from))
- .map(|content| {
- Counter::new(CounterKey::Selector(Selector::Elem(
- Self::func(),
- Some(dict! {
- "kind" => content,
- }),
- )))
- });
-
- // We get the supplement or `None`.
- // The supplement must either be set manually or the content identification
- // must have succeeded.
- let supplement = match supplement {
- Smart::Auto => {
- content.as_ref().and_then(|c| c.with::<dyn LocalName>()).map(|c| {
- Supplement::Content(TextElem::packed(
- c.local_name(TextElem::lang_in(styles)),
- ))
- })
- }
- Smart::Custom(supp) => supp,
+ .unwrap_or_else(|| self.body());
+
+ // We get the supplement or `None`. The supplement must either be set
+ // manually or the content identification must have succeeded.
+ let supplement = match self.supplement(styles) {
+ Smart::Auto => match &kind {
+ FigureKind::Elem(func) => Content::new(*func)
+ .with::<dyn LocalName>()
+ .map(|c| TextElem::packed(c.local_name(TextElem::lang_in(styles))))
+ .ok_or("unable to determine the figure's `supplement`, please specify it manually")
+ .at(self.span())?,
+ FigureKind::Name(_) => bail!(self.span(), "please specify the figure's supplement"),
+ },
+ Smart::Custom(supp) => supp.resolve(vt, [content.into()])?,
};
- // When the user wishes to number their figure, we check whether there is a
- // counter and a supplement. If so, we push the element, which is just a
- // summary of the caption properties. We also push all of the components
- // of the summary for convenient access by the user for `show` rules.
- if let Some(numbering) = numbering {
- let Some(counter) = counter else {
- bail!(self.span(), "numbering a figure requires that is has a kind");
- };
-
- let Some(supplement) = supplement else {
- bail!(self.span(), "numbering a figure requires that is has a supplement");
- };
-
- let supplement = supplement
- .resolve(vt, [content.unwrap_or_else(|| self.body()).into()])?;
-
- self.push_supplement(Smart::Custom(Some(Supplement::Content(
- supplement.clone(),
- ))));
- self.push_counter(Some(counter.clone()));
- self.push_numbering(Some(numbering.clone()));
- } else {
- self.push_supplement(Smart::Custom(None));
- self.push_counter(None);
- self.push_numbering(None);
- }
+ // Construct the figure's counter.
+ let counter = Counter::new(CounterKey::Selector(Selector::Elem(
+ Self::func(),
+ Some(dict! {
+ "kind" => kind.clone(),
+ }),
+ )));
+
+ self.push_kind(Smart::Custom(kind));
+ self.push_supplement(Smart::Custom(Supplement::Content(supplement)));
+ self.push_numbering(self.numbering(styles));
+ self.push_counter(Some(counter));
Ok(())
}
@@ -386,39 +277,109 @@ impl Refable for FigureElem {
self.numbering(styles)
}
- fn counter(&self, _styles: StyleChain) -> Counter {
+ fn counter(&self, _: StyleChain) -> Counter {
self.counter().unwrap_or_else(|| Counter::of(Self::func()))
}
}
-/// The `kind` parameter of [`FigureElem`].
+impl FigureElem {
+ /// Determines the type of the figure by looking at the content, finding all
+ /// [`Figurable`] elements and sorting them by priority then returning the highest.
+ pub fn find_figurable(&self, styles: StyleChain) -> Option<Content> {
+ self.body()
+ .query(Selector::can::<dyn Figurable>())
+ .into_iter()
+ .max_by_key(|elem| elem.with::<dyn Figurable>().unwrap().priority(styles))
+ .cloned()
+ }
+
+ /// Finds the element with the given function in the figure's content.
+ /// Returns `None` if no element with the given function is found.
+ pub fn find_of_elem(&self, func: ElemFunc) -> Option<Content> {
+ self.body()
+ .query(Selector::Elem(func, None))
+ .into_iter()
+ .next()
+ .cloned()
+ }
+
+ /// Builds the supplement and numbering of the figure.
+ /// If there is no numbering, returns [`None`].
+ ///
+ /// # Errors
+ /// If a numbering is specified but the [`Self::data()`] is `None`.
+ pub fn show_supplement_and_numbering(
+ &self,
+ vt: &mut Vt,
+ styles: StyleChain,
+ external_supplement: Option<Content>,
+ ) -> SourceResult<Option<Content>> {
+ if let (Some(numbering), Some(supplement), Some(counter)) = (
+ self.numbering(styles),
+ self.supplement(styles).as_custom().and_then(|s| s.as_content()),
+ self.counter(),
+ ) {
+ let mut name = external_supplement.unwrap_or(supplement);
+ if !name.is_empty() {
+ name += TextElem::packed("\u{a0}");
+ }
+
+ let number = counter
+ .at(vt, self.0.location().unwrap())?
+ .display(vt, &numbering)?
+ .spanned(self.span());
+
+ Ok(Some(name + number))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Builds the caption for the figure.
+ /// If there is a numbering, will also try to show the supplement and the numbering.
+ ///
+ /// # Errors
+ /// If a numbering is specified but the [`Self::element`] is `None`.
+ pub fn show_caption(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
+ let Some(mut caption) = self.caption(styles) else {
+ return Ok(Content::empty());
+ };
+
+ if let Some(sup_and_num) = self.show_supplement_and_numbering(vt, styles, None)? {
+ caption = sup_and_num + TextElem::packed(": ") + caption;
+ }
+
+ Ok(caption)
+ }
+}
+
+/// The `kind` parameter of a [`FigureElem`].
#[derive(Debug, Clone)]
-pub enum ContentParam {
- /// The content is an element function.
+pub enum FigureKind {
+ /// The kind is an element function.
Elem(ElemFunc),
-
- /// The content is a name.
+ /// The kind is a name.
Name(EcoString),
}
cast_from_value! {
- ContentParam,
+ FigureKind,
v: ElemFunc => Self::Elem(v),
v: EcoString => Self::Name(v),
}
cast_to_value! {
- v: ContentParam => match v {
- ContentParam::Elem(v) => v.into(),
- ContentParam::Name(v) => v.into(),
+ v: FigureKind => match v {
+ FigureKind::Elem(v) => v.into(),
+ FigureKind::Name(v) => v.into(),
}
}
-/// An element that can be autodetected in a figure.
-/// This trait is used to determine the type of a figure, its counter, its numbering pattern
-/// and the supplement to use for referencing it and creating the caption.
-/// The element chosen as the figure's content is the one with the highest priority.
-pub trait Figurable {
+/// An element that can be auto-detected in a figure.
+///
+/// This trait is used to determine the type of a figure. The element chosen as
+/// the figure's content is the figurable descendant with the highest priority.
+pub trait Figurable: LocalName {
/// The priority of this element.
fn priority(&self, styles: StyleChain) -> isize;
}
diff --git a/library/src/meta/heading.rs b/library/src/meta/heading.rs
index e4339dc8..dbf58573 100644
--- a/library/src/meta/heading.rs
+++ b/library/src/meta/heading.rs
@@ -101,7 +101,6 @@ impl Synthesize for HeadingElem {
self.push_level(self.level(styles));
self.push_numbering(self.numbering(styles));
self.push_outlined(self.outlined(styles));
-
Ok(())
}
}
@@ -163,7 +162,7 @@ impl Refable for HeadingElem {
styles: StyleChain,
supplement: Option<Content>,
) -> SourceResult<Content> {
- // first we create the supplement of the heading
+ // Create the supplement of the heading.
let mut supplement = if let Some(supplement) = supplement {
supplement
} else {
@@ -178,19 +177,19 @@ impl Refable for HeadingElem {
}
};
- // we append a space if the supplement is not empty
+ // Append a non-breaking space if the supplement is not empty.
if !supplement.is_empty() {
supplement += TextElem::packed('\u{a0}')
};
- // we check for a numbering
+ // Check for a numbering.
let Some(numbering) = self.numbering(styles) else {
bail!(self.span(), "only numbered headings can be referenced");
};
- // we get the counter and display it
+ // Get the counter and display it.
let numbers = Counter::of(Self::func())
- .at(vt, self.0.location().expect("missing location"))?
+ .at(vt, self.0.location().unwrap())?
.display(vt, &numbering.trimmed())?;
Ok(supplement + numbers)
@@ -204,21 +203,21 @@ impl Refable for HeadingElem {
self.numbering(styles)
}
- fn counter(&self, _styles: StyleChain) -> Counter {
+ fn counter(&self, _: StyleChain) -> Counter {
Counter::of(Self::func())
}
fn outline(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Option<Content>> {
- // we check if the heading is outlined
+ // Check whether the heading is outlined.
if !self.outlined(styles) {
return Ok(None);
}
- // We build the numbering followed by the title
+ // Build the numbering followed by the title.
let mut start = self.body();
if let Some(numbering) = self.numbering(StyleChain::default()) {
let numbers = Counter::of(HeadingElem::func())
- .at(vt, self.0.location().expect("missing location"))?
+ .at(vt, self.0.location().unwrap())?
.display(vt, &numbering)?;
start = numbers + SpaceElem::new().pack() + start;
};
diff --git a/library/src/meta/outline.rs b/library/src/meta/outline.rs
index f24df653..21305b09 100644
--- a/library/src/meta/outline.rs
+++ b/library/src/meta/outline.rs
@@ -3,15 +3,12 @@ use crate::layout::{BoxElem, HElem, HideElem, ParbreakElem, RepeatElem};
use crate::prelude::*;
use crate::text::{LinebreakElem, SpaceElem, TextElem};
-/// A section outline / table of contents / table of figures / table of tables / etc.
+/// A table of contents, figures, or other elements.
///
-/// This function generates a list of all headings in the document, up to a
-/// given depth. The [heading]($func/heading) numbering will be reproduced
-/// within the outline.
-///
-/// Alternatively, by setting the `target` parameter, the outline can be used to
-/// generate a list of all figures, tables, code blocks, etc. When the `target` parameter
-/// is set, the `depth` parameter is ignored unless it is set to `heading`.
+/// This function generates a list of all occurances of an element in the
+/// document, up to a given depth. The element's numbering and page number will
+/// be displayed in the outline alongside its title or caption. By default this
+/// generates a table of contents.
///
/// ## Example
/// ```example
@@ -24,13 +21,24 @@ use crate::text::{LinebreakElem, SpaceElem, TextElem};
/// #lorem(10)
/// ```
///
-/// ## Example: List of figures
+/// ## Alternative outlines
+/// By setting the `target` parameter, the outline can be used to generate a
+/// list of other kinds of elements than headings. In the example below, we list
+/// all figures containing images by setting `target` to `{figure.where(kind:
+/// image)}`. We could have also set it to just `figure`, but then the list
+/// would also include figures containing tables or other material. For more
+/// details on the `where` selector, [see here]($type/content.where).
+///
/// ```example
-/// #outline(target: figure.where(kind: image), title: "Table of Figures")
+/// #outline(
+/// title: [List of Figures],
+/// target: figure.where(kind: image),
+/// )
///
-/// #figure(caption: "A nice figure!")[
-/// #image("/tiger.jpg")
-/// ]
+/// #figure(
+/// image("tiger.jpg"),
+/// caption: [A nice figure!],
+/// )
/// ```
///
/// Display: Outline
@@ -39,28 +47,60 @@ use crate::text::{LinebreakElem, SpaceElem, TextElem};
pub struct OutlineElem {
/// The title of the outline.
///
- /// - When set to `{auto}`, an appropriate title for the [text
- /// language]($func/text.lang) will be used. This is the default.
+ /// - When set to `{auto}`, an appropriate title for the
+ /// [text language]($func/text.lang) will be used. This is the default.
/// - When set to `{none}`, the outline will not have a title.
/// - A custom title can be set by passing content.
#[default(Some(Smart::Auto))]
pub title: Option<Smart<Content>>,
- /// The maximum depth up to which headings are included in the outline. When
- /// this argument is `{none}`, all headings are included.
- pub depth: Option<NonZeroUsize>,
-
/// The type of element to include in the outline.
+ ///
+ /// To list figures containing a specific kind of element, like a table, you
+ /// can write `{figure.where(kind: table)}`.
+ ///
+ /// ```example
+ /// #outline(
+ /// title: [List of Tables],
+ /// target: figure.where(kind: table),
+ /// )
+ ///
+ /// #figure(
+ /// table(
+ /// columns: 4,
+ /// [t], [1], [2], [3],
+ /// [y], [0.3], [0.7], [0.5],
+ /// ),
+ /// caption: [Experiment results],
+ /// )
+ /// ```
#[default(Selector::Elem(HeadingElem::func(), Some(dict! { "outlined" => true })))]
pub target: Selector,
- /// Whether to indent the subheadings to align the start of their numbering
+ /// The maximum level up to which elements are included in the outline. When
+ /// this argument is `{none}`, all elements are included.
+ ///
+ /// ```example
+ /// #set heading(numbering: "1.")
+ /// #outline(depth: 2)
+ ///
+ /// = Yes
+ /// Top-level section.
+ ///
+ /// == Still
+ /// Subsection.
+ ///
+ /// === Nope
+ /// Not included.
+ /// ```
+ pub depth: Option<NonZeroUsize>,
+
+ /// Whether to indent the sub-elements to align the start of their numbering
/// with the title of their parents. This will only have an effect if a
/// [heading numbering]($func/heading.numbering) is set.
///
/// ```example
/// #set heading(numbering: "1.a.")
- ///
/// #outline(indent: true)
///
/// = About ACME Corp.
@@ -117,12 +157,11 @@ impl Show for OutlineElem {
};
let location = elem.location().expect("missing location");
-
- if depth < refable.level(styles) {
+ if depth < refable.level(StyleChain::default()) {
continue;
}
- let Some(outline) = refable.outline(vt, styles)? else {
+ let Some(outline) = refable.outline(vt, StyleChain::default())? else {
continue;
};
@@ -149,7 +188,7 @@ impl Show for OutlineElem {
ancestor_refable.numbering(StyleChain::default())
{
let numbers = ancestor_refable
- .counter(styles)
+ .counter(StyleChain::default())
.at(vt, ancestor.location().unwrap())?
.display(vt, &numbering)?;
diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs
index 81fd88b9..03c02eb4 100644
--- a/library/src/meta/reference.rs
+++ b/library/src/meta/reference.rs
@@ -1,4 +1,4 @@
-use super::{BibliographyElem, CiteElem, Counter, Numbering};
+use super::{BibliographyElem, CiteElem, Counter, Figurable, Numbering};
use crate::prelude::*;
/// A reference to a label or bibliography.
@@ -6,9 +6,14 @@ use crate::prelude::*;
/// The reference function produces a textual reference to a label. For example,
/// a reference to a heading will yield an appropriate string such as "Section
/// 1" for a reference to the first heading. The references are also links to
-/// the respective element.
+/// the respective element. Reference syntax can also be used to
+/// [cite]($func/cite) from a bibliography.
///
-/// Reference syntax can also be used to [cite]($func/cite) from a bibliography.
+/// Referenceable elements include [headings]($func/heading),
+/// [figures]($func/figure), and [equations]($func/equation). To create a custom
+/// referenceable element like a theorem, you can create a figure of a custom
+/// [`kind`]($func/figure.kind) and write a show rule for it. In the future,
+/// there might be a more direct way to define a custom referenceable element.
///
/// If you just want to link to a labelled element and not get an automatic
/// textual reference, consider using the [`link`]($func/link) function instead.
@@ -108,7 +113,15 @@ impl Show for RefElem {
let elem = elem.at(self.span())?;
if !elem.can::<dyn Refable>() {
- bail!(self.span(), "cannot reference {}", elem.func().name());
+ 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 supplement = match self.supplement(styles) {
diff --git a/library/src/text/raw.rs b/library/src/text/raw.rs
index 2324eb21..594fd1c6 100644
--- a/library/src/text/raw.rs
+++ b/library/src/text/raw.rs
@@ -124,7 +124,6 @@ impl RawElem {
impl Synthesize for RawElem {
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
self.push_lang(self.lang(styles));
-
Ok(())
}
}