about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYuki Okushi <huyuumi.dev@gmail.com>2021-03-04 20:01:03 +0900
committerGitHub <noreply@github.com>2021-03-04 20:01:03 +0900
commit36b7bef1cb0fb9d989f6c6e5ae9f001dea1b6a14 (patch)
treeaa019f79b4c875c607bdd95cb38b5d3929ddb398
parentf898aa3f5b4b7b85b71c4bb5447d0b7cef6deeaf (diff)
parent768d5e950953738a54480e530341964838d29da2 (diff)
downloadrust-36b7bef1cb0fb9d989f6c6e5ae9f001dea1b6a14.tar.gz
rust-36b7bef1cb0fb9d989f6c6e5ae9f001dea1b6a14.zip
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
-rw-r--r--src/librustdoc/html/layout.rs5
-rw-r--r--src/librustdoc/html/render/mod.rs17
-rw-r--r--src/librustdoc/html/static/main.js98
3 files changed, 69 insertions, 51 deletions
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 73c106f5ae0..86ec18775ea 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -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() {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 394c57c7214..fbeeb2a6fa2 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1061,10 +1061,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.
@@ -1072,11 +1074,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();
@@ -1098,9 +1104,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\">\
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index 76449a171d7..89b1362b32b 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -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();
 }());