html: extract Writer::render_{event, epilogue}
This commit is contained in:
parent
3d1b5f2115
commit
8eafdf073b
1 changed files with 344 additions and 329 deletions
673
src/html.rs
673
src/html.rs
|
@ -74,363 +74,378 @@ impl Default for Writer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Writer {
|
impl Writer {
|
||||||
fn write<'s>(
|
fn write<'s, I, W>(&mut self, mut events: I, mut out: W) -> std::fmt::Result
|
||||||
&mut self,
|
where
|
||||||
events: impl Iterator<Item = Event<'s>>,
|
I: Iterator<Item = Event<'s>>,
|
||||||
mut out: impl std::fmt::Write,
|
W: std::fmt::Write,
|
||||||
) -> std::fmt::Result {
|
{
|
||||||
for e in events {
|
events.try_for_each(|e| self.render_event(&e, &mut out))?;
|
||||||
if matches!(&e, Event::Blankline | Event::Escape) {
|
self.render_epilogue(&mut out)
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let close_para = self.close_para;
|
fn render_event<'s, W>(&mut self, e: &Event<'s>, mut out: W) -> std::fmt::Result
|
||||||
if close_para {
|
where
|
||||||
self.close_para = false;
|
W: std::fmt::Write,
|
||||||
if !matches!(&e, Event::End(Container::Footnote { .. })) {
|
{
|
||||||
// no need to add href before para close
|
if matches!(&e, Event::Blankline | Event::Escape) {
|
||||||
out.write_str("</p>")?;
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let close_para = self.close_para;
|
||||||
|
if close_para {
|
||||||
|
self.close_para = false;
|
||||||
|
if !matches!(&e, Event::End(Container::Footnote { .. })) {
|
||||||
|
// no need to add href before para close
|
||||||
|
out.write_str("</p>")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match e {
|
||||||
|
Event::Start(c, attrs) => {
|
||||||
|
if c.is_block() && !self.first_line {
|
||||||
|
out.write_char('\n')?;
|
||||||
}
|
}
|
||||||
}
|
if self.img_alt_text > 0 && !matches!(c, Container::Image(..)) {
|
||||||
|
return Ok(());
|
||||||
match e {
|
}
|
||||||
Event::Start(c, attrs) => {
|
match &c {
|
||||||
if c.is_block() && !self.first_line {
|
Container::Blockquote => out.write_str("<blockquote")?,
|
||||||
out.write_char('\n')?;
|
Container::List { kind, tight } => {
|
||||||
}
|
self.list_tightness.push(*tight);
|
||||||
if self.img_alt_text > 0 && !matches!(c, Container::Image(..)) {
|
match kind {
|
||||||
continue;
|
ListKind::Unordered | ListKind::Task => out.write_str("<ul")?,
|
||||||
}
|
ListKind::Ordered {
|
||||||
match &c {
|
numbering, start, ..
|
||||||
Container::Blockquote => out.write_str("<blockquote")?,
|
} => {
|
||||||
Container::List { kind, tight } => {
|
out.write_str("<ol")?;
|
||||||
self.list_tightness.push(*tight);
|
if *start > 1 {
|
||||||
match kind {
|
write!(out, r#" start="{}""#, start)?;
|
||||||
ListKind::Unordered | ListKind::Task => out.write_str("<ul")?,
|
}
|
||||||
ListKind::Ordered {
|
if let Some(ty) = match numbering {
|
||||||
numbering, start, ..
|
Decimal => None,
|
||||||
} => {
|
AlphaLower => Some('a'),
|
||||||
out.write_str("<ol")?;
|
AlphaUpper => Some('A'),
|
||||||
if *start > 1 {
|
RomanLower => Some('i'),
|
||||||
write!(out, r#" start="{}""#, start)?;
|
RomanUpper => Some('I'),
|
||||||
}
|
} {
|
||||||
if let Some(ty) = match numbering {
|
write!(out, r#" type="{}""#, ty)?;
|
||||||
Decimal => None,
|
|
||||||
AlphaLower => Some('a'),
|
|
||||||
AlphaUpper => Some('A'),
|
|
||||||
RomanLower => Some('i'),
|
|
||||||
RomanUpper => Some('I'),
|
|
||||||
} {
|
|
||||||
write!(out, r#" type="{}""#, ty)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Container::ListItem | Container::TaskListItem { .. } => {
|
|
||||||
out.write_str("<li")?;
|
|
||||||
}
|
|
||||||
Container::DescriptionList => out.write_str("<dl")?,
|
|
||||||
Container::DescriptionDetails => out.write_str("<dd")?,
|
|
||||||
Container::Footnote { number, .. } => {
|
|
||||||
assert!(self.footnote_number.is_none());
|
|
||||||
self.footnote_number = Some((*number).try_into().unwrap());
|
|
||||||
if !self.encountered_footnote {
|
|
||||||
self.encountered_footnote = true;
|
|
||||||
out.write_str("<section role=\"doc-endnotes\">\n<hr>\n<ol>\n")?;
|
|
||||||
}
|
|
||||||
write!(out, "<li id=\"fn{}\">", number)?;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Container::Table => out.write_str("<table")?,
|
|
||||||
Container::TableRow { .. } => out.write_str("<tr")?,
|
|
||||||
Container::Section { .. } => out.write_str("<section")?,
|
|
||||||
Container::Div { .. } => out.write_str("<div")?,
|
|
||||||
Container::Paragraph => {
|
|
||||||
if matches!(self.list_tightness.last(), Some(true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
out.write_str("<p")?;
|
|
||||||
}
|
|
||||||
Container::Heading { level, .. } => write!(out, "<h{}", level)?,
|
|
||||||
Container::TableCell { head: false, .. } => out.write_str("<td")?,
|
|
||||||
Container::TableCell { head: true, .. } => out.write_str("<th")?,
|
|
||||||
Container::Caption => out.write_str("<caption")?,
|
|
||||||
Container::DescriptionTerm => out.write_str("<dt")?,
|
|
||||||
Container::CodeBlock { .. } => out.write_str("<pre")?,
|
|
||||||
Container::Span | Container::Math { .. } => out.write_str("<span")?,
|
|
||||||
Container::Link(dst, ty) => {
|
|
||||||
if matches!(ty, LinkType::Span(SpanLinkType::Unresolved)) {
|
|
||||||
out.write_str("<a")?;
|
|
||||||
} else {
|
|
||||||
out.write_str(r#"<a href=""#)?;
|
|
||||||
if matches!(ty, LinkType::Email) {
|
|
||||||
out.write_str("mailto:")?;
|
|
||||||
}
|
|
||||||
write_attr(dst, &mut out)?;
|
|
||||||
out.write_char('"')?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Container::Image(..) => {
|
|
||||||
self.img_alt_text += 1;
|
|
||||||
if self.img_alt_text == 1 {
|
|
||||||
out.write_str("<img")?;
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Container::Verbatim => out.write_str("<code")?,
|
|
||||||
Container::RawBlock { format } | Container::RawInline { format } => {
|
|
||||||
self.raw = if format == &"html" {
|
|
||||||
Raw::Html
|
|
||||||
} else {
|
|
||||||
Raw::Other
|
|
||||||
};
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Container::Subscript => out.write_str("<sub")?,
|
|
||||||
Container::Superscript => out.write_str("<sup")?,
|
|
||||||
Container::Insert => out.write_str("<ins")?,
|
|
||||||
Container::Delete => out.write_str("<del")?,
|
|
||||||
Container::Strong => out.write_str("<strong")?,
|
|
||||||
Container::Emphasis => out.write_str("<em")?,
|
|
||||||
Container::Mark => out.write_str("<mark")?,
|
|
||||||
}
|
}
|
||||||
|
Container::ListItem | Container::TaskListItem { .. } => {
|
||||||
for (a, v) in attrs.iter().filter(|(a, _)| *a != "class") {
|
out.write_str("<li")?;
|
||||||
write!(out, r#" {}=""#, a)?;
|
|
||||||
v.parts().try_for_each(|part| write_attr(part, &mut out))?;
|
|
||||||
out.write_char('"')?;
|
|
||||||
}
|
}
|
||||||
|
Container::DescriptionList => out.write_str("<dl")?,
|
||||||
if let Container::Heading {
|
Container::DescriptionDetails => out.write_str("<dd")?,
|
||||||
id,
|
Container::Footnote { number, .. } => {
|
||||||
has_section: false,
|
assert!(self.footnote_number.is_none());
|
||||||
..
|
self.footnote_number = Some((*number).try_into().unwrap());
|
||||||
|
if !self.encountered_footnote {
|
||||||
|
self.encountered_footnote = true;
|
||||||
|
out.write_str("<section role=\"doc-endnotes\">\n<hr>\n<ol>\n")?;
|
||||||
|
}
|
||||||
|
write!(out, "<li id=\"fn{}\">", number)?;
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
| Container::Section { id } = &c
|
Container::Table => out.write_str("<table")?,
|
||||||
{
|
Container::TableRow { .. } => out.write_str("<tr")?,
|
||||||
if !attrs.iter().any(|(a, _)| a == "id") {
|
Container::Section { .. } => out.write_str("<section")?,
|
||||||
out.write_str(r#" id=""#)?;
|
Container::Div { .. } => out.write_str("<div")?,
|
||||||
write_attr(id, &mut out)?;
|
Container::Paragraph => {
|
||||||
|
if matches!(self.list_tightness.last(), Some(true)) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
out.write_str("<p")?;
|
||||||
|
}
|
||||||
|
Container::Heading { level, .. } => write!(out, "<h{}", level)?,
|
||||||
|
Container::TableCell { head: false, .. } => out.write_str("<td")?,
|
||||||
|
Container::TableCell { head: true, .. } => out.write_str("<th")?,
|
||||||
|
Container::Caption => out.write_str("<caption")?,
|
||||||
|
Container::DescriptionTerm => out.write_str("<dt")?,
|
||||||
|
Container::CodeBlock { .. } => out.write_str("<pre")?,
|
||||||
|
Container::Span | Container::Math { .. } => out.write_str("<span")?,
|
||||||
|
Container::Link(dst, ty) => {
|
||||||
|
if matches!(ty, LinkType::Span(SpanLinkType::Unresolved)) {
|
||||||
|
out.write_str("<a")?;
|
||||||
|
} else {
|
||||||
|
out.write_str(r#"<a href=""#)?;
|
||||||
|
if matches!(ty, LinkType::Email) {
|
||||||
|
out.write_str("mailto:")?;
|
||||||
|
}
|
||||||
|
write_attr(dst, &mut out)?;
|
||||||
out.write_char('"')?;
|
out.write_char('"')?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Container::Image(..) => {
|
||||||
|
self.img_alt_text += 1;
|
||||||
|
if self.img_alt_text == 1 {
|
||||||
|
out.write_str("<img")?;
|
||||||
|
} else {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Container::Verbatim => out.write_str("<code")?,
|
||||||
|
Container::RawBlock { format } | Container::RawInline { format } => {
|
||||||
|
self.raw = if format == &"html" {
|
||||||
|
Raw::Html
|
||||||
|
} else {
|
||||||
|
Raw::Other
|
||||||
|
};
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Container::Subscript => out.write_str("<sub")?,
|
||||||
|
Container::Superscript => out.write_str("<sup")?,
|
||||||
|
Container::Insert => out.write_str("<ins")?,
|
||||||
|
Container::Delete => out.write_str("<del")?,
|
||||||
|
Container::Strong => out.write_str("<strong")?,
|
||||||
|
Container::Emphasis => out.write_str("<em")?,
|
||||||
|
Container::Mark => out.write_str("<mark")?,
|
||||||
|
}
|
||||||
|
|
||||||
if attrs.iter().any(|(a, _)| a == "class")
|
for (a, v) in attrs.iter().filter(|(a, _)| *a != "class") {
|
||||||
|| matches!(
|
write!(out, r#" {}=""#, a)?;
|
||||||
c,
|
v.parts().try_for_each(|part| write_attr(part, &mut out))?;
|
||||||
Container::Div { class: Some(_) }
|
out.write_char('"')?;
|
||||||
| Container::Math { .. }
|
}
|
||||||
| Container::List {
|
|
||||||
kind: ListKind::Task,
|
if let Container::Heading {
|
||||||
..
|
id,
|
||||||
}
|
has_section: false,
|
||||||
| Container::TaskListItem { .. }
|
..
|
||||||
)
|
}
|
||||||
{
|
| Container::Section { id } = &c
|
||||||
out.write_str(r#" class=""#)?;
|
{
|
||||||
let mut first_written = false;
|
if !attrs.iter().any(|(a, _)| a == "id") {
|
||||||
if let Some(cls) = match c {
|
out.write_str(r#" id=""#)?;
|
||||||
Container::List {
|
write_attr(id, &mut out)?;
|
||||||
|
out.write_char('"')?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if attrs.iter().any(|(a, _)| a == "class")
|
||||||
|
|| matches!(
|
||||||
|
c,
|
||||||
|
Container::Div { class: Some(_) }
|
||||||
|
| Container::Math { .. }
|
||||||
|
| Container::List {
|
||||||
kind: ListKind::Task,
|
kind: ListKind::Task,
|
||||||
..
|
..
|
||||||
} => Some("task-list"),
|
|
||||||
Container::TaskListItem { checked: false } => Some("unchecked"),
|
|
||||||
Container::TaskListItem { checked: true } => Some("checked"),
|
|
||||||
Container::Math { display: false } => Some("math inline"),
|
|
||||||
Container::Math { display: true } => Some("math display"),
|
|
||||||
_ => None,
|
|
||||||
} {
|
|
||||||
first_written = true;
|
|
||||||
out.write_str(cls)?;
|
|
||||||
}
|
|
||||||
for cls in attrs
|
|
||||||
.iter()
|
|
||||||
.filter(|(a, _)| a == &"class")
|
|
||||||
.map(|(_, cls)| cls)
|
|
||||||
{
|
|
||||||
if first_written {
|
|
||||||
out.write_char(' ')?;
|
|
||||||
}
|
}
|
||||||
first_written = true;
|
| Container::TaskListItem { .. }
|
||||||
cls.parts()
|
)
|
||||||
.try_for_each(|part| write_attr(part, &mut out))?;
|
{
|
||||||
}
|
out.write_str(r#" class=""#)?;
|
||||||
// div class goes after classes from attrs
|
let mut first_written = false;
|
||||||
if let Container::Div { class: Some(cls) } = c {
|
if let Some(cls) = match c {
|
||||||
if first_written {
|
Container::List {
|
||||||
out.write_char(' ')?;
|
kind: ListKind::Task,
|
||||||
}
|
..
|
||||||
out.write_str(cls)?;
|
} => Some("task-list"),
|
||||||
}
|
Container::TaskListItem { checked: false } => Some("unchecked"),
|
||||||
out.write_char('"')?;
|
Container::TaskListItem { checked: true } => Some("checked"),
|
||||||
|
Container::Math { display: false } => Some("math inline"),
|
||||||
|
Container::Math { display: true } => Some("math display"),
|
||||||
|
_ => None,
|
||||||
|
} {
|
||||||
|
first_written = true;
|
||||||
|
out.write_str(cls)?;
|
||||||
}
|
}
|
||||||
|
for cls in attrs
|
||||||
|
.iter()
|
||||||
|
.filter(|(a, _)| a == &"class")
|
||||||
|
.map(|(_, cls)| cls)
|
||||||
|
{
|
||||||
|
if first_written {
|
||||||
|
out.write_char(' ')?;
|
||||||
|
}
|
||||||
|
first_written = true;
|
||||||
|
cls.parts()
|
||||||
|
.try_for_each(|part| write_attr(part, &mut out))?;
|
||||||
|
}
|
||||||
|
// div class goes after classes from attrs
|
||||||
|
if let Container::Div { class: Some(cls) } = c {
|
||||||
|
if first_written {
|
||||||
|
out.write_char(' ')?;
|
||||||
|
}
|
||||||
|
out.write_str(cls)?;
|
||||||
|
}
|
||||||
|
out.write_char('"')?;
|
||||||
|
}
|
||||||
|
|
||||||
match c {
|
match c {
|
||||||
Container::TableCell { alignment, .. }
|
Container::TableCell { alignment, .. }
|
||||||
if !matches!(alignment, Alignment::Unspecified) =>
|
if !matches!(alignment, Alignment::Unspecified) =>
|
||||||
{
|
{
|
||||||
let a = match alignment {
|
let a = match alignment {
|
||||||
Alignment::Unspecified => unreachable!(),
|
Alignment::Unspecified => unreachable!(),
|
||||||
Alignment::Left => "left",
|
Alignment::Left => "left",
|
||||||
Alignment::Center => "center",
|
Alignment::Center => "center",
|
||||||
Alignment::Right => "right",
|
Alignment::Right => "right",
|
||||||
};
|
};
|
||||||
write!(out, r#" style="text-align: {};">"#, a)?;
|
write!(out, r#" style="text-align: {};">"#, a)?;
|
||||||
}
|
|
||||||
Container::CodeBlock { lang } => {
|
|
||||||
if let Some(l) = lang {
|
|
||||||
out.write_str(r#"><code class="language-"#)?;
|
|
||||||
write_attr(l, &mut out)?;
|
|
||||||
out.write_str(r#"">"#)?;
|
|
||||||
} else {
|
|
||||||
out.write_str("><code>")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Container::Image(..) => {
|
|
||||||
if self.img_alt_text == 1 {
|
|
||||||
out.write_str(r#" alt=""#)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Container::Math { display } => {
|
|
||||||
out.write_str(if display { r#">\["# } else { r#">\("# })?;
|
|
||||||
}
|
|
||||||
_ => out.write_char('>')?,
|
|
||||||
}
|
}
|
||||||
}
|
Container::CodeBlock { lang } => {
|
||||||
Event::End(c) => {
|
if let Some(l) = lang {
|
||||||
if c.is_block_container() && !matches!(c, Container::Footnote { .. }) {
|
out.write_str(r#"><code class="language-"#)?;
|
||||||
out.write_char('\n')?;
|
write_attr(l, &mut out)?;
|
||||||
|
out.write_str(r#"">"#)?;
|
||||||
|
} else {
|
||||||
|
out.write_str("><code>")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if self.img_alt_text > 0 && !matches!(c, Container::Image(..)) {
|
Container::Image(..) => {
|
||||||
continue;
|
if self.img_alt_text == 1 {
|
||||||
|
out.write_str(r#" alt=""#)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
match c {
|
Container::Math { display } => {
|
||||||
Container::Blockquote => out.write_str("</blockquote>")?,
|
out.write_str(if *display { r#">\["# } else { r#">\("# })?;
|
||||||
Container::List {
|
|
||||||
kind: ListKind::Unordered | ListKind::Task,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
self.list_tightness.pop();
|
|
||||||
out.write_str("</ul>")?;
|
|
||||||
}
|
|
||||||
Container::List {
|
|
||||||
kind: ListKind::Ordered { .. },
|
|
||||||
..
|
|
||||||
} => out.write_str("</ol>")?,
|
|
||||||
Container::ListItem | Container::TaskListItem { .. } => {
|
|
||||||
out.write_str("</li>")?;
|
|
||||||
}
|
|
||||||
Container::DescriptionList => out.write_str("</dl>")?,
|
|
||||||
Container::DescriptionDetails => out.write_str("</dd>")?,
|
|
||||||
Container::Footnote { number, .. } => {
|
|
||||||
if !close_para {
|
|
||||||
// create a new paragraph
|
|
||||||
out.write_str("\n<p>")?;
|
|
||||||
}
|
|
||||||
write!(
|
|
||||||
out,
|
|
||||||
r##"<a href="#fnref{}" role="doc-backlink">↩︎︎</a></p>"##,
|
|
||||||
number,
|
|
||||||
)?;
|
|
||||||
out.write_str("\n</li>")?;
|
|
||||||
self.footnote_number = None;
|
|
||||||
}
|
|
||||||
Container::Table => out.write_str("</table>")?,
|
|
||||||
Container::TableRow { .. } => out.write_str("</tr>")?,
|
|
||||||
Container::Section { .. } => out.write_str("</section>")?,
|
|
||||||
Container::Div { .. } => out.write_str("</div>")?,
|
|
||||||
Container::Paragraph => {
|
|
||||||
if matches!(self.list_tightness.last(), Some(true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if self.footnote_number.is_none() {
|
|
||||||
out.write_str("</p>")?;
|
|
||||||
} else {
|
|
||||||
self.close_para = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Container::Heading { level, .. } => write!(out, "</h{}>", level)?,
|
|
||||||
Container::TableCell { head: false, .. } => out.write_str("</td>")?,
|
|
||||||
Container::TableCell { head: true, .. } => out.write_str("</th>")?,
|
|
||||||
Container::Caption => out.write_str("</caption>")?,
|
|
||||||
Container::DescriptionTerm => out.write_str("</dt>")?,
|
|
||||||
Container::CodeBlock { .. } => out.write_str("</code></pre>")?,
|
|
||||||
Container::Span => out.write_str("</span>")?,
|
|
||||||
Container::Link(..) => out.write_str("</a>")?,
|
|
||||||
Container::Image(src, ..) => {
|
|
||||||
if self.img_alt_text == 1 {
|
|
||||||
if !src.is_empty() {
|
|
||||||
out.write_str(r#"" src=""#)?;
|
|
||||||
write_attr(&src, &mut out)?;
|
|
||||||
}
|
|
||||||
out.write_str(r#"">"#)?;
|
|
||||||
}
|
|
||||||
self.img_alt_text -= 1;
|
|
||||||
}
|
|
||||||
Container::Verbatim => out.write_str("</code>")?,
|
|
||||||
Container::Math { display } => {
|
|
||||||
out.write_str(if display {
|
|
||||||
r#"\]</span>"#
|
|
||||||
} else {
|
|
||||||
r#"\)</span>"#
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
Container::RawBlock { .. } | Container::RawInline { .. } => {
|
|
||||||
self.raw = Raw::None;
|
|
||||||
}
|
|
||||||
Container::Subscript => out.write_str("</sub>")?,
|
|
||||||
Container::Superscript => out.write_str("</sup>")?,
|
|
||||||
Container::Insert => out.write_str("</ins>")?,
|
|
||||||
Container::Delete => out.write_str("</del>")?,
|
|
||||||
Container::Strong => out.write_str("</strong>")?,
|
|
||||||
Container::Emphasis => out.write_str("</em>")?,
|
|
||||||
Container::Mark => out.write_str("</mark>")?,
|
|
||||||
}
|
}
|
||||||
}
|
_ => out.write_char('>')?,
|
||||||
Event::Str(s) => match self.raw {
|
|
||||||
Raw::None if self.img_alt_text > 0 => write_attr(&s, &mut out)?,
|
|
||||||
Raw::None => write_text(&s, &mut out)?,
|
|
||||||
Raw::Html => out.write_str(&s)?,
|
|
||||||
Raw::Other => {}
|
|
||||||
},
|
|
||||||
Event::FootnoteReference(_tag, number) => {
|
|
||||||
if self.img_alt_text == 0 {
|
|
||||||
write!(
|
|
||||||
out,
|
|
||||||
r##"<a id="fnref{}" href="#fn{}" role="doc-noteref"><sup>{}</sup></a>"##,
|
|
||||||
number, number, number
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::Symbol(sym) => write!(out, ":{}:", sym)?,
|
|
||||||
Event::LeftSingleQuote => out.write_str("‘")?,
|
|
||||||
Event::RightSingleQuote => out.write_str("’")?,
|
|
||||||
Event::LeftDoubleQuote => out.write_str("“")?,
|
|
||||||
Event::RightDoubleQuote => out.write_str("”")?,
|
|
||||||
Event::Ellipsis => out.write_str("…")?,
|
|
||||||
Event::EnDash => out.write_str("–")?,
|
|
||||||
Event::EmDash => out.write_str("—")?,
|
|
||||||
Event::NonBreakingSpace => out.write_str(" ")?,
|
|
||||||
Event::Hardbreak => out.write_str("<br>\n")?,
|
|
||||||
Event::Softbreak => out.write_char('\n')?,
|
|
||||||
Event::Escape | Event::Blankline => unreachable!("filtered out"),
|
|
||||||
Event::ThematicBreak(attrs) => {
|
|
||||||
out.write_str("\n<hr")?;
|
|
||||||
for (a, v) in attrs.iter() {
|
|
||||||
write!(out, r#" {}=""#, a)?;
|
|
||||||
v.parts().try_for_each(|part| write_attr(part, &mut out))?;
|
|
||||||
out.write_char('"')?;
|
|
||||||
}
|
|
||||||
out.write_str(">")?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.first_line = false;
|
Event::End(c) => {
|
||||||
|
if c.is_block_container() && !matches!(c, Container::Footnote { .. }) {
|
||||||
|
out.write_char('\n')?;
|
||||||
|
}
|
||||||
|
if self.img_alt_text > 0 && !matches!(c, Container::Image(..)) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
match c {
|
||||||
|
Container::Blockquote => out.write_str("</blockquote>")?,
|
||||||
|
Container::List {
|
||||||
|
kind: ListKind::Unordered | ListKind::Task,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
self.list_tightness.pop();
|
||||||
|
out.write_str("</ul>")?;
|
||||||
|
}
|
||||||
|
Container::List {
|
||||||
|
kind: ListKind::Ordered { .. },
|
||||||
|
..
|
||||||
|
} => out.write_str("</ol>")?,
|
||||||
|
Container::ListItem | Container::TaskListItem { .. } => {
|
||||||
|
out.write_str("</li>")?;
|
||||||
|
}
|
||||||
|
Container::DescriptionList => out.write_str("</dl>")?,
|
||||||
|
Container::DescriptionDetails => out.write_str("</dd>")?,
|
||||||
|
Container::Footnote { number, .. } => {
|
||||||
|
if !close_para {
|
||||||
|
// create a new paragraph
|
||||||
|
out.write_str("\n<p>")?;
|
||||||
|
}
|
||||||
|
write!(
|
||||||
|
out,
|
||||||
|
r##"<a href="#fnref{}" role="doc-backlink">↩︎︎</a></p>"##,
|
||||||
|
number,
|
||||||
|
)?;
|
||||||
|
out.write_str("\n</li>")?;
|
||||||
|
self.footnote_number = None;
|
||||||
|
}
|
||||||
|
Container::Table => out.write_str("</table>")?,
|
||||||
|
Container::TableRow { .. } => out.write_str("</tr>")?,
|
||||||
|
Container::Section { .. } => out.write_str("</section>")?,
|
||||||
|
Container::Div { .. } => out.write_str("</div>")?,
|
||||||
|
Container::Paragraph => {
|
||||||
|
if matches!(self.list_tightness.last(), Some(true)) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if self.footnote_number.is_none() {
|
||||||
|
out.write_str("</p>")?;
|
||||||
|
} else {
|
||||||
|
self.close_para = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Container::Heading { level, .. } => write!(out, "</h{}>", level)?,
|
||||||
|
Container::TableCell { head: false, .. } => out.write_str("</td>")?,
|
||||||
|
Container::TableCell { head: true, .. } => out.write_str("</th>")?,
|
||||||
|
Container::Caption => out.write_str("</caption>")?,
|
||||||
|
Container::DescriptionTerm => out.write_str("</dt>")?,
|
||||||
|
Container::CodeBlock { .. } => out.write_str("</code></pre>")?,
|
||||||
|
Container::Span => out.write_str("</span>")?,
|
||||||
|
Container::Link(..) => out.write_str("</a>")?,
|
||||||
|
Container::Image(src, ..) => {
|
||||||
|
if self.img_alt_text == 1 {
|
||||||
|
if !src.is_empty() {
|
||||||
|
out.write_str(r#"" src=""#)?;
|
||||||
|
write_attr(src, &mut out)?;
|
||||||
|
}
|
||||||
|
out.write_str(r#"">"#)?;
|
||||||
|
}
|
||||||
|
self.img_alt_text -= 1;
|
||||||
|
}
|
||||||
|
Container::Verbatim => out.write_str("</code>")?,
|
||||||
|
Container::Math { display } => {
|
||||||
|
out.write_str(if *display {
|
||||||
|
r#"\]</span>"#
|
||||||
|
} else {
|
||||||
|
r#"\)</span>"#
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Container::RawBlock { .. } | Container::RawInline { .. } => {
|
||||||
|
self.raw = Raw::None;
|
||||||
|
}
|
||||||
|
Container::Subscript => out.write_str("</sub>")?,
|
||||||
|
Container::Superscript => out.write_str("</sup>")?,
|
||||||
|
Container::Insert => out.write_str("</ins>")?,
|
||||||
|
Container::Delete => out.write_str("</del>")?,
|
||||||
|
Container::Strong => out.write_str("</strong>")?,
|
||||||
|
Container::Emphasis => out.write_str("</em>")?,
|
||||||
|
Container::Mark => out.write_str("</mark>")?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::Str(s) => match self.raw {
|
||||||
|
Raw::None if self.img_alt_text > 0 => write_attr(s, &mut out)?,
|
||||||
|
Raw::None => write_text(s, &mut out)?,
|
||||||
|
Raw::Html => out.write_str(s)?,
|
||||||
|
Raw::Other => {}
|
||||||
|
},
|
||||||
|
Event::FootnoteReference(_tag, number) => {
|
||||||
|
if self.img_alt_text == 0 {
|
||||||
|
write!(
|
||||||
|
out,
|
||||||
|
r##"<a id="fnref{}" href="#fn{}" role="doc-noteref"><sup>{}</sup></a>"##,
|
||||||
|
number, number, number
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::Symbol(sym) => write!(out, ":{}:", sym)?,
|
||||||
|
Event::LeftSingleQuote => out.write_str("‘")?,
|
||||||
|
Event::RightSingleQuote => out.write_str("’")?,
|
||||||
|
Event::LeftDoubleQuote => out.write_str("“")?,
|
||||||
|
Event::RightDoubleQuote => out.write_str("”")?,
|
||||||
|
Event::Ellipsis => out.write_str("…")?,
|
||||||
|
Event::EnDash => out.write_str("–")?,
|
||||||
|
Event::EmDash => out.write_str("—")?,
|
||||||
|
Event::NonBreakingSpace => out.write_str(" ")?,
|
||||||
|
Event::Hardbreak => out.write_str("<br>\n")?,
|
||||||
|
Event::Softbreak => out.write_char('\n')?,
|
||||||
|
Event::Escape | Event::Blankline => unreachable!("filtered out"),
|
||||||
|
Event::ThematicBreak(attrs) => {
|
||||||
|
out.write_str("\n<hr")?;
|
||||||
|
for (a, v) in attrs.iter() {
|
||||||
|
write!(out, r#" {}=""#, a)?;
|
||||||
|
v.parts().try_for_each(|part| write_attr(part, &mut out))?;
|
||||||
|
out.write_char('"')?;
|
||||||
|
}
|
||||||
|
out.write_str(">")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
self.first_line = false;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_epilogue<W>(&mut self, mut out: W) -> std::fmt::Result
|
||||||
|
where
|
||||||
|
W: std::fmt::Write,
|
||||||
|
{
|
||||||
if self.encountered_footnote {
|
if self.encountered_footnote {
|
||||||
out.write_str("\n</ol>\n</section>")?;
|
out.write_str("\n</ol>\n</section>")?;
|
||||||
}
|
}
|
||||||
out.write_char('\n')?;
|
out.write_char('\n')?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue