diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index f68e82501e91..9496b7e2a6fb 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -14,7 +14,7 @@ //! (bundled into the rust runtime). This module self-contains the C bindings //! and necessary legwork to render markdown, and exposes all of the //! functionality through a unit-struct, `Markdown`, which has an implementation -//! of `fmt::String`. Example usage: +//! of `fmt::Display`. Example usage: //! //! ```rust,ignore //! use rustdoc::html::markdown::Markdown; @@ -29,19 +29,19 @@ use libc; use std::ascii::AsciiExt; use std::cell::RefCell; -use std::collections::HashMap; use std::default::Default; use std::ffi::CString; use std::fmt; use std::slice; use std::str; +use html::render::{with_unique_id, reset_ids}; use html::toc::TocBuilder; use html::highlight; use html::escape::Escape; use test; -/// A unit struct which has the `fmt::String` trait implemented. When +/// 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. pub struct Markdown<'a>(pub &'a str); @@ -210,10 +210,6 @@ fn collapse_whitespace(s: &str) -> String { s.split_whitespace().collect::>().join(" ") } -thread_local!(static USED_HEADER_MAP: RefCell> = { - RefCell::new(HashMap::new()) -}); - thread_local!(pub static PLAYGROUND_KRATE: RefCell>> = { RefCell::new(None) }); @@ -311,31 +307,22 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result { let opaque = unsafe { (*data).opaque as *mut hoedown_html_renderer_state }; let opaque = unsafe { &mut *((*opaque).opaque as *mut MyOpaque) }; - // Make sure our hyphenated ID is unique for this page - let id = USED_HEADER_MAP.with(|map| { - let id = match map.borrow_mut().get_mut(&id) { - None => id, - Some(a) => { *a += 1; format!("{}-{}", id, *a - 1) } - }; - map.borrow_mut().insert(id.clone(), 1); - id + let text = with_unique_id(id, |id| { + let sec = opaque.toc_builder.as_mut().map_or("".to_owned(), |builder| { + format!("{} ", builder.push(level as u32, s.clone(), id.to_owned())) + }); + + // Render the HTML + format!("\ + {sec}{}", + s, lvl = level, id = id, sec = sec) }); - - let sec = opaque.toc_builder.as_mut().map_or("".to_owned(), |builder| { - format!("{} ", builder.push(level as u32, s.clone(), id.clone())) - }); - - // Render the HTML - let text = format!("\ - {sec}{}", - s, lvl = level, id = id, sec = sec); - let text = CString::new(text).unwrap(); unsafe { hoedown_buffer_puts(ob, text.as_ptr()) } } - reset_headers(); + reset_ids(); extern fn codespan( ob: *mut hoedown_buffer, @@ -500,18 +487,6 @@ impl LangString { } } -/// By default this markdown renderer generates anchors for each header in the -/// rendered document. The anchor name is the contents of the header separated -/// by hyphens, and a thread-local map is used to disambiguate among duplicate -/// headers (numbers are appended). -/// -/// This method will reset the local table for these headers. This is typically -/// used at the beginning of rendering an entire HTML page to reset from the -/// previous state (if any). -pub fn reset_headers() { - USED_HEADER_MAP.with(|s| s.borrow_mut().clear()); -} - impl<'a> fmt::Display for Markdown<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let Markdown(md) = *self; diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 574b9b599f5f..c4fb9d5d9e7d 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -342,6 +342,35 @@ impl fmt::Display for IndexItemFunctionType { thread_local!(static CACHE_KEY: RefCell> = Default::default()); thread_local!(pub static CURRENT_LOCATION_KEY: RefCell> = RefCell::new(Vec::new())); +thread_local!(static USED_ID_MAP: RefCell> = + RefCell::new(HashMap::new())); + +/// This method resets the local table of used ID attributes. This is typically +/// used at the beginning of rendering an entire HTML page to reset from the +/// previous state (if any). +pub fn reset_ids() { + USED_ID_MAP.with(|s| s.borrow_mut().clear()); +} + +pub fn with_unique_id T>(candidate: String, f: F) -> T { + USED_ID_MAP.with(|map| { + let (id, ret) = match map.borrow_mut().get_mut(&candidate) { + None => { + let ret = f(&candidate); + (candidate, ret) + }, + Some(a) => { + let id = format!("{}-{}", candidate, *a); + let ret = f(&id); + *a += 1; + (id, ret) + } + }; + + map.borrow_mut().insert(id, 1); + ret + }) +} /// Generates the documentation for `crate` into the directory `dst` pub fn run(mut krate: clean::Crate, @@ -1274,7 +1303,7 @@ impl Context { keywords: &keywords, }; - markdown::reset_headers(); + reset_ids(); // We have a huge number of calls to write, so try to alleviate some // of the pain by using a buffered writer instead of invoking the diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index a311b938e960..ac64fd3bec0e 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -21,9 +21,10 @@ use rustc::session::search_paths::SearchPaths; use externalfiles::ExternalHtml; +use html::render::reset_ids; use html::escape::Escape; use html::markdown; -use html::markdown::{Markdown, MarkdownWithToc, find_testable_code, reset_headers}; +use html::markdown::{Markdown, MarkdownWithToc, find_testable_code}; use test::{TestOptions, Collector}; /// Separate any lines at the start of the file that begin with `%`. @@ -82,7 +83,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches, } let title = metadata[0]; - reset_headers(); + reset_ids(); let rendered = if include_toc { format!("{}", MarkdownWithToc(text))