diff --git a/bin/condense b/bin/condense index 45fde8c6..35c8032c 100755 --- a/bin/condense +++ b/bin/condense @@ -2,13 +2,14 @@ var tern = require("../lib/tern"), condense = require("../lib/condense"); var path = require("path"), fs = require("fs"); -require("../plugin/doc_comment"); +var disableDocComment = process.argv.indexOf("--disable-doc-comment") > -1; +if (!disableDocComment) require("../plugin/doc_comment"); var localDir = process.cwd(), ourDir = path.resolve(__dirname, ".."); var spans = true; function usage(exit) { - console.error("usage: " + process.argv[1] + " [--name name] [--plugin name]* [--def name]* [+extrafile.js]* [file.js]+"); + console.error("usage: " + process.argv[1] + " [--no-spans] [--disable-doc-comment] [--name name] [--plugin name]* [--def name]* [+extrafile.js]* [file.js]+"); if (exit != null) process.exit(exit); } @@ -60,6 +61,7 @@ for (var i = 2, len = process.argv.length; i < len; ++i) { loadDef(process.argv[++i]); } else if (cur == "--name" && i < len - 1) { name = process.argv[++i]; + } else if (cur == "--disable-doc-comment") { } else if (cur == "--no-spans") { spans = false; } else if (cur.charAt(0) == "-") { diff --git a/bin/test b/bin/test index 72565937..3c5e4610 100755 --- a/bin/test +++ b/bin/test @@ -7,5 +7,6 @@ require("../test/fragments").runTests(filter); require("../test/condense").runTests(filter); require("../test/timeout").runTests(filter); require("../test/reload").runTests(filter); +require("../test/def-parser").runTests(filter); process.exit(require("../test/util").hasFailed()); diff --git a/lib/condense.js b/lib/condense.js index 9895727e..1272ff60 100644 --- a/lib/condense.js +++ b/lib/condense.js @@ -94,6 +94,10 @@ function reach(type, path, id, state, byName) { var actual = type.getType(false); + if (!actual && type.path && type.path !== "?") { + state.altPaths[id] = type; + return; + } if (!actual) return; var orig = type.origin || actual.origin, relevant = false; if (orig) { @@ -113,7 +117,7 @@ state.altPaths[oldPath] = actual; } else data = {type: actual}; data.span = state.getSpan(type) || (actual != type && state.isTarget(actual.origin) && state.getSpan(actual)) || data.span; - data.doc = type.doc || (actual != type && state.isTarget(actual.origin) && type.doc) || data.doc; + data.doc = type.doc || (actual != type && state.isTarget(actual.origin) && actual.doc) || data.doc; data.data = actual.metaData; data.byName = data.byName == null ? !!byName : data.byName && byName; state.types[newPath] = data; @@ -129,6 +133,8 @@ infer.Prim.prototype.reached = function() {return true;}; + infer.UnionType.prototype.reached = function() {return true;}; + infer.Arr.prototype.reached = function(path, state, concrete) { if (!concrete) reachByName(this.getProp(""), path, "", state); return true; @@ -226,6 +232,8 @@ return name; } + infer.UnionType.prototype.typeName = function () { return this.toString(); } + infer.Prim.prototype.typeName = function() { return this.name; }; infer.Arr.prototype.typeName = function() { diff --git a/lib/def.js b/lib/def.js index 8bb83c0e..e1187030 100644 --- a/lib/def.js +++ b/lib/def.js @@ -76,7 +76,18 @@ if (computeRetStart != null) fn.computeRetSource = this.spec.slice(computeRetStart, this.pos); return fn; }, + parseUnionType: function(name, top) { + var types = []; + while (true) { + types.push(this.parseType()); + if(!this.eat("|")) break; + } + return new infer.UnionType(types); + }, parseType: function(name, top) { + if (this.eat("(")) { + return this.parseUnionType(name, top); + } if (this.eat("fn(")) { return this.parseFnType(name, top); } else if (this.eat("[")) { @@ -332,6 +343,7 @@ if (typeof inner == "string") { if (type) continue; parseType(inner, innerPath).propagate(known); + known.path = inner; } else { if (!isSimpleAnnotation(inner)) { passTwo(type, inner, innerPath); diff --git a/lib/infer.js b/lib/infer.js index 716fbf02..df73b543 100644 --- a/lib/infer.js +++ b/lib/infer.js @@ -410,6 +410,24 @@ if (this.proto) this.proto.gatherProperties(f, depth); } }); + + var UnionType = exports.UnionType = function(array) { this.value = array; } + UnionType.prototype = extend(Type.prototype, { + constructor: UnionType, + toString: function() { + var format = "("; + for (var i = 0; i < this.value.length; i++) { + format += this.value[i].getType().toString(); + if (i < this.value.length-1 ) format += "|"; + } + format += ")"; + return format; + }, + gatherProperties: function(f, depth) { + for (var i = 0; i < this.value.length; ++i) + this.value[i].gatherProperties(f, depth); + } + }) var Obj = exports.Obj = function(proto, name) { if (!this.props) this.props = Object.create(null); diff --git a/test/condense-utils.js b/test/condense-utils.js new file mode 100644 index 00000000..833ed5e4 --- /dev/null +++ b/test/condense-utils.js @@ -0,0 +1,59 @@ +var util = require("./util"); +var tern = require("../lib/tern"), condense = require("../lib/condense"); +var fs = require("fs"), path = require("path"); + +var condenseDir = "test/condense"; +exports.condenseConf = { + projectDir: path.resolve(__dirname, ".."), + resolve: function(pth) { return path.resolve(this.projectDir, pth); }, + jsonFile: function(name) { return this.resolve(condenseDir + "/" + name.replace(/\.js$/, ".json")) } +} + +exports.runTest = function(conf,options) { + var server = new tern.Server({ + defs: [util.ecma5, util.browser], + plugins: options.plugins, + projectDir: conf.resolve(condenseDir), + getFile: function(name) { + return fs.readFileSync(path.resolve(condenseDir, name), "utf8"); + } + }); + options.load.forEach(function(file) { + server.addFile(file); + }); + server.flush(function() { + var origins = options.include || options.load; + var condensed = condense.condense(origins, null, {sortOutput: true}); + var out = JSON.stringify(condensed, null, 2); + var expect = fs.readFileSync(conf.jsonFile(origins[0]), "utf8").trim(); + if (out != expect) + return util.failure("condense/" + origins[0] + ": Mismatch in condense output.\nGot " + + out + "\nExpected " + expect); + + // Test loading the condensed defs. + var server2 = new tern.Server({ + defs: [util.ecma5, util.browser, condensed], + plugins: options.plugins + }); + server2.flush(function() { + var condensed = condense.condense(origins, null, {sortOutput: true}); + var out = JSON.stringify(condensed, null, 2); + if (out != expect) + util.failure("condense/" + origins[0] + ": Mismatch in condense output after loading defs. Got " + + out + "\nExpected " + expect); + }); + }); +} + +function jsFile(f) { + return f + ".js"; +} +exports.testConf = function(condenseConf,filter,options) { + if (typeof options == "string") options = {load: [options]}; + options.load = options.load.map(jsFile); + if (options.include) options.include = options.include.map(jsFile); + if (filter && options.load[0].indexOf(filter) == -1) return; + util.addTest(); + util.addFile(); + exports.runTest(condenseConf,options); +} \ No newline at end of file diff --git a/test/condense.js b/test/condense.js index bcd8a007..c93b8290 100644 --- a/test/condense.js +++ b/test/condense.js @@ -1,61 +1,9 @@ -var util = require("./util"); -var tern = require("../lib/tern"), condense = require("../lib/condense"); -var fs = require("fs"), path = require("path"); require("../plugin/angular"); require("../plugin/node"); - -var condenseDir = "test/condense"; -function jsonFile(name) { return util.resolve(condenseDir + "/" + name.replace(/\.js$/, ".json")); } - -function runTest(options) { - var server = new tern.Server({ - defs: [util.ecma5, util.browser], - plugins: options.plugins, - projectDir: util.resolve(condenseDir), - getFile: function(name) { - return fs.readFileSync(path.resolve(condenseDir, name), "utf8"); - } - }); - options.load.forEach(function(file) { - server.addFile(file); - }); - server.flush(function() { - var origins = options.include || options.load; - var condensed = condense.condense(origins, null, {sortOutput: true}); - var out = JSON.stringify(condensed, null, 2); - var expect = fs.readFileSync(jsonFile(origins[0]), "utf8").trim(); - if (out != expect) - return util.failure("condense/" + origins[0] + ": Mismatch in condense output. Got " + - out + "\nExpected " + expect); - - // Test loading the condensed defs. - var server2 = new tern.Server({ - defs: [util.ecma5, util.browser, condensed], - plugins: options.plugins - }); - server2.flush(function() { - var condensed = condense.condense(origins, null, {sortOutput: true}); - var out = JSON.stringify(condensed, null, 2); - if (out != expect) - util.failure("condense/" + origins[0] + ": Mismatch in condense output after loading defs. Got " + - out + "\nExpected " + expect); - }); - }); -} +var condenseUtils = require("./condense-utils"); exports.runTests = function(filter) { - function jsFile(f) { - return f + ".js"; - } - function test(options) { - if (typeof options == "string") options = {load: [options]}; - options.load = options.load.map(jsFile); - if (options.include) options.include = options.include.map(jsFile); - if (filter && options.load[0].indexOf(filter) == -1) return; - util.addTest(); - util.addFile(); - runTest(options); - } + function test(options) { condenseUtils.testConf(condenseUtils.condenseConf, filter, options) }; test("basic"); test("fn"); @@ -86,4 +34,4 @@ exports.runTests = function(filter) { test({load: ["requirejs_const", "requirejs_dep"], include: ["requirejs_dep", "requirejs_const"], plugins: {requirejs: true}}); test("recursive"); -}; +} diff --git a/test/def-parser.js b/test/def-parser.js new file mode 100644 index 00000000..9463dedc --- /dev/null +++ b/test/def-parser.js @@ -0,0 +1,30 @@ +var util = require("./util"); + +var infer = require("../lib/infer"); +var def = require("../lib/def").init({}, infer); + +exports.runTests = function(filter) { + var cx = new infer.Context([],null); + infer.withContext(cx, function() { + cx.protos.Object = new infer.Obj(null, "Object.prototype"); + cx.topScope = new infer.Scope(); + cx.topScope.name = ""; + cx.protos.Array = new infer.Obj(true, "Array.prototype"); + cx.protos.Function = new infer.Obj(true, "Function.prototype"); + cx.protos.RegExp = new infer.Obj(true, "RegExp.prototype"); + cx.protos.String = new infer.Obj(true, "String.prototype"); + cx.protos.Number = new infer.Obj(true, "Number.prototype"); + cx.protos.Boolean = new infer.Obj(true, "Boolean.prototype"); + cx.str = new infer.Prim(cx.protos.String, "string"); + cx.bool = new infer.Prim(cx.protos.Boolean, "bool"); + cx.num = new infer.Prim(cx.protos.Number, "number"); + cx.curOrigin = null; + + var typeString = "(number|string)"; + var unionType = new def.TypeParser(typeString, "", null, true).parseType("??", true); + util.addTest(); + util.addFile(); + if (unionType.toString() !== "(number|string)") util.failure("parsing " + typeString + " failed, got: " + unionType.toString()); + }); + +} \ No newline at end of file