]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
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 | ||
11 | module.exports = { | |
12 | meta: { | |
13 | type: "layout", | |
14 | ||
15 | docs: { | |
16 | description: "disallow mixed spaces and tabs for indentation", | |
eb39fafa DC |
17 | recommended: true, |
18 | url: "https://eslint.org/docs/rules/no-mixed-spaces-and-tabs" | |
19 | }, | |
20 | ||
21 | schema: [ | |
22 | { | |
23 | enum: ["smart-tabs", true, false] | |
24 | } | |
25 | ], | |
26 | ||
27 | messages: { | |
28 | mixedSpacesAndTabs: "Mixed spaces and tabs." | |
29 | } | |
30 | }, | |
31 | ||
32 | create(context) { | |
33 | const sourceCode = context.getSourceCode(); | |
34 | ||
35 | let smartTabs; | |
36 | ||
37 | switch (context.options[0]) { | |
38 | case true: // Support old syntax, maybe add deprecation warning here | |
39 | case "smart-tabs": | |
40 | smartTabs = true; | |
41 | break; | |
42 | default: | |
43 | smartTabs = false; | |
44 | } | |
45 | ||
46 | //-------------------------------------------------------------------------- | |
47 | // Public | |
48 | //-------------------------------------------------------------------------- | |
49 | ||
50 | return { | |
51 | ||
52 | "Program:exit"(node) { | |
53 | const lines = sourceCode.lines, | |
54 | comments = sourceCode.getAllComments(), | |
55 | ignoredCommentLines = new Set(); | |
56 | ||
57 | // Add all lines except the first ones. | |
58 | comments.forEach(comment => { | |
59 | for (let i = comment.loc.start.line + 1; i <= comment.loc.end.line; i++) { | |
60 | ignoredCommentLines.add(i); | |
61 | } | |
62 | }); | |
63 | ||
64 | /* | |
65 | * At least one space followed by a tab | |
66 | * or the reverse before non-tab/-space | |
67 | * characters begin. | |
68 | */ | |
d3726936 | 69 | let regex = /^(?=( +|\t+))\1(?:\t| )/u; |
eb39fafa DC |
70 | |
71 | if (smartTabs) { | |
72 | ||
73 | /* | |
74 | * At least one space followed by a tab | |
75 | * before non-tab/-space characters begin. | |
76 | */ | |
d3726936 | 77 | regex = /^(?=(\t*))\1(?=( +))\2\t/u; |
eb39fafa DC |
78 | } |
79 | ||
80 | lines.forEach((line, i) => { | |
81 | const match = regex.exec(line); | |
82 | ||
83 | if (match) { | |
d3726936 TL |
84 | const lineNumber = i + 1; |
85 | const loc = { | |
86 | start: { | |
87 | line: lineNumber, | |
88 | column: match[0].length - 2 | |
89 | }, | |
90 | end: { | |
91 | line: lineNumber, | |
92 | column: match[0].length | |
93 | } | |
94 | }; | |
eb39fafa DC |
95 | |
96 | if (!ignoredCommentLines.has(lineNumber)) { | |
d3726936 | 97 | const containingNode = sourceCode.getNodeByRangeIndex(sourceCode.getIndexFromLoc(loc.start)); |
eb39fafa DC |
98 | |
99 | if (!(containingNode && ["Literal", "TemplateElement"].includes(containingNode.type))) { | |
100 | context.report({ | |
101 | node, | |
102 | loc, | |
103 | messageId: "mixedSpacesAndTabs" | |
104 | }); | |
105 | } | |
106 | } | |
107 | } | |
108 | }); | |
109 | } | |
110 | }; | |
111 | } | |
112 | }; |