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}