use crate::{
api::check::{Data, DataAnnotation},
parsers::IGNORE,
};
#[must_use]
pub fn parse_markdown(file_content: &str) -> Data<'_> {
use pulldown_cmark::{Event, Options, Parser, Tag, TagEnd};
let mut annotations: Vec<DataAnnotation> = vec![];
let mut tags = vec![];
Parser::new_ext(file_content, Options::all()).for_each(|event| {
match event {
Event::Start(tag) => {
match tag {
Tag::List(_) | Tag::Item => {
annotations.push(DataAnnotation::new_text("- "));
},
_ => {},
}
tags.push(tag);
},
Event::End(tag) => {
match tag {
TagEnd::List(_) | TagEnd::Item | TagEnd::TableRow | TagEnd::TableHead => {
annotations.push(DataAnnotation::new_text("\n"));
},
TagEnd::TableCell => {
annotations.push(DataAnnotation::new_text(" | "));
},
_ => {},
};
if tags
.last()
.is_some_and(|t| TagEnd::from(t.to_owned()) == tag)
{
tags.pop();
};
},
Event::Html(s) | Event::InlineHtml(s) => {
let data = super::html::parse_html(s.as_ref()).annotation.into_iter();
annotations.extend(data);
},
Event::InlineMath(s) | Event::DisplayMath(s) => {
annotations.push(DataAnnotation::new_markup(s));
},
Event::Text(mut s) => {
if s.chars()
.last()
.is_some_and(|c| matches!(c, '.' | '!' | '?'))
{
s = pulldown_cmark::CowStr::from(s.to_string() + " ");
}
let Some(tag) = tags.last() else {
annotations.push(DataAnnotation::new_text(s.to_owned()));
return;
};
match tag {
Tag::Heading { level, .. } => {
let s = format!("{s}\n");
annotations.push(DataAnnotation::new_text(format!(
"{} {s}\n",
"#".repeat(*level as usize)
)));
},
Tag::Emphasis => {
annotations
.push(DataAnnotation::new_interpreted_markup(format!("_{s}_"), s))
},
Tag::Strong => {
annotations.push(DataAnnotation::new_interpreted_markup(
format!("**{s}**"),
s,
))
},
Tag::Strikethrough => {
annotations
.push(DataAnnotation::new_interpreted_markup(format!("~{s}~"), s))
},
Tag::Link {
title, dest_url, ..
} => {
annotations.push(DataAnnotation::new_interpreted_markup(
format!("[{title}]({dest_url})"),
title.to_string(),
));
},
Tag::Paragraph
| Tag::List(_)
| Tag::Item
| Tag::BlockQuote(_)
| Tag::TableCell => {
annotations.push(DataAnnotation::new_text(s));
},
Tag::CodeBlock(_) | Tag::Image { .. } => {
annotations.push(DataAnnotation::new_markup(s));
},
_ => {},
}
},
Event::Code(s) => {
annotations.push(DataAnnotation::new_interpreted_markup(s, IGNORE));
},
Event::HardBreak => {
annotations.push(DataAnnotation::new_text("\n\n"));
},
Event::SoftBreak => {
if let Some(last) = annotations.last() {
if last
.text
.as_ref()
.is_some_and(|t| t.chars().last().is_some_and(|c| c.is_ascii_whitespace()))
|| last.interpret_as.as_ref().is_some_and(|t| {
t.chars().last().is_some_and(|c| c.is_ascii_whitespace())
})
{
return;
};
}
annotations.push(DataAnnotation::new_text(" "));
},
Event::FootnoteReference(_) | Event::TaskListMarker(_) | Event::Rule => {},
};
});
Data::from_iter(annotations)
}