Skip to content

Commit

Permalink
Add use-last parameter (#19)
Browse files Browse the repository at this point in the history
Closes #13.
This allows hydra to alternatively display the last instead of the first
candidate on a page, mimicking the behavior of LaTeX.
  • Loading branch information
freundTech authored Jun 30, 2024
1 parent 0c6205c commit b46c985
Show file tree
Hide file tree
Showing 19 changed files with 93 additions and 18 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# [unreleased](https://github.com/tingerrr/hydra/releases/tags/)
## Added
- `use-last` parameter on `hydra` for a more LaTeX style heading look up, thanks @freundTech!
- `hydra` now has a new `use-last` parameter
- `context` now has a new `use-last` field
- **BREAKING CHANGE** `candidates` now has a new `last` field containiing a suitable match for the last primary candidate on this page

## Removed

Expand Down
3 changes: 2 additions & 1 deletion doc/chapters/3-reference.typ
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Defines the candidates that have been found in a specific context.

```typc
(
primary: (prev: content | none, next: content | none),
primary: (prev: content | none, next: content | none, last: content | none),
ancestor: (prev: content | none, next: content | none),
)
```
Expand All @@ -61,6 +61,7 @@ displaying.
next-filter: (context, candidates) => bool,
display: (context, content) => content,
skip-starting: bool,
use-last: bool,
book: bool,
anchor: label | none,
anchor-loc: location,
Expand Down
Binary file modified doc/manual.pdf
Binary file not shown.
46 changes: 29 additions & 17 deletions src/core.typ
Original file line number Diff line number Diff line change
Expand Up @@ -84,43 +84,48 @@
#let get-candidates(ctx, scope-prev: true, scope-next: true) = {
let look-prev = selector(ctx.primary.target).before(ctx.anchor-loc)
let look-next = selector(ctx.primary.target).after(ctx.anchor-loc)
let look-last = look-next

let prev-ancestor = none
let next-ancestor = none

if ctx.ancestors != none {
let prev = query(selector(ctx.ancestors.target).before(ctx.anchor-loc))
let next = query(selector(ctx.ancestors.target).after(ctx.anchor-loc))
let prev-ancestors = query(selector(ctx.ancestors.target).before(ctx.anchor-loc))
let next-ancestors = query(selector(ctx.ancestors.target).after(ctx.anchor-loc))

if ctx.ancestors.filter != none {
prev = prev.filter(x => (ctx.ancestors.filter)(ctx, x))
next = next.filter(x => (ctx.ancestors.filter)(ctx, x))
prev-ancestors = prev-ancestors.filter(x => (ctx.ancestors.filter)(ctx, x))
next-ancestors = next-ancestors.filter(x => (ctx.ancestors.filter)(ctx, x))
}

if scope-prev and prev != () {
prev-ancestor = prev.last()
if scope-prev and prev-ancestors != () {
prev-ancestor = prev-ancestors.last()
look-prev = look-prev.after(prev-ancestor.location())
}

if scope-next and next != () {
next-ancestor = next.first()
if scope-next and next-ancestors != () {
next-ancestor = next-ancestors.first()
look-next = look-next.before(next-ancestor.location())
}
}

let prev = query(look-prev)
let next = query(look-next)
let prev-targets = query(look-prev)
let next-targets = query(look-next)
let last-targets = query(look-last)

if ctx.primary.filter != none {
prev = prev.filter(x => (ctx.primary.filter)(ctx, x))
next = next.filter(x => (ctx.primary.filter)(ctx, x))
prev-targets = prev-targets.filter(x => (ctx.primary.filter)(ctx, x))
next-targets = next-targets.filter(x => (ctx.primary.filter)(ctx, x))
last-targets = last-targets.filter(x => (ctx.primary.filter)(ctx, x))
}
last-targets = last-targets.filter(x => x.location().page() == ctx.anchor-loc.page())

let prev = if prev != () { prev.last() }
let next = if next != () { next.first() }
let prev = if prev-targets != () { prev-targets.last() }
let next = if next-targets != () { next-targets.first() }
let last = if last-targets != () { last-targets.last() }

(
primary: (prev: prev, next: next),
primary: (prev: prev, next: next, last: last),
ancestor: (prev: prev-ancestor, next: next-ancestor),
)
}
Expand Down Expand Up @@ -231,11 +236,18 @@
let candidates = get-candidates(ctx)
let prev-eligible = candidates.primary.prev != none and (ctx.prev-filter)(ctx, candidates)
let next-eligible = candidates.primary.next != none and (ctx.next-filter)(ctx, candidates)
let last-eligible = candidates.primary.last != none and (ctx.next-filter)(ctx, candidates)
let active-redundant = is-active-redundant(ctx, candidates)

if prev-eligible and not active-redundant {
if active-redundant and ctx.skip-starting {
return
}

if ctx.use-last and last-eligible {
(ctx.display)(ctx, candidates.primary.last)
} else if prev-eligible and not active-redundant {
(ctx.display)(ctx, candidates.primary.prev)
} else if next-eligible and not ctx.skip-starting {
} else if next-eligible {
(ctx.display)(ctx, candidates.primary.next)
}
}
5 changes: 5 additions & 0 deletions src/lib.typ
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
/// display. If this is `auto`, the default implementaion will be used.
/// - skip-starting (bool): Whether `hydra` should show the current candidate even if it's on top of
/// the current page.
/// - use-last (bool): If hydra should show the name of the first or last candidate on the page.
// Defaults to false.
/// - dir (direction, auto): The reading direction of the document. If this is `auto`, the text
/// direction is used. Be cautious about leaving this option on `auto` if you switch text
/// direction mid-page and use hydra outside of footers or headers.
Expand All @@ -47,6 +49,7 @@
next-filter: auto,
display: auto,
skip-starting: true,
use-last: false,
dir: auto,
binding: auto,
book: false,
Expand All @@ -57,6 +60,7 @@
util.assert.types("next-filter", next-filter, function, auto)
util.assert.types("display", display, function, auto)
util.assert.types("skip-starting", skip-starting, bool)
util.assert.types("use-last", use-last, bool)
util.assert.enum("dir", dir, ltr, rtl, auto)
util.assert.enum("binding", binding, left, right, auto)
util.assert.types("book", book, bool)
Expand All @@ -78,6 +82,7 @@
next-filter: util.auto-or(next-filter, () => default-filter),
display: util.auto-or(display, () => core.display),
skip-starting: skip-starting,
use-last: use-last,
dir: dir,
binding: binding,
book: book,
Expand Down
1 change: 1 addition & 0 deletions tests/features/use-last/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# added by typst-test
Binary file added tests/features/use-last/basic/ref/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/features/use-last/basic/ref/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/features/use-last/basic/ref/3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/features/use-last/basic/ref/4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/features/use-last/basic/ref/5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/features/use-last/basic/ref/6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/features/use-last/basic/ref/7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions tests/features/use-last/basic/test.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Synopsis:
// - Setting use-last to true makes hydra use show the last candidate on a page instead of the first

#import "/src/lib.typ": hydra

#set page(
paper: "a7",
header: context hydra(use-last: true),
)
#set heading(numbering: "1.1")
#show heading.where(level: 1): it => pagebreak(weak: true) + it
#set par(justify: true)

= Introduction
#lorem(200)

= Content
== First Section
#lorem(50)
== Second Section
#lorem(100)
== Third section
#lorem(100)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions tests/features/use-last/multiple-ancestors/test.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Synopsis:
// - When there are multiple ancestors on one page hydra should still show the last heading

#import "/src/lib.typ": hydra

#set page(
paper: "a7",
header: context hydra(2, use-last: true),
)
#set heading(numbering: "1.1")
#set par(justify: true)


= Introduction
== First Section
#lorem(50)
== Second Section
#lorem(100)
== Third section
#lorem(50)

= Other
#lorem(10)
== test
#lorem(10)
= More
#lorem(5)
== more tests
#lorem(10)

0 comments on commit b46c985

Please sign in to comment.