c9-core/plugins/c9.ide.navigate/search.js

298 wiersze
10 KiB
JavaScript

/**
* File name and definition search for the Cloud9
*
* @copyright 2013, Ajax.org B.V.
*/
define(function(require, exports, module) {
var Heap = require("./heap");
var fileTypes = {
"js": 1, "json": 11, "css": 5, "less": 5, "scss": 5, "sass": 5, "xml": 11,
"rdf": 15, "rss": 15, "svg": 5, "wsdl": 11, "xslt": 5, "atom": 5,
"mathml": 5, "mml": 5, "php": 1, "phtml": 1, "html": 5, "xhtml": 5,
"coffee": 1, "py": 1, "ru": 1, "gemspec": 5, "rake": 5, "rb": 1, "c": 1,
"cc": 1, "cpp": 1, "cxx": 1, "h": 2, "hh": 2, "hpp": 2, "cs": 1, "java": 1,
"clj": 1, "groovy": 1, "logic": 1, "lql": 1, "scala": 1, "ml": 1, "mli": 1, "md": 2,
"markdown": 2, "textile": 5, "latex": 5, "tex": 5, "ltx": 5, "lua": 1,
"pl": 1, "pm": 2, "ps1": 1, "cfm": 1, "sql": 2, "sh": 1, "bash": 1,
"bmp": 10, "djv": 10, "djvu": 10, "gif": 10, "ico": 10, "jpg": 10,
"jpeg": 10, "pbm": 10, "pgm": 10, "png": 10, "pnm": 10, "ppm": 10,
"psd": 10, "tiff": 10, "xbm": 10, "xpm": 10,
"go": 5, "hx": 5, "yaml": 5, "psql": 2
};
/**
* Search through a list of filenames.
*/
module.exports.fileSearch = function(filelist, keyword) {
if (!keyword) keyword = "";
var klen = keyword.length;
var keywordLower = keyword.toLowerCase();
/**
* FULL MATCHES >= 50
* full filename with extension 1000
* name part without the extension 201
* start of filename 200
* part of filename 100
* depth of path (or length to optimize) 200 - 10 * count("/")
* full part of path 50
*
* SCATTERED MATCHES <= 45
* number of match groups 20 - 3 * #groups
* path depth 15 - 2 * count("/")
* match diff 10 - len(diff)
*
* Extension weight -1 * lut[ext]
*/
var type = "value";
var toS = function() {
return this[type];
};
var newlist = [];
var l = filelist.length;
var big = l > 50000;
var name, res = big ? [] : new Heap(), value, ext, displayName;
for (var i = 0, s, j, k, q, p, m, n; i < l; i++) {
displayName = filelist[i];
name = displayName.toLowerCase();
// We only add items that have the keyword in it's path
value = 0;
if ((j = name.lastIndexOf(keywordLower)) > -1) {
if (big) {
res.push(displayName);
continue;
}
// We prioritize ones that have the name in the filename
if (j > (q = name.lastIndexOf("/"))) {
k = name.lastIndexOf("/" + keywordLower);
if (k > -1) {
// We give first prio to full filename matches
if (name.length == klen + 1 + k)
value += 1000;
// Then to match of name prior to extension
else if (name.lastIndexOf(".") == k + klen + 1)
value += 201;
// Then to matches from the start of the filename
else if (k == q)
value += 200;
// Then anywhere in the filename
else
value += 100;
}
// The shorter the path depth, the higher prio we give
value += 200 - Math.min(name.split("/").length * 10, 150);
}
// Detect a path search
else if (keywordLower.indexOf("/") > -1) {
var idx = keywordLower.lastIndexOf("/");
var rest = name.substr(j + idx + 1);
var file = keywordLower.substr(idx + 1);
// We give prio to full path matches
if (j === 0)
value += 1000;
// When searching for a file extension,
if (file.indexOf(".") > -1) {
// Prioritize filename matches
if (rest.indexOf(file) === 0)
value += 200;
// The shorter the filename length, the higher prio we give
value += 200 - Math.min((rest.length - file.length) * 10, 150);
}
// When the full filename is matched, give a higher prio
else if (rest.substr(file.length).match(/^\..*(\/|$)/))
value += 201;
else
value += 50;
}
// Then the rest
else
value += 50;
}
// Check for spatial matches
else {
if (big || name.split("/").length > 10)
continue;
var result;
result = matchPath(name, keywordLower);
if (!result.length || result.length > 20)
continue;
var matched = name.substring(result[0].val.length);
// The less the number of groups matched, the higher prio we give
value += 20 - Math.min((result.length - 2) * 3, 19);
// The shorter the path depth, the higher prio we give
value += 15 - Math.min(name.split("/").length * 2, 14);
// The shorter the match diff, the higher prio we give
value += 10 - Math.min(matched.length - keyword.length, 9);
value += 20; // extension
}
if (value > 0) {
newlist.push(displayName);
// Check extension
s = name.lastIndexOf(".");
if (s > -1)
value -= 10 * (fileTypes[name.substr(s + 1)] || 0) || 20;
else
value -= 20;
if (res.size() === 100 && value > res.min().value)
res.pop();
if (res.size() < 100)
res.push({
toString: toS,
value: value,
name: displayName
});
}
}
if (big) {
res.newlist = res;
return res;
}
var ret = [];
while (res.size())
ret.unshift(res.pop().name);
ret.newlist = newlist;
return ret;
};
// function score (e, term) {
// var c = 0,
// d = term.length,
// f = e.length,
// g, h, i = 1,
// j;
// if (e == term) return 1;
// for (var k = 0, l, m, n, o, p, q; k < d; ++k) {
// n = term[k], o = e.indexOf(n.toLowerCase()), p = e.indexOf(n.toUpperCase()), q = Math.min(o, p), m = q > -1 ? q : Math.max(o, p);
// if (m === -1) {
// return 0;
// }
// l = .1, e[m] === n && (l += .1), m === 0 ? (l += .6, k === 0 && (g = 1)) : e.charAt(m - 1) === " " && (l += .8), e = e.substring(m + 1, f), c += l
// }
// return h = c / d, j = (h * (d / f) + h) / 2, j /= i, g && j + .15 < 1 && (j += .15), j
// };
var treeSearch = module.exports.treeSearch = function(tree, keyword, caseInsensitive, results, head, indexProperty) {
if (caseInsensitive)
keyword = keyword.toLowerCase();
results = results || [];
head = head || 0;
for (var i = 0; i < tree.length; i++) {
var node = tree[i];
var name = indexProperty
? node[indexProperty] || ""
: node.name || node.label || node.caption || (typeof node == "string" ? node : "");
if (caseInsensitive)
name = name.toLowerCase();
var index = name.indexOf(keyword);
if (index === -1) {
if (node.items && !node.keepChildren)
results = treeSearch(node.items, keyword, caseInsensitive, results, head);
continue;
}
var result = node.clone ? node.clone() : {};
result.items = node.items
? (result.keepChildren
? result.items
: treeSearch(node.items, keyword, caseInsensitive))
: [];
if (!node.clone) {
for (var p in node) {
if (node[p] && p !== "items")
result[p] = node[p];
}
}
if (index === 0) {
results.splice(head, 0, result);
head++;
}
else {
results.push(result);
}
}
return results;
};
var matchPath = module.exports.matchPath = function (path, keyword) {
var result = [];
var pathSplits = path.split("/");
// Optimization
if (pathSplits.length > 4) {
pathSplits = [pathSplits.slice(0, pathSplits.length - 4).join("/") + "/"]
.concat(pathSplits.slice(pathSplits.length - 4, pathSplits.length));
if (pathSplits[0] == "/")
pathSplits[0] = "";
}
var value = "";
var k, i, j = -1;
for (k = pathSplits.length - 1; k >= 0 && !result.length; k--) {
value = (k > 0 ? "/" : "") + pathSplits[k] + value;
// find matched parts
var matchI = null;
var missI = null;
for (i = 0, j = 0; i < value.length && j < keyword.length; i++) {
if (value[i] === keyword[j]) {
matchI = matchI === null ? i : matchI;
j++;
if (missI !== null) {
result.push({ val: value.substring(missI, i) });
missI = null;
}
}
else {
missI = missI === null ? i : missI;
if (matchI !== null) {
result.push({ match: true, val: value.substring(matchI, i) });
matchI = null;
}
}
}
if (j !== keyword.length) {
result = [];
continue;
}
if (missI !== null)
result.push({ val: value.substring(missI, i) });
if (matchI !== null)
result.push({ match: true, val: value.substring(matchI, i) });
result.push({ val: value.substring(i, value.length) });
// Add the first non matched part if exists
if (k)
result.unshift({ val: pathSplits.slice(0, k).join('/') });
}
return result;
};
});