From 0b2746c8db6fc11965b1b6fd1d8536309a0d98b6 Mon Sep 17 00:00:00 2001 From: Oliver Middleton Date: Tue, 11 Oct 2016 09:56:30 +0100 Subject: [PATCH] rustdoc: Improve playground run buttons The main change is to stop using javascript to generate the URLs and use rustdoc instead. This also adds run buttons to the error index examples. --- src/doc/footer.inc | 1 - src/doc/rust.css | 8 +++-- src/librustdoc/html/layout.rs | 9 ----- src/librustdoc/html/markdown.rs | 43 ++++++++++++++++++---- src/librustdoc/html/render.rs | 8 ++--- src/librustdoc/html/static/extra.js | 25 ------------- src/librustdoc/html/static/playpen.js | 48 ------------------------- src/librustdoc/html/static/rustdoc.css | 1 - src/librustdoc/markdown.rs | 10 ++---- src/librustdoc/test.rs | 2 +- src/test/rustdoc/playground-empty.rs | 21 +++++++++++ src/test/rustdoc/playground-none.rs | 19 ++++++++++ src/test/rustdoc/playground.rs | 39 ++++++++++++++++++++ src/tools/error_index_generator/main.rs | 5 ++- src/tools/rustbook/build.rs | 7 +--- 15 files changed, 131 insertions(+), 115 deletions(-) delete mode 100644 src/librustdoc/html/static/extra.js delete mode 100644 src/librustdoc/html/static/playpen.js create mode 100644 src/test/rustdoc/playground-empty.rs create mode 100644 src/test/rustdoc/playground-none.rs create mode 100644 src/test/rustdoc/playground.rs diff --git a/src/doc/footer.inc b/src/doc/footer.inc index 7513e524e73..77e151235e8 100644 --- a/src/doc/footer.inc +++ b/src/doc/footer.inc @@ -5,4 +5,3 @@ or the MIT license, at your op

This file may not be copied, modified, or distributed except according to those terms.

- diff --git a/src/doc/rust.css b/src/doc/rust.css index 262db5673e8..932594b9912 100644 --- a/src/doc/rust.css +++ b/src/doc/rust.css @@ -336,13 +336,11 @@ table th { /* Code snippets */ -.rusttest { display: none; } pre.rust { position: relative; } a.test-arrow { + background-color: rgba(78, 139, 202, 0.2); display: inline-block; position: absolute; - - background-color: #4e8bca; color: #f5f5f5; padding: 5px 10px 5px 10px; border-radius: 5px; @@ -350,6 +348,10 @@ a.test-arrow { top: 5px; right: 5px; } +a.test-arrow:hover{ + background-color: #4e8bca; + text-decoration: none; +} .unstable-feature { border: 2px solid red; diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index b7c5876c4f9..5353642e294 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -19,7 +19,6 @@ pub struct Layout { pub favicon: String, pub external_html: ExternalHtml, pub krate: String, - pub playground_url: String, } pub struct Page<'a> { @@ -136,11 +135,9 @@ r##" - {play_js} "##, @@ -174,12 +171,6 @@ r##" after_content = layout.external_html.after_content, sidebar = *sidebar, krate = layout.krate, - play_url = layout.playground_url, - play_js = if layout.playground_url.is_empty() { - format!(r#""#, page.root_path) - } else { - format!(r#""#, page.root_path) - } ) } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index e9a1f650c9b..f12349e5b7c 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -31,7 +31,7 @@ use std::ascii::AsciiExt; use std::cell::RefCell; use std::default::Default; use std::ffi::CString; -use std::fmt; +use std::fmt::{self, Write}; use std::slice; use std::str; use syntax::feature_gate::UnstableFeatures; @@ -214,7 +214,9 @@ fn collapse_whitespace(s: &str) -> String { s.split_whitespace().collect::>().join(" ") } -thread_local!(pub static PLAYGROUND_KRATE: RefCell>> = { +// Information about the playground if a URL has been specified, containing an +// optional crate name and the URL. +thread_local!(pub static PLAYGROUND: RefCell, String)>> = { RefCell::new(None) }); @@ -248,24 +250,53 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result { }); let text = lines.collect::>().join("\n"); if rendered { return } - PLAYGROUND_KRATE.with(|krate| { + PLAYGROUND.with(|play| { // insert newline to clearly separate it from the // previous block so we can shorten the html output let mut s = String::from("\n"); - krate.borrow().as_ref().map(|krate| { + let playground_button = play.borrow().as_ref().and_then(|&(ref krate, ref url)| { + if url.is_empty() { + return None; + } let test = origtext.lines().map(|l| { stripped_filtered_line(l).unwrap_or(l) }).collect::>().join("\n"); let krate = krate.as_ref().map(|s| &**s); let test = test::maketest(&test, krate, false, &Default::default()); - s.push_str(&format!("{}", Escape(&test))); + let channel = if test.contains("#![feature(") { + "&version=nightly" + } else { + "" + }; + // 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#"Run"#, + url, test_escaped, channel + )) }); s.push_str(&highlight::render_with_highlighting( &text, Some("rust-example-rendered"), None, - Some("Run"))); + playground_button.as_ref().map(String::as_str))); let output = CString::new(s).unwrap(); hoedown_buffer_puts(ob, output.as_ptr()); }) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index d8dba00e7d5..77a5ff3243a 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -449,7 +449,6 @@ pub fn run(mut krate: clean::Crate, favicon: "".to_string(), external_html: external_html.clone(), krate: krate.name.clone(), - playground_url: "".to_string(), }, css_file_extension: css_file_extension.clone(), }; @@ -469,11 +468,10 @@ pub fn run(mut krate: clean::Crate, } clean::NameValue(ref x, ref s) if "html_playground_url" == *x => { - scx.layout.playground_url = s.to_string(); - markdown::PLAYGROUND_KRATE.with(|slot| { + markdown::PLAYGROUND.with(|slot| { if slot.borrow().is_none() { let name = krate.name.clone(); - *slot.borrow_mut() = Some(Some(name)); + *slot.borrow_mut() = Some((Some(name), s.clone())); } }); } @@ -659,8 +657,6 @@ fn write_shared(cx: &Context, include_bytes!("static/jquery-2.1.4.min.js"))?; write(cx.dst.join("main.js"), include_bytes!("static/main.js"))?; - write(cx.dst.join("playpen.js"), - include_bytes!("static/playpen.js"))?; write(cx.dst.join("rustdoc.css"), include_bytes!("static/rustdoc.css"))?; write(cx.dst.join("main.css"), diff --git a/src/librustdoc/html/static/extra.js b/src/librustdoc/html/static/extra.js deleted file mode 100644 index d9d97d9b883..00000000000 --- a/src/librustdoc/html/static/extra.js +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2014-2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*jslint browser: true, es5: true */ -/*globals $: true, rootPath: true */ - -document.addEventListener('DOMContentLoaded', function() { - 'use strict'; - - if (!window.playgroundUrl) { - var runButtons = document.querySelectorAll(".test-arrow"); - - for (var i = 0; i < runButtons.length; i++) { - runButtons[i].classList.remove("test-arrow"); - } - return; - } -}); diff --git a/src/librustdoc/html/static/playpen.js b/src/librustdoc/html/static/playpen.js deleted file mode 100644 index 8d8953d56e1..00000000000 --- a/src/librustdoc/html/static/playpen.js +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*jslint browser: true, es5: true */ -/*globals $: true, rootPath: true */ - -document.addEventListener('DOMContentLoaded', function() { - 'use strict'; - - if (!window.playgroundUrl) { - var runButtons = document.querySelectorAll(".test-arrow"); - - for (var i = 0; i < runButtons.length; i++) { - runButtons[i].classList.remove("test-arrow"); - } - return; - } - - var featureRegexp = new RegExp('^\s*#!\\[feature\\(\.*?\\)\\]'); - var elements = document.querySelectorAll('pre.rust-example-rendered'); - - Array.prototype.forEach.call(elements, function(el) { - el.onmouseover = function(e) { - if (el.contains(e.relatedTarget)) { - return; - } - - var a = el.querySelectorAll('a.test-arrow')[0]; - - var code = el.previousElementSibling.textContent; - - var channel = ''; - if (featureRegexp.test(code)) { - channel = '&version=nightly'; - } - - a.setAttribute('href', window.playgroundUrl + '?code=' + - encodeURIComponent(code) + channel); - }; - }); -}); diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 85ec4fe3f3f..f49b8556f66 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -575,7 +575,6 @@ pre.rust .question-mark { font-weight: bold; } -.rusttest { display: none; } pre.rust { position: relative; } a.test-arrow { background-color: rgba(78, 139, 202, 0.2); diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index f708aa54619..b617acfabbb 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -63,11 +63,9 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches, Err(LoadStringError::ReadFail) => return 1, Err(LoadStringError::BadUtf8) => return 2, }; - let playground = matches.opt_str("markdown-playground-url"); - if playground.is_some() { - markdown::PLAYGROUND_KRATE.with(|s| { *s.borrow_mut() = Some(None); }); + if let Some(playground) = matches.opt_str("markdown-playground-url") { + markdown::PLAYGROUND.with(|s| { *s.borrow_mut() = Some((None, playground)); }); } - let playground = playground.unwrap_or("".to_string()); let mut out = match File::create(&output) { Err(e) => { @@ -119,9 +117,6 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches, {before_content}

{title}

{text} - {after_content} "#, @@ -131,7 +126,6 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches, before_content = external_html.before_content, text = rendered, after_content = external_html.after_content, - playground = playground, ); match err { diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 15fdb0341cb..d1d2b14806f 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -355,7 +355,7 @@ pub fn maketest(s: &str, cratename: Option<&str>, dont_insert_main: bool, if dont_insert_main || s.contains("fn main") { prog.push_str(&everything_else); } else { - prog.push_str("fn main() {\n "); + prog.push_str("fn main() {\n"); prog.push_str(&everything_else); prog = prog.trim().into(); prog.push_str("\n}"); diff --git a/src/test/rustdoc/playground-empty.rs b/src/test/rustdoc/playground-empty.rs new file mode 100644 index 00000000000..00881a62dd0 --- /dev/null +++ b/src/test/rustdoc/playground-empty.rs @@ -0,0 +1,21 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +#![doc(html_playground_url = "")] + +//! module docs +//! +//! ``` +//! println!("Hello, world!"); +//! ``` + +// @!has foo/index.html '//a[@class="test-arrow"]' "Run" diff --git a/src/test/rustdoc/playground-none.rs b/src/test/rustdoc/playground-none.rs new file mode 100644 index 00000000000..83c312d7ab2 --- /dev/null +++ b/src/test/rustdoc/playground-none.rs @@ -0,0 +1,19 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "foo"] + +//! module docs +//! +//! ``` +//! println!("Hello, world!"); +//! ``` + +// @!has foo/index.html '//a[@class="test-arrow"]' "Run" diff --git a/src/test/rustdoc/playground.rs b/src/test/rustdoc/playground.rs new file mode 100644 index 00000000000..9eb8dec51a7 --- /dev/null +++ b/src/test/rustdoc/playground.rs @@ -0,0 +1,39 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength + +#![crate_name = "foo"] + +#![doc(html_playground_url = "https://www.example.com/")] + +//! module docs +//! +//! ``` +//! println!("Hello, world!"); +//! ``` +//! +//! ``` +//! fn main() { +//! println!("Hello, world!"); +//! } +//! ``` +//! +//! ``` +//! #![feature(something)] +//! +//! fn main() { +//! println!("Hello, world!"); +//! } +//! ``` + +// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=fn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D%0A"]' "Run" +// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=fn%20main()%20%7B%0Aprintln!(%22Hello%2C%20world!%22)%3B%0A%7D"]' "Run" +// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Bfeature(something)%5D%0A%0Afn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D%0A&version=nightly"]' "Run" diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs index 7bdf1343aa9..e33df0dfbc8 100644 --- a/src/tools/error_index_generator/main.rs +++ b/src/tools/error_index_generator/main.rs @@ -24,7 +24,7 @@ use std::path::PathBuf; use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap, ErrorMetadata}; -use rustdoc::html::markdown::Markdown; +use rustdoc::html::markdown::{Markdown, PLAYGROUND}; use rustc_serialize::json; enum OutputFormat { @@ -201,6 +201,9 @@ fn parse_args() -> (OutputFormat, PathBuf) { } fn main() { + PLAYGROUND.with(|slot| { + *slot.borrow_mut() = Some((None, String::from("https://play.rust-lang.org/"))); + }); let (format, dst) = parse_args(); if let Err(e) = main_with_result(format, &dst) { panic!("{}", e.description()); diff --git a/src/tools/rustbook/build.rs b/src/tools/rustbook/build.rs index 09c2d2510e3..d88ff48843a 100644 --- a/src/tools/rustbook/build.rs +++ b/src/tools/rustbook/build.rs @@ -131,7 +131,6 @@ fn render(book: &Book, tgt: &Path) -> CliResult<()> { { let mut buffer = BufWriter::new(File::create(&postlude)?); writeln!(&mut buffer, "")?; - writeln!(&mut buffer, "")?; writeln!(&mut buffer, "")?; } @@ -143,7 +142,7 @@ fn render(book: &Book, tgt: &Path) -> CliResult<()> { format!("-o{}", out_path.display()), format!("--html-before-content={}", prelude.display()), format!("--html-after-content={}", postlude.display()), - format!("--markdown-playground-url=https://play.rust-lang.org"), + format!("--markdown-playground-url=https://play.rust-lang.org/"), format!("--markdown-css={}", item.path_to_root.join("rustbook.css").display()), "--markdown-no-toc".to_string(), ]; @@ -158,10 +157,6 @@ fn render(book: &Book, tgt: &Path) -> CliResult<()> { // create index.html from the root README fs::copy(&tgt.join("README.html"), &tgt.join("index.html"))?; - // Copy js for playpen - let mut playpen = File::create(tgt.join("playpen.js"))?; - let js = include_bytes!("../../librustdoc/html/static/playpen.js"); - playpen.write_all(js)?; Ok(()) }