Based on a question I recently asked on how to wrap the content of a section, I think I have been able to do it:
First, the function from my previous question. The only difference is that I have used positional instead of named arguments.
#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(
/// depth: 1, // optional
/// wrapper: (heading: none, section: none) => {
/// rect[
/// heading: #box[#rect[#heading]]
///
/// section: #box[#rect[#section]]
/// ]
/// }
/// )
/// ```
///
/// Note: `#set` rules (like `#set text(lang: "fr")`) must be declared before calling `#show: wrapp-section`
#let wrapp-section(
body,
depth: 1,
wrapper: none,
) = {
// The heading of the current section
let heading = none
// The content of the current section
let section = ()
for it in body.children {
let x = it.func();
if (is-heading(it) and it.fields().at("depth") < depth) {
if heading != none {
// Complete the last section
wrapper(heading: heading, section: section.join())
heading = none
section = ()
}
it
} else if is-heading(it) and it.fields().at("depth") == depth {
if heading != none {
// Complete the last section
wrapper(heading, section.join())
heading = none
section = ()
}
heading = it
} else if heading != none {
// Continue the current section
section.push(it)
} else {
it // if not in any section (before the first heading of the appropriate depth)
}
}
// Complete the last section
if heading != none {
wrapper(heading, section.join())
}
}
Then, a recursive function that create the nesting from a heading and what I call a section (everything before the next heading of the same level)
#let nest-block(body, depth: 1) = {
wrapp-section(
depth: depth,
wrapper: (heading, content) => {
block(
stroke: (left: black),
inset: (left: 1em),
)[
#heading
#nest-block(depth: depth + 1, content)
]
}
)[#body]
}
And finally, we can use it:
// For some reason, must be done before calling #show
#set text(lang: "en")
#show: wrapp-section.with(
depth: 1,
wrapper: (heading, content) => {
heading
nest-block(depth: 2, content)
}
)
= heading 1 - a
== heading 2 - a
some
text _and_
more
=== heading 3 - a
stuff
== heading 2 - b
test
= heading 1 - c
== heading 2 - c
some
text _and_
more
=== heading 3 - c
stuff
== heading 2 - d
test
If you prefer to have the level 1 heading being nested too, you can replace the #show rule by
#show: nest-block

