Skip to main content

tui/markdown/
headings.rs

1use pulldown_cmark::{Event, Parser, Tag, TagEnd};
2
3use super::pulldown_options;
4use super::source_map::SourceMap;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct MarkdownHeading {
8    pub title: String,
9    pub level: u8,
10    pub source_line_no: usize,
11}
12
13pub fn parse_markdown_headings(text: &str) -> Vec<MarkdownHeading> {
14    let source = SourceMap::new(text);
15    let parser = Parser::new_ext(text, pulldown_options()).into_offset_iter();
16    let mut headings = Vec::new();
17    let mut active: Option<ActiveHeading> = None;
18
19    for (event, range) in parser {
20        match event {
21            Event::Start(Tag::Heading { level, .. }) => {
22                active = Some(ActiveHeading {
23                    level: level as u8,
24                    source_line_no: source.line_no_for_start(&range),
25                    title: String::new(),
26                });
27            }
28            Event::End(TagEnd::Heading(_)) => {
29                if let Some(active) = active.take() {
30                    let title = active.title.trim().to_string();
31                    if !title.is_empty() {
32                        headings.push(MarkdownHeading {
33                            title,
34                            level: active.level,
35                            source_line_no: active.source_line_no,
36                        });
37                    }
38                }
39            }
40            Event::Text(text) | Event::Code(text) => {
41                if let Some(active) = active.as_mut() {
42                    active.title.push_str(&text);
43                }
44            }
45            _ => {}
46        }
47    }
48
49    headings
50}
51
52struct ActiveHeading {
53    level: u8,
54    source_line_no: usize,
55    title: String,
56}