Skip to content

Commit ebb8423

Browse files
committed
rustdoc: Extract footnote logic into it's own module.
1 parent b27f33a commit ebb8423

File tree

2 files changed

+87
-79
lines changed

2 files changed

+87
-79
lines changed

src/librustdoc/html/markdown.rs

+5-79
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use std::sync::OnceLock;
3737
use pulldown_cmark::{
3838
BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html,
3939
};
40-
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
40+
use rustc_data_structures::fx::FxHashMap;
4141
use rustc_errors::{Diag, DiagMessage};
4242
use rustc_hir::def_id::LocalDefId;
4343
use rustc_middle::ty::TyCtxt;
@@ -57,6 +57,7 @@ use crate::html::length_limit::HtmlWithLimit;
5757
use crate::html::render::small_url_encode;
5858
use crate::html::toc::{Toc, TocBuilder};
5959

60+
mod footnotes;
6061
#[cfg(test)]
6162
mod tests;
6263

@@ -646,81 +647,6 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> {
646647
}
647648
}
648649

649-
/// Moves all footnote definitions to the end and add back links to the
650-
/// references.
651-
struct Footnotes<'a, I> {
652-
inner: I,
653-
footnotes: FxIndexMap<String, (Vec<Event<'a>>, u16)>,
654-
}
655-
656-
impl<'a, I> Footnotes<'a, I> {
657-
fn new(iter: I) -> Self {
658-
Footnotes { inner: iter, footnotes: FxIndexMap::default() }
659-
}
660-
661-
fn get_entry(&mut self, key: &str) -> &mut (Vec<Event<'a>>, u16) {
662-
let new_id = self.footnotes.len() + 1;
663-
let key = key.to_owned();
664-
self.footnotes.entry(key).or_insert((Vec::new(), new_id as u16))
665-
}
666-
}
667-
668-
impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
669-
type Item = SpannedEvent<'a>;
670-
671-
fn next(&mut self) -> Option<Self::Item> {
672-
loop {
673-
match self.inner.next() {
674-
Some((Event::FootnoteReference(ref reference), range)) => {
675-
let entry = self.get_entry(reference);
676-
let reference = format!(
677-
"<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{0}</a></sup>",
678-
(*entry).1
679-
);
680-
return Some((Event::Html(reference.into()), range));
681-
}
682-
Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => {
683-
let mut content = Vec::new();
684-
for (event, _) in &mut self.inner {
685-
if let Event::End(TagEnd::FootnoteDefinition) = event {
686-
break;
687-
}
688-
content.push(event);
689-
}
690-
let entry = self.get_entry(&def);
691-
(*entry).0 = content;
692-
}
693-
Some(e) => return Some(e),
694-
None => {
695-
if !self.footnotes.is_empty() {
696-
let mut v: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect();
697-
v.sort_by(|a, b| a.1.cmp(&b.1));
698-
let mut ret = String::from("<div class=\"footnotes\"><hr><ol>");
699-
for (mut content, id) in v {
700-
write!(ret, "<li id=\"fn{id}\">").unwrap();
701-
let mut is_paragraph = false;
702-
if let Some(&Event::End(TagEnd::Paragraph)) = content.last() {
703-
content.pop();
704-
is_paragraph = true;
705-
}
706-
html::push_html(&mut ret, content.into_iter());
707-
write!(ret, "&nbsp;<a href=\"#fnref{id}\">↩</a>").unwrap();
708-
if is_paragraph {
709-
ret.push_str("</p>");
710-
}
711-
ret.push_str("</li>");
712-
}
713-
ret.push_str("</ol></div>");
714-
return Some((Event::Html(ret.into()), 0..0));
715-
} else {
716-
return None;
717-
}
718-
}
719-
}
720-
}
721-
}
722-
}
723-
724650
/// A newtype that represents a relative line number in Markdown.
725651
///
726652
/// In other words, this represents an offset from the first line of Markdown
@@ -1408,7 +1334,7 @@ impl Markdown<'_> {
14081334
let mut s = String::with_capacity(md.len() * 3 / 2);
14091335

14101336
let p = HeadingLinks::new(p, None, ids, heading_offset);
1411-
let p = Footnotes::new(p);
1337+
let p = footnotes::Footnotes::new(p);
14121338
let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
14131339
let p = TableWrapper::new(p);
14141340
let p = CodeBlocks::new(p, codes, edition, playground);
@@ -1443,7 +1369,7 @@ impl MarkdownWithToc<'_> {
14431369

14441370
{
14451371
let p = HeadingLinks::new(p, Some(&mut toc), ids, HeadingOffset::H1);
1446-
let p = Footnotes::new(p);
1372+
let p = footnotes::Footnotes::new(p);
14471373
let p = TableWrapper::new(p.map(|(ev, _)| ev));
14481374
let p = CodeBlocks::new(p, codes, edition, playground);
14491375
html::push_html(&mut s, p);
@@ -1476,7 +1402,7 @@ impl MarkdownItemInfo<'_> {
14761402
let mut s = String::with_capacity(md.len() * 3 / 2);
14771403

14781404
let p = HeadingLinks::new(p, None, ids, HeadingOffset::H1);
1479-
let p = Footnotes::new(p);
1405+
let p = footnotes::Footnotes::new(p);
14801406
let p = TableWrapper::new(p.map(|(ev, _)| ev));
14811407
let p = p.filter(|event| {
14821408
!matches!(event, Event::Start(Tag::Paragraph) | Event::End(TagEnd::Paragraph))
+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//! Markdown footnote handling.
2+
use std::fmt::Write as _;
3+
4+
use pulldown_cmark::{Event, Tag, TagEnd, html};
5+
use rustc_data_structures::fx::FxIndexMap;
6+
7+
use super::SpannedEvent;
8+
9+
/// Moves all footnote definitions to the end and add back links to the
10+
/// references.
11+
pub(super) struct Footnotes<'a, I> {
12+
inner: I,
13+
footnotes: FxIndexMap<String, (Vec<Event<'a>>, u16)>,
14+
}
15+
16+
impl<'a, I> Footnotes<'a, I> {
17+
pub(super) fn new(iter: I) -> Self {
18+
Footnotes { inner: iter, footnotes: FxIndexMap::default() }
19+
}
20+
21+
fn get_entry(&mut self, key: &str) -> &mut (Vec<Event<'a>>, u16) {
22+
let new_id = self.footnotes.len() + 1;
23+
let key = key.to_owned();
24+
self.footnotes.entry(key).or_insert((Vec::new(), new_id as u16))
25+
}
26+
}
27+
28+
impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
29+
type Item = SpannedEvent<'a>;
30+
31+
fn next(&mut self) -> Option<Self::Item> {
32+
loop {
33+
match self.inner.next() {
34+
Some((Event::FootnoteReference(ref reference), range)) => {
35+
let entry = self.get_entry(reference);
36+
let reference = format!(
37+
"<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{0}</a></sup>",
38+
(*entry).1
39+
);
40+
return Some((Event::Html(reference.into()), range));
41+
}
42+
Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => {
43+
let mut content = Vec::new();
44+
for (event, _) in &mut self.inner {
45+
if let Event::End(TagEnd::FootnoteDefinition) = event {
46+
break;
47+
}
48+
content.push(event);
49+
}
50+
let entry = self.get_entry(&def);
51+
(*entry).0 = content;
52+
}
53+
Some(e) => return Some(e),
54+
None => {
55+
if !self.footnotes.is_empty() {
56+
let mut v: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect();
57+
v.sort_by(|a, b| a.1.cmp(&b.1));
58+
let mut ret = String::from("<div class=\"footnotes\"><hr><ol>");
59+
for (mut content, id) in v {
60+
write!(ret, "<li id=\"fn{id}\">").unwrap();
61+
let mut is_paragraph = false;
62+
if let Some(&Event::End(TagEnd::Paragraph)) = content.last() {
63+
content.pop();
64+
is_paragraph = true;
65+
}
66+
html::push_html(&mut ret, content.into_iter());
67+
write!(ret, "&nbsp;<a href=\"#fnref{id}\">↩</a>").unwrap();
68+
if is_paragraph {
69+
ret.push_str("</p>");
70+
}
71+
ret.push_str("</li>");
72+
}
73+
ret.push_str("</ol></div>");
74+
return Some((Event::Html(ret.into()), 0..0));
75+
} else {
76+
return None;
77+
}
78+
}
79+
}
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)