diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 315b7742a4cf8..7826a5d8394a7 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -37,7 +37,7 @@ use std::sync::OnceLock;
use pulldown_cmark::{
BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html,
};
-use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
+use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Diag, DiagMessage};
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::TyCtxt;
@@ -57,6 +57,7 @@ use crate::html::length_limit::HtmlWithLimit;
use crate::html::render::small_url_encode;
use crate::html::toc::{Toc, TocBuilder};
+mod footnotes;
#[cfg(test)]
mod tests;
@@ -646,81 +647,6 @@ impl<'a, I: Iterator- >> Iterator for SummaryLine<'a, I> {
}
}
-/// Moves all footnote definitions to the end and add back links to the
-/// references.
-struct Footnotes<'a, I> {
- inner: I,
- footnotes: FxIndexMap>, u16)>,
-}
-
-impl<'a, I> Footnotes<'a, I> {
- fn new(iter: I) -> Self {
- Footnotes { inner: iter, footnotes: FxIndexMap::default() }
- }
-
- fn get_entry(&mut self, key: &str) -> &mut (Vec>, u16) {
- let new_id = self.footnotes.len() + 1;
- let key = key.to_owned();
- self.footnotes.entry(key).or_insert((Vec::new(), new_id as u16))
- }
-}
-
-impl<'a, I: Iterator
- >> Iterator for Footnotes<'a, I> {
- type Item = SpannedEvent<'a>;
-
- fn next(&mut self) -> Option {
- loop {
- match self.inner.next() {
- Some((Event::FootnoteReference(ref reference), range)) => {
- let entry = self.get_entry(reference);
- let reference = format!(
- "{0}",
- (*entry).1
- );
- return Some((Event::Html(reference.into()), range));
- }
- Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => {
- let mut content = Vec::new();
- for (event, _) in &mut self.inner {
- if let Event::End(TagEnd::FootnoteDefinition) = event {
- break;
- }
- content.push(event);
- }
- let entry = self.get_entry(&def);
- (*entry).0 = content;
- }
- Some(e) => return Some(e),
- None => {
- if !self.footnotes.is_empty() {
- let mut v: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect();
- v.sort_by(|a, b| a.1.cmp(&b.1));
- let mut ret = String::from("");
- return Some((Event::Html(ret.into()), 0..0));
- } else {
- return None;
- }
- }
- }
- }
- }
-}
-
/// A newtype that represents a relative line number in Markdown.
///
/// In other words, this represents an offset from the first line of Markdown
@@ -1408,7 +1334,7 @@ impl Markdown<'_> {
let mut s = String::with_capacity(md.len() * 3 / 2);
let p = HeadingLinks::new(p, None, ids, heading_offset);
- let p = Footnotes::new(p);
+ let p = footnotes::Footnotes::new(p);
let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
let p = TableWrapper::new(p);
let p = CodeBlocks::new(p, codes, edition, playground);
@@ -1443,7 +1369,7 @@ impl MarkdownWithToc<'_> {
{
let p = HeadingLinks::new(p, Some(&mut toc), ids, HeadingOffset::H1);
- let p = Footnotes::new(p);
+ let p = footnotes::Footnotes::new(p);
let p = TableWrapper::new(p.map(|(ev, _)| ev));
let p = CodeBlocks::new(p, codes, edition, playground);
html::push_html(&mut s, p);
@@ -1476,7 +1402,7 @@ impl MarkdownItemInfo<'_> {
let mut s = String::with_capacity(md.len() * 3 / 2);
let p = HeadingLinks::new(p, None, ids, HeadingOffset::H1);
- let p = Footnotes::new(p);
+ let p = footnotes::Footnotes::new(p);
let p = TableWrapper::new(p.map(|(ev, _)| ev));
let p = p.filter(|event| {
!matches!(event, Event::Start(Tag::Paragraph) | Event::End(TagEnd::Paragraph))
diff --git a/src/librustdoc/html/markdown/footnotes.rs b/src/librustdoc/html/markdown/footnotes.rs
new file mode 100644
index 0000000000000..3f0e586b8e372
--- /dev/null
+++ b/src/librustdoc/html/markdown/footnotes.rs
@@ -0,0 +1,113 @@
+//! Markdown footnote handling.
+use std::fmt::Write as _;
+
+use pulldown_cmark::{Event, Tag, TagEnd, html};
+use rustc_data_structures::fx::FxIndexMap;
+
+use super::SpannedEvent;
+
+/// Moves all footnote definitions to the end and add back links to the
+/// references.
+pub(super) struct Footnotes<'a, I> {
+ inner: I,
+ footnotes: FxIndexMap>,
+}
+
+/// The definition of a single footnote.
+struct FootnoteDef<'a> {
+ content: Vec>,
+ /// The number that appears in the footnote reference and list.
+ id: u16,
+}
+
+impl<'a, I> Footnotes<'a, I> {
+ pub(super) fn new(iter: I) -> Self {
+ Footnotes { inner: iter, footnotes: FxIndexMap::default() }
+ }
+
+ fn get_entry(&mut self, key: &str) -> (&mut Vec>, u16) {
+ let new_id = self.footnotes.len() + 1;
+ let key = key.to_owned();
+ let FootnoteDef { content, id } = self
+ .footnotes
+ .entry(key)
+ .or_insert(FootnoteDef { content: Vec::new(), id: new_id as u16 });
+ // Don't allow changing the ID of existing entrys, but allow changing the contents.
+ (content, *id)
+ }
+}
+
+impl<'a, I: Iterator
- >> Iterator for Footnotes<'a, I> {
+ type Item = SpannedEvent<'a>;
+
+ fn next(&mut self) -> Option {
+ loop {
+ match self.inner.next() {
+ Some((Event::FootnoteReference(ref reference), range)) => {
+ // When we see a reference (to a footnote we may not know) the definition of,
+ // reserve a number for it, and emit a link to that number.
+ let (_, id) = self.get_entry(reference);
+ let reference =
+ format!("{0}", id);
+ return Some((Event::Html(reference.into()), range));
+ }
+ Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => {
+ // When we see a footnote definition, collect the assocated content, and store
+ // that for rendering later.
+ let content = collect_footnote_def(&mut self.inner);
+ let (entry_content, _) = self.get_entry(&def);
+ *entry_content = content;
+ }
+ Some(e) => return Some(e),
+ None => {
+ if !self.footnotes.is_empty() {
+ // After all the markdown is emmited, emit an
then all the footnotes
+ // in a list.
+ let defs: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect();
+ let defs_html = render_footnotes_defs(defs);
+ return Some((Event::Html(defs_html.into()), 0..0));
+ } else {
+ return None;
+ }
+ }
+ }
+ }
+ }
+}
+
+fn collect_footnote_def<'a>(events: impl Iterator- >) -> Vec> {
+ let mut content = Vec::new();
+ for (event, _) in events {
+ if let Event::End(TagEnd::FootnoteDefinition) = event {
+ break;
+ }
+ content.push(event);
+ }
+ content
+}
+
+fn render_footnotes_defs(mut footnotes: Vec>) -> String {
+ let mut ret = String::from("");
+
+ ret
+}