mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1029162 - Update tern to 0.6.2. r=past
This commit is contained in:
parent
703d2f56da
commit
4639208c3e
10
toolkit/devtools/tern/README
Normal file
10
toolkit/devtools/tern/README
Normal file
@ -0,0 +1,10 @@
|
||||
This is the Tern code-analysis engine packaged for the Mozilla Project.
|
||||
|
||||
Tern is a stand-alone code-analysis engine for JavaScript. It is intended to be used with a code editor plugin to enhance the editor's support for intelligent JavaScript editing
|
||||
|
||||
|
||||
# Upgrade
|
||||
|
||||
Currently used version is 0.6.2. To upgrade, download the latest release from http://ternjs.net/, and copy the files from lib/ into this directory.
|
||||
|
||||
You may also need to update the CodeMirror plugin found in browser/devtools/sourceeditor/codemirror/tern, but it will most likely work without updating.
|
0
toolkit/devtools/tern/comment.js
Normal file → Executable file
0
toolkit/devtools/tern/comment.js
Normal file → Executable file
55
toolkit/devtools/tern/condense.js
Normal file → Executable file
55
toolkit/devtools/tern/condense.js
Normal file → Executable file
@ -83,10 +83,6 @@
|
||||
return len;
|
||||
}
|
||||
|
||||
function isConcrete(path) {
|
||||
return !/\!|<i>/.test(path);
|
||||
}
|
||||
|
||||
function hop(obj, prop) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, prop);
|
||||
}
|
||||
@ -96,7 +92,7 @@
|
||||
o.proto.hasCtor && !o.hasCtor;
|
||||
}
|
||||
|
||||
function reach(type, path, id, state) {
|
||||
function reach(type, path, id, state, byName) {
|
||||
var actual = type.getType(false);
|
||||
if (!actual) return;
|
||||
var orig = type.origin || actual.origin, relevant = false;
|
||||
@ -119,21 +115,22 @@
|
||||
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.data = actual.metaData;
|
||||
data.byName = data.byName == null ? !!byName : data.byName && byName;
|
||||
state.types[newPath] = data;
|
||||
}
|
||||
} else {
|
||||
if (relevant) state.altPaths[newPath] = actual;
|
||||
}
|
||||
}
|
||||
function reachTypeOnly(aval, path, id, state) {
|
||||
function reachByName(aval, path, id, state) {
|
||||
var type = aval.getType();
|
||||
if (type) reach(type, path, id, state);
|
||||
if (type) reach(type, path, id, state, true);
|
||||
}
|
||||
|
||||
infer.Prim.prototype.reached = function() {return true;};
|
||||
|
||||
infer.Arr.prototype.reached = function(path, state, concrete) {
|
||||
if (!concrete) reachTypeOnly(this.getProp("<i>"), path, "<i>", state);
|
||||
if (!concrete) reachByName(this.getProp("<i>"), path, "<i>", state);
|
||||
return true;
|
||||
};
|
||||
|
||||
@ -141,8 +138,8 @@
|
||||
infer.Obj.prototype.reached.call(this, path, state, concrete);
|
||||
if (!concrete) {
|
||||
for (var i = 0; i < this.args.length; ++i)
|
||||
reachTypeOnly(this.args[i], path, "!" + i, state);
|
||||
reachTypeOnly(this.retval, path, "!ret", state);
|
||||
reachByName(this.args[i], path, "!" + i, state);
|
||||
reachByName(this.retval, path, "!ret", state);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
@ -178,17 +175,20 @@
|
||||
}
|
||||
|
||||
function createPath(parts, state) {
|
||||
var base = state.output;
|
||||
for (var i = parts.length - 1; i >= 0; --i) if (!isConcrete(parts[i])) {
|
||||
var def = parts.slice(0, i + 1).join(".");
|
||||
var defs = state.output["!define"];
|
||||
if (hop(defs, def)) base = defs[def];
|
||||
else defs[def] = base = {};
|
||||
parts = parts.slice(i + 1);
|
||||
}
|
||||
for (var i = 0; i < parts.length; ++i) {
|
||||
if (hop(base, parts[i])) base = base[parts[i]];
|
||||
else base = base[parts[i]] = {};
|
||||
var base = state.output, defs = state.output["!define"];
|
||||
for (var i = 0, path; i < parts.length; ++i) {
|
||||
var part = parts[i], known = path && state.types[path];
|
||||
path = path ? path + "." + part : part;
|
||||
var me = state.types[path];
|
||||
if (part.charAt(0) == "!" ||
|
||||
known && known.type.constructor != infer.Obj ||
|
||||
me && me.byName) {
|
||||
if (hop(defs, path)) base = defs[path];
|
||||
else defs[path] = base = {};
|
||||
} else {
|
||||
if (hop(base, parts[i])) base = base[part];
|
||||
else base = base[part] = {};
|
||||
}
|
||||
}
|
||||
return base;
|
||||
}
|
||||
@ -208,7 +208,10 @@
|
||||
|
||||
function storeAlt(path, type, state) {
|
||||
var parts = path.split("."), last = parts.pop();
|
||||
if (last[0] == "!") return;
|
||||
var known = state.types[parts.join(".")];
|
||||
var base = createPath(parts, state);
|
||||
if (known && known.type.constructor != infer.Obj) return;
|
||||
if (!hop(base, last)) base[last] = type.nameOverride || type.path;
|
||||
}
|
||||
|
||||
@ -268,10 +271,12 @@
|
||||
|
||||
function sortObject(obj) {
|
||||
var props = [], out = {};
|
||||
for (var prop in obj) props.push({name: prop, val: obj[prop]});
|
||||
props.sort(function(a, b) { return a.name < b.name ? -1 : 1; });
|
||||
for (var i = 0; i < props.length; ++i)
|
||||
out[props[i].name] = props[i].val;
|
||||
for (var prop in obj) props.push(prop);
|
||||
props.sort();
|
||||
for (var i = 0; i < props.length; ++i) {
|
||||
var prop = props[i];
|
||||
out[prop] = obj[prop];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
24
toolkit/devtools/tern/def.js
Normal file → Executable file
24
toolkit/devtools/tern/def.js
Normal file → Executable file
@ -1,10 +1,10 @@
|
||||
// Type description parser
|
||||
|
||||
//
|
||||
// Type description JSON files (such as ecma5.json and browser.json)
|
||||
// are used to
|
||||
//
|
||||
// A) describe types that come from native code
|
||||
|
||||
//
|
||||
// B) to cheaply load the types for big libraries, or libraries that
|
||||
// can't be inferred well
|
||||
|
||||
@ -81,6 +81,10 @@
|
||||
return this.parseFnType(name, top);
|
||||
} else if (this.eat("[")) {
|
||||
var inner = this.parseType();
|
||||
if (inner == infer.ANull && this.spec == "[b.<i>]") {
|
||||
var b = parsePath("b");
|
||||
console.log(b.props["<i>"].types.length);
|
||||
}
|
||||
this.eat("]") || this.error();
|
||||
if (top && this.base) {
|
||||
infer.Arr.call(this.base, inner);
|
||||
@ -250,10 +254,10 @@
|
||||
if (!fn) {
|
||||
base = infer.ANull;
|
||||
} else if (prop == "!ret") {
|
||||
base = fn.retval && fn.retval.getType() || infer.ANull;
|
||||
base = fn.retval && fn.retval.getType(false) || infer.ANull;
|
||||
} else {
|
||||
var arg = fn.args && fn.args[Number(prop.slice(1))];
|
||||
base = (arg && arg.getType()) || infer.ANull;
|
||||
base = (arg && arg.getType(false)) || infer.ANull;
|
||||
}
|
||||
}
|
||||
} else if (base instanceof infer.Obj) {
|
||||
@ -278,7 +282,7 @@
|
||||
}
|
||||
|
||||
function isSimpleAnnotation(spec) {
|
||||
if (!spec["!type"] || /^fn\(/.test(spec["!type"])) return false;
|
||||
if (!spec["!type"] || /^(fn\(|\[)/.test(spec["!type"])) return false;
|
||||
for (var prop in spec)
|
||||
if (prop != "!type" && prop != "!doc" && prop != "!url" && prop != "!span" && prop != "!data")
|
||||
return false;
|
||||
@ -304,7 +308,7 @@
|
||||
var inner = spec[name];
|
||||
if (typeof inner == "string" || isSimpleAnnotation(inner)) continue;
|
||||
var prop = base.defProp(name);
|
||||
passOne(prop.getType(), inner, path ? path + "." + name : name).propagate(prop);
|
||||
passOne(prop.getType(false), inner, path ? path + "." + name : name).propagate(prop);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
@ -328,7 +332,7 @@
|
||||
|
||||
for (var name in spec) if (hop(spec, name) && name.charCodeAt(0) != 33) {
|
||||
var inner = spec[name], known = base.defProp(name), innerPath = path ? path + "." + name : name;
|
||||
var type = known.getType();
|
||||
var type = known.getType(false);
|
||||
if (typeof inner == "string") {
|
||||
if (type) continue;
|
||||
parseType(inner, innerPath).propagate(known);
|
||||
@ -337,7 +341,7 @@
|
||||
passTwo(type, inner, innerPath);
|
||||
} else if (!type) {
|
||||
parseType(inner["!type"], innerPath, null, true).propagate(known);
|
||||
type = known.getType();
|
||||
type = known.getType(false);
|
||||
if (type instanceof infer.Obj) copyInfo(inner, type);
|
||||
} else continue;
|
||||
if (inner["!doc"]) known.doc = inner["!doc"];
|
||||
@ -408,12 +412,12 @@
|
||||
addType: function(tp) {
|
||||
if (tp instanceof infer.Obj && this.created++ < 5) {
|
||||
var derived = new infer.Obj(tp), spec = this.spec;
|
||||
if (spec instanceof infer.AVal) spec = spec.getType();
|
||||
if (spec instanceof infer.AVal) spec = spec.getType(false);
|
||||
if (spec instanceof infer.Obj) for (var prop in spec.props) {
|
||||
var cur = spec.props[prop].types[0];
|
||||
var p = derived.defProp(prop);
|
||||
if (cur && cur instanceof infer.Obj && cur.props.value) {
|
||||
var vtp = cur.props.value.getType();
|
||||
var vtp = cur.props.value.getType(false);
|
||||
if (vtp) p.addType(vtp);
|
||||
}
|
||||
}
|
||||
|
65
toolkit/devtools/tern/infer.js
Normal file → Executable file
65
toolkit/devtools/tern/infer.js
Normal file → Executable file
@ -119,7 +119,17 @@
|
||||
return canonicalType(this.types);
|
||||
},
|
||||
|
||||
computedPropType: function() {
|
||||
if (!this.propertyOf || !this.propertyOf.hasProp("<i>")) return null;
|
||||
var computedProp = this.propertyOf.getProp("<i>");
|
||||
if (computedProp == this) return null;
|
||||
return computedProp.getType();
|
||||
},
|
||||
|
||||
makeupType: function() {
|
||||
var computed = this.computedPropType();
|
||||
if (computed) return computed;
|
||||
|
||||
if (!this.forward) return null;
|
||||
for (var i = this.forward.length - 1; i >= 0; --i) {
|
||||
var hint = this.forward[i].typeHint();
|
||||
@ -129,7 +139,7 @@
|
||||
var props = Object.create(null), foundProp = null;
|
||||
for (var i = 0; i < this.forward.length; ++i) {
|
||||
var prop = this.forward[i].propHint();
|
||||
if (prop && prop != "length" && prop != "<i>" && prop != "✖") {
|
||||
if (prop && prop != "length" && prop != "<i>" && prop != "✖" && prop != cx.completingProperty) {
|
||||
props[prop] = true;
|
||||
foundProp = prop;
|
||||
}
|
||||
@ -163,6 +173,8 @@
|
||||
var prop = this.forward[i].propHint();
|
||||
if (prop) f(prop, null, 0);
|
||||
}
|
||||
var guessed = this.makeupType();
|
||||
if (guessed) guessed.gatherProperties(f);
|
||||
}
|
||||
});
|
||||
|
||||
@ -224,7 +236,8 @@
|
||||
},
|
||||
propHint: function() { return this.prop; },
|
||||
propagatesTo: function() {
|
||||
return {target: this.target, pathExt: "." + this.prop};
|
||||
if (this.prop == "<i>" || !/[^\w_]/.test(this.prop))
|
||||
return {target: this.target, pathExt: "." + this.prop};
|
||||
}
|
||||
});
|
||||
|
||||
@ -379,6 +392,7 @@
|
||||
|
||||
var Type = exports.Type = function() {};
|
||||
Type.prototype = extend(ANull, {
|
||||
constructor: Type,
|
||||
propagate: function(c, w) { c.addType(this, w); },
|
||||
hasType: function(other) { return other == this; },
|
||||
isEmpty: function() { return false; },
|
||||
@ -388,6 +402,7 @@
|
||||
|
||||
var Prim = exports.Prim = function(proto, name) { this.name = name; this.proto = proto; };
|
||||
Prim.prototype = extend(Type.prototype, {
|
||||
constructor: Prim,
|
||||
toString: function() { return this.name; },
|
||||
getProp: function(prop) {return this.proto.hasProp(prop) || ANull;},
|
||||
gatherProperties: function(f, depth) {
|
||||
@ -407,6 +422,7 @@
|
||||
this.origin = cx.curOrigin;
|
||||
};
|
||||
Obj.prototype = extend(Type.prototype, {
|
||||
constructor: Obj,
|
||||
toString: function(maxDepth) {
|
||||
if (!maxDepth && this.name) return this.name;
|
||||
var props = [], etc = false;
|
||||
@ -430,6 +446,7 @@
|
||||
defProp: function(prop, originNode) {
|
||||
var found = this.hasProp(prop, false);
|
||||
if (found) {
|
||||
if (found.maybePurge) found.maybePurge = false;
|
||||
if (originNode && !found.originNode) found.originNode = originNode;
|
||||
return found;
|
||||
}
|
||||
@ -441,6 +458,7 @@
|
||||
this.maybeUnregProtoPropHandler();
|
||||
} else {
|
||||
av = new AVal;
|
||||
av.propertyOf = this;
|
||||
}
|
||||
|
||||
this.props[prop] = av;
|
||||
@ -453,7 +471,9 @@
|
||||
var found = this.hasProp(prop, true) || (this.maybeProps && this.maybeProps[prop]);
|
||||
if (found) return found;
|
||||
if (prop == "__proto__" || prop == "✖") return ANull;
|
||||
return this.ensureMaybeProps()[prop] = new AVal;
|
||||
var av = this.ensureMaybeProps()[prop] = new AVal;
|
||||
av.propertyOf = this;
|
||||
return av;
|
||||
},
|
||||
broadcastProp: function(prop, val, local) {
|
||||
if (local) {
|
||||
@ -529,6 +549,7 @@
|
||||
this.retval = retval;
|
||||
};
|
||||
Fn.prototype = extend(Obj.prototype, {
|
||||
constructor: Fn,
|
||||
toString: function(maxDepth) {
|
||||
if (maxDepth) maxDepth--;
|
||||
var str = "fn(";
|
||||
@ -576,6 +597,7 @@
|
||||
if (contentType) contentType.propagate(content);
|
||||
};
|
||||
Arr.prototype = extend(Obj.prototype, {
|
||||
constructor: Arr,
|
||||
toString: function(maxDepth) {
|
||||
return "[" + toString(this.getProp("<i>").getType(), maxDepth, this) + "]";
|
||||
}
|
||||
@ -636,6 +658,23 @@
|
||||
finally { cx = old; }
|
||||
};
|
||||
|
||||
exports.TimedOut = function() {
|
||||
this.message = "Timed out";
|
||||
this.stack = (new Error()).stack;
|
||||
}
|
||||
exports.TimedOut.prototype = Object.create(Error.prototype);
|
||||
exports.TimedOut.prototype.name = "infer.TimedOut";
|
||||
|
||||
var timeout;
|
||||
exports.withTimeout = function(ms, f) {
|
||||
var end = +new Date + ms;
|
||||
var oldEnd = timeout;
|
||||
if (oldEnd && oldEnd < end) return f();
|
||||
timeout = end;
|
||||
try { return f(); }
|
||||
finally { timeout = oldEnd; }
|
||||
};
|
||||
|
||||
exports.addOrigin = function(origin) {
|
||||
if (cx.origins.indexOf(origin) < 0) cx.origins.push(origin);
|
||||
};
|
||||
@ -652,6 +691,8 @@
|
||||
try {
|
||||
var ret = f(add);
|
||||
for (var i = 0; i < list.length; i += 4) {
|
||||
if (timeout && +new Date >= timeout)
|
||||
throw new exports.TimedOut();
|
||||
depth = list[i + 3] + 1;
|
||||
list[i + 1].addType(list[i], list[i + 2]);
|
||||
}
|
||||
@ -668,6 +709,7 @@
|
||||
this.prev = prev;
|
||||
};
|
||||
Scope.prototype = extend(Obj.prototype, {
|
||||
constructor: Scope,
|
||||
defVar: function(name, originNode) {
|
||||
for (var s = this; ; s = s.proto) {
|
||||
var found = s.props[name];
|
||||
@ -717,8 +759,9 @@
|
||||
var oldOrigin = cx.curOrigin;
|
||||
cx.curOrigin = fn.origin;
|
||||
var scopeCopy = new Scope(scope.prev);
|
||||
scopeCopy.originNode = scope.originNode;
|
||||
for (var v in scope.props) {
|
||||
var local = scopeCopy.defProp(v);
|
||||
var local = scopeCopy.defProp(v, scope.props[v].originNode);
|
||||
for (var i = 0; i < args.length; ++i) if (fn.argNames[i] == v && i < args.length)
|
||||
args[i].propagate(local);
|
||||
}
|
||||
@ -780,15 +823,13 @@
|
||||
// SCOPE GATHERING PASS
|
||||
|
||||
function addVar(scope, nameNode) {
|
||||
var val = scope.defProp(nameNode.name, nameNode);
|
||||
if (val.maybePurge) val.maybePurge = false;
|
||||
return val;
|
||||
return scope.defProp(nameNode.name, nameNode);
|
||||
}
|
||||
|
||||
var scopeGatherer = walk.make({
|
||||
Function: function(node, scope, c) {
|
||||
var inner = node.body.scope = new Scope(scope);
|
||||
inner.node = node;
|
||||
inner.originNode = node;
|
||||
var argVals = [], argNames = [];
|
||||
for (var i = 0; i < node.params.length; ++i) {
|
||||
var param = node.params[i];
|
||||
@ -1075,10 +1116,13 @@
|
||||
},
|
||||
|
||||
ReturnStatement: function(node, scope, c) {
|
||||
if (node.argument && scope.fnType) {
|
||||
if (!node.argument) return;
|
||||
var output = ANull;
|
||||
if (scope.fnType) {
|
||||
if (scope.fnType.retval == ANull) scope.fnType.retval = new AVal;
|
||||
infer(node.argument, scope, c, scope.fnType.retval);
|
||||
output = scope.fnType.retval;
|
||||
}
|
||||
infer(node.argument, scope, c, output);
|
||||
},
|
||||
|
||||
ForInStatement: function(node, scope, c) {
|
||||
@ -1186,6 +1230,7 @@
|
||||
Obj.prototype.purge = function(test) {
|
||||
if (this.purgeGen == cx.purgeGen) return true;
|
||||
this.purgeGen = cx.purgeGen;
|
||||
var props = [];
|
||||
for (var p in this.props) {
|
||||
var av = this.props[p];
|
||||
if (test(av, av.originNode))
|
||||
|
0
toolkit/devtools/tern/signal.js
Normal file → Executable file
0
toolkit/devtools/tern/signal.js
Normal file → Executable file
179
toolkit/devtools/tern/tern.js
Normal file → Executable file
179
toolkit/devtools/tern/tern.js
Normal file → Executable file
@ -17,13 +17,14 @@
|
||||
var plugins = Object.create(null);
|
||||
exports.registerPlugin = function(name, init) { plugins[name] = init; };
|
||||
|
||||
var defaultOptions = {
|
||||
var defaultOptions = exports.defaultOptions = {
|
||||
debug: false,
|
||||
async: false,
|
||||
getFile: function(_f, c) { if (this.async) c(null, null); },
|
||||
defs: [],
|
||||
plugins: {},
|
||||
fetchTimeout: 1000
|
||||
fetchTimeout: 1000,
|
||||
dependencyBudget: 20000
|
||||
};
|
||||
|
||||
var queryTypes = {
|
||||
@ -63,15 +64,16 @@
|
||||
|
||||
exports.defineQueryType = function(name, desc) { queryTypes[name] = desc; };
|
||||
|
||||
function File(name) {
|
||||
function File(name, parent) {
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
this.scope = this.text = this.ast = this.lineOffsets = null;
|
||||
}
|
||||
File.prototype.asLineChar = function(pos) { return asLineChar(this, pos); };
|
||||
|
||||
function updateText(file, text, srv) {
|
||||
file.text = text;
|
||||
file.ast = infer.parse(text, srv.passes, {directSourceFile: file});
|
||||
file.ast = infer.parse(text, srv.passes, {directSourceFile: file, allowReturnOutsideFunction: true});
|
||||
file.lineOffsets = null;
|
||||
}
|
||||
|
||||
@ -83,6 +85,8 @@
|
||||
|
||||
this.handlers = Object.create(null);
|
||||
this.files = [];
|
||||
this.fileMap = Object.create(null);
|
||||
this.budgets = Object.create(null);
|
||||
this.uses = 0;
|
||||
this.pending = 0;
|
||||
this.asyncError = null;
|
||||
@ -102,13 +106,16 @@
|
||||
this.reset();
|
||||
};
|
||||
Server.prototype = signal.mixin({
|
||||
addFile: function(name, /*optional*/ text) {
|
||||
ensureFile(this, name, text);
|
||||
addFile: function(name, /*optional*/ text, parent) {
|
||||
// Don't crash when sloppy plugins pass non-existent parent ids
|
||||
if (parent && !parent in this.fileMap) parent = null;
|
||||
ensureFile(this, name, parent, text);
|
||||
},
|
||||
delFile: function(name) {
|
||||
for (var i = 0, f; i < this.files.length; ++i) if ((f = this.files[i]).name == name) {
|
||||
clearFile(this, f);
|
||||
clearFile(this, f, null, true);
|
||||
this.files.splice(i--, 1);
|
||||
delete this.fileMap[name];
|
||||
return;
|
||||
}
|
||||
},
|
||||
@ -116,6 +123,7 @@
|
||||
this.signal("reset");
|
||||
this.cx = new infer.Context(this.defs, this);
|
||||
this.uses = 0;
|
||||
this.budgets = Object.create(null);
|
||||
for (var i = 0; i < this.files.length; ++i) {
|
||||
var file = this.files[i];
|
||||
file.scope = null;
|
||||
@ -131,18 +139,18 @@
|
||||
c(err, data);
|
||||
if (self.uses > 40) {
|
||||
self.reset();
|
||||
analyzeAll(self, function(){});
|
||||
analyzeAll(self, null, function(){});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
findFile: function(name) {
|
||||
return findFile(this.files, name);
|
||||
return this.fileMap[name];
|
||||
},
|
||||
|
||||
flush: function(c) {
|
||||
var cx = this.cx;
|
||||
analyzeAll(this, function(err) {
|
||||
analyzeAll(this, null, function(err) {
|
||||
if (err) return c(err);
|
||||
infer.withContext(cx, c);
|
||||
});
|
||||
@ -169,27 +177,28 @@
|
||||
if (files.length) ++srv.uses;
|
||||
for (var i = 0; i < files.length; ++i) {
|
||||
var file = files[i];
|
||||
ensureFile(srv, file.name, file.type == "full" ? file.text : null);
|
||||
ensureFile(srv, file.name, null, file.type == "full" ? file.text : null);
|
||||
}
|
||||
|
||||
var timeBudget = typeof doc.timeout == "number" ? [doc.timeout] : null;
|
||||
if (!query) {
|
||||
analyzeAll(srv, function(){});
|
||||
analyzeAll(srv, timeBudget, function(){});
|
||||
return;
|
||||
}
|
||||
|
||||
var queryType = queryTypes[query.type];
|
||||
if (queryType.takesFile) {
|
||||
if (typeof query.file != "string") return c(".query.file must be a string");
|
||||
if (!/^#/.test(query.file)) ensureFile(srv, query.file);
|
||||
if (!/^#/.test(query.file)) ensureFile(srv, query.file, null);
|
||||
}
|
||||
|
||||
analyzeAll(srv, function(err) {
|
||||
analyzeAll(srv, timeBudget, function(err) {
|
||||
if (err) return c(err);
|
||||
var file = queryType.takesFile && resolveFile(srv, files, query.file);
|
||||
if (queryType.fullFile && file.type == "part")
|
||||
return c("Can't run a " + query.type + " query on a file fragment");
|
||||
|
||||
infer.withContext(srv.cx, function() {
|
||||
function run() {
|
||||
var result;
|
||||
try {
|
||||
result = queryType.run(srv, query, file);
|
||||
@ -198,7 +207,8 @@
|
||||
return c(e);
|
||||
}
|
||||
c(null, result);
|
||||
});
|
||||
}
|
||||
infer.withContext(srv.cx, timeBudget ? function() { infer.withTimeout(timeBudget[0], run); } : run);
|
||||
});
|
||||
}
|
||||
|
||||
@ -214,16 +224,21 @@
|
||||
return file;
|
||||
}
|
||||
|
||||
function ensureFile(srv, name, text) {
|
||||
var known = findFile(srv.files, name);
|
||||
function ensureFile(srv, name, parent, text) {
|
||||
var known = srv.findFile(name);
|
||||
if (known) {
|
||||
if (text) clearFile(srv, known, text);
|
||||
if (text != null) clearFile(srv, known, text);
|
||||
if (parentDepth(known.parent) > parentDepth(parent)) {
|
||||
known.parent = parent;
|
||||
if (known.excluded) known.excluded = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var file = new File(name);
|
||||
var file = new File(name, parent);
|
||||
srv.files.push(file);
|
||||
if (text) {
|
||||
srv.fileMap[name] = file;
|
||||
if (text != null) {
|
||||
updateText(file, text, srv);
|
||||
} else if (srv.options.async) {
|
||||
srv.startAsyncAction();
|
||||
@ -236,14 +251,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
function clearFile(srv, file, newText) {
|
||||
function clearFile(srv, file, newText, purgeVars) {
|
||||
if (file.scope) {
|
||||
infer.withContext(srv.cx, function() {
|
||||
// FIXME try to batch purges into a single pass (each call needs
|
||||
// to traverse the whole graph)
|
||||
infer.purgeTypes(file.name);
|
||||
infer.markVariablesDefinedBy(file.scope, file.name);
|
||||
infer.purgeMarkedVariables(file.scope);
|
||||
if (purgeVars) {
|
||||
infer.markVariablesDefinedBy(file.scope, file.name);
|
||||
infer.purgeMarkedVariables(file.scope);
|
||||
}
|
||||
});
|
||||
file.scope = null;
|
||||
}
|
||||
@ -271,37 +288,48 @@
|
||||
if (done) c();
|
||||
}
|
||||
|
||||
function waitOnFetch(srv, c) {
|
||||
function waitOnFetch(srv, timeBudget, c) {
|
||||
var done = function() {
|
||||
srv.off("everythingFetched", done);
|
||||
clearTimeout(timeout);
|
||||
analyzeAll(srv, c);
|
||||
analyzeAll(srv, timeBudget, c);
|
||||
};
|
||||
srv.on("everythingFetched", done);
|
||||
var timeout = setTimeout(done, srv.options.fetchTimeout);
|
||||
}
|
||||
|
||||
function analyzeAll(srv, c) {
|
||||
if (srv.pending) return waitOnFetch(srv, c);
|
||||
function analyzeAll(srv, timeBudget, c) {
|
||||
if (srv.pending) return waitOnFetch(srv, timeBudget, c);
|
||||
|
||||
var e = srv.fetchError;
|
||||
if (e) { srv.fetchError = null; return c(e); }
|
||||
|
||||
var done = true;
|
||||
for (var i = 0; i < srv.files.length; ++i) {
|
||||
var file = srv.files[i];
|
||||
if (file.text == null) done = false;
|
||||
else if (file.scope == null) analyzeFile(srv, file);
|
||||
// The second inner loop might add new files. The outer loop keeps
|
||||
// repeating both inner loops until all files have been looked at.
|
||||
for (var i = 0; i < srv.files.length;) {
|
||||
var toAnalyze = [];
|
||||
for (; i < srv.files.length; ++i) {
|
||||
var file = srv.files[i];
|
||||
if (file.text == null) done = false;
|
||||
else if (file.scope == null && !file.excluded) toAnalyze.push(file);
|
||||
}
|
||||
toAnalyze.sort(function(a, b) { return parentDepth(a.parent) - parentDepth(b.parent); });
|
||||
for (var j = 0; j < toAnalyze.length; j++) {
|
||||
var file = toAnalyze[j];
|
||||
if (file.parent && !chargeOnBudget(srv, file)) {
|
||||
file.excluded = true;
|
||||
} else if (timeBudget) {
|
||||
var startTime = +new Date;
|
||||
infer.withTimeout(timeBudget[0], function() { analyzeFile(srv, file); });
|
||||
timeBudget[0] -= +new Date - startTime;
|
||||
} else {
|
||||
analyzeFile(srv, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (done) c();
|
||||
else waitOnFetch(srv, c);
|
||||
}
|
||||
|
||||
function findFile(arr, name) {
|
||||
for (var i = 0; i < arr.length; ++i) {
|
||||
var file = arr[i];
|
||||
if (file.name == name && file.type != "part") return file;
|
||||
}
|
||||
else waitOnFetch(srv, timeBudget, c);
|
||||
}
|
||||
|
||||
function firstLine(str) {
|
||||
@ -335,15 +363,15 @@
|
||||
|
||||
function resolveFile(srv, localFiles, name) {
|
||||
var isRef = name.match(/^#(\d+)$/);
|
||||
if (!isRef) return findFile(srv.files, name);
|
||||
if (!isRef) return srv.findFile(name);
|
||||
|
||||
var file = localFiles[isRef[1]];
|
||||
if (!file) throw ternError("Reference to unknown file " + name);
|
||||
if (file.type == "full") return findFile(srv.files, file.name);
|
||||
if (file.type == "full") return srv.findFile(file.name);
|
||||
|
||||
// This is a partial file
|
||||
|
||||
var realFile = file.backing = findFile(srv.files, file.name);
|
||||
var realFile = file.backing = srv.findFile(file.name);
|
||||
var offset = file.offset;
|
||||
if (file.offsetLines) offset = {line: file.offsetLines, ch: 0};
|
||||
file.offset = offset = resolvePos(realFile, file.offsetLines == null ? file.offset : {line: file.offsetLines, ch: 0}, true);
|
||||
@ -371,7 +399,7 @@
|
||||
var scopeEnd = infer.scopeAt(realFile.ast, pos + text.length, realFile.scope);
|
||||
var scope = file.scope = scopeDepth(scopeStart) < scopeDepth(scopeEnd) ? scopeEnd : scopeStart;
|
||||
infer.markVariablesDefinedBy(scopeStart, file.name, pos, pos + file.text.length);
|
||||
file.ast = infer.parse(file.text, srv.passes, {directSourceFile: file});
|
||||
file.ast = infer.parse(file.text, srv.passes, {directSourceFile: file, allowReturnOutsideFunction: true});
|
||||
infer.analyze(file.ast, file.name, scope, srv.passes);
|
||||
infer.purgeMarkedVariables(scopeStart);
|
||||
|
||||
@ -399,6 +427,45 @@
|
||||
return file;
|
||||
}
|
||||
|
||||
// Budget management
|
||||
|
||||
function astSize(node) {
|
||||
var size = 0;
|
||||
walk.simple(node, {Expression: function() { ++size; }});
|
||||
return size;
|
||||
}
|
||||
|
||||
function parentDepth(srv, parent) {
|
||||
var depth = 0;
|
||||
while (parent) {
|
||||
parent = srv.findFile(parent).parent;
|
||||
++depth;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
function budgetName(srv, file) {
|
||||
for (;;) {
|
||||
var parent = srv.findFile(file.parent);
|
||||
if (!parent.parent) break;
|
||||
file = parent;
|
||||
}
|
||||
return file.name;
|
||||
}
|
||||
|
||||
function chargeOnBudget(srv, file) {
|
||||
var bName = budgetName(srv, file);
|
||||
var size = astSize(file.ast);
|
||||
var known = srv.budgets[bName];
|
||||
if (known == null)
|
||||
known = srv.budgets[bName] = srv.options.dependencyBudget;
|
||||
if (known < size) return false;
|
||||
srv.budgets[bName] = known - size;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Query helpers
|
||||
|
||||
function isPosition(val) {
|
||||
return typeof val == "number" || typeof val == "object" &&
|
||||
typeof val.line == "number" && typeof val.ch == "number";
|
||||
@ -511,6 +578,10 @@
|
||||
node.start == start - 1 && node.end <= end + 1;
|
||||
}
|
||||
|
||||
var jsKeywords = ("break do instanceof typeof case else new var " +
|
||||
"catch finally return void continue for switch while debugger " +
|
||||
"function this with default if throw delete in try").split(" ");
|
||||
|
||||
function findCompletions(srv, query, file) {
|
||||
if (query.end == null) throw ternError("missing .query.end field");
|
||||
var wordStart = resolvePos(file, query.end), wordEnd = wordStart, text = file.text;
|
||||
@ -552,11 +623,13 @@
|
||||
}
|
||||
|
||||
var memberExpr = infer.findExpressionAround(file.ast, null, wordStart, file.scope, "MemberExpression");
|
||||
var hookname;
|
||||
if (memberExpr &&
|
||||
(memberExpr.node.computed ? isStringAround(memberExpr.node.property, wordStart, wordEnd)
|
||||
: memberExpr.node.object.end < wordStart)) {
|
||||
var prop = memberExpr.node.property;
|
||||
prop = prop.type == "Literal" ? prop.value.slice(1) : prop.name;
|
||||
srv.cx.completingProperty = prop;
|
||||
|
||||
memberExpr.node = memberExpr.node.object;
|
||||
var tp = infer.expressionType(memberExpr);
|
||||
@ -567,11 +640,17 @@
|
||||
}
|
||||
if (!completions.length && word.length >= 2 && query.guess !== false)
|
||||
for (var prop in srv.cx.props) gather(prop, srv.cx.props[prop][0], 0);
|
||||
hookname = "memberCompletion";
|
||||
} else {
|
||||
infer.forAllLocalsAt(file.ast, wordStart, file.scope, gather);
|
||||
if (query.includeKeywords) jsKeywords.forEach(function(kw) { gather(kw, null, 0); });
|
||||
hookname = "completion";
|
||||
}
|
||||
if (srv.passes[hookname])
|
||||
srv.passes[hookname].forEach(function(hook) {hook(file, wordStart, wordEnd, gather);});
|
||||
|
||||
if (query.sort !== false) completions.sort(compareCompletions);
|
||||
srv.cx.completingProperty = null;
|
||||
|
||||
return {start: outputPos(query, file, wordStart),
|
||||
end: outputPos(query, file, wordEnd),
|
||||
@ -668,7 +747,7 @@
|
||||
target.start = query.lineCharPositions ? {line: Number(m[2]), ch: Number(m[3])} : Number(m[1]);
|
||||
target.end = query.lineCharPositions ? {line: Number(m[5]), ch: Number(m[6])} : Number(m[4]);
|
||||
} else {
|
||||
var file = findFile(srv.files, span.origin);
|
||||
var file = srv.findFile(span.origin);
|
||||
target.start = outputPos(query, file, span.node.start);
|
||||
target.end = outputPos(query, file, span.node.end);
|
||||
}
|
||||
@ -690,7 +769,7 @@
|
||||
}
|
||||
|
||||
if (span && span.node) { // refers to a loaded file
|
||||
var spanFile = span.node.sourceFile || findFile(srv.files, span.origin);
|
||||
var spanFile = span.node.sourceFile || srv.findFile(span.origin);
|
||||
var start = outputPos(query, spanFile, span.node.start), end = outputPos(query, spanFile, span.node.end);
|
||||
result.start = start; result.end = end;
|
||||
result.file = span.origin;
|
||||
@ -726,17 +805,17 @@
|
||||
};
|
||||
}
|
||||
|
||||
if (scope.node) {
|
||||
if (scope.originNode) {
|
||||
type = "local";
|
||||
if (checkShadowing) {
|
||||
for (var prev = scope.prev; prev; prev = prev.prev)
|
||||
if (checkShadowing in prev.props) break;
|
||||
if (prev) infer.findRefs(scope.node, scope, checkShadowing, prev, function(node) {
|
||||
if (prev) infer.findRefs(scope.originNode, scope, checkShadowing, prev, function(node) {
|
||||
throw ternError("Renaming `" + name + "` to `" + checkShadowing + "` would shadow the definition used at line " +
|
||||
(asLineChar(file, node.start).line + 1));
|
||||
});
|
||||
}
|
||||
infer.findRefs(scope.node, scope, name, scope, storeRef(file));
|
||||
infer.findRefs(scope.originNode, scope, name, scope, storeRef(file));
|
||||
} else {
|
||||
type = "global";
|
||||
for (var i = 0; i < srv.files.length; ++i) {
|
||||
@ -810,5 +889,5 @@
|
||||
return {files: srv.files.map(function(f){return f.name;})};
|
||||
}
|
||||
|
||||
exports.version = "0.5.1";
|
||||
exports.version = "0.6.2";
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user