diff --git a/lib/rules/no-mixed-spaces-and-tabs.js b/lib/rules/no-mixed-spaces-and-tabs.js index 7b1e2c4a2a7..5bbc35287fa 100644 --- a/lib/rules/no-mixed-spaces-and-tabs.js +++ b/lib/rules/no-mixed-spaces-and-tabs.js @@ -30,7 +30,6 @@ module.exports = { const sourceCode = context.getSourceCode(); let smartTabs; - const ignoredLocs = []; switch (context.options[0]) { case true: // Support old syntax, maybe add deprecation warning here @@ -41,47 +40,23 @@ module.exports = { smartTabs = false; } - /** - * Determines if a given line and column are before a location. - * @param {Location} loc The location object from an AST node. - * @param {int} line The line to check. - * @param {int} column The column to check. - * @returns {boolean} True if the line and column are before the location, false if not. - * @private - */ - function beforeLoc(loc, line, column) { - if (line < loc.start.line) { - return true; - } - return line === loc.start.line && column < loc.start.column; - } - - /** - * Determines if a given line and column are after a location. - * @param {Location} loc The location object from an AST node. - * @param {int} line The line to check. - * @param {int} column The column to check. - * @returns {boolean} True if the line and column are after the location, false if not. - * @private - */ - function afterLoc(loc, line, column) { - if (line > loc.end.line) { - return true; - } - return line === loc.end.line && column > loc.end.column; - } - //-------------------------------------------------------------------------- // Public //-------------------------------------------------------------------------- return { - TemplateElement(node) { - ignoredLocs.push(node.loc); - }, - "Program:exit"(node) { + const lines = sourceCode.lines, + comments = sourceCode.getAllComments(), + ignoredCommentLines = new Set(); + + // Add all lines except the first ones. + comments.forEach(comment => { + for (let i = comment.loc.start.line + 1; i <= comment.loc.end.line; i++) { + ignoredCommentLines.add(i); + } + }); /* * At least one space followed by a tab @@ -89,24 +64,6 @@ module.exports = { * characters begin. */ let regex = /^(?=[\t ]*(\t | \t))/u; - const lines = sourceCode.lines, - comments = sourceCode.getAllComments(); - - comments.forEach(comment => { - ignoredLocs.push(comment.loc); - }); - - ignoredLocs.sort((first, second) => { - if (beforeLoc(first, second.start.line, second.start.column)) { - return 1; - } - - if (beforeLoc(second, first.start.line, second.start.column)) { - return -1; - } - - return 0; - }); if (smartTabs) { @@ -122,25 +79,19 @@ module.exports = { if (match) { const lineNumber = i + 1, - column = match.index + 1; + column = match.index + 1, + loc = { line: lineNumber, column }; - for (let j = 0; j < ignoredLocs.length; j++) { - if (beforeLoc(ignoredLocs[j], lineNumber, column)) { - continue; - } - if (afterLoc(ignoredLocs[j], lineNumber, column)) { - continue; - } + if (!ignoredCommentLines.has(lineNumber)) { + const containingNode = sourceCode.getNodeByRangeIndex(sourceCode.getIndexFromLoc(loc)); - return; + if (!(containingNode && ["Literal", "TemplateElement"].includes(containingNode.type))) { + context.report({ node, loc, message: "Mixed spaces and tabs." }); + } } - - context.report({ node, loc: { line: lineNumber, column }, message: "Mixed spaces and tabs." }); } }); } - }; - } }; diff --git a/tests/lib/rules/no-mixed-spaces-and-tabs.js b/tests/lib/rules/no-mixed-spaces-and-tabs.js index d1333b2dbb1..3e39c51dcd3 100644 --- a/tests/lib/rules/no-mixed-spaces-and-tabs.js +++ b/tests/lib/rules/no-mixed-spaces-and-tabs.js @@ -25,6 +25,12 @@ ruleTester.run("no-mixed-spaces-and-tabs", rule, { "\t/*\n\t * Hello\n\t */", "// foo\n\t/**\n\t * Hello\n\t */", "/*\n\n \t \n*/", + "/*\t */ //", + "/*\n \t*/ //", + "/*\n\t *//*\n \t*/", + "// \t", + "/*\n*/\t ", + "/* \t\n\t \n \t\n\t */ \t", { code: "\tvar x = 5,\n\t y = 2;", options: [true] @@ -69,6 +75,8 @@ ruleTester.run("no-mixed-spaces-and-tabs", rule, { code: "`foo${ 5 }\t `;", env: { es6: true } }, + "' \t\\\n\t multiline string';", + "'\t \\\n \tmultiline string';", { code: "\tvar x = 5,\n\t y = 2;", options: ["smart-tabs"] @@ -96,6 +104,56 @@ ruleTester.run("no-mixed-spaces-and-tabs", rule, { } ] }, + { + code: " \t/* comment */", + errors: [ + { + message: "Mixed spaces and tabs.", + type: "Program", + line: 1 + } + ] + }, + { + code: "\t // comment", + errors: [ + { + message: "Mixed spaces and tabs.", + type: "Program", + line: 1 + } + ] + }, + { + code: "\t var a /* comment */ = 1;", + errors: [ + { + message: "Mixed spaces and tabs.", + type: "Program", + line: 1 + } + ] + }, + { + code: " \tvar b = 1; // comment", + errors: [ + { + message: "Mixed spaces and tabs.", + type: "Program", + line: 1 + } + ] + }, + { + code: "/**/\n \t/*\n \t*/", + errors: [ + { + message: "Mixed spaces and tabs.", + type: "Program", + line: 2 + } + ] + }, { code: "\t var x = 5, y = 2, z = 5;\n\n\t \tvar j =\t x + y;\nz *= j;", errors: [ @@ -157,6 +215,26 @@ ruleTester.run("no-mixed-spaces-and-tabs", rule, { column: 2 } ] + }, + { + code: " \t'';", + errors: [ + { + message: "Mixed spaces and tabs.", + type: "Program", + line: 1 + } + ] + }, + { + code: "''\n\t ", + errors: [ + { + message: "Mixed spaces and tabs.", + type: "Program", + line: 2 + } + ] } ] });