UNPKG

4.62 kBJavaScriptView Raw
1/**
2 * @fileoverview Disallow mixed spaces and tabs for indentation
3 * @author Jary Niebur
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Rule Definition
9//------------------------------------------------------------------------------
10
11module.exports = {
12 meta: {
13 docs: {
14 description: "disallow mixed spaces and tabs for indentation",
15 category: "Stylistic Issues",
16 recommended: true,
17 url: "https://eslint.org/docs/rules/no-mixed-spaces-and-tabs"
18 },
19
20 schema: [
21 {
22 enum: ["smart-tabs", true, false]
23 }
24 ]
25 },
26
27 create(context) {
28 const sourceCode = context.getSourceCode();
29
30 let smartTabs;
31 const ignoredLocs = [];
32
33 switch (context.options[0]) {
34 case true: // Support old syntax, maybe add deprecation warning here
35 case "smart-tabs":
36 smartTabs = true;
37 break;
38 default:
39 smartTabs = false;
40 }
41
42 /**
43 * Determines if a given line and column are before a location.
44 * @param {Location} loc The location object from an AST node.
45 * @param {int} line The line to check.
46 * @param {int} column The column to check.
47 * @returns {boolean} True if the line and column are before the location, false if not.
48 * @private
49 */
50 function beforeLoc(loc, line, column) {
51 if (line < loc.start.line) {
52 return true;
53 }
54 return line === loc.start.line && column < loc.start.column;
55 }
56
57 /**
58 * Determines if a given line and column are after a location.
59 * @param {Location} loc The location object from an AST node.
60 * @param {int} line The line to check.
61 * @param {int} column The column to check.
62 * @returns {boolean} True if the line and column are after the location, false if not.
63 * @private
64 */
65 function afterLoc(loc, line, column) {
66 if (line > loc.end.line) {
67 return true;
68 }
69 return line === loc.end.line && column > loc.end.column;
70 }
71
72 //--------------------------------------------------------------------------
73 // Public
74 //--------------------------------------------------------------------------
75
76 return {
77
78 TemplateElement(node) {
79 ignoredLocs.push(node.loc);
80 },
81
82 "Program:exit"(node) {
83
84 /*
85 * At least one space followed by a tab
86 * or the reverse before non-tab/-space
87 * characters begin.
88 */
89 let regex = /^(?=[\t ]*(\t | \t))/;
90 const lines = sourceCode.lines,
91 comments = sourceCode.getAllComments();
92
93 comments.forEach(comment => {
94 ignoredLocs.push(comment.loc);
95 });
96
97 ignoredLocs.sort((first, second) => {
98 if (beforeLoc(first, second.start.line, second.start.column)) {
99 return 1;
100 }
101
102 if (beforeLoc(second, first.start.line, second.start.column)) {
103 return -1;
104 }
105
106 return 0;
107 });
108
109 if (smartTabs) {
110
111 /*
112 * At least one space followed by a tab
113 * before non-tab/-space characters begin.
114 */
115 regex = /^(?=[\t ]* \t)/;
116 }
117
118 lines.forEach((line, i) => {
119 const match = regex.exec(line);
120
121 if (match) {
122 const lineNumber = i + 1,
123 column = match.index + 1;
124
125 for (let j = 0; j < ignoredLocs.length; j++) {
126 if (beforeLoc(ignoredLocs[j], lineNumber, column)) {
127 continue;
128 }
129 if (afterLoc(ignoredLocs[j], lineNumber, column)) {
130 continue;
131 }
132
133 return;
134 }
135
136 context.report({ node, loc: { line: lineNumber, column }, message: "Mixed spaces and tabs." });
137 }
138 });
139 }
140
141 };
142
143 }
144};