mirror of
https://github.com/typst/typst
synced 2025-05-15 01:25:28 +08:00
Fix backlinks (#4409)
This commit is contained in:
parent
0d93ccd4bf
commit
a2c9807159
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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>()
|
||||
|
Loading…
x
Reference in New Issue
Block a user