diff options
Diffstat (limited to 'src/librustdoc/html/markdown.rs')
-rw-r--r-- | src/librustdoc/html/markdown.rs | 145 |
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={}{}&edition={}">Run</a>"#, - url, test_escaped, channel, edition, + "<a class=\"test-arrow\" \ + target=\"_blank\" \ + href=\"{url}?code={test_escaped}{channel}&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, " <a href=\"#fnref{}\">↩</a>", id).unwrap(); + write!(ret, " <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)] |