sable-markdown/src/parser/blocks/heading.rs@main
raw
1use nom::{
2 IResult, Parser,
3 branch::alt,
4 character::complete::{char, space0, space1},
5 combinator::{opt, value},
6 multi::{many_m_n, many1},
7 sequence::{preceded, terminated},
8};
9
10use crate::{
11 ast::{Block, Heading, HeadingKind, SetextHeading},
12 parser::util::{line_terminated, not_eof_or_eol1},
13};
14
15pub(super) fn heading_v1<'a>() -> impl FnMut(&'a str) -> IResult<&'a str, Heading> {
18 move |input: &'a str| {
19 let (input, (prefix, _, content)) = (
20 many_m_n(1, 6, char('#')),
21 space1,
22 line_terminated(not_eof_or_eol1),
23 )
24 .parse(input)?;
25
26 let (_, content) = crate::parser::inline::inline_many0().parse(content)?;
27
28 let heading = Heading {
29 kind: HeadingKind::Atx(prefix.len() as u8),
30 content,
31 };
32
33 Ok((input, heading))
34 }
35}
36
37pub(super) fn heading_v2_or_paragraph<'a>() -> impl FnMut(&'a str) -> IResult<&'a str, Block> {
41 move |input: &'a str| {
42 let (input, (content, level)) = (
43 crate::parser::blocks::paragraph::paragraph(true),
44 opt(heading_v2_level()),
45 )
46 .parse(input)?;
47
48 if let Some(level) = level {
49 let heading = Heading {
50 kind: HeadingKind::Setext(level),
51 content,
52 };
53 return Ok((input, Block::Heading(heading)));
54 }
55
56 Ok((input, Block::Paragraph(content)))
57 }
58}
59
60pub(super) fn heading_v2_level<'a>() -> impl FnMut(&'a str) -> IResult<&'a str, SetextHeading> {
61 move |input: &'a str| {
62 let setext_parser = alt((
63 value(SetextHeading::Level1, many1(char('='))),
64 value(SetextHeading::Level2, many1(char('-'))),
65 ));
66
67 let r = line_terminated(preceded(
68 many_m_n(0, 3, char(' ')),
69 terminated(setext_parser, space0),
70 ))
71 .parse(input)?;
72
73 Ok(r)
74 }
75}
76