Load rustdoc's JS search index on-demand.
Instead of being loaded on every page, the JS search index is now loaded when either (a) there is a `?search=` param, or (b) the search input is focused. This saves both CPU and bandwidth. As of Feb 2021, https://doc.rust-lang.org/search-index1.50.0.js is 273,838 bytes gzipped or 2,544,939 bytes uncompressed. Evaluating it takes 445 ms of CPU time in Chrome 88 on a i7-10710U CPU (out of a total ~2,100 ms page reload). Generate separate JS file with crate names. This is much smaller than the full search index, and is used in the "hot path" to draw the page. In particular it's used to crate the dropdown for the search bar, and to append a list of crates to the sidebar (on some pages). Skip early search that can bypass 500ms timeout. This was occurring when someone had typed some text during the load of search-index.js. Their query was usually not ready to execute, and the search itself is fairly expensive, delaying the overall load, which delayed the input / keyup events, which delayed eventually executing the query.
This commit is contained in:
parent
94736c434e
commit
768d5e9509
3 changed files with 69 additions and 51 deletions
|
@ -58,6 +58,7 @@ crate fn render<T: Print, S: Print>(
|
|||
{style_files}\
|
||||
<script id=\"default-settings\"{default_settings}></script>\
|
||||
<script src=\"{static_root_path}storage{suffix}.js\"></script>\
|
||||
<script src=\"{static_root_path}crates{suffix}.js\"></script>\
|
||||
<noscript><link rel=\"stylesheet\" href=\"{static_root_path}noscript{suffix}.css\"></noscript>\
|
||||
{css_extension}\
|
||||
{favicon}\
|
||||
|
@ -112,10 +113,10 @@ crate fn render<T: Print, S: Print>(
|
|||
<section id=\"search\" class=\"content hidden\"></section>\
|
||||
<section class=\"footer\"></section>\
|
||||
{after_content}\
|
||||
<div id=\"rustdoc-vars\" data-root-path=\"{root_path}\" data-current-crate=\"{krate}\"></div>
|
||||
<div id=\"rustdoc-vars\" data-root-path=\"{root_path}\" data-current-crate=\"{krate}\" \
|
||||
data-search-js=\"{root_path}search-index{suffix}.js\"></div>
|
||||
<script src=\"{static_root_path}main{suffix}.js\"></script>\
|
||||
{extra_scripts}\
|
||||
<script defer src=\"{root_path}search-index{suffix}.js\"></script>\
|
||||
</body>\
|
||||
</html>",
|
||||
css_extension = if layout.css_file_extension.is_some() {
|
||||
|
|
|
@ -1039,10 +1039,12 @@ themePicker.onblur = handleThemeButtonsBlur;
|
|||
cx.shared.fs.write(&dst, v.as_bytes())?;
|
||||
}
|
||||
|
||||
// Update the search index
|
||||
// Update the search index and crate list.
|
||||
let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix));
|
||||
let (mut all_indexes, mut krates) = try_err!(collect_json(&dst, &krate.name.as_str()), &dst);
|
||||
all_indexes.push(search_index);
|
||||
krates.push(krate.name.to_string());
|
||||
krates.sort();
|
||||
|
||||
// Sort the indexes by crate so the file will be generated identically even
|
||||
// with rustdoc running in parallel.
|
||||
|
@ -1050,11 +1052,15 @@ themePicker.onblur = handleThemeButtonsBlur;
|
|||
{
|
||||
let mut v = String::from("var searchIndex = JSON.parse('{\\\n");
|
||||
v.push_str(&all_indexes.join(",\\\n"));
|
||||
// "addSearchOptions" has to be called first so the crate filtering can be set before the
|
||||
// search might start (if it's set into the URL for example).
|
||||
v.push_str("\\\n}');\naddSearchOptions(searchIndex);initSearch(searchIndex);");
|
||||
v.push_str("\\\n}');\ninitSearch(searchIndex);");
|
||||
cx.shared.fs.write(&dst, &v)?;
|
||||
}
|
||||
|
||||
let crate_list_dst = cx.dst.join(&format!("crates{}.js", cx.shared.resource_suffix));
|
||||
let crate_list =
|
||||
format!("window.ALL_CRATES = [{}];", krates.iter().map(|k| format!("\"{}\"", k)).join(","));
|
||||
cx.shared.fs.write(&crate_list_dst, &crate_list)?;
|
||||
|
||||
if options.enable_index_page {
|
||||
if let Some(index_page) = options.index_page.clone() {
|
||||
let mut md_opts = options.clone();
|
||||
|
@ -1076,9 +1082,6 @@ themePicker.onblur = handleThemeButtonsBlur;
|
|||
extra_scripts: &[],
|
||||
static_extra_scripts: &[],
|
||||
};
|
||||
krates.push(krate.name.to_string());
|
||||
krates.sort();
|
||||
krates.dedup();
|
||||
|
||||
let content = format!(
|
||||
"<h1 class=\"fqn\">\
|
||||
|
|
|
@ -42,6 +42,7 @@ if (!DOMTokenList.prototype.remove) {
|
|||
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;
|
||||
}
|
||||
var sidebarVars = document.getElementById("sidebar-vars");
|
||||
if (sidebarVars) {
|
||||
|
@ -1922,8 +1923,8 @@ function defocusSearchBar() {
|
|||
return searchWords;
|
||||
}
|
||||
|
||||
function startSearch() {
|
||||
var callback = function() {
|
||||
function registerSearchEvents() {
|
||||
var searchAfter500ms = function() {
|
||||
clearInputTimeout();
|
||||
if (search_input.value.length === 0) {
|
||||
if (browserSupportsHistoryApi()) {
|
||||
|
@ -1935,8 +1936,8 @@ function defocusSearchBar() {
|
|||
searchTimeout = setTimeout(search, 500);
|
||||
}
|
||||
};
|
||||
search_input.onkeyup = callback;
|
||||
search_input.oninput = callback;
|
||||
search_input.onkeyup = searchAfter500ms;
|
||||
search_input.oninput = searchAfter500ms;
|
||||
document.getElementsByClassName("search-form")[0].onsubmit = function(e) {
|
||||
e.preventDefault();
|
||||
clearInputTimeout();
|
||||
|
@ -1999,7 +2000,6 @@ function defocusSearchBar() {
|
|||
}
|
||||
});
|
||||
}
|
||||
search();
|
||||
|
||||
// This is required in firefox to avoid this problem: Navigating to a search result
|
||||
// with the keyboard, hitting enter, and then hitting back would take you back to
|
||||
|
@ -2017,8 +2017,14 @@ function defocusSearchBar() {
|
|||
}
|
||||
|
||||
index = buildIndex(rawSearchIndex);
|
||||
startSearch();
|
||||
registerSearchEvents();
|
||||
// If there's a search term in the URL, execute the search now.
|
||||
if (getQueryStringParams().search) {
|
||||
search();
|
||||
}
|
||||
};
|
||||
|
||||
function addSidebarCrates(crates) {
|
||||
// Draw a convenient sidebar of known crates if we have a listing
|
||||
if (window.rootPath === "../" || window.rootPath === "./") {
|
||||
var sidebar = document.getElementsByClassName("sidebar-elems")[0];
|
||||
|
@ -2029,14 +2035,6 @@ function defocusSearchBar() {
|
|||
var ul = document.createElement("ul");
|
||||
div.appendChild(ul);
|
||||
|
||||
var crates = [];
|
||||
for (var crate in rawSearchIndex) {
|
||||
if (!hasOwnProperty(rawSearchIndex, crate)) {
|
||||
continue;
|
||||
}
|
||||
crates.push(crate);
|
||||
}
|
||||
crates.sort();
|
||||
for (var i = 0; i < crates.length; ++i) {
|
||||
var klass = "crate";
|
||||
if (window.rootPath !== "./" && crates[i] === window.currentCrate) {
|
||||
|
@ -2044,9 +2042,6 @@ function defocusSearchBar() {
|
|||
}
|
||||
var link = document.createElement("a");
|
||||
link.href = window.rootPath + crates[i] + "/index.html";
|
||||
// The summary in the search index has HTML, so we need to
|
||||
// dynamically render it as plaintext.
|
||||
link.title = convertHTMLToPlaintext(rawSearchIndex[crates[i]].doc);
|
||||
link.className = klass;
|
||||
link.textContent = crates[i];
|
||||
|
||||
|
@ -2057,7 +2052,7 @@ function defocusSearchBar() {
|
|||
sidebar.appendChild(div);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert HTML to plaintext:
|
||||
|
@ -2862,37 +2857,18 @@ function defocusSearchBar() {
|
|||
}
|
||||
}
|
||||
|
||||
window.addSearchOptions = function(crates) {
|
||||
function addSearchOptions(crates) {
|
||||
var elem = document.getElementById("crate-search");
|
||||
|
||||
if (!elem) {
|
||||
enableSearchInput();
|
||||
return;
|
||||
}
|
||||
var crates_text = [];
|
||||
if (Object.keys(crates).length > 1) {
|
||||
for (var crate in crates) {
|
||||
if (hasOwnProperty(crates, crate)) {
|
||||
crates_text.push(crate);
|
||||
}
|
||||
}
|
||||
}
|
||||
crates_text.sort(function(a, b) {
|
||||
var lower_a = a.toLowerCase();
|
||||
var lower_b = b.toLowerCase();
|
||||
|
||||
if (lower_a < lower_b) {
|
||||
return -1;
|
||||
} else if (lower_a > lower_b) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
var savedCrate = getSettingValue("saved-filter-crate");
|
||||
for (var i = 0, len = crates_text.length; i < len; ++i) {
|
||||
for (var i = 0, len = crates.length; i < len; ++i) {
|
||||
var option = document.createElement("option");
|
||||
option.value = crates_text[i];
|
||||
option.innerText = crates_text[i];
|
||||
option.value = crates[i];
|
||||
option.innerText = crates[i];
|
||||
elem.appendChild(option);
|
||||
// Set the crate filter from saved storage, if the current page has the saved crate
|
||||
// filter.
|
||||
|
@ -2900,7 +2876,7 @@ function defocusSearchBar() {
|
|||
// If not, ignore the crate filter -- we want to support filtering for crates on sites
|
||||
// like doc.rust-lang.org where the crates may differ from page to page while on the
|
||||
// same domain.
|
||||
if (crates_text[i] === savedCrate) {
|
||||
if (crates[i] === savedCrate) {
|
||||
elem.value = savedCrate;
|
||||
}
|
||||
}
|
||||
|
@ -2969,6 +2945,44 @@ function defocusSearchBar() {
|
|||
buildHelperPopup = function() {};
|
||||
}
|
||||
|
||||
function loadScript(url) {
|
||||
var script = document.createElement('script');
|
||||
script.src = url;
|
||||
document.head.append(script);
|
||||
}
|
||||
|
||||
function setupSearchLoader() {
|
||||
var searchLoaded = false;
|
||||
function loadSearch() {
|
||||
if (!searchLoaded) {
|
||||
searchLoaded = true;
|
||||
loadScript(window.searchJS);
|
||||
}
|
||||
}
|
||||
|
||||
// `crates{version}.js` should always be loaded before this script, so we can use it safely.
|
||||
addSearchOptions(window.ALL_CRATES);
|
||||
addSidebarCrates(window.ALL_CRATES);
|
||||
|
||||
search_input.addEventListener("focus", function() {
|
||||
search_input.origPlaceholder = search_input.placeholder;
|
||||
search_input.placeholder = "Type your search here.";
|
||||
loadSearch();
|
||||
});
|
||||
search_input.addEventListener("blur", function() {
|
||||
search_input.placeholder = search_input.origPlaceholder;
|
||||
});
|
||||
enableSearchInput();
|
||||
|
||||
var crateSearchDropDown = document.getElementById("crate-search");
|
||||
crateSearchDropDown.addEventListener("focus", loadSearch);
|
||||
var params = getQueryStringParams();
|
||||
if (params.search !== undefined) {
|
||||
loadSearch();
|
||||
}
|
||||
}
|
||||
|
||||
onHashChange(null);
|
||||
window.onhashchange = onHashChange;
|
||||
setupSearchLoader();
|
||||
}());
|
||||
|
|
Loading…
Add table
Reference in a new issue