Improved the search by including a levenshtein distance calculation which enables the docs search function to be more forgiving for spelling mistakes
This commit is contained in:
@@ -58,7 +58,7 @@
|
|||||||
}
|
}
|
||||||
$('#' + from)[0].scrollIntoView();
|
$('#' + from)[0].scrollIntoView();
|
||||||
$('.line-numbers span').removeClass('line-highlighted');
|
$('.line-numbers span').removeClass('line-highlighted');
|
||||||
for (i = from; i <= to; i += 1) {
|
for (i = from; i <= to; ++i) {
|
||||||
$('#' + i).addClass('line-highlighted');
|
$('#' + i).addClass('line-highlighted');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
stripped = '',
|
stripped = '',
|
||||||
len = rootPath.match(/\.\.\//g).length + 1;
|
len = rootPath.match(/\.\.\//g).length + 1;
|
||||||
|
|
||||||
for (i = 0; i < len; i += 1) {
|
for (i = 0; i < len; ++i) {
|
||||||
match = url.match(/\/[^\/]*$/);
|
match = url.match(/\/[^\/]*$/);
|
||||||
if (i < len - 1) {
|
if (i < len - 1) {
|
||||||
stripped = match[0] + stripped;
|
stripped = match[0] + stripped;
|
||||||
@@ -111,9 +111,44 @@
|
|||||||
|
|
||||||
document.location.href = url;
|
document.location.href = url;
|
||||||
});
|
});
|
||||||
|
/**
|
||||||
|
* Code from Stackoverflow to compute the Levenshtein distance between two strings
|
||||||
|
* Written by Marco de Wit at http://stackoverflow.com/a/18514751/745719
|
||||||
|
*/
|
||||||
|
var levenshtein = (function() {
|
||||||
|
var row2 = [];
|
||||||
|
return function(s1, s2) {
|
||||||
|
if (s1 === s2) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
var s1_len = s1.length, s2_len = s2.length;
|
||||||
|
if (s1_len && s2_len) {
|
||||||
|
var i1 = 0, i2 = 0, a, b, c, c2, row = 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;
|
||||||
|
} else {
|
||||||
|
return s1_len + s2_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
function initSearch(rawSearchIndex) {
|
function initSearch(rawSearchIndex) {
|
||||||
var currentResults, index, searchIndex;
|
var currentResults, index, searchIndex;
|
||||||
|
var MAX_LEV_DISTANCE = 3;
|
||||||
var params = getQueryStringParams();
|
var params = getQueryStringParams();
|
||||||
|
|
||||||
// Populate search bar with query string search term when provided,
|
// Populate search bar with query string search term when provided,
|
||||||
@@ -140,7 +175,7 @@
|
|||||||
split = valLower.split("::");
|
split = valLower.split("::");
|
||||||
|
|
||||||
//remove empty keywords
|
//remove empty keywords
|
||||||
for (var j = 0; j < split.length; j++) {
|
for (var j = 0; j < split.length; ++j) {
|
||||||
split[j].toLowerCase();
|
split[j].toLowerCase();
|
||||||
if (split[j] === "") {
|
if (split[j] === "") {
|
||||||
split.splice(j, 1);
|
split.splice(j, 1);
|
||||||
@@ -153,7 +188,7 @@
|
|||||||
val.charAt(val.length - 1) === val.charAt(0))
|
val.charAt(val.length - 1) === val.charAt(0))
|
||||||
{
|
{
|
||||||
val = val.substr(1, val.length - 2);
|
val = val.substr(1, val.length - 2);
|
||||||
for (var i = 0; i < nSearchWords; i += 1) {
|
for (var i = 0; i < nSearchWords; ++i) {
|
||||||
if (searchWords[i] === val) {
|
if (searchWords[i] === val) {
|
||||||
// filter type: ... queries
|
// filter type: ... queries
|
||||||
if (typeFilter < 0 || typeFilter === searchIndex[i].ty) {
|
if (typeFilter < 0 || typeFilter === searchIndex[i].ty) {
|
||||||
@@ -167,15 +202,31 @@
|
|||||||
} else {
|
} else {
|
||||||
// gather matching search results up to a certain maximum
|
// gather matching search results up to a certain maximum
|
||||||
val = val.replace(/\_/g, "");
|
val = val.replace(/\_/g, "");
|
||||||
for (var i = 0; i < split.length; i++) {
|
for (var i = 0; i < split.length; ++i) {
|
||||||
for (var j = 0; j < nSearchWords; j += 1) {
|
for (var j = 0; j < nSearchWords; ++j) {
|
||||||
|
var lev_distance;
|
||||||
if (searchWords[j].indexOf(split[i]) > -1 ||
|
if (searchWords[j].indexOf(split[i]) > -1 ||
|
||||||
searchWords[j].indexOf(val) > -1 ||
|
searchWords[j].indexOf(val) > -1 ||
|
||||||
searchWords[j].replace(/_/g, "").indexOf(val) > -1)
|
searchWords[j].replace(/_/g, "").indexOf(val) > -1)
|
||||||
{
|
{
|
||||||
// filter type: ... queries
|
// filter type: ... queries
|
||||||
if (typeFilter < 0 || typeFilter === searchIndex[j].ty) {
|
if (typeFilter < 0 || typeFilter === searchIndex[j].ty) {
|
||||||
results.push({id: j, index: searchWords[j].replace(/_/g, "").indexOf(val)});
|
results.push({
|
||||||
|
id: j,
|
||||||
|
index: searchWords[j].replace(/_/g, "").indexOf(val),
|
||||||
|
lev: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
(lev_distance = levenshtein(searchWords[j], val)) <=
|
||||||
|
MAX_LEV_DISTANCE) {
|
||||||
|
if (typeFilter < 0 || typeFilter === searchIndex[j].ty) {
|
||||||
|
results.push({
|
||||||
|
id: j,
|
||||||
|
index: 0,
|
||||||
|
// we want lev results to go lower than others
|
||||||
|
lev: lev_distance,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (results.length === max) {
|
if (results.length === max) {
|
||||||
@@ -186,7 +237,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
var nresults = results.length;
|
var nresults = results.length;
|
||||||
for (var i = 0; i < nresults; i += 1) {
|
for (var i = 0; i < nresults; ++i) {
|
||||||
results[i].word = searchWords[results[i].id];
|
results[i].word = searchWords[results[i].id];
|
||||||
results[i].item = searchIndex[results[i].id] || {};
|
results[i].item = searchIndex[results[i].id] || {};
|
||||||
}
|
}
|
||||||
@@ -198,6 +249,12 @@
|
|||||||
results.sort(function(aaa, bbb) {
|
results.sort(function(aaa, bbb) {
|
||||||
var a, b;
|
var 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)
|
// sort by crate (non-current crate goes later)
|
||||||
a = (aaa.item.crate !== window.currentCrate);
|
a = (aaa.item.crate !== window.currentCrate);
|
||||||
b = (bbb.item.crate !== window.currentCrate);
|
b = (bbb.item.crate !== window.currentCrate);
|
||||||
@@ -255,7 +312,7 @@
|
|||||||
results[i].id = -1;
|
results[i].id = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (var i = 0; i < results.length; i++) {
|
for (var i = 0; i < results.length; ++i) {
|
||||||
var result = results[i],
|
var result = results[i],
|
||||||
name = result.item.name.toLowerCase(),
|
name = result.item.name.toLowerCase(),
|
||||||
path = result.item.path.toLowerCase(),
|
path = result.item.path.toLowerCase(),
|
||||||
@@ -285,38 +342,23 @@
|
|||||||
* @return {[boolean]} [Whether the result is valid or not]
|
* @return {[boolean]} [Whether the result is valid or not]
|
||||||
*/
|
*/
|
||||||
function validateResult(name, path, keys, parent) {
|
function validateResult(name, path, keys, parent) {
|
||||||
//initially valid
|
for (var i=0; i < keys.length; ++i) {
|
||||||
var validate = true;
|
// each check is for validation so we negate the conditions and invalidate
|
||||||
//if there is a parent, then validate against parent
|
if (!(
|
||||||
if (parent !== undefined) {
|
// check for an exact name match
|
||||||
for (var i = 0; i < keys.length; i++) {
|
name.toLowerCase().indexOf(keys[i]) > -1 ||
|
||||||
// if previous keys are valid and current key is in the
|
// then an exact path match
|
||||||
// path, name or parent
|
|
||||||
if ((validate) &&
|
|
||||||
(name.toLowerCase().indexOf(keys[i]) > -1 ||
|
|
||||||
path.toLowerCase().indexOf(keys[i]) > -1 ||
|
path.toLowerCase().indexOf(keys[i]) > -1 ||
|
||||||
parent.name.toLowerCase().indexOf(keys[i]) > -1))
|
// next if there is a parent, check for exact parent match
|
||||||
{
|
(parent !== undefined &&
|
||||||
validate = true;
|
parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
|
||||||
} else {
|
// lastly check to see if the name was a levenshtein match
|
||||||
validate = false;
|
levenshtein(name.toLowerCase(), keys[i]) <=
|
||||||
|
MAX_LEV_DISTANCE)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
return true;
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
// if previous keys are valid and current key is in the
|
|
||||||
// path, name
|
|
||||||
if ((validate) &&
|
|
||||||
(name.toLowerCase().indexOf(keys[i]) > -1 ||
|
|
||||||
path.toLowerCase().indexOf(keys[i]) > -1))
|
|
||||||
{
|
|
||||||
validate = true;
|
|
||||||
} else {
|
|
||||||
validate = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return validate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getQuery() {
|
function getQuery() {
|
||||||
@@ -496,7 +538,7 @@
|
|||||||
|
|
||||||
resultIndex = execQuery(query, 20000, index);
|
resultIndex = execQuery(query, 20000, index);
|
||||||
len = resultIndex.length;
|
len = resultIndex.length;
|
||||||
for (i = 0; i < len; i += 1) {
|
for (i = 0; i < len; ++i) {
|
||||||
if (resultIndex[i].id > -1) {
|
if (resultIndex[i].id > -1) {
|
||||||
obj = searchIndex[resultIndex[i].id];
|
obj = searchIndex[resultIndex[i].id];
|
||||||
filterdata.push([obj.name, obj.ty, obj.path, obj.desc]);
|
filterdata.push([obj.name, obj.ty, obj.path, obj.desc]);
|
||||||
@@ -568,7 +610,7 @@
|
|||||||
// faster analysis operations
|
// faster analysis operations
|
||||||
var len = items.length;
|
var len = items.length;
|
||||||
var lastPath = "";
|
var lastPath = "";
|
||||||
for (var i = 0; i < len; i += 1) {
|
for (var i = 0; i < len; ++i) {
|
||||||
var rawRow = items[i];
|
var rawRow = items[i];
|
||||||
var row = {crate: crate, ty: rawRow[0], name: rawRow[1],
|
var row = {crate: crate, ty: rawRow[0], name: rawRow[1],
|
||||||
path: rawRow[2] || lastPath, desc: rawRow[3],
|
path: rawRow[2] || lastPath, desc: rawRow[3],
|
||||||
@@ -640,7 +682,7 @@
|
|||||||
crates.push(crate);
|
crates.push(crate);
|
||||||
}
|
}
|
||||||
crates.sort();
|
crates.sort();
|
||||||
for (var i = 0; i < crates.length; i++) {
|
for (var i = 0; i < crates.length; ++i) {
|
||||||
var klass = 'crate';
|
var klass = 'crate';
|
||||||
if (crates[i] == window.currentCrate) {
|
if (crates[i] == window.currentCrate) {
|
||||||
klass += ' current';
|
klass += ' current';
|
||||||
@@ -657,10 +699,10 @@
|
|||||||
window.register_implementors = function(imp) {
|
window.register_implementors = function(imp) {
|
||||||
var list = $('#implementors-list');
|
var list = $('#implementors-list');
|
||||||
var libs = Object.getOwnPropertyNames(imp);
|
var libs = Object.getOwnPropertyNames(imp);
|
||||||
for (var i = 0; i < libs.length; i++) {
|
for (var i = 0; i < libs.length; ++i) {
|
||||||
if (libs[i] == currentCrate) continue;
|
if (libs[i] == currentCrate) continue;
|
||||||
var structs = imp[libs[i]];
|
var structs = imp[libs[i]];
|
||||||
for (var j = 0; j < structs.length; j++) {
|
for (var j = 0; j < structs.length; ++j) {
|
||||||
var code = $('<code>').append(structs[j]);
|
var code = $('<code>').append(structs[j]);
|
||||||
$.each(code.find('a'), function(idx, a) {
|
$.each(code.find('a'), function(idx, a) {
|
||||||
var href = $(a).attr('href');
|
var href = $(a).attr('href');
|
||||||
|
|||||||
Reference in New Issue
Block a user