Skip to main content

quatrain/contrib/
toc.rs

1use crate::{Event, Tag};
2use std::cell::RefCell;
3use std::collections::VecDeque;
4use std::ops::Range;
5
6#[derive(Debug, Clone)]
7struct Node<'e>(u32, Vec<Event<'e>>);
8
9impl<'e> Node<'e> {
10    fn new(level: u32) -> Self {
11        Self(level, Vec::with_capacity(1))
12    }
13
14    fn push(&mut self, e: Event<'e>) {
15        self.1.push(e)
16    }
17}
18
19#[derive(Debug, Clone)]
20struct List<'e>(RefCell<VecDeque<Node<'e>>>);
21
22impl<'e> List<'e> {
23    fn new() -> Self {
24        List(RefCell::new(VecDeque::new()))
25    }
26
27    fn create(&self, level: u32) {
28        self.0.borrow_mut().push_back(Node::new(level));
29    }
30
31    fn add(&self, event: Event<'e>) {
32        // within method should ensure the create been called before the add.
33        let index = self.0.borrow().len() - 1;
34        self.0.borrow_mut().get_mut(index).unwrap().push(event);
35    }
36}
37
38#[derive(Debug, Clone)]
39pub struct ToC<'e> {
40    range: Range<u32>,
41    list: List<'e>,
42}
43
44impl<'e> ToC<'e> {
45    pub fn new(range: Range<u32>) -> Self {
46        Self {
47            range,
48            list: List::new(),
49        }
50    }
51
52    pub fn start<'f>(&self, event: &Event<'f>) -> bool {
53        match &event {
54            Event::Start(Tag::Heading(n)) if self.range.contains(&n) => {
55                self.list.create(*n);
56                true
57            }
58            _ => false,
59        }
60    }
61
62    pub fn end<'f>(&self, event: &Event<'f>) -> bool {
63        match &event {
64            // It should be impossible to construct nesting headings
65            // using only Markdown syntax.
66            Event::End(Tag::Heading(_)) => true,
67            _ => false,
68        }
69    }
70
71    pub fn within(&self, e: Event<'e>) -> Option<Event<'e>> {
72        self.list.add(e.clone());
73        Some(e)
74    }
75
76    pub fn generate(&self) -> impl Iterator<Item = Event<'e>> {
77        let mut v = vec![];
78        v.push(Event::Html("<div class=\"sidebar toc\">\n".into()));
79        let mut indent = if self.range.start - 1 > 0 {
80            self.range.start - 1
81        } else {
82            0
83        };
84        for Node(level, ref mut events) in self.list.0.borrow_mut().iter_mut() {
85            push_tag(&mut v, indent, *level);
86            v.push(Event::Start(Tag::Item));
87            v.append(events);
88            v.push(Event::End(Tag::Item));
89            indent = *level;
90        }
91        while indent > 0 {
92            v.push(Event::End(Tag::List(None)));
93            indent -= 1;
94        }
95        v.push(Event::Html("</div>\n".into()));
96        v.into_iter()
97    }
98}
99
100fn push_tag<'e>(v: &mut Vec<Event<'e>>, indent: u32, level: u32) {
101    let mut sub = if level > indent {
102        level - indent
103    } else {
104        indent - level
105    };
106    while sub > 0 {
107        if level > indent {
108            v.push(Event::Start(Tag::List(None)));
109            sub -= 1;
110        } else {
111            v.push(Event::End(Tag::List(None)));
112            sub -= 1;
113        }
114    }
115}