Fix backlinks (#4409)

This commit is contained in:
Laurenz 2024-06-17 14:15:24 +02:00 committed by GitHub
parent 0d93ccd4bf
commit a2c9807159
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 61 additions and 23 deletions

View File

@ -168,6 +168,15 @@ fn is_in_rect(pos: Point, size: Size, click: Point) -> bool {
#[cfg(test)]
mod tests {
//! This can be used in a normal test to determine positions:
//! ```
//! #set page(background: place(
//! dx: 10pt,
//! dy: 10pt,
//! square(size: 2pt, fill: red),
//! ))
//! ```
use std::num::NonZeroUsize;
use typst::layout::{Abs, Point, Position};
@ -200,7 +209,16 @@ mod tests {
fn test_click(text: &str, click: Point, expected: Option<Jump>) {
let world = TestWorld::new(text);
let doc = typst::compile(&world).output.unwrap();
assert_eq!(jump_from_click(&world, &doc, &doc.pages[0].frame, click), expected);
let jump = jump_from_click(&world, &doc, &doc.pages[0].frame, click);
if let (Some(Jump::Position(pos)), Some(Jump::Position(expected))) =
(&jump, &expected)
{
assert_eq!(pos.page, expected.page);
assert_approx_eq!(pos.point.x, expected.point.x);
assert_approx_eq!(pos.point.y, expected.point.y);
} else {
assert_eq!(jump, expected);
}
}
#[track_caller]
@ -240,4 +258,10 @@ mod tests {
test_cursor(s, 12, None);
test_cursor(s, 14, pos(1, 37.55, 16.58));
}
#[test]
fn test_backlink() {
let s = "#footnote[Hi]";
test_click(s, point(10.0, 10.0), pos(1, 18.5, 37.1).map(Jump::Position));
}
}

View File

@ -137,20 +137,26 @@ impl Content {
self.inner.label
}
/// Set the label of the content.
/// Attach a label to the content.
pub fn labelled(mut self, label: Label) -> Self {
self.make_mut().label = Some(label);
self.set_label(label);
self
}
/// Check whether a show rule recipe is disabled.
pub fn is_guarded(&self, index: RecipeIndex) -> bool {
self.inner.lifecycle.contains(index.0)
/// Set the label of the content.
pub fn set_label(&mut self, label: Label) {
self.make_mut().label = Some(label);
}
/// Whether this content has already been prepared.
pub fn is_prepared(&self) -> bool {
self.inner.lifecycle.contains(0)
/// Assigns a location to the content.
///
/// This identifies the content and e.g. makes it linkable by
/// `.linked(Destination::Location(loc))`.
///
/// Useful in combination with [`Location::variant`].
pub fn located(mut self, loc: Location) -> Self {
self.set_location(loc);
self
}
/// Set the location of the content.
@ -158,12 +164,22 @@ impl Content {
self.make_mut().location = Some(location);
}
/// Check whether a show rule recipe is disabled.
pub fn is_guarded(&self, index: RecipeIndex) -> bool {
self.inner.lifecycle.contains(index.0)
}
/// Disable a show rule recipe.
pub fn guarded(mut self, index: RecipeIndex) -> Self {
self.make_mut().lifecycle.insert(index.0);
self
}
/// Whether this content has already been prepared.
pub fn is_prepared(&self) -> bool {
self.inner.lifecycle.contains(0)
}
/// Mark this content as prepared.
pub fn mark_prepared(&mut self) {
self.make_mut().lifecycle.insert(0);
@ -488,15 +504,6 @@ impl Content {
self.styled(LinkElem::set_dests(smallvec![dest]))
}
/// Make the content linkable by `.linked(Destination::Location(loc))`.
///
/// Should be used in combination with [`Location::variant`].
pub fn backlinked(self, loc: Location) -> Self {
let mut backlink = Content::empty().spanned(self.span());
backlink.set_location(loc);
self
}
/// Set alignments for this content.
pub fn aligned(self, align: Alignment) -> Self {
self.styled(AlignElem::set_alignment(align))

View File

@ -833,13 +833,16 @@ impl<'a> Generator<'a> {
let dest = Destination::Location(*location);
content = content.linked(dest);
}
content.backlinked(backlink)
content
});
// Render the main reference content.
let reference = renderer
.display_elem_children(&item.content, &mut prefix)
.backlinked(backlink);
let mut reference =
renderer.display_elem_children(&item.content, &mut prefix);
// Attach a backlink to either the prefix or the reference so that
// we can link to the bibliography entry.
prefix.as_mut().unwrap_or(&mut reference).set_location(backlink);
output.push((prefix, reference));
}

View File

@ -286,7 +286,8 @@ impl Show for Packed<FootnoteEntry> {
.pack()
.spanned(span)
.linked(Destination::Location(loc))
.backlinked(loc.variant(1));
.located(loc.variant(1));
Ok(Content::sequence([
HElem::new(self.indent(styles).into()).pack(),
sup,

View File

@ -119,6 +119,8 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> {
.store(EquationElem::new(content.clone()).pack().spanned(content.span()));
}
// Styled elements and sequences can (at least currently) also have
// labels, so this needs to happen before they are handled.
if let Some(realized) = process(self.engine, &mut self.locator, content, styles)?
{
self.engine.route.increase();

View File

@ -168,6 +168,7 @@ fn verdict<'a>(
&& map.is_empty()
&& (prepared || {
target.label().is_none()
&& target.location().is_none()
&& !target.can::<dyn ShowSet>()
&& !target.can::<dyn Locatable>()
&& !target.can::<dyn Synthesize>()