Skip to content

Commit 36b7bef

Browse files
authored
Rollup merge of #82310 - jsha:rustdoc-search-onfocus, r=GuillaumeGomez
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). Tested on Firefox and Chrome. New: https://jacob.hoffman-andrews.com/rust/search-on-demand/std/primitive.slice.html https://jacob.hoffman-andrews.com/rust/search-on-demand/std/primitive.slice.html?search=fn Old: https://jacob.hoffman-andrews.com/rust/search-on-load/std/primitive.slice.html https://jacob.hoffman-andrews.com/rust/search-on-load/std/primitive.slice.html?search=fn
2 parents f898aa3 + 768d5e9 commit 36b7bef

File tree

3 files changed

+69
-51
lines changed

3 files changed

+69
-51
lines changed

src/librustdoc/html/layout.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ crate fn render<T: Print, S: Print>(
5858
{style_files}\
5959
<script id=\"default-settings\"{default_settings}></script>\
6060
<script src=\"{static_root_path}storage{suffix}.js\"></script>\
61+
<script src=\"{static_root_path}crates{suffix}.js\"></script>\
6162
<noscript><link rel=\"stylesheet\" href=\"{static_root_path}noscript{suffix}.css\"></noscript>\
6263
{css_extension}\
6364
{favicon}\
@@ -112,10 +113,10 @@ crate fn render<T: Print, S: Print>(
112113
<section id=\"search\" class=\"content hidden\"></section>\
113114
<section class=\"footer\"></section>\
114115
{after_content}\
115-
<div id=\"rustdoc-vars\" data-root-path=\"{root_path}\" data-current-crate=\"{krate}\"></div>
116+
<div id=\"rustdoc-vars\" data-root-path=\"{root_path}\" data-current-crate=\"{krate}\" \
117+
data-search-js=\"{root_path}search-index{suffix}.js\"></div>
116118
<script src=\"{static_root_path}main{suffix}.js\"></script>\
117119
{extra_scripts}\
118-
<script defer src=\"{root_path}search-index{suffix}.js\"></script>\
119120
</body>\
120121
</html>",
121122
css_extension = if layout.css_file_extension.is_some() {

src/librustdoc/html/render/mod.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -1061,22 +1061,28 @@ themePicker.onblur = handleThemeButtonsBlur;
10611061
cx.shared.fs.write(&dst, v.as_bytes())?;
10621062
}
10631063

1064-
// Update the search index
1064+
// Update the search index and crate list.
10651065
let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix));
10661066
let (mut all_indexes, mut krates) = try_err!(collect_json(&dst, &krate.name.as_str()), &dst);
10671067
all_indexes.push(search_index);
1068+
krates.push(krate.name.to_string());
1069+
krates.sort();
10681070

10691071
// Sort the indexes by crate so the file will be generated identically even
10701072
// with rustdoc running in parallel.
10711073
all_indexes.sort();
10721074
{
10731075
let mut v = String::from("var searchIndex = JSON.parse('{\\\n");
10741076
v.push_str(&all_indexes.join(",\\\n"));
1075-
// "addSearchOptions" has to be called first so the crate filtering can be set before the
1076-
// search might start (if it's set into the URL for example).
1077-
v.push_str("\\\n}');\naddSearchOptions(searchIndex);initSearch(searchIndex);");
1077+
v.push_str("\\\n}');\ninitSearch(searchIndex);");
10781078
cx.shared.fs.write(&dst, &v)?;
10791079
}
1080+
1081+
let crate_list_dst = cx.dst.join(&format!("crates{}.js", cx.shared.resource_suffix));
1082+
let crate_list =
1083+
format!("window.ALL_CRATES = [{}];", krates.iter().map(|k| format!("\"{}\"", k)).join(","));
1084+
cx.shared.fs.write(&crate_list_dst, &crate_list)?;
1085+
10801086
if options.enable_index_page {
10811087
if let Some(index_page) = options.index_page.clone() {
10821088
let mut md_opts = options.clone();
@@ -1098,9 +1104,6 @@ themePicker.onblur = handleThemeButtonsBlur;
10981104
extra_scripts: &[],
10991105
static_extra_scripts: &[],
11001106
};
1101-
krates.push(krate.name.to_string());
1102-
krates.sort();
1103-
krates.dedup();
11041107

11051108
let content = format!(
11061109
"<h1 class=\"fqn\">\

src/librustdoc/html/static/main.js

+56-42
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ if (!DOMTokenList.prototype.remove) {
4242
if (rustdocVars) {
4343
window.rootPath = rustdocVars.attributes["data-root-path"].value;
4444
window.currentCrate = rustdocVars.attributes["data-current-crate"].value;
45+
window.searchJS = rustdocVars.attributes["data-search-js"].value;
4546
}
4647
var sidebarVars = document.getElementById("sidebar-vars");
4748
if (sidebarVars) {
@@ -1922,8 +1923,8 @@ function defocusSearchBar() {
19221923
return searchWords;
19231924
}
19241925

1925-
function startSearch() {
1926-
var callback = function() {
1926+
function registerSearchEvents() {
1927+
var searchAfter500ms = function() {
19271928
clearInputTimeout();
19281929
if (search_input.value.length === 0) {
19291930
if (browserSupportsHistoryApi()) {
@@ -1935,8 +1936,8 @@ function defocusSearchBar() {
19351936
searchTimeout = setTimeout(search, 500);
19361937
}
19371938
};
1938-
search_input.onkeyup = callback;
1939-
search_input.oninput = callback;
1939+
search_input.onkeyup = searchAfter500ms;
1940+
search_input.oninput = searchAfter500ms;
19401941
document.getElementsByClassName("search-form")[0].onsubmit = function(e) {
19411942
e.preventDefault();
19421943
clearInputTimeout();
@@ -1999,7 +2000,6 @@ function defocusSearchBar() {
19992000
}
20002001
});
20012002
}
2002-
search();
20032003

20042004
// This is required in firefox to avoid this problem: Navigating to a search result
20052005
// with the keyboard, hitting enter, and then hitting back would take you back to
@@ -2017,8 +2017,14 @@ function defocusSearchBar() {
20172017
}
20182018

20192019
index = buildIndex(rawSearchIndex);
2020-
startSearch();
2020+
registerSearchEvents();
2021+
// If there's a search term in the URL, execute the search now.
2022+
if (getQueryStringParams().search) {
2023+
search();
2024+
}
2025+
};
20212026

2027+
function addSidebarCrates(crates) {
20222028
// Draw a convenient sidebar of known crates if we have a listing
20232029
if (window.rootPath === "../" || window.rootPath === "./") {
20242030
var sidebar = document.getElementsByClassName("sidebar-elems")[0];
@@ -2029,24 +2035,13 @@ function defocusSearchBar() {
20292035
var ul = document.createElement("ul");
20302036
div.appendChild(ul);
20312037

2032-
var crates = [];
2033-
for (var crate in rawSearchIndex) {
2034-
if (!hasOwnProperty(rawSearchIndex, crate)) {
2035-
continue;
2036-
}
2037-
crates.push(crate);
2038-
}
2039-
crates.sort();
20402038
for (var i = 0; i < crates.length; ++i) {
20412039
var klass = "crate";
20422040
if (window.rootPath !== "./" && crates[i] === window.currentCrate) {
20432041
klass += " current";
20442042
}
20452043
var link = document.createElement("a");
20462044
link.href = window.rootPath + crates[i] + "/index.html";
2047-
// The summary in the search index has HTML, so we need to
2048-
// dynamically render it as plaintext.
2049-
link.title = convertHTMLToPlaintext(rawSearchIndex[crates[i]].doc);
20502045
link.className = klass;
20512046
link.textContent = crates[i];
20522047

@@ -2057,7 +2052,7 @@ function defocusSearchBar() {
20572052
sidebar.appendChild(div);
20582053
}
20592054
}
2060-
};
2055+
}
20612056

20622057
/**
20632058
* Convert HTML to plaintext:
@@ -2862,45 +2857,26 @@ function defocusSearchBar() {
28622857
}
28632858
}
28642859

2865-
window.addSearchOptions = function(crates) {
2860+
function addSearchOptions(crates) {
28662861
var elem = document.getElementById("crate-search");
28672862

28682863
if (!elem) {
28692864
enableSearchInput();
28702865
return;
28712866
}
2872-
var crates_text = [];
2873-
if (Object.keys(crates).length > 1) {
2874-
for (var crate in crates) {
2875-
if (hasOwnProperty(crates, crate)) {
2876-
crates_text.push(crate);
2877-
}
2878-
}
2879-
}
2880-
crates_text.sort(function(a, b) {
2881-
var lower_a = a.toLowerCase();
2882-
var lower_b = b.toLowerCase();
2883-
2884-
if (lower_a < lower_b) {
2885-
return -1;
2886-
} else if (lower_a > lower_b) {
2887-
return 1;
2888-
}
2889-
return 0;
2890-
});
28912867
var savedCrate = getSettingValue("saved-filter-crate");
2892-
for (var i = 0, len = crates_text.length; i < len; ++i) {
2868+
for (var i = 0, len = crates.length; i < len; ++i) {
28932869
var option = document.createElement("option");
2894-
option.value = crates_text[i];
2895-
option.innerText = crates_text[i];
2870+
option.value = crates[i];
2871+
option.innerText = crates[i];
28962872
elem.appendChild(option);
28972873
// Set the crate filter from saved storage, if the current page has the saved crate
28982874
// filter.
28992875
//
29002876
// If not, ignore the crate filter -- we want to support filtering for crates on sites
29012877
// like doc.rust-lang.org where the crates may differ from page to page while on the
29022878
// same domain.
2903-
if (crates_text[i] === savedCrate) {
2879+
if (crates[i] === savedCrate) {
29042880
elem.value = savedCrate;
29052881
}
29062882
}
@@ -2969,6 +2945,44 @@ function defocusSearchBar() {
29692945
buildHelperPopup = function() {};
29702946
}
29712947

2948+
function loadScript(url) {
2949+
var script = document.createElement('script');
2950+
script.src = url;
2951+
document.head.append(script);
2952+
}
2953+
2954+
function setupSearchLoader() {
2955+
var searchLoaded = false;
2956+
function loadSearch() {
2957+
if (!searchLoaded) {
2958+
searchLoaded = true;
2959+
loadScript(window.searchJS);
2960+
}
2961+
}
2962+
2963+
// `crates{version}.js` should always be loaded before this script, so we can use it safely.
2964+
addSearchOptions(window.ALL_CRATES);
2965+
addSidebarCrates(window.ALL_CRATES);
2966+
2967+
search_input.addEventListener("focus", function() {
2968+
search_input.origPlaceholder = search_input.placeholder;
2969+
search_input.placeholder = "Type your search here.";
2970+
loadSearch();
2971+
});
2972+
search_input.addEventListener("blur", function() {
2973+
search_input.placeholder = search_input.origPlaceholder;
2974+
});
2975+
enableSearchInput();
2976+
2977+
var crateSearchDropDown = document.getElementById("crate-search");
2978+
crateSearchDropDown.addEventListener("focus", loadSearch);
2979+
var params = getQueryStringParams();
2980+
if (params.search !== undefined) {
2981+
loadSearch();
2982+
}
2983+
}
2984+
29722985
onHashChange(null);
29732986
window.onhashchange = onHashChange;
2987+
setupSearchLoader();
29742988
}());

0 commit comments

Comments
 (0)