diff options
author | isaacs <i@izs.me> | 2011-03-09 04:04:04 +0300 |
---|---|---|
committer | isaacs <i@izs.me> | 2011-03-22 01:55:54 +0300 |
commit | 7fa48daf31bbf96a73a886b2d844e6eb04d569da (patch) | |
tree | e3613e205aaaa1daad4beb2a28c568f762a78aaa /lib/search.js | |
parent | ea43a3d6421a10056041c1c17f3d2aa17340a74c (diff) |
Much nicer search implementation.
Diffstat (limited to 'lib/search.js')
-rw-r--r-- | lib/search.js | 260 |
1 files changed, 117 insertions, 143 deletions
diff --git a/lib/search.js b/lib/search.js index 62d392770..9fea92025 100644 --- a/lib/search.js +++ b/lib/search.js @@ -47,8 +47,13 @@ function search (args, silent, staleness, cb_) { if (typeof searchexclude === "string") { searchexclude = searchexclude.split(/\s+/) } else searchexclude = [] - getFilteredData( staleness, searchopts.concat(args), searchexclude - , function (er, data) { + var opts = searchopts.concat(args).map(function (s) { + return s.toLowerCase() + }).filter(function (s) { return s }) + searchexclude = searchexclude.map(function (s) { + return s.toLowerCase() + }) + getFilteredData( staleness, opts, searchexclude, function (er, data) { // now data is the list of data that we want to show. // prettify and print it, and then provide the raw // data to the cb. @@ -60,117 +65,63 @@ function search (args, silent, staleness, cb_) { } function getFilteredData (staleness, args, notArgs, cb) { - var processedData = {} - getMergedData(staleness, function (er, data) { + registry.get("/", null, staleness, function (er, data) { if (er) return cb(er) - Object.keys(data).sort(strcmp).forEach(function (p) { - var pkg = data[p] - if (pkg.url && (!pkg.versions || !Object.keys(pkg.versions).length)) { - processedData[pkg.name] = getWords(pkg) - } else { - Object.keys(pkg.versions).sort(semver.compare).forEach(function (v) { - pkg.name = p - processedData[pkg.name+"@"+v] = getWords(pkg, v) - }) - } - }) - var filtered = {} - Object.keys(processedData).forEach(function (name) { - var d = processedData[name] - , pass = true - , test = [name].concat(npm.config.get("description") - ? d.description : []) - .concat(d.keywords) - .concat(d.words) - for (var i = 0, l = args.length; i < l; i ++) { - pass = false - for (var ii = 0, ll = test.length; ii < ll; ii ++) { - if (test[ii].indexOf(args[i]) !== -1) { - pass = true - break - } - } - if (!pass) break - } - if (pass) for (var i = 0, l = notArgs.length; i < l; i ++) { - for (var ii = 0, ll = test.length; ii < ll; ii ++) { - if (test[ii].indexOf(notArgs[i]) !== -1) { - pass = false - break - } - } - if (!pass) break - } - if (pass) filtered[name] = d - }) - return cb(null, filtered) + return cb(null, filter(data, args, notArgs)) }) } -function getWords (pkg, version) { - var d = { data : pkg - , words : [] - , name : pkg.name + (version ? "@" + version : "") - } - if (pkg.maintainers && pkg.maintainers.length) { - d.words.push.apply(d.words, pkg.maintainers.map(function (m) { - return "=" + m.name - })) - } - var desc = pkg.description - , kw = pkg.keywords - if (pkg.url && !version) { - d.words.push("<"+pkg.url+">") - } else { - var v = pkg.versions[version] - if (v.description) desc = v.description - if (v.keywords) kw = v.keywords - if (v.tags.length) d.words.push.apply(d.words, v.tags) - if (v.active) d.words.push("active") - } - if (!kw) kw = [] - if (!Array.isArray(kw)) kw = kw.split(/\s+/) - d.keywords = kw - d.description = npm.config.get("description") && desc || "" - return d +function filter (data, args, notArgs) { + // data={<name>:{package data}} + return Object.keys(data).map(function (d) { + return data[d] + }).map(stripData).map(getWords).filter(function (data) { + return filterWords(data, args, notArgs) + }).reduce(function (l, r) { + l[r.name] = r + return l + }, {}) } -function getMergedData (staleness, cb) { - registry.get("/", null, staleness, function (er, remote) { - if (er) return cb(er) - return cb(null, merge(remote)) - }) +function stripData (data) { + return { name:data.name + , description:npm.config.get("description") ? data.description : "" + , maintainers:data.maintainers.map(function (m) { + return "=" + m.name + }) + , url:!Object.keys(data.versions).length ? data.url : null + , keywords:data.keywords || [] + } } -function strcmp (a, b) { - a = a.toLowerCase() - b = b.toLowerCase() - return a === b ? 0 : a > b ? 1 : -1 +function getWords (data) { + data.words = [ data.name ] + .concat(data.description) + .concat(data.maintainers) + .concat(data.url && ("<" + data.url + ">")) + .concat(data.keywords) + .map(function (f) { return f && f.trim && f.trim() }) + .filter(function (f) { return f }) + .join(" ") + .toLowerCase() + return data } -function prettify (data, args) { - var pkgs = Object.keys(data) - , attrs = [] - , names = [] - , pretty = [] - , beginAttrList = 28 - pkgs.forEach(function (name) { - var pkg = data[name] - pretty.push({name:name,attrs:data[name].words.sort(strcmp).join(" ") - ,description:data[name].description - ,keywords:data[name].keywords}) - }) - var colors = [36, 32, 33, 31, 35 ] - , c = 0 - , l = colors.length - var maxNameLen = 0 - var space = " " - pretty.forEach(function (line) { - maxNameLen = Math.min(Math.max(maxNameLen, line.name.length), space.length) - }) - maxNameLen += 2 +function filterWords (data, args, notArgs) { + var words = data.words + for (var i = 0, l = args.length; i < l; i ++) { + if (words.indexOf(args[i]) === -1) { + //console.error("fail %j", [args[i], words]) + return false + } + } + for (var i = 0, l = notArgs.length; i < l; i ++) { + if (words.indexOf(notArgs[i]) !== -1) return false + } + return true +} - // turn each line obj into a single line, only as much ws as necessary. +function prettify (data, args) { try { var stdio = process.binding("stdio") , cols = stdio.isatty(stdio.stdoutFD) ? @@ -178,50 +129,73 @@ function prettify (data, args) { : stdio.getWindowSize ? stdio.getWindowSize()[1] : Infinity ) : Infinity - } - catch (ex) { cols = Infinity } - pretty = pretty.map(function (line) { - var addSpace = maxNameLen - line.name.length - return (line.name + (space.substr(0, addSpace) || "") + " " - + line.attrs + " " - + (line.description ? line.description + " " : "") - + (line.keywords.length ? " " + line.keywords.join(" ") : "")) - .substr(0, cols) - }) + } catch (ex) { cols = Infinity } - if (args && pretty.length) args.forEach(function (arg) { - pretty = pretty.map(function (line) { - return line.split(arg).join("\033["+colors[c]+"m" + arg + "\033[m") + // name, desc, author, keywords + var longest = [] + , spaces + , maxLen = [20, 80, 25] + return Object.keys(data).map(function (d) { + return data[d] + }).map(function (data) { + // turn a pkg data into a string + // [name,who,desc,targets,keywords] tuple + // also set longest to the longest name + if (typeof data.keywords === "string") { + data.keywords = data.keywords.split(/[,\s]+/) + } + if (!Array.isArray(data.keywords)) data.keywords = [] + var l = [ data.name + , data.description || "" + , data.maintainers.join(" ") + , (data.keywords || []).join(" ") + ] + l.forEach(function (s, i) { + longest[i] = Math.min(maxLen[i] || Infinity + ,Math.max(longest[i] || 0, s.length)) + l[i] = l[i].replace(/\s+/g, " ") }) - c = (c + 1) % l - }) - if (!pretty.length) pretty = ["Nothing found"] - pretty.push("") - return pretty.join("\n") + return l + }).map(function (line) { + return line.map(function (s, i) { + spaces = spaces || longest.map(function (n) { + return new Array(n + 2).join(" ") + }) + return (s + spaces[i].substr(s.length)) + }).join(" ").substr(0, cols) + }).sort(function (a, b) { + return a === b ? 0 : a > b ? 1 : -1 + }).map(function (line) { + // colorize! + args.forEach(function (arg, i) { + line = addColorMarker(line, arg, i) + }) + return colorize(line) + }).join("\n") } -function merge (remote) { - var merged = {} - // now merge in the remote stuff. - for (var p in remote) { - merged[p] = merged[p] || {versions:{}} - for (var d in remote[p]) if (!merged[p].hasOwnProperty(d)) { - merged[p][d] = remote[p][d] - } - for (var v in remote[p].versions) { - merged[p].versions[v] = merged[p].versions[v] || {} - merged[p].versions[v].tags = [] - var descs = remote[p].descriptions - if (descs && descs[v] && descs[v] !== remote[p].description) { - merged[p].versions[v].description = descs[v] - } - Object.keys(remote[p]["dist-tags"]).forEach(function (tag) { - if (remote[p]["dist-tags"][tag] === v) { - merged[p].versions[v].tags.push(tag) - } - }) - } +var colors = [36, 32, 33, 31, 35 ] + , cl = colors.length +function addColorMarker (str, arg, i) { + var m = i % cl + 1 + , markStart = String.fromCharCode(m) + , markEnd = String.fromCharCode(0) + , pieces = str.toLowerCase().split(arg) + , p = 0 + return pieces.map(function (piece, i) { + piece = str.substr(p, piece.length) + var mark = markStart + + str.substr(p+piece.length, arg.length) + + markEnd + p += piece.length + arg.length + return piece + mark + }).join("") + return str.split(arg).join(mark) +} +function colorize (line) { + for (var i = 0; i < cl; i ++) { + var m = i + 1 + line = line.split(String.fromCharCode(m)).join("\033["+colors[i]+"m") } - return merged + return line.split("\u0000").join("\033[0m") } - |