Skip to content

Commit 9497a47

Browse files
committed
Add new search without ack (using grep and perl)
1 parent c012736 commit 9497a47

File tree

2 files changed

+222
-7
lines changed

2 files changed

+222
-7
lines changed

core/cb.search/code.js

Lines changed: 102 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,115 @@ var _ = require("lodash");
44
var Q = require("q");
55
var spawn = require('child_process').spawn;
66

7+
var types = require("./types");
8+
9+
var config = {
10+
grepCmd: "grep",
11+
perlCmd: "perl",
12+
platform: os.platform()
13+
};
14+
15+
var assembleCommand = function(options) {
16+
var include = "";
17+
var cmd = config.grepCmd + " -s -r --color=never --binary-files=without-match -n " +
18+
(!options.casesensitive ? "-i " : "") +
19+
(process.platform != "darwin" ? "-P " : "");
20+
21+
if (options.pattern) { // handles grep peculiarities with --include
22+
if (options.pattern.split(",").length > 1)
23+
include = "{" + options.pattern + "}";
24+
else
25+
include = options.pattern;
26+
}
27+
else {
28+
include = (process.platform != "darwin" ? "\\" : "") + "*{" + types.PATTERN_EXT + "}";
29+
}
30+
31+
if (options.maxresults)
32+
cmd += "-m " + parseInt(options.maxresults, 10);
33+
if (options.wholeword)
34+
cmd += " -w";
35+
36+
var query = options.query;
37+
if (!query)
38+
return;
39+
40+
// grep has a funny way of handling new lines (that is to say, it's non-existent)
41+
// if we're not doing a regex search, then we must split everything between the
42+
// new lines, escape the content, and then smush it back together; due to
43+
// new lines, this is also why we're now passing -P as default to grep
44+
if (!options.replaceAll && !options.regexp) {
45+
var splitQuery = query.split("\\n");
46+
47+
for (var q in splitQuery) {
48+
splitQuery[q] = types.grepEscapeRegExp(splitQuery[q]);
49+
}
50+
query = splitQuery.join("\\n");
51+
}
52+
53+
query = query.replace(new RegExp("\\\'", "g"), "'\\''"); // ticks must be double escaped for BSD grep
54+
55+
cmd += " " + types.PATTERN_EDIR + " " +
56+
" --include=" + include +
57+
" '" + query.replace(/-/g, "\\-") + "'" +
58+
" \"" + types.escapeShell(options.path) + "\"";
59+
60+
if (options.replaceAll) {
61+
if (!options.replacement)
62+
options.replacement = "";
63+
64+
if (options.regexp)
65+
query = types.escapeRegExp(query);
66+
67+
// pipe the grep results into perl
68+
cmd += " -l | xargs " + config.perlCmd +
69+
// print the grep result to STDOUT (to arrange in parseSearchResult())
70+
" -pi -e 'print STDOUT \"$ARGV:$.:$_\"" +
71+
// do the actual replace
72+
" if s/" + query + "/" + options.replacement + "/mg" + ( options.casesensitive ? "" : "i" ) + ";'";
73+
}
74+
75+
var args = ["-c", cmd];
76+
args.command = "bash";
77+
return args;
78+
};
79+
80+
781
var search = function(root, args) {
882
var d = Q.defer();
983
var results = {};
1084

11-
var proc = spawn('ack', ['"'+args.query+'"', '--nocolor'], {
12-
cwd: root,
13-
env: process.env
85+
args = _.defaults(args || {}, {
86+
pattern: null,
87+
casesensitive: false,
88+
maxresults: null,
89+
wholeword: false,
90+
regexp: null,
91+
92+
// replace
93+
replaceAll: false,
94+
replacement: null
1495
});
96+
97+
if (!args.query) return Q.reject(new Error("Need a query to search for code"));
98+
99+
var command = assembleCommand(_.extend({}, args, {
100+
path: root
101+
}));
102+
103+
var proc = spawn(command.command, command);
15104
proc.stdout.on('data', function(data) {
16105
data = data.toString();
17106

18107
_.each(data.toString().split("\n"), function(line) {
19108
var parts = line.split(":");
20109
if (parts.length < 3) return;
21110

22-
results[parts[0]] = results[parts[0]] || [];
23-
results[parts[0]].push({
24-
'line': parts[1],
111+
var _path = path.normalize(parts[0]);
112+
113+
results[_path] = results[_path] || [];
114+
results[_path].push({
115+
'line': parseInt(parts[1]),
25116
'content': parts[2]
26117
});
27118
});
@@ -31,7 +122,11 @@ var search = function(root, args) {
31122
d.reject(err)
32123
});
33124
proc.on('exit', function(code) {
34-
d.resolve(results)
125+
if (code !== 0) {
126+
d.reject(new Error("ack exited with code "+code));
127+
} else {
128+
d.resolve(results);
129+
}
35130
});
36131

37132
return d.promise;

core/cb.search/types.js

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
var _ = require("lodash");
2+
3+
var escapeRegExp = function(str) {
4+
return str.replace(/([.*+?\^${}()|\[\]\/\\])/g, "\\$1");
5+
};
6+
7+
// taken from http://xregexp.com/
8+
var grepEscapeRegExp = function(str) {
9+
return str.replace(/[[\]{}()*+?.,\\^$|#\s"']/g, "\\$&");
10+
};
11+
12+
var escapeShell = function(str) {
13+
return str.replace(/([\\"'`$\s\(\)<>])/g, "\\$1");
14+
};
15+
16+
17+
// file types
18+
var IGNORE_DIRS = {
19+
".bzr" : "Bazaar",
20+
".cdv" : "Codeville",
21+
"~.dep" : "Interface Builder",
22+
"~.dot" : "Interface Builder",
23+
"~.nib" : "Interface Builder",
24+
"~.plst" : "Interface Builder",
25+
".git" : "Git",
26+
".hg" : "Mercurial",
27+
".pc" : "quilt",
28+
".svn" : "Subversion",
29+
"_MTN" : "Monotone",
30+
"blib" : "Perl module building",
31+
"CVS" : "CVS",
32+
"RCS" : "RCS",
33+
"SCCS" : "SCCS",
34+
"_darcs" : "darcs",
35+
"_sgbak" : "Vault/Fortress",
36+
"autom4te.cache" : "autoconf",
37+
"cover_db" : "Devel::Cover",
38+
"_build" : "Module::Build",
39+
"node_modules" : "Node",
40+
};
41+
42+
var MAPPINGS = {
43+
"actionscript": ["as", "mxml"],
44+
"ada" : ["ada", "adb", "ads"],
45+
"asm" : ["asm", "s"],
46+
"batch" : ["bat", "cmd"],
47+
//"binary" : q{Binary files, as defined by Perl's -B op (default: off)},
48+
"cc" : ["c", "h", "xs"],
49+
"cfmx" : ["cfc", "cfm", "cfml"],
50+
"clojure" : ["clj"],
51+
"cpp" : ["cpp", "cc", "cxx", "m", "hpp", "hh", "h", "hxx"],
52+
"csharp" : ["cs"],
53+
"css" : ["css", "less", "scss", "sass"],
54+
"coffee" : ["coffee"],
55+
"elisp" : ["el"],
56+
"erlang" : ["erl", "hrl"],
57+
"fortran" : ["f", "f77", "f90", "f95", "f03", "for", "ftn", "fpp"],
58+
"haskell" : ["hs", "lhs"],
59+
"hh" : ["h"],
60+
"html" : ["htm", "html", "shtml", "xhtml"],
61+
"jade" : ["jade"],
62+
"java" : ["java", "properties"],
63+
"groovy" : ["groovy"],
64+
"js" : ["js"],
65+
"json" : ["json"],
66+
"latex" : ["latex", "ltx"],
67+
"jsp" : ["jsp", "jspx", "jhtm", "jhtml"],
68+
"lisp" : ["lisp", "lsp"],
69+
"logiql" : ["logic", "lql"],
70+
"lua" : ["lua"],
71+
"make" : ["makefile", "Makefile"],
72+
"mason" : ["mas", "mhtml", "mpl", "mtxt"],
73+
"markdown" : ["md", "markdown"],
74+
"objc" : ["m", "h"],
75+
"objcpp" : ["mm", "h"],
76+
"ocaml" : ["ml", "mli"],
77+
"parrot" : ["pir", "pasm", "pmc", "ops", "pod", "pg", "tg"],
78+
"perl" : ["pl", "pm", "pod", "t"],
79+
"php" : ["php", "phpt", "php3", "php4", "php5", "phtml"],
80+
"plone" : ["pt", "cpt", "metadata", "cpy", "py"],
81+
"powershell" : ["ps1"],
82+
"python" : ["py"],
83+
"rake" : ["rakefile"],
84+
"ruby" : ["rb", "ru", "rhtml", "rjs", "rxml", "erb", "rake", "gemspec"],
85+
"scala" : ["scala"],
86+
"scheme" : ["scm", "ss"],
87+
"shell" : ["sh", "bash", "csh", "tcsh", "ksh", "zsh"],
88+
//"skipped" : "q"{"Files but not directories normally skipped by ack ("default": "off")},
89+
"smalltalk" : ["st"],
90+
"sql" : ["sql", "ctl"],
91+
"tcl" : ["tcl", "itcl", "itk"],
92+
"tex" : ["tex", "cls", "sty"],
93+
"text" : ["txt"],
94+
"textile" : ["textile"],
95+
"tt" : ["tt", "tt2", "ttml"],
96+
"vb" : ["bas", "cls", "frm", "ctl", "vb", "resx"],
97+
"vim" : ["vim"],
98+
"yaml" : ["yaml", "yml"],
99+
"xml" : ["xml", "dtd", "xslt", "ent", "rdf", "rss", "svg", "wsdl", "atom", "mathml", "mml"]
100+
};
101+
var exts = [];
102+
for (var type in MAPPINGS) {
103+
exts = exts.concat(MAPPINGS[type]);
104+
}
105+
// grep pattern matching for extensions
106+
var PATTERN_EXT = _.unique(exts).join(",");
107+
108+
var dirs = _.keys(IGNORE_DIRS);
109+
var PATTERN_DIR = escapeRegExp(dirs.join("|"));
110+
var PATTERN_EDIR = " --exclude-dir="+dirs.join(" --exclude-dir=");
111+
112+
module.exports = {
113+
'grepEscapeRegExp': grepEscapeRegExp,
114+
'escapeRegExp': escapeRegExp,
115+
'escapeShell': escapeShell,
116+
117+
'PATTERN_EXT': PATTERN_EXT,
118+
'PATTERN_DIR': PATTERN_DIR,
119+
'PATTERN_EDIR': PATTERN_EDIR
120+
};

0 commit comments

Comments
 (0)