Permalink
Cannot retrieve contributors at this time
| /** | |
| * @fileoverview Disallow mixed spaces and tabs for indentation | |
| * @author Jary Niebur | |
| */ | |
| "use strict"; | |
| //------------------------------------------------------------------------------ | |
| // Rule Definition | |
| //------------------------------------------------------------------------------ | |
| module.exports = { | |
| meta: { | |
| type: "layout", | |
| docs: { | |
| description: "disallow mixed spaces and tabs for indentation", | |
| category: "Stylistic Issues", | |
| recommended: true, | |
| url: "https://eslint.org/docs/rules/no-mixed-spaces-and-tabs" | |
| }, | |
| schema: [ | |
| { | |
| enum: ["smart-tabs", true, false] | |
| } | |
| ] | |
| }, | |
| create(context) { | |
| const sourceCode = context.getSourceCode(); | |
| let smartTabs; | |
| const ignoredLocs = []; | |
| switch (context.options[0]) { | |
| case true: // Support old syntax, maybe add deprecation warning here | |
| case "smart-tabs": | |
| smartTabs = true; | |
| break; | |
| default: | |
| 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) { | |
| /* | |
| * At least one space followed by a tab | |
| * or the reverse before non-tab/-space | |
| * 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) { | |
| /* | |
| * At least one space followed by a tab | |
| * before non-tab/-space characters begin. | |
| */ | |
| regex = /^(?=[\t ]* \t)/u; | |
| } | |
| lines.forEach((line, i) => { | |
| const match = regex.exec(line); | |
| if (match) { | |
| const lineNumber = i + 1, | |
| column = match.index + 1; | |
| for (let j = 0; j < ignoredLocs.length; j++) { | |
| if (beforeLoc(ignoredLocs[j], lineNumber, column)) { | |
| continue; | |
| } | |
| if (afterLoc(ignoredLocs[j], lineNumber, column)) { | |
| continue; | |
| } | |
| return; | |
| } | |
| context.report({ node, loc: { line: lineNumber, column }, message: "Mixed spaces and tabs." }); | |
| } | |
| }); | |
| } | |
| }; | |
| } | |
| }; |