Compare commits

...

5 Commits

Author SHA1 Message Date
Y.D.X.
7a61743e9f
Merge 3a96fe70d885c99173a1cd4a67ac95cfcb7f4373 into e9f1b5825a9d37ca0c173a7b2830ba36a27ca9e0 2025-07-24 13:50:47 +02:00
Laurenz
e9f1b5825a
Lint for iterations over hash types (#6652) 2025-07-24 11:34:08 +00:00
Y.D.X.
3a96fe70d8
Migrate to Rust 2024
Follows #6637
2025-07-22 22:12:42 +08:00
Y.D.X.
e0eaf269d4
Merge branch 'main' into bookmark 2025-07-22 21:12:19 +08:00
Y.D.X. (Gitpod)
3bc1280668
Include numbering in PDF bookmark
Resolves #2416
2025-07-18 00:53:01 +08:00
5 changed files with 34 additions and 0 deletions

View File

@ -159,6 +159,7 @@ strip = true
[workspace.lints.clippy] [workspace.lints.clippy]
blocks_in_conditions = "allow" blocks_in_conditions = "allow"
comparison_chain = "allow" comparison_chain = "allow"
iter_over_hash_type = "warn"
manual_range_contains = "allow" manual_range_contains = "allow"
mutable_key_type = "allow" mutable_key_type = "allow"
uninlined_format_args = "warn" uninlined_format_args = "warn"

View File

@ -139,6 +139,7 @@ impl Watcher {
fn update(&mut self, iter: impl IntoIterator<Item = PathBuf>) -> StrResult<()> { fn update(&mut self, iter: impl IntoIterator<Item = PathBuf>) -> StrResult<()> {
// Mark all files as not "seen" so that we may unwatch them if they // Mark all files as not "seen" so that we may unwatch them if they
// aren't in the dependency list. // aren't in the dependency list.
#[allow(clippy::iter_over_hash_type, reason = "order does not matter")]
for seen in self.watched.values_mut() { for seen in self.watched.values_mut() {
*seen = false; *seen = false;
} }

View File

@ -173,6 +173,7 @@ impl SystemWorld {
/// Reset the compilation state in preparation of a new compilation. /// Reset the compilation state in preparation of a new compilation.
pub fn reset(&mut self) { pub fn reset(&mut self) {
#[allow(clippy::iter_over_hash_type, reason = "order does not matter")]
for slot in self.slots.get_mut().values_mut() { for slot in self.slots.get_mut().values_mut() {
slot.reset(); slot.reset();
} }

View File

@ -1,5 +1,6 @@
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use ecow::EcoString;
use typst_utils::NonZeroExt; use typst_utils::NonZeroExt;
use crate::diag::SourceResult; use crate::diag::SourceResult;
@ -103,6 +104,16 @@ pub struct HeadingElem {
/// ``` /// ```
pub numbering: Option<Numbering>, pub numbering: Option<Numbering>,
/// Plain-text displayed numbering.
///
/// This field is only necessary for creating PDF bookmarks.
/// The problem is that in the export stage, we don't have access to the World/Engine etc.
/// which is needed to resolve numbers and numbering patterns (or functions) into a concrete string/content.
/// Therefore, we have to save the result before the export stage.
#[internal]
#[synthesized]
pub numbering_displayed: EcoString,
/// A supplement for the heading. /// A supplement for the heading.
/// ///
/// For references to headings, this is added before the referenced number. /// For references to headings, this is added before the referenced number.
@ -202,10 +213,23 @@ impl Synthesize for Packed<HeadingElem> {
} }
}; };
let numbering_displayed = if let (Some(numbering), Some(location)) =
(self.numbering.get_ref(styles).as_ref(), self.location())
{
Some(
self.counter()
.display_at_loc(engine, location, styles, numbering)?
.plain_text(),
)
} else {
None
};
let elem = self.as_mut(); let elem = self.as_mut();
elem.level.set(Smart::Custom(elem.resolve_level(styles))); elem.level.set(Smart::Custom(elem.resolve_level(styles)));
elem.supplement elem.supplement
.set(Smart::Custom(Some(Supplement::Content(supplement)))); .set(Smart::Custom(Some(Supplement::Content(supplement))));
elem.numbering_displayed = numbering_displayed;
Ok(()) Ok(())
} }
} }

View File

@ -128,6 +128,13 @@ impl<'a> HeadingNode<'a> {
let pos = gc.document.introspector.position(loc); let pos = gc.document.introspector.position(loc);
let page_index = pos.page.get() - 1; let page_index = pos.page.get() - 1;
// Prepend the numbering to title if it exists
let title = match &self.element.numbering_displayed {
// The space should be a `h(0.3em)`, but only plain-texts are supported here.
Some(num) => format!("{num} {title}"),
None => title,
};
if let Some(index) = gc.page_index_converter.pdf_page_index(page_index) { if let Some(index) = gc.page_index_converter.pdf_page_index(page_index) {
let y = (pos.point.y - Abs::pt(10.0)).max(Abs::zero()); let y = (pos.point.y - Abs::pt(10.0)).max(Abs::zero());
let dest = XyzDestination::new( let dest = XyzDestination::new(