diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b890a980d43..c91be9ad7b8 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -308,6 +308,11 @@ impl Item { pub fn collapsed_doc_value(&self) -> Option { self.attrs.collapsed_doc_value() } + + pub fn links(&self) -> Vec<(String, String)> { + self.attrs.links() + } + pub fn is_crate(&self) -> bool { match self.inner { StrippedItem(box ModuleItem(Module { is_crate: true, ..})) | @@ -791,6 +796,20 @@ impl Attributes { None } } + + /// Get links as a vector + /// + /// Cache must be populated before call + pub fn links(&self) -> Vec<(String, String)> { + use html::format::href; + self.links.iter().filter_map(|&(ref s, did)| { + if let Some((href, ..)) = href(did) { + Some((s.clone(), href)) + } else { + None + } + }).collect() + } } impl AttributesExt for Attributes { diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs index f8320330ad2..f7d07af04ea 100644 --- a/src/librustdoc/externalfiles.rs +++ b/src/librustdoc/externalfiles.rs @@ -37,7 +37,7 @@ impl ExternalHtml { ) .and_then(|(ih, bc)| load_external_files(md_before_content) - .map(|m_bc| (ih, format!("{}{}", bc, Markdown(&m_bc, render)))) + .map(|m_bc| (ih, format!("{}{}", bc, Markdown(&m_bc, &[], render)))) ) .and_then(|(ih, bc)| load_external_files(after_content) @@ -45,7 +45,7 @@ impl ExternalHtml { ) .and_then(|(ih, bc, ac)| load_external_files(md_after_content) - .map(|m_ac| (ih, bc, format!("{}{}", ac, Markdown(&m_ac, render)))) + .map(|m_ac| (ih, bc, format!("{}{}", ac, Markdown(&m_ac, &[], render)))) ) .map(|(ih, bc, ac)| ExternalHtml { diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 9bb35da246c..9e875a7c280 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -56,15 +56,16 @@ pub enum RenderType { /// A unit struct which has the `fmt::Display` trait implemented. When /// formatted, this struct will emit the HTML corresponding to the rendered /// version of the contained markdown string. -// The second parameter is whether we need a shorter version or not. -pub struct Markdown<'a>(pub &'a str, pub RenderType); +/// The second parameter is a list of link replacements +// The third parameter is whether we need a shorter version or not. +pub struct Markdown<'a>(pub &'a str, pub &'a [(String, String)], pub RenderType); /// A unit struct like `Markdown`, that renders the markdown with a /// table of contents. pub struct MarkdownWithToc<'a>(pub &'a str, pub RenderType); /// A unit struct like `Markdown`, that renders the markdown escaping HTML tags. pub struct MarkdownHtml<'a>(pub &'a str, pub RenderType); /// A unit struct like `Markdown`, that renders only the first paragraph. -pub struct MarkdownSummaryLine<'a>(pub &'a str); +pub struct MarkdownSummaryLine<'a>(pub &'a str, pub &'a [(String, String)]); /// Controls whether a line will be hidden or shown in HTML output. /// @@ -247,6 +248,38 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'a, I> { } } +/// Make headings links with anchor ids and build up TOC. +struct LinkReplacer<'a, 'b, I: Iterator>> { + inner: I, + links: &'b [(String, String)] +} + +impl<'a, 'b, I: Iterator>> LinkReplacer<'a, 'b, I> { + fn new(iter: I, links: &'b [(String, String)]) -> Self { + LinkReplacer { + inner: iter, + links + } + } +} + +impl<'a, 'b, I: Iterator>> Iterator for LinkReplacer<'a, 'b, I> { + type Item = Event<'a>; + + fn next(&mut self) -> Option { + let event = self.inner.next(); + if let Some(Event::Start(Tag::Link(dest, text))) = event { + if let Some(&(_, ref replace)) = self.links.into_iter().find(|link| &*link.0 == &*dest) { + Some(Event::Start(Tag::Link(replace.to_owned().into(), text))) + } else { + Some(Event::Start(Tag::Link(dest, text))) + } + } else { + event + } + } +} + /// Make headings links with anchor ids and build up TOC. struct HeadingLinks<'a, 'b, I: Iterator>> { inner: I, @@ -996,7 +1029,7 @@ impl LangString { impl<'a> fmt::Display for Markdown<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let Markdown(md, render_type) = *self; + let Markdown(md, links, render_type) = *self; // This is actually common enough to special-case if md.is_empty() { return Ok(()) } @@ -1012,7 +1045,7 @@ impl<'a> fmt::Display for Markdown<'a> { let mut s = String::with_capacity(md.len() * 3 / 2); html::push_html(&mut s, - Footnotes::new(CodeBlocks::new(HeadingLinks::new(p, None)))); + Footnotes::new(CodeBlocks::new(LinkReplacer::new(HeadingLinks::new(p, None), links)))); fmt.write_str(&s) } @@ -1079,7 +1112,7 @@ impl<'a> fmt::Display for MarkdownHtml<'a> { impl<'a> fmt::Display for MarkdownSummaryLine<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let MarkdownSummaryLine(md) = *self; + let MarkdownSummaryLine(md, links) = *self; // This is actually common enough to special-case if md.is_empty() { return Ok(()) } @@ -1087,7 +1120,7 @@ impl<'a> fmt::Display for MarkdownSummaryLine<'a> { let mut s = String::new(); - html::push_html(&mut s, SummaryLine::new(p)); + html::push_html(&mut s, LinkReplacer::new(SummaryLine::new(p), links)); fmt.write_str(&s) } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index f335e073d56..52200f5afd7 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1861,12 +1861,13 @@ fn document(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item) -> fmt::Re /// rendering between Pulldown and Hoedown. fn render_markdown(w: &mut fmt::Formatter, md_text: &str, + links: Vec<(String, String)>, span: Span, render_type: RenderType, prefix: &str, scx: &SharedContext) -> fmt::Result { - let (hoedown_output, pulldown_output) = render_text(|ty| format!("{}", Markdown(md_text, ty))); + let (hoedown_output, pulldown_output) = render_text(|ty| format!("{}", Markdown(md_text, &links, ty))); let mut differences = html_diff::get_differences(&pulldown_output, &hoedown_output); differences.retain(|s| { match *s { @@ -1898,7 +1899,7 @@ fn document_short(w: &mut fmt::Formatter, item: &clean::Item, link: AssocItemLin } else { format!("{}", &plain_summary_line(Some(s))) }; - render_markdown(w, &markdown, item.source.clone(), cx.render_type, prefix, &cx.shared)?; + render_markdown(w, &markdown, item.links(), item.source.clone(), cx.render_type, prefix, &cx.shared)?; } else if !prefix.is_empty() { write!(w, "
{}
", prefix)?; } @@ -1924,7 +1925,7 @@ fn document_full(w: &mut fmt::Formatter, item: &clean::Item, cx: &Context, prefix: &str) -> fmt::Result { if let Some(s) = cx.shared.maybe_collapsed_doc_value(item) { debug!("Doc block: =====\n{}\n=====", s); - render_markdown(w, &*s, item.source.clone(), cx.render_type, prefix, &cx.shared)?; + render_markdown(w, &*s, item.links(), item.source.clone(), cx.render_type, prefix, &cx.shared)?; } else if !prefix.is_empty() { write!(w, "
{}
", prefix)?; } @@ -2146,10 +2147,10 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, stab_docs = stab_docs, docs = if cx.render_type == RenderType::Hoedown { format!("{}", - shorter(Some(&Markdown(doc_value, + shorter(Some(&Markdown(doc_value, &item.links(), RenderType::Hoedown).to_string()))) } else { - format!("{}", MarkdownSummaryLine(doc_value)) + format!("{}", MarkdownSummaryLine(doc_value, &item.links())) }, class = myitem.type_(), stab = myitem.stability_class().unwrap_or("".to_string()), @@ -3338,7 +3339,7 @@ fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLi write!(w, "")?; write!(w, "\n")?; if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) { - write!(w, "
{}
", Markdown(&*dox, cx.render_type))?; + write!(w, "
{}
", Markdown(&*dox, &i.impl_item.links(), cx.render_type))?; } } diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index af93505293c..9af2ebf0661 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -104,7 +104,7 @@ pub fn render(input: &Path, mut output: PathBuf, matches: &getopts::Matches, } else { // Save the state of USED_ID_MAP so it only gets updated once even // though we're rendering twice. - render_text(|ty| format!("{}", Markdown(text, ty))) + render_text(|ty| format!("{}", Markdown(text, &[], ty))) }; let mut differences = html_diff::get_differences(&pulldown_output, &hoedown_output);