Refactor and document figures
This commit is contained in:
parent
f347ed4314
commit
23715e813e
@ -10,6 +10,9 @@ use crate::prelude::*;
|
|||||||
/// the [grid documentation]($func/grid) for more information on how to size the
|
/// the [grid documentation]($func/grid) for more information on how to size the
|
||||||
/// table tracks.
|
/// table tracks.
|
||||||
///
|
///
|
||||||
|
/// To give a table a caption and make it [referenceable]($func/ref), put it
|
||||||
|
/// into a [figure]($func/figure).
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #table(
|
/// #table(
|
||||||
|
@ -165,7 +165,6 @@ impl Synthesize for EquationElem {
|
|||||||
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
|
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
|
||||||
self.push_block(self.block(styles));
|
self.push_block(self.block(styles));
|
||||||
self.push_numbering(self.numbering(styles));
|
self.push_numbering(self.numbering(styles));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -317,7 +316,7 @@ impl Refable for EquationElem {
|
|||||||
self.numbering(styles)
|
self.numbering(styles)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn counter(&self, _styles: StyleChain) -> Counter {
|
fn counter(&self, _: StyleChain) -> Counter {
|
||||||
Counter::of(Self::func())
|
Counter::of(Self::func())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,6 @@ impl BibliographyElem {
|
|||||||
impl Synthesize for BibliographyElem {
|
impl Synthesize for BibliographyElem {
|
||||||
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
|
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
|
||||||
self.push_style(self.style(styles));
|
self.push_style(self.style(styles));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -322,7 +321,6 @@ impl Synthesize for CiteElem {
|
|||||||
self.push_supplement(self.supplement(styles));
|
self.push_supplement(self.supplement(styles));
|
||||||
self.push_brackets(self.brackets(styles));
|
self.push_brackets(self.brackets(styles));
|
||||||
self.push_style(self.style(styles));
|
self.push_style(self.style(styles));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -496,9 +496,7 @@ cast_from_value! {
|
|||||||
|
|
||||||
Self::Selector(Selector::Elem(element, None))
|
Self::Selector(Selector::Elem(element, None))
|
||||||
},
|
},
|
||||||
selector: Selector => {
|
selector: Selector => Self::Selector(selector),
|
||||||
Self::Selector(selector)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for CounterKey {
|
impl Debug for CounterKey {
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use ecow::eco_vec;
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Count, Counter, CounterKey, CounterUpdate, LocalName, Numbering, NumberingPattern,
|
Count, Counter, CounterKey, CounterUpdate, LocalName, Numbering, NumberingPattern,
|
||||||
};
|
};
|
||||||
@ -12,85 +10,63 @@ use crate::text::TextElem;
|
|||||||
|
|
||||||
/// A figure with an optional caption.
|
/// A figure with an optional caption.
|
||||||
///
|
///
|
||||||
/// ## Content detection
|
/// Automatically detects its contents to select the correct counting track.
|
||||||
/// By default, the figure will attempt to automatically detect the content
|
/// For example, figures containing images will be numbered separately from
|
||||||
/// and use a priority list to detect which content is likely
|
/// figures containing tables.
|
||||||
/// 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.
|
|
||||||
///
|
///
|
||||||
|
/// ## Examples
|
||||||
|
/// The example below shows a basic figure with an image:
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #figure(caption: [ Hello, world! ], kind: table)[
|
/// @glacier shows a glacier. Glaciers
|
||||||
/// #table(
|
/// are complex systems.
|
||||||
/// columns: (auto, 1fr),
|
///
|
||||||
/// image("molecular.jpg", width: 32pt),
|
/// #figure(
|
||||||
/// [ A first picture ],
|
/// image("glacier.jpg", width: 80%),
|
||||||
/// image("molecular.jpg", width: 32pt),
|
/// caption: [A curious figure.],
|
||||||
/// [ A second picture ],
|
/// ) <glacier>
|
||||||
/// )
|
|
||||||
/// ]
|
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// If you use an element that is not supported by the figure, and set it as its `content` parameter,
|
/// You can also insert [tables]($func/table) into figures to give them a
|
||||||
/// to be able to make an outline or reference it, you will need to manually specify the supplement
|
/// caption. The figure will detect this and automatically use a separate
|
||||||
/// and counter. Otherwise the figure will produce an error.
|
/// counter.
|
||||||
///
|
///
|
||||||
/// ## 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.
|
|
||||||
///
|
|
||||||
/// The overriding of these values is done as follows:
|
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #figure(caption: [ Hello, world! ], kind: "hello", supplement: "Molecule")[
|
/// #figure(
|
||||||
/// #image("molecular.jpg", width: 32pt)
|
/// table(
|
||||||
/// ]
|
/// columns: 4,
|
||||||
|
/// [t], [1], [2], [3],
|
||||||
|
/// [y], [0.3s], [0.4s], [0.8s],
|
||||||
|
/// ),
|
||||||
|
/// caption: [Timing results],
|
||||||
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// The default counters are defined as follows:
|
/// This behaviour can be overridden by explictly specifying the figure's
|
||||||
/// - for (tables)[$func/table]: `counter(figure.where(kind: table))`
|
/// `kind`. All figures of the same kind share a common counter.
|
||||||
/// - 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
|
/// ## Modifying the appearance
|
||||||
/// counting behaviour of figures.
|
/// 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.
|
||||||
///
|
///
|
||||||
/// ## Numbering
|
|
||||||
/// By default, the figure will be numbered using the `1` [numbering pattern]($func/numbering).
|
|
||||||
/// This can be overridden by using the `numbering` parameter.
|
|
||||||
///
|
|
||||||
/// ## 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`.
|
|
||||||
///
|
|
||||||
/// ## 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
|
/// ```example
|
||||||
/// = Pipeline
|
/// #show figure: it => align(center)[
|
||||||
/// @lab shows the central step of
|
/// #it.caption |
|
||||||
/// our molecular testing pipeline.
|
/// #emph[
|
||||||
|
/// #it.supplement
|
||||||
|
/// #it.counter.display(it.numbering)
|
||||||
|
/// ]
|
||||||
|
/// #v(10pt, weak: true)
|
||||||
|
/// #it.body
|
||||||
|
/// ]
|
||||||
///
|
///
|
||||||
/// #figure(
|
/// #figure(
|
||||||
/// image("molecular.jpg", width: 80%),
|
/// image("molecular.jpg", width: 80%),
|
||||||
/// caption: [
|
/// caption: [
|
||||||
/// The molecular testing pipeline.
|
/// The molecular testing pipeline.
|
||||||
/// ],
|
/// ],
|
||||||
/// ) <lab>
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Display: Figure
|
/// Display: Figure
|
||||||
@ -104,224 +80,139 @@ pub struct FigureElem {
|
|||||||
/// The figure's caption.
|
/// The figure's caption.
|
||||||
pub caption: Option<Content>,
|
pub caption: Option<Content>,
|
||||||
|
|
||||||
/// The figure's supplement, if not provided, the figure will attempt to
|
/// The kind of the figure this is.
|
||||||
/// automatically detect the counter from the content.
|
|
||||||
///
|
///
|
||||||
/// ## Custom figure type
|
/// If set to `{auto}`, the figure will try to automatically determine its
|
||||||
/// If you are using a custom figure type and would like to figure to be
|
/// kind. All figures of the same kind share a common counter.
|
||||||
/// referenced, you will need to manually specify the supplement, using either
|
///
|
||||||
/// a function or a string.
|
/// 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.
|
||||||
|
///
|
||||||
|
/// 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
|
/// ```example
|
||||||
/// #figure(caption: "My custom figure", kind: "foo", supplement: "Bar")[
|
/// #figure(
|
||||||
/// #block[ The inside of my custom figure! ]
|
/// circle(radius: 10pt),
|
||||||
/// ]
|
/// caption: [A curious atom.],
|
||||||
|
/// kind: "atom",
|
||||||
|
/// supplement: [Atom],
|
||||||
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
#[default(Smart::Auto)]
|
#[default(Smart::Auto)]
|
||||||
pub supplement: Smart<Option<Supplement>>,
|
pub kind: Smart<FigureKind>,
|
||||||
|
|
||||||
/// Whether the figure should appear in the list of figures/tables/code.
|
/// The figure's supplement.
|
||||||
/// Defaults to `true`.
|
///
|
||||||
#[default(true)]
|
/// If set to `{auto}`, the figure will try to automatically determine the
|
||||||
pub outlined: bool,
|
/// 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
|
/// How to number the figure. Accepts a
|
||||||
/// [numbering pattern or function]($func/numbering).
|
/// [numbering pattern or function]($func/numbering).
|
||||||
|
///
|
||||||
|
/// Defaults to `{"1"}`.
|
||||||
#[default(Some(NumberingPattern::from_str("1").unwrap().into()))]
|
#[default(Some(NumberingPattern::from_str("1").unwrap().into()))]
|
||||||
pub numbering: Option<Numbering>,
|
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.
|
/// The vertical gap between the body and caption.
|
||||||
#[default(Em::new(0.65).into())]
|
#[default(Em::new(0.65).into())]
|
||||||
pub gap: Length,
|
pub gap: Length,
|
||||||
|
|
||||||
/// Convenience field to get access to the figures counter, if any.
|
/// Whether the figure should appear in an [`outline`]($func/outline)
|
||||||
/// If the figure is not numbered, this will be `none`.
|
/// of figures.
|
||||||
/// Otherwise it will be set to the counter being used by this figure.
|
///
|
||||||
|
/// Defaults to `{true}`.
|
||||||
|
#[default(true)]
|
||||||
|
pub outlined: bool,
|
||||||
|
|
||||||
|
/// Convenience field to get access to the counter for this figure.
|
||||||
|
///
|
||||||
|
/// 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]
|
#[synthesized]
|
||||||
#[internal]
|
|
||||||
pub counter: Option<Counter>,
|
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`].
|
|
||||||
///
|
|
||||||
/// # 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Synthesize for FigureElem {
|
impl Synthesize for FigureElem {
|
||||||
fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
|
fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
|
||||||
self.push_numbering(self.numbering(styles));
|
// 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,
|
||||||
|
};
|
||||||
|
|
||||||
// We get the numbering or `None`.
|
let content = match &kind {
|
||||||
let numbering = self.numbering(styles);
|
FigureKind::Elem(func) => self.find_of_elem(*func),
|
||||||
let supplement = self.supplement(styles);
|
FigureKind::Name(_) => None,
|
||||||
|
}
|
||||||
|
.unwrap_or_else(|| self.body());
|
||||||
|
|
||||||
// We get the content or `None`.
|
// We get the supplement or `None`. The supplement must either be set
|
||||||
let content = match self.kind(styles) {
|
// manually or the content identification must have succeeded.
|
||||||
Smart::Auto => match self.determine_type(styles, supplement.is_auto()) {
|
let supplement = match self.supplement(styles) {
|
||||||
Some(ty) => Some(ty),
|
Smart::Auto => match &kind {
|
||||||
None => bail!(
|
FigureKind::Elem(func) => Content::new(*func)
|
||||||
self.span(),
|
.with::<dyn LocalName>()
|
||||||
"unable to determine figure type, use `kind` to manually specify it"
|
.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(ContentParam::Elem(ty)) => self.find_elem(ty),
|
Smart::Custom(supp) => supp.resolve(vt, [content.into()])?,
|
||||||
Smart::Custom(ContentParam::Name(_)) => None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.kind(styles).is_auto() {
|
// Construct the figure's counter.
|
||||||
if let Some(content) = &content {
|
let counter = Counter::new(CounterKey::Selector(Selector::Elem(
|
||||||
self.push_kind(Smart::Custom(ContentParam::Elem(content.func())));
|
Self::func(),
|
||||||
}
|
Some(dict! {
|
||||||
}
|
"kind" => kind.clone(),
|
||||||
|
}),
|
||||||
|
)));
|
||||||
|
|
||||||
// The list of choices is the following:
|
self.push_kind(Smart::Custom(kind));
|
||||||
// 1. If there is a detected content, we use the counter `counter(figure.where(kind: detected_content))`
|
self.push_supplement(Smart::Custom(Supplement::Content(supplement)));
|
||||||
// 2. If there is a name/elem, we use the counter `counter(figure.where(kind: name/elem))`
|
self.push_numbering(self.numbering(styles));
|
||||||
// 4. We return None.
|
self.push_counter(Some(counter));
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -386,39 +277,109 @@ impl Refable for FigureElem {
|
|||||||
self.numbering(styles)
|
self.numbering(styles)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn counter(&self, _styles: StyleChain) -> Counter {
|
fn counter(&self, _: StyleChain) -> Counter {
|
||||||
self.counter().unwrap_or_else(|| Counter::of(Self::func()))
|
self.counter().unwrap_or_else(|| Counter::of(Self::func()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `kind` parameter of [`FigureElem`].
|
impl FigureElem {
|
||||||
#[derive(Debug, Clone)]
|
/// Determines the type of the figure by looking at the content, finding all
|
||||||
pub enum ContentParam {
|
/// [`Figurable`] elements and sorting them by priority then returning the highest.
|
||||||
/// The content is an element function.
|
pub fn find_figurable(&self, styles: StyleChain) -> Option<Content> {
|
||||||
Elem(ElemFunc),
|
self.body()
|
||||||
|
.query(Selector::can::<dyn Figurable>())
|
||||||
|
.into_iter()
|
||||||
|
.max_by_key(|elem| elem.with::<dyn Figurable>().unwrap().priority(styles))
|
||||||
|
.cloned()
|
||||||
|
}
|
||||||
|
|
||||||
/// The content is a name.
|
/// 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 FigureKind {
|
||||||
|
/// The kind is an element function.
|
||||||
|
Elem(ElemFunc),
|
||||||
|
/// The kind is a name.
|
||||||
Name(EcoString),
|
Name(EcoString),
|
||||||
}
|
}
|
||||||
|
|
||||||
cast_from_value! {
|
cast_from_value! {
|
||||||
ContentParam,
|
FigureKind,
|
||||||
v: ElemFunc => Self::Elem(v),
|
v: ElemFunc => Self::Elem(v),
|
||||||
v: EcoString => Self::Name(v),
|
v: EcoString => Self::Name(v),
|
||||||
}
|
}
|
||||||
|
|
||||||
cast_to_value! {
|
cast_to_value! {
|
||||||
v: ContentParam => match v {
|
v: FigureKind => match v {
|
||||||
ContentParam::Elem(v) => v.into(),
|
FigureKind::Elem(v) => v.into(),
|
||||||
ContentParam::Name(v) => v.into(),
|
FigureKind::Name(v) => v.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An element that can be autodetected in a figure.
|
/// An element that can be auto-detected 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.
|
/// This trait is used to determine the type of a figure. The element chosen as
|
||||||
/// The element chosen as the figure's content is the one with the highest priority.
|
/// the figure's content is the figurable descendant with the highest priority.
|
||||||
pub trait Figurable {
|
pub trait Figurable: LocalName {
|
||||||
/// The priority of this element.
|
/// The priority of this element.
|
||||||
fn priority(&self, styles: StyleChain) -> isize;
|
fn priority(&self, styles: StyleChain) -> isize;
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,6 @@ impl Synthesize for HeadingElem {
|
|||||||
self.push_level(self.level(styles));
|
self.push_level(self.level(styles));
|
||||||
self.push_numbering(self.numbering(styles));
|
self.push_numbering(self.numbering(styles));
|
||||||
self.push_outlined(self.outlined(styles));
|
self.push_outlined(self.outlined(styles));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,7 +162,7 @@ impl Refable for HeadingElem {
|
|||||||
styles: StyleChain,
|
styles: StyleChain,
|
||||||
supplement: Option<Content>,
|
supplement: Option<Content>,
|
||||||
) -> SourceResult<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 {
|
let mut supplement = if let Some(supplement) = supplement {
|
||||||
supplement
|
supplement
|
||||||
} else {
|
} 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() {
|
if !supplement.is_empty() {
|
||||||
supplement += TextElem::packed('\u{a0}')
|
supplement += TextElem::packed('\u{a0}')
|
||||||
};
|
};
|
||||||
|
|
||||||
// we check for a numbering
|
// Check for a numbering.
|
||||||
let Some(numbering) = self.numbering(styles) else {
|
let Some(numbering) = self.numbering(styles) else {
|
||||||
bail!(self.span(), "only numbered headings can be referenced");
|
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())
|
let numbers = Counter::of(Self::func())
|
||||||
.at(vt, self.0.location().expect("missing location"))?
|
.at(vt, self.0.location().unwrap())?
|
||||||
.display(vt, &numbering.trimmed())?;
|
.display(vt, &numbering.trimmed())?;
|
||||||
|
|
||||||
Ok(supplement + numbers)
|
Ok(supplement + numbers)
|
||||||
@ -204,21 +203,21 @@ impl Refable for HeadingElem {
|
|||||||
self.numbering(styles)
|
self.numbering(styles)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn counter(&self, _styles: StyleChain) -> Counter {
|
fn counter(&self, _: StyleChain) -> Counter {
|
||||||
Counter::of(Self::func())
|
Counter::of(Self::func())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn outline(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Option<Content>> {
|
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) {
|
if !self.outlined(styles) {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We build the numbering followed by the title
|
// Build the numbering followed by the title.
|
||||||
let mut start = self.body();
|
let mut start = self.body();
|
||||||
if let Some(numbering) = self.numbering(StyleChain::default()) {
|
if let Some(numbering) = self.numbering(StyleChain::default()) {
|
||||||
let numbers = Counter::of(HeadingElem::func())
|
let numbers = Counter::of(HeadingElem::func())
|
||||||
.at(vt, self.0.location().expect("missing location"))?
|
.at(vt, self.0.location().unwrap())?
|
||||||
.display(vt, &numbering)?;
|
.display(vt, &numbering)?;
|
||||||
start = numbers + SpaceElem::new().pack() + start;
|
start = numbers + SpaceElem::new().pack() + start;
|
||||||
};
|
};
|
||||||
|
@ -3,15 +3,12 @@ use crate::layout::{BoxElem, HElem, HideElem, ParbreakElem, RepeatElem};
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::text::{LinebreakElem, SpaceElem, TextElem};
|
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
|
/// This function generates a list of all occurances of an element in the
|
||||||
/// given depth. The [heading]($func/heading) numbering will be reproduced
|
/// document, up to a given depth. The element's numbering and page number will
|
||||||
/// within the outline.
|
/// be displayed in the outline alongside its title or caption. By default this
|
||||||
///
|
/// generates a table of contents.
|
||||||
/// 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`.
|
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```example
|
/// ```example
|
||||||
@ -24,13 +21,24 @@ use crate::text::{LinebreakElem, SpaceElem, TextElem};
|
|||||||
/// #lorem(10)
|
/// #lorem(10)
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ## Example: List of figures
|
/// ## Alternative outlines
|
||||||
/// ```example
|
/// By setting the `target` parameter, the outline can be used to generate a
|
||||||
/// #outline(target: figure.where(kind: image), title: "Table of Figures")
|
/// 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).
|
||||||
///
|
///
|
||||||
/// #figure(caption: "A nice figure!")[
|
/// ```example
|
||||||
/// #image("/tiger.jpg")
|
/// #outline(
|
||||||
/// ]
|
/// title: [List of Figures],
|
||||||
|
/// target: figure.where(kind: image),
|
||||||
|
/// )
|
||||||
|
///
|
||||||
|
/// #figure(
|
||||||
|
/// image("tiger.jpg"),
|
||||||
|
/// caption: [A nice figure!],
|
||||||
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Display: Outline
|
/// Display: Outline
|
||||||
@ -39,28 +47,60 @@ use crate::text::{LinebreakElem, SpaceElem, TextElem};
|
|||||||
pub struct OutlineElem {
|
pub struct OutlineElem {
|
||||||
/// The title of the outline.
|
/// The title of the outline.
|
||||||
///
|
///
|
||||||
/// - When set to `{auto}`, an appropriate title for the [text
|
/// - When set to `{auto}`, an appropriate title for the
|
||||||
/// language]($func/text.lang) will be used. This is the default.
|
/// [text language]($func/text.lang) will be used. This is the default.
|
||||||
/// - When set to `{none}`, the outline will not have a title.
|
/// - When set to `{none}`, the outline will not have a title.
|
||||||
/// - A custom title can be set by passing content.
|
/// - A custom title can be set by passing content.
|
||||||
#[default(Some(Smart::Auto))]
|
#[default(Some(Smart::Auto))]
|
||||||
pub title: Option<Smart<Content>>,
|
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.
|
/// 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 })))]
|
#[default(Selector::Elem(HeadingElem::func(), Some(dict! { "outlined" => true })))]
|
||||||
pub target: Selector,
|
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
|
/// with the title of their parents. This will only have an effect if a
|
||||||
/// [heading numbering]($func/heading.numbering) is set.
|
/// [heading numbering]($func/heading.numbering) is set.
|
||||||
///
|
///
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #set heading(numbering: "1.a.")
|
/// #set heading(numbering: "1.a.")
|
||||||
///
|
|
||||||
/// #outline(indent: true)
|
/// #outline(indent: true)
|
||||||
///
|
///
|
||||||
/// = About ACME Corp.
|
/// = About ACME Corp.
|
||||||
@ -117,12 +157,11 @@ impl Show for OutlineElem {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let location = elem.location().expect("missing location");
|
let location = elem.location().expect("missing location");
|
||||||
|
if depth < refable.level(StyleChain::default()) {
|
||||||
if depth < refable.level(styles) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(outline) = refable.outline(vt, styles)? else {
|
let Some(outline) = refable.outline(vt, StyleChain::default())? else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -149,7 +188,7 @@ impl Show for OutlineElem {
|
|||||||
ancestor_refable.numbering(StyleChain::default())
|
ancestor_refable.numbering(StyleChain::default())
|
||||||
{
|
{
|
||||||
let numbers = ancestor_refable
|
let numbers = ancestor_refable
|
||||||
.counter(styles)
|
.counter(StyleChain::default())
|
||||||
.at(vt, ancestor.location().unwrap())?
|
.at(vt, ancestor.location().unwrap())?
|
||||||
.display(vt, &numbering)?;
|
.display(vt, &numbering)?;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::{BibliographyElem, CiteElem, Counter, Numbering};
|
use super::{BibliographyElem, CiteElem, Counter, Figurable, Numbering};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
/// A reference to a label or bibliography.
|
/// 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,
|
/// 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
|
/// 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
|
/// 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
|
/// 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.
|
/// textual reference, consider using the [`link`]($func/link) function instead.
|
||||||
@ -108,7 +113,15 @@ impl Show for RefElem {
|
|||||||
|
|
||||||
let elem = elem.at(self.span())?;
|
let elem = elem.at(self.span())?;
|
||||||
if !elem.can::<dyn Refable>() {
|
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) {
|
let supplement = match self.supplement(styles) {
|
||||||
|
@ -124,7 +124,6 @@ impl RawElem {
|
|||||||
impl Synthesize for RawElem {
|
impl Synthesize for RawElem {
|
||||||
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
|
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
|
||||||
self.push_lang(self.lang(styles));
|
self.push_lang(self.lang(styles));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -354,28 +354,11 @@ impl Content {
|
|||||||
self.attrs.push(Attr::Location(location));
|
self.attrs.push(Attr::Location(location));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gives an iterator over the children of this content
|
|
||||||
pub fn children(&self) -> impl Iterator<Item = &Content> {
|
|
||||||
self.attrs.iter().filter_map(Attr::child)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gives an iterator over the children of this content that are contained
|
|
||||||
/// within the arguments of the content.
|
|
||||||
pub fn children_in_args(&self) -> impl Iterator<Item = &Content> {
|
|
||||||
self.attrs
|
|
||||||
.iter()
|
|
||||||
.filter_map(Attr::value)
|
|
||||||
.filter_map(|value| match value {
|
|
||||||
Value::Content(content) => Some(content),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Queries the content tree for all elements that match the given selector.
|
/// Queries the content tree for all elements that match the given selector.
|
||||||
///
|
///
|
||||||
/// # Show rules
|
/// # Show rules
|
||||||
/// Elements produced in `show` rules will not be included in the results.
|
/// Elements produced in `show` rules will not be included in the results.
|
||||||
pub fn query(&self, selector: Selector) -> Vec<Content> {
|
pub fn query(&self, selector: Selector) -> Vec<&Content> {
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
self.query_into(&selector, &mut results);
|
self.query_into(&selector, &mut results);
|
||||||
results
|
results
|
||||||
@ -383,9 +366,9 @@ impl Content {
|
|||||||
|
|
||||||
/// Queries the content tree for all elements that match the given selector
|
/// Queries the content tree for all elements that match the given selector
|
||||||
/// and stores the results inside of the `results` vec.
|
/// and stores the results inside of the `results` vec.
|
||||||
fn query_into(&self, selector: &Selector, results: &mut Vec<Content>) {
|
fn query_into<'a>(&'a self, selector: &Selector, results: &mut Vec<&'a Content>) {
|
||||||
if selector.matches(self) {
|
if selector.matches(self) {
|
||||||
results.push(self.clone());
|
results.push(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
for attr in &self.attrs {
|
for attr in &self.attrs {
|
||||||
@ -397,7 +380,11 @@ impl Content {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Walks a given value to find any content that matches the selector.
|
/// Walks a given value to find any content that matches the selector.
|
||||||
fn walk_value(value: &Value, selector: &Selector, results: &mut Vec<Content>) {
|
fn walk_value<'a>(
|
||||||
|
value: &'a Value,
|
||||||
|
selector: &Selector,
|
||||||
|
results: &mut Vec<&'a Content>,
|
||||||
|
) {
|
||||||
match value {
|
match value {
|
||||||
Value::Content(content) => content.query_into(selector, results),
|
Value::Content(content) => content.query_into(selector, results),
|
||||||
Value::Array(array) => {
|
Value::Array(array) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user