no-mixed-spaces-and-tabs.js
4.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/**
* @fileoverview Disallow mixed spaces and tabs for indentation
* @author Jary Niebur
* @copyright 2014 Nicholas C. Zakas. All rights reserved.
* @copyright 2014 Jary Niebur. All rights reserved.
* See LICENSE in root directory for full license.
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
var smartTabs,
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": function(node) {
ignoredLocs.push(node.loc);
},
"Program:exit": function(node) {
/*
* At least one space followed by a tab
* or the reverse before non-tab/-space
* characters begin.
*/
var regex = /^(?=[\t ]*(\t | \t))/,
match,
lines = context.getSourceLines(),
comments = context.getAllComments();
comments.forEach(function(comment) {
ignoredLocs.push(comment.loc);
});
ignoredLocs.sort(function(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)/;
}
lines.forEach(function(line, i) {
match = regex.exec(line);
if (match) {
var lineNumber = i + 1,
column = match.index + 1;
for (var j = 0; j < ignoredLocs.length; j++) {
if (beforeLoc(ignoredLocs[j], lineNumber, column)) {
continue;
}
if (afterLoc(ignoredLocs[j], lineNumber, column)) {
continue;
}
return;
}
context.report(node, { line: lineNumber, column: column }, "Mixed spaces and tabs.");
}
});
}
};
};
module.exports.schema = [
{
"enum": ["smart-tabs", true, false]
}
];