diff --git a/lib/rules/no-multiple-empty-lines.js b/lib/rules/no-multiple-empty-lines.js index daab826f4c3d..96b043918938 100644 --- a/lib/rules/no-multiple-empty-lines.js +++ b/lib/rules/no-multiple-empty-lines.js @@ -17,6 +17,8 @@ module.exports = { recommended: false }, + fixable: "whitespace", + schema: [ { type: "object", @@ -76,21 +78,34 @@ module.exports = { "Program:exit": function checkBlankLines(node) { var lines = sourceCode.lines, - currentLocation = -1, - lastLocation, + fullLines = sourceCode.text.match(/.*(\r\n|\r|\n|\u2028|\u2029)/g), + firstNonBlankLine = -1, + trimmedLines = [], + linesRangeStart = [], blankCounter = 0, + currentLocation, + lastLocation, location, firstOfEndingBlankLines, - firstNonBlankLine = -1, - trimmedLines = []; + diff, + fix, + rangeStart, + rangeEnd; + + fix = function(fixer) { + return fixer.removeRange([rangeStart, rangeEnd]); + }; + linesRangeStart.push(0); lines.forEach(function(str, i) { - var trimmed = str.trim(); + var length = i < fullLines.length ? fullLines[i].length : 0, + trimmed = str.trim(); if ((firstNonBlankLine === -1) && (trimmed !== "")) { firstNonBlankLine = i; } + linesRangeStart.push(linesRangeStart[linesRangeStart.length - 1] + length); trimmedLines.push(trimmed); }); @@ -122,8 +137,15 @@ module.exports = { // Aggregate and count blank lines if (firstNonBlankLine > maxBOF) { - context.report(node, 0, - "Too many blank lines at the beginning of file. Max of " + maxBOF + " allowed."); + diff = firstNonBlankLine - maxBOF; + rangeStart = linesRangeStart[firstNonBlankLine - diff]; + rangeEnd = linesRangeStart[firstNonBlankLine]; + context.report({ + node: node, + loc: node.loc.start, + message: "Too many blank lines at the beginning of file. Max of " + maxBOF + " allowed.", + fix: fix + }); } currentLocation = firstNonBlankLine - 1; @@ -144,20 +166,29 @@ module.exports = { // within the file, not at the end if (blankCounter >= max) { + diff = blankCounter - max + 1; + rangeStart = linesRangeStart[location.line - diff]; + rangeEnd = linesRangeStart[location.line]; + context.report({ node: node, loc: location, - message: "More than " + max + " blank " + (max === 1 ? "line" : "lines") + " not allowed." + message: "More than " + max + " blank " + (max === 1 ? "line" : "lines") + " not allowed.", + fix: fix }); } } else { // inside the last blank lines if (blankCounter > maxEOF) { + diff = blankCounter - maxEOF + 1; + rangeStart = linesRangeStart[location.line - diff]; + rangeEnd = linesRangeStart[location.line - 1]; context.report({ node: node, loc: location, - message: "Too many blank lines at the end of file. Max of " + maxEOF + " allowed." + message: "Too many blank lines at the end of file. Max of " + maxEOF + " allowed.", + fix: fix }); } } diff --git a/tests/lib/rules/no-multiple-empty-lines.js b/tests/lib/rules/no-multiple-empty-lines.js index 428dc98aac62..08c3d4020022 100644 --- a/tests/lib/rules/no-multiple-empty-lines.js +++ b/tests/lib/rules/no-multiple-empty-lines.js @@ -116,6 +116,10 @@ ruleTester.run("no-multiple-empty-lines", rule, { code: "\n// valid 11\nvar a = 1;\n", options: [{ max: 2, maxBOF: 1 }] }, + { + code: "// valid 12\r\n// windows line endings\r\nvar a = 5;\r\nvar b = 3;\r\n\r\n", + options: [ { max: 1 } ] + }, // template strings { @@ -151,99 +155,130 @@ ruleTester.run("no-multiple-empty-lines", rule, { invalid: [ { code: "// invalid 1\nvar a = 5;\n\n\nvar b = 3;", + output: "// invalid 1\nvar a = 5;\n\nvar b = 3;", errors: [ getExpectedError(1) ], options: [ { max: 1 } ] }, { code: "// invalid 2\n\n\n\n\nvar a = 5;", + output: "// invalid 2\n\n\nvar a = 5;", errors: [ getExpectedError(2) ], options: [ { max: 2 } ] }, { code: "// invalid 3\nvar a = 5;\n\n\n\n", + output: "// invalid 3\nvar a = 5;\n\n\n", errors: [ getExpectedErrorEOF(2) ], options: [ { max: 2 } ] }, { code: "// invalid 4\nvar a = 5;\n \n \n \n", + output: "// invalid 4\nvar a = 5;\n \n \n", errors: [ getExpectedErrorEOF(2) ], options: [ { max: 2 } ] }, { code: "// invalid 5\nvar a=5;\n\n\n\nvar b = 3;", + output: "// invalid 5\nvar a=5;\n\n\nvar b = 3;", errors: [ getExpectedError(2) ], options: [ { max: 2 } ] }, { code: "// invalid 6\nvar a=5;\n\n\n\nvar b = 3;\n", + output: "// invalid 6\nvar a=5;\n\n\nvar b = 3;\n", errors: [ getExpectedError(2) ], options: [ { max: 2 } ] }, { code: "// invalid 7\nvar a = 5;\n\n\n\nb = 3;\nvar c = 5;\n\n\n\nvar d = 3;", + output: "// invalid 7\nvar a = 5;\n\n\nb = 3;\nvar c = 5;\n\n\nvar d = 3;", errors: 2, options: [ { max: 2 } ] }, { code: "// invalid 8\nvar a = 5;\n\n\n\n\n\n\n\n\n\n\n\n\n\nb = 3;", + output: "// invalid 8\nvar a = 5;\n\n\nb = 3;", errors: [ getExpectedError(2) ], options: [ { max: 2 } ] }, { code: "// invalid 9\nvar a=5;\n\n\n\n\n", + output: "// invalid 9\nvar a=5;\n\n\n", errors: [ getExpectedErrorEOF(2) ], options: [ { max: 2 } ] }, { code: "// invalid 10\nvar a = 5;\n\nvar b = 3;", + output: "// invalid 10\nvar a = 5;\nvar b = 3;", errors: [ getExpectedError(0) ], options: [ { max: 0 } ] }, { code: "// invalid 11\nvar a = 5;\n\n\n", + output: "// invalid 11\nvar a = 5;\n\n", errors: [ getExpectedErrorEOF(1) ], options: [ { max: 5, maxEOF: 1 } ] }, { code: "// invalid 12\nvar a = 5;\n\n\n\n\n\n", + output: "// invalid 12\nvar a = 5;\n\n\n\n\n", errors: [ getExpectedErrorEOF(4) ], options: [ { max: 0, maxEOF: 4 } ] }, { code: "// invalid 13\n\n\n\n\n\n\n\n\nvar a = 5;\n\n\n", + output: "// invalid 13\n\n\n\n\n\n\n\n\nvar a = 5;\n\n", errors: [ getExpectedErrorEOF(1) ], options: [ { max: 10, maxEOF: 1 } ] }, { code: "// invalid 14\nvar a = 5;\n\n", + output: "// invalid 14\nvar a = 5;\n", errors: [ getExpectedErrorEOF(0) ], options: [ { max: 2, maxEOF: 0 } ] }, { code: "\n\n// invalid 15\nvar a = 5;\n", + output: "\n// invalid 15\nvar a = 5;\n", errors: [ getExpectedErrorBOF(1) ], options: [ { max: 5, maxBOF: 1 } ] }, { code: "\n\n\n\n\n// invalid 16\nvar a = 5;\n", + output: "\n\n\n\n// invalid 16\nvar a = 5;\n", errors: [ getExpectedErrorBOF(4)], options: [ { max: 0, maxBOF: 4 } ] }, { code: "\n\n// invalid 17\n\n\n\n\n\n\n\n\nvar a = 5;\n", + output: "\n// invalid 17\n\n\n\n\n\n\n\n\nvar a = 5;\n", errors: [ getExpectedErrorBOF(1) ], options: [ { max: 10, maxBOF: 1 } ] }, { code: "\n// invalid 18\nvar a = 5;\n", + output: "// invalid 18\nvar a = 5;\n", errors: [ getExpectedErrorBOF(0) ], options: [ { max: 2, maxBOF: 0 } ] }, { code: "\n\n\n// invalid 19\nvar a = 5;\n\n", + output: "// invalid 19\nvar a = 5;\n", errors: [ getExpectedErrorBOF(0), getExpectedErrorEOF(0) ], options: [ { max: 2, maxBOF: 0, maxEOF: 0 } ] + }, + { + code: "// invalid 20\r\n// windows line endings\r\nvar a = 5;\r\nvar b = 3;\r\n\r\n\r\n", + output: "// invalid 20\r\n// windows line endings\r\nvar a = 5;\r\nvar b = 3;\r\n\r\n", + errors: [ getExpectedErrorEOF(1) ], + options: [ { max: 1 } ] + }, + { + code: "// invalid 21\n// unix line endings\nvar a = 5;\nvar b = 3;\n\n\n", + output: "// invalid 21\n// unix line endings\nvar a = 5;\nvar b = 3;\n\n", + errors: [ getExpectedErrorEOF(1) ], + options: [ { max: 1 } ] } ] });