@Y.D.X First of all, thanks a lot, and secondly sorry to not have responded earlier.
You did nudge me in the right direction. Here is the solution to my original question:
#let is-heading(it) = {
it.func() == heading
}
/// A show rule that wrap everything between two headings of the same level
///
/// Usage: ```
/// #show: wrapp-section.with(
/// wrapper: it => rect(it),
/// include-heading-in-wrapper: false, // optional
/// depth: 1, // optional
/// )
/// ```
///
/// Note: `#set` rules (like `#set text(lang: "fr")`) must be declared before calling `#show: wrapp-section`
#let wrapp-section(
body,
wrapper: none,
depth: 1,
include-heading-in-wrapper: false,
) = {
// The wrapper function
let fn = none
// The fenced elements to be wrapped
let fenced = ()
for it in body.children {
let x = it.func();
if (is-heading(it) and it.fields().at("depth") < depth) {
if fn != none {
// Complete the last fence
fn(fenced.join())
fn = none
fenced = ()
}
it
} else if is-heading(it) and it.fields().at("depth") == depth {
if fn != none {
// Complete the last fence
fn(fenced.join())
fn = none
fenced = ()
}
// Print the heading itself and start a new fence
if include-heading-in-wrapper {
fn = wrapper
fenced.push(it)
} else {
it
fn = wrapper
}
} else if fn != none {
// Continue the fence
fenced.push(it)
} else {
it // if not in any fence
}
}
// Complete the last fence
if fn != none {
fn(fenced.join())
}
}
And a usage example:
#set text(lang: "en")
#show: wrapp-section.with(
wrapper: it => rect(it),
include-heading-in-wrapper: true,
depth: 2,
)
= heading 1 - a
== heading 2 - a
some
text _and_
more
=== heading 3 - a
stuff
== heading 2 - b
test
Which generates:
The only thing I would like to improve would be to factorize the repeated block:
if fn != none {
// Complete the last fence
fn(fenced.join())
fn = none
fenced = ()
}
