summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/typst-pdf/src/outline.rs2
-rw-r--r--crates/typst-syntax/src/ast.rs2
-rw-r--r--crates/typst/src/eval/markup.rs4
-rw-r--r--crates/typst/src/foundations/func.rs7
-rw-r--r--crates/typst/src/model/bibliography.rs2
-rw-r--r--crates/typst/src/model/heading.rs69
-rw-r--r--crates/typst/src/model/outline.rs2
7 files changed, 74 insertions, 14 deletions
diff --git a/crates/typst-pdf/src/outline.rs b/crates/typst-pdf/src/outline.rs
index a060c175..e247c322 100644
--- a/crates/typst-pdf/src/outline.rs
+++ b/crates/typst-pdf/src/outline.rs
@@ -117,7 +117,7 @@ struct HeadingNode<'a> {
impl<'a> HeadingNode<'a> {
fn leaf(element: &'a Packed<HeadingElem>) -> Self {
HeadingNode {
- level: element.level(StyleChain::default()),
+ level: element.resolve_level(StyleChain::default()),
// 'bookmarked' set to 'auto' falls back to the value of 'outlined'.
bookmarked: element
.bookmarked(StyleChain::default())
diff --git a/crates/typst-syntax/src/ast.rs b/crates/typst-syntax/src/ast.rs
index 26a26160..8f8eaac4 100644
--- a/crates/typst-syntax/src/ast.rs
+++ b/crates/typst-syntax/src/ast.rs
@@ -695,7 +695,7 @@ impl<'a> Heading<'a> {
}
/// The section depth (number of equals signs).
- pub fn level(self) -> NonZeroUsize {
+ pub fn depth(self) -> NonZeroUsize {
self.0
.children()
.find(|node| node.kind() == SyntaxKind::HeadingMarker)
diff --git a/crates/typst/src/eval/markup.rs b/crates/typst/src/eval/markup.rs
index d7d400e7..1bb12d49 100644
--- a/crates/typst/src/eval/markup.rs
+++ b/crates/typst/src/eval/markup.rs
@@ -208,9 +208,9 @@ impl Eval for ast::Heading<'_> {
type Output = Content;
fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
- let level = self.level();
+ let depth = self.depth();
let body = self.body().eval(vm)?;
- Ok(HeadingElem::new(body).with_level(level).pack())
+ Ok(HeadingElem::new(body).with_depth(depth).pack())
}
}
diff --git a/crates/typst/src/foundations/func.rs b/crates/typst/src/foundations/func.rs
index bb839994..7871e297 100644
--- a/crates/typst/src/foundations/func.rs
+++ b/crates/typst/src/foundations/func.rs
@@ -341,6 +341,13 @@ impl Func {
/// Returns a selector that filters for elements belonging to this function
/// whose fields have the values of the given arguments.
+ ///
+ /// ```example
+ /// #show heading.where(level: 2): set text(blue)
+ /// = Section
+ /// == Subsection
+ /// === Sub-subection
+ /// ```
#[func]
pub fn where_(
self,
diff --git a/crates/typst/src/model/bibliography.rs b/crates/typst/src/model/bibliography.rs
index 19fc44ea..e3464026 100644
--- a/crates/typst/src/model/bibliography.rs
+++ b/crates/typst/src/model/bibliography.rs
@@ -220,7 +220,7 @@ impl Show for Packed<BibliographyElem> {
seq.push(
HeadingElem::new(title)
- .with_level(NonZeroUsize::ONE)
+ .with_level(Smart::Custom(NonZeroUsize::ONE))
.pack()
.spanned(self.span()),
);
diff --git a/crates/typst/src/model/heading.rs b/crates/typst/src/model/heading.rs
index b958438b..cd342bec 100644
--- a/crates/typst/src/model/heading.rs
+++ b/crates/typst/src/model/heading.rs
@@ -17,7 +17,7 @@ use crate::util::{option_eq, NonZeroExt};
/// With headings, you can structure your document into sections. Each heading
/// has a _level,_ which starts at one and is unbounded upwards. This level
/// indicates the logical role of the following content (section, subsection,
-/// etc.) A top-level heading indicates a top-level section of the document
+/// etc.) A top-level heading indicates a top-level section of the document
/// (not the document's title).
///
/// Typst can automatically number your headings for you. To enable numbering,
@@ -42,12 +42,54 @@ use crate::util::{option_eq, NonZeroExt};
/// # Syntax
/// Headings have dedicated syntax: They can be created by starting a line with
/// one or multiple equals signs, followed by a space. The number of equals
-/// signs determines the heading's logical nesting depth.
+/// signs determines the heading's logical nesting depth. The `{offset}` field
+/// can be set to configure the starting depth.
#[elem(Locatable, Synthesize, Count, Show, ShowSet, LocalName, Refable, Outlinable)]
pub struct HeadingElem {
- /// The logical nesting depth of the heading, starting from one.
+ /// The absolute nesting depth of the heading, starting from one. If set
+ /// to `{auto}`, it is computed from `{offset + depth}`.
+ ///
+ /// This is primarily useful for usage in [show rules]($styling/#show-rules)
+ /// (either with [`where`]($function.where) selectors or by accessing the
+ /// level directly on a shown heading).
+ ///
+ /// ```example
+ /// #show heading.where(level: 2): set text(red)
+ ///
+ /// = Level 1
+ /// == Level 2
+ ///
+ /// #set heading(offset: 1)
+ /// = Also level 2
+ /// == Level 3
+ /// ```
+ pub level: Smart<NonZeroUsize>,
+
+ /// The relative nesting depth of the heading, starting from one. This is
+ /// combined with `{offset}` to compute the actual `{level}`.
+ ///
+ /// This is set by the heading syntax, such that `[== Heading]` creates a
+ /// heading with logical depth 2, but actual level `{offset + 2}`. If you
+ /// construct a heading manually, you should typically prefer this over
+ /// setting the absolute `level`.
#[default(NonZeroUsize::ONE)]
- pub level: NonZeroUsize,
+ pub depth: NonZeroUsize,
+
+ /// The starting offset of each heading's level, used to turn its relative
+ /// `{depth}` into its absolute `{level}`.
+ ///
+ /// ```example
+ /// = Level 1
+ ///
+ /// #set heading(offset: 1, numbering: "1.1")
+ /// = Level 2
+ ///
+ /// #heading(offset: 2, depth: 2)[
+ /// I'm level 4
+ /// ]
+ /// ```
+ #[default(0)]
+ pub offset: usize,
/// How to number the heading. Accepts a
/// [numbering pattern or function]($numbering).
@@ -126,6 +168,15 @@ pub struct HeadingElem {
pub body: Content,
}
+impl HeadingElem {
+ pub fn resolve_level(&self, styles: StyleChain) -> NonZeroUsize {
+ self.level(styles).unwrap_or_else(|| {
+ NonZeroUsize::new(self.offset(styles) + self.depth(styles).get())
+ .expect("overflow to 0 on NoneZeroUsize + usize")
+ })
+ }
+}
+
impl Synthesize for Packed<HeadingElem> {
fn synthesize(
&mut self,
@@ -140,7 +191,9 @@ impl Synthesize for Packed<HeadingElem> {
}
};
- self.push_supplement(Smart::Custom(Some(Supplement::Content(supplement))));
+ let elem = self.as_mut();
+ elem.push_level(Smart::Custom(elem.resolve_level(styles)));
+ elem.push_supplement(Smart::Custom(Some(Supplement::Content(supplement))));
Ok(())
}
}
@@ -163,7 +216,7 @@ impl Show for Packed<HeadingElem> {
impl ShowSet for Packed<HeadingElem> {
fn show_set(&self, styles: StyleChain) -> Styles {
- let level = (**self).level(styles).get();
+ let level = (**self).resolve_level(styles).get();
let scale = match level {
1 => 1.4,
2 => 1.2,
@@ -189,7 +242,7 @@ impl Count for Packed<HeadingElem> {
(**self)
.numbering(StyleChain::default())
.is_some()
- .then(|| CounterUpdate::Step((**self).level(StyleChain::default())))
+ .then(|| CounterUpdate::Step((**self).resolve_level(StyleChain::default())))
}
}
@@ -236,7 +289,7 @@ impl Outlinable for Packed<HeadingElem> {
}
fn level(&self) -> NonZeroUsize {
- (**self).level(StyleChain::default())
+ (**self).resolve_level(StyleChain::default())
}
}
diff --git a/crates/typst/src/model/outline.rs b/crates/typst/src/model/outline.rs
index cb8d5563..bec98b7d 100644
--- a/crates/typst/src/model/outline.rs
+++ b/crates/typst/src/model/outline.rs
@@ -197,7 +197,7 @@ impl Show for Packed<OutlineElem> {
seq.push(
HeadingElem::new(title)
- .with_level(NonZeroUsize::ONE)
+ .with_depth(NonZeroUsize::ONE)
.pack()
.spanned(self.span()),
);