Rollup merge of #91062 - jsha:static-file-replace, r=jyn514,GuillaumeGomez

rustdoc: Consolidate static-file replacement mechanism

There were a few places in rustdoc where we would take static JS or CSS and rewrite it at doc generation time to insert values. This consolidates all the CSS instances into one CSS file and replaces the JS examples with data- attributes on the rustdoc-vars div.

Demo https://rustdoc.crud.net/jsha/static-file-replace/test_docs/

r? ``@GuillaumeGomez``
This commit is contained in:
Matthias Krüger 2021-11-27 11:46:43 +01:00 committed by GitHub
commit 55f8b5f559
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 118 additions and 132 deletions

View File

@ -552,7 +552,7 @@ impl Options {
))
.emit();
}
themes.push(StylePath { path: theme_file, disabled: true });
themes.push(StylePath { path: theme_file });
}
}

View File

@ -39,7 +39,10 @@ macro_rules! try_none {
match $e {
Some(e) => e,
None => {
return Err(Error::new(io::Error::new(io::ErrorKind::Other, "not found"), $file));
return Err(<crate::error::Error as crate::docfs::PathError>::new(
io::Error::new(io::ErrorKind::Other, "not found"),
$file,
));
}
}
}};

View File

@ -2,8 +2,8 @@ use std::path::PathBuf;
use rustc_data_structures::fx::FxHashMap;
use crate::error::Error;
use crate::externalfiles::ExternalHtml;
use crate::html::escape::Escape;
use crate::html::format::{Buffer, Print};
use crate::html::render::{ensure_trailing_slash, StylePath};
@ -50,10 +50,11 @@ struct PageLayout<'a> {
static_root_path: &'a str,
page: &'a Page<'a>,
layout: &'a Layout,
style_files: String,
themes: Vec<String>,
sidebar: String,
content: String,
krate_with_trailing_slash: String,
crate rustdoc_version: &'a str,
}
crate fn render<T: Print, S: Print>(
@ -66,29 +67,24 @@ crate fn render<T: Print, S: Print>(
) -> String {
let static_root_path = page.get_static_root_path();
let krate_with_trailing_slash = ensure_trailing_slash(&layout.krate).to_string();
let style_files = style_files
let mut themes: Vec<String> = style_files
.iter()
.filter_map(|t| t.path.file_stem().map(|stem| (stem, t.disabled)))
.filter_map(|t| t.0.to_str().map(|path| (path, t.1)))
.map(|t| {
format!(
r#"<link rel="stylesheet" type="text/css" href="{}.css" {} {}>"#,
Escape(&format!("{}{}{}", static_root_path, t.0, page.resource_suffix)),
if t.1 { "disabled" } else { "" },
if t.0 == "light" { "id=\"themeStyle\"" } else { "" }
)
})
.collect::<String>();
.map(StylePath::basename)
.collect::<Result<_, Error>>()
.unwrap_or_default();
themes.sort();
let rustdoc_version = rustc_interface::util::version_str().unwrap_or("unknown version");
let content = Buffer::html().to_display(t); // Note: This must happen before making the sidebar.
let sidebar = Buffer::html().to_display(sidebar);
let teractx = tera::Context::from_serialize(PageLayout {
static_root_path,
page,
layout,
style_files,
themes,
sidebar,
content,
krate_with_trailing_slash,
rustdoc_version,
})
.unwrap();
templates.render("page.html", &teractx).unwrap()

View File

@ -504,9 +504,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
// by the browser as the theme stylesheet. The theme system (hackily) works by
// changing the href to this stylesheet. All other themes are disabled to
// prevent rule conflicts
scx.style_files.push(StylePath { path: PathBuf::from("light.css"), disabled: false });
scx.style_files.push(StylePath { path: PathBuf::from("dark.css"), disabled: true });
scx.style_files.push(StylePath { path: PathBuf::from("ayu.css"), disabled: true });
scx.style_files.push(StylePath { path: PathBuf::from("light.css") });
scx.style_files.push(StylePath { path: PathBuf::from("dark.css") });
scx.style_files.push(StylePath { path: PathBuf::from("ayu.css") });
let dst = output;
scx.ensure_dir(&dst)?;
@ -596,9 +596,13 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
page.description = "Settings of Rustdoc";
page.root_path = "./";
let mut style_files = self.shared.style_files.clone();
let sidebar = "<h2 class=\"location\">Settings</h2><div class=\"sidebar-elems\"></div>";
style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false });
let theme_names: Vec<String> = self
.shared
.style_files
.iter()
.map(StylePath::basename)
.collect::<Result<_, Error>>()?;
let v = layout::render(
&self.shared.templates,
&self.shared.layout,
@ -607,9 +611,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
settings(
self.shared.static_root_path.as_deref().unwrap_or("./"),
&self.shared.resource_suffix,
&self.shared.style_files,
theme_names,
)?,
&style_files,
&self.shared.style_files,
);
self.shared.fs.write(settings_file, v)?;
if let Some(ref redirections) = self.shared.redirections {

View File

@ -64,7 +64,6 @@ use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
use crate::clean::{self, ItemId, RenderedLink, SelfTy};
use crate::docfs::PathError;
use crate::error::Error;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
@ -173,8 +172,12 @@ impl Serialize for TypeWithKind {
crate struct StylePath {
/// The path to the theme
crate path: PathBuf,
/// What the `disabled` attribute should be set to in the HTML tag
crate disabled: bool,
}
impl StylePath {
pub fn basename(&self) -> Result<String, Error> {
Ok(try_none!(try_none!(self.path.file_stem(), &self.path).to_str(), &self.path).to_string())
}
}
fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) {
@ -353,7 +356,7 @@ enum Setting {
js_data_name: &'static str,
description: &'static str,
default_value: &'static str,
options: Vec<(String, String)>,
options: Vec<String>,
},
}
@ -393,10 +396,9 @@ impl Setting {
options
.iter()
.map(|opt| format!(
"<option value=\"{}\" {}>{}</option>",
opt.0,
if opt.0 == default_value { "selected" } else { "" },
opt.1,
"<option value=\"{name}\" {}>{name}</option>",
if opt == default_value { "selected" } else { "" },
name = opt,
))
.collect::<String>(),
root_path,
@ -421,18 +423,7 @@ impl<T: Into<Setting>> From<(&'static str, Vec<T>)> for Setting {
}
}
fn settings(root_path: &str, suffix: &str, themes: &[StylePath]) -> Result<String, Error> {
let theme_names: Vec<(String, String)> = themes
.iter()
.map(|entry| {
let theme =
try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path)
.to_string();
Ok((theme.clone(), theme))
})
.collect::<Result<_, Error>>()?;
fn settings(root_path: &str, suffix: &str, theme_names: Vec<String>) -> Result<String, Error> {
// (id, explanation, default value)
let settings: &[Setting] = &[
(
@ -469,10 +460,11 @@ fn settings(root_path: &str, suffix: &str, themes: &[StylePath]) -> Result<Strin
<span class=\"in-band\">Rustdoc settings</span>\
</h1>\
<div class=\"settings\">{}</div>\
<script src=\"{}settings{}.js\"></script>",
<link rel=\"stylesheet\" href=\"{root_path}settings{suffix}.css\">\
<script src=\"{root_path}settings{suffix}.js\"></script>",
settings.iter().map(|s| s.display(root_path, suffix)).collect::<String>(),
root_path,
suffix
root_path = root_path,
suffix = suffix
))
}

View File

@ -181,42 +181,34 @@ pub(super) fn write_shared(
cx.write_shared(SharedResource::InvocationSpecific { basename: p }, content, &options.emit)
};
fn add_background_image_to_css(
cx: &Context<'_>,
css: &mut String,
rule: &str,
file: &'static str,
) {
css.push_str(&format!(
"{} {{ background-image: url({}); }}",
rule,
SharedResource::ToolchainSpecific { basename: file }
// Given "foo.svg", return e.g. "url(\"foo1.58.0.svg\")"
fn ver_url(cx: &Context<'_>, basename: &'static str) -> String {
format!(
"url(\"{}\")",
SharedResource::ToolchainSpecific { basename }
.path(cx)
.file_name()
.unwrap()
.to_str()
.unwrap()
))
)
}
// Add all the static files. These may already exist, but we just
// overwrite them anyway to make sure that they're fresh and up-to-date.
let mut rustdoc_css = static_files::RUSTDOC_CSS.to_owned();
add_background_image_to_css(
// We use the AUTOREPLACE mechanism to inject into our static JS and CSS certain
// values that are only known at doc build time. Since this mechanism is somewhat
// surprising when reading the code, please limit it to rustdoc.css.
write_minify(
"rustdoc.css",
static_files::RUSTDOC_CSS
.replace(
"/* AUTOREPLACE: */url(\"toggle-minus.svg\")",
&ver_url(cx, "toggle-minus.svg"),
)
.replace("/* AUTOREPLACE: */url(\"toggle-plus.svg\")", &ver_url(cx, "toggle-plus.svg"))
.replace("/* AUTOREPLACE: */url(\"down-arrow.svg\")", &ver_url(cx, "down-arrow.svg")),
cx,
&mut rustdoc_css,
"details.undocumented[open] > summary::before, \
details.rustdoc-toggle[open] > summary::before, \
details.rustdoc-toggle[open] > summary.hideme::before",
"toggle-minus.svg",
);
add_background_image_to_css(
cx,
&mut rustdoc_css,
"details.undocumented > summary::before, details.rustdoc-toggle > summary::before",
"toggle-plus.svg",
);
write_minify("rustdoc.css", rustdoc_css, cx, options)?;
options,
)?;
// Add all the static files. These may already exist, but we just
// overwrite them anyway to make sure that they're fresh and up-to-date.
@ -228,12 +220,12 @@ pub(super) fn write_shared(
let mut themes: FxHashSet<String> = FxHashSet::default();
for entry in &cx.shared.style_files {
let theme = try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path);
let theme = entry.basename()?;
let extension =
try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path);
// Handle the official themes
match theme {
match theme.as_str() {
"light" => write_minify("light.css", static_files::themes::LIGHT, cx, options)?,
"dark" => write_minify("dark.css", static_files::themes::DARK, cx, options)?,
"ayu" => write_minify("ayu.css", static_files::themes::AYU, cx, options)?,
@ -265,26 +257,7 @@ pub(super) fn write_shared(
let mut themes: Vec<&String> = themes.iter().collect();
themes.sort();
// FIXME: this should probably not be a toolchain file since it depends on `--theme`.
// But it seems a shame to copy it over and over when it's almost always the same.
// Maybe we can change the representation to move this out of main.js?
write_minify(
"main.js",
static_files::MAIN_JS
.replace(
"/* INSERT THEMES HERE */",
&format!(" = {}", serde_json::to_string(&themes).unwrap()),
)
.replace(
"/* INSERT RUSTDOC_VERSION HERE */",
&format!(
"rustdoc {}",
rustc_interface::util::version_str().unwrap_or("unknown version")
),
),
cx,
options,
)?;
write_minify("main.js", static_files::MAIN_JS, cx, options)?;
write_minify("search.js", static_files::SEARCH_JS, cx, options)?;
write_minify("settings.js", static_files::SETTINGS_JS, cx, options)?;
@ -292,18 +265,7 @@ pub(super) fn write_shared(
write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT, cx, options)?;
}
{
write_minify(
"storage.js",
format!(
"var resourcesSuffix = \"{}\";{}",
cx.shared.resource_suffix,
static_files::STORAGE_JS
),
cx,
options,
)?;
}
write_minify("storage.js", static_files::STORAGE_JS, cx, options)?;
if cx.shared.layout.scrape_examples_extension {
cx.write_minify(

View File

@ -828,6 +828,7 @@ h2.small-section-header > .anchor {
background-color: transparent;
background-size: 20px;
background-position: calc(100% - 1px) 56%;
background-image: /* AUTOREPLACE: */url("down-arrow.svg");
}
.search-container > .top-button {
position: absolute;
@ -1610,6 +1611,16 @@ details.rustdoc-toggle[open] > summary.hideme > span {
display: none;
}
details.undocumented[open] > summary::before,
details.rustdoc-toggle[open] > summary::before,
details.rustdoc-toggle[open] > summary.hideme::before {
background-image: /* AUTOREPLACE: */url("toggle-minus.svg");
}
details.undocumented > summary::before, details.rustdoc-toggle > summary::before {
background-image: /* AUTOREPLACE: */url("toggle-plus.svg");
}
details.rustdoc-toggle[open] > summary::before,
details.rustdoc-toggle[open] > summary.hideme::before {
width: 17px;

View File

@ -37,14 +37,29 @@ if (!DOMTokenList.prototype.remove) {
};
}
(function () {
var rustdocVars = document.getElementById("rustdoc-vars");
if (rustdocVars) {
window.rootPath = rustdocVars.attributes["data-root-path"].value;
window.currentCrate = rustdocVars.attributes["data-current-crate"].value;
window.searchJS = rustdocVars.attributes["data-search-js"].value;
window.searchIndexJS = rustdocVars.attributes["data-search-index-js"].value;
// Get a value from the rustdoc-vars div, which is used to convey data from
// Rust to the JS. If there is no such element, return null.
function getVar(name) {
var el = document.getElementById("rustdoc-vars");
if (el) {
return el.attributes["data-" + name].value;
} else {
return null;
}
}
// Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL
// for a resource under the root-path, with the resource-suffix.
function resourcePath(basename, extension) {
return getVar("root-path") + basename + getVar("resource-suffix") + extension;
}
(function () {
window.rootPath = getVar("root-path");
window.currentCrate = getVar("current-crate");
window.searchJS = resourcePath("search", ".js");
window.searchIndexJS = resourcePath("search-index", ".js");
var sidebarVars = document.getElementById("sidebar-vars");
if (sidebarVars) {
window.sidebarCurrent = {
@ -115,7 +130,7 @@ function hideThemeButtonState() {
(function () {
var themeChoices = getThemesElement();
var themePicker = getThemePickerElement();
var availableThemes/* INSERT THEMES HERE */;
var availableThemes = getVar("themes").split(",");
function switchThemeButtonState() {
if (themeChoices.style.display === "block") {
@ -980,7 +995,7 @@ function hideThemeButtonState() {
var rustdoc_version = document.createElement("span");
rustdoc_version.className = "bottom";
var rustdoc_version_code = document.createElement("code");
rustdoc_version_code.innerText = "/* INSERT RUSTDOC_VERSION HERE */";
rustdoc_version_code.innerText = "rustdoc " + getVar("rustdoc-version");
rustdoc_version.appendChild(rustdoc_version_code);
container.appendChild(rustdoc_version);

View File

@ -1,5 +1,3 @@
// From rust:
/* global resourcesSuffix */
var darkThemes = ["dark", "ayu"];
window.currentTheme = document.getElementById("themeStyle");
window.mainTheme = document.getElementById("mainThemeStyle");
@ -107,9 +105,8 @@ function getCurrentValue(name) {
}
function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) {
var fullBasicCss = "rustdoc" + resourcesSuffix + ".css";
var fullNewTheme = newTheme + resourcesSuffix + ".css";
var newHref = mainStyleElem.href.replace(fullBasicCss, fullNewTheme);
var newHref = mainStyleElem.href.replace(
/\/rustdoc([^/]*)\.css/, "/" + newTheme + "$1" + ".css");
// If this new value comes from a system setting or from the previously
// saved theme, no need to save it.

View File

@ -12,7 +12,16 @@
<link rel="stylesheet" type="text/css" {# -#}
href="{{static_root_path | safe}}rustdoc{{page.resource_suffix}}.css" {# -#}
id="mainThemeStyle"> {#- -#}
{{- style_files | safe -}}
{%- for theme in themes -%}
<link rel="stylesheet" type="text/css" {# -#}
href="{{static_root_path | safe}}{{theme}}{{page.resource_suffix}}.css" {#- -#}
{%- if theme == "light" -%}
id="themeStyle"
{%- else -%}
disabled
{%- endif -%}
>
{%- endfor -%}
<script id="default-settings" {# -#}
{% for k, v in layout.default_settings %}
data-{{k}}="{{v}}"
@ -49,11 +58,6 @@
href="{{static_root_path | safe}}favicon{{page.resource_suffix}}.svg"> {#- -#}
{%- endif -%}
{{- layout.external_html.in_header | safe -}}
<style type="text/css"> {#- -#}
#crate-search{ {#- -#}
background-image:url("{{static_root_path | safe}}down-arrow{{page.resource_suffix}}.svg"); {#- -#}
} {#- -#}
</style> {#- -#}
</head> {#- -#}
<body class="rustdoc {{page.css_class}}"> {#- -#}
<!--[if lte IE 11]> {#- -#}
@ -114,8 +118,10 @@
<div id="rustdoc-vars" {# -#}
data-root-path="{{page.root_path | safe}}" {# -#}
data-current-crate="{{layout.krate}}" {# -#}
data-search-index-js="{{page.root_path | safe}}search-index{{page.resource_suffix}}.js" {# -#}
data-search-js="{{static_root_path | safe}}search{{page.resource_suffix}}.js"> {#- -#}
data-themes="{{themes | join(sep=",") }}" {# -#}
data-resource-suffix="{{page.resource_suffix}}" {# -#}
data-rustdoc-version="{{rustdoc_version}}" {# -#}
> {#- -#}
</div>
</body> {#- -#}
</html> {#- -#}

View File

@ -3,7 +3,7 @@
// @has static_root_path/struct.SomeStruct.html
// @matches - '"/cache/main\.js"'
// @!matches - '"\.\./main\.js"'
// @matches - '"\.\./search-index\.js"'
// @matches - 'data-root-path="\.\./"'
// @!matches - '"/cache/search-index\.js"'
pub struct SomeStruct;