Skip to content

Commit 36b15f0

Browse files
Fix multiple footnotes and improve testing
1 parent 08a741e commit 36b15f0

File tree

2 files changed

+42
-25
lines changed

2 files changed

+42
-25
lines changed

src/librustdoc/html/markdown.rs

+33-21
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
use std::ascii::AsciiExt;
2929
use std::cell::RefCell;
30+
use std::collections::HashMap;
3031
use std::default::Default;
3132
use std::fmt::{self, Write};
3233
use std::str;
@@ -135,27 +136,27 @@ macro_rules! event_loop_break {
135136

136137
struct ParserWrapper<'a> {
137138
parser: Parser<'a>,
138-
footnotes: Vec<String>,
139-
current_footnote_id: u16,
139+
// The key is the footnote reference. The value is the footnote definition and the id.
140+
footnotes: HashMap<String, (String, u16)>,
140141
}
141142

142143
impl<'a> ParserWrapper<'a> {
143144
pub fn new(s: &'a str) -> ParserWrapper<'a> {
144145
ParserWrapper {
145146
parser: Parser::new_ext(s, pulldown_cmark::OPTION_ENABLE_TABLES |
146147
pulldown_cmark::OPTION_ENABLE_FOOTNOTES),
147-
footnotes: Vec::new(),
148-
current_footnote_id: 1,
148+
footnotes: HashMap::new(),
149149
}
150150
}
151+
151152
pub fn next(&mut self) -> Option<Event<'a>> {
152153
self.parser.next()
153154
}
154155

155-
pub fn get_next_footnote_id(&mut self) -> u16 {
156-
let tmp = self.current_footnote_id;
157-
self.current_footnote_id += 1;
158-
tmp
156+
pub fn get_entry(&mut self, key: &str) -> &mut (String, u16) {
157+
let new_id = self.footnotes.keys().count() + 1;
158+
let key = key.to_owned();
159+
self.footnotes.entry(key).or_insert((String::new(), new_id as u16))
159160
}
160161
}
161162

@@ -450,10 +451,11 @@ pub fn render(w: &mut fmt::Formatter,
450451

451452
fn footnote(parser: &mut ParserWrapper, buffer: &mut String,
452453
toc_builder: &mut Option<TocBuilder>, shorter: MarkdownOutputStyle,
453-
mut definition: String, id: &mut Option<&mut String>) {
454-
event_loop_break!(parser, toc_builder, shorter, definition, true, id,
454+
id: &mut Option<&mut String>) {
455+
let mut content = String::new();
456+
event_loop_break!(parser, toc_builder, shorter, content, true, id,
455457
Event::End(Tag::FootnoteDefinition(_)));
456-
buffer.push_str(&definition);
458+
buffer.push_str(&content);
457459
}
458460

459461
fn rule(parser: &mut ParserWrapper, buffer: &mut String, toc_builder: &mut Option<TocBuilder>,
@@ -507,17 +509,24 @@ pub fn render(w: &mut fmt::Formatter,
507509
}
508510
Event::Start(Tag::FootnoteDefinition(ref def)) => {
509511
let mut content = String::new();
510-
footnote(parser, &mut content, toc_builder, shorter, def.as_ref().to_owned(),
511-
id);
512-
let cur_len = parser.footnotes.len() + 1;
513-
parser.footnotes.push(format!("<li id=\"ref{}\">{}<a href=\"#supref{0}\" \
514-
rev=\"footnote\">↩</a></li>",
515-
cur_len, content));
516-
}
517-
Event::FootnoteReference(_) => {
512+
let def = def.as_ref();
513+
footnote(parser, &mut content, toc_builder, shorter, id);
514+
let entry = parser.get_entry(def);
515+
let cur_id = (*entry).1;
516+
(*entry).0.push_str(&format!("<li id=\"ref{}\">{}&nbsp;<a href=\"#supref{0}\" \
517+
rev=\"footnote\">↩</a></p></li>",
518+
cur_id,
519+
if content.ends_with("</p>") {
520+
&content[..content.len() - 4]
521+
} else {
522+
&content
523+
}));
524+
}
525+
Event::FootnoteReference(ref reference) => {
526+
let entry = parser.get_entry(reference.as_ref());
518527
buffer.push_str(&format!("<sup id=\"supref{0}\"><a href=\"#ref{0}\">{0}</a>\
519528
</sup>",
520-
parser.get_next_footnote_id()));
529+
(*entry).1));
521530
}
522531
Event::Html(h) | Event::InlineHtml(h) => {
523532
buffer.push_str(&*h);
@@ -545,7 +554,10 @@ pub fn render(w: &mut fmt::Formatter,
545554
}
546555
if !parser.footnotes.is_empty() {
547556
buffer.push_str(&format!("<div class=\"footnotes\"><hr><ol>{}</ol></div>",
548-
parser.footnotes.join("")));
557+
parser.footnotes.values()
558+
.map(|&(ref s, _)| s.as_str())
559+
.collect::<Vec<_>>()
560+
.join("")));
549561
}
550562
let mut ret = toc_builder.map_or(Ok(()), |builder| {
551563
write!(w, "<nav id=\"TOC\">{}</nav>", builder.into_toc())

src/test/rustdoc/check-rule-image-footnote.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@
1010

1111
#![crate_name = "foo"]
1212

13+
// ignore-tidy-linelength
14+
1315
// @has foo/fn.f.html
14-
// @has - '<p>hard break: after hard break</p><hr>'
15-
// @has - '<img src="https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png" alt="Rust">'
16-
// @has - '<li id="ref1">'
17-
// @has - '<sup id="supref1"><a href="#ref1">1</a></sup>'
16+
// @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>'
1817
/// markdown test
1918
///
2019
/// this is a [link].
@@ -28,8 +27,14 @@
2827
///
2928
/// a footnote[^footnote].
3029
///
30+
/// another footnote[^footnotebis].
31+
///
3132
/// [^footnote]: Thing
3233
///
34+
///
35+
/// [^footnotebis]: Another Thing
36+
///
37+
///
3338
/// ![Rust](https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png)
3439
#[deprecated(note = "Struct<T>")]
3540
pub fn f() {}

0 commit comments

Comments
 (0)