Implement base table of contents logic
This commit is contained in:
parent
d604ca637d
commit
17f49c2107
|
@ -10,8 +10,14 @@ A discussion on pretty much every aspect of the mobile rhythm game "Arcaea".
|
|||
|
||||
# Why I love arcaea
|
||||
|
||||
## What is arcaea
|
||||
- explain the base mechanics
|
||||
## Introduction
|
||||
|
||||
- What is arcaea
|
||||
- What this article is
|
||||
- How to read this article
|
||||
|
||||
::: toc
|
||||
:::
|
||||
|
||||
## What makes a good rhythm game
|
||||
- I don't need to reinvent the wheel here, I can link that one `mental checkpoint` video
|
||||
|
|
59
src/html.rs
59
src/html.rs
|
@ -1,3 +1,4 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
|
||||
|
@ -311,6 +312,64 @@ impl<'s> Writer<'s> {
|
|||
self.states.push(State::Ignore);
|
||||
}
|
||||
// }}}
|
||||
// {{{ Table of contents
|
||||
Container::Div { class: "toc" } => {
|
||||
template!("templates/table-of-contents.html", out)?.feed_fully(
|
||||
out,
|
||||
|label, out| {
|
||||
if label == "content" {
|
||||
let mut level_stack = Vec::with_capacity(6);
|
||||
level_stack.push(1);
|
||||
|
||||
for (i, heading) in self.metadata.toc.iter().enumerate() {
|
||||
loop {
|
||||
let level = level_stack.last().unwrap();
|
||||
match heading.level.cmp(level) {
|
||||
Ordering::Greater => {
|
||||
writeln!(out, "<ol>")?;
|
||||
level_stack.push(heading.level);
|
||||
break;
|
||||
}
|
||||
Ordering::Equal => {
|
||||
if i != 0 {
|
||||
writeln!(out, "</li>")?;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
Ordering::Less => {
|
||||
writeln!(out, "</li></ol>")?;
|
||||
level_stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write!(out, r##"<li><a href="#{}">"##, heading.id)?;
|
||||
|
||||
for event in &heading.events {
|
||||
self.render_event(event, out)?;
|
||||
}
|
||||
|
||||
writeln!(out, "</a>")?;
|
||||
}
|
||||
|
||||
for _ in 0..level_stack.len() - 1 {
|
||||
writeln!(out, "</li></ol>")?;
|
||||
}
|
||||
|
||||
writeln!(out, "</li>")?;
|
||||
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
},
|
||||
)?;
|
||||
|
||||
// We don't care about the contents of this block
|
||||
self.states.push(State::Ignore);
|
||||
}
|
||||
// }}}
|
||||
// {{{ Div
|
||||
Container::Div { class } => {
|
||||
if has_role(attrs, "description") {
|
||||
|
|
|
@ -129,6 +129,7 @@ impl<'a> TemplateRenderer<'a> {
|
|||
}
|
||||
// }}}
|
||||
|
||||
/// Automatically fill in placeholders until the provided lambda returns false.
|
||||
pub fn feed<W: std::fmt::Write>(
|
||||
&mut self,
|
||||
out: &mut W,
|
||||
|
@ -144,6 +145,18 @@ impl<'a> TemplateRenderer<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Equivalent to running [Self::feed] and then [Self::finish].
|
||||
pub fn feed_fully<W: std::fmt::Write>(
|
||||
mut self,
|
||||
out: &mut W,
|
||||
f: impl FnMut(&str, &mut W) -> anyhow::Result<bool>,
|
||||
) -> anyhow::Result<()> {
|
||||
self.feed(out, f)?;
|
||||
self.finish(out)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
// {{{ Macro
|
||||
|
|
9
src/templates/table-of-contents.html
Normal file
9
src/templates/table-of-contents.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<details>
|
||||
<summary>Toggle table of contents</summary>
|
||||
<nav role="doc-toc" aria-labelledby="toc-title">
|
||||
<h2 id="toc-title">Table of Contents</h2>
|
||||
<ol>
|
||||
{{content}}
|
||||
</ol>
|
||||
</nav>
|
||||
</details>
|
Loading…
Reference in a new issue