Skip to content

Commit

Permalink
Support description shards (#285)
Browse files Browse the repository at this point in the history
* Load desc shards

* Add searchState compatible js

* Bump std search index

* Feat DescShardManager

* Integrate DocSearchV2

* Store desc shards into storage

* Remove legacy DocSearch

* Fix nightly search index

* Add std desc shards

* Split desc shards setter

* Compatible with libdocrust DocSearch for searchState

* Polish desc shards index format

* Load search.js to get descShards

* Store searchIndex as json array format

* Support source mode for std docs search

* Support add new crate's searchIndex

* Move loadScript to script/lib.js

* Check crate version compatibility in docs.rs
  • Loading branch information
Folyd authored Jun 23, 2024
1 parent 46afbd0 commit 3597c31
Show file tree
Hide file tree
Showing 19 changed files with 3,605 additions and 808 deletions.
2 changes: 1 addition & 1 deletion content-script-bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ export {
storage,
settings,
IndexSetter,
CrateDocManager
CrateDocManager,
}
2 changes: 1 addition & 1 deletion extension/content-script-bundle.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 15 additions & 14 deletions extension/crate-manager.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import storage from "./core/storage.js";
import IndexSetter from "./index-setter.js";

export default class CrateDocManager {
static async getCrates() {
Expand Down Expand Up @@ -30,7 +31,7 @@ export default class CrateDocManager {
let crate = Object.entries(crates).find(([_, { crateName }]) => crateName == name);
if (crate) {
let libName = crate[0];
return await storage.getItem(`@${libName}`);
return new Map(await storage.getItem(`@${libName}`));
} else {
return null;
}
Expand All @@ -43,26 +44,26 @@ export default class CrateDocManager {
//
// Here is the rule: https://docs.rs/{crateName}/{crateVersion}/{libName}
//
// Ensure `searchIndex` is a Object, not a Map.
static async addCrate({ libName, crateVersion, searchIndex, crateName }) {
if (searchIndex && libName in searchIndex) {
await storage.setItem(`@${libName}`, searchIndex);
let doc = searchIndex[libName]["doc"];
let crates = await CrateDocManager.getCrates();
if (libName in crates) {
// Don't override the time if the crate exists
crates[libName] = { version: crateVersion, doc, time: crates[libName].time, crateName };
} else {
crates[libName] = { version: crateVersion, doc, time: Date.now(), crateName };
}
await storage.setItem("crates", crates);
// The caller should ensure `searchIndex` is a Map, not a Object.
static async addCrate({ libName, crateVersion, crateTitle, searchIndex, crateName, descShards }) {
await storage.setItem(`@${libName}`, searchIndex);
let doc = crateTitle;
let crates = await CrateDocManager.getCrates();
if (libName in crates) {
// Don't override the time if the crate exists
crates[libName] = { version: crateVersion, doc, time: crates[libName].time, crateName };
} else {
crates[libName] = { version: crateVersion, doc, time: Date.now(), crateName };
}
await storage.setItem("crates", crates);
IndexSetter.setDescShards(libName, descShards);
}

static async removeCrate(name) {
let crates = await CrateDocManager.getCrates();
delete crates[name];
await storage.setItem("crates", crates);
await storage.removeItem(`@${name}`);
await storage.removeItem(`desc-shards-${name}`);
}
};
28 changes: 24 additions & 4 deletions extension/index-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import rfcsIndex from "./index/rfcs.js";
import rustcIndex from "./index/rustc.js";
import targetsIndex from "./index/targets.js";
import searchIndex from "./index/std-docs.js";
import stdDescShards from "./index/desc-shards/std.js";
import { mapping, crateIndex } from "./index/crates.js";
import storage from "./core/storage.js";
import IndexSetter from "./index-setter.js";
Expand All @@ -18,13 +19,32 @@ import IndexSetter from "./index-setter.js";

export default class IndexManager extends IndexSetter {
static async getStdStableIndex() {
// Convert default map searchIndex to Object since rust 1.76.0
return await storage.getItem('index-std-stable') || Object.fromEntries(searchIndex);
let index = await storage.getItem('index-std-stable');
if (index?.length > 0) {
return new Map(index);
} else {
return searchIndex;
}
}

static async getStdNightlyIndex() {
// Convert default map searchIndex to Object since rust 1.76.0
return await storage.getItem('index-std-nightly') || Object.fromEntries(searchIndex);
let index = await storage.getItem('index-std-nightly');
if (index?.length > 0) {
return new Map(index);
} else {
// Structure clone search index is required
return structuredClone(searchIndex);
}

}

static async getDescShards(crate) {
let descShards = await storage.getItem(`desc-shards-${crate}`);
if (descShards) {
return new Map(descShards);
} else {
return stdDescShards;
}
}

static async getBookIndex() {
Expand Down
6 changes: 6 additions & 0 deletions extension/index-setter.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ export default class IndexSetter {
storage.setItem('index-std-nightly', index);
}

static setDescShards(crate, shards) {
if (shards) {
storage.setItem(`desc-shards-${crate}`, shards);
}
}

static setBookIndex(index) {
storage.setItem('index-book', index);
}
Expand Down
2 changes: 2 additions & 0 deletions extension/index/desc-shards/std.js

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions extension/index/std-docs.js

Large diffs are not rendered by default.

56 changes: 31 additions & 25 deletions extension/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,13 @@ export default class RustSearchOmnibox {
lintSearcher,
commandManager,
}) {
// All dynamic setting items. Those items will been updated
// in chrome.storage.onchange listener callback.
let isOfflineMode = await settings.isOfflineMode;

function formatDoc(index, doc) {
let content = doc.href;
let description = doc.displayPath + `<match>${doc.name}</match>`;
if (doc.desc) {
description += ` - <dim>${Compat.escape(Compat.eliminateTags(doc.desc))}</dim>`;
}

if (doc.queryType === "s" || doc.queryType === "src") {
let url = new URL(doc.href);
url.search = "?mode=src";
content = url.toString();
description = `[Source code] ${description}`;
}
return { content, description };
}

Expand All @@ -51,21 +41,18 @@ export default class RustSearchOmnibox {
];
}

const docsSearchMixins = {
onSearch: (query) => {
return stdSearcher.search(query);
omnibox.bootstrap({
onSearch: async (query) => {
const result = await stdSearcher.search(query);
return result.others || [];
},
onFormat: formatDoc,
onAppend: async (query) => {
return [{
content: stdSearcher.getSearchUrl(query),
description: `Search Rust docs <match>${query}</match> on ${await settings.isOfflineMode ? "offline mode" : stdSearcher.getRootPath()}`,
content: await stdSearcher.getSearchUrl(query),
description: `Search Rust docs <match>${query}</match> on ${await settings.isOfflineMode ? "offline mode" : await stdSearcher.rootPath}`,
}];
},
};

omnibox.bootstrap({
...docsSearchMixins,
onEmptyNavigate: (content, disposition) => {
commandManager.handleCommandEnterEvent(content, disposition);
},
Expand Down Expand Up @@ -97,25 +84,44 @@ export default class RustSearchOmnibox {

omnibox.addRegexQueryEvent(/^s(?:rc)?:/i, {
name: "Source code",
...docsSearchMixins,
onSearch: async (query) => {
query = query.replace(/^s(?:rc)?:/i, "");
const result = await stdSearcher.search(query);
return result.others || [];
},
onFormat: (index, doc) => {
let { content, description } = formatDoc(index, doc);
let url = new URL(doc.href);
url.search = "?mode=src";
content = url.toString();
description = `[Source code] ${description}`;
return { content, description };
},
onAppend: async (query) => {
return [{
content: await stdSearcher.getSearchUrl(query),
description: `Search Rust docs <match>${query}</match> on ${await settings.isOfflineMode ? "offline mode" : await stdSearcher.rootPath}`,
}];
},
});

// Nightly std docs search
omnibox.addPrefixQueryEvent("/", {
name: "Nightly docs",
onSearch: (query) => {
onSearch: async (query) => {
query = query.replaceAll("/", "").trim();
return nightlySearcher.search(query);
const result = await nightlySearcher.search(query);
return result.others || [];
},
onFormat: (index, doc) => {
let { content, description } = formatDoc(index, doc);
return { content, description: '[Nightly] ' + description };
},
onAppend: (query) => {
onAppend: async (query) => {
query = query.replaceAll("/", "").trim();
return [{
content: nightlySearcher.getSearchUrl(query),
description: `Search nightly Rust docs <match>${query}</match> on ${nightlySearcher.getRootPath()}`,
content: await nightlySearcher.getSearchUrl(query),
description: `Search nightly Rust docs <match>${query}</match> on ${nightlySearcher.rootPath}`,
}];
},
});
Expand Down
9 changes: 2 additions & 7 deletions extension/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,8 @@ async function start(omnibox) {
}),
);

let stdSearcher = new DocSearch("std", await IndexManager.getStdStableIndex(), () => {
return isOfflineMode ? offlineDocPath : "https://doc.rust-lang.org/";
});
let nightlySearcher = new DocSearch("std", await IndexManager.getStdNightlyIndex(), () => {
// Nightly docs doesn't support offline mode yet.
return "https://doc.rust-lang.org/nightly/";
});
let nightlySearcher = new DocSearch("std", await IndexManager.getStdNightlyIndex(), "https://doc.rust-lang.org/nightly/");
let stdSearcher = new DocSearch("std", await IndexManager.getStdStableIndex(), isOfflineMode ? offlineDocPath : "https://doc.rust-lang.org/");

RustSearchOmnibox.run({
omnibox,
Expand Down
Loading

0 comments on commit 3597c31

Please sign in to comment.