cargo fmt
This commit is contained in:
parent
71172c45db
commit
c615b21bf3
|
@ -1,9 +1,9 @@
|
|||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
extern crate comrak;
|
||||
extern crate test;
|
||||
|
||||
use comrak::{parse_document, format_html, Arena, ComrakOptions};
|
||||
use comrak::{format_html, parse_document, Arena, ComrakOptions};
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
|
@ -21,4 +21,3 @@ fn bench_progit(b: &mut Bencher) {
|
|||
format_html(root, &ComrakOptions::default(), &mut output).unwrap()
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -3,13 +3,15 @@ extern crate comrak;
|
|||
fn small() {
|
||||
use comrak::{markdown_to_html, ComrakOptions};
|
||||
|
||||
assert_eq!(markdown_to_html("Hello, **世界**!", &ComrakOptions::default()),
|
||||
"<p>Hello, <strong>世界</strong>!</p>\n");
|
||||
assert_eq!(
|
||||
markdown_to_html("Hello, **世界**!", &ComrakOptions::default()),
|
||||
"<p>Hello, <strong>世界</strong>!</p>\n"
|
||||
);
|
||||
}
|
||||
|
||||
fn large() {
|
||||
use comrak::{parse_document, format_html, Arena, ComrakOptions};
|
||||
use comrak::nodes::{AstNode, NodeValue};
|
||||
use comrak::{format_html, parse_document, Arena, ComrakOptions};
|
||||
|
||||
// The returned nodes are created in the supplied Arena, and are bound by its lifetime.
|
||||
let arena = Arena::new();
|
||||
|
@ -17,24 +19,29 @@ fn large() {
|
|||
let root = parse_document(
|
||||
&arena,
|
||||
"This is my input.\n\n1. Also my input.\n2. Certainly my input.\n",
|
||||
&ComrakOptions::default());
|
||||
&ComrakOptions::default(),
|
||||
);
|
||||
|
||||
fn iter_nodes<'a, F>(node: &'a AstNode<'a>, f: &F)
|
||||
where F : Fn(&'a AstNode<'a>) {
|
||||
where
|
||||
F: Fn(&'a AstNode<'a>),
|
||||
{
|
||||
f(node);
|
||||
for c in node.children() {
|
||||
iter_nodes(c, f);
|
||||
}
|
||||
}
|
||||
|
||||
iter_nodes(root, &|node| {
|
||||
match &mut node.data.borrow_mut().value {
|
||||
&mut NodeValue::Text(ref mut text) => {
|
||||
let orig = std::mem::replace(text, vec![]);
|
||||
*text = String::from_utf8(orig).unwrap().replace("my", "your").as_bytes().to_vec();
|
||||
}
|
||||
_ => (),
|
||||
iter_nodes(root, &|node| match &mut node.data.borrow_mut().value {
|
||||
&mut NodeValue::Text(ref mut text) => {
|
||||
let orig = std::mem::replace(text, vec![]);
|
||||
*text = String::from_utf8(orig)
|
||||
.unwrap()
|
||||
.replace("my", "your")
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
|
||||
let mut html = vec![];
|
||||
|
@ -46,7 +53,8 @@ fn large() {
|
|||
<ol>\n\
|
||||
<li>Also your input.</li>\n\
|
||||
<li>Certainly your input.</li>\n\
|
||||
</ol>\n");
|
||||
</ol>\n"
|
||||
);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -16,7 +16,7 @@ const INDENT: usize = 4;
|
|||
const CLOSE_NEWLINE: bool = false;
|
||||
|
||||
use comrak::nodes::{AstNode, NodeValue};
|
||||
use comrak::{parse_document, Arena, ComrakOptions, ComrakExtensionOptions};
|
||||
use comrak::{parse_document, Arena, ComrakExtensionOptions, ComrakOptions};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
format_strings = false
|
||||
reorder_imports = true
|
||||
error_on_line_overflow = false
|
||||
|
|
496
src/cm.rs
496
src/cm.rs
|
@ -170,7 +170,8 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
|
|||
|
||||
let nextc = nextc.map_or(0, |&c| c);
|
||||
|
||||
let needs_escaping = c < 0x80 && escaping != Escaping::Literal
|
||||
let needs_escaping = c < 0x80
|
||||
&& escaping != Escaping::Literal
|
||||
&& ((escaping == Escaping::Normal
|
||||
&& (c < 0x20
|
||||
|| c == b'*'
|
||||
|
@ -287,33 +288,40 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
|
|||
if !(match node.data.borrow().value {
|
||||
NodeValue::Item(..) => true,
|
||||
_ => false,
|
||||
} && node.previous_sibling().is_none() && entering)
|
||||
} && node.previous_sibling().is_none()
|
||||
&& entering)
|
||||
{
|
||||
self.in_tight_list_item = self.get_in_tight_list_item(node);
|
||||
}
|
||||
|
||||
match node.data.borrow().value {
|
||||
NodeValue::Document => (),
|
||||
NodeValue::BlockQuote => if entering {
|
||||
write!(self, "> ").unwrap();
|
||||
self.begin_content = true;
|
||||
write!(self.prefix, "> ").unwrap();
|
||||
} else {
|
||||
let new_len = self.prefix.len() - 2;
|
||||
self.prefix.truncate(new_len);
|
||||
self.blankline();
|
||||
},
|
||||
NodeValue::List(..) => if !entering && match node.next_sibling() {
|
||||
Some(next_sibling) => match next_sibling.data.borrow().value {
|
||||
NodeValue::CodeBlock(..) | NodeValue::List(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
} {
|
||||
self.cr();
|
||||
write!(self, "<!-- end list -->").unwrap();
|
||||
self.blankline();
|
||||
},
|
||||
NodeValue::BlockQuote => {
|
||||
if entering {
|
||||
write!(self, "> ").unwrap();
|
||||
self.begin_content = true;
|
||||
write!(self.prefix, "> ").unwrap();
|
||||
} else {
|
||||
let new_len = self.prefix.len() - 2;
|
||||
self.prefix.truncate(new_len);
|
||||
self.blankline();
|
||||
}
|
||||
}
|
||||
NodeValue::List(..) => {
|
||||
if !entering
|
||||
&& match node.next_sibling() {
|
||||
Some(next_sibling) => match next_sibling.data.borrow().value {
|
||||
NodeValue::CodeBlock(..) | NodeValue::List(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
{
|
||||
self.cr();
|
||||
write!(self, "<!-- end list -->").unwrap();
|
||||
self.blankline();
|
||||
}
|
||||
}
|
||||
NodeValue::Item(..) => {
|
||||
let parent = match node.parent().unwrap().data.borrow().value {
|
||||
NodeValue::List(ref nl) => *nl,
|
||||
|
@ -342,7 +350,8 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
|
|||
"."
|
||||
},
|
||||
if list_number < 10 { " " } else { " " }
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
listmarker.len()
|
||||
};
|
||||
|
||||
|
@ -365,124 +374,150 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
|
|||
NodeValue::DescriptionList => (),
|
||||
NodeValue::DescriptionItem(..) => (),
|
||||
NodeValue::DescriptionTerm => (),
|
||||
NodeValue::DescriptionDetails => if entering {
|
||||
write!(self, ": ").unwrap()
|
||||
},
|
||||
NodeValue::Heading(ref nch) => if entering {
|
||||
for _ in 0..nch.level {
|
||||
write!(self, "#").unwrap();
|
||||
NodeValue::DescriptionDetails => {
|
||||
if entering {
|
||||
write!(self, ": ").unwrap()
|
||||
}
|
||||
write!(self, " ").unwrap();
|
||||
self.begin_content = true;
|
||||
self.no_linebreaks = true;
|
||||
} else {
|
||||
self.no_linebreaks = false;
|
||||
self.blankline();
|
||||
},
|
||||
NodeValue::CodeBlock(ref ncb) => if entering {
|
||||
let first_in_list_item = node.previous_sibling().is_none() && match node.parent() {
|
||||
Some(parent) => match parent.data.borrow().value {
|
||||
NodeValue::Item(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if !first_in_list_item {
|
||||
}
|
||||
NodeValue::Heading(ref nch) => {
|
||||
if entering {
|
||||
for _ in 0..nch.level {
|
||||
write!(self, "#").unwrap();
|
||||
}
|
||||
write!(self, " ").unwrap();
|
||||
self.begin_content = true;
|
||||
self.no_linebreaks = true;
|
||||
} else {
|
||||
self.no_linebreaks = false;
|
||||
self.blankline();
|
||||
}
|
||||
|
||||
if ncb.info.is_empty()
|
||||
&& (ncb.literal.len() > 2 && !isspace(ncb.literal[0])
|
||||
&& !(isspace(ncb.literal[ncb.literal.len() - 1])
|
||||
&& isspace(ncb.literal[ncb.literal.len() - 2])))
|
||||
&& !first_in_list_item
|
||||
{
|
||||
write!(self, " ").unwrap();
|
||||
write!(self.prefix, " ").unwrap();
|
||||
self.write_all(&ncb.literal).unwrap();
|
||||
let new_len = self.prefix.len() - 4;
|
||||
self.prefix.truncate(new_len);
|
||||
} else {
|
||||
let fence_char =
|
||||
if ncb.info.contains(&b'`') {
|
||||
b'~'
|
||||
} else {
|
||||
b'`'
|
||||
}
|
||||
NodeValue::CodeBlock(ref ncb) => {
|
||||
if entering {
|
||||
let first_in_list_item = node.previous_sibling().is_none()
|
||||
&& match node.parent() {
|
||||
Some(parent) => match parent.data.borrow().value {
|
||||
NodeValue::Item(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
let numticks = max(3, longest_char_sequence(&ncb.literal, fence_char) + 1);
|
||||
for _ in 0..numticks {
|
||||
write!(self, "{}", fence_char as char).unwrap();
|
||||
|
||||
if !first_in_list_item {
|
||||
self.blankline();
|
||||
}
|
||||
if !ncb.info.is_empty() {
|
||||
|
||||
if ncb.info.is_empty()
|
||||
&& (ncb.literal.len() > 2
|
||||
&& !isspace(ncb.literal[0])
|
||||
&& !(isspace(ncb.literal[ncb.literal.len() - 1])
|
||||
&& isspace(ncb.literal[ncb.literal.len() - 2])))
|
||||
&& !first_in_list_item
|
||||
{
|
||||
write!(self, " ").unwrap();
|
||||
write!(self.prefix, " ").unwrap();
|
||||
self.write_all(&ncb.literal).unwrap();
|
||||
let new_len = self.prefix.len() - 4;
|
||||
self.prefix.truncate(new_len);
|
||||
} else {
|
||||
let fence_char = if ncb.info.contains(&b'`') { b'~' } else { b'`' };
|
||||
let numticks = max(3, longest_char_sequence(&ncb.literal, fence_char) + 1);
|
||||
for _ in 0..numticks {
|
||||
write!(self, "{}", fence_char as char).unwrap();
|
||||
}
|
||||
if !ncb.info.is_empty() {
|
||||
write!(self, " ").unwrap();
|
||||
self.write_all(&ncb.info).unwrap();
|
||||
}
|
||||
self.cr();
|
||||
self.write_all(&ncb.literal).unwrap();
|
||||
self.cr();
|
||||
for _ in 0..numticks {
|
||||
write!(self, "{}", fence_char as char).unwrap();
|
||||
}
|
||||
}
|
||||
self.blankline();
|
||||
}
|
||||
}
|
||||
NodeValue::HtmlBlock(ref nhb) => {
|
||||
if entering {
|
||||
self.blankline();
|
||||
self.write_all(&nhb.literal).unwrap();
|
||||
self.blankline();
|
||||
}
|
||||
}
|
||||
NodeValue::ThematicBreak => {
|
||||
if entering {
|
||||
self.blankline();
|
||||
write!(self, "-----").unwrap();
|
||||
self.blankline();
|
||||
}
|
||||
}
|
||||
NodeValue::Paragraph => {
|
||||
if !entering {
|
||||
self.blankline();
|
||||
}
|
||||
}
|
||||
NodeValue::Text(ref literal) => {
|
||||
if entering {
|
||||
self.output(literal, allow_wrap, Escaping::Normal);
|
||||
}
|
||||
}
|
||||
NodeValue::LineBreak => {
|
||||
if entering {
|
||||
if !self.options.render.hardbreaks {
|
||||
write!(self, " ").unwrap();
|
||||
}
|
||||
self.cr();
|
||||
}
|
||||
}
|
||||
NodeValue::SoftBreak => {
|
||||
if entering {
|
||||
if !self.no_linebreaks
|
||||
&& self.options.render.width == 0
|
||||
&& !self.options.render.hardbreaks
|
||||
{
|
||||
self.cr();
|
||||
} else {
|
||||
self.output(&[b' '], allow_wrap, Escaping::Literal);
|
||||
}
|
||||
}
|
||||
}
|
||||
NodeValue::Code(ref literal) => {
|
||||
if entering {
|
||||
let numticks = shortest_unused_sequence(literal, b'`');
|
||||
for _ in 0..numticks {
|
||||
write!(self, "`").unwrap();
|
||||
}
|
||||
let pad = literal.is_empty()
|
||||
|| literal[0] == b'`'
|
||||
|| literal[literal.len() - 1] == b'`'
|
||||
|| literal[0] == b' '
|
||||
|| literal[literal.len() - 1] == b' ';
|
||||
if pad {
|
||||
write!(self, " ").unwrap();
|
||||
}
|
||||
self.output(literal, allow_wrap, Escaping::Literal);
|
||||
if pad {
|
||||
write!(self, " ").unwrap();
|
||||
self.write_all(&ncb.info).unwrap();
|
||||
}
|
||||
self.cr();
|
||||
self.write_all(&ncb.literal).unwrap();
|
||||
self.cr();
|
||||
for _ in 0..numticks {
|
||||
write!(self, "{}", fence_char as char).unwrap();
|
||||
write!(self, "`").unwrap();
|
||||
}
|
||||
}
|
||||
self.blankline();
|
||||
},
|
||||
NodeValue::HtmlBlock(ref nhb) => if entering {
|
||||
self.blankline();
|
||||
self.write_all(&nhb.literal).unwrap();
|
||||
self.blankline();
|
||||
},
|
||||
NodeValue::ThematicBreak => if entering {
|
||||
self.blankline();
|
||||
write!(self, "-----").unwrap();
|
||||
self.blankline();
|
||||
},
|
||||
NodeValue::Paragraph => if !entering {
|
||||
self.blankline();
|
||||
},
|
||||
NodeValue::Text(ref literal) => if entering {
|
||||
self.output(literal, allow_wrap, Escaping::Normal);
|
||||
},
|
||||
NodeValue::LineBreak => if entering {
|
||||
if !self.options.render.hardbreaks {
|
||||
write!(self, " ").unwrap();
|
||||
}
|
||||
NodeValue::HtmlInline(ref literal) => {
|
||||
if entering {
|
||||
self.write_all(literal).unwrap();
|
||||
}
|
||||
self.cr();
|
||||
},
|
||||
NodeValue::SoftBreak => if entering {
|
||||
if !self.no_linebreaks && self.options.render.width == 0 && !self.options.render.hardbreaks {
|
||||
self.cr();
|
||||
}
|
||||
NodeValue::Strong => {
|
||||
if entering {
|
||||
write!(self, "**").unwrap();
|
||||
} else {
|
||||
self.output(&[b' '], allow_wrap, Escaping::Literal);
|
||||
write!(self, "**").unwrap();
|
||||
}
|
||||
},
|
||||
NodeValue::Code(ref literal) => if entering {
|
||||
let numticks = shortest_unused_sequence(literal, b'`');
|
||||
for _ in 0..numticks {
|
||||
write!(self, "`").unwrap();
|
||||
}
|
||||
let pad = literal.is_empty() ||
|
||||
literal[0] == b'`' || literal[literal.len() - 1] == b'`' ||
|
||||
literal[0] == b' ' || literal[literal.len() - 1] == b' ';
|
||||
if pad {
|
||||
write!(self, " ").unwrap();
|
||||
}
|
||||
self.output(literal, allow_wrap, Escaping::Literal);
|
||||
if pad {
|
||||
write!(self, " ").unwrap();
|
||||
}
|
||||
for _ in 0..numticks {
|
||||
write!(self, "`").unwrap();
|
||||
}
|
||||
},
|
||||
NodeValue::HtmlInline(ref literal) => if entering {
|
||||
self.write_all(literal).unwrap();
|
||||
},
|
||||
NodeValue::Strong => if entering {
|
||||
write!(self, "**").unwrap();
|
||||
} else {
|
||||
write!(self, "**").unwrap();
|
||||
},
|
||||
}
|
||||
NodeValue::Emph => {
|
||||
let emph_delim = if match node.parent() {
|
||||
Some(parent) => match parent.data.borrow().value {
|
||||
|
@ -500,58 +535,68 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
|
|||
|
||||
self.write_all(&[emph_delim]).unwrap();
|
||||
}
|
||||
NodeValue::TaskItem(checked) => if entering {
|
||||
if checked {
|
||||
write!(self, "[x] ").unwrap();
|
||||
} else {
|
||||
write!(self, "[ ] ").unwrap();
|
||||
}
|
||||
},
|
||||
NodeValue::Strikethrough => if entering {
|
||||
write!(self, "~").unwrap();
|
||||
} else {
|
||||
write!(self, "~").unwrap();
|
||||
},
|
||||
NodeValue::Superscript => if entering {
|
||||
write!(self, "^").unwrap();
|
||||
} else {
|
||||
write!(self, "^").unwrap();
|
||||
},
|
||||
NodeValue::Link(ref nl) => if is_autolink(node, nl) {
|
||||
NodeValue::TaskItem(checked) => {
|
||||
if entering {
|
||||
write!(self, "<").unwrap();
|
||||
if nl.url.len() >= 7 && &nl.url[..7] == b"mailto:" {
|
||||
self.write_all(&nl.url[7..]).unwrap();
|
||||
if checked {
|
||||
write!(self, "[x] ").unwrap();
|
||||
} else {
|
||||
self.write_all(&nl.url).unwrap();
|
||||
write!(self, "[ ] ").unwrap();
|
||||
}
|
||||
write!(self, ">").unwrap();
|
||||
return false;
|
||||
}
|
||||
} else if entering {
|
||||
write!(self, "[").unwrap();
|
||||
} else {
|
||||
write!(self, "](").unwrap();
|
||||
self.output(&nl.url, false, Escaping::URL);
|
||||
if !nl.title.is_empty() {
|
||||
write!(self, " \"").unwrap();
|
||||
self.output(&nl.title, false, Escaping::Title);
|
||||
write!(self, "\"").unwrap();
|
||||
}
|
||||
NodeValue::Strikethrough => {
|
||||
if entering {
|
||||
write!(self, "~").unwrap();
|
||||
} else {
|
||||
write!(self, "~").unwrap();
|
||||
}
|
||||
write!(self, ")").unwrap();
|
||||
},
|
||||
NodeValue::Image(ref nl) => if entering {
|
||||
write!(self, "![").unwrap();
|
||||
} else {
|
||||
write!(self, "](").unwrap();
|
||||
self.output(&nl.url, false, Escaping::URL);
|
||||
if !nl.title.is_empty() {
|
||||
self.output(&[b' ', b'"'], allow_wrap, Escaping::Literal);
|
||||
self.output(&nl.title, false, Escaping::Title);
|
||||
write!(self, "\"").unwrap();
|
||||
}
|
||||
NodeValue::Superscript => {
|
||||
if entering {
|
||||
write!(self, "^").unwrap();
|
||||
} else {
|
||||
write!(self, "^").unwrap();
|
||||
}
|
||||
write!(self, ")").unwrap();
|
||||
},
|
||||
}
|
||||
NodeValue::Link(ref nl) => {
|
||||
if is_autolink(node, nl) {
|
||||
if entering {
|
||||
write!(self, "<").unwrap();
|
||||
if nl.url.len() >= 7 && &nl.url[..7] == b"mailto:" {
|
||||
self.write_all(&nl.url[7..]).unwrap();
|
||||
} else {
|
||||
self.write_all(&nl.url).unwrap();
|
||||
}
|
||||
write!(self, ">").unwrap();
|
||||
return false;
|
||||
}
|
||||
} else if entering {
|
||||
write!(self, "[").unwrap();
|
||||
} else {
|
||||
write!(self, "](").unwrap();
|
||||
self.output(&nl.url, false, Escaping::URL);
|
||||
if !nl.title.is_empty() {
|
||||
write!(self, " \"").unwrap();
|
||||
self.output(&nl.title, false, Escaping::Title);
|
||||
write!(self, "\"").unwrap();
|
||||
}
|
||||
write!(self, ")").unwrap();
|
||||
}
|
||||
}
|
||||
NodeValue::Image(ref nl) => {
|
||||
if entering {
|
||||
write!(self, "![").unwrap();
|
||||
} else {
|
||||
write!(self, "](").unwrap();
|
||||
self.output(&nl.url, false, Escaping::URL);
|
||||
if !nl.title.is_empty() {
|
||||
self.output(&[b' ', b'"'], allow_wrap, Escaping::Literal);
|
||||
self.output(&nl.title, false, Escaping::Title);
|
||||
write!(self, "\"").unwrap();
|
||||
}
|
||||
write!(self, ")").unwrap();
|
||||
}
|
||||
}
|
||||
NodeValue::Table(..) => {
|
||||
if entering {
|
||||
self.custom_escape = Some(table_escape);
|
||||
|
@ -560,59 +605,68 @@ impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
|
|||
}
|
||||
self.blankline();
|
||||
}
|
||||
NodeValue::TableRow(..) => if entering {
|
||||
self.cr();
|
||||
write!(self, "|").unwrap();
|
||||
},
|
||||
NodeValue::TableCell => if entering {
|
||||
write!(self, " ").unwrap();
|
||||
} else {
|
||||
write!(self, " |").unwrap();
|
||||
NodeValue::TableRow(..) => {
|
||||
if entering {
|
||||
self.cr();
|
||||
write!(self, "|").unwrap();
|
||||
}
|
||||
}
|
||||
NodeValue::TableCell => {
|
||||
if entering {
|
||||
write!(self, " ").unwrap();
|
||||
} else {
|
||||
write!(self, " |").unwrap();
|
||||
|
||||
let row = &node.parent().unwrap().data.borrow().value;
|
||||
let in_header = match *row {
|
||||
NodeValue::TableRow(header) => header,
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
if in_header && node.next_sibling().is_none() {
|
||||
let table = &node.parent().unwrap().parent().unwrap().data.borrow().value;
|
||||
let alignments = match *table {
|
||||
NodeValue::Table(ref alignments) => alignments,
|
||||
let row = &node.parent().unwrap().data.borrow().value;
|
||||
let in_header = match *row {
|
||||
NodeValue::TableRow(header) => header,
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
self.cr();
|
||||
write!(self, "|").unwrap();
|
||||
for a in alignments {
|
||||
write!(
|
||||
self,
|
||||
" {} |",
|
||||
match *a {
|
||||
TableAlignment::Left => ":--",
|
||||
TableAlignment::Center => ":-:",
|
||||
TableAlignment::Right => "--:",
|
||||
TableAlignment::None => "---",
|
||||
}
|
||||
).unwrap();
|
||||
if in_header && node.next_sibling().is_none() {
|
||||
let table = &node.parent().unwrap().parent().unwrap().data.borrow().value;
|
||||
let alignments = match *table {
|
||||
NodeValue::Table(ref alignments) => alignments,
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
self.cr();
|
||||
write!(self, "|").unwrap();
|
||||
for a in alignments {
|
||||
write!(
|
||||
self,
|
||||
" {} |",
|
||||
match *a {
|
||||
TableAlignment::Left => ":--",
|
||||
TableAlignment::Center => ":-:",
|
||||
TableAlignment::Right => "--:",
|
||||
TableAlignment::None => "---",
|
||||
}
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
self.cr();
|
||||
}
|
||||
self.cr();
|
||||
}
|
||||
},
|
||||
NodeValue::FootnoteDefinition(_) => if entering {
|
||||
self.footnote_ix += 1;
|
||||
let footnote_ix = self.footnote_ix;
|
||||
write!(self, "[^{}]:\n", footnote_ix).unwrap();
|
||||
write!(self.prefix, " ").unwrap();
|
||||
} else {
|
||||
let new_len = self.prefix.len() - 4;
|
||||
self.prefix.truncate(new_len);
|
||||
},
|
||||
NodeValue::FootnoteReference(ref r) => if entering {
|
||||
self.write_all(b"[^").unwrap();
|
||||
self.write_all(r).unwrap();
|
||||
self.write_all(b"]").unwrap();
|
||||
},
|
||||
}
|
||||
NodeValue::FootnoteDefinition(_) => {
|
||||
if entering {
|
||||
self.footnote_ix += 1;
|
||||
let footnote_ix = self.footnote_ix;
|
||||
write!(self, "[^{}]:\n", footnote_ix).unwrap();
|
||||
write!(self.prefix, " ").unwrap();
|
||||
} else {
|
||||
let new_len = self.prefix.len() - 4;
|
||||
self.prefix.truncate(new_len);
|
||||
}
|
||||
}
|
||||
NodeValue::FootnoteReference(ref r) => {
|
||||
if entering {
|
||||
self.write_all(b"[^").unwrap();
|
||||
self.write_all(r).unwrap();
|
||||
self.write_all(b"]").unwrap();
|
||||
}
|
||||
}
|
||||
};
|
||||
true
|
||||
}
|
||||
|
|
492
src/html.rs
492
src/html.rs
|
@ -89,8 +89,7 @@ impl Anchorizer {
|
|||
/// ```
|
||||
pub fn anchorize(&mut self, header: String) -> String {
|
||||
lazy_static! {
|
||||
static ref REJECTED_CHARS: Regex =
|
||||
Regex::new(r"[^\p{L}\p{M}\p{N}\p{Pc} -]").unwrap();
|
||||
static ref REJECTED_CHARS: Regex = Regex::new(r"[^\p{L}\p{M}\p{N}\p{Pc} -]").unwrap();
|
||||
}
|
||||
|
||||
let mut id = header;
|
||||
|
@ -374,59 +373,73 @@ impl<'o> HtmlFormatter<'o> {
|
|||
output.extend_from_slice(literal)
|
||||
}
|
||||
NodeValue::LineBreak | NodeValue::SoftBreak => output.push(b' '),
|
||||
_ => for n in node.children() {
|
||||
self.collect_text(n, output);
|
||||
},
|
||||
_ => {
|
||||
for n in node.children() {
|
||||
self.collect_text(n, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn format_node<'a>(&mut self, node: &'a AstNode<'a>, entering: bool) -> io::Result<bool> {
|
||||
match node.data.borrow().value {
|
||||
NodeValue::Document => (),
|
||||
NodeValue::BlockQuote => if entering {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"<blockquote>\n")?;
|
||||
} else {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"</blockquote>\n")?;
|
||||
},
|
||||
NodeValue::List(ref nl) => if entering {
|
||||
self.cr()?;
|
||||
if nl.list_type == ListType::Bullet {
|
||||
self.output.write_all(b"<ul>\n")?;
|
||||
} else if nl.start == 1 {
|
||||
self.output.write_all(b"<ol>\n")?;
|
||||
NodeValue::BlockQuote => {
|
||||
if entering {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"<blockquote>\n")?;
|
||||
} else {
|
||||
write!(self.output, "<ol start=\"{}\">\n", nl.start)?;
|
||||
self.cr()?;
|
||||
self.output.write_all(b"</blockquote>\n")?;
|
||||
}
|
||||
} else if nl.list_type == ListType::Bullet {
|
||||
self.output.write_all(b"</ul>\n")?;
|
||||
} else {
|
||||
self.output.write_all(b"</ol>\n")?;
|
||||
},
|
||||
NodeValue::Item(..) => if entering {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"<li>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</li>\n")?;
|
||||
},
|
||||
NodeValue::DescriptionList => if entering {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"<dl>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</dl>\n")?;
|
||||
},
|
||||
}
|
||||
NodeValue::List(ref nl) => {
|
||||
if entering {
|
||||
self.cr()?;
|
||||
if nl.list_type == ListType::Bullet {
|
||||
self.output.write_all(b"<ul>\n")?;
|
||||
} else if nl.start == 1 {
|
||||
self.output.write_all(b"<ol>\n")?;
|
||||
} else {
|
||||
write!(self.output, "<ol start=\"{}\">\n", nl.start)?;
|
||||
}
|
||||
} else if nl.list_type == ListType::Bullet {
|
||||
self.output.write_all(b"</ul>\n")?;
|
||||
} else {
|
||||
self.output.write_all(b"</ol>\n")?;
|
||||
}
|
||||
}
|
||||
NodeValue::Item(..) => {
|
||||
if entering {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"<li>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</li>\n")?;
|
||||
}
|
||||
}
|
||||
NodeValue::DescriptionList => {
|
||||
if entering {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"<dl>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</dl>\n")?;
|
||||
}
|
||||
}
|
||||
NodeValue::DescriptionItem(..) => (),
|
||||
NodeValue::DescriptionTerm => if entering {
|
||||
self.output.write_all(b"<dt>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</dt>\n")?;
|
||||
},
|
||||
NodeValue::DescriptionDetails => if entering {
|
||||
self.output.write_all(b"<dd>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</dd>\n")?;
|
||||
},
|
||||
NodeValue::DescriptionTerm => {
|
||||
if entering {
|
||||
self.output.write_all(b"<dt>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</dt>\n")?;
|
||||
}
|
||||
}
|
||||
NodeValue::DescriptionDetails => {
|
||||
if entering {
|
||||
self.output.write_all(b"<dd>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</dd>\n")?;
|
||||
}
|
||||
}
|
||||
NodeValue::Heading(ref nch) => {
|
||||
if entering {
|
||||
self.cr()?;
|
||||
|
@ -450,47 +463,53 @@ impl<'o> HtmlFormatter<'o> {
|
|||
write!(self.output, "</h{}>\n", nch.level)?;
|
||||
}
|
||||
}
|
||||
NodeValue::CodeBlock(ref ncb) => if entering {
|
||||
self.cr()?;
|
||||
NodeValue::CodeBlock(ref ncb) => {
|
||||
if entering {
|
||||
self.cr()?;
|
||||
|
||||
if ncb.info.is_empty() {
|
||||
self.output.write_all(b"<pre><code>")?;
|
||||
} else {
|
||||
let mut first_tag = 0;
|
||||
while first_tag < ncb.info.len() && !isspace(ncb.info[first_tag]) {
|
||||
first_tag += 1;
|
||||
}
|
||||
|
||||
if self.options.render.github_pre_lang {
|
||||
self.output.write_all(b"<pre lang=\"")?;
|
||||
self.escape(&ncb.info[..first_tag])?;
|
||||
self.output.write_all(b"\"><code>")?;
|
||||
if ncb.info.is_empty() {
|
||||
self.output.write_all(b"<pre><code>")?;
|
||||
} else {
|
||||
self.output.write_all(b"<pre><code class=\"language-")?;
|
||||
self.escape(&ncb.info[..first_tag])?;
|
||||
self.output.write_all(b"\">")?;
|
||||
let mut first_tag = 0;
|
||||
while first_tag < ncb.info.len() && !isspace(ncb.info[first_tag]) {
|
||||
first_tag += 1;
|
||||
}
|
||||
|
||||
if self.options.render.github_pre_lang {
|
||||
self.output.write_all(b"<pre lang=\"")?;
|
||||
self.escape(&ncb.info[..first_tag])?;
|
||||
self.output.write_all(b"\"><code>")?;
|
||||
} else {
|
||||
self.output.write_all(b"<pre><code class=\"language-")?;
|
||||
self.escape(&ncb.info[..first_tag])?;
|
||||
self.output.write_all(b"\">")?;
|
||||
}
|
||||
}
|
||||
self.escape(&ncb.literal)?;
|
||||
self.output.write_all(b"</code></pre>\n")?;
|
||||
}
|
||||
self.escape(&ncb.literal)?;
|
||||
self.output.write_all(b"</code></pre>\n")?;
|
||||
},
|
||||
NodeValue::HtmlBlock(ref nhb) => if entering {
|
||||
self.cr()?;
|
||||
if self.options.render.escape {
|
||||
self.escape(&nhb.literal)?;
|
||||
} else if !self.options.render.unsafe_ {
|
||||
self.output.write_all(b"<!-- raw HTML omitted -->")?;
|
||||
} else if self.options.extension.tagfilter {
|
||||
tagfilter_block(&nhb.literal, &mut self.output)?;
|
||||
} else {
|
||||
self.output.write_all(&nhb.literal)?;
|
||||
}
|
||||
NodeValue::HtmlBlock(ref nhb) => {
|
||||
if entering {
|
||||
self.cr()?;
|
||||
if self.options.render.escape {
|
||||
self.escape(&nhb.literal)?;
|
||||
} else if !self.options.render.unsafe_ {
|
||||
self.output.write_all(b"<!-- raw HTML omitted -->")?;
|
||||
} else if self.options.extension.tagfilter {
|
||||
tagfilter_block(&nhb.literal, &mut self.output)?;
|
||||
} else {
|
||||
self.output.write_all(&nhb.literal)?;
|
||||
}
|
||||
self.cr()?;
|
||||
}
|
||||
self.cr()?;
|
||||
},
|
||||
NodeValue::ThematicBreak => if entering {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"<hr />\n")?;
|
||||
},
|
||||
}
|
||||
NodeValue::ThematicBreak => {
|
||||
if entering {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"<hr />\n")?;
|
||||
}
|
||||
}
|
||||
NodeValue::Paragraph => {
|
||||
let tight = match node
|
||||
.parent()
|
||||
|
@ -501,11 +520,11 @@ impl<'o> HtmlFormatter<'o> {
|
|||
_ => false,
|
||||
};
|
||||
|
||||
let tight = tight || (match node.parent().map(|n| n.data.borrow().value.clone())
|
||||
{
|
||||
Some(NodeValue::DescriptionTerm) => true,
|
||||
_ => false,
|
||||
});
|
||||
let tight = tight
|
||||
|| (match node.parent().map(|n| n.data.borrow().value.clone()) {
|
||||
Some(NodeValue::DescriptionTerm) => true,
|
||||
_ => false,
|
||||
});
|
||||
|
||||
if !tight {
|
||||
if entering {
|
||||
|
@ -524,116 +543,142 @@ impl<'o> HtmlFormatter<'o> {
|
|||
}
|
||||
}
|
||||
}
|
||||
NodeValue::Text(ref literal) => if entering {
|
||||
self.escape(literal)?;
|
||||
},
|
||||
NodeValue::LineBreak => if entering {
|
||||
self.output.write_all(b"<br />\n")?;
|
||||
},
|
||||
NodeValue::SoftBreak => if entering {
|
||||
if self.options.render.hardbreaks {
|
||||
NodeValue::Text(ref literal) => {
|
||||
if entering {
|
||||
self.escape(literal)?;
|
||||
}
|
||||
}
|
||||
NodeValue::LineBreak => {
|
||||
if entering {
|
||||
self.output.write_all(b"<br />\n")?;
|
||||
} else {
|
||||
self.output.write_all(b"\n")?;
|
||||
}
|
||||
},
|
||||
NodeValue::Code(ref literal) => if entering {
|
||||
self.output.write_all(b"<code>")?;
|
||||
self.escape(literal)?;
|
||||
self.output.write_all(b"</code>")?;
|
||||
},
|
||||
NodeValue::HtmlInline(ref literal) => if entering {
|
||||
if self.options.render.escape {
|
||||
self.escape(&literal)?;
|
||||
} else if !self.options.render.unsafe_ {
|
||||
self.output.write_all(b"<!-- raw HTML omitted -->")?;
|
||||
} else if self.options.extension.tagfilter && tagfilter(literal) {
|
||||
self.output.write_all(b"<")?;
|
||||
self.output.write_all(&literal[1..])?;
|
||||
} else {
|
||||
self.output.write_all(literal)?;
|
||||
}
|
||||
},
|
||||
NodeValue::Strong => if entering {
|
||||
self.output.write_all(b"<strong>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</strong>")?;
|
||||
},
|
||||
NodeValue::Emph => if entering {
|
||||
self.output.write_all(b"<em>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</em>")?;
|
||||
},
|
||||
NodeValue::Strikethrough => if entering {
|
||||
self.output.write_all(b"<del>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</del>")?;
|
||||
},
|
||||
NodeValue::Superscript => if entering {
|
||||
self.output.write_all(b"<sup>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</sup>")?;
|
||||
},
|
||||
NodeValue::Link(ref nl) => if entering {
|
||||
self.output.write_all(b"<a href=\"")?;
|
||||
if self.options.render.unsafe_ || !dangerous_url(&nl.url) {
|
||||
self.escape_href(&nl.url)?;
|
||||
}
|
||||
if !nl.title.is_empty() {
|
||||
self.output.write_all(b"\" title=\"")?;
|
||||
self.escape(&nl.title)?;
|
||||
}
|
||||
self.output.write_all(b"\">")?;
|
||||
} else {
|
||||
self.output.write_all(b"</a>")?;
|
||||
},
|
||||
NodeValue::Image(ref nl) => if entering {
|
||||
self.output.write_all(b"<img src=\"")?;
|
||||
if self.options.render.unsafe_ || !dangerous_url(&nl.url) {
|
||||
self.escape_href(&nl.url)?;
|
||||
}
|
||||
self.output.write_all(b"\" alt=\"")?;
|
||||
return Ok(true);
|
||||
} else {
|
||||
if !nl.title.is_empty() {
|
||||
self.output.write_all(b"\" title=\"")?;
|
||||
self.escape(&nl.title)?;
|
||||
}
|
||||
self.output.write_all(b"\" />")?;
|
||||
},
|
||||
NodeValue::Table(..) => if entering {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"<table>\n")?;
|
||||
} else {
|
||||
if !node
|
||||
.last_child()
|
||||
.unwrap()
|
||||
.same_node(node.first_child().unwrap())
|
||||
{
|
||||
self.cr()?;
|
||||
self.output.write_all(b"</tbody>\n")?;
|
||||
}
|
||||
self.cr()?;
|
||||
self.output.write_all(b"</table>\n")?;
|
||||
},
|
||||
NodeValue::TableRow(header) => if entering {
|
||||
self.cr()?;
|
||||
if header {
|
||||
self.output.write_all(b"<thead>\n")?;
|
||||
} else if let Some(n) = node.previous_sibling() {
|
||||
if let NodeValue::TableRow(true) = n.data.borrow().value {
|
||||
self.output.write_all(b"<tbody>\n")?;
|
||||
}
|
||||
NodeValue::SoftBreak => {
|
||||
if entering {
|
||||
if self.options.render.hardbreaks {
|
||||
self.output.write_all(b"<br />\n")?;
|
||||
} else {
|
||||
self.output.write_all(b"\n")?;
|
||||
}
|
||||
}
|
||||
self.output.write_all(b"<tr>")?;
|
||||
} else {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"</tr>")?;
|
||||
if header {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"</thead>")?;
|
||||
}
|
||||
NodeValue::Code(ref literal) => {
|
||||
if entering {
|
||||
self.output.write_all(b"<code>")?;
|
||||
self.escape(literal)?;
|
||||
self.output.write_all(b"</code>")?;
|
||||
}
|
||||
},
|
||||
}
|
||||
NodeValue::HtmlInline(ref literal) => {
|
||||
if entering {
|
||||
if self.options.render.escape {
|
||||
self.escape(&literal)?;
|
||||
} else if !self.options.render.unsafe_ {
|
||||
self.output.write_all(b"<!-- raw HTML omitted -->")?;
|
||||
} else if self.options.extension.tagfilter && tagfilter(literal) {
|
||||
self.output.write_all(b"<")?;
|
||||
self.output.write_all(&literal[1..])?;
|
||||
} else {
|
||||
self.output.write_all(literal)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
NodeValue::Strong => {
|
||||
if entering {
|
||||
self.output.write_all(b"<strong>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</strong>")?;
|
||||
}
|
||||
}
|
||||
NodeValue::Emph => {
|
||||
if entering {
|
||||
self.output.write_all(b"<em>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</em>")?;
|
||||
}
|
||||
}
|
||||
NodeValue::Strikethrough => {
|
||||
if entering {
|
||||
self.output.write_all(b"<del>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</del>")?;
|
||||
}
|
||||
}
|
||||
NodeValue::Superscript => {
|
||||
if entering {
|
||||
self.output.write_all(b"<sup>")?;
|
||||
} else {
|
||||
self.output.write_all(b"</sup>")?;
|
||||
}
|
||||
}
|
||||
NodeValue::Link(ref nl) => {
|
||||
if entering {
|
||||
self.output.write_all(b"<a href=\"")?;
|
||||
if self.options.render.unsafe_ || !dangerous_url(&nl.url) {
|
||||
self.escape_href(&nl.url)?;
|
||||
}
|
||||
if !nl.title.is_empty() {
|
||||
self.output.write_all(b"\" title=\"")?;
|
||||
self.escape(&nl.title)?;
|
||||
}
|
||||
self.output.write_all(b"\">")?;
|
||||
} else {
|
||||
self.output.write_all(b"</a>")?;
|
||||
}
|
||||
}
|
||||
NodeValue::Image(ref nl) => {
|
||||
if entering {
|
||||
self.output.write_all(b"<img src=\"")?;
|
||||
if self.options.render.unsafe_ || !dangerous_url(&nl.url) {
|
||||
self.escape_href(&nl.url)?;
|
||||
}
|
||||
self.output.write_all(b"\" alt=\"")?;
|
||||
return Ok(true);
|
||||
} else {
|
||||
if !nl.title.is_empty() {
|
||||
self.output.write_all(b"\" title=\"")?;
|
||||
self.escape(&nl.title)?;
|
||||
}
|
||||
self.output.write_all(b"\" />")?;
|
||||
}
|
||||
}
|
||||
NodeValue::Table(..) => {
|
||||
if entering {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"<table>\n")?;
|
||||
} else {
|
||||
if !node
|
||||
.last_child()
|
||||
.unwrap()
|
||||
.same_node(node.first_child().unwrap())
|
||||
{
|
||||
self.cr()?;
|
||||
self.output.write_all(b"</tbody>\n")?;
|
||||
}
|
||||
self.cr()?;
|
||||
self.output.write_all(b"</table>\n")?;
|
||||
}
|
||||
}
|
||||
NodeValue::TableRow(header) => {
|
||||
if entering {
|
||||
self.cr()?;
|
||||
if header {
|
||||
self.output.write_all(b"<thead>\n")?;
|
||||
} else if let Some(n) = node.previous_sibling() {
|
||||
if let NodeValue::TableRow(true) = n.data.borrow().value {
|
||||
self.output.write_all(b"<tbody>\n")?;
|
||||
}
|
||||
}
|
||||
self.output.write_all(b"<tr>")?;
|
||||
} else {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"</tr>")?;
|
||||
if header {
|
||||
self.cr()?;
|
||||
self.output.write_all(b"</thead>")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
NodeValue::TableCell => {
|
||||
let row = &node.parent().unwrap().data.borrow().value;
|
||||
let in_header = match *row {
|
||||
|
@ -682,36 +727,43 @@ impl<'o> HtmlFormatter<'o> {
|
|||
self.output.write_all(b"</td>")?;
|
||||
}
|
||||
}
|
||||
NodeValue::FootnoteDefinition(_) => if entering {
|
||||
if self.footnote_ix == 0 {
|
||||
self.output
|
||||
.write_all(b"<section class=\"footnotes\">\n<ol>\n")?;
|
||||
}
|
||||
self.footnote_ix += 1;
|
||||
write!(self.output, "<li id=\"fn{}\">\n", self.footnote_ix)?;
|
||||
} else {
|
||||
if self.put_footnote_backref()? {
|
||||
self.output.write_all(b"\n")?;
|
||||
}
|
||||
self.output.write_all(b"</li>\n")?;
|
||||
},
|
||||
NodeValue::FootnoteReference(ref r) => if entering {
|
||||
let r = str::from_utf8(r).unwrap();
|
||||
write!(
|
||||
self.output,
|
||||
"<sup class=\"footnote-ref\"><a href=\"#fn{}\" id=\"fnref{}\">{}</a></sup>",
|
||||
r, r, r
|
||||
)?;
|
||||
},
|
||||
NodeValue::TaskItem(checked) => if entering {
|
||||
if checked {
|
||||
self.output
|
||||
.write_all(b"<input type=\"checkbox\" disabled=\"\" checked=\"\" /> ")?;
|
||||
NodeValue::FootnoteDefinition(_) => {
|
||||
if entering {
|
||||
if self.footnote_ix == 0 {
|
||||
self.output
|
||||
.write_all(b"<section class=\"footnotes\">\n<ol>\n")?;
|
||||
}
|
||||
self.footnote_ix += 1;
|
||||
write!(self.output, "<li id=\"fn{}\">\n", self.footnote_ix)?;
|
||||
} else {
|
||||
self.output
|
||||
.write_all(b"<input type=\"checkbox\" disabled=\"\" /> ")?;
|
||||
if self.put_footnote_backref()? {
|
||||
self.output.write_all(b"\n")?;
|
||||
}
|
||||
self.output.write_all(b"</li>\n")?;
|
||||
}
|
||||
},
|
||||
}
|
||||
NodeValue::FootnoteReference(ref r) => {
|
||||
if entering {
|
||||
let r = str::from_utf8(r).unwrap();
|
||||
write!(
|
||||
self.output,
|
||||
"<sup class=\"footnote-ref\"><a href=\"#fn{}\" id=\"fnref{}\">{}</a></sup>",
|
||||
r, r, r
|
||||
)?;
|
||||
}
|
||||
}
|
||||
NodeValue::TaskItem(checked) => {
|
||||
if entering {
|
||||
if checked {
|
||||
self.output.write_all(
|
||||
b"<input type=\"checkbox\" disabled=\"\" checked=\"\" /> ",
|
||||
)?;
|
||||
} else {
|
||||
self.output
|
||||
.write_all(b"<input type=\"checkbox\" disabled=\"\" /> ")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
|
|
@ -100,9 +100,12 @@ mod tests;
|
|||
|
||||
pub use cm::format_document as format_commonmark;
|
||||
pub use html::format_document as format_html;
|
||||
pub use parser::{parse_document, parse_document_with_broken_link_callback, ComrakOptions, ComrakExtensionOptions, ComrakParseOptions, ComrakRenderOptions};
|
||||
pub use typed_arena::Arena;
|
||||
pub use html::Anchorizer;
|
||||
pub use parser::{
|
||||
parse_document, parse_document_with_broken_link_callback, ComrakExtensionOptions,
|
||||
ComrakOptions, ComrakParseOptions, ComrakRenderOptions,
|
||||
};
|
||||
pub use typed_arena::Arena;
|
||||
|
||||
/// Render Markdown to HTML.
|
||||
///
|
||||
|
|
28
src/main.rs
28
src/main.rs
|
@ -9,7 +9,9 @@ extern crate shell_words;
|
|||
#[cfg(not(windows))]
|
||||
extern crate xdg;
|
||||
|
||||
use comrak::{Arena, ComrakOptions, ComrakExtensionOptions, ComrakParseOptions, ComrakRenderOptions};
|
||||
use comrak::{
|
||||
Arena, ComrakExtensionOptions, ComrakOptions, ComrakParseOptions, ComrakRenderOptions,
|
||||
};
|
||||
|
||||
use std::boxed::Box;
|
||||
use std::collections::BTreeSet;
|
||||
|
@ -147,11 +149,11 @@ if the file does not exist.\
|
|||
}
|
||||
}
|
||||
matches = app.get_matches_from(args);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("failed to parse {}: {}", config_file_path, e);
|
||||
process::exit(EXIT_PARSE_CONFIG);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,17 +204,19 @@ if the file does not exist.\
|
|||
None => {
|
||||
std::io::stdin().read_to_end(&mut s)?;
|
||||
}
|
||||
Some(fs) => for f in fs {
|
||||
match fs::File::open(f) {
|
||||
Ok(mut io) => {
|
||||
io.read_to_end(&mut s)?;
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("failed to read {}: {}", f, e);
|
||||
process::exit(EXIT_READ_INPUT);
|
||||
Some(fs) => {
|
||||
for f in fs {
|
||||
match fs::File::open(f) {
|
||||
Ok(mut io) => {
|
||||
io.read_to_end(&mut s)?;
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("failed to read {}: {}", f, e);
|
||||
process::exit(EXIT_READ_INPUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
let arena = Arena::new();
|
||||
|
|
|
@ -418,10 +418,11 @@ pub fn can_contain_type<'a>(node: &'a AstNode<'a>, child: &NodeValue) -> bool {
|
|||
| NodeValue::DescriptionTerm
|
||||
| NodeValue::DescriptionDetails
|
||||
| NodeValue::Item(..) => {
|
||||
child.block() && match *child {
|
||||
NodeValue::Item(..) => false,
|
||||
_ => true,
|
||||
}
|
||||
child.block()
|
||||
&& match *child {
|
||||
NodeValue::Item(..) => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
NodeValue::List(..) => match *child {
|
||||
|
|
|
@ -136,24 +136,26 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
|
|||
new_inl = Some(make_inline(self.arena, NodeValue::Text(b"!".to_vec())));
|
||||
}
|
||||
}
|
||||
_ => if self.options.extension.strikethrough && c == '~' {
|
||||
new_inl = Some(self.handle_delim(b'~'));
|
||||
} else if self.options.extension.superscript && c == '^' {
|
||||
new_inl = Some(self.handle_delim(b'^'));
|
||||
} else {
|
||||
let endpos = self.find_special_char();
|
||||
let mut contents = self.input[self.pos..endpos].to_vec();
|
||||
self.pos = endpos;
|
||||
_ => {
|
||||
if self.options.extension.strikethrough && c == '~' {
|
||||
new_inl = Some(self.handle_delim(b'~'));
|
||||
} else if self.options.extension.superscript && c == '^' {
|
||||
new_inl = Some(self.handle_delim(b'^'));
|
||||
} else {
|
||||
let endpos = self.find_special_char();
|
||||
let mut contents = self.input[self.pos..endpos].to_vec();
|
||||
self.pos = endpos;
|
||||
|
||||
if self
|
||||
.peek_char()
|
||||
.map_or(false, |&c| strings::is_line_end_char(c))
|
||||
{
|
||||
strings::rtrim(&mut contents);
|
||||
if self
|
||||
.peek_char()
|
||||
.map_or(false, |&c| strings::is_line_end_char(c))
|
||||
{
|
||||
strings::rtrim(&mut contents);
|
||||
}
|
||||
|
||||
new_inl = Some(make_inline(self.arena, NodeValue::Text(contents)));
|
||||
}
|
||||
|
||||
new_inl = Some(make_inline(self.arena, NodeValue::Text(contents)));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(inl) = new_inl {
|
||||
|
@ -273,7 +275,8 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
|
|||
opener,
|
||||
openers_bottom[closer.unwrap().length % 3]
|
||||
[closer.unwrap().delim_char as usize],
|
||||
) {
|
||||
)
|
||||
{
|
||||
if opener.unwrap().can_open
|
||||
&& opener.unwrap().delim_char == closer.unwrap().delim_char
|
||||
{
|
||||
|
@ -617,11 +620,13 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
|
|||
.chars()
|
||||
.next()
|
||||
{
|
||||
Some(x) => if (x as usize) < 256 && self.skip_chars[x as usize] {
|
||||
'\n'
|
||||
} else {
|
||||
x
|
||||
},
|
||||
Some(x) => {
|
||||
if (x as usize) < 256 && self.skip_chars[x as usize] {
|
||||
'\n'
|
||||
} else {
|
||||
x
|
||||
}
|
||||
}
|
||||
None => '\n',
|
||||
}
|
||||
};
|
||||
|
@ -650,20 +655,24 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
|
|||
.chars()
|
||||
.next()
|
||||
{
|
||||
Some(x) => if (x as usize) < 256 && self.skip_chars[x as usize] {
|
||||
'\n'
|
||||
} else {
|
||||
x
|
||||
},
|
||||
Some(x) => {
|
||||
if (x as usize) < 256 && self.skip_chars[x as usize] {
|
||||
'\n'
|
||||
} else {
|
||||
x
|
||||
}
|
||||
}
|
||||
None => '\n',
|
||||
}
|
||||
};
|
||||
|
||||
let left_flanking = numdelims > 0 && !after_char.is_whitespace()
|
||||
let left_flanking = numdelims > 0
|
||||
&& !after_char.is_whitespace()
|
||||
&& !(after_char.is_punctuation()
|
||||
&& !before_char.is_whitespace()
|
||||
&& !before_char.is_punctuation());
|
||||
let right_flanking = numdelims > 0 && !before_char.is_whitespace()
|
||||
let right_flanking = numdelims > 0
|
||||
&& !before_char.is_whitespace()
|
||||
&& !(before_char.is_punctuation()
|
||||
&& !after_char.is_whitespace()
|
||||
&& !after_char.is_punctuation());
|
||||
|
@ -724,10 +733,9 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
|
|||
closer_num_chars -= use_delims;
|
||||
|
||||
if self.options.extension.strikethrough && opener_char == b'~' {
|
||||
if opener_num_chars != closer_num_chars ||
|
||||
opener_num_chars > 0 {
|
||||
return None
|
||||
}
|
||||
if opener_num_chars != closer_num_chars || opener_num_chars > 0 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
opener
|
||||
|
@ -960,7 +968,10 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
|
|||
// Attempt to use the provided broken link callback if a reference cannot be resolved
|
||||
if reff.is_none() {
|
||||
if let Some(ref mut callback) = self.callback {
|
||||
reff = callback(&lab).map(|(url, title)| Reference {url: url, title: title});
|
||||
reff = callback(&lab).map(|(url, title)| Reference {
|
||||
url: url,
|
||||
title: title,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -977,7 +988,8 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
|
|||
text.is_some() && n.next_sibling().is_none()
|
||||
}
|
||||
_ => false,
|
||||
} {
|
||||
}
|
||||
{
|
||||
let text = text.unwrap();
|
||||
if text.len() > 1 && text[0] == b'^' {
|
||||
let inl = make_inline(self.arena, NodeValue::FootnoteReference(text[1..].to_vec()));
|
||||
|
|
|
@ -7,8 +7,8 @@ use ctype::{isdigit, isspace};
|
|||
use entity;
|
||||
use nodes;
|
||||
use nodes::{
|
||||
Ast, AstNode, ListDelimType, ListType, NodeCodeBlock, NodeDescriptionItem,
|
||||
NodeHeading, NodeHtmlBlock, NodeList, NodeValue,
|
||||
Ast, AstNode, ListDelimType, ListType, NodeCodeBlock, NodeDescriptionItem, NodeHeading,
|
||||
NodeHtmlBlock, NodeList, NodeValue,
|
||||
};
|
||||
use regex::bytes::Regex;
|
||||
use scanners;
|
||||
|
@ -529,11 +529,13 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
self.blank = false;
|
||||
self.partially_consumed_tab = false;
|
||||
|
||||
if self.line_number == 0 && line.len() >= 3
|
||||
if self.line_number == 0
|
||||
&& line.len() >= 3
|
||||
&& unsafe { str::from_utf8_unchecked(line) }
|
||||
.chars()
|
||||
.next()
|
||||
.unwrap() == '\u{feff}'
|
||||
.unwrap()
|
||||
== '\u{feff}'
|
||||
{
|
||||
self.offset += 3;
|
||||
}
|
||||
|
@ -594,12 +596,16 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
self.find_first_nonspace(line);
|
||||
|
||||
match ast.value {
|
||||
NodeValue::BlockQuote => if !self.parse_block_quote_prefix(line) {
|
||||
return (false, container, should_continue);
|
||||
},
|
||||
NodeValue::Item(ref nl) => if !self.parse_node_item_prefix(line, container, nl) {
|
||||
return (false, container, should_continue);
|
||||
},
|
||||
NodeValue::BlockQuote => {
|
||||
if !self.parse_block_quote_prefix(line) {
|
||||
return (false, container, should_continue);
|
||||
}
|
||||
}
|
||||
NodeValue::Item(ref nl) => {
|
||||
if !self.parse_node_item_prefix(line, container, nl) {
|
||||
return (false, container, should_continue);
|
||||
}
|
||||
}
|
||||
NodeValue::DescriptionItem(ref di) => {
|
||||
if !self.parse_description_item_prefix(line, container, di) {
|
||||
return (false, container, should_continue);
|
||||
|
@ -610,12 +616,16 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
return (false, container, should_continue);
|
||||
}
|
||||
}
|
||||
NodeValue::HtmlBlock(ref nhb) => if !self.parse_html_block_prefix(nhb.block_type) {
|
||||
return (false, container, should_continue);
|
||||
},
|
||||
NodeValue::Paragraph => if self.blank {
|
||||
return (false, container, should_continue);
|
||||
},
|
||||
NodeValue::HtmlBlock(ref nhb) => {
|
||||
if !self.parse_html_block_prefix(nhb.block_type) {
|
||||
return (false, container, should_continue);
|
||||
}
|
||||
}
|
||||
NodeValue::Paragraph => {
|
||||
if self.blank {
|
||||
return (false, container, should_continue);
|
||||
}
|
||||
}
|
||||
NodeValue::Table(..) => {
|
||||
if !table::matches(&line[self.first_nonspace..]) {
|
||||
return (false, container, should_continue);
|
||||
|
@ -664,7 +674,8 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
&& unwrap_into(
|
||||
scanners::atx_heading_start(&line[self.first_nonspace..]),
|
||||
&mut matched,
|
||||
) {
|
||||
)
|
||||
{
|
||||
let heading_startpos = self.first_nonspace;
|
||||
let offset = self.offset;
|
||||
self.advance_offset(line, heading_startpos + matched - offset, false);
|
||||
|
@ -673,7 +684,8 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
let mut hashpos = line[self.first_nonspace..]
|
||||
.iter()
|
||||
.position(|&c| c == b'#')
|
||||
.unwrap() + self.first_nonspace;
|
||||
.unwrap()
|
||||
+ self.first_nonspace;
|
||||
let mut level = 0;
|
||||
while line[hashpos] == b'#' {
|
||||
level += 1;
|
||||
|
@ -688,7 +700,8 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
&& unwrap_into(
|
||||
scanners::open_code_fence(&line[self.first_nonspace..]),
|
||||
&mut matched,
|
||||
) {
|
||||
)
|
||||
{
|
||||
let first_nonspace = self.first_nonspace;
|
||||
let offset = self.offset;
|
||||
let ncb = NodeCodeBlock {
|
||||
|
@ -711,20 +724,23 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
scanners::html_block_start_7(&line[self.first_nonspace..]),
|
||||
&mut matched,
|
||||
),
|
||||
}) {
|
||||
})
|
||||
{
|
||||
let nhb = NodeHtmlBlock {
|
||||
block_type: matched as u8,
|
||||
literal: Vec::new(),
|
||||
};
|
||||
|
||||
*container = self.add_child(*container, NodeValue::HtmlBlock(nhb));
|
||||
} else if !indented && match container.data.borrow().value {
|
||||
NodeValue::Paragraph => unwrap_into(
|
||||
scanners::setext_heading_line(&line[self.first_nonspace..]),
|
||||
&mut sc,
|
||||
),
|
||||
_ => false,
|
||||
} {
|
||||
} else if !indented
|
||||
&& match container.data.borrow().value {
|
||||
NodeValue::Paragraph => unwrap_into(
|
||||
scanners::setext_heading_line(&line[self.first_nonspace..]),
|
||||
&mut sc,
|
||||
),
|
||||
_ => false,
|
||||
}
|
||||
{
|
||||
let has_content = {
|
||||
let mut ast = container.data.borrow_mut();
|
||||
self.resolve_reference_link_definitions(&mut ast.content)
|
||||
|
@ -740,21 +756,25 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
let adv = line.len() - 1 - self.offset;
|
||||
self.advance_offset(line, adv, false);
|
||||
}
|
||||
} else if !indented && match (&container.data.borrow().value, all_matched) {
|
||||
(&NodeValue::Paragraph, false) => false,
|
||||
_ => unwrap_into(
|
||||
scanners::thematic_break(&line[self.first_nonspace..]),
|
||||
&mut matched,
|
||||
),
|
||||
} {
|
||||
} else if !indented
|
||||
&& match (&container.data.borrow().value, all_matched) {
|
||||
(&NodeValue::Paragraph, false) => false,
|
||||
_ => unwrap_into(
|
||||
scanners::thematic_break(&line[self.first_nonspace..]),
|
||||
&mut matched,
|
||||
),
|
||||
}
|
||||
{
|
||||
*container = self.add_child(*container, NodeValue::ThematicBreak);
|
||||
let adv = line.len() - 1 - self.offset;
|
||||
self.advance_offset(line, adv, false);
|
||||
} else if !indented && self.options.extension.footnotes
|
||||
} else if !indented
|
||||
&& self.options.extension.footnotes
|
||||
&& unwrap_into(
|
||||
scanners::footnote_definition(&line[self.first_nonspace..]),
|
||||
&mut matched,
|
||||
) {
|
||||
)
|
||||
{
|
||||
let mut c = &line[self.first_nonspace + 2..self.first_nonspace + matched];
|
||||
c = c.split(|&e| e == b']').next().unwrap();
|
||||
let offset = self.first_nonspace + matched - self.offset;
|
||||
|
@ -770,10 +790,12 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
if strings::is_space_or_tab(line[self.offset]) {
|
||||
self.advance_offset(line, 1, true);
|
||||
}
|
||||
} else if (!indented || match container.data.borrow().value {
|
||||
NodeValue::List(..) => true,
|
||||
_ => false,
|
||||
}) && self.indent < 4
|
||||
} else if (!indented
|
||||
|| match container.data.borrow().value {
|
||||
NodeValue::List(..) => true,
|
||||
_ => false,
|
||||
})
|
||||
&& self.indent < 4
|
||||
&& unwrap_into_2(
|
||||
parse_list_marker(
|
||||
line,
|
||||
|
@ -785,7 +807,8 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
),
|
||||
&mut matched,
|
||||
&mut nl,
|
||||
) {
|
||||
)
|
||||
{
|
||||
let offset = self.first_nonspace + matched - self.offset;
|
||||
self.advance_offset(line, offset, false);
|
||||
let (save_partially_consumed_tab, save_offset, save_column) =
|
||||
|
@ -838,13 +861,15 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
};
|
||||
|
||||
match new_container {
|
||||
Some((new_container, replace)) => if replace {
|
||||
container.insert_after(new_container);
|
||||
container.detach();
|
||||
*container = new_container;
|
||||
} else {
|
||||
*container = new_container;
|
||||
},
|
||||
Some((new_container, replace)) => {
|
||||
if replace {
|
||||
container.insert_after(new_container);
|
||||
container.detach();
|
||||
*container = new_container;
|
||||
} else {
|
||||
*container = new_container;
|
||||
}
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
@ -1094,10 +1119,12 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
|
||||
if !self.current.same_node(last_matched_container)
|
||||
&& container.same_node(last_matched_container)
|
||||
&& !self.blank && match self.current.data.borrow().value {
|
||||
NodeValue::Paragraph => true,
|
||||
_ => false,
|
||||
} {
|
||||
&& !self.blank
|
||||
&& match self.current.data.borrow().value {
|
||||
NodeValue::Paragraph => true,
|
||||
_ => false,
|
||||
}
|
||||
{
|
||||
self.add_line(self.current, line);
|
||||
} else {
|
||||
while !self.current.same_node(last_matched_container) {
|
||||
|
@ -1396,9 +1423,11 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
},
|
||||
);
|
||||
}
|
||||
_ => for n in node.children() {
|
||||
Self::find_footnote_definitions(n, map);
|
||||
},
|
||||
_ => {
|
||||
for n in node.children() {
|
||||
Self::find_footnote_definitions(n, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1421,9 +1450,11 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
replace = Some(name.clone());
|
||||
}
|
||||
}
|
||||
_ => for n in node.children() {
|
||||
Self::find_footnote_references(n, map, ix);
|
||||
},
|
||||
_ => {
|
||||
for n in node.children() {
|
||||
Self::find_footnote_references(n, map, ix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mut label) = replace {
|
||||
|
@ -1547,13 +1578,16 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
);
|
||||
|
||||
let mut lab: Vec<u8> = match subj.link_label() {
|
||||
Some(lab) => if lab.is_empty() {
|
||||
return None;
|
||||
} else {
|
||||
lab
|
||||
},
|
||||
Some(lab) => {
|
||||
if lab.is_empty() {
|
||||
return None;
|
||||
} else {
|
||||
lab
|
||||
}
|
||||
}
|
||||
None => return None,
|
||||
}.to_vec();
|
||||
}
|
||||
.to_vec();
|
||||
|
||||
if subj.peek_char() != Some(&(b':')) {
|
||||
return None;
|
||||
|
@ -1569,7 +1603,11 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
|
|||
|
||||
let beforetitle = subj.pos;
|
||||
subj.spnl();
|
||||
let title_search = if subj.pos == beforetitle { None } else { scanners::link_title(&subj.input[subj.pos..]) };
|
||||
let title_search = if subj.pos == beforetitle {
|
||||
None
|
||||
} else {
|
||||
scanners::link_title(&subj.input[subj.pos..])
|
||||
};
|
||||
let title = match title_search {
|
||||
Some(matchlen) => {
|
||||
let t = &subj.input[subj.pos..subj.pos + matchlen];
|
||||
|
|
Loading…
Reference in New Issue