summaryrefslogtreecommitdiffstats
path: root/src/librustdoc/html/markdown.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/html/markdown.rs')
-rw-r--r--src/librustdoc/html/markdown.rs145
1 files changed, 101 insertions, 44 deletions
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index fd00277e2..98cc38a10 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -50,7 +50,7 @@ use crate::html::render::small_url_encode;
use crate::html::toc::TocBuilder;
use pulldown_cmark::{
- html, BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag,
+ html, BrokenLink, CodeBlockKind, CowStr, Event, LinkType, OffsetIter, Options, Parser, Tag,
};
#[cfg(test)]
@@ -246,10 +246,9 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
return Some(Event::Html(
format!(
"<div class=\"example-wrap\">\
- <pre class=\"language-{}\"><code>{}</code></pre>\
+ <pre class=\"language-{lang}\"><code>{text}</code></pre>\
</div>",
- lang,
- Escape(&original_text),
+ text = Escape(&original_text),
)
.into(),
));
@@ -288,8 +287,9 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
let test_escaped = small_url_encode(test);
Some(format!(
- r#"<a class="test-arrow" target="_blank" href="{}?code={}{}&amp;edition={}">Run</a>"#,
- url, test_escaped, channel, edition,
+ "<a class=\"test-arrow\" \
+ target=\"_blank\" \
+ href=\"{url}?code={test_escaped}{channel}&amp;edition={edition}\">Run</a>",
))
});
@@ -308,7 +308,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
// insert newline to clearly separate it from the
// previous block so we can shorten the html output
let mut s = Buffer::new();
- s.push_str("\n");
+ s.push('\n');
highlight::render_example_with_highlighting(
&text,
@@ -349,7 +349,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
dest,
title,
))) => {
- debug!("saw start of shortcut link to {} with title {}", dest, title);
+ debug!("saw start of shortcut link to {dest} with title {title}");
// If this is a shortcut link, it was resolved by the broken_link_callback.
// So the URL will already be updated properly.
let link = self.links.iter().find(|&link| *link.href == **dest);
@@ -370,7 +370,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
dest,
_,
))) => {
- debug!("saw end of shortcut link to {}", dest);
+ debug!("saw end of shortcut link to {dest}");
if self.links.iter().any(|link| *link.href == **dest) {
assert!(self.shortcut_link.is_some(), "saw closing link without opening tag");
self.shortcut_link = None;
@@ -379,7 +379,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
// Handle backticks in inline code blocks, but only if we're in the middle of a shortcut link.
// [`fn@f`]
Some(Event::Code(text)) => {
- trace!("saw code {}", text);
+ trace!("saw code {text}");
if let Some(link) = self.shortcut_link {
// NOTE: this only replaces if the code block is the *entire* text.
// If only part of the link has code highlighting, the disambiguator will not be removed.
@@ -394,7 +394,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
l.href == link.href
&& Some(&**text) == l.original_text.get(1..l.original_text.len() - 1)
}) {
- debug!("replacing {} with {}", text, link.new_text);
+ debug!("replacing {text} with {new_text}", new_text = link.new_text);
*text = CowStr::Borrowed(&link.new_text);
}
}
@@ -402,7 +402,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
// Replace plain text in links, but only in the middle of a shortcut link.
// [fn@f]
Some(Event::Text(text)) => {
- trace!("saw text {}", text);
+ trace!("saw text {text}");
if let Some(link) = self.shortcut_link {
// NOTE: same limitations as `Event::Code`
if let Some(link) = self
@@ -410,7 +410,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
.iter()
.find(|l| l.href == link.href && **text == *l.original_text)
{
- debug!("replacing {} with {}", text, link.new_text);
+ debug!("replacing {text} with {new_text}", new_text = link.new_text);
*text = CowStr::Borrowed(&link.new_text);
}
}
@@ -522,18 +522,16 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator
let mut html_header = String::new();
html::push_html(&mut html_header, self.buf.iter().map(|(ev, _)| ev.clone()));
let sec = builder.push(level as u32, html_header, id.clone());
- self.buf.push_front((Event::Html(format!("{} ", sec).into()), 0..0));
+ self.buf.push_front((Event::Html(format!("{sec} ").into()), 0..0));
}
let level =
std::cmp::min(level as u32 + (self.heading_offset as u32), MAX_HEADER_LEVEL);
- self.buf.push_back((Event::Html(format!("</a></h{}>", level).into()), 0..0));
+ self.buf.push_back((Event::Html(format!("</a></h{level}>").into()), 0..0));
let start_tags = format!(
"<h{level} id=\"{id}\">\
<a href=\"#{id}\">",
- id = id,
- level = level
);
return Some((Event::Html(start_tags.into()), 0..0));
}
@@ -683,14 +681,14 @@ impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
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();
+ write!(ret, "<li id=\"fn{id}\">").unwrap();
let mut is_paragraph = false;
if let Some(&Event::End(Tag::Paragraph)) = content.last() {
content.pop();
is_paragraph = true;
}
html::push_html(&mut ret, content.into_iter());
- write!(ret, "&nbsp;<a href=\"#fnref{}\">↩</a>", id).unwrap();
+ write!(ret, "&nbsp;<a href=\"#fnref{id}\">↩</a>").unwrap();
if is_paragraph {
ret.push_str("</p>");
}
@@ -961,7 +959,7 @@ impl LangString {
} {
if let Some(extra) = extra {
extra.error_invalid_codeblock_attr(
- format!("unknown attribute `{}`. Did you mean `{}`?", x, flag),
+ format!("unknown attribute `{x}`. Did you mean `{flag}`?"),
help,
);
}
@@ -1040,7 +1038,7 @@ impl MarkdownWithToc<'_> {
html::push_html(&mut s, p);
}
- format!("<nav id=\"TOC\">{}</nav>{}", toc.into_toc().print(), s)
+ format!("<nav id=\"TOC\">{toc}</nav>{s}", toc = toc.into_toc().print())
}
}
@@ -1242,6 +1240,7 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin
pub(crate) struct MarkdownLink {
pub kind: LinkType,
pub link: String,
+ pub display_text: Option<String>,
pub range: MarkdownLinkRange,
}
@@ -1265,8 +1264,8 @@ impl MarkdownLinkRange {
}
}
-pub(crate) fn markdown_links<R>(
- md: &str,
+pub(crate) fn markdown_links<'md, R>(
+ md: &'md str,
preprocess_link: impl Fn(MarkdownLink) -> Option<R>,
) -> Vec<R> {
if md.is_empty() {
@@ -1377,32 +1376,90 @@ pub(crate) fn markdown_links<R>(
MarkdownLinkRange::Destination(range.clone())
};
- Parser::new_with_broken_link_callback(
+ let mut broken_link_callback = |link: BrokenLink<'md>| Some((link.reference, "".into()));
+ let mut event_iter = Parser::new_with_broken_link_callback(
md,
main_body_opts(),
- Some(&mut |link: BrokenLink<'_>| Some((link.reference, "".into()))),
+ Some(&mut broken_link_callback),
)
- .into_offset_iter()
- .filter_map(|(event, span)| match event {
- Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => {
- let range = match link_type {
- // Link is pulled from the link itself.
- LinkType::ReferenceUnknown | LinkType::ShortcutUnknown => {
- span_for_offset_backward(span, b'[', b']')
- }
- LinkType::CollapsedUnknown => span_for_offset_forward(span, b'[', b']'),
- LinkType::Inline => span_for_offset_backward(span, b'(', b')'),
- // Link is pulled from elsewhere in the document.
- LinkType::Reference | LinkType::Collapsed | LinkType::Shortcut => {
- span_for_link(&dest, span)
+ .into_offset_iter();
+ let mut links = Vec::new();
+
+ while let Some((event, span)) = event_iter.next() {
+ match event {
+ Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => {
+ let range = match link_type {
+ // Link is pulled from the link itself.
+ LinkType::ReferenceUnknown | LinkType::ShortcutUnknown => {
+ span_for_offset_backward(span, b'[', b']')
+ }
+ LinkType::CollapsedUnknown => span_for_offset_forward(span, b'[', b']'),
+ LinkType::Inline => span_for_offset_backward(span, b'(', b')'),
+ // Link is pulled from elsewhere in the document.
+ LinkType::Reference | LinkType::Collapsed | LinkType::Shortcut => {
+ span_for_link(&dest, span)
+ }
+ LinkType::Autolink | LinkType::Email => unreachable!(),
+ };
+
+ let display_text = if matches!(
+ link_type,
+ LinkType::Inline
+ | LinkType::ReferenceUnknown
+ | LinkType::Reference
+ | LinkType::Shortcut
+ | LinkType::ShortcutUnknown
+ ) {
+ collect_link_data(&mut event_iter)
+ } else {
+ None
+ };
+
+ if let Some(link) = preprocess_link(MarkdownLink {
+ kind: link_type,
+ link: dest.into_string(),
+ display_text,
+ range,
+ }) {
+ links.push(link);
}
- LinkType::Autolink | LinkType::Email => unreachable!(),
- };
- preprocess_link(MarkdownLink { kind: link_type, range, link: dest.into_string() })
+ }
+ _ => {}
}
- _ => None,
- })
- .collect()
+ }
+
+ links
+}
+
+/// Collects additional data of link.
+fn collect_link_data<'input, 'callback>(
+ event_iter: &mut OffsetIter<'input, 'callback>,
+) -> Option<String> {
+ let mut display_text: Option<String> = None;
+ let mut append_text = |text: CowStr<'_>| {
+ if let Some(display_text) = &mut display_text {
+ display_text.push_str(&text);
+ } else {
+ display_text = Some(text.to_string());
+ }
+ };
+
+ while let Some((event, _span)) = event_iter.next() {
+ match event {
+ Event::Text(text) => {
+ append_text(text);
+ }
+ Event::Code(code) => {
+ append_text(code);
+ }
+ Event::End(_) => {
+ break;
+ }
+ _ => {}
+ }
+ }
+
+ display_text
}
#[derive(Debug)]