Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #3 from gjtorikian/faster-pussycat

Faster pussycat
  • Loading branch information...
commit 11dd268bc678f4cc22378b8a31f0cc78b951c672 2 parents 4c1e964 + 3b23852
Garen Torikian authored
4 .travis.yml
View
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+ - "0.10"
+ - "0.8"
4 bin/nak
View
@@ -102,7 +102,5 @@ function makeQuery(query, replacement) {
options.replacement = replacement;
}
- if (options.ackmate) {
- options.cleanQuery = new RegExp(query, flags.substr(1));
- }
+ options.queryClean = new RegExp(query, flags.substr(1));
}
56 lib/finalizer.js
View
@@ -1,13 +1,11 @@
var fs = require("fs"),
- mergesort = require("./mergesort"),
- isBinaryFile = require("isbinaryfile"),
- StringDecoder = require('string_decoder').StringDecoder,
- decoder = new StringDecoder('utf8');
+ mergesort = require("./mergesort");
-module.exports = function(options, allPaths, allFiles, printCb, done) {
- if (options.list) {
- var dirKeys = mergesort(Object.keys(allPaths)), results = "", parent = "";
+module.exports = function(options, allPaths, allContents, printCb, done) {
+ var dirKeys = mergesort(Object.keys(allPaths)), parent = "";
+ if (options.list) {
+ var results = "";
for (var d = 0, dLength = dirKeys.length; d < dLength; d++) {
parent = allPaths[dirKeys[d]];
results += mergesort(parent).join("\n") + "\n";
@@ -16,47 +14,47 @@ module.exports = function(options, allPaths, allFiles, printCb, done) {
printCb(results);
}
else {
- for (var m = 0, allFilesLength = allFiles.length; m < allFilesLength; m++) {
- var path = allFiles[m],
- fileBuffer = fs.readFileSync(path);
+ var dirKeys = mergesort(Object.keys(allPaths)), allFiles = [];
+ for (var d = 0, dLength = dirKeys.length; d < dLength; d++) {
+ parent = allPaths[dirKeys[d]];
+ allFiles = mergesort(parent);
- var contents = decoder.write(fileBuffer);
+ for (var m = 0, allFilesLength = allFiles.length; m < allFilesLength; m++) {
+ var path = allFiles[m],
+ contents = allContents[path],
+ contentsSplit = contents.split("\n"),
- options.query.lastIndex = 0;
- // don't bother working if there's not even a match
- if ( options.query.test(contents) ) {
- var oMatchCount = contents.match(options.query).length,
- matchCount = oMatchCount,
- lines = "";
+ lines = "",
+
+ totalMatches = contents.match(options.query).length,
+ matchCount = totalMatches;
if (options.replacement) {
var replacedContents = contents.replace(options.query, options.replacement);
fs.writeFile(path, replacedContents, "utf8");
}
- var contents = contents.split("\n");
-
- for (var i = 0, strLength = contents.length; matchCount && i < strLength; i++) {
- options.query.lastIndex = 0; // query is set with "g" so this resets the position
- if (options.query.test(contents[i])) {
- var matchLine = (options.replacement ? contents[i].replace(options.query, options.replacement) : contents[i]);
+ for (var i = 0, strLength = contentsSplit.length; matchCount && i < strLength; i++) {
+ if (options.queryClean.test(contentsSplit[i])) {
+ var matchLine = (options.replacement ? contentsSplit[i].replace(options.query, options.replacement) : contentsSplit[i]);
- if (!options.ackmate)
+ if (!options.ackmate) {
lines += "\t" + (i+1) + ": " + matchLine + "\n";
+ path = path + ":";
+ }
else { // slow due to match()
- var result = matchLine.match(options.cleanQuery);
+ var result = options.queryClean.exec(matchLine);
lines += (i+1) + ";" + result.index + " " + result[0].length + ": " + matchLine + "\n";
+ path = ":" + path;
}
+
matchCount--;
}
}
-
- path = options.ackmate ? ":" + path : path + ":";
- printCb(path, lines, oMatchCount, allFiles);
+ printCb(path, lines, totalMatches);
}
}
-
done();
}
}
118 lib/ignorer.js
View
@@ -1,62 +1,62 @@
// filched mostly from https://github.com/andreyvit/pathspec.js
// given the ignore rules, creates an array of regexps to test files against
-module.exports = (function () {
- var rules = [], exclusions, exclusionsLength;
-
- var Ignorer = function(exclusions, length) {
- var r = 0, exclusionsLength = length,
-
- makeRegExp = function(wildcard) {
- // if the rule has an asterisk, escape the segment and swap * for .?
- return new RegExp(escapeRegExp(wildcard).replace(/\\\*/g, ".?"));
- };
-
- while (r < exclusionsLength) rules.push(makeRegExp(exclusions[r++]));
-
- this.isIgnored = function(path, showHidden) {
- if (!showHidden && (/^\.\w/.test(path) || /\/\.\w/.test(path))) {
- return true;
- }
-
- var r = 0;
- while (r < exclusionsLength) {
- if ( rules[r++].test(path) ) return true;
- }
-
- return false;
- }
- };
-
-
- Ignorer.makeWildcardRegExp = function(str, fullConversion) {
- if (!str)
- return "";
-
- // remove all whitespace
- str = str.replace(/\s/g, "");
- str = escapeRegExp(str);
-
- // convert wildcard norms to regex ones
- str = str.replace(/\\\*/g, ".*");
- str = str.replace(/\\\?/g, ".");
-
- if (fullConversion) {
- // we wants pipe seperation, not commas
- // (this is a regexp list with ORs)
- str = str.replace(/,/g, "|");
-
- str = new RegExp("(?:" + str + ")");
-
- return str;
- }
-
- return str;
- };
-
- var escapeRegExp = Ignorer.escapeRegExp = function(str) {
- return str.replace(/([\/'*+?|()\[\]{}.^$])/g, '\\$1');
- }
-
- return Ignorer;
-})();
+function Ignorer(exclusions, length) {
+ makeRegExp = function(wildcard) {
+ // if the rule has an asterisk, escape the segment and swap * for .?
+ return new RegExp(escapeRegExp(wildcard).replace(/\\\*/g, ".?"));
+ };
+
+ var r = 0, rules = [];
+ while (r < length) rules.push(makeRegExp(exclusions[r++]));
+
+ var searchRules = "var r = 0; while (r < " + length + ") { if ( [" + rules + "][r++].test(path) ) return true; } return false;";
+ this.searchRulesFn = new Function("path", searchRules);
+}
+
+Ignorer.prototype.isFileIgnored = function(file, path, showHidden) {
+ if (!showHidden && (/^\.\w/.test(file))) {
+ return true;
+ }
+
+ return this.searchRulesFn(path);
+}
+
+Ignorer.prototype.isDirIgnored = function(path, showHidden) {
+ if (!showHidden && (/\/\.\w/.test(path))) {
+ return true;
+ }
+
+ return this.searchRulesFn(path);
+}
+
+Ignorer.makeWildcardRegExp = function(str, fullConversion) {
+ if (!str)
+ return "";
+
+ // remove all whitespace
+ str = str.replace(/\s/g, "");
+ str = escapeRegExp(str);
+
+ // convert wildcard norms to regex ones
+ str = str.replace(/\\\*/g, ".*");
+ str = str.replace(/\\\?/g, ".");
+
+ if (fullConversion) {
+ // we wants pipe seperation, not commas
+ // (this is a regexp list with ORs)
+ str = str.replace(/,/g, "|");
+
+ str = new RegExp("(?:" + str + ")");
+
+ return str;
+ }
+
+ return str;
+};
+
+var escapeRegExp = Ignorer.escapeRegExp = function(str) {
+ return str.replace(/([\/'*+?|()\[\]{}.^$])/g, '\\$1');
+}
+
+module.exports = Ignorer;
20 lib/options.js
View
@@ -33,16 +33,16 @@ var parser = {};
module.exports = parser;
// Option definitions.
var schema = [
- ['l', 'list', '', ' list files encountered'],
- ['H', 'hidden', '', ' search hidden files and directories (default off)'],
- ['c', 'color', '', ' adds color to results (default off)'],
- ['a', 'pathToNakignore', ':', ' path to an additional nakignore file'],
- ['q', 'literal', '', ' do not parse PATTERN as a regular expression; match it literally'],
- ['w', 'wordRegexp', '', ' only match whole words'],
- ['i', 'ignoreCase', '', ' match case insensitively'],
- ['G', 'fileSearch', ':', ' comma-separated list of wildcard files to only search on'],
- ['d', 'ignore', ':', ' comma-separated list of wildcard files to additionally ignore'],
- ['', 'ackmate', '', ' output results in a format parseable by AckMate']
+ ['l', 'list', '', ' list files encountered'],
+ ['H', 'hidden', '', ' search hidden files and directories (default off)'],
+ ['c', 'color', '', ' adds color to results (default off)'],
+ ['a', 'pathToNakignore', ':', ' path to an additional nakignore file'],
+ ['q', 'literal', '', ' do not parse PATTERN as a regular expression; match it literally'],
+ ['w', 'wordRegexp', '', ' only match whole words'],
+ ['i', 'ignoreCase', '', ' match case insensitively'],
+ ['G', 'fileSearch', ':', ' comma-separated list of wildcard files to only search on'],
+ ['d', 'ignore', ':', ' comma-separated list of wildcard files to additionally ignore'],
+ ['', 'ackmate', '', ' output results in a format parseable by AckMate']
],
parseArgs = parser.parseArgs = function(passedArgs) {
44 lib/walkdir.js
View
@@ -9,9 +9,9 @@ var fs = require("fs"),
Ignorer = require("./ignorer"),
ignorer = null,
isBinaryFile = require("isbinaryfile"),
- binaryBuffer = new Buffer(1024),
+ binaryBuffer = new Buffer(512),
allPaths = {},
- allFiles = [],
+ allContents = {},
ended = 0,
jobs = 0,
DIR_SEP = _path.sep,
@@ -39,7 +39,7 @@ walker.walkdir = function(fpath, options, printCb, done) {
// called at the end of the walk
finalizer = function() {
- require('./finalizer')(options, allPaths, allFiles, printCb, done);
+ require('./finalizer')(options, allPaths, allContents, printCb, done);
}
statter = function (parent, path) {
@@ -52,10 +52,17 @@ walker.walkdir = function(fpath, options, printCb, done) {
}
else {
var fd = fs.openSync(path,'r');
- var bytes = fs.readSync(fd, binaryBuffer, 0, 1024, 0);
+ var bytes = fs.readSync(fd, binaryBuffer, 0, 512, 0);
if (!isBinaryFile(binaryBuffer, stat.size)) {
- allFiles.push(path);
+ // we have a file; let's read it here, so that the later
+ // allPaths sort isn't so long
+ var contents = fs.readFileSync(path, "utf8");
+
+ if ( options.queryClean.test(contents) ) {
+ allPaths[parent].push(path);
+ allContents[path] = contents;
+ }
}
fs.closeSync(fd);
@@ -64,8 +71,7 @@ walker.walkdir = function(fpath, options, printCb, done) {
else if (stat.isDirectory()) readdir(path);
};
- // not sure why, but sync here is like 200ms faster than async;
- // still, lstat is SLOW, but what other way to determine if something is a directory or file ?
+ // lstat is SLOW, but what other way to determine if something is a directory or file ?
statAction(fs.lstatSync(path));
},
@@ -73,40 +79,28 @@ walker.walkdir = function(fpath, options, printCb, done) {
var readdirAction = function(files) {
job(-1);
- if (!files) { return "Permission error (or other failure) on: " + files; }
+ if (!files) { return;}
- var fileCount = files.length, i = 0;
+ var fileCount = files.length, i = 0, prefix = path + DIR_SEP, file;
allPaths[path] = [];
while (i < fileCount) {
- var file = files[i++], filename = path + DIR_SEP + file;
- if (options.filesInclude.test(file) && !ignorer.isIgnored(filename, options.hidden)) {
+ var file = files[i++], filename = prefix + file;
+ if (options.filesInclude.test(file) && !ignorer.isFileIgnored(file, filename, options.hidden)) {
statter(path, filename);
}
}
};
- if (!ignorer.isIgnored(path, options.hidden)) {
+ if (!ignorer.isDirIgnored(path, options.hidden)) {
job(1);
// async doesn't matter, we sort results at end anyway
fs.readdir(path, function(err, files) {
- if (err) { console.error(err); process.exit(1); }
readdirAction(files);
});
}
};
// This is the main entry point
-
- // from a pipe, we're going to get an array of file names
- // just toss them to be processed
- if (options.piped) {
- var files = fpath.split("\n");
- for (var f = 0, filesLength = files.length; f < filesLength; f++) {
- if (files[f].length)
- statter(files[f]);
- }
- }
- else
- readdir(fpath);
+ readdir(fpath);
}
9 package.json
View
@@ -15,7 +15,10 @@
"bin": { "nak": "./bin/nak" },
"engines": { "node": ">= 0.8.x" },
"repository" :
- { "type" : "git",
- "url" : "http://github.com/gjtorikian/nak.git"
- }
+ { "type" : "git",
+ "url" : "http://github.com/gjtorikian/nak.git"
+ },
+ "scripts": {
+ "test": "mocha tests/test_runner.js"
+ }
}
4 tests/filelist_test.js
View
@@ -5,8 +5,8 @@ var Exec = require("child_process").exec;
var basePath = __dirname + "/filelist_fixtures";
-var nakPath = "node ../bin/nak";
-//var nakPath = "node ../build/nak.min";
+var nakPath = "node bin/nak";
+//var nakPath = "node build/nak.min";
var options1 = [
"-l",
18 tests/search_test.js
View
@@ -5,50 +5,50 @@ var Exec = require("child_process").exec;
var basePath = __dirname + "/search_fixtures";
-var nakPath = "node ../bin/nak";
-//var nakPath = "node ../build/nak.min";
+var nakPath = "node bin/nak";
+//var nakPath = "node build/nak.min";
var options1 = [
- "-a ../.nakignore",
+ "-a .nakignore",
"-i",
"-q",
"'sriracha'",
basePath
],
options2 = [
- "-a ../.nakignore",
+ "-a .nakignore",
"-q",
"'Messenger'",
basePath
],
options3 = [
- "-a ../.nakignore",
+ "-a .nakignore",
"-q",
"-w",
"'gastro'",
basePath
],
options4 = [
- "-a ../.nakignore",
+ "-a .nakignore",
"-i",
"'pb.'",
basePath
],
options5 = [
- "-a ../.nakignore",
+ "-a .nakignore",
"-H",
"'.+wave'",
basePath
],
options6 = [
- "-a ../.nakignore",
+ "-a .nakignore",
"-G '*.txt, file*.gif'",
"-i",
"'shorts'",
basePath
],
options7 = [
- "-a ../.nakignore",
+ "-a .nakignore",
"--ignore 'file*.txt'",
"'williamsburg'",
"-H",
4 tests/sorting_test.js
View
@@ -5,8 +5,8 @@ var Exec = require("child_process").exec;
var basePath = __dirname + "/sorting_fixtures";
-var nakPath = "node ../bin/nak";
-//var nakPath = "node ../build/nak.min";
+var nakPath = "node bin/nak";
+//var nakPath = "node build/nak.min";
var options = [
"-a ../.nakignore",
Please sign in to comment.
Something went wrong with that request. Please try again.