commit
488b719fde
@ -67,32 +67,57 @@ Buffer.prototype.removeLast = function (cha) {
|
||||
|
||||
Buffer.prototype.newline = function (i, removeLast) {
|
||||
if (this.format.compact) return;
|
||||
|
||||
if (_.isBoolean(i)) {
|
||||
removeLast = i;
|
||||
i = null;
|
||||
}
|
||||
removeLast = removeLast || false;
|
||||
|
||||
if (_.isNumber(i)) {
|
||||
if (this.endsWith("{\n")) i--;
|
||||
if (this.endsWith(util.repeat(i, "\n"))) return;
|
||||
|
||||
for (var j = 0; j < i; j++) {
|
||||
this.newline(null, removeLast);
|
||||
while (i--) {
|
||||
this._newline(removeLast);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_.isBoolean(i)) {
|
||||
removeLast = i;
|
||||
}
|
||||
|
||||
this._newline(removeLast);
|
||||
};
|
||||
|
||||
Buffer.prototype._newline = function (removeLast) {
|
||||
if (removeLast && this.isLast("\n")) this.removeLast("\n");
|
||||
|
||||
this.removeLast(" ");
|
||||
|
||||
// remove whitespace if last character was a newline
|
||||
this.buf = this.buf.replace(/\n +$/, "\n");
|
||||
|
||||
this._removeSpacesAfterLastNewline();
|
||||
this._push("\n");
|
||||
};
|
||||
|
||||
/**
|
||||
* If buffer ends with a newline and some spaces after it, trim those spaces.
|
||||
*/
|
||||
Buffer.prototype._removeSpacesAfterLastNewline = function () {
|
||||
var lastNewlineIndex = this.buf.lastIndexOf('\n');
|
||||
if (lastNewlineIndex === -1)
|
||||
return;
|
||||
|
||||
var index = this.buf.length - 1;
|
||||
while (index > lastNewlineIndex) {
|
||||
if (this.buf[index] !== ' ') {
|
||||
break;
|
||||
}
|
||||
|
||||
index--;
|
||||
}
|
||||
|
||||
if (index === lastNewlineIndex) {
|
||||
this.buf = this.buf.substring(0, index + 1);
|
||||
}
|
||||
};
|
||||
|
||||
Buffer.prototype.push = function (str, noIndent) {
|
||||
if (this._indent && !noIndent && str !== "\n") {
|
||||
// we have an indent level and we aren't pushing a newline
|
||||
|
||||
@ -13,7 +13,7 @@ var find = function (obj, node, parent) {
|
||||
for (var i = 0; i < types.length; i++) {
|
||||
var type = types[i];
|
||||
|
||||
if (t["is" + type](node)) {
|
||||
if (t.is(type, node)) {
|
||||
var fn = obj[type];
|
||||
result = fn(node, parent);
|
||||
if (result != null) break;
|
||||
@ -28,13 +28,11 @@ function Node(node, parent) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
Node.prototype.isUserWhitespacable = function () {
|
||||
return t.isUserWhitespacable(this.node);
|
||||
Node.isUserWhitespacable = function (node) {
|
||||
return t.isUserWhitespacable(node);
|
||||
};
|
||||
|
||||
Node.prototype.needsWhitespace = function (type) {
|
||||
var parent = this.parent;
|
||||
var node = this.node;
|
||||
Node.needsWhitespace = function (node, parent, type) {
|
||||
if (!node) return 0;
|
||||
|
||||
if (t.isExpressionStatement(node)) {
|
||||
@ -51,18 +49,15 @@ Node.prototype.needsWhitespace = function (type) {
|
||||
return lines || 0;
|
||||
};
|
||||
|
||||
Node.prototype.needsWhitespaceBefore = function () {
|
||||
return this.needsWhitespace("before");
|
||||
Node.needsWhitespaceBefore = function (node, parent) {
|
||||
return Node.needsWhitespace(node, parent, "before");
|
||||
};
|
||||
|
||||
Node.prototype.needsWhitespaceAfter = function () {
|
||||
return this.needsWhitespace("after");
|
||||
Node.needsWhitespaceAfter = function (node, parent) {
|
||||
return Node.needsWhitespace(node, parent, "after");
|
||||
};
|
||||
|
||||
Node.prototype.needsParens = function () {
|
||||
var parent = this.parent;
|
||||
var node = this.node;
|
||||
|
||||
Node.needsParens = function (node, parent) {
|
||||
if (!parent) return false;
|
||||
|
||||
if (t.isNewExpression(parent) && parent.callee === node) {
|
||||
@ -77,10 +72,7 @@ Node.prototype.needsParens = function () {
|
||||
return find(parens, node, parent);
|
||||
};
|
||||
|
||||
Node.prototype.needsParensNoLineTerminator = function () {
|
||||
var parent = this.parent;
|
||||
var node = this.node;
|
||||
|
||||
Node.needsParensNoLineTerminator = function (node, parent) {
|
||||
if (!parent) return false;
|
||||
|
||||
// no comments
|
||||
@ -100,17 +92,18 @@ Node.prototype.needsParensNoLineTerminator = function () {
|
||||
return false;
|
||||
};
|
||||
|
||||
_.each(Node.prototype, function (fn, key) {
|
||||
Node[key] = function (node, parent) {
|
||||
var n = new Node(node, parent);
|
||||
|
||||
_.each(Node, function (fn, key) {
|
||||
Node.prototype[key] = function () {
|
||||
// Avoid leaking arguments to prevent deoptimization
|
||||
var skipCount = 2;
|
||||
var args = new Array(arguments.length - skipCount);
|
||||
var args = new Array(arguments.length + 2);
|
||||
|
||||
args[0] = this.node;
|
||||
args[1] = this.parent;
|
||||
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
args[i] = arguments[i + 2];
|
||||
args[i + 2] = arguments[i];
|
||||
}
|
||||
|
||||
return n[key].apply(n, args);
|
||||
return Node[key].apply(null, args);
|
||||
};
|
||||
});
|
||||
|
||||
@ -1,10 +1,38 @@
|
||||
module.exports = Whitespace;
|
||||
|
||||
var _ = require("lodash");
|
||||
var _ = require("lodash");
|
||||
|
||||
/**
|
||||
* Returns `i`th number from `base`, continuing from 0 when `max` is reached.
|
||||
* Useful for shifting `for` loop by a fixed number but going over all items.
|
||||
*
|
||||
* @param {Number} i Current index in the loop
|
||||
* @param {Number} base Start index for which to return 0
|
||||
* @param {Number} max Array length
|
||||
* @returns {Number} shiftedIndex
|
||||
*/
|
||||
function getLookupIndex(i, base, max) {
|
||||
i += base;
|
||||
|
||||
if (i >= max)
|
||||
i -= max;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
function Whitespace(tokens, comments) {
|
||||
this.tokens = _.sortBy(tokens.concat(comments), "start");
|
||||
this.used = [];
|
||||
this.used = {};
|
||||
|
||||
// Profiling this code shows that while generator passes over it, indexes
|
||||
// returned by `getNewlinesBefore` and `getNewlinesAfter` are always increasing.
|
||||
|
||||
// We use this implementation detail for an optimization: instead of always
|
||||
// starting to look from `this.tokens[0]`, we will start `for` loops from the
|
||||
// previous successful match. We will enumerate all tokens—but the common
|
||||
// case will be much faster.
|
||||
|
||||
this._lastFoundIndex = 0;
|
||||
}
|
||||
|
||||
Whitespace.prototype.getNewlinesBefore = function (node) {
|
||||
@ -13,13 +41,17 @@ Whitespace.prototype.getNewlinesBefore = function (node) {
|
||||
var tokens = this.tokens;
|
||||
var token;
|
||||
|
||||
for (var i = 0; i < tokens.length; i++) {
|
||||
for (var j = 0; j < tokens.length; j++) {
|
||||
// optimize for forward traversal by shifting for loop index
|
||||
var i = getLookupIndex(j, this._lastFoundIndex, this.tokens.length);
|
||||
token = tokens[i];
|
||||
|
||||
// this is the token this node starts with
|
||||
if (node.start === token.start) {
|
||||
startToken = tokens[i - 1];
|
||||
endToken = token;
|
||||
|
||||
this._lastFoundIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -33,13 +65,17 @@ Whitespace.prototype.getNewlinesAfter = function (node) {
|
||||
var tokens = this.tokens;
|
||||
var token;
|
||||
|
||||
for (var i = 0; i < tokens.length; i++) {
|
||||
for (var j = 0; j < tokens.length; j++) {
|
||||
// optimize for forward traversal by shifting for loop index
|
||||
var i = getLookupIndex(j, this._lastFoundIndex, this.tokens.length);
|
||||
token = tokens[i];
|
||||
|
||||
// this is the token this node ends with
|
||||
if (node.end === token.end) {
|
||||
startToken = token;
|
||||
endToken = tokens[i + 1];
|
||||
|
||||
this._lastFoundIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -60,15 +96,14 @@ Whitespace.prototype.getNewlinesAfter = function (node) {
|
||||
Whitespace.prototype.getNewlinesBetween = function (startToken, endToken) {
|
||||
var start = startToken ? startToken.loc.end.line : 1;
|
||||
var end = endToken.loc.start.line;
|
||||
|
||||
var lines = 0;
|
||||
|
||||
for (var line = start; line < end; line++) {
|
||||
if (!_.contains(this.used, line)) {
|
||||
this.used.push(line);
|
||||
if (typeof this.used[line] === 'undefined') {
|
||||
this.used[line] = true;
|
||||
lines++;
|
||||
}
|
||||
}
|
||||
|
||||
return lines;
|
||||
};
|
||||
};
|
||||
@ -7,14 +7,25 @@ t.NATIVE_TYPE_NAMES = ["Array", "Object", "Number", "Boolean", "Date", "Array",
|
||||
|
||||
//
|
||||
|
||||
var addAssert = function (type, is) {
|
||||
/**
|
||||
* Registers `is[Type]` and `assert[Type]` generated functions for a given `type`.
|
||||
* Pass `skipAliasCheck` to force it to directly compare `node.type` with `type`.
|
||||
*
|
||||
* @param {String} type
|
||||
* @param {Boolean?} skipAliasCheck
|
||||
*/
|
||||
function registerType(type, skipAliasCheck) {
|
||||
var is = t["is" + type] = function (node, opts) {
|
||||
return t.is(type, node, opts, skipAliasCheck);
|
||||
};
|
||||
|
||||
t["assert" + type] = function (node, opts) {
|
||||
opts = opts || {};
|
||||
if (!is(node, opts)) {
|
||||
throw new Error("Expected type " + JSON.stringify(type) + " with option " + JSON.stringify(opts));
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
t.STATEMENT_OR_BLOCK_KEYS = ["consequent", "body"];
|
||||
|
||||
@ -22,14 +33,59 @@ t.STATEMENT_OR_BLOCK_KEYS = ["consequent", "body"];
|
||||
|
||||
t.VISITOR_KEYS = require("./visitor-keys");
|
||||
|
||||
_.each(t.VISITOR_KEYS, function (keys, type) {
|
||||
var is = t["is" + type] = function (node, opts) {
|
||||
return node && node.type === type && t.shallowEqual(node, opts);
|
||||
};
|
||||
t.ALIAS_KEYS = require("./alias-keys");
|
||||
|
||||
addAssert(type, is);
|
||||
t.FLIPPED_ALIAS_KEYS = {};
|
||||
|
||||
_.each(t.VISITOR_KEYS, function (keys, type) {
|
||||
registerType(type, true);
|
||||
});
|
||||
|
||||
_.each(t.ALIAS_KEYS, function (aliases, type) {
|
||||
_.each(aliases, function (alias) {
|
||||
var types = t.FLIPPED_ALIAS_KEYS[alias] = t.FLIPPED_ALIAS_KEYS[alias] || [];
|
||||
types.push(type);
|
||||
});
|
||||
});
|
||||
|
||||
_.each(t.FLIPPED_ALIAS_KEYS, function (types, type) {
|
||||
t[type.toUpperCase() + "_TYPES"] = types;
|
||||
registerType(type, false);
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns whether `node` is of given `type`.
|
||||
* For better performance, use this instead of `is[Type]` when `type` is unknown.
|
||||
* Optionally, pass `skipAliasCheck` to directly compare `node.type` with `type`.
|
||||
*
|
||||
* @param {String} type
|
||||
* @param {Node} node
|
||||
* @param {Object?} opts
|
||||
* @param {Boolean?} skipAliasCheck
|
||||
* @returns {Boolean} isOfType
|
||||
*/
|
||||
t.is = function (type, node, opts, skipAliasCheck) {
|
||||
if (!node) return;
|
||||
|
||||
var typeMatches = (type === node.type);
|
||||
|
||||
if (!typeMatches && !skipAliasCheck) {
|
||||
var aliases = t.FLIPPED_ALIAS_KEYS[type];
|
||||
|
||||
if (typeof aliases !== 'undefined')
|
||||
typeMatches = aliases.indexOf(node.type) > -1;
|
||||
}
|
||||
|
||||
if (!typeMatches) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof opts !== 'undefined')
|
||||
return t.shallowEqual(node, opts);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
t.BUILDER_KEYS = _.defaults(require("./builder-keys"), t.VISITOR_KEYS);
|
||||
@ -45,29 +101,6 @@ _.each(t.BUILDER_KEYS, function (keys, type) {
|
||||
};
|
||||
});
|
||||
|
||||
//
|
||||
|
||||
t.ALIAS_KEYS = require("./alias-keys");
|
||||
|
||||
t.FLIPPED_ALIAS_KEYS = {};
|
||||
|
||||
_.each(t.ALIAS_KEYS, function (aliases, type) {
|
||||
_.each(aliases, function (alias) {
|
||||
var types = t.FLIPPED_ALIAS_KEYS[alias] = t.FLIPPED_ALIAS_KEYS[alias] || [];
|
||||
types.push(type);
|
||||
});
|
||||
});
|
||||
|
||||
_.each(t.FLIPPED_ALIAS_KEYS, function (types, type) {
|
||||
t[type.toUpperCase() + "_TYPES"] = types;
|
||||
|
||||
var is = t["is" + type] = function (node, opts) {
|
||||
return node && types.indexOf(node.type) >= 0 && t.shallowEqual(node, opts);
|
||||
};
|
||||
|
||||
addAssert(type, is);
|
||||
});
|
||||
|
||||
/**
|
||||
* Description
|
||||
*
|
||||
@ -139,17 +172,17 @@ t.toSequenceExpression = function (nodes, scope) {
|
||||
//
|
||||
|
||||
t.shallowEqual = function (actual, expected) {
|
||||
var same = true;
|
||||
var keys = Object.keys(expected);
|
||||
var key;
|
||||
|
||||
if (expected) {
|
||||
_.each(expected, function (val, key) {
|
||||
if (actual[key] !== val) {
|
||||
return same = false;
|
||||
}
|
||||
});
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
key = keys[i];
|
||||
|
||||
if (actual[key] !== expected[key])
|
||||
return false;
|
||||
}
|
||||
|
||||
return same;
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user