Remove thread-local for playground config

This commit is contained in:
Mark Rousskov 2019-08-10 18:07:07 -04:00
parent ade8b02828
commit dbad77ffdd
6 changed files with 147 additions and 117 deletions

View File

@ -378,7 +378,7 @@ impl Options {
&matches.opt_strs("html-after-content"), &matches.opt_strs("html-after-content"),
&matches.opt_strs("markdown-before-content"), &matches.opt_strs("markdown-before-content"),
&matches.opt_strs("markdown-after-content"), &matches.opt_strs("markdown-after-content"),
&diag, &mut id_map, edition) { &diag, &mut id_map, edition, &None) {
Some(eh) => eh, Some(eh) => eh,
None => return Err(3), None => return Err(3),
}; };

View File

@ -4,7 +4,7 @@ use std::str;
use errors; use errors;
use crate::syntax::feature_gate::UnstableFeatures; use crate::syntax::feature_gate::UnstableFeatures;
use crate::syntax::edition::Edition; use crate::syntax::edition::Edition;
use crate::html::markdown::{IdMap, ErrorCodes, Markdown}; use crate::html::markdown::{IdMap, ErrorCodes, Markdown, Playground};
use std::cell::RefCell; use std::cell::RefCell;
@ -24,7 +24,7 @@ pub struct ExternalHtml {
impl ExternalHtml { impl ExternalHtml {
pub fn load(in_header: &[String], before_content: &[String], after_content: &[String], pub fn load(in_header: &[String], before_content: &[String], after_content: &[String],
md_before_content: &[String], md_after_content: &[String], diag: &errors::Handler, md_before_content: &[String], md_after_content: &[String], diag: &errors::Handler,
id_map: &mut IdMap, edition: Edition) id_map: &mut IdMap, edition: Edition, playground: &Option<Playground>)
-> Option<ExternalHtml> { -> Option<ExternalHtml> {
let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()); let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
load_external_files(in_header, diag) load_external_files(in_header, diag)
@ -36,7 +36,7 @@ impl ExternalHtml {
load_external_files(md_before_content, diag) load_external_files(md_before_content, diag)
.map(|m_bc| (ih, .map(|m_bc| (ih,
format!("{}{}", bc, Markdown(&m_bc, &[], RefCell::new(id_map), format!("{}{}", bc, Markdown(&m_bc, &[], RefCell::new(id_map),
codes, edition)))) codes, edition, playground))))
) )
.and_then(|(ih, bc)| .and_then(|(ih, bc)|
load_external_files(after_content, diag) load_external_files(after_content, diag)
@ -46,7 +46,7 @@ impl ExternalHtml {
load_external_files(md_after_content, diag) load_external_files(md_after_content, diag)
.map(|m_ac| (ih, bc, .map(|m_ac| (ih, bc,
format!("{}{}", ac, Markdown(&m_ac, &[], RefCell::new(id_map), format!("{}{}", ac, Markdown(&m_ac, &[], RefCell::new(id_map),
codes, edition)))) codes, edition, playground))))
) )
.map(|(ih, bc, ac)| .map(|(ih, bc, ac)|
ExternalHtml { ExternalHtml {

View File

@ -17,7 +17,7 @@
//! let s = "My *markdown* _text_"; //! let s = "My *markdown* _text_";
//! let mut id_map = IdMap::new(); //! let mut id_map = IdMap::new();
//! let html = format!("{}", Markdown(s, &[], RefCell::new(&mut id_map), //! let html = format!("{}", Markdown(s, &[], RefCell::new(&mut id_map),
//! ErrorCodes::Yes, Edition::Edition2015)); //! ErrorCodes::Yes, Edition::Edition2015, None));
//! // ... something using html //! // ... something using html
//! ``` //! ```
@ -59,6 +59,7 @@ pub struct Markdown<'a>(
pub ErrorCodes, pub ErrorCodes,
/// Default edition to use when parsing doctests (to add a `fn main`). /// Default edition to use when parsing doctests (to add a `fn main`).
pub Edition, pub Edition,
pub &'a Option<Playground>,
); );
/// A tuple struct like `Markdown` that renders the markdown with a table of contents. /// A tuple struct like `Markdown` that renders the markdown with a table of contents.
pub struct MarkdownWithToc<'a>( pub struct MarkdownWithToc<'a>(
@ -66,9 +67,16 @@ pub struct MarkdownWithToc<'a>(
pub RefCell<&'a mut IdMap>, pub RefCell<&'a mut IdMap>,
pub ErrorCodes, pub ErrorCodes,
pub Edition, pub Edition,
pub &'a Option<Playground>,
); );
/// A tuple struct like `Markdown` that renders the markdown escaping HTML tags. /// A tuple struct like `Markdown` that renders the markdown escaping HTML tags.
pub struct MarkdownHtml<'a>(pub &'a str, pub RefCell<&'a mut IdMap>, pub ErrorCodes, pub Edition); pub struct MarkdownHtml<'a>(
pub &'a str,
pub RefCell<&'a mut IdMap>,
pub ErrorCodes,
pub Edition,
pub &'a Option<Playground>,
);
/// A tuple struct like `Markdown` that renders only the first paragraph. /// A tuple struct like `Markdown` that renders only the first paragraph.
pub struct MarkdownSummaryLine<'a>(pub &'a str, pub &'a [(String, String)]); pub struct MarkdownSummaryLine<'a>(pub &'a str, pub &'a [(String, String)]);
@ -155,30 +163,39 @@ fn slugify(c: char) -> Option<char> {
} }
} }
// Information about the playground if a URL has been specified, containing an #[derive(Clone, Debug)]
// optional crate name and the URL. pub struct Playground {
thread_local!(pub static PLAYGROUND: RefCell<Option<(Option<String>, String)>> = { pub crate_name: Option<String>,
RefCell::new(None) pub url: String,
}); }
/// Adds syntax highlighting and playground Run buttons to Rust code blocks. /// Adds syntax highlighting and playground Run buttons to Rust code blocks.
struct CodeBlocks<'a, I: Iterator<Item = Event<'a>>> { struct CodeBlocks<'p, 'a, I: Iterator<Item = Event<'a>>> {
inner: I, inner: I,
check_error_codes: ErrorCodes, check_error_codes: ErrorCodes,
edition: Edition, edition: Edition,
// Information about the playground if a URL has been specified, containing an
// optional crate name and the URL.
playground: &'p Option<Playground>,
} }
impl<'a, I: Iterator<Item = Event<'a>>> CodeBlocks<'a, I> { impl<'p, 'a, I: Iterator<Item = Event<'a>>> CodeBlocks<'p, 'a, I> {
fn new(iter: I, error_codes: ErrorCodes, edition: Edition) -> Self { fn new(
iter: I,
error_codes: ErrorCodes,
edition: Edition,
playground: &'p Option<Playground>,
) -> Self {
CodeBlocks { CodeBlocks {
inner: iter, inner: iter,
check_error_codes: error_codes, check_error_codes: error_codes,
edition, edition,
playground,
} }
} }
} }
impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> { impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
type Item = Event<'a>; type Item = Event<'a>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
@ -213,86 +230,86 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
} }
let lines = origtext.lines().filter_map(|l| map_line(l).for_html()); let lines = origtext.lines().filter_map(|l| map_line(l).for_html());
let text = lines.collect::<Vec<Cow<'_, str>>>().join("\n"); let text = lines.collect::<Vec<Cow<'_, str>>>().join("\n");
PLAYGROUND.with(|play| { // insert newline to clearly separate it from the
// insert newline to clearly separate it from the // previous block so we can shorten the html output
// previous block so we can shorten the html output let mut s = String::from("\n");
let mut s = String::from("\n"); let playground_button = self.playground.as_ref().and_then(|playground| {
let playground_button = play.borrow().as_ref().and_then(|&(ref krate, ref url)| { let krate = &playground.crate_name;
if url.is_empty() { let url = &playground.url;
return None; if url.is_empty() {
} return None;
let test = origtext.lines() }
.map(|l| map_line(l).for_code()) let test = origtext.lines()
.collect::<Vec<Cow<'_, str>>>().join("\n"); .map(|l| map_line(l).for_code())
let krate = krate.as_ref().map(|s| &**s); .collect::<Vec<Cow<'_, str>>>().join("\n");
let (test, _) = test::make_test(&test, krate, false, let krate = krate.as_ref().map(|s| &**s);
&Default::default(), edition); let (test, _) = test::make_test(&test, krate, false,
let channel = if test.contains("#![feature(") { &Default::default(), edition);
"&amp;version=nightly" let channel = if test.contains("#![feature(") {
} else { "&amp;version=nightly"
""
};
let edition_string = format!("&amp;edition={}", edition);
// These characters don't need to be escaped in a URI.
// FIXME: use a library function for percent encoding.
fn dont_escape(c: u8) -> bool {
(b'a' <= c && c <= b'z') ||
(b'A' <= c && c <= b'Z') ||
(b'0' <= c && c <= b'9') ||
c == b'-' || c == b'_' || c == b'.' ||
c == b'~' || c == b'!' || c == b'\'' ||
c == b'(' || c == b')' || c == b'*'
}
let mut test_escaped = String::new();
for b in test.bytes() {
if dont_escape(b) {
test_escaped.push(char::from(b));
} else {
write!(test_escaped, "%{:02X}", b).unwrap();
}
}
Some(format!(
r#"<a class="test-arrow" target="_blank" href="{}?code={}{}{}">Run</a>"#,
url, test_escaped, channel, edition_string
))
});
let tooltip = if ignore {
Some(("This example is not tested".to_owned(), "ignore"))
} else if compile_fail {
Some(("This example deliberately fails to compile".to_owned(), "compile_fail"))
} else if explicit_edition {
Some((format!("This code runs with edition {}", edition), "edition"))
} else { } else {
None ""
}; };
if let Some((s1, s2)) = tooltip { let edition_string = format!("&amp;edition={}", edition);
s.push_str(&highlight::render_with_highlighting(
&text, // These characters don't need to be escaped in a URI.
Some(&format!("rust-example-rendered{}", // FIXME: use a library function for percent encoding.
if ignore { " ignore" } fn dont_escape(c: u8) -> bool {
else if compile_fail { " compile_fail" } (b'a' <= c && c <= b'z') ||
else if explicit_edition { " edition " } (b'A' <= c && c <= b'Z') ||
else { "" })), (b'0' <= c && c <= b'9') ||
playground_button.as_ref().map(String::as_str), c == b'-' || c == b'_' || c == b'.' ||
Some((s1.as_str(), s2)))); c == b'~' || c == b'!' || c == b'\'' ||
Some(Event::Html(s.into())) c == b'(' || c == b')' || c == b'*'
} else {
s.push_str(&highlight::render_with_highlighting(
&text,
Some(&format!("rust-example-rendered{}",
if ignore { " ignore" }
else if compile_fail { " compile_fail" }
else if explicit_edition { " edition " }
else { "" })),
playground_button.as_ref().map(String::as_str),
None));
Some(Event::Html(s.into()))
} }
}) let mut test_escaped = String::new();
for b in test.bytes() {
if dont_escape(b) {
test_escaped.push(char::from(b));
} else {
write!(test_escaped, "%{:02X}", b).unwrap();
}
}
Some(format!(
r#"<a class="test-arrow" target="_blank" href="{}?code={}{}{}">Run</a>"#,
url, test_escaped, channel, edition_string
))
});
let tooltip = if ignore {
Some(("This example is not tested".to_owned(), "ignore"))
} else if compile_fail {
Some(("This example deliberately fails to compile".to_owned(), "compile_fail"))
} else if explicit_edition {
Some((format!("This code runs with edition {}", edition), "edition"))
} else {
None
};
if let Some((s1, s2)) = tooltip {
s.push_str(&highlight::render_with_highlighting(
&text,
Some(&format!("rust-example-rendered{}",
if ignore { " ignore" }
else if compile_fail { " compile_fail" }
else if explicit_edition { " edition " }
else { "" })),
playground_button.as_ref().map(String::as_str),
Some((s1.as_str(), s2))));
Some(Event::Html(s.into()))
} else {
s.push_str(&highlight::render_with_highlighting(
&text,
Some(&format!("rust-example-rendered{}",
if ignore { " ignore" }
else if compile_fail { " compile_fail" }
else if explicit_edition { " edition " }
else { "" })),
playground_button.as_ref().map(String::as_str),
None));
Some(Event::Html(s.into()))
}
} }
} }
@ -676,7 +693,7 @@ impl LangString {
impl<'a> fmt::Display for Markdown<'a> { impl<'a> fmt::Display for Markdown<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let Markdown(md, links, ref ids, codes, edition) = *self; let Markdown(md, links, ref ids, codes, edition, playground) = *self;
let mut ids = ids.borrow_mut(); let mut ids = ids.borrow_mut();
// This is actually common enough to special-case // This is actually common enough to special-case
@ -695,7 +712,7 @@ impl<'a> fmt::Display for Markdown<'a> {
let p = HeadingLinks::new(p, None, &mut ids); let p = HeadingLinks::new(p, None, &mut ids);
let p = LinkReplacer::new(p, links); let p = LinkReplacer::new(p, links);
let p = CodeBlocks::new(p, codes, edition); let p = CodeBlocks::new(p, codes, edition, playground);
let p = Footnotes::new(p); let p = Footnotes::new(p);
html::push_html(&mut s, p); html::push_html(&mut s, p);
@ -705,7 +722,7 @@ impl<'a> fmt::Display for Markdown<'a> {
impl<'a> fmt::Display for MarkdownWithToc<'a> { impl<'a> fmt::Display for MarkdownWithToc<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let MarkdownWithToc(md, ref ids, codes, edition) = *self; let MarkdownWithToc(md, ref ids, codes, edition, playground) = *self;
let mut ids = ids.borrow_mut(); let mut ids = ids.borrow_mut();
let p = Parser::new_ext(md, opts()); let p = Parser::new_ext(md, opts());
@ -716,7 +733,7 @@ impl<'a> fmt::Display for MarkdownWithToc<'a> {
{ {
let p = HeadingLinks::new(p, Some(&mut toc), &mut ids); let p = HeadingLinks::new(p, Some(&mut toc), &mut ids);
let p = CodeBlocks::new(p, codes, edition); let p = CodeBlocks::new(p, codes, edition, playground);
let p = Footnotes::new(p); let p = Footnotes::new(p);
html::push_html(&mut s, p); html::push_html(&mut s, p);
} }
@ -729,7 +746,7 @@ impl<'a> fmt::Display for MarkdownWithToc<'a> {
impl<'a> fmt::Display for MarkdownHtml<'a> { impl<'a> fmt::Display for MarkdownHtml<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let MarkdownHtml(md, ref ids, codes, edition) = *self; let MarkdownHtml(md, ref ids, codes, edition, playground) = *self;
let mut ids = ids.borrow_mut(); let mut ids = ids.borrow_mut();
// This is actually common enough to special-case // This is actually common enough to special-case
@ -745,7 +762,7 @@ impl<'a> fmt::Display for MarkdownHtml<'a> {
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, &mut ids); let p = HeadingLinks::new(p, None, &mut ids);
let p = CodeBlocks::new(p, codes, edition); let p = CodeBlocks::new(p, codes, edition, playground);
let p = Footnotes::new(p); let p = Footnotes::new(p);
html::push_html(&mut s, p); html::push_html(&mut s, p);

View File

@ -170,6 +170,7 @@ struct Context {
/// The map used to ensure all generated 'id=' attributes are unique. /// The map used to ensure all generated 'id=' attributes are unique.
id_map: Rc<RefCell<IdMap>>, id_map: Rc<RefCell<IdMap>>,
pub shared: Arc<SharedContext>, pub shared: Arc<SharedContext>,
playground: Option<markdown::Playground>,
} }
struct SharedContext { struct SharedContext {
@ -574,9 +575,11 @@ pub fn run(mut krate: clean::Crate,
}; };
// If user passed in `--playground-url` arg, we fill in crate name here // If user passed in `--playground-url` arg, we fill in crate name here
let mut playground = None;
if let Some(url) = playground_url { if let Some(url) = playground_url {
markdown::PLAYGROUND.with(|slot| { playground = Some(markdown::Playground {
*slot.borrow_mut() = Some((Some(krate.name.clone()), url)); crate_name: Some(krate.name.clone()),
url,
}); });
} }
@ -592,9 +595,9 @@ pub fn run(mut krate: clean::Crate,
scx.layout.logo = s.to_string(); scx.layout.logo = s.to_string();
} }
(sym::html_playground_url, Some(s)) => { (sym::html_playground_url, Some(s)) => {
markdown::PLAYGROUND.with(|slot| { playground = Some(markdown::Playground {
let name = krate.name.clone(); crate_name: Some(krate.name.clone()),
*slot.borrow_mut() = Some((Some(name), s.to_string())); url: s.to_string(),
}); });
} }
(sym::issue_tracker_base_url, Some(s)) => { (sym::issue_tracker_base_url, Some(s)) => {
@ -618,6 +621,7 @@ pub fn run(mut krate: clean::Crate,
edition, edition,
id_map: Rc::new(RefCell::new(id_map)), id_map: Rc::new(RefCell::new(id_map)),
shared: Arc::new(scx), shared: Arc::new(scx),
playground,
}; };
// Crawl the crate to build various caches used for the output // Crawl the crate to build various caches used for the output
@ -2592,7 +2596,7 @@ fn render_markdown(w: &mut fmt::Formatter<'_>,
if is_hidden { " hidden" } else { "" }, if is_hidden { " hidden" } else { "" },
prefix, prefix,
Markdown(md_text, &links, RefCell::new(&mut ids), Markdown(md_text, &links, RefCell::new(&mut ids),
cx.codes, cx.edition)) cx.codes, cx.edition, &cx.playground))
} }
fn document_short( fn document_short(
@ -2957,7 +2961,8 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
if let Some(note) = note { if let Some(note) = note {
let mut ids = cx.id_map.borrow_mut(); let mut ids = cx.id_map.borrow_mut();
let html = MarkdownHtml(&note, RefCell::new(&mut ids), error_codes, cx.edition); let html = MarkdownHtml(
&note, RefCell::new(&mut ids), error_codes, cx.edition, &cx.playground);
message.push_str(&format!(": {}", html)); message.push_str(&format!(": {}", html));
} }
stability.push(format!("<div class='stab deprecated'>{}</div>", message)); stability.push(format!("<div class='stab deprecated'>{}</div>", message));
@ -3006,7 +3011,13 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
message = format!( message = format!(
"<details><summary>{}</summary>{}</details>", "<details><summary>{}</summary>{}</details>",
message, message,
MarkdownHtml(&unstable_reason, RefCell::new(&mut ids), error_codes, cx.edition) MarkdownHtml(
&unstable_reason,
RefCell::new(&mut ids),
error_codes,
cx.edition,
&cx.playground,
)
); );
} }
@ -4237,7 +4248,7 @@ fn render_impl(w: &mut fmt::Formatter<'_>, cx: &Context, i: &Impl, link: AssocIt
let mut ids = cx.id_map.borrow_mut(); let mut ids = cx.id_map.borrow_mut();
write!(w, "<div class='docblock'>{}</div>", write!(w, "<div class='docblock'>{}</div>",
Markdown(&*dox, &i.impl_item.links(), RefCell::new(&mut ids), Markdown(&*dox, &i.impl_item.links(), RefCell::new(&mut ids),
cx.codes, cx.edition))?; cx.codes, cx.edition, &cx.playground))?;
} }
} }

View File

@ -60,9 +60,10 @@ pub fn render(
}; };
let playground_url = options.markdown_playground_url let playground_url = options.markdown_playground_url
.or(options.playground_url); .or(options.playground_url);
if let Some(playground) = playground_url { let playground = playground_url.map(|url| markdown::Playground {
markdown::PLAYGROUND.with(|s| { *s.borrow_mut() = Some((None, playground)); }); crate_name: None,
} url,
});
let mut out = match File::create(&output) { let mut out = match File::create(&output) {
Err(e) => { Err(e) => {
@ -82,9 +83,9 @@ pub fn render(
let mut ids = IdMap::new(); let mut ids = IdMap::new();
let error_codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()); let error_codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
let text = if !options.markdown_no_toc { let text = if !options.markdown_no_toc {
MarkdownWithToc(text, RefCell::new(&mut ids), error_codes, edition).to_string() MarkdownWithToc(text, RefCell::new(&mut ids), error_codes, edition, &playground).to_string()
} else { } else {
Markdown(text, &[], RefCell::new(&mut ids), error_codes, edition).to_string() Markdown(text, &[], RefCell::new(&mut ids), error_codes, edition, &playground).to_string()
}; };
let err = write!( let err = write!(

View File

@ -16,7 +16,7 @@ use std::cell::RefCell;
use syntax::edition::DEFAULT_EDITION; use syntax::edition::DEFAULT_EDITION;
use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap, ErrorMetadata}; use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap, ErrorMetadata};
use rustdoc::html::markdown::{Markdown, IdMap, ErrorCodes, PLAYGROUND}; use rustdoc::html::markdown::{Markdown, IdMap, ErrorCodes, Playground};
use rustc_serialize::json; use rustc_serialize::json;
enum OutputFormat { enum OutputFormat {
@ -95,9 +95,13 @@ impl Formatter for HTMLFormatter {
match info.description { match info.description {
Some(ref desc) => { Some(ref desc) => {
let mut id_map = self.0.borrow_mut(); let mut id_map = self.0.borrow_mut();
let playground = Playground {
crate_name: None,
url: String::from("https://play.rust-lang.org/"),
};
write!(output, "{}", write!(output, "{}",
Markdown(desc, &[], RefCell::new(&mut id_map), Markdown(desc, &[], RefCell::new(&mut id_map),
ErrorCodes::Yes, DEFAULT_EDITION))? ErrorCodes::Yes, DEFAULT_EDITION, &Some(playground)))?
}, },
None => write!(output, "<p>No description.</p>\n")?, None => write!(output, "<p>No description.</p>\n")?,
} }
@ -260,9 +264,6 @@ fn parse_args() -> (OutputFormat, PathBuf) {
fn main() { fn main() {
env_logger::init(); env_logger::init();
PLAYGROUND.with(|slot| {
*slot.borrow_mut() = Some((None, String::from("https://play.rust-lang.org/")));
});
let (format, dst) = parse_args(); let (format, dst) = parse_args();
let result = syntax::with_default_globals(move || { let result = syntax::with_default_globals(move || {
main_with_result(format, &dst) main_with_result(format, &dst)