Skip to content

Commit

Permalink
validateIndentation: autofixing!
Browse files Browse the repository at this point in the history
  • Loading branch information
mikesherov committed Feb 21, 2015
1 parent 43d658e commit bf2ea5b
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 5 deletions.
6 changes: 4 additions & 2 deletions lib/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ Errors.prototype = {
* @param {String} message
* @param {Number|Object} line
* @param {Number} [column] optional if line is an object
* @param {Boolean} [fixed] optional if error was fixed at the time of creation
*/
add: function(message, line, column) {
add: function(message, line, column, fixed) {
if (typeof line === 'object') {
column = line.column;
line = line.line;
Expand All @@ -46,7 +47,8 @@ Errors.prototype = {
this._addError({
message: message,
line: line,
column: column
column: column,
fixed: fixed || false
});
},

Expand Down
11 changes: 11 additions & 0 deletions lib/js-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ var JsFile = function(filename, source, tree, options) {
};

JsFile.prototype = {
/**
* Returns the first line break character encountered in the file.
* Assumes LF if the file is only one line.
*
* @returns {String}
*/
getLineBreak: function() {
var lineBreaks = this.getLineBreaks();
return lineBreaks.length ? lineBreaks[0] : '\n';
},

/**
* Returns all line break characters from the file.
*
Expand Down
73 changes: 70 additions & 3 deletions lib/rules/validate-indentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,51 @@ module.exports.prototype = {
return node[childrenProperty];
}

function getIndentationFromLine(i) {
function editWhitespaceByLine(token, callback) {
var lines = token.whitespaceBefore.split(lineBreak);
if (!lines.length) {
return;
}

callback(lines);
token.whitespaceBefore = lines.join(lineBreak);
}

function fixLine(lineNumber, expectedIndentation) {
var token = file.getFirstTokenOnLine(lineNumber, {includeComments: true});
var newWhitespace = (new Array(expectedIndentation + 1)).join(indentChar);

if (!token) {
fixEmptyLine(lineNumber, newWhitespace);
return;
}

editWhitespaceByLine(token, function(lines) {
lines[lines.length - 1] = newWhitespace;
});
}

function fixEmptyLine(lineNumber, newWhitespace) {
var token;
do {
token = file.getFirstTokenOnLine(++lineNumber, {includeComments: true});
} while (!token);

editWhitespaceByLine(token, function(lines) {
if (lines[0] !== '') {
lines[0] = newWhitespace;
}

if (lines[lines.length - 1] !== '') {
lines[lines.length - 1] = newWhitespace;
}
for (var i = 1; i < lines.length - 1; i++) {
lines[i] = newWhitespace;
}
});
}

function getIndentationFromLine(line) {
var rNotIndentChar = new RegExp('[^' + indentChar + ']');
var firstContent = line.search(rNotIndentChar);
if (firstContent === -1) {
Expand All @@ -255,21 +299,43 @@ module.exports.prototype = {
}

function checkIndentations() {
var lineAugment = 0;

linesToCheck.forEach(function(line, i) {
var lineNumber = i + 1;
var actualIndentation = line.indentation;
var expectedIndentation = getExpectedIndentation(line, actualIndentation);

// do not augment this line considering this line changes indentation
if (line.pop.length || line.push.length) {
lineAugment = 0;
}

if (line.check) {
if (actualIndentation !== expectedIndentation) {
errors.add(
'Expected indentation of ' + expectedIndentation + ' characters',
i + 1,
expectedIndentation
lineNumber,
expectedIndentation,
true
);
// for multiline statements, we need move subsequent lines over the correct
// number of spaces to match the change made to the first line of the statement.
lineAugment = expectedIndentation - actualIndentation;
// correct the indentation so that future lines
// can be validated appropriately
actualIndentation = expectedIndentation;
fixLine(lineNumber, expectedIndentation);
}
} else if (lineAugment !== 0 && !line.empty) {
// in the case that we moved a previous line over a certain number spaces,
// we need to move this line over as well
fixLine(lineNumber, actualIndentation + lineAugment);
}

// do not augment the next line considering this line changes indentation
if (line.pop.length || line.push.length) {
lineAugment = 0;
}

if (line.push.length) {
Expand Down Expand Up @@ -490,6 +556,7 @@ module.exports.prototype = {

var indentChar = this._indentChar;
var indentSize = this._indentSize;
var lineBreak = file.getLineBreak();

var indentStack = [0];
var linesToCheck = file.getLines().map(function(line) {
Expand Down
53 changes: 53 additions & 0 deletions test/data/validate-indentation/fix-expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
if (a) {
var b = c;
var d = e
* f;
var e = f; // <-
// MISALIGNED COMMENT
function g() {
if (h) {
var i = j;
} // <-
} // <-

while (k) l++;
while (m) {
n--; // ->
} // <-

do {
o = p +
q; // NO ERROR: DON'T VALIDATE MULTILINE STATEMENTS
o = p +
q;
} while(r); // <-

for (var s in t) {
u++;
}

for (;;) { // <-
v++; // <-
}

if ( w ) {
x++;
} else if (y) {
z++; // <-
aa++;
} else { // <-
bb++; // ->
} // ->
}

if (g) {
// COMMENT
var a = b +
c;

var e = f + g;

}
else if(c) {
d++;
}
11 changes: 11 additions & 0 deletions test/data/validate-indentation/fix-include-empty-expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

if (a) {

var b = c;

while (c) {
d++;
}


}
12 changes: 12 additions & 0 deletions test/data/validate-indentation/fix-include-empty-input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

if (a) {

var b = c;

while (c) {
d++;
}


}

53 changes: 53 additions & 0 deletions test/data/validate-indentation/fix-input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
if (a) {
var b = c;
var d = e
* f;
var e = f; // <-
// MISALIGNED COMMENT
function g() {
if (h) {
var i = j;
} // <-
} // <-

while (k) l++;
while (m) {
n--; // ->
} // <-

do {
o = p +
q; // NO ERROR: DON'T VALIDATE MULTILINE STATEMENTS
o = p +
q;
} while(r); // <-

for (var s in t) {
u++;
}

for (;;) { // <-
v++; // <-
}

if ( w ) {
x++;
} else if (y) {
z++; // <-
aa++;
} else { // <-
bb++; // ->
} // ->
}

if (g) {
// COMMENT
var a = b +
c;

var e = f + g;

}
else if(c) {
d++;
}
16 changes: 16 additions & 0 deletions test/rules/validate-indentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,22 @@ describe('rules/validate-indentation', function() {
});
});

describe('fixing', function() {
it('should fix files with 2 spaces', function() {
checker.configure({ validateIndentation: 2 });
var fixtureInput = readData('validate-indentation/fix-input.js');
var fixtureExpected = readData('validate-indentation/fix-expected.js');
assert.equal(checker.fixString(fixtureInput).output, fixtureExpected);
});

it('should fix files with 2 spaces and includeEmptyLines', function() {
checker.configure({ validateIndentation: { value: 2, includeEmptyLines: true } });
var fixtureInput = readData('validate-indentation/fix-include-empty-input.js');
var fixtureExpected = readData('validate-indentation/fix-include-empty-expected.js');
assert.equal(checker.fixString(fixtureInput).output, fixtureExpected);
});
});

describe('switch identation', function() {
beforeEach(function() {
checker.configure({ validateIndentation: 4 });
Expand Down

0 comments on commit bf2ea5b

Please sign in to comment.