about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/html/layout.rs3
-rw-r--r--src/librustdoc/html/render/write_shared.rs3
-rw-r--r--src/librustdoc/html/static/main.js1842
-rw-r--r--src/librustdoc/html/static/search.js1512
-rw-r--r--src/librustdoc/html/static_files.rs3
-rw-r--r--src/tools/rustdoc-js/tester.js12
6 files changed, 1682 insertions, 1693 deletions
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 68d70f27c8c..dc67a63d333 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -113,7 +113,8 @@ crate fn render<T: Print, S: Print>(
     <section class=\"footer\"></section>\
     {after_content}\
     <div id=\"rustdoc-vars\" data-root-path=\"{root_path}\" data-current-crate=\"{krate}\" \
-       data-search-js=\"{root_path}search-index{suffix}.js\"></div>
+       data-search-index-js=\"{root_path}search-index{suffix}.js\" \
+       data-search-js=\"{static_root_path}search{suffix}.js\"></div>
     <script src=\"{static_root_path}main{suffix}.js\"></script>\
     {extra_scripts}\
 </body>\
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 90e56d00a11..78bcd40af75 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -222,6 +222,7 @@ pub(super) fn write_shared(
             &format!(" = {}", serde_json::to_string(&themes).unwrap()),
         ),
     )?;
+    write_minify("search.js", static_files::SEARCH_JS)?;
     write_minify("settings.js", static_files::SETTINGS_JS)?;
     if cx.shared.include_sources {
         write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT)?;
@@ -409,7 +410,7 @@ pub(super) fn write_shared(
     write_crate("search-index.js", &|| {
         let mut v = String::from("var searchIndex = JSON.parse('{\\\n");
         v.push_str(&all_indexes.join(",\\\n"));
-        v.push_str("\\\n}');\ninitSearch(searchIndex);");
+        v.push_str("\\\n}');\nif (window.initSearch) {window.initSearch(searchIndex)};");
         Ok(v.into_bytes())
     })?;
 
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index d71cc15a457..f017fd846b1 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -1,4 +1,3 @@
-// ignore-tidy-filelength
 // Local js definitions:
 /* global addClass, getSettingValue, hasClass */
 /* global onEach, onEachLazy, hasOwnProperty, removeClass, updateLocalStorage */
@@ -44,6 +43,7 @@ if (!DOMTokenList.prototype.remove) {
         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;
     }
     var sidebarVars = document.getElementById("sidebar-vars");
     if (sidebarVars) {
@@ -77,14 +77,6 @@ function getVirtualKey(ev) {
     return String.fromCharCode(c);
 }
 
-function getSearchInput() {
-    return document.getElementsByClassName("search-input")[0];
-}
-
-function getSearchElement() {
-    return document.getElementById("search");
-}
-
 var THEME_PICKER_ELEMENT_ID = "theme-picker";
 var THEMES_ELEMENT_ID = "theme-choices";
 
@@ -101,16 +93,6 @@ function getNakedUrl() {
     return window.location.href.split("?")[0].split("#")[0];
 }
 
-// Sets the focus on the search bar at the top of the page
-function focusSearchBar() {
-    getSearchInput().focus();
-}
-
-// Removes the focus from the search bar.
-function defocusSearchBar() {
-    getSearchInput().blur();
-}
-
 function showThemeButtonState() {
     var themePicker = getThemePickerElement();
     var themeChoices = getThemesElement();
@@ -173,67 +155,154 @@ function hideThemeButtonState() {
 (function() {
     "use strict";
 
-    // This mapping table should match the discriminants of
-    // `rustdoc::html::item_type::ItemType` type in Rust.
-    var itemTypes = ["mod",
-                     "externcrate",
-                     "import",
-                     "struct",
-                     "enum",
-                     "fn",
-                     "type",
-                     "static",
-                     "trait",
-                     "impl",
-                     "tymethod",
-                     "method",
-                     "structfield",
-                     "variant",
-                     "macro",
-                     "primitive",
-                     "associatedtype",
-                     "constant",
-                     "associatedconstant",
-                     "union",
-                     "foreigntype",
-                     "keyword",
-                     "existential",
-                     "attr",
-                     "derive",
-                     "traitalias"];
+    window.searchState = {
+      loadingText: "Loading search results...",
+      input: document.getElementsByClassName("search-input")[0],
+      outputElement: function() {
+        return document.getElementById("search");
+      },
+      title: null,
+      titleBeforeSearch: document.title,
+      timeout: null,
+      // On the search screen, so you remain on the last tab you opened.
+      //
+      // 0 for "In Names"
+      // 1 for "In Parameters"
+      // 2 for "In Return Types"
+      currentTab: 0,
+      mouseMovedAfterSearch: true,
+      clearInputTimeout: function() {
+        if (searchState.timeout !== null) {
+            clearTimeout(searchState.timeout);
+            searchState.timeout = null;
+        }
+      },
+      // Sets the focus on the search bar at the top of the page
+      focus: function() {
+          searchState.input.focus();
+      },
+      // Removes the focus from the search bar.
+      defocus: function() {
+          searchState.input.blur();
+      },
+      showResults: function(search) {
+        if (search === null || typeof search === 'undefined') {
+            search = searchState.outputElement();
+        }
+        addClass(main, "hidden");
+        removeClass(search, "hidden");
+        searchState.mouseMovedAfterSearch = false;
+        document.title = searchState.title;
+      },
+      hideResults: function(search) {
+        if (search === null || typeof search === 'undefined') {
+            search = searchState.outputElement();
+        }
+        addClass(search, "hidden");
+        removeClass(main, "hidden");
+        document.title = searchState.titleBeforeSearch;
+        // We also remove the query parameter from the URL.
+        if (searchState.browserSupportsHistoryApi()) {
+            history.replaceState("", window.currentCrate + " - Rust",
+                getNakedUrl() + window.location.hash);
+        }
+      },
+      getQueryStringParams: function() {
+        var params = {};
+        window.location.search.substring(1).split("&").
+            map(function(s) {
+                var pair = s.split("=");
+                params[decodeURIComponent(pair[0])] =
+                    typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
+            });
+        return params;
+      },
+      putBackSearch: function(search_input) {
+        var search = searchState.outputElement();
+        if (search_input.value !== "" && hasClass(search, "hidden")) {
+            searchState.showResults(search);
+            if (searchState.browserSupportsHistoryApi()) {
+                var extra = "?search=" + encodeURIComponent(search_input.value);
+                history.replaceState(search_input.value, "",
+                    getNakedUrl() + extra + window.location.hash);
+            }
+            document.title = searchState.title;
+        }
+      },
+      browserSupportsHistoryApi: function() {
+          return window.history && typeof window.history.pushState === "function";
+      },
+      setup: function() {
+        var search_input = searchState.input;
+        if (!searchState.input) {
+            return;
+        }
+        function loadScript(url) {
+            var script = document.createElement('script');
+            script.src = url;
+            document.head.append(script);
+        }
 
-    var disableShortcuts = getSettingValue("disable-shortcuts") === "true";
-    var search_input = getSearchInput();
-    var searchTimeout = null;
-    var toggleAllDocsId = "toggle-all-docs";
+        var searchLoaded = false;
+        function loadSearch() {
+            if (!searchLoaded) {
+                searchLoaded = true;
+                loadScript(window.searchJS);
+                loadScript(window.searchIndexJS);
+            }
+        }
 
-    // On the search screen, so you remain on the last tab you opened.
-    //
-    // 0 for "In Names"
-    // 1 for "In Parameters"
-    // 2 for "In Return Types"
-    var currentTab = 0;
+        search_input.addEventListener("focus", function() {
+            searchState.putBackSearch(this);
+            search_input.origPlaceholder = searchState.input.placeholder;
+            search_input.placeholder = "Type your search here.";
+            loadSearch();
+        });
+        search_input.addEventListener("blur", function() {
+            search_input.placeholder = searchState.input.origPlaceholder;
+        });
 
-    var mouseMovedAfterSearch = true;
+        document.addEventListener("mousemove", function() {
+          searchState.mouseMovedAfterSearch = true;
+        });
 
-    var titleBeforeSearch = document.title;
-    var searchTitle = null;
+        search_input.removeAttribute('disabled');
 
-    function removeEmptyStringsFromArray(x) {
-        for (var i = 0, len = x.length; i < len; ++i) {
-            if (x[i] === "") {
-                x.splice(i, 1);
-                i -= 1;
-            }
+        // `crates{version}.js` should always be loaded before this script, so we can use it safely.
+        searchState.addCrateDropdown(window.ALL_CRATES);
+        var params = searchState.getQueryStringParams();
+        if (params.search !== undefined) {
+            var search = searchState.outputElement();
+            search.innerHTML = "<h3 style=\"text-align: center;\">" +
+               searchState.loadingText + "</h3>";
+            searchState.showResults(search);
+            loadSearch();
         }
-    }
+      },
+      addCrateDropdown: function(crates) {
+        var elem = document.getElementById("crate-search");
 
-    function clearInputTimeout() {
-        if (searchTimeout !== null) {
-            clearTimeout(searchTimeout);
-            searchTimeout = null;
+        if (!elem) {
+            return;
         }
-    }
+        var savedCrate = getSettingValue("saved-filter-crate");
+        for (var i = 0, len = crates.length; i < len; ++i) {
+            var option = document.createElement("option");
+            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.
+            //
+            // 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[i] === savedCrate) {
+                elem.value = savedCrate;
+            }
+        }
+      },
+    };
 
     function getPageId() {
         if (window.location.hash) {
@@ -276,65 +345,23 @@ function hideThemeButtonState() {
         document.getElementsByTagName("body")[0].style.marginTop = "";
     }
 
-    function showSearchResults(search) {
-        if (search === null || typeof search === 'undefined') {
-            search = getSearchElement();
-        }
-        addClass(main, "hidden");
-        removeClass(search, "hidden");
-        mouseMovedAfterSearch = false;
-        document.title = searchTitle;
-    }
-
-    function hideSearchResults(search) {
-        if (search === null || typeof search === 'undefined') {
-            search = getSearchElement();
-        }
-        addClass(search, "hidden");
-        removeClass(main, "hidden");
-        document.title = titleBeforeSearch;
-        // We also remove the query parameter from the URL.
-        if (browserSupportsHistoryApi()) {
-            history.replaceState("", window.currentCrate + " - Rust",
-                getNakedUrl() + window.location.hash);
-        }
-    }
-
-    // used for special search precedence
-    var TY_PRIMITIVE = itemTypes.indexOf("primitive");
-    var TY_KEYWORD = itemTypes.indexOf("keyword");
-
-    function getQueryStringParams() {
-        var params = {};
-        window.location.search.substring(1).split("&").
-            map(function(s) {
-                var pair = s.split("=");
-                params[decodeURIComponent(pair[0])] =
-                    typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
-            });
-        return params;
-    }
-
-    function browserSupportsHistoryApi() {
-        return window.history && typeof window.history.pushState === "function";
-    }
-
     function isHidden(elem) {
         return elem.offsetHeight === 0;
     }
 
+    var toggleAllDocsId = "toggle-all-docs";
     var main = document.getElementById("main");
     var savedHash = "";
 
     function handleHashes(ev) {
         var elem;
-        var search = getSearchElement();
+        var search = searchState.outputElement();
         if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) {
             // This block occurs when clicking on an element in the navbar while
             // in a search.
-            hideSearchResults(search);
+            searchState.hideResults(search);
             var hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
-            if (browserSupportsHistoryApi()) {
+            if (searchState.browserSupportsHistoryApi()) {
                 // `window.location.search`` contains all the query parameters, not just `search`.
                 history.replaceState(hash, "",
                     getNakedUrl() + window.location.search + "#" + hash);
@@ -475,18 +502,19 @@ function hideThemeButtonState() {
 
     function handleEscape(ev) {
         var help = getHelpElement(false);
-        var search = getSearchElement();
+        var search = searchState.outputElement();
         if (hasClass(help, "hidden") === false) {
             displayHelp(false, ev, help);
         } else if (hasClass(search, "hidden") === false) {
-            clearInputTimeout();
+            searchState.clearInputTimeout();
             ev.preventDefault();
-            hideSearchResults(search);
+            searchState.hideResults(search);
         }
-        defocusSearchBar();
+        searchState.defocus();
         hideThemeButtonState();
     }
 
+    var disableShortcuts = getSettingValue("disable-shortcuts") === "true";
     function handleShortcut(ev) {
         // Don't interfere with browser shortcuts
         if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts === true) {
@@ -509,7 +537,7 @@ function hideThemeButtonState() {
             case "S":
                 displayHelp(false, ev);
                 ev.preventDefault();
-                focusSearchBar();
+                searchState.focus();
                 break;
 
             case "+":
@@ -596,15 +624,13 @@ function hideThemeButtonState() {
     document.addEventListener("keypress", handleShortcut);
     document.addEventListener("keydown", handleShortcut);
 
-    document.addEventListener("mousemove", function() { mouseMovedAfterSearch = true; });
-
     var handleSourceHighlight = (function() {
         var prev_line_id = 0;
 
         var set_fragment = function(name) {
             var x = window.scrollX,
                 y = window.scrollY;
-            if (browserSupportsHistoryApi()) {
+            if (searchState.browserSupportsHistoryApi()) {
                 history.replaceState(null, null, "#" + name);
                 highlightSourceLines();
             } else {
@@ -686,1444 +712,6 @@ function hideThemeButtonState() {
         }
     }());
 
-    /**
-     * A function to compute the Levenshtein distance between two strings
-     * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
-     * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
-     * This code is an unmodified version of the code written by Marco de Wit
-     * and was found at http://stackoverflow.com/a/18514751/745719
-     */
-    var levenshtein_row2 = [];
-    function levenshtein(s1, s2) {
-        if (s1 === s2) {
-            return 0;
-        }
-        var s1_len = s1.length, s2_len = s2.length;
-        if (s1_len && s2_len) {
-            var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2;
-            while (i1 < s1_len) {
-                row[i1] = ++i1;
-            }
-            while (i2 < s2_len) {
-                c2 = s2.charCodeAt(i2);
-                a = i2;
-                ++i2;
-                b = i2;
-                for (i1 = 0; i1 < s1_len; ++i1) {
-                    c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
-                    a = row[i1];
-                    b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
-                    row[i1] = b;
-                }
-            }
-            return b;
-        }
-        return s1_len + s2_len;
-    }
-
-    window.initSearch = function(rawSearchIndex) {
-        var MAX_LEV_DISTANCE = 3;
-        var MAX_RESULTS = 200;
-        var GENERICS_DATA = 1;
-        var NAME = 0;
-        var INPUTS_DATA = 0;
-        var OUTPUT_DATA = 1;
-        var NO_TYPE_FILTER = -1;
-        var currentResults, index, searchIndex;
-        var ALIASES = {};
-        var params = getQueryStringParams();
-
-        // Populate search bar with query string search term when provided,
-        // but only if the input bar is empty. This avoid the obnoxious issue
-        // where you start trying to do a search, and the index loads, and
-        // suddenly your search is gone!
-        if (search_input.value === "") {
-            search_input.value = params.search || "";
-        }
-
-        /**
-         * Executes the query and builds an index of results
-         * @param  {[Object]} query      [The user query]
-         * @param  {[type]} searchWords  [The list of search words to query
-         *                                against]
-         * @param  {[type]} filterCrates [Crate to search in if defined]
-         * @return {[type]}              [A search index of results]
-         */
-        function execQuery(query, searchWords, filterCrates) {
-            function itemTypeFromName(typename) {
-                for (var i = 0, len = itemTypes.length; i < len; ++i) {
-                    if (itemTypes[i] === typename) {
-                        return i;
-                    }
-                }
-                return NO_TYPE_FILTER;
-            }
-
-            var valLower = query.query.toLowerCase(),
-                val = valLower,
-                typeFilter = itemTypeFromName(query.type),
-                results = {}, results_in_args = {}, results_returned = {},
-                split = valLower.split("::");
-
-            removeEmptyStringsFromArray(split);
-
-            function transformResults(results, isType) {
-                var out = [];
-                for (var i = 0, len = results.length; i < len; ++i) {
-                    if (results[i].id > -1) {
-                        var obj = searchIndex[results[i].id];
-                        obj.lev = results[i].lev;
-                        if (isType !== true || obj.type) {
-                            var res = buildHrefAndPath(obj);
-                            obj.displayPath = pathSplitter(res[0]);
-                            obj.fullPath = obj.displayPath + obj.name;
-                            // To be sure than it some items aren't considered as duplicate.
-                            obj.fullPath += "|" + obj.ty;
-                            obj.href = res[1];
-                            out.push(obj);
-                            if (out.length >= MAX_RESULTS) {
-                                break;
-                            }
-                        }
-                    }
-                }
-                return out;
-            }
-
-            function sortResults(results, isType) {
-                var ar = [];
-                for (var entry in results) {
-                    if (hasOwnProperty(results, entry)) {
-                        ar.push(results[entry]);
-                    }
-                }
-                results = ar;
-                var i, len, result;
-                for (i = 0, len = results.length; i < len; ++i) {
-                    result = results[i];
-                    result.word = searchWords[result.id];
-                    result.item = searchIndex[result.id] || {};
-                }
-                // if there are no results then return to default and fail
-                if (results.length === 0) {
-                    return [];
-                }
-
-                results.sort(function(aaa, bbb) {
-                    var a, b;
-
-                    // sort by exact match with regard to the last word (mismatch goes later)
-                    a = (aaa.word !== val);
-                    b = (bbb.word !== val);
-                    if (a !== b) { return a - b; }
-
-                    // Sort by non levenshtein results and then levenshtein results by the distance
-                    // (less changes required to match means higher rankings)
-                    a = (aaa.lev);
-                    b = (bbb.lev);
-                    if (a !== b) { return a - b; }
-
-                    // sort by crate (non-current crate goes later)
-                    a = (aaa.item.crate !== window.currentCrate);
-                    b = (bbb.item.crate !== window.currentCrate);
-                    if (a !== b) { return a - b; }
-
-                    // sort by item name length (longer goes later)
-                    a = aaa.word.length;
-                    b = bbb.word.length;
-                    if (a !== b) { return a - b; }
-
-                    // sort by item name (lexicographically larger goes later)
-                    a = aaa.word;
-                    b = bbb.word;
-                    if (a !== b) { return (a > b ? +1 : -1); }
-
-                    // sort by index of keyword in item name (no literal occurrence goes later)
-                    a = (aaa.index < 0);
-                    b = (bbb.index < 0);
-                    if (a !== b) { return a - b; }
-                    // (later literal occurrence, if any, goes later)
-                    a = aaa.index;
-                    b = bbb.index;
-                    if (a !== b) { return a - b; }
-
-                    // special precedence for primitive and keyword pages
-                    if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) ||
-                        (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) {
-                        return -1;
-                    }
-                    if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) ||
-                        (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) {
-                        return 1;
-                    }
-
-                    // sort by description (no description goes later)
-                    a = (aaa.item.desc === "");
-                    b = (bbb.item.desc === "");
-                    if (a !== b) { return a - b; }
-
-                    // sort by type (later occurrence in `itemTypes` goes later)
-                    a = aaa.item.ty;
-                    b = bbb.item.ty;
-                    if (a !== b) { return a - b; }
-
-                    // sort by path (lexicographically larger goes later)
-                    a = aaa.item.path;
-                    b = bbb.item.path;
-                    if (a !== b) { return (a > b ? +1 : -1); }
-
-                    // que sera, sera
-                    return 0;
-                });
-
-                for (i = 0, len = results.length; i < len; ++i) {
-                    var result = results[i];
-
-                    // this validation does not make sense when searching by types
-                    if (result.dontValidate) {
-                        continue;
-                    }
-                    var name = result.item.name.toLowerCase(),
-                        path = result.item.path.toLowerCase(),
-                        parent = result.item.parent;
-
-                    if (isType !== true &&
-                        validateResult(name, path, split, parent) === false)
-                    {
-                        result.id = -1;
-                    }
-                }
-                return transformResults(results);
-            }
-
-            function extractGenerics(val) {
-                val = val.toLowerCase();
-                if (val.indexOf("<") !== -1) {
-                    var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">"));
-                    return {
-                        name: val.substring(0, val.indexOf("<")),
-                        generics: values.split(/\s*,\s*/),
-                    };
-                }
-                return {
-                    name: val,
-                    generics: [],
-                };
-            }
-
-            function getObjectNameFromId(id) {
-                if (typeof id === "number") {
-                    return searchIndex[id].name;
-                }
-                return id;
-            }
-
-            function checkGenerics(obj, val) {
-                // The names match, but we need to be sure that all generics kinda
-                // match as well.
-                var tmp_lev, elem_name;
-                if (val.generics.length > 0) {
-                    if (obj.length > GENERICS_DATA &&
-                          obj[GENERICS_DATA].length >= val.generics.length) {
-                        var elems = Object.create(null);
-                        var elength = object[GENERICS_DATA].length;
-                        for (var x = 0; x < elength; ++x) {
-                            elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
-                        }
-                        var total = 0;
-                        var done = 0;
-                        // We need to find the type that matches the most to remove it in order
-                        // to move forward.
-                        var vlength = val.generics.length;
-                        for (x = 0; x < vlength; ++x) {
-                            var lev = MAX_LEV_DISTANCE + 1;
-                            var firstGeneric = getObjectNameFromId(val.generics[x]);
-                            var match = null;
-                            if (elems[firstGeneric]) {
-                                match = firstGeneric;
-                                lev = 0;
-                            } else {
-                                for (elem_name in elems) {
-                                    tmp_lev = levenshtein(elem_name, firstGeneric);
-                                    if (tmp_lev < lev) {
-                                        lev = tmp_lev;
-                                        match = elem_name;
-                                    }
-                                }
-                            }
-                            if (match !== null) {
-                                elems[match] -= 1;
-                                if (elems[match] == 0) {
-                                    delete elems[match];
-                                }
-                                total += lev;
-                                done += 1;
-                            } else {
-                                return MAX_LEV_DISTANCE + 1;
-                            }
-                        }
-                        return Math.ceil(total / done);
-                    }
-                }
-                return MAX_LEV_DISTANCE + 1;
-            }
-
-            // Check for type name and type generics (if any).
-            function checkType(obj, val, literalSearch) {
-                var lev_distance = MAX_LEV_DISTANCE + 1;
-                var len, x, firstGeneric;
-                if (obj[NAME] === val.name) {
-                    if (literalSearch === true) {
-                        if (val.generics && val.generics.length !== 0) {
-                            if (obj.length > GENERICS_DATA &&
-                                  obj[GENERICS_DATA].length >= val.generics.length) {
-                                var elems = Object.create(null);
-                                len = obj[GENERICS_DATA].length;
-                                for (x = 0; x < len; ++x) {
-                                    elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
-                                }
-
-                                var allFound = true;
-                                len = val.generics.length;
-                                for (x = 0; x < len; ++x) {
-                                    firstGeneric = getObjectNameFromId(val.generics[x]);
-                                    if (elems[firstGeneric]) {
-                                        elems[firstGeneric] -= 1;
-                                    } else {
-                                        allFound = false;
-                                        break;
-                                    }
-                                }
-                                if (allFound === true) {
-                                    return true;
-                                }
-                            } else {
-                                return false;
-                            }
-                        }
-                        return true;
-                    }
-                    // If the type has generics but don't match, then it won't return at this point.
-                    // Otherwise, `checkGenerics` will return 0 and it'll return.
-                    if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
-                        var tmp_lev = checkGenerics(obj, val);
-                        if (tmp_lev <= MAX_LEV_DISTANCE) {
-                            return tmp_lev;
-                        }
-                    } else {
-                        return 0;
-                    }
-                }
-                // Names didn't match so let's check if one of the generic types could.
-                if (literalSearch === true) {
-                     if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
-                        return obj[GENERICS_DATA].some(
-                            function(name) {
-                                return name === val.name;
-                            });
-                    }
-                    return false;
-                }
-                lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance);
-                if (lev_distance <= MAX_LEV_DISTANCE) {
-                    // The generics didn't match but the name kinda did so we give it
-                    // a levenshtein distance value that isn't *this* good so it goes
-                    // into the search results but not too high.
-                    lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2);
-                } else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
-                    // We can check if the type we're looking for is inside the generics!
-                    var olength = obj[GENERICS_DATA].length;
-                    for (x = 0; x < olength; ++x) {
-                        lev_distance = Math.min(levenshtein(obj[GENERICS_DATA][x], val.name),
-                                                lev_distance);
-                    }
-                }
-                // Now whatever happens, the returned distance is "less good" so we should mark it
-                // as such, and so we add 1 to the distance to make it "less good".
-                return lev_distance + 1;
-            }
-
-            function findArg(obj, val, literalSearch, typeFilter) {
-                var lev_distance = MAX_LEV_DISTANCE + 1;
-
-                if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) {
-                    var length = obj.type[INPUTS_DATA].length;
-                    for (var i = 0; i < length; i++) {
-                        var tmp = obj.type[INPUTS_DATA][i];
-                        if (typePassesFilter(typeFilter, tmp[1]) === false) {
-                            continue;
-                        }
-                        tmp = checkType(tmp, val, literalSearch);
-                        if (literalSearch === true) {
-                            if (tmp === true) {
-                                return true;
-                            }
-                            continue;
-                        }
-                        lev_distance = Math.min(tmp, lev_distance);
-                        if (lev_distance === 0) {
-                            return 0;
-                        }
-                    }
-                }
-                return literalSearch === true ? false : lev_distance;
-            }
-
-            function checkReturned(obj, val, literalSearch, typeFilter) {
-                var lev_distance = MAX_LEV_DISTANCE + 1;
-
-                if (obj && obj.type && obj.type.length > OUTPUT_DATA) {
-                    var ret = obj.type[OUTPUT_DATA];
-                    if (typeof ret[0] === "string") {
-                        ret = [ret];
-                    }
-                    for (var x = 0, len = ret.length; x < len; ++x) {
-                        var tmp = ret[x];
-                        if (typePassesFilter(typeFilter, tmp[1]) === false) {
-                            continue;
-                        }
-                        tmp = checkType(tmp, val, literalSearch);
-                        if (literalSearch === true) {
-                            if (tmp === true) {
-                                return true;
-                            }
-                            continue;
-                        }
-                        lev_distance = Math.min(tmp, lev_distance);
-                        if (lev_distance === 0) {
-                            return 0;
-                        }
-                    }
-                }
-                return literalSearch === true ? false : lev_distance;
-            }
-
-            function checkPath(contains, lastElem, ty) {
-                if (contains.length === 0) {
-                    return 0;
-                }
-                var ret_lev = MAX_LEV_DISTANCE + 1;
-                var path = ty.path.split("::");
-
-                if (ty.parent && ty.parent.name) {
-                    path.push(ty.parent.name.toLowerCase());
-                }
-
-                var length = path.length;
-                var clength = contains.length;
-                if (clength > length) {
-                    return MAX_LEV_DISTANCE + 1;
-                }
-                for (var i = 0; i < length; ++i) {
-                    if (i + clength > length) {
-                        break;
-                    }
-                    var lev_total = 0;
-                    var aborted = false;
-                    for (var x = 0; x < clength; ++x) {
-                        var lev = levenshtein(path[i + x], contains[x]);
-                        if (lev > MAX_LEV_DISTANCE) {
-                            aborted = true;
-                            break;
-                        }
-                        lev_total += lev;
-                    }
-                    if (aborted === false) {
-                        ret_lev = Math.min(ret_lev, Math.round(lev_total / clength));
-                    }
-                }
-                return ret_lev;
-            }
-
-            function typePassesFilter(filter, type) {
-                // No filter
-                if (filter <= NO_TYPE_FILTER) return true;
-
-                // Exact match
-                if (filter === type) return true;
-
-                // Match related items
-                var name = itemTypes[type];
-                switch (itemTypes[filter]) {
-                    case "constant":
-                        return name === "associatedconstant";
-                    case "fn":
-                        return name === "method" || name === "tymethod";
-                    case "type":
-                        return name === "primitive" || name === "associatedtype";
-                    case "trait":
-                        return name === "traitalias";
-                }
-
-                // No match
-                return false;
-            }
-
-            function createAliasFromItem(item) {
-                return {
-                    crate: item.crate,
-                    name: item.name,
-                    path: item.path,
-                    desc: item.desc,
-                    ty: item.ty,
-                    parent: item.parent,
-                    type: item.type,
-                    is_alias: true,
-                };
-            }
-
-            function handleAliases(ret, query, filterCrates) {
-                // We separate aliases and crate aliases because we want to have current crate
-                // aliases to be before the others in the displayed results.
-                var aliases = [];
-                var crateAliases = [];
-                if (filterCrates !== undefined) {
-                    if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) {
-                        var query_aliases = ALIASES[filterCrates][query.search];
-                        var len = query_aliases.length;
-                        for (var i = 0; i < len; ++i) {
-                            aliases.push(createAliasFromItem(searchIndex[query_aliases[i]]));
-                        }
-                    }
-                } else {
-                    Object.keys(ALIASES).forEach(function(crate) {
-                        if (ALIASES[crate][query.search]) {
-                            var pushTo = crate === window.currentCrate ? crateAliases : aliases;
-                            var query_aliases = ALIASES[crate][query.search];
-                            var len = query_aliases.length;
-                            for (var i = 0; i < len; ++i) {
-                                pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]]));
-                            }
-                        }
-                    });
-                }
-
-                var sortFunc = function(aaa, bbb) {
-                    if (aaa.path < bbb.path) {
-                        return 1;
-                    } else if (aaa.path === bbb.path) {
-                        return 0;
-                    }
-                    return -1;
-                };
-                crateAliases.sort(sortFunc);
-                aliases.sort(sortFunc);
-
-                var pushFunc = function(alias) {
-                    alias.alias = query.raw;
-                    var res = buildHrefAndPath(alias);
-                    alias.displayPath = pathSplitter(res[0]);
-                    alias.fullPath = alias.displayPath + alias.name;
-                    alias.href = res[1];
-
-                    ret.others.unshift(alias);
-                    if (ret.others.length > MAX_RESULTS) {
-                        ret.others.pop();
-                    }
-                };
-                onEach(aliases, pushFunc);
-                onEach(crateAliases, pushFunc);
-            }
-
-            // quoted values mean literal search
-            var nSearchWords = searchWords.length;
-            var i, it;
-            var ty;
-            var fullId;
-            var returned;
-            var in_args;
-            var len;
-            if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
-                val.charAt(val.length - 1) === val.charAt(0))
-            {
-                val = extractGenerics(val.substr(1, val.length - 2));
-                for (i = 0; i < nSearchWords; ++i) {
-                    if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
-                        continue;
-                    }
-                    in_args = findArg(searchIndex[i], val, true, typeFilter);
-                    returned = checkReturned(searchIndex[i], val, true, typeFilter);
-                    ty = searchIndex[i];
-                    fullId = ty.id;
-
-                    if (searchWords[i] === val.name
-                        && typePassesFilter(typeFilter, searchIndex[i].ty)
-                        && results[fullId] === undefined) {
-                        results[fullId] = {
-                            id: i,
-                            index: -1,
-                            dontValidate: true,
-                        };
-                    }
-                    if (in_args === true && results_in_args[fullId] === undefined) {
-                        results_in_args[fullId] = {
-                            id: i,
-                            index: -1,
-                            dontValidate: true,
-                        };
-                    }
-                    if (returned === true && results_returned[fullId] === undefined) {
-                        results_returned[fullId] = {
-                            id: i,
-                            index: -1,
-                            dontValidate: true,
-                        };
-                    }
-                }
-                query.inputs = [val];
-                query.output = val;
-                query.search = val;
-            // searching by type
-            } else if (val.search("->") > -1) {
-                var trimmer = function(s) { return s.trim(); };
-                var parts = val.split("->").map(trimmer);
-                var input = parts[0];
-                // sort inputs so that order does not matter
-                var inputs = input.split(",").map(trimmer).sort();
-                for (i = 0, len = inputs.length; i < len; ++i) {
-                    inputs[i] = extractGenerics(inputs[i]);
-                }
-                var output = extractGenerics(parts[1]);
-
-                for (i = 0; i < nSearchWords; ++i) {
-                    if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
-                        continue;
-                    }
-                    var type = searchIndex[i].type;
-                    ty = searchIndex[i];
-                    if (!type) {
-                        continue;
-                    }
-                    fullId = ty.id;
-
-                    returned = checkReturned(ty, output, true, NO_TYPE_FILTER);
-                    if (output.name === "*" || returned === true) {
-                        in_args = false;
-                        var is_module = false;
-
-                        if (input === "*") {
-                            is_module = true;
-                        } else {
-                            var allFound = true;
-                            for (it = 0, len = inputs.length; allFound === true && it < len; it++) {
-                                allFound = checkType(type, inputs[it], true);
-                            }
-                            in_args = allFound;
-                        }
-                        if (in_args === true) {
-                            results_in_args[fullId] = {
-                                id: i,
-                                index: -1,
-                                dontValidate: true,
-                            };
-                        }
-                        if (returned === true) {
-                            results_returned[fullId] = {
-                                id: i,
-                                index: -1,
-                                dontValidate: true,
-                            };
-                        }
-                        if (is_module === true) {
-                            results[fullId] = {
-                                id: i,
-                                index: -1,
-                                dontValidate: true,
-                            };
-                        }
-                    }
-                }
-                query.inputs = inputs.map(function(input) {
-                    return input.name;
-                });
-                query.output = output.name;
-            } else {
-                query.inputs = [val];
-                query.output = val;
-                query.search = val;
-                // gather matching search results up to a certain maximum
-                val = val.replace(/\_/g, "");
-
-                var valGenerics = extractGenerics(val);
-
-                var paths = valLower.split("::");
-                removeEmptyStringsFromArray(paths);
-                val = paths[paths.length - 1];
-                var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1);
-
-                var lev, j;
-                for (j = 0; j < nSearchWords; ++j) {
-                    ty = searchIndex[j];
-                    if (!ty || (filterCrates !== undefined && ty.crate !== filterCrates)) {
-                        continue;
-                    }
-                    var lev_add = 0;
-                    if (paths.length > 1) {
-                        lev = checkPath(contains, paths[paths.length - 1], ty);
-                        if (lev > MAX_LEV_DISTANCE) {
-                            continue;
-                        } else if (lev > 0) {
-                            lev_add = lev / 10;
-                        }
-                    }
-
-                    returned = MAX_LEV_DISTANCE + 1;
-                    in_args = MAX_LEV_DISTANCE + 1;
-                    var index = -1;
-                    // we want lev results to go lower than others
-                    lev = MAX_LEV_DISTANCE + 1;
-                    fullId = ty.id;
-
-                    if (searchWords[j].indexOf(split[i]) > -1 ||
-                        searchWords[j].indexOf(val) > -1 ||
-                        ty.normalizedName.indexOf(val) > -1)
-                    {
-                        // filter type: ... queries
-                        if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) {
-                            index = ty.normalizedName.indexOf(val);
-                        }
-                    }
-                    if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) {
-                        if (typePassesFilter(typeFilter, ty.ty) === false) {
-                            lev = MAX_LEV_DISTANCE + 1;
-                        } else {
-                            lev += 1;
-                        }
-                    }
-                    in_args = findArg(ty, valGenerics, false, typeFilter);
-                    returned = checkReturned(ty, valGenerics, false, typeFilter);
-
-                    lev += lev_add;
-                    if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) {
-                        if (val.length < 6) {
-                            lev -= 1;
-                        } else {
-                            lev = 0;
-                        }
-                    }
-                    if (in_args <= MAX_LEV_DISTANCE) {
-                        if (results_in_args[fullId] === undefined) {
-                            results_in_args[fullId] = {
-                                id: j,
-                                index: index,
-                                lev: in_args,
-                            };
-                        }
-                        results_in_args[fullId].lev =
-                            Math.min(results_in_args[fullId].lev, in_args);
-                    }
-                    if (returned <= MAX_LEV_DISTANCE) {
-                        if (results_returned[fullId] === undefined) {
-                            results_returned[fullId] = {
-                                id: j,
-                                index: index,
-                                lev: returned,
-                            };
-                        }
-                        results_returned[fullId].lev =
-                            Math.min(results_returned[fullId].lev, returned);
-                    }
-                    if (index !== -1 || lev <= MAX_LEV_DISTANCE) {
-                        if (index !== -1 && paths.length < 2) {
-                            lev = 0;
-                        }
-                        if (results[fullId] === undefined) {
-                            results[fullId] = {
-                                id: j,
-                                index: index,
-                                lev: lev,
-                            };
-                        }
-                        results[fullId].lev = Math.min(results[fullId].lev, lev);
-                    }
-                }
-            }
-
-            var ret = {
-                "in_args": sortResults(results_in_args, true),
-                "returned": sortResults(results_returned, true),
-                "others": sortResults(results),
-            };
-            handleAliases(ret, query, filterCrates);
-            return ret;
-        }
-
-        /**
-         * Validate performs the following boolean logic. For example:
-         * "File::open" will give IF A PARENT EXISTS => ("file" && "open")
-         * exists in (name || path || parent) OR => ("file" && "open") exists in
-         * (name || path )
-         *
-         * This could be written functionally, but I wanted to minimise
-         * functions on stack.
-         *
-         * @param  {[string]} name   [The name of the result]
-         * @param  {[string]} path   [The path of the result]
-         * @param  {[string]} keys   [The keys to be used (["file", "open"])]
-         * @param  {[object]} parent [The parent of the result]
-         * @return {[boolean]}       [Whether the result is valid or not]
-         */
-        function validateResult(name, path, keys, parent) {
-            for (var i = 0, len = keys.length; i < len; ++i) {
-                // each check is for validation so we negate the conditions and invalidate
-                if (!(
-                    // check for an exact name match
-                    name.indexOf(keys[i]) > -1 ||
-                    // then an exact path match
-                    path.indexOf(keys[i]) > -1 ||
-                    // next if there is a parent, check for exact parent match
-                    (parent !== undefined && parent.name !== undefined &&
-                        parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
-                    // lastly check to see if the name was a levenshtein match
-                    levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        function getQuery(raw) {
-            var matches, type, query;
-            query = raw;
-
-            matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
-            if (matches) {
-                type = matches[1].replace(/^const$/, "constant");
-                query = query.substring(matches[0].length);
-            }
-
-            return {
-                raw: raw,
-                query: query,
-                type: type,
-                id: query + type
-            };
-        }
-
-        function initSearchNav() {
-            var hoverTimeout;
-
-            var click_func = function(e) {
-                var el = e.target;
-                // to retrieve the real "owner" of the event.
-                while (el.tagName !== "TR") {
-                    el = el.parentNode;
-                }
-                var dst = e.target.getElementsByTagName("a");
-                if (dst.length < 1) {
-                    return;
-                }
-                dst = dst[0];
-                if (window.location.pathname === dst.pathname) {
-                    hideSearchResults();
-                    document.location.href = dst.href;
-                }
-            };
-            var mouseover_func = function(e) {
-                if (mouseMovedAfterSearch) {
-                    var el = e.target;
-                    // to retrieve the real "owner" of the event.
-                    while (el.tagName !== "TR") {
-                        el = el.parentNode;
-                    }
-                    clearTimeout(hoverTimeout);
-                    hoverTimeout = setTimeout(function() {
-                        onEachLazy(document.getElementsByClassName("search-results"), function(e) {
-                            onEachLazy(e.getElementsByClassName("result"), function(i_e) {
-                                removeClass(i_e, "highlighted");
-                            });
-                        });
-                        addClass(el, "highlighted");
-                    }, 20);
-                }
-            };
-            onEachLazy(document.getElementsByClassName("search-results"), function(e) {
-                onEachLazy(e.getElementsByClassName("result"), function(i_e) {
-                    i_e.onclick = click_func;
-                    i_e.onmouseover = mouseover_func;
-                });
-            });
-
-            search_input.onkeydown = function(e) {
-                // "actives" references the currently highlighted item in each search tab.
-                // Each array in "actives" represents a tab.
-                var actives = [[], [], []];
-                // "current" is used to know which tab we're looking into.
-                var current = 0;
-                onEachLazy(document.getElementById("results").childNodes, function(e) {
-                    onEachLazy(e.getElementsByClassName("highlighted"), function(h_e) {
-                        actives[current].push(h_e);
-                    });
-                    current += 1;
-                });
-
-                if (e.which === 38) { // up
-                    if (e.ctrlKey) { // Going through result tabs.
-                        printTab(currentTab > 0 ? currentTab - 1 : 2);
-                    } else {
-                        if (!actives[currentTab].length ||
-                            !actives[currentTab][0].previousElementSibling) {
-                            return;
-                        }
-                        addClass(actives[currentTab][0].previousElementSibling, "highlighted");
-                        removeClass(actives[currentTab][0], "highlighted");
-                    }
-                    e.preventDefault();
-                } else if (e.which === 40) { // down
-                    if (e.ctrlKey) { // Going through result tabs.
-                        printTab(currentTab > 1 ? 0 : currentTab + 1);
-                    } else if (!actives[currentTab].length) {
-                        var results = document.getElementById("results").childNodes;
-                        if (results.length > 0) {
-                            var res = results[currentTab].getElementsByClassName("result");
-                            if (res.length > 0) {
-                                addClass(res[0], "highlighted");
-                            }
-                        }
-                    } else if (actives[currentTab][0].nextElementSibling) {
-                        addClass(actives[currentTab][0].nextElementSibling, "highlighted");
-                        removeClass(actives[currentTab][0], "highlighted");
-                    }
-                    e.preventDefault();
-                } else if (e.which === 13) { // return
-                    if (actives[currentTab].length) {
-                        document.location.href =
-                            actives[currentTab][0].getElementsByTagName("a")[0].href;
-                    }
-                } else if (e.which === 16) { // shift
-                    // Does nothing, it's just to avoid losing "focus" on the highlighted element.
-                } else if (actives[currentTab].length > 0) {
-                    removeClass(actives[currentTab][0], "highlighted");
-                }
-            };
-        }
-
-        function buildHrefAndPath(item) {
-            var displayPath;
-            var href;
-            var type = itemTypes[item.ty];
-            var name = item.name;
-            var path = item.path;
-
-            if (type === "mod") {
-                displayPath = path + "::";
-                href = window.rootPath + path.replace(/::/g, "/") + "/" +
-                       name + "/index.html";
-            } else if (type === "primitive" || type === "keyword") {
-                displayPath = "";
-                href = window.rootPath + path.replace(/::/g, "/") +
-                       "/" + type + "." + name + ".html";
-            } else if (type === "externcrate") {
-                displayPath = "";
-                href = window.rootPath + name + "/index.html";
-            } else if (item.parent !== undefined) {
-                var myparent = item.parent;
-                var anchor = "#" + type + "." + name;
-                var parentType = itemTypes[myparent.ty];
-                var pageType = parentType;
-                var pageName = myparent.name;
-
-                if (parentType === "primitive") {
-                    displayPath = myparent.name + "::";
-                } else if (type === "structfield" && parentType === "variant") {
-                    // Structfields belonging to variants are special: the
-                    // final path element is the enum name.
-                    var enumNameIdx = item.path.lastIndexOf("::");
-                    var enumName = item.path.substr(enumNameIdx + 2);
-                    path = item.path.substr(0, enumNameIdx);
-                    displayPath = path + "::" + enumName + "::" + myparent.name + "::";
-                    anchor = "#variant." + myparent.name + ".field." + name;
-                    pageType = "enum";
-                    pageName = enumName;
-                } else {
-                    displayPath = path + "::" + myparent.name + "::";
-                }
-                href = window.rootPath + path.replace(/::/g, "/") +
-                       "/" + pageType +
-                       "." + pageName +
-                       ".html" + anchor;
-            } else {
-                displayPath = item.path + "::";
-                href = window.rootPath + item.path.replace(/::/g, "/") +
-                       "/" + type + "." + name + ".html";
-            }
-            return [displayPath, href];
-        }
-
-        function escape(content) {
-            var h1 = document.createElement("h1");
-            h1.textContent = content;
-            return h1.innerHTML;
-        }
-
-        function pathSplitter(path) {
-            var tmp = "<span>" + path.replace(/::/g, "::</span><span>");
-            if (tmp.endsWith("<span>")) {
-                return tmp.slice(0, tmp.length - 6);
-            }
-            return tmp;
-        }
-
-        function addTab(array, query, display) {
-            var extraStyle = "";
-            if (display === false) {
-                extraStyle = " style=\"display: none;\"";
-            }
-
-            var output = "";
-            var duplicates = {};
-            var length = 0;
-            if (array.length > 0) {
-                output = "<table class=\"search-results\"" + extraStyle + ">";
-
-                array.forEach(function(item) {
-                    var name, type;
-
-                    name = item.name;
-                    type = itemTypes[item.ty];
-
-                    if (item.is_alias !== true) {
-                        if (duplicates[item.fullPath]) {
-                            return;
-                        }
-                        duplicates[item.fullPath] = true;
-                    }
-                    length += 1;
-
-                    output += "<tr class=\"" + type + " result\"><td>" +
-                              "<a href=\"" + item.href + "\">" +
-                              (item.is_alias === true ?
-                               ("<span class=\"alias\"><b>" + item.alias + " </b></span><span " +
-                                  "class=\"grey\"><i>&nbsp;- see&nbsp;</i></span>") : "") +
-                              item.displayPath + "<span class=\"" + type + "\">" +
-                              name + "</span></a></td><td>" +
-                              "<a href=\"" + item.href + "\">" +
-                              "<span class=\"desc\">" + item.desc +
-                              "&nbsp;</span></a></td></tr>";
-                });
-                output += "</table>";
-            } else {
-                output = "<div class=\"search-failed\"" + extraStyle + ">No results :(<br/>" +
-                    "Try on <a href=\"https://duckduckgo.com/?q=" +
-                    encodeURIComponent("rust " + query.query) +
-                    "\">DuckDuckGo</a>?<br/><br/>" +
-                    "Or try looking in one of these:<ul><li>The <a " +
-                    "href=\"https://doc.rust-lang.org/reference/index.html\">Rust Reference</a> " +
-                    " for technical details about the language.</li><li><a " +
-                    "href=\"https://doc.rust-lang.org/rust-by-example/index.html\">Rust By " +
-                    "Example</a> for expository code examples.</a></li><li>The <a " +
-                    "href=\"https://doc.rust-lang.org/book/index.html\">Rust Book</a> for " +
-                    "introductions to language features and the language itself.</li><li><a " +
-                    "href=\"https://docs.rs\">Docs.rs</a> for documentation of crates released on" +
-                    " <a href=\"https://crates.io/\">crates.io</a>.</li></ul></div>";
-            }
-            return [output, length];
-        }
-
-        function makeTabHeader(tabNb, text, nbElems) {
-            if (currentTab === tabNb) {
-                return "<button class=\"selected\">" + text +
-                       " <div class=\"count\">(" + nbElems + ")</div></button>";
-            }
-            return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>";
-        }
-
-        function showResults(results) {
-            var search = getSearchElement();
-            if (results.others.length === 1
-                && getSettingValue("go-to-only-result") === "true"
-                // By default, the search DOM element is "empty" (meaning it has no children not
-                // text content). Once a search has been run, it won't be empty, even if you press
-                // ESC or empty the search input (which also "cancels" the search).
-                && (!search.firstChild || search.firstChild.innerText !== getSearchLoadingText()))
-            {
-                var elem = document.createElement("a");
-                elem.href = results.others[0].href;
-                elem.style.display = "none";
-                // For firefox, we need the element to be in the DOM so it can be clicked.
-                document.body.appendChild(elem);
-                elem.click();
-                return;
-            }
-            var query = getQuery(search_input.value);
-
-            currentResults = query.id;
-
-            var ret_others = addTab(results.others, query);
-            var ret_in_args = addTab(results.in_args, query, false);
-            var ret_returned = addTab(results.returned, query, false);
-
-            // Navigate to the relevant tab if the current tab is empty, like in case users search
-            // for "-> String". If they had selected another tab previously, they have to click on
-            // it again.
-            if ((currentTab === 0 && ret_others[1] === 0) ||
-                    (currentTab === 1 && ret_in_args[1] === 0) ||
-                    (currentTab === 2 && ret_returned[1] === 0)) {
-                if (ret_others[1] !== 0) {
-                    currentTab = 0;
-                } else if (ret_in_args[1] !== 0) {
-                    currentTab = 1;
-                } else if (ret_returned[1] !== 0) {
-                    currentTab = 2;
-                }
-            }
-
-            var output = "<h1>Results for " + escape(query.query) +
-                (query.type ? " (type: " + escape(query.type) + ")" : "") + "</h1>" +
-                "<div id=\"titles\">" +
-                makeTabHeader(0, "In Names", ret_others[1]) +
-                makeTabHeader(1, "In Parameters", ret_in_args[1]) +
-                makeTabHeader(2, "In Return Types", ret_returned[1]) +
-                "</div><div id=\"results\">" +
-                ret_others[0] + ret_in_args[0] + ret_returned[0] + "</div>";
-
-            search.innerHTML = output;
-            showSearchResults(search);
-            initSearchNav();
-            var elems = document.getElementById("titles").childNodes;
-            elems[0].onclick = function() { printTab(0); };
-            elems[1].onclick = function() { printTab(1); };
-            elems[2].onclick = function() { printTab(2); };
-            printTab(currentTab);
-        }
-
-        function execSearch(query, searchWords, filterCrates) {
-            function getSmallest(arrays, positions, notDuplicates) {
-                var start = null;
-
-                for (var it = 0, len = positions.length; it < len; ++it) {
-                    if (arrays[it].length > positions[it] &&
-                        (start === null || start > arrays[it][positions[it]].lev) &&
-                        !notDuplicates[arrays[it][positions[it]].fullPath]) {
-                        start = arrays[it][positions[it]].lev;
-                    }
-                }
-                return start;
-            }
-
-            function mergeArrays(arrays) {
-                var ret = [];
-                var positions = [];
-                var notDuplicates = {};
-
-                for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) {
-                    positions.push(0);
-                }
-                while (ret.length < MAX_RESULTS) {
-                    var smallest = getSmallest(arrays, positions, notDuplicates);
-
-                    if (smallest === null) {
-                        break;
-                    }
-                    for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) {
-                        if (arrays[x].length > positions[x] &&
-                                arrays[x][positions[x]].lev === smallest &&
-                                !notDuplicates[arrays[x][positions[x]].fullPath]) {
-                            ret.push(arrays[x][positions[x]]);
-                            notDuplicates[arrays[x][positions[x]].fullPath] = true;
-                            positions[x] += 1;
-                        }
-                    }
-                }
-                return ret;
-            }
-
-            var queries = query.raw.split(",");
-            var results = {
-                "in_args": [],
-                "returned": [],
-                "others": [],
-            };
-
-            for (var i = 0, len = queries.length; i < len; ++i) {
-                query = queries[i].trim();
-                if (query.length !== 0) {
-                    var tmp = execQuery(getQuery(query), searchWords, filterCrates);
-
-                    results.in_args.push(tmp.in_args);
-                    results.returned.push(tmp.returned);
-                    results.others.push(tmp.others);
-                }
-            }
-            if (queries.length > 1) {
-                return {
-                    "in_args": mergeArrays(results.in_args),
-                    "returned": mergeArrays(results.returned),
-                    "others": mergeArrays(results.others),
-                };
-            }
-            return {
-                "in_args": results.in_args[0],
-                "returned": results.returned[0],
-                "others": results.others[0],
-            };
-        }
-
-        function getFilterCrates() {
-            var elem = document.getElementById("crate-search");
-
-            if (elem && elem.value !== "All crates" && hasOwnProperty(rawSearchIndex, elem.value)) {
-                return elem.value;
-            }
-            return undefined;
-        }
-
-        function search(e, forced) {
-            var params = getQueryStringParams();
-            var query = getQuery(search_input.value.trim());
-
-            if (e) {
-                e.preventDefault();
-            }
-
-            if (query.query.length === 0) {
-                return;
-            }
-            if (forced !== true && query.id === currentResults) {
-                if (query.query.length > 0) {
-                    putBackSearch(search_input);
-                }
-                return;
-            }
-
-            // Update document title to maintain a meaningful browser history
-            searchTitle = "Results for " + query.query + " - Rust";
-
-            // Because searching is incremental by character, only the most
-            // recent search query is added to the browser history.
-            if (browserSupportsHistoryApi()) {
-                var newURL = getNakedUrl() + "?search=" + encodeURIComponent(query.raw) +
-                    window.location.hash;
-                if (!history.state && !params.search) {
-                    history.pushState(query, "", newURL);
-                } else {
-                    history.replaceState(query, "", newURL);
-                }
-            }
-
-            var filterCrates = getFilterCrates();
-            showResults(execSearch(query, index, filterCrates));
-        }
-
-        function buildIndex(rawSearchIndex) {
-            searchIndex = [];
-            var searchWords = [];
-            var i, word;
-            var currentIndex = 0;
-            var id = 0;
-
-            for (var crate in rawSearchIndex) {
-                if (!hasOwnProperty(rawSearchIndex, crate)) { continue; }
-
-                var crateSize = 0;
-
-                searchWords.push(crate);
-                var normalizedName = crate.indexOf("_") === -1
-                    ? crate
-                    : crate.replace(/_/g, "");
-                // This object should have exactly the same set of fields as the "row"
-                // object defined below. Your JavaScript runtime will thank you.
-                // https://mathiasbynens.be/notes/shapes-ics
-                var crateRow = {
-                    crate: crate,
-                    ty: 1, // == ExternCrate
-                    name: crate,
-                    path: "",
-                    desc: rawSearchIndex[crate].doc,
-                    parent: undefined,
-                    type: null,
-                    id: id,
-                    normalizedName: normalizedName,
-                };
-                id += 1;
-                searchIndex.push(crateRow);
-                currentIndex += 1;
-
-                // an array of (Number) item types
-                var itemTypes = rawSearchIndex[crate].t;
-                // an array of (String) item names
-                var itemNames = rawSearchIndex[crate].n;
-                // an array of (String) full paths (or empty string for previous path)
-                var itemPaths = rawSearchIndex[crate].q;
-                // an array of (String) descriptions
-                var itemDescs = rawSearchIndex[crate].d;
-                // an array of (Number) the parent path index + 1 to `paths`, or 0 if none
-                var itemParentIdxs = rawSearchIndex[crate].i;
-                // an array of (Object | null) the type of the function, if any
-                var itemFunctionSearchTypes = rawSearchIndex[crate].f;
-                // an array of [(Number) item type,
-                //              (String) name]
-                var paths = rawSearchIndex[crate].p;
-                // a array of [(String) alias name
-                //             [Number] index to items]
-                var aliases = rawSearchIndex[crate].a;
-
-                // convert `rawPaths` entries into object form
-                var len = paths.length;
-                for (i = 0; i < len; ++i) {
-                    paths[i] = {ty: paths[i][0], name: paths[i][1]};
-                }
-
-                // convert `item*` into an object form, and construct word indices.
-                //
-                // before any analysis is performed lets gather the search terms to
-                // search against apart from the rest of the data.  This is a quick
-                // operation that is cached for the life of the page state so that
-                // all other search operations have access to this cached data for
-                // faster analysis operations
-                len = itemTypes.length;
-                var lastPath = "";
-                for (i = 0; i < len; ++i) {
-                    // This object should have exactly the same set of fields as the "crateRow"
-                    // object defined above.
-                    if (typeof itemNames[i] === "string") {
-                        word = itemNames[i].toLowerCase();
-                        searchWords.push(word);
-                    } else {
-                        word = "";
-                        searchWords.push("");
-                    }
-                    var normalizedName = word.indexOf("_") === -1
-                        ? word
-                        : word.replace(/_/g, "");
-                    var row = {
-                        crate: crate,
-                        ty: itemTypes[i],
-                        name: itemNames[i],
-                        path: itemPaths[i] ? itemPaths[i] : lastPath,
-                        desc: itemDescs[i],
-                        parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
-                        type: itemFunctionSearchTypes[i],
-                        id: id,
-                        normalizedName: normalizedName,
-                    };
-                    id += 1;
-                    searchIndex.push(row);
-                    lastPath = row.path;
-                    crateSize += 1;
-                }
-
-                if (aliases) {
-                    ALIASES[crate] = {};
-                    var j, local_aliases;
-                    for (var alias_name in aliases) {
-                        if (!aliases.hasOwnProperty(alias_name)) { continue; }
-
-                        if (!ALIASES[crate].hasOwnProperty(alias_name)) {
-                            ALIASES[crate][alias_name] = [];
-                        }
-                        local_aliases = aliases[alias_name];
-                        for (j = 0, len = local_aliases.length; j < len; ++j) {
-                            ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex);
-                        }
-                    }
-                }
-                currentIndex += crateSize;
-            }
-            return searchWords;
-        }
-
-        function registerSearchEvents() {
-            var searchAfter500ms = function() {
-                clearInputTimeout();
-                if (search_input.value.length === 0) {
-                    if (browserSupportsHistoryApi()) {
-                        history.replaceState("", window.currentCrate + " - Rust",
-                            getNakedUrl() + window.location.hash);
-                    }
-                    hideSearchResults();
-                } else {
-                    searchTimeout = setTimeout(search, 500);
-                }
-            };
-            search_input.onkeyup = searchAfter500ms;
-            search_input.oninput = searchAfter500ms;
-            document.getElementsByClassName("search-form")[0].onsubmit = function(e) {
-                e.preventDefault();
-                clearInputTimeout();
-                search();
-            };
-            search_input.onchange = function(e) {
-                if (e.target !== document.activeElement) {
-                    // To prevent doing anything when it's from a blur event.
-                    return;
-                }
-                // Do NOT e.preventDefault() here. It will prevent pasting.
-                clearInputTimeout();
-                // zero-timeout necessary here because at the time of event handler execution the
-                // pasted content is not in the input field yet. Shouldn’t make any difference for
-                // change, though.
-                setTimeout(search, 0);
-            };
-            search_input.onpaste = search_input.onchange;
-
-            var selectCrate = document.getElementById("crate-search");
-            if (selectCrate) {
-                selectCrate.onchange = function() {
-                    updateLocalStorage("rustdoc-saved-filter-crate", selectCrate.value);
-                    search(undefined, true);
-                };
-            }
-
-            // Push and pop states are used to add search results to the browser
-            // history.
-            if (browserSupportsHistoryApi()) {
-                // Store the previous <title> so we can revert back to it later.
-                var previousTitle = document.title;
-
-                window.addEventListener("popstate", function(e) {
-                    var params = getQueryStringParams();
-                    // Revert to the previous title manually since the History
-                    // API ignores the title parameter.
-                    document.title = previousTitle;
-                    // When browsing forward to search results the previous
-                    // search will be repeated, so the currentResults are
-                    // cleared to ensure the search is successful.
-                    currentResults = null;
-                    // Synchronize search bar with query string state and
-                    // perform the search. This will empty the bar if there's
-                    // nothing there, which lets you really go back to a
-                    // previous state with nothing in the bar.
-                    if (params.search && params.search.length > 0) {
-                        search_input.value = params.search;
-                        // Some browsers fire "onpopstate" for every page load
-                        // (Chrome), while others fire the event only when actually
-                        // popping a state (Firefox), which is why search() is
-                        // called both here and at the end of the startSearch()
-                        // function.
-                        search(e);
-                    } else {
-                        search_input.value = "";
-                        // When browsing back from search results the main page
-                        // visibility must be reset.
-                        hideSearchResults();
-                    }
-                });
-            }
-
-            // 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
-            // the doc page, rather than the search that should overlay it.
-            // This was an interaction between the back-forward cache and our handlers
-            // that try to sync state between the URL and the search input. To work around it,
-            // do a small amount of re-init on page show.
-            window.onpageshow = function(){
-                var qSearch = getQueryStringParams().search;
-                if (search_input.value === "" && qSearch) {
-                    search_input.value = qSearch;
-                }
-                search();
-            };
-        }
-
-        index = buildIndex(rawSearchIndex);
-        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 === "./") {
@@ -2216,6 +804,9 @@ function hideThemeButtonState() {
         block("foreigntype", "Foreign Types");
         block("keyword", "Keywords");
         block("traitalias", "Trait Aliases");
+
+        // `crates{version}.js` should always be loaded before this script, so we can use it safely.
+        addSidebarCrates(window.ALL_CRATES);
     };
 
     window.register_implementors = function(imp) {
@@ -2813,60 +1404,6 @@ function hideThemeButtonState() {
         };
     });
 
-    // In the search display, allows to switch between tabs.
-    function printTab(nb) {
-        if (nb === 0 || nb === 1 || nb === 2) {
-            currentTab = nb;
-        }
-        var nb_copy = nb;
-        onEachLazy(document.getElementById("titles").childNodes, function(elem) {
-            if (nb_copy === 0) {
-                addClass(elem, "selected");
-            } else {
-                removeClass(elem, "selected");
-            }
-            nb_copy -= 1;
-        });
-        onEachLazy(document.getElementById("results").childNodes, function(elem) {
-            if (nb === 0) {
-                elem.style.display = "";
-            } else {
-                elem.style.display = "none";
-            }
-            nb -= 1;
-        });
-    }
-
-    function putBackSearch(search_input) {
-        var search = getSearchElement();
-        if (search_input.value !== "" && hasClass(search, "hidden")) {
-            showSearchResults(search);
-            if (browserSupportsHistoryApi()) {
-                var extra = "?search=" + encodeURIComponent(search_input.value);
-                history.replaceState(search_input.value, "",
-                    getNakedUrl() + extra + window.location.hash);
-            }
-            document.title = searchTitle;
-        }
-    }
-
-    function getSearchLoadingText() {
-        return "Loading search results...";
-    }
-
-    if (search_input) {
-        search_input.onfocus = function() {
-            putBackSearch(this);
-        };
-    }
-
-    var params = getQueryStringParams();
-    if (params && params.search) {
-        var search = getSearchElement();
-        search.innerHTML = "<h3 style=\"text-align: center;\">" + getSearchLoadingText() + "</h3>";
-        showSearchResults(search);
-    }
-
     var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0];
     if (sidebar_menu) {
         sidebar_menu.onclick = function() {
@@ -2899,30 +1436,6 @@ function hideThemeButtonState() {
         });
     }
 
-    function addSearchOptions(crates) {
-        var elem = document.getElementById("crate-search");
-
-        if (!elem) {
-            return;
-        }
-        var savedCrate = getSettingValue("saved-filter-crate");
-        for (var i = 0, len = crates.length; i < len; ++i) {
-            var option = document.createElement("option");
-            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.
-            //
-            // 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[i] === savedCrate) {
-                elem.value = savedCrate;
-            }
-        }
-    };
-
     function buildHelperPopup() {
         var popup = document.createElement("aside");
         addClass(popup, "hidden");
@@ -2980,55 +1493,14 @@ function hideThemeButtonState() {
         container.appendChild(div_infos);
 
         popup.appendChild(container);
-        insertAfter(popup, getSearchElement());
+        insertAfter(popup, searchState.outputElement());
         // So that it's only built once and then it'll do nothing when called!
         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;
-        });
-        search_input.removeAttribute('disabled');
-
-        var crateSearchDropDown = document.getElementById("crate-search");
-        // `crateSearchDropDown` can be null in case there is only crate because in that case, the
-        // crate filter dropdown is removed.
-        if (crateSearchDropDown) {
-            crateSearchDropDown.addEventListener("focus", loadSearch);
-        }
-        var params = getQueryStringParams();
-        if (params.search !== undefined) {
-            loadSearch();
-        }
-    }
-
     onHashChange(null);
     window.onhashchange = onHashChange;
-    setupSearchLoader();
+    searchState.setup();
 }());
 
 function copy_path(but) {
diff --git a/src/librustdoc/html/static/search.js b/src/librustdoc/html/static/search.js
new file mode 100644
index 00000000000..538c811c710
--- /dev/null
+++ b/src/librustdoc/html/static/search.js
@@ -0,0 +1,1512 @@
+(function() {
+// This mapping table should match the discriminants of
+// `rustdoc::html::item_type::ItemType` type in Rust.
+var itemTypes = ["mod",
+                    "externcrate",
+                    "import",
+                    "struct",
+                    "enum",
+                    "fn",
+                    "type",
+                    "static",
+                    "trait",
+                    "impl",
+                    "tymethod",
+                    "method",
+                    "structfield",
+                    "variant",
+                    "macro",
+                    "primitive",
+                    "associatedtype",
+                    "constant",
+                    "associatedconstant",
+                    "union",
+                    "foreigntype",
+                    "keyword",
+                    "existential",
+                    "attr",
+                    "derive",
+                    "traitalias"];
+
+// used for special search precedence
+var TY_PRIMITIVE = itemTypes.indexOf("primitive");
+var TY_KEYWORD = itemTypes.indexOf("keyword");
+
+// In the search display, allows to switch between tabs.
+function printTab(nb) {
+    if (nb === 0 || nb === 1 || nb === 2) {
+        searchState.currentTab = nb;
+    }
+    var nb_copy = nb;
+    onEachLazy(document.getElementById("titles").childNodes, function(elem) {
+        if (nb_copy === 0) {
+            addClass(elem, "selected");
+        } else {
+            removeClass(elem, "selected");
+        }
+        nb_copy -= 1;
+    });
+    onEachLazy(document.getElementById("results").childNodes, function(elem) {
+        if (nb === 0) {
+            elem.style.display = "";
+        } else {
+            elem.style.display = "none";
+        }
+        nb -= 1;
+    });
+}
+
+function removeEmptyStringsFromArray(x) {
+    for (var i = 0, len = x.length; i < len; ++i) {
+        if (x[i] === "") {
+            x.splice(i, 1);
+            i -= 1;
+        }
+    }
+}
+
+/**
+ * A function to compute the Levenshtein distance between two strings
+ * Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
+ * Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
+ * This code is an unmodified version of the code written by Marco de Wit
+ * and was found at http://stackoverflow.com/a/18514751/745719
+ */
+var levenshtein_row2 = [];
+function levenshtein(s1, s2) {
+    if (s1 === s2) {
+        return 0;
+    }
+    var s1_len = s1.length, s2_len = s2.length;
+    if (s1_len && s2_len) {
+        var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2;
+        while (i1 < s1_len) {
+            row[i1] = ++i1;
+        }
+        while (i2 < s2_len) {
+            c2 = s2.charCodeAt(i2);
+            a = i2;
+            ++i2;
+            b = i2;
+            for (i1 = 0; i1 < s1_len; ++i1) {
+                c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
+                a = row[i1];
+                b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
+                row[i1] = b;
+            }
+        }
+        return b;
+    }
+    return s1_len + s2_len;
+}
+
+window.initSearch = function(rawSearchIndex) {
+    var MAX_LEV_DISTANCE = 3;
+    var MAX_RESULTS = 200;
+    var GENERICS_DATA = 1;
+    var NAME = 0;
+    var INPUTS_DATA = 0;
+    var OUTPUT_DATA = 1;
+    var NO_TYPE_FILTER = -1;
+    var currentResults, index, searchIndex;
+    var ALIASES = {};
+    var params = searchState.getQueryStringParams();
+
+    // Populate search bar with query string search term when provided,
+    // but only if the input bar is empty. This avoid the obnoxious issue
+    // where you start trying to do a search, and the index loads, and
+    // suddenly your search is gone!
+    if (searchState.input.value === "") {
+        searchState.input.value = params.search || "";
+    }
+
+    /**
+     * Executes the query and builds an index of results
+     * @param  {[Object]} query      [The user query]
+     * @param  {[type]} searchWords  [The list of search words to query
+     *                                against]
+     * @param  {[type]} filterCrates [Crate to search in if defined]
+     * @return {[type]}              [A search index of results]
+     */
+    function execQuery(query, searchWords, filterCrates) {
+        function itemTypeFromName(typename) {
+            for (var i = 0, len = itemTypes.length; i < len; ++i) {
+                if (itemTypes[i] === typename) {
+                    return i;
+                }
+            }
+            return NO_TYPE_FILTER;
+        }
+
+        var valLower = query.query.toLowerCase(),
+            val = valLower,
+            typeFilter = itemTypeFromName(query.type),
+            results = {}, results_in_args = {}, results_returned = {},
+            split = valLower.split("::");
+
+        removeEmptyStringsFromArray(split);
+
+        function transformResults(results, isType) {
+            var out = [];
+            for (var i = 0, len = results.length; i < len; ++i) {
+                if (results[i].id > -1) {
+                    var obj = searchIndex[results[i].id];
+                    obj.lev = results[i].lev;
+                    if (isType !== true || obj.type) {
+                        var res = buildHrefAndPath(obj);
+                        obj.displayPath = pathSplitter(res[0]);
+                        obj.fullPath = obj.displayPath + obj.name;
+                        // To be sure than it some items aren't considered as duplicate.
+                        obj.fullPath += "|" + obj.ty;
+                        obj.href = res[1];
+                        out.push(obj);
+                        if (out.length >= MAX_RESULTS) {
+                            break;
+                        }
+                    }
+                }
+            }
+            return out;
+        }
+
+        function sortResults(results, isType) {
+            var ar = [];
+            for (var entry in results) {
+                if (hasOwnProperty(results, entry)) {
+                    ar.push(results[entry]);
+                }
+            }
+            results = ar;
+            var i, len, result;
+            for (i = 0, len = results.length; i < len; ++i) {
+                result = results[i];
+                result.word = searchWords[result.id];
+                result.item = searchIndex[result.id] || {};
+            }
+            // if there are no results then return to default and fail
+            if (results.length === 0) {
+                return [];
+            }
+
+            results.sort(function(aaa, bbb) {
+                var a, b;
+
+                // sort by exact match with regard to the last word (mismatch goes later)
+                a = (aaa.word !== val);
+                b = (bbb.word !== val);
+                if (a !== b) { return a - b; }
+
+                // Sort by non levenshtein results and then levenshtein results by the distance
+                // (less changes required to match means higher rankings)
+                a = (aaa.lev);
+                b = (bbb.lev);
+                if (a !== b) { return a - b; }
+
+                // sort by crate (non-current crate goes later)
+                a = (aaa.item.crate !== window.currentCrate);
+                b = (bbb.item.crate !== window.currentCrate);
+                if (a !== b) { return a - b; }
+
+                // sort by item name length (longer goes later)
+                a = aaa.word.length;
+                b = bbb.word.length;
+                if (a !== b) { return a - b; }
+
+                // sort by item name (lexicographically larger goes later)
+                a = aaa.word;
+                b = bbb.word;
+                if (a !== b) { return (a > b ? +1 : -1); }
+
+                // sort by index of keyword in item name (no literal occurrence goes later)
+                a = (aaa.index < 0);
+                b = (bbb.index < 0);
+                if (a !== b) { return a - b; }
+                // (later literal occurrence, if any, goes later)
+                a = aaa.index;
+                b = bbb.index;
+                if (a !== b) { return a - b; }
+
+                // special precedence for primitive and keyword pages
+                if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) ||
+                    (aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) {
+                    return -1;
+                }
+                if ((bbb.item.ty === TY_PRIMITIVE && aaa.item.ty !== TY_PRIMITIVE) ||
+                    (bbb.item.ty === TY_KEYWORD && aaa.item.ty !== TY_KEYWORD)) {
+                    return 1;
+                }
+
+                // sort by description (no description goes later)
+                a = (aaa.item.desc === "");
+                b = (bbb.item.desc === "");
+                if (a !== b) { return a - b; }
+
+                // sort by type (later occurrence in `itemTypes` goes later)
+                a = aaa.item.ty;
+                b = bbb.item.ty;
+                if (a !== b) { return a - b; }
+
+                // sort by path (lexicographically larger goes later)
+                a = aaa.item.path;
+                b = bbb.item.path;
+                if (a !== b) { return (a > b ? +1 : -1); }
+
+                // que sera, sera
+                return 0;
+            });
+
+            for (i = 0, len = results.length; i < len; ++i) {
+                var result = results[i];
+
+                // this validation does not make sense when searching by types
+                if (result.dontValidate) {
+                    continue;
+                }
+                var name = result.item.name.toLowerCase(),
+                    path = result.item.path.toLowerCase(),
+                    parent = result.item.parent;
+
+                if (isType !== true &&
+                    validateResult(name, path, split, parent) === false)
+                {
+                    result.id = -1;
+                }
+            }
+            return transformResults(results);
+        }
+
+        function extractGenerics(val) {
+            val = val.toLowerCase();
+            if (val.indexOf("<") !== -1) {
+                var values = val.substring(val.indexOf("<") + 1, val.lastIndexOf(">"));
+                return {
+                    name: val.substring(0, val.indexOf("<")),
+                    generics: values.split(/\s*,\s*/),
+                };
+            }
+            return {
+                name: val,
+                generics: [],
+            };
+        }
+
+        function getObjectNameFromId(id) {
+            if (typeof id === "number") {
+                return searchIndex[id].name;
+            }
+            return id;
+        }
+
+        function checkGenerics(obj, val) {
+            // The names match, but we need to be sure that all generics kinda
+            // match as well.
+            var tmp_lev, elem_name;
+            if (val.generics.length > 0) {
+                if (obj.length > GENERICS_DATA &&
+                      obj[GENERICS_DATA].length >= val.generics.length) {
+                    var elems = Object.create(null);
+                    var elength = object[GENERICS_DATA].length;
+                    for (var x = 0; x < elength; ++x) {
+                        elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
+                    }
+                    var total = 0;
+                    var done = 0;
+                    // We need to find the type that matches the most to remove it in order
+                    // to move forward.
+                    var vlength = val.generics.length;
+                    for (x = 0; x < vlength; ++x) {
+                        var lev = MAX_LEV_DISTANCE + 1;
+                        var firstGeneric = getObjectNameFromId(val.generics[x]);
+                        var match = null;
+                        if (elems[firstGeneric]) {
+                            match = firstGeneric;
+                            lev = 0;
+                        } else {
+                            for (elem_name in elems) {
+                                tmp_lev = levenshtein(elem_name, firstGeneric);
+                                if (tmp_lev < lev) {
+                                    lev = tmp_lev;
+                                    match = elem_name;
+                                }
+                            }
+                        }
+                        if (match !== null) {
+                            elems[match] -= 1;
+                            if (elems[match] == 0) {
+                                delete elems[match];
+                            }
+                            total += lev;
+                            done += 1;
+                        } else {
+                            return MAX_LEV_DISTANCE + 1;
+                        }
+                    }
+                    return Math.ceil(total / done);
+                }
+            }
+            return MAX_LEV_DISTANCE + 1;
+        }
+
+        // Check for type name and type generics (if any).
+        function checkType(obj, val, literalSearch) {
+            var lev_distance = MAX_LEV_DISTANCE + 1;
+            var len, x, firstGeneric;
+            if (obj[NAME] === val.name) {
+                if (literalSearch === true) {
+                    if (val.generics && val.generics.length !== 0) {
+                        if (obj.length > GENERICS_DATA &&
+                              obj[GENERICS_DATA].length >= val.generics.length) {
+                            var elems = Object.create(null);
+                            len = obj[GENERICS_DATA].length;
+                            for (x = 0; x < len; ++x) {
+                                elems[getObjectNameFromId(obj[GENERICS_DATA][x])] += 1;
+                            }
+
+                            var allFound = true;
+                            len = val.generics.length;
+                            for (x = 0; x < len; ++x) {
+                                firstGeneric = getObjectNameFromId(val.generics[x]);
+                                if (elems[firstGeneric]) {
+                                    elems[firstGeneric] -= 1;
+                                } else {
+                                    allFound = false;
+                                    break;
+                                }
+                            }
+                            if (allFound === true) {
+                                return true;
+                            }
+                        } else {
+                            return false;
+                        }
+                    }
+                    return true;
+                }
+                // If the type has generics but don't match, then it won't return at this point.
+                // Otherwise, `checkGenerics` will return 0 and it'll return.
+                if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
+                    var tmp_lev = checkGenerics(obj, val);
+                    if (tmp_lev <= MAX_LEV_DISTANCE) {
+                        return tmp_lev;
+                    }
+                } else {
+                    return 0;
+                }
+            }
+            // Names didn't match so let's check if one of the generic types could.
+            if (literalSearch === true) {
+                 if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
+                    return obj[GENERICS_DATA].some(
+                        function(name) {
+                            return name === val.name;
+                        });
+                }
+                return false;
+            }
+            lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance);
+            if (lev_distance <= MAX_LEV_DISTANCE) {
+                // The generics didn't match but the name kinda did so we give it
+                // a levenshtein distance value that isn't *this* good so it goes
+                // into the search results but not too high.
+                lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2);
+            } else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
+                // We can check if the type we're looking for is inside the generics!
+                var olength = obj[GENERICS_DATA].length;
+                for (x = 0; x < olength; ++x) {
+                    lev_distance = Math.min(levenshtein(obj[GENERICS_DATA][x], val.name),
+                                            lev_distance);
+                }
+            }
+            // Now whatever happens, the returned distance is "less good" so we should mark it
+            // as such, and so we add 1 to the distance to make it "less good".
+            return lev_distance + 1;
+        }
+
+        function findArg(obj, val, literalSearch, typeFilter) {
+            var lev_distance = MAX_LEV_DISTANCE + 1;
+
+            if (obj && obj.type && obj.type[INPUTS_DATA] && obj.type[INPUTS_DATA].length > 0) {
+                var length = obj.type[INPUTS_DATA].length;
+                for (var i = 0; i < length; i++) {
+                    var tmp = obj.type[INPUTS_DATA][i];
+                    if (typePassesFilter(typeFilter, tmp[1]) === false) {
+                        continue;
+                    }
+                    tmp = checkType(tmp, val, literalSearch);
+                    if (literalSearch === true) {
+                        if (tmp === true) {
+                            return true;
+                        }
+                        continue;
+                    }
+                    lev_distance = Math.min(tmp, lev_distance);
+                    if (lev_distance === 0) {
+                        return 0;
+                    }
+                }
+            }
+            return literalSearch === true ? false : lev_distance;
+        }
+
+        function checkReturned(obj, val, literalSearch, typeFilter) {
+            var lev_distance = MAX_LEV_DISTANCE + 1;
+
+            if (obj && obj.type && obj.type.length > OUTPUT_DATA) {
+                var ret = obj.type[OUTPUT_DATA];
+                if (typeof ret[0] === "string") {
+                    ret = [ret];
+                }
+                for (var x = 0, len = ret.length; x < len; ++x) {
+                    var tmp = ret[x];
+                    if (typePassesFilter(typeFilter, tmp[1]) === false) {
+                        continue;
+                    }
+                    tmp = checkType(tmp, val, literalSearch);
+                    if (literalSearch === true) {
+                        if (tmp === true) {
+                            return true;
+                        }
+                        continue;
+                    }
+                    lev_distance = Math.min(tmp, lev_distance);
+                    if (lev_distance === 0) {
+                        return 0;
+                    }
+                }
+            }
+            return literalSearch === true ? false : lev_distance;
+        }
+
+        function checkPath(contains, lastElem, ty) {
+            if (contains.length === 0) {
+                return 0;
+            }
+            var ret_lev = MAX_LEV_DISTANCE + 1;
+            var path = ty.path.split("::");
+
+            if (ty.parent && ty.parent.name) {
+                path.push(ty.parent.name.toLowerCase());
+            }
+
+            var length = path.length;
+            var clength = contains.length;
+            if (clength > length) {
+                return MAX_LEV_DISTANCE + 1;
+            }
+            for (var i = 0; i < length; ++i) {
+                if (i + clength > length) {
+                    break;
+                }
+                var lev_total = 0;
+                var aborted = false;
+                for (var x = 0; x < clength; ++x) {
+                    var lev = levenshtein(path[i + x], contains[x]);
+                    if (lev > MAX_LEV_DISTANCE) {
+                        aborted = true;
+                        break;
+                    }
+                    lev_total += lev;
+                }
+                if (aborted === false) {
+                    ret_lev = Math.min(ret_lev, Math.round(lev_total / clength));
+                }
+            }
+            return ret_lev;
+        }
+
+        function typePassesFilter(filter, type) {
+            // No filter
+            if (filter <= NO_TYPE_FILTER) return true;
+
+            // Exact match
+            if (filter === type) return true;
+
+            // Match related items
+            var name = itemTypes[type];
+            switch (itemTypes[filter]) {
+                case "constant":
+                    return name === "associatedconstant";
+                case "fn":
+                    return name === "method" || name === "tymethod";
+                case "type":
+                    return name === "primitive" || name === "associatedtype";
+                case "trait":
+                    return name === "traitalias";
+            }
+
+            // No match
+            return false;
+        }
+
+        function createAliasFromItem(item) {
+            return {
+                crate: item.crate,
+                name: item.name,
+                path: item.path,
+                desc: item.desc,
+                ty: item.ty,
+                parent: item.parent,
+                type: item.type,
+                is_alias: true,
+            };
+        }
+
+        function handleAliases(ret, query, filterCrates) {
+            // We separate aliases and crate aliases because we want to have current crate
+            // aliases to be before the others in the displayed results.
+            var aliases = [];
+            var crateAliases = [];
+            if (filterCrates !== undefined) {
+                if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) {
+                    var query_aliases = ALIASES[filterCrates][query.search];
+                    var len = query_aliases.length;
+                    for (var i = 0; i < len; ++i) {
+                        aliases.push(createAliasFromItem(searchIndex[query_aliases[i]]));
+                    }
+                }
+            } else {
+                Object.keys(ALIASES).forEach(function(crate) {
+                    if (ALIASES[crate][query.search]) {
+                        var pushTo = crate === window.currentCrate ? crateAliases : aliases;
+                        var query_aliases = ALIASES[crate][query.search];
+                        var len = query_aliases.length;
+                        for (var i = 0; i < len; ++i) {
+                            pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]]));
+                        }
+                    }
+                });
+            }
+
+            var sortFunc = function(aaa, bbb) {
+                if (aaa.path < bbb.path) {
+                    return 1;
+                } else if (aaa.path === bbb.path) {
+                    return 0;
+                }
+                return -1;
+            };
+            crateAliases.sort(sortFunc);
+            aliases.sort(sortFunc);
+
+            var pushFunc = function(alias) {
+                alias.alias = query.raw;
+                var res = buildHrefAndPath(alias);
+                alias.displayPath = pathSplitter(res[0]);
+                alias.fullPath = alias.displayPath + alias.name;
+                alias.href = res[1];
+
+                ret.others.unshift(alias);
+                if (ret.others.length > MAX_RESULTS) {
+                    ret.others.pop();
+                }
+            };
+            onEach(aliases, pushFunc);
+            onEach(crateAliases, pushFunc);
+        }
+
+        // quoted values mean literal search
+        var nSearchWords = searchWords.length;
+        var i, it;
+        var ty;
+        var fullId;
+        var returned;
+        var in_args;
+        var len;
+        if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
+            val.charAt(val.length - 1) === val.charAt(0))
+        {
+            val = extractGenerics(val.substr(1, val.length - 2));
+            for (i = 0; i < nSearchWords; ++i) {
+                if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
+                    continue;
+                }
+                in_args = findArg(searchIndex[i], val, true, typeFilter);
+                returned = checkReturned(searchIndex[i], val, true, typeFilter);
+                ty = searchIndex[i];
+                fullId = ty.id;
+
+                if (searchWords[i] === val.name
+                    && typePassesFilter(typeFilter, searchIndex[i].ty)
+                    && results[fullId] === undefined) {
+                    results[fullId] = {
+                        id: i,
+                        index: -1,
+                        dontValidate: true,
+                    };
+                }
+                if (in_args === true && results_in_args[fullId] === undefined) {
+                    results_in_args[fullId] = {
+                        id: i,
+                        index: -1,
+                        dontValidate: true,
+                    };
+                }
+                if (returned === true && results_returned[fullId] === undefined) {
+                    results_returned[fullId] = {
+                        id: i,
+                        index: -1,
+                        dontValidate: true,
+                    };
+                }
+            }
+            query.inputs = [val];
+            query.output = val;
+            query.search = val;
+        // searching by type
+        } else if (val.search("->") > -1) {
+            var trimmer = function(s) { return s.trim(); };
+            var parts = val.split("->").map(trimmer);
+            var input = parts[0];
+            // sort inputs so that order does not matter
+            var inputs = input.split(",").map(trimmer).sort();
+            for (i = 0, len = inputs.length; i < len; ++i) {
+                inputs[i] = extractGenerics(inputs[i]);
+            }
+            var output = extractGenerics(parts[1]);
+
+            for (i = 0; i < nSearchWords; ++i) {
+                if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
+                    continue;
+                }
+                var type = searchIndex[i].type;
+                ty = searchIndex[i];
+                if (!type) {
+                    continue;
+                }
+                fullId = ty.id;
+
+                returned = checkReturned(ty, output, true, NO_TYPE_FILTER);
+                if (output.name === "*" || returned === true) {
+                    in_args = false;
+                    var is_module = false;
+
+                    if (input === "*") {
+                        is_module = true;
+                    } else {
+                        var allFound = true;
+                        for (it = 0, len = inputs.length; allFound === true && it < len; it++) {
+                            allFound = checkType(type, inputs[it], true);
+                        }
+                        in_args = allFound;
+                    }
+                    if (in_args === true) {
+                        results_in_args[fullId] = {
+                            id: i,
+                            index: -1,
+                            dontValidate: true,
+                        };
+                    }
+                    if (returned === true) {
+                        results_returned[fullId] = {
+                            id: i,
+                            index: -1,
+                            dontValidate: true,
+                        };
+                    }
+                    if (is_module === true) {
+                        results[fullId] = {
+                            id: i,
+                            index: -1,
+                            dontValidate: true,
+                        };
+                    }
+                }
+            }
+            query.inputs = inputs.map(function(input) {
+                return input.name;
+            });
+            query.output = output.name;
+        } else {
+            query.inputs = [val];
+            query.output = val;
+            query.search = val;
+            // gather matching search results up to a certain maximum
+            val = val.replace(/\_/g, "");
+
+            var valGenerics = extractGenerics(val);
+
+            var paths = valLower.split("::");
+            removeEmptyStringsFromArray(paths);
+            val = paths[paths.length - 1];
+            var contains = paths.slice(0, paths.length > 1 ? paths.length - 1 : 1);
+
+            var lev, j;
+            for (j = 0; j < nSearchWords; ++j) {
+                ty = searchIndex[j];
+                if (!ty || (filterCrates !== undefined && ty.crate !== filterCrates)) {
+                    continue;
+                }
+                var lev_add = 0;
+                if (paths.length > 1) {
+                    lev = checkPath(contains, paths[paths.length - 1], ty);
+                    if (lev > MAX_LEV_DISTANCE) {
+                        continue;
+                    } else if (lev > 0) {
+                        lev_add = lev / 10;
+                    }
+                }
+
+                returned = MAX_LEV_DISTANCE + 1;
+                in_args = MAX_LEV_DISTANCE + 1;
+                var index = -1;
+                // we want lev results to go lower than others
+                lev = MAX_LEV_DISTANCE + 1;
+                fullId = ty.id;
+
+                if (searchWords[j].indexOf(split[i]) > -1 ||
+                    searchWords[j].indexOf(val) > -1 ||
+                    ty.normalizedName.indexOf(val) > -1)
+                {
+                    // filter type: ... queries
+                    if (typePassesFilter(typeFilter, ty.ty) && results[fullId] === undefined) {
+                        index = ty.normalizedName.indexOf(val);
+                    }
+                }
+                if ((lev = levenshtein(searchWords[j], val)) <= MAX_LEV_DISTANCE) {
+                    if (typePassesFilter(typeFilter, ty.ty) === false) {
+                        lev = MAX_LEV_DISTANCE + 1;
+                    } else {
+                        lev += 1;
+                    }
+                }
+                in_args = findArg(ty, valGenerics, false, typeFilter);
+                returned = checkReturned(ty, valGenerics, false, typeFilter);
+
+                lev += lev_add;
+                if (lev > 0 && val.length > 3 && searchWords[j].indexOf(val) > -1) {
+                    if (val.length < 6) {
+                        lev -= 1;
+                    } else {
+                        lev = 0;
+                    }
+                }
+                if (in_args <= MAX_LEV_DISTANCE) {
+                    if (results_in_args[fullId] === undefined) {
+                        results_in_args[fullId] = {
+                            id: j,
+                            index: index,
+                            lev: in_args,
+                        };
+                    }
+                    results_in_args[fullId].lev =
+                        Math.min(results_in_args[fullId].lev, in_args);
+                }
+                if (returned <= MAX_LEV_DISTANCE) {
+                    if (results_returned[fullId] === undefined) {
+                        results_returned[fullId] = {
+                            id: j,
+                            index: index,
+                            lev: returned,
+                        };
+                    }
+                    results_returned[fullId].lev =
+                        Math.min(results_returned[fullId].lev, returned);
+                }
+                if (index !== -1 || lev <= MAX_LEV_DISTANCE) {
+                    if (index !== -1 && paths.length < 2) {
+                        lev = 0;
+                    }
+                    if (results[fullId] === undefined) {
+                        results[fullId] = {
+                            id: j,
+                            index: index,
+                            lev: lev,
+                        };
+                    }
+                    results[fullId].lev = Math.min(results[fullId].lev, lev);
+                }
+            }
+        }
+
+        var ret = {
+            "in_args": sortResults(results_in_args, true),
+            "returned": sortResults(results_returned, true),
+            "others": sortResults(results),
+        };
+        handleAliases(ret, query, filterCrates);
+        return ret;
+    }
+
+    /**
+     * Validate performs the following boolean logic. For example:
+     * "File::open" will give IF A PARENT EXISTS => ("file" && "open")
+     * exists in (name || path || parent) OR => ("file" && "open") exists in
+     * (name || path )
+     *
+     * This could be written functionally, but I wanted to minimise
+     * functions on stack.
+     *
+     * @param  {[string]} name   [The name of the result]
+     * @param  {[string]} path   [The path of the result]
+     * @param  {[string]} keys   [The keys to be used (["file", "open"])]
+     * @param  {[object]} parent [The parent of the result]
+     * @return {[boolean]}       [Whether the result is valid or not]
+     */
+    function validateResult(name, path, keys, parent) {
+        for (var i = 0, len = keys.length; i < len; ++i) {
+            // each check is for validation so we negate the conditions and invalidate
+            if (!(
+                // check for an exact name match
+                name.indexOf(keys[i]) > -1 ||
+                // then an exact path match
+                path.indexOf(keys[i]) > -1 ||
+                // next if there is a parent, check for exact parent match
+                (parent !== undefined && parent.name !== undefined &&
+                    parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
+                // lastly check to see if the name was a levenshtein match
+                levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    function getQuery(raw) {
+        var matches, type, query;
+        query = raw;
+
+        matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
+        if (matches) {
+            type = matches[1].replace(/^const$/, "constant");
+            query = query.substring(matches[0].length);
+        }
+
+        return {
+            raw: raw,
+            query: query,
+            type: type,
+            id: query + type
+        };
+    }
+
+    function initSearchNav() {
+        var hoverTimeout;
+
+        var click_func = function(e) {
+            var el = e.target;
+            // to retrieve the real "owner" of the event.
+            while (el.tagName !== "TR") {
+                el = el.parentNode;
+            }
+            var dst = e.target.getElementsByTagName("a");
+            if (dst.length < 1) {
+                return;
+            }
+            dst = dst[0];
+            if (window.location.pathname === dst.pathname) {
+                searchState.hideResults();
+                document.location.href = dst.href;
+            }
+        };
+        var mouseover_func = function(e) {
+            if (searchState.mouseMovedAfterSearch) {
+                var el = e.target;
+                // to retrieve the real "owner" of the event.
+                while (el.tagName !== "TR") {
+                    el = el.parentNode;
+                }
+                clearTimeout(hoverTimeout);
+                hoverTimeout = setTimeout(function() {
+                    onEachLazy(document.getElementsByClassName("search-results"), function(e) {
+                        onEachLazy(e.getElementsByClassName("result"), function(i_e) {
+                            removeClass(i_e, "highlighted");
+                        });
+                    });
+                    addClass(el, "highlighted");
+                }, 20);
+            }
+        };
+        onEachLazy(document.getElementsByClassName("search-results"), function(e) {
+            onEachLazy(e.getElementsByClassName("result"), function(i_e) {
+                i_e.onclick = click_func;
+                i_e.onmouseover = mouseover_func;
+            });
+        });
+
+        searchState.input.onkeydown = function(e) {
+            // "actives" references the currently highlighted item in each search tab.
+            // Each array in "actives" represents a tab.
+            var actives = [[], [], []];
+            // "current" is used to know which tab we're looking into.
+            var current = 0;
+            onEachLazy(document.getElementById("results").childNodes, function(e) {
+                onEachLazy(e.getElementsByClassName("highlighted"), function(h_e) {
+                    actives[current].push(h_e);
+                });
+                current += 1;
+            });
+
+            var currentTab = searchState.currentTab;
+            if (e.which === 38) { // up
+                if (e.ctrlKey) { // Going through result tabs.
+                    printTab(currentTab > 0 ? currentTab - 1 : 2);
+                } else {
+                    if (!actives[currentTab].length ||
+                        !actives[currentTab][0].previousElementSibling) {
+                        return;
+                    }
+                    addClass(actives[currentTab][0].previousElementSibling, "highlighted");
+                    removeClass(actives[currentTab][0], "highlighted");
+                }
+                e.preventDefault();
+            } else if (e.which === 40) { // down
+                if (e.ctrlKey) { // Going through result tabs.
+                    printTab(currentTab > 1 ? 0 : currentTab + 1);
+                } else if (!actives[currentTab].length) {
+                    var results = document.getElementById("results").childNodes;
+                    if (results.length > 0) {
+                        var res = results[currentTab].getElementsByClassName("result");
+                        if (res.length > 0) {
+                            addClass(res[0], "highlighted");
+                        }
+                    }
+                } else if (actives[currentTab][0].nextElementSibling) {
+                    addClass(actives[currentTab][0].nextElementSibling, "highlighted");
+                    removeClass(actives[currentTab][0], "highlighted");
+                }
+                e.preventDefault();
+            } else if (e.which === 13) { // return
+                if (actives[currentTab].length) {
+                    document.location.href =
+                        actives[currentTab][0].getElementsByTagName("a")[0].href;
+                }
+            } else if (e.which === 16) { // shift
+                // Does nothing, it's just to avoid losing "focus" on the highlighted element.
+            } else if (actives[currentTab].length > 0) {
+                removeClass(actives[currentTab][0], "highlighted");
+            }
+        };
+    }
+
+    function buildHrefAndPath(item) {
+        var displayPath;
+        var href;
+        var type = itemTypes[item.ty];
+        var name = item.name;
+        var path = item.path;
+
+        if (type === "mod") {
+            displayPath = path + "::";
+            href = window.rootPath + path.replace(/::/g, "/") + "/" +
+                   name + "/index.html";
+        } else if (type === "primitive" || type === "keyword") {
+            displayPath = "";
+            href = window.rootPath + path.replace(/::/g, "/") +
+                   "/" + type + "." + name + ".html";
+        } else if (type === "externcrate") {
+            displayPath = "";
+            href = window.rootPath + name + "/index.html";
+        } else if (item.parent !== undefined) {
+            var myparent = item.parent;
+            var anchor = "#" + type + "." + name;
+            var parentType = itemTypes[myparent.ty];
+            var pageType = parentType;
+            var pageName = myparent.name;
+
+            if (parentType === "primitive") {
+                displayPath = myparent.name + "::";
+            } else if (type === "structfield" && parentType === "variant") {
+                // Structfields belonging to variants are special: the
+                // final path element is the enum name.
+                var enumNameIdx = item.path.lastIndexOf("::");
+                var enumName = item.path.substr(enumNameIdx + 2);
+                path = item.path.substr(0, enumNameIdx);
+                displayPath = path + "::" + enumName + "::" + myparent.name + "::";
+                anchor = "#variant." + myparent.name + ".field." + name;
+                pageType = "enum";
+                pageName = enumName;
+            } else {
+                displayPath = path + "::" + myparent.name + "::";
+            }
+            href = window.rootPath + path.replace(/::/g, "/") +
+                   "/" + pageType +
+                   "." + pageName +
+                   ".html" + anchor;
+        } else {
+            displayPath = item.path + "::";
+            href = window.rootPath + item.path.replace(/::/g, "/") +
+                   "/" + type + "." + name + ".html";
+        }
+        return [displayPath, href];
+    }
+
+    function escape(content) {
+        var h1 = document.createElement("h1");
+        h1.textContent = content;
+        return h1.innerHTML;
+    }
+
+    function pathSplitter(path) {
+        var tmp = "<span>" + path.replace(/::/g, "::</span><span>");
+        if (tmp.endsWith("<span>")) {
+            return tmp.slice(0, tmp.length - 6);
+        }
+        return tmp;
+    }
+
+    function addTab(array, query, display) {
+        var extraStyle = "";
+        if (display === false) {
+            extraStyle = " style=\"display: none;\"";
+        }
+
+        var output = "";
+        var duplicates = {};
+        var length = 0;
+        if (array.length > 0) {
+            output = "<table class=\"search-results\"" + extraStyle + ">";
+
+            array.forEach(function(item) {
+                var name, type;
+
+                name = item.name;
+                type = itemTypes[item.ty];
+
+                if (item.is_alias !== true) {
+                    if (duplicates[item.fullPath]) {
+                        return;
+                    }
+                    duplicates[item.fullPath] = true;
+                }
+                length += 1;
+
+                output += "<tr class=\"" + type + " result\"><td>" +
+                          "<a href=\"" + item.href + "\">" +
+                          (item.is_alias === true ?
+                           ("<span class=\"alias\"><b>" + item.alias + " </b></span><span " +
+                              "class=\"grey\"><i>&nbsp;- see&nbsp;</i></span>") : "") +
+                          item.displayPath + "<span class=\"" + type + "\">" +
+                          name + "</span></a></td><td>" +
+                          "<a href=\"" + item.href + "\">" +
+                          "<span class=\"desc\">" + item.desc +
+                          "&nbsp;</span></a></td></tr>";
+            });
+            output += "</table>";
+        } else {
+            output = "<div class=\"search-failed\"" + extraStyle + ">No results :(<br/>" +
+                "Try on <a href=\"https://duckduckgo.com/?q=" +
+                encodeURIComponent("rust " + query.query) +
+                "\">DuckDuckGo</a>?<br/><br/>" +
+                "Or try looking in one of these:<ul><li>The <a " +
+                "href=\"https://doc.rust-lang.org/reference/index.html\">Rust Reference</a> " +
+                " for technical details about the language.</li><li><a " +
+                "href=\"https://doc.rust-lang.org/rust-by-example/index.html\">Rust By " +
+                "Example</a> for expository code examples.</a></li><li>The <a " +
+                "href=\"https://doc.rust-lang.org/book/index.html\">Rust Book</a> for " +
+                "introductions to language features and the language itself.</li><li><a " +
+                "href=\"https://docs.rs\">Docs.rs</a> for documentation of crates released on" +
+                " <a href=\"https://crates.io/\">crates.io</a>.</li></ul></div>";
+        }
+        return [output, length];
+    }
+
+    function makeTabHeader(tabNb, text, nbElems) {
+        if (searchState.currentTab === tabNb) {
+            return "<button class=\"selected\">" + text +
+                   " <div class=\"count\">(" + nbElems + ")</div></button>";
+        }
+        return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>";
+    }
+
+    function showResults(results) {
+        var search = searchState.outputElement();
+        if (results.others.length === 1
+            && getSettingValue("go-to-only-result") === "true"
+            // By default, the search DOM element is "empty" (meaning it has no children not
+            // text content). Once a search has been run, it won't be empty, even if you press
+            // ESC or empty the search input (which also "cancels" the search).
+            && (!search.firstChild || search.firstChild.innerText !== searchState.loadingText))
+        {
+            var elem = document.createElement("a");
+            elem.href = results.others[0].href;
+            elem.style.display = "none";
+            // For firefox, we need the element to be in the DOM so it can be clicked.
+            document.body.appendChild(elem);
+            elem.click();
+            return;
+        }
+        var query = getQuery(searchState.input.value);
+
+        currentResults = query.id;
+
+        var ret_others = addTab(results.others, query);
+        var ret_in_args = addTab(results.in_args, query, false);
+        var ret_returned = addTab(results.returned, query, false);
+
+        // Navigate to the relevant tab if the current tab is empty, like in case users search
+        // for "-> String". If they had selected another tab previously, they have to click on
+        // it again.
+        var currentTab = searchState.currentTab;
+        if ((currentTab === 0 && ret_others[1] === 0) ||
+                (currentTab === 1 && ret_in_args[1] === 0) ||
+                (currentTab === 2 && ret_returned[1] === 0)) {
+            if (ret_others[1] !== 0) {
+                currentTab = 0;
+            } else if (ret_in_args[1] !== 0) {
+                currentTab = 1;
+            } else if (ret_returned[1] !== 0) {
+                currentTab = 2;
+            }
+        }
+
+        var output = "<h1>Results for " + escape(query.query) +
+            (query.type ? " (type: " + escape(query.type) + ")" : "") + "</h1>" +
+            "<div id=\"titles\">" +
+            makeTabHeader(0, "In Names", ret_others[1]) +
+            makeTabHeader(1, "In Parameters", ret_in_args[1]) +
+            makeTabHeader(2, "In Return Types", ret_returned[1]) +
+            "</div><div id=\"results\">" +
+            ret_others[0] + ret_in_args[0] + ret_returned[0] + "</div>";
+
+        search.innerHTML = output;
+        searchState.showResults(search);
+        initSearchNav();
+        var elems = document.getElementById("titles").childNodes;
+        elems[0].onclick = function() { printTab(0); };
+        elems[1].onclick = function() { printTab(1); };
+        elems[2].onclick = function() { printTab(2); };
+        printTab(currentTab);
+    }
+
+    function execSearch(query, searchWords, filterCrates) {
+        function getSmallest(arrays, positions, notDuplicates) {
+            var start = null;
+
+            for (var it = 0, len = positions.length; it < len; ++it) {
+                if (arrays[it].length > positions[it] &&
+                    (start === null || start > arrays[it][positions[it]].lev) &&
+                    !notDuplicates[arrays[it][positions[it]].fullPath]) {
+                    start = arrays[it][positions[it]].lev;
+                }
+            }
+            return start;
+        }
+
+        function mergeArrays(arrays) {
+            var ret = [];
+            var positions = [];
+            var notDuplicates = {};
+
+            for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) {
+                positions.push(0);
+            }
+            while (ret.length < MAX_RESULTS) {
+                var smallest = getSmallest(arrays, positions, notDuplicates);
+
+                if (smallest === null) {
+                    break;
+                }
+                for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) {
+                    if (arrays[x].length > positions[x] &&
+                            arrays[x][positions[x]].lev === smallest &&
+                            !notDuplicates[arrays[x][positions[x]].fullPath]) {
+                        ret.push(arrays[x][positions[x]]);
+                        notDuplicates[arrays[x][positions[x]].fullPath] = true;
+                        positions[x] += 1;
+                    }
+                }
+            }
+            return ret;
+        }
+
+        var queries = query.raw.split(",");
+        var results = {
+            "in_args": [],
+            "returned": [],
+            "others": [],
+        };
+
+        for (var i = 0, len = queries.length; i < len; ++i) {
+            query = queries[i].trim();
+            if (query.length !== 0) {
+                var tmp = execQuery(getQuery(query), searchWords, filterCrates);
+
+                results.in_args.push(tmp.in_args);
+                results.returned.push(tmp.returned);
+                results.others.push(tmp.others);
+            }
+        }
+        if (queries.length > 1) {
+            return {
+                "in_args": mergeArrays(results.in_args),
+                "returned": mergeArrays(results.returned),
+                "others": mergeArrays(results.others),
+            };
+        }
+        return {
+            "in_args": results.in_args[0],
+            "returned": results.returned[0],
+            "others": results.others[0],
+        };
+    }
+
+    function getFilterCrates() {
+        var elem = document.getElementById("crate-search");
+
+        if (elem && elem.value !== "All crates" && hasOwnProperty(rawSearchIndex, elem.value)) {
+            return elem.value;
+        }
+        return undefined;
+    }
+
+    function search(e, forced) {
+        var params = searchState.getQueryStringParams();
+        var query = getQuery(searchState.input.value.trim());
+
+        if (e) {
+            e.preventDefault();
+        }
+
+        if (query.query.length === 0) {
+            return;
+        }
+        if (forced !== true && query.id === currentResults) {
+            if (query.query.length > 0) {
+                searchState.putBackSearch(searchState.input);
+            }
+            return;
+        }
+
+        // Update document title to maintain a meaningful browser history
+        searchState.title = "Results for " + query.query + " - Rust";
+
+        // Because searching is incremental by character, only the most
+        // recent search query is added to the browser history.
+        if (searchState.browserSupportsHistoryApi()) {
+            var newURL = getNakedUrl() + "?search=" + encodeURIComponent(query.raw) +
+                window.location.hash;
+            if (!history.state && !params.search) {
+                history.pushState(query, "", newURL);
+            } else {
+                history.replaceState(query, "", newURL);
+            }
+        }
+
+        var filterCrates = getFilterCrates();
+        showResults(execSearch(query, index, filterCrates));
+    }
+
+    function buildIndex(rawSearchIndex) {
+        searchIndex = [];
+        var searchWords = [];
+        var i, word;
+        var currentIndex = 0;
+        var id = 0;
+
+        for (var crate in rawSearchIndex) {
+            if (!hasOwnProperty(rawSearchIndex, crate)) { continue; }
+
+            var crateSize = 0;
+
+            searchWords.push(crate);
+            var normalizedName = crate.indexOf("_") === -1
+                ? crate
+                : crate.replace(/_/g, "");
+            // This object should have exactly the same set of fields as the "row"
+            // object defined below. Your JavaScript runtime will thank you.
+            // https://mathiasbynens.be/notes/shapes-ics
+            var crateRow = {
+                crate: crate,
+                ty: 1, // == ExternCrate
+                name: crate,
+                path: "",
+                desc: rawSearchIndex[crate].doc,
+                parent: undefined,
+                type: null,
+                id: id,
+                normalizedName: normalizedName,
+            };
+            id += 1;
+            searchIndex.push(crateRow);
+            currentIndex += 1;
+
+            // an array of (Number) item types
+            var itemTypes = rawSearchIndex[crate].t;
+            // an array of (String) item names
+            var itemNames = rawSearchIndex[crate].n;
+            // an array of (String) full paths (or empty string for previous path)
+            var itemPaths = rawSearchIndex[crate].q;
+            // an array of (String) descriptions
+            var itemDescs = rawSearchIndex[crate].d;
+            // an array of (Number) the parent path index + 1 to `paths`, or 0 if none
+            var itemParentIdxs = rawSearchIndex[crate].i;
+            // an array of (Object | null) the type of the function, if any
+            var itemFunctionSearchTypes = rawSearchIndex[crate].f;
+            // an array of [(Number) item type,
+            //              (String) name]
+            var paths = rawSearchIndex[crate].p;
+            // a array of [(String) alias name
+            //             [Number] index to items]
+            var aliases = rawSearchIndex[crate].a;
+
+            // convert `rawPaths` entries into object form
+            var len = paths.length;
+            for (i = 0; i < len; ++i) {
+                paths[i] = {ty: paths[i][0], name: paths[i][1]};
+            }
+
+            // convert `item*` into an object form, and construct word indices.
+            //
+            // before any analysis is performed lets gather the search terms to
+            // search against apart from the rest of the data.  This is a quick
+            // operation that is cached for the life of the page state so that
+            // all other search operations have access to this cached data for
+            // faster analysis operations
+            len = itemTypes.length;
+            var lastPath = "";
+            for (i = 0; i < len; ++i) {
+                // This object should have exactly the same set of fields as the "crateRow"
+                // object defined above.
+                if (typeof itemNames[i] === "string") {
+                    word = itemNames[i].toLowerCase();
+                    searchWords.push(word);
+                } else {
+                    word = "";
+                    searchWords.push("");
+                }
+                var normalizedName = word.indexOf("_") === -1
+                    ? word
+                    : word.replace(/_/g, "");
+                var row = {
+                    crate: crate,
+                    ty: itemTypes[i],
+                    name: itemNames[i],
+                    path: itemPaths[i] ? itemPaths[i] : lastPath,
+                    desc: itemDescs[i],
+                    parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
+                    type: itemFunctionSearchTypes[i],
+                    id: id,
+                    normalizedName: normalizedName,
+                };
+                id += 1;
+                searchIndex.push(row);
+                lastPath = row.path;
+                crateSize += 1;
+            }
+
+            if (aliases) {
+                ALIASES[crate] = {};
+                var j, local_aliases;
+                for (var alias_name in aliases) {
+                    if (!aliases.hasOwnProperty(alias_name)) { continue; }
+
+                    if (!ALIASES[crate].hasOwnProperty(alias_name)) {
+                        ALIASES[crate][alias_name] = [];
+                    }
+                    local_aliases = aliases[alias_name];
+                    for (j = 0, len = local_aliases.length; j < len; ++j) {
+                        ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex);
+                    }
+                }
+            }
+            currentIndex += crateSize;
+        }
+        return searchWords;
+    }
+
+    function registerSearchEvents() {
+        var searchAfter500ms = function() {
+            searchState.clearInputTimeout();
+            if (searchState.input.value.length === 0) {
+                if (searchState.browserSupportsHistoryApi()) {
+                    history.replaceState("", window.currentCrate + " - Rust",
+                        getNakedUrl() + window.location.hash);
+                }
+                searchState.hideResults();
+            } else {
+                searchState.timeout = setTimeout(search, 500);
+            }
+        };
+        searchState.input.onkeyup = searchAfter500ms;
+        searchState.input.oninput = searchAfter500ms;
+        document.getElementsByClassName("search-form")[0].onsubmit = function(e) {
+            e.preventDefault();
+            searchState.clearInputTimeout();
+            search();
+        };
+        searchState.input.onchange = function(e) {
+            if (e.target !== document.activeElement) {
+                // To prevent doing anything when it's from a blur event.
+                return;
+            }
+            // Do NOT e.preventDefault() here. It will prevent pasting.
+            searchState.clearInputTimeout();
+            // zero-timeout necessary here because at the time of event handler execution the
+            // pasted content is not in the input field yet. Shouldn’t make any difference for
+            // change, though.
+            setTimeout(search, 0);
+        };
+        searchState.input.onpaste = searchState.input.onchange;
+
+        var selectCrate = document.getElementById("crate-search");
+        if (selectCrate) {
+            selectCrate.onchange = function() {
+                updateLocalStorage("rustdoc-saved-filter-crate", selectCrate.value);
+                search(undefined, true);
+            };
+        }
+
+        // Push and pop states are used to add search results to the browser
+        // history.
+        if (searchState.browserSupportsHistoryApi()) {
+            // Store the previous <title> so we can revert back to it later.
+            var previousTitle = document.title;
+
+            window.addEventListener("popstate", function(e) {
+                var params = searchState.getQueryStringParams();
+                // Revert to the previous title manually since the History
+                // API ignores the title parameter.
+                document.title = previousTitle;
+                // When browsing forward to search results the previous
+                // search will be repeated, so the currentResults are
+                // cleared to ensure the search is successful.
+                currentResults = null;
+                // Synchronize search bar with query string state and
+                // perform the search. This will empty the bar if there's
+                // nothing there, which lets you really go back to a
+                // previous state with nothing in the bar.
+                if (params.search && params.search.length > 0) {
+                    searchState.input.value = params.search;
+                    // Some browsers fire "onpopstate" for every page load
+                    // (Chrome), while others fire the event only when actually
+                    // popping a state (Firefox), which is why search() is
+                    // called both here and at the end of the startSearch()
+                    // function.
+                    search(e);
+                } else {
+                    searchState.input.value = "";
+                    // When browsing back from search results the main page
+                    // visibility must be reset.
+                    searchState.hideResults();
+                }
+            });
+        }
+
+        // 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
+        // the doc page, rather than the search that should overlay it.
+        // This was an interaction between the back-forward cache and our handlers
+        // that try to sync state between the URL and the search input. To work around it,
+        // do a small amount of re-init on page show.
+        window.onpageshow = function(){
+            var qSearch = searchState.getQueryStringParams().search;
+            if (searchState.input.value === "" && qSearch) {
+                searchState.input.value = qSearch;
+            }
+            search();
+        };
+    }
+
+    index = buildIndex(rawSearchIndex);
+    registerSearchEvents();
+    // If there's a search term in the URL, execute the search now.
+    if (searchState.getQueryStringParams().search) {
+        search();
+    }
+};
+
+if (window.searchIndex !== undefined) {
+  initSearch(window.searchIndex);
+}
+
+})();
diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs
index b3ac865d55e..2b73bd5d52e 100644
--- a/src/librustdoc/html/static_files.rs
+++ b/src/librustdoc/html/static_files.rs
@@ -24,6 +24,9 @@ crate static NORMALIZE_CSS: &str = include_str!("static/normalize.css");
 /// including search behavior and docblock folding, among others.
 crate static MAIN_JS: &str = include_str!("static/main.js");
 
+/// The file contents of `search.js`, which contains the search behavior.
+crate static SEARCH_JS: &str = include_str!("static/search.js");
+
 /// The file contents of `settings.js`, which contains the JavaScript used to handle the settings
 /// page.
 crate static SETTINGS_JS: &str = include_str!("static/settings.js");
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index a551a97bda5..e583bd225a9 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -246,7 +246,7 @@ function lookForEntry(entry, data) {
     return null;
 }
 
-function loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate) {
+function loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate) {
     if (searchIndex[searchIndex.length - 1].length === 0) {
         searchIndex.pop();
     }
@@ -270,9 +270,9 @@ function loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate) {
     ALIASES = {};
     finalJS += 'window = { "currentCrate": "' + crate + '", rootPath: "../" };\n';
     finalJS += loadThings(["hasOwnProperty", "onEach"], 'function', extractFunction, storageJs);
-    finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, mainJs);
-    finalJS += loadThings(variablesToLoad, 'variable', extractVariable, mainJs);
-    finalJS += loadThings(functionsToLoad, 'function', extractFunction, mainJs);
+    finalJS += loadThings(arraysToLoad, 'array', extractArrayVariable, searchJs);
+    finalJS += loadThings(variablesToLoad, 'variable', extractVariable, searchJs);
+    finalJS += loadThings(functionsToLoad, 'function', extractFunction, searchJs);
 
     var loaded = loadContent(finalJS);
     var index = loaded.buildIndex(searchIndex.rawSearchIndex);
@@ -382,12 +382,12 @@ function runChecks(testFile, loaded, index) {
 }
 
 function load_files(doc_folder, resource_suffix, crate) {
-    var mainJs = readFile(path.join(doc_folder, "main" + resource_suffix + ".js"));
+    var searchJs = readFile(path.join(doc_folder, "search" + resource_suffix + ".js"));
     var storageJs = readFile(path.join(doc_folder, "storage" + resource_suffix + ".js"));
     var searchIndex = readFile(
         path.join(doc_folder, "search-index" + resource_suffix + ".js")).split("\n");
 
-    return loadMainJsAndIndex(mainJs, searchIndex, storageJs, crate);
+    return loadSearchJsAndIndex(searchJs, searchIndex, storageJs, crate);
 }
 
 function showHelp() {