1use crate::parser::parse_md_item;
2use pulldown_cmark::{Event, HeadingLevel, Options, Parser, Tag};
3use std::num::ParseIntError;
4use std::ops::Range;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum MDStyle {
10 Heading1 = 0,
11 Heading2,
12 Heading3,
13 Heading4,
14 Heading5,
15 Heading6,
16
17 Paragraph,
18 BlockQuote,
19 CodeBlock,
20 MathDisplay,
21 Rule = 10,
22 Html,
23
24 Link,
25 LinkDef,
26 Image,
27 FootnoteDefinition,
28 FootnoteReference,
29
30 List,
31 Item,
32 TaskListMarker,
33 ItemTag = 20,
34 DefinitionList,
35 DefinitionListTitle,
36 DefinitionListDefinition,
37
38 Table,
39 TableHead,
40 TableRow,
41 TableCell,
42
43 Emphasis,
44 Strong,
45 Strikethrough = 30,
46 CodeInline,
47 MathInline,
48
49 MetadataBlock,
50}
51
52impl From<MDStyle> for usize {
53 fn from(value: MDStyle) -> Self {
54 value as usize
55 }
56}
57
58impl TryFrom<usize> for MDStyle {
59 type Error = ParseIntError;
60
61 fn try_from(value: usize) -> Result<Self, Self::Error> {
62 use MDStyle::*;
63 Ok(match value {
64 0 => Heading1,
65 1 => Heading2,
66 2 => Heading3,
67 3 => Heading4,
68 4 => Heading5,
69 5 => Heading6,
70
71 6 => Paragraph,
72 7 => BlockQuote,
73 8 => CodeBlock,
74 9 => MathDisplay,
75 10 => Rule,
76 11 => Html,
77
78 12 => Link,
79 13 => LinkDef,
80 14 => Image,
81 15 => FootnoteDefinition,
82 16 => FootnoteReference,
83
84 17 => List,
85 18 => Item,
86 19 => TaskListMarker,
87 20 => ItemTag,
88 21 => DefinitionList,
89 22 => DefinitionListTitle,
90 23 => DefinitionListDefinition,
91
92 24 => Table,
93 25 => TableHead,
94 26 => TableRow,
95 27 => TableCell,
96
97 28 => Emphasis,
98 29 => Strong,
99 30 => Strikethrough,
100 31 => CodeInline,
101 32 => MathInline,
102
103 33 => MetadataBlock,
104 _ => return Err("256".parse::<u8>().expect_err("should fail")),
105 })
106 }
107}
108
109pub fn parse_md_styles(txt: &str) -> Vec<(Range<usize>, usize)> {
115 let mut styles = Vec::new();
116
117 let p = Parser::new_ext(
118 txt,
119 Options::ENABLE_MATH
120 | Options::ENABLE_TASKLISTS
121 | Options::ENABLE_TABLES
122 | Options::ENABLE_STRIKETHROUGH
123 | Options::ENABLE_SMART_PUNCTUATION
124 | Options::ENABLE_FOOTNOTES
125 | Options::ENABLE_GFM
126 | Options::ENABLE_DEFINITION_LIST,
127 )
128 .into_offset_iter();
129
130 for (_, linkdef) in p.reference_definitions().iter() {
131 styles.push((linkdef.span.clone(), MDStyle::LinkDef as usize));
132 }
133
134 for (e, r) in p {
135 match e {
136 Event::Start(Tag::Heading { level, .. }) => match level {
137 HeadingLevel::H1 => styles.push((r, MDStyle::Heading1 as usize)),
138 HeadingLevel::H2 => styles.push((r, MDStyle::Heading2 as usize)),
139 HeadingLevel::H3 => styles.push((r, MDStyle::Heading3 as usize)),
140 HeadingLevel::H4 => styles.push((r, MDStyle::Heading4 as usize)),
141 HeadingLevel::H5 => styles.push((r, MDStyle::Heading5 as usize)),
142 HeadingLevel::H6 => styles.push((r, MDStyle::Heading6 as usize)),
143 },
144 Event::Start(Tag::BlockQuote(_)) => {
145 styles.push((r, MDStyle::BlockQuote as usize));
146 }
147 Event::Start(Tag::CodeBlock(_)) => {
148 styles.push((r, MDStyle::CodeBlock as usize));
149 }
150 Event::Start(Tag::FootnoteDefinition(_)) => {
151 styles.push((r, MDStyle::FootnoteDefinition as usize));
152 }
153 Event::Start(Tag::Item) => {
154 let item_text = &txt[r.clone()];
156 let item = parse_md_item(r.start, item_text).expect("md item");
157 styles.push((
158 item.mark_bytes.start..item.mark_bytes.end,
159 MDStyle::ItemTag as usize,
160 ));
161 styles.push((r, MDStyle::Item as usize));
162 }
163 Event::Start(Tag::Emphasis) => {
164 styles.push((r, MDStyle::Emphasis as usize));
165 }
166 Event::Start(Tag::Strong) => {
167 styles.push((r, MDStyle::Strong as usize));
168 }
169 Event::Start(Tag::Strikethrough) => {
170 styles.push((r, MDStyle::Strikethrough as usize));
171 }
172 Event::Start(Tag::Link { .. }) => {
173 styles.push((r, MDStyle::Link as usize));
174 }
175 Event::Start(Tag::Image { .. }) => {
176 styles.push((r, MDStyle::Image as usize));
177 }
178 Event::Start(Tag::MetadataBlock { .. }) => {
179 styles.push((r, MDStyle::MetadataBlock as usize));
180 }
181 Event::Start(Tag::Paragraph) => {
182 styles.push((r, MDStyle::Paragraph as usize));
183 }
184 Event::Start(Tag::HtmlBlock) => {
185 styles.push((r, MDStyle::Html as usize));
186 }
187 Event::Start(Tag::List(_)) => {
188 styles.push((r, MDStyle::List as usize));
189 }
190 Event::Start(Tag::Table(_)) => {
191 styles.push((r, MDStyle::Table as usize));
192 }
193 Event::Start(Tag::TableHead) => {
194 styles.push((r, MDStyle::TableHead as usize));
195 }
196 Event::Start(Tag::TableRow) => {
197 styles.push((r, MDStyle::TableRow as usize));
198 }
199 Event::Start(Tag::TableCell) => {
200 styles.push((r, MDStyle::TableCell as usize));
201 }
202 Event::Start(Tag::DefinitionList) => {
203 styles.push((r, MDStyle::DefinitionList as usize));
204 }
205 Event::Start(Tag::DefinitionListTitle) => {
206 styles.push((r, MDStyle::DefinitionListTitle as usize));
207 }
208 Event::Start(Tag::DefinitionListDefinition) => {
209 styles.push((r, MDStyle::DefinitionListDefinition as usize));
210 }
211 Event::Start(Tag::Superscript) => {
212 styles.push((r, MDStyle::Paragraph as usize));
213 }
214 Event::Start(Tag::Subscript) => {
215 styles.push((r, MDStyle::Paragraph as usize));
216 }
217
218 Event::Code(_) => {
219 styles.push((r, MDStyle::CodeInline as usize));
220 }
221 Event::InlineMath(_) => {
222 styles.push((r, MDStyle::MathInline as usize));
223 }
224 Event::DisplayMath(_) => {
225 styles.push((r, MDStyle::MathDisplay as usize));
226 }
227 Event::FootnoteReference(_) => {
228 styles.push((r, MDStyle::FootnoteReference as usize));
229 }
230 Event::Rule => {
231 styles.push((r, MDStyle::Rule as usize));
232 }
233 Event::TaskListMarker(_) => {
234 styles.push((r, MDStyle::TaskListMarker as usize));
235 }
236 Event::Html(_) | Event::InlineHtml(_) => {
237 styles.push((r, MDStyle::Html as usize));
238 }
239
240 Event::End(_) => {}
241 Event::Text(_) => {}
242 Event::SoftBreak => {}
243 Event::HardBreak => {}
244 }
245 }
246
247 styles
248}