Repository: huhu/rust-search-extension (language: rust)

Issue:
Indexing is broken in the latest version
When I do `rs @some_indexed_crate query` the search works fine, but the search results are all broken URLs.

Screenshot examples for query `rs @teloxide Update` (ALL suggested urls are broken):
![2024-03-05_13-30](https://github.com/huhu/rust-search-extension/assets/38225716/aae6692b-e800-499d-b29f-140fbe21cad9)
![2024-03-05_13-30_1](https://github.com/huhu/rust-search-extension/assets/38225716/1cf30ea2-74e5-4912-8d21-483b1e38e98c)

A correct result would be:

![2024-03-05_13-31](https://github.com/huhu/rust-search-extension/assets/38225716/9a911673-408e-4bc2-b452-4dcf0a5eb26d)

Extension version: 1.13.0
Browser: firefox 122.0.1


Discussion / hints:
Bisecting reveals that 4cca580 is the first bad commit. This seems like a `rustdoc` version compatibility issue considering that indexing works well for the latest `tokio` doc.
Quick notes: One possible solution for this is to use the `itemTypes` provided by the `search-*.js`. Another is to parse the date in the filename `search-index-*.js` and opt for the new or old `itemTypes` based on the date.

Fix patch:
diff --git a/extension/script/add-search-index.js b/extension/script/add-search-index.js
index 10f6c087..280339ce 100644
--- a/extension/script/add-search-index.js
+++ b/extension/script/add-search-index.js
@@ -12,13 +12,29 @@
             if (crateVersion === 'latest') {
                 crateVersion = parseCrateVersionFromDOM();
             }
+
+            let searchIndex = getSearchIndex();
+
+            // `itemTypes` was reordered in rust-lang/rust@28f17d97a,
+            // which first shipped in rustc 1.76.0-nightly (1e9dda77b 2023-11-22),
+            // preceded by rustc 1.76.0-nightly (2f8d81f9d 2023-11-21).
+            //
+            // Mark each index item as using old `itemTypes` if no rustdoc version
+            // is available or if the version date is less than 2023-11-22.
+            let date = getRustdocVersionDate();
+            if (!date || date < "2023-11-22") {
+                for (let indexItem of Object.values(searchIndex || {})) {
+                    indexItem.oldItemTypes = true;
+                }
+            }
+
             window.postMessage({
                 direction: "rust-search-extension:docs.rs",
                 message: {
                     libName,
                     crateName,
                     crateVersion,
-                    searchIndex: getSearchIndex(),
+                    searchIndex,
                 },
             }, "*");
         } else if (location.pathname.startsWith("/nightly/nightly-rustc/") &&
@@ -96,6 +112,10 @@
         }
     }
 
+    function getRustdocVersionDate() {
+        return getVar("rustdoc-version")?.match(/\d{4}-\d{2}-\d{2}/)?.[0];
+    }
+
     // ======== Following function mirrored to librustdoc main.js ========
 
     // Get rustdoc variable from DOM.
diff --git a/extension/search/docs/base.js b/extension/search/docs/base.js
index ccb27a36..78534552 100644
--- a/extension/search/docs/base.js
+++ b/extension/search/docs/base.js
@@ -29,9 +29,18 @@ const itemTypes = [
     "traitalias", // 25
     "generic",
 ];
-// used for special search precedence
-const TY_PRIMITIVE = itemTypes.indexOf("primitive");
-const TY_KEYWORD = itemTypes.indexOf("keyword");
+
+// `itemTypes` was reordered in rust-lang/rust@28f17d97a,
+// we should upgrade the item type when the rustdoc version is
+// earlier than that commit, which is when `indexItem.oldItemTypes`
+// is set to `true` in script/add-search-index.js.
+//
+// Calculated by `oldItemTypes.map(ty => newItemTypes.indexOf(ty))`.
+const upgradeItemType = [
+    2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+    12, 13, 14, 15, 16, 1, 17, 18, 19, 20,
+    21, 0, 22, 23, 24, 25, 26
+];
 
 // Max levenshtein distance.
 const MAX_LEV_DISTANCE = 2;
@@ -124,7 +133,13 @@ class DocSearch {
                 // convert `paths` into an object form
                 for (let i = 0; i < paths.length; ++i) {
                     if (Array.isArray(paths[i])) {
-                        paths[i] = { ty: paths[i][0], name: paths[i][1] };
+                        let ty = paths[i][0];
+                        // See the comments on `upgradeItemType`.
+                        if (indexItem.oldItemTypes) {
+                            ty = upgradeItemType[ty];
+                        }
+
+                        paths[i] = { ty, name: paths[i][1] };
                     }
                 }
 
@@ -138,7 +153,17 @@ class DocSearch {
                 let len = itemTypes.length;
                 let lastPath = "";
                 for (let i = 0; i < len; ++i) {
-                    let ty = itemTypes[i];
+                    let rawTy = itemTypes[i];
+                    // `itemTypes` changed from number array to string since Rust 1.69,
+                    // we should compat both versions.
+                    // see this PR: https://github.com/rust-lang/rust/pull/108013
+                    let ty = typeof rawTy === 'string' ? rawTy.charCodeAt(0) - charA : rawTy;
+
+                    // See the comments on `upgradeItemType`.
+                    if (indexItem.oldItemTypes) {
+                        ty = upgradeItemType[ty];
+                    }
+
                     let path = lastPath;
                     // check if itemPaths is map
                     // Since Rust 1.70, the itemPaths has changed from array to map.
@@ -150,10 +175,7 @@ class DocSearch {
                     }
                     let row = {
                         crate: crateName,
-                        // itemTypes changed from number array to string since Rust 1.69,
-                        // we should compat both versions.
-                        // see this PR: https://github.com/rust-lang/rust/pull/108013 
-                        ty: typeof ty === 'string' ? itemTypes.charCodeAt(i) - charA : ty,
+                        ty,
                         name: itemNames[i],
                         path,
                         desc: itemDescs[i],
@@ -186,7 +208,13 @@ class DocSearch {
                 // convert `paths` into an object form
                 for (let i = 0; i < paths.length; ++i) {
                     if (Array.isArray(paths[i])) {
-                        paths[i] = { ty: paths[i][0], name: paths[i][1] };
+                        let ty = paths[i][0];
+                        // See the comments on `upgradeItemType`.
+                        if (indexItem.oldItemTypes) {
+                            ty = upgradeItemType[ty];
+                        }
+
+                        paths[i] = { ty, name: paths[i][1] };
                     }
                 }
 
@@ -201,9 +229,16 @@ class DocSearch {
                 let lastPath = "";
                 for (let i = 0; i < len; ++i) {
                     const rawRow = items[i];
+
+                    let ty = rawRow[0];
+                    // See the comments on `upgradeItemType`.
+                    if (indexItem.oldItemTypes) {
+                        ty = upgradeItemType[ty];
+                    }
+
                     let row = {
                         crate: crateName,
-                        ty: rawRow[0],
+                        ty,
                         name: rawRow[1],
                         path: rawRow[2] || lastPath,
                         desc: rawRow[3],
@@ -479,16 +514,6 @@ class DocSearch {
                 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 === '');


Test patch: