about summary refs log tree commit diff
path: root/src/librustdoc/html/static/js
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/html/static/js')
-rw-r--r--src/librustdoc/html/static/js/externs.js19
-rw-r--r--src/librustdoc/html/static/js/main.js20
-rw-r--r--src/librustdoc/html/static/js/search.js87
-rw-r--r--src/librustdoc/html/static/js/storage.js43
4 files changed, 129 insertions, 40 deletions
diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js
index d24148b9556..8cebce7ae86 100644
--- a/src/librustdoc/html/static/js/externs.js
+++ b/src/librustdoc/html/static/js/externs.js
@@ -239,20 +239,27 @@ let FunctionType;
  * `doc` contains the description of the crate.
  *
  * `p` is a list of path/type pairs. It is used for parents and function parameters.
+ * The first item is the type, the second is the name, the third is the visible path (if any) and
+ * the fourth is the canonical path used for deduplication (if any).
+ *
+ * `r` is the canonical path used for deduplication of re-exported items.
+ * It is not used for associated items like methods (that's the fourth element
+ * of `p`) but is used for modules items like free functions.
  *
  * `c` is an array of item indices that are deprecated.
  * @typedef {{
  *   doc: string,
  *   a: Object,
  *   n: Array<string>,
- *   t: String,
+ *   t: string,
  *   d: Array<string>,
- *   q: Array<[Number, string]>,
- *   i: Array<Number>,
+ *   q: Array<[number, string]>,
+ *   i: Array<number>,
  *   f: string,
- *   p: Array<Object>,
- *   b: Array<[Number, String]>,
- *   c: Array<Number>
+ *   p: Array<[number, string] | [number, string, number] | [number, string, number, number]>,
+ *   b: Array<[number, String]>,
+ *   c: Array<number>,
+ *   r: Array<[number, number]>,
  * }}
  */
 let RawSearchIndexCrate;
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index ee7d19634b4..64c35660778 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -1798,31 +1798,15 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
         document.execCommand("copy");
         document.body.removeChild(el);
 
-        // There is always one children, but multiple childNodes.
-        but.children[0].style.display = "none";
-
-        let tmp;
-        if (but.childNodes.length < 2) {
-            tmp = document.createTextNode("✓");
-            but.appendChild(tmp);
-        } else {
-            onEachLazy(but.childNodes, e => {
-                if (e.nodeType === Node.TEXT_NODE) {
-                    tmp = e;
-                    return true;
-                }
-            });
-            tmp.textContent = "✓";
-        }
+        but.classList.add("clicked");
 
         if (reset_button_timeout !== null) {
             window.clearTimeout(reset_button_timeout);
         }
 
         function reset_button() {
-            tmp.textContent = "";
             reset_button_timeout = null;
-            but.children[0].style.display = "";
+            but.classList.remove("clicked");
         }
 
         reset_button_timeout = window.setTimeout(reset_button, 1000);
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 3daf1ad22de..76a6fc9008e 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -79,6 +79,7 @@ const longItemTypes = [
 
 // used for special search precedence
 const TY_GENERIC = itemTypes.indexOf("generic");
+const TY_IMPORT = itemTypes.indexOf("import");
 const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../";
 
 // Hard limit on how deep to recurse into generics when doing type-driven search.
@@ -785,6 +786,37 @@ function initSearch(rawSearchIndex) {
                 }
                 elems.push(makePrimitiveElement(name, { bindingName, generics }));
             }
+        } else if (parserState.userQuery[parserState.pos] === "&") {
+            if (parserState.typeFilter !== null && parserState.typeFilter !== "primitive") {
+                throw [
+                    "Invalid search type: primitive ",
+                    "&",
+                    " and ",
+                    parserState.typeFilter,
+                    " both specified",
+                ];
+            }
+            parserState.typeFilter = null;
+            parserState.pos += 1;
+            let c = parserState.userQuery[parserState.pos];
+            while (c === " " && parserState.pos < parserState.length) {
+                parserState.pos += 1;
+                c = parserState.userQuery[parserState.pos];
+            }
+            const generics = [];
+            if (parserState.userQuery.slice(parserState.pos, parserState.pos + 3) === "mut") {
+                generics.push(makePrimitiveElement("mut", { typeFilter: "keyword"}));
+                parserState.pos += 3;
+                c = parserState.userQuery[parserState.pos];
+            }
+            while (c === " " && parserState.pos < parserState.length) {
+                parserState.pos += 1;
+                c = parserState.userQuery[parserState.pos];
+            }
+            if (!isEndCharacter(c) && parserState.pos < parserState.length) {
+                getFilteredNextElem(query, parserState, generics, isInGenerics);
+            }
+            elems.push(makePrimitiveElement("reference", { generics }));
         } else {
             const isStringElem = parserState.userQuery[start] === "\"";
             // We handle the strings on their own mostly to make code easier to follow.
@@ -1324,14 +1356,23 @@ function initSearch(rawSearchIndex) {
                     obj.dist = result.dist;
                     const 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;
 
+                    // To be sure than it some items aren't considered as duplicate.
+                    obj.fullPath = res[2] + "|" + obj.ty;
                     if (duplicates.has(obj.fullPath)) {
                         continue;
                     }
+
+                    // Exports are specifically not shown if the items they point at
+                    // are already in the results.
+                    if (obj.ty === TY_IMPORT && duplicates.has(res[2])) {
+                        continue;
+                    }
+                    if (duplicates.has(res[2] + "|" + TY_IMPORT)) {
+                        continue;
+                    }
                     duplicates.add(obj.fullPath);
+                    duplicates.add(res[2]);
 
                     obj.href = res[1];
                     out.push(obj);
@@ -1454,16 +1495,7 @@ function initSearch(rawSearchIndex) {
                 return 0;
             });
 
-            const transformed = transformResults(result_list);
-            const descs = await Promise.all(transformed.map(result => {
-                return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ?
-                    "" :
-                    searchState.loadDesc(result);
-            }));
-            for (const [i, result] of transformed.entries()) {
-                result.desc = descs[i];
-            }
-            return transformed;
+            return transformResults(result_list);
         }
 
         /**
@@ -2085,6 +2117,7 @@ function initSearch(rawSearchIndex) {
                 path: item.path,
                 descShard: item.descShard,
                 descIndex: item.descIndex,
+                exactPath: item.exactPath,
                 ty: item.ty,
                 parent: item.parent,
                 type: item.type,
@@ -2506,6 +2539,16 @@ function initSearch(rawSearchIndex) {
             sorted_others,
             parsedQuery);
         handleAliases(ret, parsedQuery.original.replace(/"/g, ""), filterCrates, currentCrate);
+        await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => {
+            const descs = await Promise.all(list.map(result => {
+                return searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ?
+                    "" :
+                    searchState.loadDesc(result);
+            }));
+            for (const [i, result] of list.entries()) {
+                result.desc = descs[i];
+            }
+        }));
         if (parsedQuery.error !== null && ret.others.length !== 0) {
             // It means some doc aliases were found so let's "remove" the error!
             ret.query.error = null;
@@ -2538,6 +2581,7 @@ function initSearch(rawSearchIndex) {
         const type = itemTypes[item.ty];
         const name = item.name;
         let path = item.path;
+        let exactPath = item.exactPath;
 
         if (type === "mod") {
             displayPath = path + "::";
@@ -2559,6 +2603,7 @@ function initSearch(rawSearchIndex) {
             const parentType = itemTypes[myparent.ty];
             let pageType = parentType;
             let pageName = myparent.name;
+            exactPath = `${myparent.exactPath}::${myparent.name}`;
 
             if (parentType === "primitive") {
                 displayPath = myparent.name + "::";
@@ -2587,7 +2632,7 @@ function initSearch(rawSearchIndex) {
             href = ROOT_PATH + item.path.replace(/::/g, "/") +
                 "/" + type + "." + name + ".html";
         }
-        return [displayPath, href];
+        return [displayPath, href, `${exactPath}::${name}`];
     }
 
     function pathSplitter(path) {
@@ -2980,6 +3025,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                 id: pathIndex,
                 ty: TY_GENERIC,
                 path: null,
+                exactPath: null,
                 generics,
                 bindings,
             };
@@ -2989,6 +3035,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                 id: null,
                 ty: null,
                 path: null,
+                exactPath: null,
                 generics,
                 bindings,
             };
@@ -2998,6 +3045,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                 id: buildTypeMapIndex(item.name, isAssocType),
                 ty: item.ty,
                 path: item.path,
+                exactPath: item.exactPath,
                 generics,
                 bindings,
             };
@@ -3453,6 +3501,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                 path: "",
                 descShard,
                 descIndex,
+                exactPath: "",
+                desc: crateCorpus.doc,
                 parent: undefined,
                 type: null,
                 id,
@@ -3478,6 +3528,9 @@ ${item.displayPath}<span class="${type}">${name}</span>\
             // i.e. if indices 4 and 11 are present, but 5-10 and 12-13 are not present,
             // 5-10 will fall back to the path for 4 and 12-13 will fall back to the path for 11
             const itemPaths = new Map(crateCorpus.q);
+            // An array of [(Number) item index, (Number) path index]
+            // Used to de-duplicate inlined and re-exported stuff
+            const itemReexports = new Map(crateCorpus.r);
             // an array of (Number) the parent path index + 1 to `paths`, or 0 if none
             const itemParentIdxs = crateCorpus.i;
             // a map Number, string for impl disambiguators
@@ -3511,9 +3564,10 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                     path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath;
                     lastPath = path;
                 }
+                const exactPath = elem.length > 3 ? itemPaths.get(elem[3]) : path;
 
-                lowercasePaths.push({ty: ty, name: name.toLowerCase(), path: path});
-                paths[i] = {ty: ty, name: name, path: path};
+                lowercasePaths.push({ty, name: name.toLowerCase(), path, exactPath});
+                paths[i] = {ty, name, path, exactPath};
             }
 
             // convert `item*` into an object form, and construct word indices.
@@ -3572,6 +3626,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                     path,
                     descShard,
                     descIndex,
+                    exactPath: itemReexports.has(i) ? itemPaths.get(itemReexports.get(i)) : path,
                     parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
                     type,
                     id,
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index 73c543567c0..4a27ca92fff 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -239,3 +239,46 @@ window.addEventListener("pageshow", ev => {
         setTimeout(updateSidebarWidth, 0);
     }
 });
+
+// Custom elements are used to insert some JS-dependent features into Rustdoc,
+// because the [parser] runs the connected callback
+// synchronously. It needs to be added synchronously so that nothing below it
+// becomes visible until after it's done. Otherwise, you get layout jank.
+//
+// That's also why this is in storage.js and not main.js.
+//
+// [parser]: https://html.spec.whatwg.org/multipage/parsing.html
+class RustdocSearchElement extends HTMLElement {
+    constructor() {
+        super();
+    }
+    connectedCallback() {
+        const rootPath = getVar("root-path");
+        const currentCrate = getVar("current-crate");
+        this.innerHTML = `<nav class="sub">
+            <form class="search-form">
+                <span></span> <!-- This empty span is a hacky fix for Safari - See #93184 -->
+                <div id="sidebar-button" tabindex="-1">
+                    <a href="${rootPath}${currentCrate}/all.html" title="show sidebar"></a>
+                </div>
+                <input
+                    class="search-input"
+                    name="search"
+                    aria-label="Run search in the documentation"
+                    autocomplete="off"
+                    spellcheck="false"
+                    placeholder="Type ‘S’ or ‘/’ to search, ‘?’ for more options…"
+                    type="search">
+                <div id="help-button" tabindex="-1">
+                    <a href="${rootPath}help.html" title="help">?</a>
+                </div>
+                <div id="settings-menu" tabindex="-1">
+                    <a href="${rootPath}settings.html" title="settings">
+                        Settings
+                    </a>
+                </div>
+            </form>
+        </nav>`;
+    }
+}
+window.customElements.define("rustdoc-search", RustdocSearchElement);