diff --git a/README.md b/README.md index 36e53bf..415c98f 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,9 @@ Requirements * node.js * `make` +On newer Debian and Ubuntu systems, please install nodejs-legacy in addition +to nodejs. + Building -------- To install: @@ -68,4 +71,3 @@ The directory structure mostly follows the CommonJS packaging scheme: [6]: http://mxr.mozilla.org/mozilla/source/js/narcissus/ [parser]: http://localhost:8080/html/parser.html - diff --git a/bin/jsctags.js b/bin/jsctags.js index 242cd6d..a2b2250 100755 --- a/bin/jsctags.js +++ b/bin/jsctags.js @@ -1,4 +1,10 @@ #!/usr/bin/env node +/** + * If you get: + * /usr/bin/env: node: No such file or directory + * Please install nodejs-legacy. + */ + /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -50,6 +56,7 @@ var Tags = ctags.Tags; function usage() { util.puts("usage: jsctags [options] path0 [.. pathN]"); util.puts("options:"); + util.puts(" -e, --emacs emit Emacs TAGS"); util.puts(" -f, --output file place output in the given file (-f " + "- for stdout)"); util.puts(" -h, --help display this usage info"); @@ -65,7 +72,7 @@ function usage() { var opts; try { - opts = getopt("help|h", "jsonp|j=s", "libroot|L=s@", "oneprog", "output|o|f=s", + opts = getopt("help|h", "emacs|e", "jsonp|j=s", "libroot|L=s@", "oneprog", "output|o|f=s", "sort|=s", "warning|W=s"); } catch (e) { util.puts(e); @@ -260,12 +267,16 @@ if (opts.hasOwnProperty('output')) { } } else if (opts.hasOwnProperty('jsonp')) { out = fs.createWriteStream("tags.jsonp"); +} else if (opts.hasOwnProperty('emacs')) { + out = fs.createWriteStream("TAGS"); } else { out = fs.createWriteStream("tags"); } if (opts.hasOwnProperty('jsonp')) { tags.writeJSONP(out, opts.jsonp); +} else if (opts.hasOwnProperty('emacs')) { + tags.writeEmacs(out, opts); } else { tags.write(out, opts); } diff --git a/lib/jsctags/ctags/index.js b/lib/jsctags/ctags/index.js index fff958c..f076e27 100644 --- a/lib/jsctags/ctags/index.js +++ b/lib/jsctags/ctags/index.js @@ -43,11 +43,12 @@ var TagWriter = require('./writer').TagWriter; var Trait = require('../traits').Trait; exports.Tags = function (initialTags) { - this.tags = initialTags != null ? initialTags : []; this.init(); }; exports.Tags.prototype = Object.create(Object.prototype, Trait.compose(Trait({ + tags: [], + _search: function (id, pred) { var shadowTag = { name: id }; var tags = this.tags; diff --git a/lib/jsctags/ctags/writer.js b/lib/jsctags/ctags/writer.js index bee3183..b19ccc6 100644 --- a/lib/jsctags/ctags/writer.js +++ b/lib/jsctags/ctags/writer.js @@ -41,101 +41,128 @@ var _ = require('../underscore')._; const ESCAPES = { "\\": "\\\\", "\n": "\\n", "\r": "\\r", "\t": "\\t" }; var METATAGS = [ - { name: '!_TAG_FILE_FORMAT', tagfile: 2, addr: "/extended format/" }, - { - name: '!_TAG_FILE_SORTED', - tagfile: 0, - addr: "/0=unsorted, 1=sorted, 2=foldcase/" - }, - { - name: '!_TAG_PROGRAM_AUTHOR', - tagfile: "Patrick Walton", - addr: "/pwalton@mozilla.com/" - }, - { name: '!_TAG_PROGRAM_NAME', tagfile: "jsctags" }, - { - name: '!_TAG_PROGRAM_URL', - tagfile: "http://github.com/pcwalton/jsctags", - addr: "/GitHub repository/" - }, - { name: '!_TAG_PROGRAM_VERSION', tagfile: "0.1" } + { name: '!_TAG_FILE_FORMAT', tagfile: 2, addr: "/extended format/" }, + { + name: '!_TAG_FILE_SORTED', + tagfile: 0, + addr: "/0=unsorted, 1=sorted, 2=foldcase/" + }, + { + name: '!_TAG_PROGRAM_AUTHOR', + tagfile: "Patrick Walton", + addr: "/pwalton@mozilla.com/" + }, + { name: '!_TAG_PROGRAM_NAME', tagfile: "jsctags" }, + { + name: '!_TAG_PROGRAM_URL', + tagfile: "http://github.com/pcwalton/jsctags", + addr: "/GitHub repository/" + }, + { name: '!_TAG_PROGRAM_VERSION', tagfile: "0.1" } ]; const SPECIAL_FIELDS = { addr: true, kind: true, name: true, tagfile: true }; exports.TagWriter = Trait({ - tags: Trait.required, - - init: function() { }, - - write: function(stream, opts) { - if (!opts.hasOwnProperty('sort') || opts.sort === 'no') { - var sortfunc = function(a, b) { return parseInt(a.sortno, 10) - parseInt(b.sortno, 10); }; - } else if (opts.sort === 'yes') { - var sortfunc = function(a, b) { - if (a.name === b.name) return 0; - else if (a.name < b.name) return -1; - else return 1; - }; - for (var i in METATAGS) { - if (METATAGS[i].name == '!_TAG_FILE_SORTED') { - METATAGS[i].tagfile = 1; - break; - } - } - } else { - throw new Error("Invalid value for --sort flag."); - } - this.tags = this.tags.sort(sortfunc); - this.tags = METATAGS.concat(this.tags); - - var lines = this.tags.map(function(tag) { - delete tag.sortno; - var buf = [ tag.name, "\t", tag.tagfile, "\t" ]; - - var addr = tag.addr; - buf.push(addr !== undefined ? addr : "//"); - - var tagfields = []; - for (var key in tag) { - if (!(key in SPECIAL_FIELDS)) { - tagfields.push(key); - } - } - tagfields.sort(); - - var kind = tag.kind; - if (kind === undefined && tagfields.length === 0) { - buf.push("\n"); - return buf.join(""); - } - - buf.push(";\""); - - if (kind !== undefined) { - buf.push("\t", kind); - } - - tagfields.forEach(function(tagfield) { - buf.push("\t", tagfield, ":"); - - var escape = function(str) { return ESCAPES[str]; }; - buf.push(tag[tagfield].replace("[\\\n\r\t]", escape)); - }); - - buf.push("\n"); - return buf.join(""); - }); - - lines.forEach(function(line) { stream.write(line); }); - }, - - writeJSONP: function(stream, func) { - this.tags.map(function(tag) { delete tag.sortno; }); - var json = JSON.stringify(this.tags); - _([ func, "(", json, ");" ]).each(function(str) { - stream.write(str); - }); - }, + tags: Trait.required, + + init: function() { }, + + write: function(stream, opts) { + if (!opts.hasOwnProperty('sort') || opts.sort === 'no') { + var sortfunc = function(a, b) { return parseInt(a.sortno, 10) - parseInt(b.sortno, 10); }; + } else if (opts.sort === 'yes') { + var sortfunc = function(a, b) { + if (a.name === b.name) return 0; + else if (a.name < b.name) return -1; + else return 1; + }; + for (var i in METATAGS) { + if (METATAGS[i].name == '!_TAG_FILE_SORTED') { + METATAGS[i].tagfile = 1; + break; + } + } + } else { + throw new Error("Invalid value for --sort flag."); + } + this.tags = this.tags.sort(sortfunc); + this.tags = METATAGS.concat(this.tags); + + var lines = this.tags.map(function(tag) { + delete tag.sortno; + var buf = [ tag.name, "\t", tag.tagfile, "\t" ]; + + var addr = tag.addr; + buf.push(addr !== undefined ? addr : "//"); + + var tagfields = []; + for (var key in tag) { + if (!(key in SPECIAL_FIELDS)) { + tagfields.push(key); + } + } + tagfields.sort(); + + var kind = tag.kind; + if (kind === undefined && tagfields.length === 0) { + buf.push("\n"); + return buf.join(""); + } + + buf.push(";\""); + + if (kind !== undefined) { + buf.push("\t", kind); + } + + tagfields.forEach(function(tagfield) { + buf.push("\t", tagfield, ":"); + + var escape = function(str) { return ESCAPES[str]; }; + buf.push(tag[tagfield].replace("[\\\n\r\t]", escape)); + }); + + buf.push("\n"); + return buf.join(""); + }); + + lines.forEach(function(line) { stream.write(line); }); + }, + + writeEmacs: function(stream, opts) { + var sections = {}; + this.tags.map(function(tag) { + if (!sections[tag.tagfile]) + sections[tag.tagfile] = new Array(); + sections[tag.tagfile].push(tag); + }); + for (var filename in sections) { + if (sections.hasOwnProperty(filename)) { + stream.write(" \n"); + var section = sections[filename]; + var lines = section.map(function(tag) { + var line_number = tag["lineno"]; + var addr = tag.addr.substr(2, tag.addr.length-4); + addr = addr.replace ("\\t", "\t"); + var buf = [ addr, "", tag.name, "", line_number, ",1"]; + buf.push("\n"); + return buf.join(""); + }); + var s = lines.join("\n"); + var size = s.length; + var buf = [ filename, ",", size, "\n" ] + stream.write(buf.join("")); + lines.forEach(function(line) { stream.write(line); }); + } + }; + }, + + writeJSONP: function(stream, func) { + this.tags.map(function(tag) { delete tag.sortno; }); + var json = JSON.stringify(this.tags); + _([ func, "(", json, ");" ]).each(function(str) { + stream.write(str); + }); + }, }); -