mirror of
https://github.com/huhu/rust-search-extension
synced 2024-11-15 08:07:08 +00:00
Add CrateSearch code
This commit is contained in:
parent
591640129d
commit
e945c3cd29
2 changed files with 113 additions and 0 deletions
111
extension/crate-search.js
Normal file
111
extension/crate-search.js
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prototype function to perform levenshtein contain search.
|
||||||
|
String.prototype.levenshteinContains = function(keyword) {
|
||||||
|
let len = keyword.length;
|
||||||
|
if (this === keyword) return true;
|
||||||
|
|
||||||
|
for (let i = 0; i <= this.length - len; i++) {
|
||||||
|
if (levenshtein(this.substring(i, len), keyword) <= 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
function cleanMinifiedUrl(rawUrl) {
|
||||||
|
if (rawUrl === null) return null;
|
||||||
|
return "https://" + rawUrl
|
||||||
|
.replace("D/", "docs.rs/")
|
||||||
|
.replace("C/", "crates.io/")
|
||||||
|
.replace("O/", "github.io/")
|
||||||
|
.replace("G/", "github.com/");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean the raw crate index.
|
||||||
|
function cleanCrateIndex(rawCrateIndex) {
|
||||||
|
let crateIndex = {};
|
||||||
|
for (let [crateId, crate] of Object.entries(rawCrateIndex)) {
|
||||||
|
crateIndex[crateId] = {
|
||||||
|
id: crate.i,
|
||||||
|
description: crate.d,
|
||||||
|
documentation: cleanMinifiedUrl(crate.o),
|
||||||
|
homepage: cleanMinifiedUrl(crate.h),
|
||||||
|
repository: cleanMinifiedUrl(crate.r),
|
||||||
|
downloads: crate.x,
|
||||||
|
version: crate.v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crateIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CrateSearch(crateIndex) {
|
||||||
|
this.crateIndex = cleanCrateIndex(crateIndex);
|
||||||
|
this.crateIds = Object.keys(crateIndex).sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform prefix levenshtein search.
|
||||||
|
* @param keyword the keyword to search against.
|
||||||
|
* @param limit the max result length, default is 5.
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
CrateSearch.prototype.search = function(keyword, limit = 5) {
|
||||||
|
keyword = keyword.replace(/[-_]/ig, "");
|
||||||
|
let result = [];
|
||||||
|
for (let rawCrateId of this.crateIds) {
|
||||||
|
let crateId = rawCrateId.replace(/[-_]/ig, "");
|
||||||
|
if (crateId.length < keyword.length) continue;
|
||||||
|
|
||||||
|
let index = crateId.indexOf(keyword);
|
||||||
|
if (index !== -1) {
|
||||||
|
result.push({
|
||||||
|
id: rawCrateId,
|
||||||
|
matchIndex: index,
|
||||||
|
});
|
||||||
|
} else if (keyword.length >= 3 && crateId.levenshteinContains(keyword)) {
|
||||||
|
result.push({
|
||||||
|
id: rawCrateId,
|
||||||
|
matchIndex: 999, // Levenshtein contain result always has lowest matchIndex.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sort the result, the lower matchIndex and length, the higher rank.
|
||||||
|
return result.sort((a, b) => {
|
||||||
|
if (a.matchIndex === b.matchIndex) {
|
||||||
|
return a.id.length - b.id.length;
|
||||||
|
}
|
||||||
|
return a.matchIndex - b.matchIndex;
|
||||||
|
})
|
||||||
|
.slice(0, limit)
|
||||||
|
.map(item => {
|
||||||
|
return this.crateIndex[item.id];
|
||||||
|
});
|
||||||
|
};
|
|
@ -127,6 +127,8 @@ function buildQuery(raw) {
|
||||||
var matches, type, query;
|
var matches, type, query;
|
||||||
query = raw;
|
query = raw;
|
||||||
|
|
||||||
|
// let query = "fn:unwrap";
|
||||||
|
// then the matches is ["fn:", "fn", index: 0, input: "fn:unwrap", groups: undefined]
|
||||||
matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
|
matches = query.match(/^(fn|mod|struct|enum|trait|type|const|macro)\s*:\s*/i);
|
||||||
if (matches) {
|
if (matches) {
|
||||||
type = matches[1].replace(/^const$/, 'constant');
|
type = matches[1].replace(/^const$/, 'constant');
|
||||||
|
|
Loading…
Reference in a new issue