Slightly refactor and add explanations for optimized functions

This commit is contained in:
Dan Abramov 2015-01-15 03:38:46 +03:00
parent 58a91ee9e9
commit a452f781b8
4 changed files with 67 additions and 13 deletions

View File

@ -92,15 +92,14 @@ Buffer.prototype._newline = function (removeLast) {
this.removeLast(" ");
// remove whitespace if last character was a newline
var trimStart = this._getPostNewlineTrailingSpaceStart();
if (trimStart) {
this.buf = this.buf.substring(0, trimStart);
}
this._removeSpacesAfterLastNewline();
this._push("\n");
};
Buffer.prototype._getPostNewlineTrailingSpaceStart = function () {
/**
* 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;
@ -115,7 +114,7 @@ Buffer.prototype._getPostNewlineTrailingSpaceStart = function () {
}
if (index === lastNewlineIndex) {
return index + 1;
this.buf = this.buf.substring(0, index + 1);
}
};

View File

@ -3,7 +3,15 @@ module.exports = Whitespace;
var _ = require("lodash");
var util = require("../util");
// a helper to iterate array from arbitrary index
/**
* 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;
@ -17,7 +25,14 @@ function Whitespace(tokens, comments) {
this.tokens = _.sortBy(tokens.concat(comments), "start");
this.used = [];
// speed up common case where methods are called for next tokens
// 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;
}
@ -28,6 +43,7 @@ Whitespace.prototype.getNewlinesBefore = function (node) {
var token;
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];
@ -35,6 +51,7 @@ Whitespace.prototype.getNewlinesBefore = function (node) {
if (node.start === token.start) {
startToken = tokens[i - 1];
endToken = token;
this._lastFoundIndex = i;
break;
}
@ -50,6 +67,7 @@ Whitespace.prototype.getNewlinesAfter = function (node) {
var token;
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];
@ -57,6 +75,7 @@ Whitespace.prototype.getNewlinesAfter = function (node) {
if (node.end === token.end) {
startToken = token;
endToken = tokens[i + 1];
this._lastFoundIndex = i;
break;
}

View File

@ -7,9 +7,16 @@ t.NATIVE_TYPE_NAMES = ["Array", "Object", "Number", "Boolean", "Date", "Array",
//
function registerType(type, skipAliases) {
/**
* 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, skipAliases);
return t.is(type, node, opts, skipAliasCheck);
};
t["assert" + type] = function (node, opts) {
@ -46,12 +53,23 @@ _.each(t.FLIPPED_ALIAS_KEYS, function (types, type) {
registerType(type, false);
});
t.is = function (type, node, opts, skipAliases) {
/**
* 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 && !skipAliases) {
if (!typeMatches && !skipAliasCheck) {
var aliases = t.FLIPPED_ALIAS_KEYS[type];
if (typeof aliases !== 'undefined')

View File

@ -204,6 +204,24 @@ exports.repeat = function (width, cha) {
return result;
};
/**
* Merges a range of integers defined by `start` and `end` into an existing
* array of `ranges`, optimizing for common cases and attempting to keep them
* sorted. Returns the count of integers that have been added to `ranges` during
* the call.
*
* @example
* > var ranges = [{ start: 1, end: 3}, { start: 8, end : 10 }];
* > mergeIntegerRange(ranges, 6, 9);
* 2
* > ranges
* [{ start: 1, end: 3}, { start: 6, end : 10 }];
*
* @param {Array} ranges An array of ranges that function will mutate
* @param {Number} start Beginning of range being added
* @param {Number} end End of range being added
* @returns {Number} pointsAdded How many new integers have been added
*/
exports.mergeIntegerRange = function (ranges, start, end) {
var pointsAdded = 0;
var insertIndex;