rustdoc: Extract footnote logic into it's own module.
This commit is contained in:
parent
b27f33a4d9
commit
ebb842328a
2 changed files with 87 additions and 79 deletions
|
@ -37,7 +37,7 @@ use std::sync::OnceLock;
|
||||||
use pulldown_cmark::{
|
use pulldown_cmark::{
|
||||||
BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html,
|
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_errors::{Diag, DiagMessage};
|
||||||
use rustc_hir::def_id::LocalDefId;
|
use rustc_hir::def_id::LocalDefId;
|
||||||
use rustc_middle::ty::TyCtxt;
|
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::render::small_url_encode;
|
||||||
use crate::html::toc::{Toc, TocBuilder};
|
use crate::html::toc::{Toc, TocBuilder};
|
||||||
|
|
||||||
|
mod footnotes;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
@ -646,81 +647,6 @@ impl<'a, I: Iterator<Item = Event<'a>>> 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<String, (Vec<Event<'a>>, 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<Event<'a>>, 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<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
|
|
||||||
type Item = SpannedEvent<'a>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
loop {
|
|
||||||
match self.inner.next() {
|
|
||||||
Some((Event::FootnoteReference(ref reference), range)) => {
|
|
||||||
let entry = self.get_entry(reference);
|
|
||||||
let reference = format!(
|
|
||||||
"<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{0}</a></sup>",
|
|
||||||
(*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("<div class=\"footnotes\"><hr><ol>");
|
|
||||||
for (mut content, id) in v {
|
|
||||||
write!(ret, "<li id=\"fn{id}\">").unwrap();
|
|
||||||
let mut is_paragraph = false;
|
|
||||||
if let Some(&Event::End(TagEnd::Paragraph)) = content.last() {
|
|
||||||
content.pop();
|
|
||||||
is_paragraph = true;
|
|
||||||
}
|
|
||||||
html::push_html(&mut ret, content.into_iter());
|
|
||||||
write!(ret, " <a href=\"#fnref{id}\">↩</a>").unwrap();
|
|
||||||
if is_paragraph {
|
|
||||||
ret.push_str("</p>");
|
|
||||||
}
|
|
||||||
ret.push_str("</li>");
|
|
||||||
}
|
|
||||||
ret.push_str("</ol></div>");
|
|
||||||
return Some((Event::Html(ret.into()), 0..0));
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A newtype that represents a relative line number in Markdown.
|
/// A newtype that represents a relative line number in Markdown.
|
||||||
///
|
///
|
||||||
/// In other words, this represents an offset from the first line of 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 mut s = String::with_capacity(md.len() * 3 / 2);
|
||||||
|
|
||||||
let p = HeadingLinks::new(p, None, ids, heading_offset);
|
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 = LinkReplacer::new(p.map(|(ev, _)| ev), links);
|
||||||
let p = TableWrapper::new(p);
|
let p = TableWrapper::new(p);
|
||||||
let p = CodeBlocks::new(p, codes, edition, playground);
|
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 = 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 = TableWrapper::new(p.map(|(ev, _)| ev));
|
||||||
let p = CodeBlocks::new(p, codes, edition, playground);
|
let p = CodeBlocks::new(p, codes, edition, playground);
|
||||||
html::push_html(&mut s, p);
|
html::push_html(&mut s, p);
|
||||||
|
@ -1476,7 +1402,7 @@ impl MarkdownItemInfo<'_> {
|
||||||
let mut s = String::with_capacity(md.len() * 3 / 2);
|
let mut s = String::with_capacity(md.len() * 3 / 2);
|
||||||
|
|
||||||
let p = HeadingLinks::new(p, None, ids, HeadingOffset::H1);
|
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 = TableWrapper::new(p.map(|(ev, _)| ev));
|
||||||
let p = p.filter(|event| {
|
let p = p.filter(|event| {
|
||||||
!matches!(event, Event::Start(Tag::Paragraph) | Event::End(TagEnd::Paragraph))
|
!matches!(event, Event::Start(Tag::Paragraph) | Event::End(TagEnd::Paragraph))
|
||||||
|
|
82
src/librustdoc/html/markdown/footnotes.rs
Normal file
82
src/librustdoc/html/markdown/footnotes.rs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
//! 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<String, (Vec<Event<'a>>, 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<Event<'a>>, 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<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
|
||||||
|
type Item = SpannedEvent<'a>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
|
match self.inner.next() {
|
||||||
|
Some((Event::FootnoteReference(ref reference), range)) => {
|
||||||
|
let entry = self.get_entry(reference);
|
||||||
|
let reference = format!(
|
||||||
|
"<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{0}</a></sup>",
|
||||||
|
(*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("<div class=\"footnotes\"><hr><ol>");
|
||||||
|
for (mut content, id) in v {
|
||||||
|
write!(ret, "<li id=\"fn{id}\">").unwrap();
|
||||||
|
let mut is_paragraph = false;
|
||||||
|
if let Some(&Event::End(TagEnd::Paragraph)) = content.last() {
|
||||||
|
content.pop();
|
||||||
|
is_paragraph = true;
|
||||||
|
}
|
||||||
|
html::push_html(&mut ret, content.into_iter());
|
||||||
|
write!(ret, " <a href=\"#fnref{id}\">↩</a>").unwrap();
|
||||||
|
if is_paragraph {
|
||||||
|
ret.push_str("</p>");
|
||||||
|
}
|
||||||
|
ret.push_str("</li>");
|
||||||
|
}
|
||||||
|
ret.push_str("</ol></div>");
|
||||||
|
return Some((Event::Html(ret.into()), 0..0));
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue