]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-trailing-spaces.js
1930098bea00404a393a14b8936ed3ef2f9864fa
[pve-eslint.git] / eslint / lib / rules / no-trailing-spaces.js
1 /**
2 * @fileoverview Disallow trailing spaces at the end of lines.
3 * @author Nodeca Team <https://github.com/nodeca>
4 */
5 "use strict";
6
7 //------------------------------------------------------------------------------
8 // Requirements
9 //------------------------------------------------------------------------------
10
11 const astUtils = require("./utils/ast-utils");
12
13 //------------------------------------------------------------------------------
14 // Rule Definition
15 //------------------------------------------------------------------------------
16
17 module.exports = {
18 meta: {
19 type: "layout",
20
21 docs: {
22 description: "disallow trailing whitespace at the end of lines",
23 recommended: false,
24 url: "https://eslint.org/docs/rules/no-trailing-spaces"
25 },
26
27 fixable: "whitespace",
28
29 schema: [
30 {
31 type: "object",
32 properties: {
33 skipBlankLines: {
34 type: "boolean",
35 default: false
36 },
37 ignoreComments: {
38 type: "boolean",
39 default: false
40 }
41 },
42 additionalProperties: false
43 }
44 ],
45
46 messages: {
47 trailingSpace: "Trailing spaces not allowed."
48 }
49 },
50
51 create(context) {
52 const sourceCode = context.getSourceCode();
53
54 const BLANK_CLASS = "[ \t\u00a0\u2000-\u200b\u3000]",
55 SKIP_BLANK = `^${BLANK_CLASS}*$`,
56 NONBLANK = `${BLANK_CLASS}+$`;
57
58 const options = context.options[0] || {},
59 skipBlankLines = options.skipBlankLines || false,
60 ignoreComments = options.ignoreComments || false;
61
62 /**
63 * Report the error message
64 * @param {ASTNode} node node to report
65 * @param {int[]} location range information
66 * @param {int[]} fixRange Range based on the whole program
67 * @returns {void}
68 */
69 function report(node, location, fixRange) {
70
71 /*
72 * Passing node is a bit dirty, because message data will contain big
73 * text in `source`. But... who cares :) ?
74 * One more kludge will not make worse the bloody wizardry of this
75 * plugin.
76 */
77 context.report({
78 node,
79 loc: location,
80 messageId: "trailingSpace",
81 fix(fixer) {
82 return fixer.removeRange(fixRange);
83 }
84 });
85 }
86
87 /**
88 * Given a list of comment nodes, return the line numbers for those comments.
89 * @param {Array} comments An array of comment nodes.
90 * @returns {number[]} An array of line numbers containing comments.
91 */
92 function getCommentLineNumbers(comments) {
93 const lines = new Set();
94
95 comments.forEach(comment => {
96 const endLine = comment.type === "Block"
97 ? comment.loc.end.line - 1
98 : comment.loc.end.line;
99
100 for (let i = comment.loc.start.line; i <= endLine; i++) {
101 lines.add(i);
102 }
103 });
104
105 return lines;
106 }
107
108 //--------------------------------------------------------------------------
109 // Public
110 //--------------------------------------------------------------------------
111
112 return {
113
114 Program: function checkTrailingSpaces(node) {
115
116 /*
117 * Let's hack. Since Espree does not return whitespace nodes,
118 * fetch the source code and do matching via regexps.
119 */
120
121 const re = new RegExp(NONBLANK, "u"),
122 skipMatch = new RegExp(SKIP_BLANK, "u"),
123 lines = sourceCode.lines,
124 linebreaks = sourceCode.getText().match(astUtils.createGlobalLinebreakMatcher()),
125 comments = sourceCode.getAllComments(),
126 commentLineNumbers = getCommentLineNumbers(comments);
127
128 let totalLength = 0,
129 fixRange = [];
130
131 for (let i = 0, ii = lines.length; i < ii; i++) {
132 const lineNumber = i + 1;
133
134 /*
135 * Always add linebreak length to line length to accommodate for line break (\n or \r\n)
136 * Because during the fix time they also reserve one spot in the array.
137 * Usually linebreak length is 2 for \r\n (CRLF) and 1 for \n (LF)
138 */
139 const linebreakLength = linebreaks && linebreaks[i] ? linebreaks[i].length : 1;
140 const lineLength = lines[i].length + linebreakLength;
141
142 const matches = re.exec(lines[i]);
143
144 if (matches) {
145 const location = {
146 start: {
147 line: lineNumber,
148 column: matches.index
149 },
150 end: {
151 line: lineNumber,
152 column: lineLength - linebreakLength
153 }
154 };
155
156 const rangeStart = totalLength + location.start.column;
157 const rangeEnd = totalLength + location.end.column;
158 const containingNode = sourceCode.getNodeByRangeIndex(rangeStart);
159
160 if (containingNode && containingNode.type === "TemplateElement" &&
161 rangeStart > containingNode.parent.range[0] &&
162 rangeEnd < containingNode.parent.range[1]) {
163 totalLength += lineLength;
164 continue;
165 }
166
167 /*
168 * If the line has only whitespace, and skipBlankLines
169 * is true, don't report it
170 */
171 if (skipBlankLines && skipMatch.test(lines[i])) {
172 totalLength += lineLength;
173 continue;
174 }
175
176 fixRange = [rangeStart, rangeEnd];
177
178 if (!ignoreComments || !commentLineNumbers.has(lineNumber)) {
179 report(node, location, fixRange);
180 }
181 }
182
183 totalLength += lineLength;
184 }
185 }
186
187 };
188 }
189 };