tingerrr / hydra

A typst package for displaying the active section in the page header or footer.
MIT License
21 stars 3 forks source link

search goes out of scope #5

Closed lkndl closed 8 months ago

lkndl commented 9 months ago

I'm building alternating page headers showing the current chapter on even, and the current section on odd pages. Here is a small example:

#import "@preview/hydra:0.2.0": hydra

#set heading(numbering: "1.1")
#show heading.where(level: 1): it => pagebreak(weak: true) + it
#set par(justify: true)

#set page(paper: "a7",
  header: locate(loc => {
    if calc.even(loc.page()) {
      hydra(sel: heading.where(level: 1))
    } else {
      [#h(1fr)#hydra(sel: heading.where(level: 2))]
    }
  }),
)

= First chapter
#lorem(10)
== First chapter first section
#lorem(150)
== First chapter second section
#lorem(20)
= Second
#lorem(10)
=== Second chapter first subsection
#lorem(30)
=== Second chapter second subsection
#lorem(40)

As you can see, the header on the last page refers to First chapter first section; but this is wrong because we're now in Second chapter. Are you ok with a PR if I can fix this (make hydra not cross higher-level headings), or do you think waiting for the "new features for general handling of headings" is better?

Second thing, not a bug: If you have a look at Second chapter, what do you think about a descend-until-level: 3 option? (I felt ridiculous giving Motivation and Outline full section status :zany_face: )

tingerrr commented 9 months ago

Hmm, I am wondering if this is related to typst#2631.

Are you ok with a PR if I can fix this (make hydra not cross higher-level headings), or do you think waiting for the "new features for general handling of headings" is better?

If you find the issue, absolutely! I'm the features mentioned there would likely not help, as they're intended for declaring chapters without stepping heading counters and such.

Regarding your second point, if I understood this right, what you're looking for is sel: (heading, (_, h) => h.level >= 2), this will consider all headings below chapters for querying.

One last thing, if you use a non-standard margin or different paper size than a4 (I assume in your example it was for illustrating the problem, but it deserves to be said regardless), then remember to pass paper or top-margin to hydra so it handles headings starting at the top of the page correctly.

EDIT: So, weirdly enough, using sel: (heading, (_, h) => h.level >= 2) also fixes the lookup on the last page, It looks like the search should be stopped early in cases where only a selector like heading.where is used, good catch!.

lkndl commented 9 months ago

so we can solve 1) with

sel: heading.where(level: 2),
prev-filter: (ctx, p, n) => query(heading.where(level: 1).after(p.location()).before(loc), loc).len() == 0,
// in words: There is no higher-level heading between `p` (which we want to use) and `loc`

and it totally makes sense why it's necessary. Bit awkward though; might be worth it to introduce another top-level thing in lib that takes a level?

2) Yep that's clear, but thanks for teaching me that syntax. :D What I was looking for was something else: Say we want to use level 2 headings as headers, but now find a chapter that has none of them but a bunch of level 3s; then use those instead. By now I think that idea is very weird, though. So scratch 2)

re: EDIT: Yeah but mixing levels isn't what I want :upside_down_face: and i don't think it's related to #2631?

tingerrr commented 9 months ago

Regarding 2), it exhibits the same erroneous behavior for sel: (heading, (_, h) => h.level == 2), which at least reassures me that those are equivalent. The search should be stopped once we encounter a level 1 heading, which requires knowing more about the opaque selector type. I'll probably end up parsing the repr of it, as I can't access the fields of a heading.wher(level: x) to get to x otherwise.

In your case what should the last page actually display, nothing sicne there is no last section? That would be consistent with what I had in mind regarding the early stopping of the search.

lkndl commented 9 months ago

in my case where I want it to be only level 2 headings then nothing yes :white_check_mark:

regarding 2) hydra() as it is now is meant for headings of any level, and then shouldn't go up past a level 1, it should just show that level 1 heading and it does! Right? If it were to get a level: <x> option, sel would kind of have to be heading and you can just calculate x-1 and then query what not to cross? i mean it's not don't cross level 1 as in always 1 it's don't go out of scope :D

tingerrr commented 9 months ago

I'll push a fix within the next few days, I want to include proper documentation beforehand. I also got carried away while over engineering my test framework for typst packages, but at least the releases should be regression free after that.