kopia lustrzana https://github.com/c9/core
330 wiersze
8.9 KiB
JavaScript
330 wiersze
8.9 KiB
JavaScript
define(function(require, exports, module) {
|
|
|
|
var PROPER = require('plugins/c9.ide.language.javascript/scope_analyzer').PROPER;
|
|
var MAYBE_PROPER = require('plugins/c9.ide.language.javascript/scope_analyzer').MAYBE_PROPER;
|
|
var NOT_PROPER = require('plugins/c9.ide.language.javascript/scope_analyzer').NOT_PROPER;
|
|
var MAX_VALUES_LENGTH = 15;
|
|
|
|
var valueRegistry = {};
|
|
|
|
var contextStack = [];
|
|
|
|
function Property(values, confidence, path, row) {
|
|
this.values = values;
|
|
this.confidence = confidence;
|
|
this.path = path;
|
|
this.row = row;
|
|
}
|
|
|
|
function Value(name, node) {
|
|
this.init(name, node);
|
|
}
|
|
|
|
Value.enterContext = function(name) {
|
|
contextStack.push([name, 0]);
|
|
};
|
|
|
|
Value.leaveContext = function() {
|
|
contextStack.pop();
|
|
};
|
|
|
|
Value.prototype.init = function(name, node) {
|
|
var guid = '';
|
|
for (var i = 0; i < contextStack.length; i++) {
|
|
guid += contextStack[i][0] + '[' + contextStack[i][1] + ']/';
|
|
}
|
|
var top = contextStack[contextStack.length - 1];
|
|
if (top) {
|
|
top[1]++;
|
|
}
|
|
if (name) {
|
|
guid += name;
|
|
} else {
|
|
guid = guid.substring(0, guid.length - 1);
|
|
}
|
|
this.guid = guid;
|
|
this.properties = {};
|
|
this.doc = null;
|
|
this.docUrl = null;
|
|
if (node) {
|
|
this.pos = node.getPos();
|
|
}
|
|
valueRegistry[guid] = this;
|
|
};
|
|
|
|
Value.prototype.get = function(name) {
|
|
var coll = new ValueCollection();
|
|
if (this.properties['_' + name]) {
|
|
coll.extend(this.properties['_' + name].values);
|
|
}
|
|
|
|
if (name !== '__proto__') {
|
|
this.get('__proto__').forEach(function(p) {
|
|
coll.extendPrototype(p.get(name).toArray());
|
|
});
|
|
}
|
|
coll.deref();
|
|
return coll;
|
|
};
|
|
|
|
Value.prototype.getPropertyNames = function() {
|
|
var results = Object.keys(this.properties).map(function(s) {
|
|
return s.substr(1);
|
|
});
|
|
this.get('__proto__').forEach(function(p) {
|
|
return Object.keys(p.properties).forEach(function (s) {
|
|
results.push(s.substr(1));
|
|
});
|
|
});
|
|
return results;
|
|
};
|
|
|
|
Value.prototype.toJSON = function() {
|
|
var fieldJSON = {};
|
|
var properties = this.properties;
|
|
for (var p in properties) {
|
|
if (properties.hasOwnProperty(p)) {
|
|
var prop = properties[p];
|
|
return new Property(prop.values.map(function(v) { return v.guid; }), prop.confidence, prop.path, prop.row);
|
|
}
|
|
}
|
|
return {
|
|
guid: this.guid,
|
|
doc: this.doc,
|
|
properties: fieldJSON,
|
|
pos: this.pos
|
|
};
|
|
};
|
|
|
|
Value.prototype.markProperDeclaration = function(uname, confidence, path, row) {
|
|
if (!this.properties[uname])
|
|
return;
|
|
if (path)
|
|
this.properties[uname].path = path;
|
|
if (row)
|
|
this.properties[uname].row = row;
|
|
if (confidence && this.properties[uname].confidence < PROPER)
|
|
this.properties[uname].confidence += confidence;
|
|
};
|
|
|
|
Value.prototype.isProperDeclaration = function(name) {
|
|
if (!this.properties['_' + name])
|
|
return false;
|
|
if (this.properties['_' + name].confidence > MAYBE_PROPER)
|
|
return true;
|
|
var result;
|
|
if (name !== '__proto__') {
|
|
this.get('__proto__').forEach(function(p) {
|
|
if (p.isProperDeclaration(name))
|
|
result = true;
|
|
});
|
|
}
|
|
return result;
|
|
};
|
|
|
|
Value.prototype.hint = function(name, v, declarationConfidence, path, row) {
|
|
if (!v)
|
|
throw Error("Hinting an empty value!");
|
|
if (!this.properties['_' + name]) {
|
|
this.properties['_' + name] = new Property([v], declarationConfidence, path, row);
|
|
}
|
|
else {
|
|
var currentValues = this.properties['_' + name].values;
|
|
this.markProperDeclaration('_' + name, declarationConfidence, row);
|
|
for (var i = 0; i < currentValues.length; i++) {
|
|
if (currentValues[i].guid === v.guid) {
|
|
return;
|
|
}
|
|
}
|
|
currentValues.push(v);
|
|
}
|
|
};
|
|
|
|
Value.prototype.hintMultiple = function(name, valueColl, declarationConfidence, path, row) {
|
|
// TODO: Optimize
|
|
var _self = this;
|
|
valueColl.forEach(function(v) {
|
|
_self.hint(name, v, NOT_PROPER);
|
|
});
|
|
// Set confidence only once because it would add up otherwise
|
|
this.markProperDeclaration('_' + name, declarationConfidence, path, row);
|
|
};
|
|
|
|
function ValueCollection(values, prototypeValues) {
|
|
this.values = values || [];
|
|
this.prototypeValues = prototypeValues || [];
|
|
}
|
|
|
|
ValueCollection.prototype.extend = function(coll) {
|
|
if (coll instanceof ValueCollection) {
|
|
for (var i = 0; i < coll.values.length; i++) {
|
|
this.add(coll.values[i]);
|
|
}
|
|
for (var i = 0; i < coll.prototypeValues.length; i++) {
|
|
this.addFromPrototype(coll.prototypeValues[i]);
|
|
}
|
|
} else {
|
|
for (var i = 0; i < coll.length; i++) {
|
|
this.add(coll[i]);
|
|
}
|
|
}
|
|
};
|
|
|
|
ValueCollection.prototype.extendPrototype = function(coll) {
|
|
this.prototypeValues = this.prototypeValues.concat(coll);
|
|
};
|
|
|
|
ValueCollection.prototype.toArray = function() {
|
|
return this.values.concat(this.prototypeValues);
|
|
};
|
|
|
|
ValueCollection.prototype.add = function(value) {
|
|
if (!value)
|
|
throw Error("Adding empty value!");
|
|
if (this.values.length > MAX_VALUES_LENGTH)
|
|
return;
|
|
this.values.push(value);
|
|
};
|
|
|
|
ValueCollection.prototype.addFromPrototype = function(value) {
|
|
if (this.prototypeValues.length > MAX_VALUES_LENGTH)
|
|
return;
|
|
this.prototypeValues.push(value);
|
|
};
|
|
|
|
ValueCollection.prototype.forEach = function(fn) {
|
|
this.values.forEach(fn);
|
|
this.prototypeValues.forEach(fn);
|
|
};
|
|
|
|
ValueCollection.prototype.deref = function() {
|
|
var values = this.values;
|
|
for (var i = 0; i < values.length; i++)
|
|
if (typeof values[i] === 'string')
|
|
values[i] = valueRegistry[values[i]];
|
|
values = this.prototypeValues;
|
|
for (var i = 0; i < values.length; i++)
|
|
if (typeof values[i] === 'string')
|
|
values[i] = valueRegistry[values[i]];
|
|
};
|
|
|
|
ValueCollection.prototype.isEmpty = function() {
|
|
return this.values.length === 0 && this.prototypeValues.length === 0;
|
|
};
|
|
|
|
function FunctionValue(name, node, callOnly) {
|
|
this.init(name, node);
|
|
this.node = node;
|
|
this.callOnly = callOnly;
|
|
if (name || node) {
|
|
this.hintMultiple('__proto__', lookupValue("es5:Function").get('prototype'));
|
|
}
|
|
}
|
|
|
|
FunctionValue.prototype = new Value('<ignore>');
|
|
|
|
FunctionValue.prototype.getFargs = function() {
|
|
if (this.fargs)
|
|
return this.fargs;
|
|
else if (this.node) {
|
|
var fargs = [];
|
|
var fargsNode = this.node[1];
|
|
for (var i = 0; i < fargsNode.length; i++) {
|
|
fargs.push(fargsNode[i][0].value);
|
|
}
|
|
this.fargs = fargs;
|
|
return fargs;
|
|
}
|
|
else
|
|
return [];
|
|
};
|
|
|
|
FunctionValue.prototype.toJSON = function() {
|
|
var json = Value.prototype.toJSON.call(this);
|
|
json.fargs = this.getFargs();
|
|
return json;
|
|
};
|
|
|
|
function SerializedFunctionValue(name) {
|
|
this.init(name);
|
|
}
|
|
|
|
SerializedFunctionValue.prototype = new FunctionValue();
|
|
|
|
function instantiate(fn, initVal, node, name) {
|
|
var value = initVal || new Value(name, node);
|
|
value.hintMultiple('__proto__', fn.get('prototype'));
|
|
value.hint('constructor', fn, PROPER);
|
|
return value;
|
|
}
|
|
|
|
function lookupValue(guid) {
|
|
if (!valueRegistry[guid])
|
|
throw Error("Could not find " + guid);
|
|
return valueRegistry[guid];
|
|
}
|
|
|
|
function fromJSON(json) {
|
|
if (typeof json === "string")
|
|
return json;
|
|
|
|
var properties = json.properties || {};
|
|
var value;
|
|
|
|
if (properties._return !== undefined) {
|
|
value = new SerializedFunctionValue();
|
|
if (json.fargs) {
|
|
value.fargs = json.fargs;
|
|
for (var i = 0; i < value.fargs.length; i++) {
|
|
if (!value.fargs[i].type)
|
|
value.fargs[i].type = ["es5:Object"];
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
value = new Value();
|
|
}
|
|
|
|
for (var p in properties) {
|
|
var prop = properties[p];
|
|
// Allow property values as [v] or {values: [v]}
|
|
if (!prop.forEach) {
|
|
(prop.values || []).forEach(function(v) {
|
|
value.hint(p.substr(1), fromJSON(v), PROPER, v.path || json.path, v.row);
|
|
});
|
|
}
|
|
else {
|
|
prop.forEach(function(v) {
|
|
value.hint(p.substr(1), fromJSON(v), PROPER, v.path || json.path, v.row);
|
|
});
|
|
}
|
|
}
|
|
|
|
if (json.guid) {
|
|
valueRegistry[json.guid] = value;
|
|
}
|
|
value.guid = json.guid;
|
|
value.doc = json.doc;
|
|
value.docUrl = json.docUrl;
|
|
value.path = json.path;
|
|
value.row = json.row;
|
|
|
|
return value;
|
|
}
|
|
|
|
exports.Value = Value;
|
|
exports.ValueCollection = ValueCollection;
|
|
exports.FunctionValue = FunctionValue;
|
|
exports.instantiate = instantiate;
|
|
exports.fromJSON = fromJSON;
|
|
exports.lookupValue = lookupValue;
|
|
|
|
exports.getRegistry = function() { return valueRegistry; };
|
|
|
|
exports.reset = function() {
|
|
valueRegistry = {};
|
|
contextStack = [];
|
|
};
|
|
|
|
});
|