mirror of
https://github.com/typst/typst
synced 2025-05-18 11:05:28 +08:00
Fix and simplify reference supplements
- Fixes #873 by properly handling `none` supplement for`ref`. - Fixes #523 by adding a `supplement` parameter to `math.equation` - In the future, we can remove supplement functions in favor of show-set rules with fine-grained selectors. Currently, this is not possible because show-set + synthesis doesn't play well together
This commit is contained in:
parent
183997d5fe
commit
f4fd6855e7
@ -42,8 +42,10 @@ use self::fragment::*;
|
|||||||
use self::row::*;
|
use self::row::*;
|
||||||
use self::spacing::*;
|
use self::spacing::*;
|
||||||
use crate::layout::{HElem, ParElem, Spacing};
|
use crate::layout::{HElem, ParElem, Spacing};
|
||||||
use crate::meta::Refable;
|
use crate::meta::Supplement;
|
||||||
use crate::meta::{Count, Counter, CounterUpdate, LocalName, Numbering};
|
use crate::meta::{
|
||||||
|
Count, Counter, CounterUpdate, LocalName, Numbering, Outlinable, Refable,
|
||||||
|
};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::text::{
|
use crate::text::{
|
||||||
families, variant, FontFamily, FontList, LinebreakElem, SpaceElem, TextElem, TextSize,
|
families, variant, FontFamily, FontList, LinebreakElem, SpaceElem, TextElem, TextSize,
|
||||||
@ -140,7 +142,8 @@ pub fn module() -> Module {
|
|||||||
/// Display: Equation
|
/// Display: Equation
|
||||||
/// Category: math
|
/// Category: math
|
||||||
#[element(
|
#[element(
|
||||||
Locatable, Synthesize, Show, Finalize, Layout, LayoutMath, Count, LocalName, Refable
|
Locatable, Synthesize, Show, Finalize, Layout, LayoutMath, Count, LocalName, Refable,
|
||||||
|
Outlinable
|
||||||
)]
|
)]
|
||||||
pub struct EquationElem {
|
pub struct EquationElem {
|
||||||
/// Whether the equation is displayed as a separate block.
|
/// Whether the equation is displayed as a separate block.
|
||||||
@ -160,15 +163,44 @@ pub struct EquationElem {
|
|||||||
/// ```
|
/// ```
|
||||||
pub numbering: Option<Numbering>,
|
pub numbering: Option<Numbering>,
|
||||||
|
|
||||||
|
/// A supplement for the equation.
|
||||||
|
///
|
||||||
|
/// For references to equations, this is added before the referenced number.
|
||||||
|
///
|
||||||
|
/// If a function is specified, it is passed the referenced equation and
|
||||||
|
/// should return content.
|
||||||
|
///
|
||||||
|
/// ```example
|
||||||
|
/// #set math.equation(numbering: "(1)", supplement: [Eq.])
|
||||||
|
///
|
||||||
|
/// We define:
|
||||||
|
/// $ phi.alt := (1 + sqrt(5)) / 2 $ <ratio>
|
||||||
|
///
|
||||||
|
/// With @ratio, we get:
|
||||||
|
/// $ F_n = floor(1 / sqrt(5) phi.alt^n) $
|
||||||
|
/// ```
|
||||||
|
pub supplement: Smart<Option<Supplement>>,
|
||||||
|
|
||||||
/// The contents of the equation.
|
/// The contents of the equation.
|
||||||
#[required]
|
#[required]
|
||||||
pub body: Content,
|
pub body: Content,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Synthesize for EquationElem {
|
impl Synthesize for EquationElem {
|
||||||
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
|
fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
|
||||||
|
// Resolve the supplement.
|
||||||
|
let supplement = match self.supplement(styles) {
|
||||||
|
Smart::Auto => TextElem::packed(self.local_name_in(styles)),
|
||||||
|
Smart::Custom(None) => Content::empty(),
|
||||||
|
Smart::Custom(Some(supplement)) => {
|
||||||
|
supplement.resolve(vt, [self.clone().into()])?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
self.push_block(self.block(styles));
|
self.push_block(self.block(styles));
|
||||||
self.push_numbering(self.numbering(styles));
|
self.push_numbering(self.numbering(styles));
|
||||||
|
self.push_supplement(Smart::Custom(Some(Supplement::Content(supplement))));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,41 +334,45 @@ impl LocalName for EquationElem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Refable for EquationElem {
|
impl Refable for EquationElem {
|
||||||
fn reference(
|
fn supplement(&self) -> Content {
|
||||||
&self,
|
// After synthesis, this should always be custom content.
|
||||||
vt: &mut Vt,
|
match self.supplement(StyleChain::default()) {
|
||||||
supplement: Option<Content>,
|
Smart::Custom(Some(Supplement::Content(content))) => content,
|
||||||
lang: Lang,
|
_ => Content::empty(),
|
||||||
region: Option<Region>,
|
}
|
||||||
) -> SourceResult<Content> {
|
}
|
||||||
// first we create the supplement of the heading
|
|
||||||
let mut supplement =
|
|
||||||
supplement.unwrap_or_else(|| TextElem::packed(self.local_name(lang, region)));
|
|
||||||
|
|
||||||
// we append a space if the supplement is not empty
|
fn counter(&self) -> Counter {
|
||||||
if !supplement.is_empty() {
|
Counter::of(Self::func())
|
||||||
supplement += TextElem::packed('\u{a0}')
|
|
||||||
};
|
|
||||||
|
|
||||||
// we check for a numbering
|
|
||||||
let Some(numbering) = self.numbering(StyleChain::default()) else {
|
|
||||||
bail!(self.span(), "only numbered equations can be referenced");
|
|
||||||
};
|
|
||||||
|
|
||||||
// we get the counter and display it
|
|
||||||
let numbers = Counter::of(Self::func())
|
|
||||||
.at(vt, self.0.location().expect("missing location"))?
|
|
||||||
.display(vt, &numbering.trimmed())?;
|
|
||||||
|
|
||||||
Ok(supplement + numbers)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn numbering(&self) -> Option<Numbering> {
|
fn numbering(&self) -> Option<Numbering> {
|
||||||
self.numbering(StyleChain::default())
|
self.numbering(StyleChain::default())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn counter(&self) -> Counter {
|
impl Outlinable for EquationElem {
|
||||||
Counter::of(Self::func())
|
fn outline(&self, vt: &mut Vt) -> SourceResult<Option<Content>> {
|
||||||
|
let Some(numbering) = self.numbering(StyleChain::default()) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
// After synthesis, this should always be custom content.
|
||||||
|
let mut supplement = match self.supplement(StyleChain::default()) {
|
||||||
|
Smart::Custom(Some(Supplement::Content(content))) => content,
|
||||||
|
_ => Content::empty(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !supplement.is_empty() {
|
||||||
|
supplement += TextElem::packed("\u{a0}");
|
||||||
|
}
|
||||||
|
|
||||||
|
let numbers = self
|
||||||
|
.counter()
|
||||||
|
.at(vt, self.0.location().unwrap())?
|
||||||
|
.display(vt, &numbering)?;
|
||||||
|
|
||||||
|
Ok(Some(supplement + numbers))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ use super::{
|
|||||||
Count, Counter, CounterKey, CounterUpdate, LocalName, Numbering, NumberingPattern,
|
Count, Counter, CounterKey, CounterUpdate, LocalName, Numbering, NumberingPattern,
|
||||||
};
|
};
|
||||||
use crate::layout::{BlockElem, VElem};
|
use crate::layout::{BlockElem, VElem};
|
||||||
use crate::meta::{Refable, Supplement};
|
use crate::meta::{Outlinable, Refable, Supplement};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::text::TextElem;
|
use crate::text::TextElem;
|
||||||
use crate::visualize::ImageElem;
|
use crate::visualize::ImageElem;
|
||||||
@ -77,7 +77,7 @@ use crate::visualize::ImageElem;
|
|||||||
///
|
///
|
||||||
/// Display: Figure
|
/// Display: Figure
|
||||||
/// Category: meta
|
/// Category: meta
|
||||||
#[element(Locatable, Synthesize, Count, Show, Finalize, Refable)]
|
#[element(Locatable, Synthesize, Count, Show, Finalize, Refable, Outlinable)]
|
||||||
pub struct FigureElem {
|
pub struct FigureElem {
|
||||||
/// The content of the figure. Often, an [image]($func/image).
|
/// The content of the figure. Often, an [image]($func/image).
|
||||||
#[required]
|
#[required]
|
||||||
@ -120,8 +120,9 @@ pub struct FigureElem {
|
|||||||
/// language]($func/text.lang). If you are using a custom figure type, you
|
/// language]($func/text.lang). If you are using a custom figure type, you
|
||||||
/// will need to manually specify the supplement.
|
/// will need to manually specify the supplement.
|
||||||
///
|
///
|
||||||
/// This can also be set to a function that receives the figure's body to
|
/// If a function is specified, it is passed the first descendant of the
|
||||||
/// select the supplement based on the figure's contents.
|
/// specified `kind` (typically, the figure's body) and should return
|
||||||
|
/// content.
|
||||||
///
|
///
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #figure(
|
/// #figure(
|
||||||
@ -131,8 +132,7 @@ pub struct FigureElem {
|
|||||||
/// kind: "foo",
|
/// kind: "foo",
|
||||||
/// )
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
#[default(Smart::Auto)]
|
pub supplement: Smart<Option<Supplement>>,
|
||||||
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).
|
||||||
@ -163,52 +163,54 @@ pub struct FigureElem {
|
|||||||
|
|
||||||
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<()> {
|
||||||
// Determine the figure's kind.
|
|
||||||
let kind = match self.kind(styles) {
|
|
||||||
Smart::Auto => self
|
|
||||||
.find_figurable()
|
|
||||||
.map(|elem| FigureKind::Elem(elem.func()))
|
|
||||||
.unwrap_or_else(|| FigureKind::Elem(ImageElem::func())),
|
|
||||||
Smart::Custom(kind) => kind,
|
|
||||||
};
|
|
||||||
|
|
||||||
let content = match &kind {
|
|
||||||
FigureKind::Elem(func) => self.find_of_elem(*func),
|
|
||||||
FigureKind::Name(_) => None,
|
|
||||||
}
|
|
||||||
.unwrap_or_else(|| self.body());
|
|
||||||
|
|
||||||
let numbering = self.numbering(styles);
|
let numbering = self.numbering(styles);
|
||||||
|
|
||||||
// We get the supplement or `None`. The supplement must either be set
|
// Determine the figure's kind.
|
||||||
// manually or the content identification must have succeeded.
|
let kind = self.kind(styles).unwrap_or_else(|| {
|
||||||
let supplement = match self.supplement(styles) {
|
self.body()
|
||||||
Smart::Auto => match &kind {
|
.query_first(Selector::can::<dyn Figurable>())
|
||||||
FigureKind::Elem(func) => {
|
.cloned()
|
||||||
let elem = Content::new(*func).with::<dyn LocalName>().map(|c| {
|
.map(|elem| FigureKind::Elem(elem.func()))
|
||||||
TextElem::packed(c.local_name(
|
.unwrap_or_else(|| FigureKind::Elem(ImageElem::func()))
|
||||||
TextElem::lang_in(styles),
|
});
|
||||||
TextElem::region_in(styles),
|
|
||||||
))
|
|
||||||
});
|
|
||||||
|
|
||||||
if numbering.is_some() {
|
// Resolve the supplement.
|
||||||
Some(elem
|
let supplement = match self.supplement(styles) {
|
||||||
.ok_or("unable to determine the figure's `supplement`, please specify it manually")
|
Smart::Auto => {
|
||||||
.at(self.span())?)
|
// Default to the local name for the kind, if available.
|
||||||
} else {
|
let name = match &kind {
|
||||||
elem
|
FigureKind::Elem(func) => {
|
||||||
|
let empty = Content::new(*func);
|
||||||
|
empty.with::<dyn LocalName>().map(|c| {
|
||||||
|
TextElem::packed(c.local_name(
|
||||||
|
TextElem::lang_in(styles),
|
||||||
|
TextElem::region_in(styles),
|
||||||
|
))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
FigureKind::Name(_) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if numbering.is_some() && name.is_none() {
|
||||||
|
bail!(self.span(), "please specify the figure's supplement")
|
||||||
}
|
}
|
||||||
FigureKind::Name(_) => {
|
|
||||||
if numbering.is_some() {
|
name.unwrap_or_default()
|
||||||
bail!(self.span(), "please specify the figure's supplement")
|
}
|
||||||
} else {
|
Smart::Custom(None) => Content::empty(),
|
||||||
None
|
Smart::Custom(Some(supplement)) => {
|
||||||
|
// Resolve the supplement with the first descendant of the kind or
|
||||||
|
// just the body, if none was found.
|
||||||
|
let descendant = match kind {
|
||||||
|
FigureKind::Elem(func) => {
|
||||||
|
self.body().query_first(Selector::Elem(func, None)).cloned()
|
||||||
}
|
}
|
||||||
}
|
FigureKind::Name(_) => None,
|
||||||
},
|
};
|
||||||
Smart::Custom(supp) => Some(supp.resolve(vt, [content.into()])?),
|
|
||||||
|
let target = descendant.unwrap_or_else(|| self.body());
|
||||||
|
supplement.resolve(vt, [target.into()])?
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Construct the figure's counter.
|
// Construct the figure's counter.
|
||||||
@ -221,9 +223,7 @@ impl Synthesize for FigureElem {
|
|||||||
|
|
||||||
self.push_caption(self.caption(styles));
|
self.push_caption(self.caption(styles));
|
||||||
self.push_kind(Smart::Custom(kind));
|
self.push_kind(Smart::Custom(kind));
|
||||||
self.push_supplement(Smart::Custom(Supplement::Content(
|
self.push_supplement(Smart::Custom(Some(Supplement::Content(supplement))));
|
||||||
supplement.unwrap_or_default(),
|
|
||||||
)));
|
|
||||||
self.push_numbering(numbering);
|
self.push_numbering(numbering);
|
||||||
self.push_outlined(self.outlined(styles));
|
self.push_outlined(self.outlined(styles));
|
||||||
self.push_counter(Some(counter));
|
self.push_counter(Some(counter));
|
||||||
@ -235,16 +235,15 @@ impl Synthesize for FigureElem {
|
|||||||
impl Show for FigureElem {
|
impl Show for FigureElem {
|
||||||
#[tracing::instrument(name = "FigureElem::show", skip_all)]
|
#[tracing::instrument(name = "FigureElem::show", skip_all)]
|
||||||
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
|
fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> {
|
||||||
// We build the body of the figure.
|
|
||||||
let mut realized = self.body();
|
let mut realized = self.body();
|
||||||
|
|
||||||
// We build the caption, if any.
|
// Build the caption, if any.
|
||||||
if self.caption(styles).is_some() {
|
if let Some(caption) = self.full_caption(vt)? {
|
||||||
realized += VElem::weak(self.gap(styles).into()).pack();
|
realized += VElem::weak(self.gap(styles).into()).pack();
|
||||||
realized += self.show_caption(vt)?;
|
realized += caption;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We wrap the contents in a block.
|
// Wrap the contents in a block.
|
||||||
Ok(BlockElem::new()
|
Ok(BlockElem::new()
|
||||||
.with_body(Some(realized))
|
.with_body(Some(realized))
|
||||||
.pack()
|
.pack()
|
||||||
@ -270,100 +269,60 @@ impl Count for FigureElem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Refable for FigureElem {
|
impl Refable for FigureElem {
|
||||||
fn reference(
|
fn supplement(&self) -> Content {
|
||||||
&self,
|
// After synthesis, this should always be custom content.
|
||||||
vt: &mut Vt,
|
match self.supplement(StyleChain::default()) {
|
||||||
supplement: Option<Content>,
|
Smart::Custom(Some(Supplement::Content(content))) => content,
|
||||||
_: Lang,
|
_ => Content::empty(),
|
||||||
_: Option<Region>,
|
|
||||||
) -> SourceResult<Content> {
|
|
||||||
// If the figure is not numbered, we cannot reference it.
|
|
||||||
// Otherwise we build the supplement and numbering scheme.
|
|
||||||
let Some(desc) = self.show_supplement_and_numbering(vt, supplement)? else {
|
|
||||||
bail!(self.span(), "cannot reference unnumbered figure")
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(desc)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn outline(
|
|
||||||
&self,
|
|
||||||
vt: &mut Vt,
|
|
||||||
_: Lang,
|
|
||||||
_: Option<Region>,
|
|
||||||
) -> SourceResult<Option<Content>> {
|
|
||||||
// If the figure is not outlined, it is not referenced.
|
|
||||||
if !self.outlined(StyleChain::default()) {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.show_caption(vt).map(Some)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn numbering(&self) -> Option<Numbering> {
|
|
||||||
self.numbering(StyleChain::default())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn counter(&self) -> Counter {
|
fn counter(&self) -> Counter {
|
||||||
self.counter().unwrap_or_else(|| Counter::of(Self::func()))
|
self.counter().unwrap_or_else(|| Counter::of(Self::func()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn numbering(&self) -> Option<Numbering> {
|
||||||
|
self.numbering(StyleChain::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Outlinable for FigureElem {
|
||||||
|
fn outline(&self, vt: &mut Vt) -> SourceResult<Option<Content>> {
|
||||||
|
if !self.outlined(StyleChain::default()) {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.full_caption(vt)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FigureElem {
|
impl FigureElem {
|
||||||
/// Determines the type of the figure by looking at the content, finding all
|
/// Builds the full caption for the figure (with supplement and numbering).
|
||||||
/// [`Figurable`] elements and sorting them by priority then returning the highest.
|
pub fn full_caption(&self, vt: &mut Vt) -> SourceResult<Option<Content>> {
|
||||||
pub fn find_figurable(&self) -> Option<Content> {
|
|
||||||
self.body().query_first(Selector::can::<dyn Figurable>()).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_first(Selector::Elem(func, None)).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds the supplement and numbering of the figure. Returns [`None`] if
|
|
||||||
/// there is no numbering.
|
|
||||||
pub fn show_supplement_and_numbering(
|
|
||||||
&self,
|
|
||||||
vt: &mut Vt,
|
|
||||||
external_supplement: Option<Content>,
|
|
||||||
) -> SourceResult<Option<Content>> {
|
|
||||||
if let (Some(numbering), Some(supplement), Some(counter)) = (
|
|
||||||
self.numbering(StyleChain::default()),
|
|
||||||
self.supplement(StyleChain::default())
|
|
||||||
.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.
|
|
||||||
pub fn show_caption(&self, vt: &mut Vt) -> SourceResult<Content> {
|
|
||||||
let Some(mut caption) = self.caption(StyleChain::default()) else {
|
let Some(mut caption) = self.caption(StyleChain::default()) else {
|
||||||
return Ok(Content::empty());
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(sup_and_num) = self.show_supplement_and_numbering(vt, None)? {
|
if let (
|
||||||
caption = sup_and_num + TextElem::packed(": ") + caption;
|
Smart::Custom(Some(Supplement::Content(mut supplement))),
|
||||||
|
Some(counter),
|
||||||
|
Some(numbering),
|
||||||
|
) = (
|
||||||
|
self.supplement(StyleChain::default()),
|
||||||
|
self.counter(),
|
||||||
|
self.numbering(StyleChain::default()),
|
||||||
|
) {
|
||||||
|
let numbers =
|
||||||
|
counter.at(vt, self.0.location().unwrap())?.display(vt, &numbering)?;
|
||||||
|
|
||||||
|
if !supplement.is_empty() {
|
||||||
|
supplement += TextElem::packed("\u{a0}");
|
||||||
|
}
|
||||||
|
|
||||||
|
caption = supplement + numbers + TextElem::packed(": ") + caption;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(caption)
|
Ok(Some(caption))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use typst::font::FontWeight;
|
use typst::font::FontWeight;
|
||||||
use typst::util::option_eq;
|
use typst::util::option_eq;
|
||||||
|
|
||||||
use super::{Counter, CounterUpdate, LocalName, Numbering, Refable};
|
use super::{Counter, CounterUpdate, LocalName, Numbering, Outlinable, Refable};
|
||||||
use crate::layout::{BlockElem, HElem, VElem};
|
use crate::layout::{BlockElem, HElem, VElem};
|
||||||
use crate::meta::{Count, Supplement};
|
use crate::meta::{Count, Supplement};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -42,7 +42,7 @@ use crate::text::{SpaceElem, TextElem, TextSize};
|
|||||||
///
|
///
|
||||||
/// Display: Heading
|
/// Display: Heading
|
||||||
/// Category: meta
|
/// Category: meta
|
||||||
#[element(Locatable, Synthesize, Count, Show, Finalize, LocalName, Refable)]
|
#[element(Locatable, Synthesize, Count, Show, Finalize, LocalName, Refable, Outlinable)]
|
||||||
pub struct HeadingElem {
|
pub struct HeadingElem {
|
||||||
/// The logical nesting depth of the heading, starting from one.
|
/// The logical nesting depth of the heading, starting from one.
|
||||||
#[default(NonZeroUsize::ONE)]
|
#[default(NonZeroUsize::ONE)]
|
||||||
@ -62,11 +62,13 @@ pub struct HeadingElem {
|
|||||||
|
|
||||||
/// A supplement for the heading.
|
/// A supplement for the heading.
|
||||||
///
|
///
|
||||||
/// For references to headings, this is added before the
|
/// For references to headings, this is added before the referenced number.
|
||||||
/// referenced number.
|
///
|
||||||
|
/// If a function is specified, it is passed the referenced heading and
|
||||||
|
/// should return content.
|
||||||
///
|
///
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #set heading(numbering: "1.", supplement: "Chapter")
|
/// #set heading(numbering: "1.", supplement: [Chapter])
|
||||||
///
|
///
|
||||||
/// = Introduction <intro>
|
/// = Introduction <intro>
|
||||||
/// In @intro, we see how to turn
|
/// In @intro, we see how to turn
|
||||||
@ -74,7 +76,6 @@ pub struct HeadingElem {
|
|||||||
/// in @intro[Part], it is done
|
/// in @intro[Part], it is done
|
||||||
/// manually.
|
/// manually.
|
||||||
/// ```
|
/// ```
|
||||||
#[default(Smart::Auto)]
|
|
||||||
pub supplement: Smart<Option<Supplement>>,
|
pub supplement: Smart<Option<Supplement>>,
|
||||||
|
|
||||||
/// Whether the heading should appear in the outline.
|
/// Whether the heading should appear in the outline.
|
||||||
@ -98,12 +99,21 @@ pub struct HeadingElem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Synthesize for HeadingElem {
|
impl Synthesize for HeadingElem {
|
||||||
fn synthesize(&mut self, _vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
|
fn synthesize(&mut self, vt: &mut Vt, styles: StyleChain) -> SourceResult<()> {
|
||||||
|
// Resolve the supplement.
|
||||||
|
let supplement = match self.supplement(styles) {
|
||||||
|
Smart::Auto => TextElem::packed(self.local_name_in(styles)),
|
||||||
|
Smart::Custom(None) => Content::empty(),
|
||||||
|
Smart::Custom(Some(supplement)) => {
|
||||||
|
supplement.resolve(vt, [self.clone().into()])?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
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_supplement(self.supplement(styles));
|
self.push_supplement(Smart::Custom(Some(Supplement::Content(supplement))));
|
||||||
self.push_outlined(self.outlined(styles));
|
self.push_outlined(self.outlined(styles));
|
||||||
self.push_supplement(self.supplement(styles));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -160,77 +170,42 @@ cast_from_value! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Refable for HeadingElem {
|
impl Refable for HeadingElem {
|
||||||
fn reference(
|
fn supplement(&self) -> Content {
|
||||||
&self,
|
// After synthesis, this should always be custom content.
|
||||||
vt: &mut Vt,
|
match self.supplement(StyleChain::default()) {
|
||||||
supplement: Option<Content>,
|
Smart::Custom(Some(Supplement::Content(content))) => content,
|
||||||
lang: Lang,
|
_ => Content::empty(),
|
||||||
region: Option<Region>,
|
}
|
||||||
) -> SourceResult<Content> {
|
|
||||||
// Create the supplement of the heading.
|
|
||||||
let mut supplement = if let Some(supplement) = supplement {
|
|
||||||
supplement
|
|
||||||
} else {
|
|
||||||
match self.supplement(StyleChain::default()) {
|
|
||||||
Smart::Auto => TextElem::packed(self.local_name(lang, region)),
|
|
||||||
Smart::Custom(None) => Content::empty(),
|
|
||||||
Smart::Custom(Some(supplement)) => {
|
|
||||||
supplement.resolve(vt, std::iter::once(Value::from(self.clone())))?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Append a non-breaking space if the supplement is not empty.
|
|
||||||
if !supplement.is_empty() {
|
|
||||||
supplement += TextElem::packed('\u{a0}')
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check for a numbering.
|
|
||||||
let Some(numbering) = self.numbering(StyleChain::default()) else {
|
|
||||||
bail!(self.span(), "only numbered headings can be referenced");
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get the counter and display it.
|
|
||||||
let numbers = Counter::of(Self::func())
|
|
||||||
.at(vt, self.0.location().unwrap())?
|
|
||||||
.display(vt, &numbering.trimmed())?;
|
|
||||||
|
|
||||||
Ok(supplement + numbers)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn level(&self) -> usize {
|
|
||||||
self.level(StyleChain::default()).get()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn numbering(&self) -> Option<Numbering> {
|
|
||||||
self.numbering(StyleChain::default())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn counter(&self) -> Counter {
|
fn counter(&self) -> Counter {
|
||||||
Counter::of(Self::func())
|
Counter::of(Self::func())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn outline(
|
fn numbering(&self) -> Option<Numbering> {
|
||||||
&self,
|
self.numbering(StyleChain::default())
|
||||||
vt: &mut Vt,
|
}
|
||||||
_: Lang,
|
}
|
||||||
_: Option<Region>,
|
|
||||||
) -> SourceResult<Option<Content>> {
|
impl Outlinable for HeadingElem {
|
||||||
// Check whether the heading is outlined.
|
fn outline(&self, vt: &mut Vt) -> SourceResult<Option<Content>> {
|
||||||
if !self.outlined(StyleChain::default()) {
|
if !self.outlined(StyleChain::default()) {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the numbering followed by the title.
|
let mut content = 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(Self::func())
|
||||||
.at(vt, self.0.location().unwrap())?
|
.at(vt, self.0.location().unwrap())?
|
||||||
.display(vt, &numbering)?;
|
.display(vt, &numbering)?;
|
||||||
start = numbers + SpaceElem::new().pack() + start;
|
content = numbers + SpaceElem::new().pack() + content;
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Some(start))
|
Ok(Some(content))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn level(&self) -> NonZeroUsize {
|
||||||
|
self.level(StyleChain::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ pub use self::reference::*;
|
|||||||
pub use self::state::*;
|
pub use self::state::*;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::text::TextElem;
|
||||||
|
|
||||||
/// Hook up all meta definitions.
|
/// Hook up all meta definitions.
|
||||||
pub(super) fn define(global: &mut Scope) {
|
pub(super) fn define(global: &mut Scope) {
|
||||||
@ -55,4 +56,9 @@ pub(super) fn define(global: &mut Scope) {
|
|||||||
pub trait LocalName {
|
pub trait LocalName {
|
||||||
/// Get the name in the given language and (optionally) region.
|
/// Get the name in the given language and (optionally) region.
|
||||||
fn local_name(&self, lang: Lang, region: Option<Region>) -> &'static str;
|
fn local_name(&self, lang: Lang, region: Option<Region>) -> &'static str;
|
||||||
|
|
||||||
|
/// Resolve the local name with a style chain.
|
||||||
|
fn local_name_in(&self, styles: StyleChain) -> &'static str {
|
||||||
|
self.local_name(TextElem::lang_in(styles), TextElem::region_in(styles))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,8 +86,11 @@ pub struct OutlineElem {
|
|||||||
/// caption: [Experiment results],
|
/// caption: [Experiment results],
|
||||||
/// )
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
#[default(Selector::Elem(HeadingElem::func(), Some(dict! { "outlined" => true })))]
|
#[default(LocatableSelector(Selector::Elem(
|
||||||
pub target: Selector,
|
HeadingElem::func(),
|
||||||
|
Some(dict! { "outlined" => true })
|
||||||
|
)))]
|
||||||
|
pub target: LocatableSelector,
|
||||||
|
|
||||||
/// The maximum level up to which elements are included in the outline. When
|
/// The maximum level up to which elements are included in the outline. When
|
||||||
/// this argument is `{none}`, all elements are included.
|
/// this argument is `{none}`, all elements are included.
|
||||||
@ -157,23 +160,21 @@ impl Show for OutlineElem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let indent = self.indent(styles);
|
let indent = self.indent(styles);
|
||||||
let depth = self.depth(styles).map_or(usize::MAX, NonZeroUsize::get);
|
let depth = self.depth(styles).unwrap_or(NonZeroUsize::new(usize::MAX).unwrap());
|
||||||
let lang = TextElem::lang_in(styles);
|
|
||||||
let region = TextElem::region_in(styles);
|
|
||||||
|
|
||||||
let mut ancestors: Vec<&Content> = vec![];
|
let mut ancestors: Vec<&Content> = vec![];
|
||||||
let elems = vt.introspector.query(&self.target(styles));
|
let elems = vt.introspector.query(&self.target(styles).0);
|
||||||
|
|
||||||
for elem in &elems {
|
for elem in &elems {
|
||||||
let Some(refable) = elem.with::<dyn Refable>() else {
|
let Some(outlinable) = elem.with::<dyn Outlinable>() else {
|
||||||
bail!(elem.span(), "outlined elements must be referenceable");
|
bail!(self.span(), "cannot outline {}", elem.func().name());
|
||||||
};
|
};
|
||||||
|
|
||||||
if depth < refable.level() {
|
if depth < outlinable.level() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(outline) = refable.outline(vt, lang, region)? else {
|
let Some(outline) = outlinable.outline(vt)? else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -183,8 +184,8 @@ impl Show for OutlineElem {
|
|||||||
// This is only applicable for elements with a hierarchy/level.
|
// This is only applicable for elements with a hierarchy/level.
|
||||||
while ancestors
|
while ancestors
|
||||||
.last()
|
.last()
|
||||||
.and_then(|ancestor| ancestor.with::<dyn Refable>())
|
.and_then(|ancestor| ancestor.with::<dyn Outlinable>())
|
||||||
.map_or(false, |last| last.level() >= refable.level())
|
.map_or(false, |last| last.level() >= outlinable.level())
|
||||||
{
|
{
|
||||||
ancestors.pop();
|
ancestors.pop();
|
||||||
}
|
}
|
||||||
@ -193,10 +194,10 @@ impl Show for OutlineElem {
|
|||||||
if indent {
|
if indent {
|
||||||
let mut hidden = Content::empty();
|
let mut hidden = Content::empty();
|
||||||
for ancestor in &ancestors {
|
for ancestor in &ancestors {
|
||||||
let ancestor_refable = ancestor.with::<dyn Refable>().unwrap();
|
let ancestor_outlinable = ancestor.with::<dyn Outlinable>().unwrap();
|
||||||
|
|
||||||
if let Some(numbering) = ancestor_refable.numbering() {
|
if let Some(numbering) = ancestor_outlinable.numbering() {
|
||||||
let numbers = ancestor_refable
|
let numbers = ancestor_outlinable
|
||||||
.counter()
|
.counter()
|
||||||
.at(vt, ancestor.location().unwrap())?
|
.at(vt, ancestor.location().unwrap())?
|
||||||
.display(vt, &numbering)?;
|
.display(vt, &numbering)?;
|
||||||
@ -285,3 +286,15 @@ impl LocalName for OutlineElem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Marks an element as being able to be outlined. This is used to implement the
|
||||||
|
/// `#outline()` element.
|
||||||
|
pub trait Outlinable: Refable {
|
||||||
|
/// Produce an outline item for this element.
|
||||||
|
fn outline(&self, vt: &mut Vt) -> SourceResult<Option<Content>>;
|
||||||
|
|
||||||
|
/// Returns the nesting level of this element.
|
||||||
|
fn level(&self) -> NonZeroUsize {
|
||||||
|
NonZeroUsize::ONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -95,6 +95,9 @@ pub struct RefElem {
|
|||||||
/// For references to headings or figures, this is added before the
|
/// For references to headings or figures, this is added before the
|
||||||
/// referenced number. For citations, this can be used to add a page number.
|
/// 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
|
/// ```example
|
||||||
/// #set heading(numbering: "1.")
|
/// #set heading(numbering: "1.")
|
||||||
/// #set ref(supplement: it => {
|
/// #set ref(supplement: it => {
|
||||||
@ -149,43 +152,57 @@ impl Show for RefElem {
|
|||||||
|
|
||||||
let target = self.target();
|
let target = self.target();
|
||||||
let elem = vt.introspector.query_label(&self.target());
|
let elem = vt.introspector.query_label(&self.target());
|
||||||
|
let span = self.span();
|
||||||
|
|
||||||
if BibliographyElem::has(vt, &target.0) {
|
if BibliographyElem::has(vt, &target.0) {
|
||||||
if elem.is_ok() {
|
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())?;
|
let elem = elem.at(span)?;
|
||||||
if !elem.can::<dyn Refable>() {
|
let refable = elem
|
||||||
if elem.can::<dyn Figurable>() {
|
.with::<dyn Refable>()
|
||||||
bail!(
|
.ok_or_else(|| {
|
||||||
self.span(),
|
if elem.can::<dyn Figurable>() {
|
||||||
"cannot reference {} directly, try putting it into a figure",
|
eco_format!(
|
||||||
elem.func().name()
|
"cannot reference {} directly, try putting it into a figure",
|
||||||
);
|
elem.func().name()
|
||||||
} else {
|
)
|
||||||
bail!(self.span(), "cannot reference {}", 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) {
|
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)) => {
|
Smart::Custom(Some(supplement)) => {
|
||||||
Some(supplement.resolve(vt, [(*elem).clone().into()])?)
|
supplement.resolve(vt, [(*elem).clone().into()])?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let lang = TextElem::lang_in(styles);
|
let mut content = numbers;
|
||||||
let region = TextElem::region_in(styles);
|
if !supplement.is_empty() {
|
||||||
let reference = elem
|
content = supplement + TextElem::packed("\u{a0}") + content;
|
||||||
.with::<dyn Refable>()
|
}
|
||||||
.expect("element should be refable")
|
|
||||||
.reference(vt, supplement, lang, region)?;
|
|
||||||
|
|
||||||
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,
|
vt: &mut Vt,
|
||||||
args: impl IntoIterator<Item = Value>,
|
args: impl IntoIterator<Item = Value>,
|
||||||
) -> SourceResult<Content> {
|
) -> SourceResult<Content> {
|
||||||
match self {
|
Ok(match self {
|
||||||
Supplement::Content(content) => Ok(content.clone()),
|
Supplement::Content(content) => content.clone(),
|
||||||
Supplement::Func(func) => func.call_vt(vt, args).map(|v| v.display()),
|
Supplement::Func(func) => func.call_vt(vt, args)?.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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,46 +255,14 @@ cast_to_value! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Marks an element as being able to be referenced. This is used to implement
|
/// 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
|
/// the `@ref` element.
|
||||||
/// by the [`RefElem`].
|
|
||||||
pub trait Refable {
|
pub trait Refable {
|
||||||
/// Tries to build a reference content for this element.
|
/// The supplement, if not overriden by the reference.
|
||||||
///
|
fn supplement(&self) -> Content;
|
||||||
/// # 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 numbering of this element.
|
|
||||||
fn numbering(&self) -> Option<Numbering>;
|
|
||||||
|
|
||||||
/// Returns the counter of this element.
|
/// Returns the counter of this element.
|
||||||
fn counter(&self) -> Counter;
|
fn counter(&self) -> Counter;
|
||||||
|
|
||||||
|
/// Returns the numbering of this element.
|
||||||
|
fn numbering(&self) -> Option<Numbering>;
|
||||||
}
|
}
|
||||||
|
@ -463,6 +463,12 @@ impl Cast for LocatableSelector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<LocatableSelector> for Value {
|
||||||
|
fn from(value: LocatableSelector) -> Self {
|
||||||
|
value.0.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A selector that can be used with show rules.
|
/// A selector that can be used with show rules.
|
||||||
///
|
///
|
||||||
/// Hopefully, this is made obsolete by a more powerful showing mechanism in the
|
/// Hopefully, this is made obsolete by a more powerful showing mechanism in the
|
||||||
@ -518,6 +524,12 @@ impl Cast for ShowableSelector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ShowableSelector> for Value {
|
||||||
|
fn from(value: ShowableSelector) -> Self {
|
||||||
|
value.0.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A show rule transformation that can be applied to a match.
|
/// A show rule transformation that can be applied to a match.
|
||||||
#[derive(Clone, PartialEq, Hash)]
|
#[derive(Clone, PartialEq, Hash)]
|
||||||
pub enum Transform {
|
pub enum Transform {
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 39 KiB |
@ -21,102 +21,28 @@ As seen in @intro, we proceed.
|
|||||||
@foo
|
@foo
|
||||||
|
|
||||||
---
|
---
|
||||||
|
#set heading(numbering: "1.", supplement: [Chapter])
|
||||||
|
#set math.equation(numbering: "(1)", supplement: [Eq.])
|
||||||
|
|
||||||
#show ref: it => {
|
= Intro
|
||||||
if it.element != none and it.element.func() == figure {
|
|
||||||
let element = it.element
|
|
||||||
"["
|
|
||||||
element.supplement
|
|
||||||
"-"
|
|
||||||
str(element.counter.at(element.location()).at(0))
|
|
||||||
"]"
|
|
||||||
// it
|
|
||||||
} else {
|
|
||||||
it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#figure(
|
#figure(
|
||||||
image("/cylinder.svg", height: 3cm),
|
image("/cylinder.svg", height: 1cm),
|
||||||
caption: [A sylinder.],
|
caption: [A cylinder.],
|
||||||
supplement: "Fig",
|
supplement: "Fig",
|
||||||
) <fig1>
|
) <fig1>
|
||||||
|
|
||||||
#figure(
|
#figure(
|
||||||
image("/tiger.jpg", height: 3cm),
|
image("/tiger.jpg", height: 1cm),
|
||||||
caption: [A tiger.],
|
caption: [A tiger.],
|
||||||
supplement: "Figg",
|
supplement: "Tig",
|
||||||
) <fig2>
|
) <fig2>
|
||||||
|
|
||||||
#figure(
|
$ A = 1 $ <eq1>
|
||||||
$ A = 1 $,
|
|
||||||
kind: "equation",
|
|
||||||
supplement: "Equa",
|
|
||||||
|
|
||||||
) <eq1>
|
#set math.equation(supplement: none)
|
||||||
@fig1
|
$ A = 1 $ <eq2>
|
||||||
|
|
||||||
@fig2
|
@fig1, @fig2, @eq1, (@eq2)
|
||||||
|
|
||||||
@eq1
|
#set ref(supplement: none)
|
||||||
|
@fig1, @fig2, @eq1, @eq2
|
||||||
---
|
|
||||||
#set heading(numbering: (..nums) => {
|
|
||||||
nums.pos().map(str).join(".")
|
|
||||||
}, supplement: [Chapt])
|
|
||||||
|
|
||||||
#show ref: it => {
|
|
||||||
if it.element != none and it.element.func() == heading {
|
|
||||||
let element = it.element
|
|
||||||
"["
|
|
||||||
emph(element.supplement)
|
|
||||||
"-"
|
|
||||||
numbering(element.numbering, ..counter(heading).at(element.location()))
|
|
||||||
"]"
|
|
||||||
} else {
|
|
||||||
it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
= Introduction <intro>
|
|
||||||
|
|
||||||
= Summary <sum>
|
|
||||||
|
|
||||||
== Subsection <sub>
|
|
||||||
|
|
||||||
@intro
|
|
||||||
|
|
||||||
@sum
|
|
||||||
|
|
||||||
@sub
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#show ref: it => {
|
|
||||||
if it.element != none {
|
|
||||||
if it.element.func() == text {
|
|
||||||
let element = it.element
|
|
||||||
"["
|
|
||||||
element
|
|
||||||
"]"
|
|
||||||
} else if it.element.func() == underline {
|
|
||||||
let element = it.element
|
|
||||||
"{"
|
|
||||||
element
|
|
||||||
"}"
|
|
||||||
} else {
|
|
||||||
it
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@txt
|
|
||||||
|
|
||||||
Ref something unreferable <txt>
|
|
||||||
|
|
||||||
@under
|
|
||||||
#underline[
|
|
||||||
Some underline text.
|
|
||||||
] <under>
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user