commit
488b719fde
@ -67,32 +67,57 @@ Buffer.prototype.removeLast = function (cha) {
|
|||||||
|
|
||||||
Buffer.prototype.newline = function (i, removeLast) {
|
Buffer.prototype.newline = function (i, removeLast) {
|
||||||
if (this.format.compact) return;
|
if (this.format.compact) return;
|
||||||
|
removeLast = removeLast || false;
|
||||||
if (_.isBoolean(i)) {
|
|
||||||
removeLast = i;
|
|
||||||
i = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_.isNumber(i)) {
|
if (_.isNumber(i)) {
|
||||||
if (this.endsWith("{\n")) i--;
|
if (this.endsWith("{\n")) i--;
|
||||||
if (this.endsWith(util.repeat(i, "\n"))) return;
|
if (this.endsWith(util.repeat(i, "\n"))) return;
|
||||||
|
|
||||||
for (var j = 0; j < i; j++) {
|
while (i--) {
|
||||||
this.newline(null, removeLast);
|
this._newline(removeLast);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_.isBoolean(i)) {
|
||||||
|
removeLast = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._newline(removeLast);
|
||||||
|
};
|
||||||
|
|
||||||
|
Buffer.prototype._newline = function (removeLast) {
|
||||||
if (removeLast && this.isLast("\n")) this.removeLast("\n");
|
if (removeLast && this.isLast("\n")) this.removeLast("\n");
|
||||||
|
|
||||||
this.removeLast(" ");
|
this.removeLast(" ");
|
||||||
|
|
||||||
// remove whitespace if last character was a newline
|
// remove whitespace if last character was a newline
|
||||||
this.buf = this.buf.replace(/\n +$/, "\n");
|
this._removeSpacesAfterLastNewline();
|
||||||
|
|
||||||
this._push("\n");
|
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) {
|
Buffer.prototype.push = function (str, noIndent) {
|
||||||
if (this._indent && !noIndent && str !== "\n") {
|
if (this._indent && !noIndent && str !== "\n") {
|
||||||
// we have an indent level and we aren't pushing a newline
|
// 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++) {
|
for (var i = 0; i < types.length; i++) {
|
||||||
var type = types[i];
|
var type = types[i];
|
||||||
|
|
||||||
if (t["is" + type](node)) {
|
if (t.is(type, node)) {
|
||||||
var fn = obj[type];
|
var fn = obj[type];
|
||||||
result = fn(node, parent);
|
result = fn(node, parent);
|
||||||
if (result != null) break;
|
if (result != null) break;
|
||||||
@ -28,13 +28,11 @@ function Node(node, parent) {
|
|||||||
this.node = node;
|
this.node = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node.prototype.isUserWhitespacable = function () {
|
Node.isUserWhitespacable = function (node) {
|
||||||
return t.isUserWhitespacable(this.node);
|
return t.isUserWhitespacable(node);
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype.needsWhitespace = function (type) {
|
Node.needsWhitespace = function (node, parent, type) {
|
||||||
var parent = this.parent;
|
|
||||||
var node = this.node;
|
|
||||||
if (!node) return 0;
|
if (!node) return 0;
|
||||||
|
|
||||||
if (t.isExpressionStatement(node)) {
|
if (t.isExpressionStatement(node)) {
|
||||||
@ -51,18 +49,15 @@ Node.prototype.needsWhitespace = function (type) {
|
|||||||
return lines || 0;
|
return lines || 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype.needsWhitespaceBefore = function () {
|
Node.needsWhitespaceBefore = function (node, parent) {
|
||||||
return this.needsWhitespace("before");
|
return Node.needsWhitespace(node, parent, "before");
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype.needsWhitespaceAfter = function () {
|
Node.needsWhitespaceAfter = function (node, parent) {
|
||||||
return this.needsWhitespace("after");
|
return Node.needsWhitespace(node, parent, "after");
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype.needsParens = function () {
|
Node.needsParens = function (node, parent) {
|
||||||
var parent = this.parent;
|
|
||||||
var node = this.node;
|
|
||||||
|
|
||||||
if (!parent) return false;
|
if (!parent) return false;
|
||||||
|
|
||||||
if (t.isNewExpression(parent) && parent.callee === node) {
|
if (t.isNewExpression(parent) && parent.callee === node) {
|
||||||
@ -77,10 +72,7 @@ Node.prototype.needsParens = function () {
|
|||||||
return find(parens, node, parent);
|
return find(parens, node, parent);
|
||||||
};
|
};
|
||||||
|
|
||||||
Node.prototype.needsParensNoLineTerminator = function () {
|
Node.needsParensNoLineTerminator = function (node, parent) {
|
||||||
var parent = this.parent;
|
|
||||||
var node = this.node;
|
|
||||||
|
|
||||||
if (!parent) return false;
|
if (!parent) return false;
|
||||||
|
|
||||||
// no comments
|
// no comments
|
||||||
@ -100,17 +92,18 @@ Node.prototype.needsParensNoLineTerminator = function () {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
_.each(Node.prototype, function (fn, key) {
|
_.each(Node, function (fn, key) {
|
||||||
Node[key] = function (node, parent) {
|
Node.prototype[key] = function () {
|
||||||
var n = new Node(node, parent);
|
|
||||||
|
|
||||||
// Avoid leaking arguments to prevent deoptimization
|
// Avoid leaking arguments to prevent deoptimization
|
||||||
var skipCount = 2;
|
var args = new Array(arguments.length + 2);
|
||||||
var args = new Array(arguments.length - skipCount);
|
|
||||||
|
args[0] = this.node;
|
||||||
|
args[1] = this.parent;
|
||||||
|
|
||||||
for (var i = 0; i < args.length; i++) {
|
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;
|
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) {
|
function Whitespace(tokens, comments) {
|
||||||
this.tokens = _.sortBy(tokens.concat(comments), "start");
|
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) {
|
Whitespace.prototype.getNewlinesBefore = function (node) {
|
||||||
@ -13,13 +41,17 @@ Whitespace.prototype.getNewlinesBefore = function (node) {
|
|||||||
var tokens = this.tokens;
|
var tokens = this.tokens;
|
||||||
var token;
|
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];
|
token = tokens[i];
|
||||||
|
|
||||||
// this is the token this node starts with
|
// this is the token this node starts with
|
||||||
if (node.start === token.start) {
|
if (node.start === token.start) {
|
||||||
startToken = tokens[i - 1];
|
startToken = tokens[i - 1];
|
||||||
endToken = token;
|
endToken = token;
|
||||||
|
|
||||||
|
this._lastFoundIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,13 +65,17 @@ Whitespace.prototype.getNewlinesAfter = function (node) {
|
|||||||
var tokens = this.tokens;
|
var tokens = this.tokens;
|
||||||
var token;
|
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];
|
token = tokens[i];
|
||||||
|
|
||||||
// this is the token this node ends with
|
// this is the token this node ends with
|
||||||
if (node.end === token.end) {
|
if (node.end === token.end) {
|
||||||
startToken = token;
|
startToken = token;
|
||||||
endToken = tokens[i + 1];
|
endToken = tokens[i + 1];
|
||||||
|
|
||||||
|
this._lastFoundIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,15 +96,14 @@ Whitespace.prototype.getNewlinesAfter = function (node) {
|
|||||||
Whitespace.prototype.getNewlinesBetween = function (startToken, endToken) {
|
Whitespace.prototype.getNewlinesBetween = function (startToken, endToken) {
|
||||||
var start = startToken ? startToken.loc.end.line : 1;
|
var start = startToken ? startToken.loc.end.line : 1;
|
||||||
var end = endToken.loc.start.line;
|
var end = endToken.loc.start.line;
|
||||||
|
|
||||||
var lines = 0;
|
var lines = 0;
|
||||||
|
|
||||||
for (var line = start; line < end; line++) {
|
for (var line = start; line < end; line++) {
|
||||||
if (!_.contains(this.used, line)) {
|
if (typeof this.used[line] === 'undefined') {
|
||||||
this.used.push(line);
|
this.used[line] = true;
|
||||||
lines++;
|
lines++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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) {
|
t["assert" + type] = function (node, opts) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
if (!is(node, opts)) {
|
if (!is(node, opts)) {
|
||||||
throw new Error("Expected type " + JSON.stringify(type) + " with option " + JSON.stringify(opts));
|
throw new Error("Expected type " + JSON.stringify(type) + " with option " + JSON.stringify(opts));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
t.STATEMENT_OR_BLOCK_KEYS = ["consequent", "body"];
|
t.STATEMENT_OR_BLOCK_KEYS = ["consequent", "body"];
|
||||||
|
|
||||||
@ -22,14 +33,59 @@ t.STATEMENT_OR_BLOCK_KEYS = ["consequent", "body"];
|
|||||||
|
|
||||||
t.VISITOR_KEYS = require("./visitor-keys");
|
t.VISITOR_KEYS = require("./visitor-keys");
|
||||||
|
|
||||||
_.each(t.VISITOR_KEYS, function (keys, type) {
|
t.ALIAS_KEYS = require("./alias-keys");
|
||||||
var is = t["is" + type] = function (node, opts) {
|
|
||||||
return node && node.type === type && t.shallowEqual(node, opts);
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
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
|
* Description
|
||||||
*
|
*
|
||||||
@ -139,17 +172,17 @@ t.toSequenceExpression = function (nodes, scope) {
|
|||||||
//
|
//
|
||||||
|
|
||||||
t.shallowEqual = function (actual, expected) {
|
t.shallowEqual = function (actual, expected) {
|
||||||
var same = true;
|
var keys = Object.keys(expected);
|
||||||
|
var key;
|
||||||
|
|
||||||
if (expected) {
|
for (var i = 0; i < keys.length; i++) {
|
||||||
_.each(expected, function (val, key) {
|
key = keys[i];
|
||||||
if (actual[key] !== val) {
|
|
||||||
return same = false;
|
if (actual[key] !== expected[key])
|
||||||
}
|
return false;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return same;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user