html: render lists, list items
This commit is contained in:
parent
2f616c41b7
commit
1f71df82ef
1 changed files with 63 additions and 25 deletions
88
src/html.rs
88
src/html.rs
|
@ -1,6 +1,8 @@
|
||||||
use crate::Atom;
|
use crate::Atom;
|
||||||
use crate::Container;
|
use crate::Container;
|
||||||
use crate::Event;
|
use crate::Event;
|
||||||
|
use crate::List;
|
||||||
|
use crate::OrderedListNumbering::*;
|
||||||
|
|
||||||
/// Generate HTML from parsed events and push it to a unicode-accepting buffer or stream.
|
/// Generate HTML from parsed events and push it to a unicode-accepting buffer or stream.
|
||||||
pub fn push<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write>(out: W, events: I) {
|
pub fn push<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write>(out: W, events: I) {
|
||||||
|
@ -99,9 +101,29 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
|
||||||
}
|
}
|
||||||
match &c {
|
match &c {
|
||||||
Container::Blockquote => self.out.write_str("<blockquote")?,
|
Container::Blockquote => self.out.write_str("<blockquote")?,
|
||||||
Container::List(..) => todo!(),
|
Container::List(List::Unordered | List::Task) => {
|
||||||
Container::ListItem => self.out.write_str("<li")?,
|
self.out.write_str("<ul")?;
|
||||||
Container::TaskListItem { .. } => todo!(),
|
}
|
||||||
|
Container::List(List::Ordered {
|
||||||
|
numbering, start, ..
|
||||||
|
}) => {
|
||||||
|
self.out.write_str("<ol")?;
|
||||||
|
if *start > 1 {
|
||||||
|
write!(self.out, r#" start="{}""#, start)?;
|
||||||
|
}
|
||||||
|
if let Some(ty) = match numbering {
|
||||||
|
Decimal => None,
|
||||||
|
AlphaLower => Some('a'),
|
||||||
|
AlphaUpper => Some('A'),
|
||||||
|
RomanLower => Some('i'),
|
||||||
|
RomanUpper => Some('I'),
|
||||||
|
} {
|
||||||
|
write!(self.out, r#" type="{}""#, ty)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Container::ListItem | Container::TaskListItem { .. } => {
|
||||||
|
self.out.write_str("<li")?;
|
||||||
|
}
|
||||||
Container::DescriptionDetails => self.out.write_str("<dd")?,
|
Container::DescriptionDetails => self.out.write_str("<dd")?,
|
||||||
Container::Footnote { number, .. } => {
|
Container::Footnote { number, .. } => {
|
||||||
assert!(self.footnote_number.is_none());
|
assert!(self.footnote_number.is_none());
|
||||||
|
@ -153,42 +175,53 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
|
||||||
Container::Mark => self.out.write_str("<mark")?,
|
Container::Mark => self.out.write_str("<mark")?,
|
||||||
Container::SingleQuoted => self.out.write_str("‘")?,
|
Container::SingleQuoted => self.out.write_str("‘")?,
|
||||||
Container::DoubleQuoted => self.out.write_str("“")?,
|
Container::DoubleQuoted => self.out.write_str("“")?,
|
||||||
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches!(c, Container::SingleQuoted | Container::DoubleQuoted) {
|
if matches!(c, Container::SingleQuoted | Container::DoubleQuoted) {
|
||||||
continue; // TODO add span to allow attributes?
|
continue; // TODO add span to allow attributes?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (a, v) in attrs.iter().filter(|(a, _)| *a != "class") {
|
||||||
|
write!(self.out, r#" {}="{}""#, a, v)?;
|
||||||
|
}
|
||||||
|
|
||||||
if attrs.iter().any(|(a, _)| a == "class")
|
if attrs.iter().any(|(a, _)| a == "class")
|
||||||
|| matches!(
|
|| matches!(
|
||||||
c,
|
c,
|
||||||
Container::Div { class: Some(_) } | Container::Math { .. }
|
Container::Div { class: Some(_) }
|
||||||
|
| Container::Math { .. }
|
||||||
|
| Container::List(List::Task)
|
||||||
|
| Container::TaskListItem { .. }
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
self.out.write_str(r#" class=""#)?;
|
self.out.write_str(r#" class=""#)?;
|
||||||
let mut classes = attrs
|
let mut first_written = false;
|
||||||
|
if let Some(cls) = match c {
|
||||||
|
Container::List(List::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;
|
||||||
|
self.out.write_str(cls)?;
|
||||||
|
}
|
||||||
|
for cls in attrs
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(a, _)| a == &"class")
|
.filter(|(a, _)| a == &"class")
|
||||||
.map(|(_, cls)| cls);
|
.map(|(_, cls)| cls)
|
||||||
let has_attr = if let Container::Math { display } = c {
|
{
|
||||||
self.out.write_str(if display {
|
if first_written {
|
||||||
"math display"
|
|
||||||
} else {
|
|
||||||
"math inline"
|
|
||||||
})?;
|
|
||||||
true
|
|
||||||
} else if let Some(cls) = classes.next() {
|
|
||||||
self.out.write_str(cls)?;
|
|
||||||
for cls in classes {
|
|
||||||
self.out.write_char(' ')?;
|
self.out.write_char(' ')?;
|
||||||
self.out.write_str(cls)?;
|
|
||||||
}
|
}
|
||||||
true
|
first_written = true;
|
||||||
} else {
|
self.out.write_str(cls)?;
|
||||||
false
|
}
|
||||||
};
|
// div class goes after classes from attrs
|
||||||
if let Container::Div { class: Some(cls) } = c {
|
if let Container::Div { class: Some(cls) } = c {
|
||||||
if has_attr {
|
if first_written {
|
||||||
self.out.write_char(' ')?;
|
self.out.write_char(' ')?;
|
||||||
}
|
}
|
||||||
self.out.write_str(cls)?;
|
self.out.write_str(cls)?;
|
||||||
|
@ -223,9 +256,13 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
|
||||||
}
|
}
|
||||||
match c {
|
match c {
|
||||||
Container::Blockquote => self.out.write_str("</blockquote>")?,
|
Container::Blockquote => self.out.write_str("</blockquote>")?,
|
||||||
Container::List(..) => todo!(),
|
Container::List(List::Unordered | List::Task) => {
|
||||||
Container::ListItem => self.out.write_str("</li>")?,
|
self.out.write_str("</ul>")?;
|
||||||
Container::TaskListItem { .. } => todo!(),
|
}
|
||||||
|
Container::List(List::Ordered { .. }) => self.out.write_str("</ol>")?,
|
||||||
|
Container::ListItem | Container::TaskListItem { .. } => {
|
||||||
|
self.out.write_str("</li>")?;
|
||||||
|
}
|
||||||
Container::DescriptionDetails => self.out.write_str("</dd>")?,
|
Container::DescriptionDetails => self.out.write_str("</dd>")?,
|
||||||
Container::Footnote { number, .. } => {
|
Container::Footnote { number, .. } => {
|
||||||
if !self.footnote_backlink_written {
|
if !self.footnote_backlink_written {
|
||||||
|
@ -291,6 +328,7 @@ impl<'s, I: Iterator<Item = Event<'s>>, W: std::fmt::Write> Writer<'s, I, W> {
|
||||||
Container::Mark => self.out.write_str("</mark>")?,
|
Container::Mark => self.out.write_str("</mark>")?,
|
||||||
Container::SingleQuoted => self.out.write_str("’")?,
|
Container::SingleQuoted => self.out.write_str("’")?,
|
||||||
Container::DoubleQuoted => self.out.write_str("”")?,
|
Container::DoubleQuoted => self.out.write_str("”")?,
|
||||||
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Str(s) => {
|
Event::Str(s) => {
|
||||||
|
|
Loading…
Reference in a new issue