1
Fork 0

Fix multiple footnotes and improve testing

This commit is contained in:
Guillaume Gomez 2017-03-30 17:29:54 -06:00
parent 08a741eabf
commit 36b15f0409
2 changed files with 41 additions and 24 deletions

View file

@ -27,6 +27,7 @@
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap;
use std::default::Default; use std::default::Default;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use std::str; use std::str;
@ -135,8 +136,8 @@ macro_rules! event_loop_break {
struct ParserWrapper<'a> { struct ParserWrapper<'a> {
parser: Parser<'a>, parser: Parser<'a>,
footnotes: Vec<String>, // The key is the footnote reference. The value is the footnote definition and the id.
current_footnote_id: u16, footnotes: HashMap<String, (String, u16)>,
} }
impl<'a> ParserWrapper<'a> { impl<'a> ParserWrapper<'a> {
@ -144,18 +145,18 @@ impl<'a> ParserWrapper<'a> {
ParserWrapper { ParserWrapper {
parser: Parser::new_ext(s, pulldown_cmark::OPTION_ENABLE_TABLES | parser: Parser::new_ext(s, pulldown_cmark::OPTION_ENABLE_TABLES |
pulldown_cmark::OPTION_ENABLE_FOOTNOTES), pulldown_cmark::OPTION_ENABLE_FOOTNOTES),
footnotes: Vec::new(), footnotes: HashMap::new(),
current_footnote_id: 1,
} }
} }
pub fn next(&mut self) -> Option<Event<'a>> { pub fn next(&mut self) -> Option<Event<'a>> {
self.parser.next() self.parser.next()
} }
pub fn get_next_footnote_id(&mut self) -> u16 { pub fn get_entry(&mut self, key: &str) -> &mut (String, u16) {
let tmp = self.current_footnote_id; let new_id = self.footnotes.keys().count() + 1;
self.current_footnote_id += 1; let key = key.to_owned();
tmp self.footnotes.entry(key).or_insert((String::new(), new_id as u16))
} }
} }
@ -450,10 +451,11 @@ pub fn render(w: &mut fmt::Formatter,
fn footnote(parser: &mut ParserWrapper, buffer: &mut String, fn footnote(parser: &mut ParserWrapper, buffer: &mut String,
toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle, toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle,
mut definition: String, id: &mut Option<&mut String>) { id: &mut Option<&mut String>) {
event_loop_break!(parser, toc_builder, shorter, definition, true, id, let mut content = String::new();
event_loop_break!(parser, toc_builder, shorter, content, true, id,
Event::End(Tag::FootnoteDefinition(_))); Event::End(Tag::FootnoteDefinition(_)));
buffer.push_str(&definition); buffer.push_str(&content);
} }
fn rule(parser: &mut ParserWrapper, buffer: &mut String, toc_builder: &mut Option<TocBuilder>, fn rule(parser: &mut ParserWrapper, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
@ -507,17 +509,24 @@ pub fn render(w: &mut fmt::Formatter,
} }
Event::Start(Tag::FootnoteDefinition(ref def)) => { Event::Start(Tag::FootnoteDefinition(ref def)) => {
let mut content = String::new(); let mut content = String::new();
footnote(parser, &mut content, toc_builder, shorter, def.as_ref().to_owned(), let def = def.as_ref();
id); footnote(parser, &mut content, toc_builder, shorter, id);
let cur_len = parser.footnotes.len() + 1; let entry = parser.get_entry(def);
parser.footnotes.push(format!("<li id=\"ref{}\">{}<a href=\"#supref{0}\" \ let cur_id = (*entry).1;
rev=\"footnote\">↩</a></li>", (*entry).0.push_str(&format!("<li id=\"ref{}\">{}&nbsp;<a href=\"#supref{0}\" \
cur_len, content)); rev=\"footnote\">↩</a></p></li>",
cur_id,
if content.ends_with("</p>") {
&content[..content.len() - 4]
} else {
&content
}));
} }
Event::FootnoteReference(_) => { Event::FootnoteReference(ref reference) => {
let entry = parser.get_entry(reference.as_ref());
buffer.push_str(&format!("<sup id=\"supref{0}\"><a href=\"#ref{0}\">{0}</a>\ buffer.push_str(&format!("<sup id=\"supref{0}\"><a href=\"#ref{0}\">{0}</a>\
</sup>", </sup>",
parser.get_next_footnote_id())); (*entry).1));
} }
Event::Html(h) | Event::InlineHtml(h) => { Event::Html(h) | Event::InlineHtml(h) => {
buffer.push_str(&*h); buffer.push_str(&*h);
@ -545,7 +554,10 @@ pub fn render(w: &mut fmt::Formatter,
} }
if !parser.footnotes.is_empty() { if !parser.footnotes.is_empty() {
buffer.push_str(&format!("<div class=\"footnotes\"><hr><ol>{}</ol></div>", buffer.push_str(&format!("<div class=\"footnotes\"><hr><ol>{}</ol></div>",
parser.footnotes.join(""))); parser.footnotes.values()
.map(|&(ref s, _)| s.as_str())
.collect::<Vec<_>>()
.join("")));
} }
let mut ret = toc_builder.map_or(Ok(()), |builder| { let mut ret = toc_builder.map_or(Ok(()), |builder| {
write!(w, "<nav id=\"TOC\">{}</nav>", builder.into_toc()) write!(w, "<nav id=\"TOC\">{}</nav>", builder.into_toc())

View file

@ -10,11 +10,10 @@
#![crate_name = "foo"] #![crate_name = "foo"]
// ignore-tidy-linelength
// @has foo/fn.f.html // @has foo/fn.f.html
// @has - '<p>hard break: after hard break</p><hr>' // @has - '<p>markdown test</p><p>this is a <a href="https://example.com" title="this is a title">link</a>.</p><p>hard break: after hard break</p><hr><p>a footnote<sup id="supref1"><a href="#ref1">1</a></sup>.</p><p>another footnote<sup id="supref2"><a href="#ref2">2</a></sup>.</p><p><img src="https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png" alt="Rust"></p><div class="footnotes"><hr><ol><li id="ref1"><p>Thing&nbsp;<a href="#supref1" rev="footnote">↩</a></p></li><li id="ref2"><p>Another Thing&nbsp;<a href="#supref2" rev="footnote">↩</a></p></li></ol></div>'
// @has - '<img src="https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png" alt="Rust">'
// @has - '<li id="ref1">'
// @has - '<sup id="supref1"><a href="#ref1">1</a></sup>'
/// markdown test /// markdown test
/// ///
/// this is a [link]. /// this is a [link].
@ -28,8 +27,14 @@
/// ///
/// a footnote[^footnote]. /// a footnote[^footnote].
/// ///
/// another footnote[^footnotebis].
///
/// [^footnote]: Thing /// [^footnote]: Thing
/// ///
///
/// [^footnotebis]: Another Thing
///
///
/// ![Rust](https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png) /// ![Rust](https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png)
#[deprecated(note = "Struct<T>")] #[deprecated(note = "Struct<T>")]
pub fn f() {} pub fn f() {}