2 * @fileoverview Disallow trailing spaces at the end of lines.
3 * @author Nodeca Team <https://github.com/nodeca>
7 //------------------------------------------------------------------------------
9 //------------------------------------------------------------------------------
11 const astUtils = require("./utils/ast-utils");
13 //------------------------------------------------------------------------------
15 //------------------------------------------------------------------------------
22 description: "disallow trailing whitespace at the end of lines",
23 category: "Stylistic Issues",
25 url: "https://eslint.org/docs/rules/no-trailing-spaces"
28 fixable: "whitespace",
43 additionalProperties: false
48 trailingSpace: "Trailing spaces not allowed."
53 const sourceCode = context.getSourceCode();
55 const BLANK_CLASS = "[ \t\u00a0\u2000-\u200b\u3000]",
56 SKIP_BLANK = `^${BLANK_CLASS}*$`,
57 NONBLANK = `${BLANK_CLASS}+$`;
59 const options = context.options[0] || {},
60 skipBlankLines = options.skipBlankLines || false,
61 ignoreComments = options.ignoreComments || false;
64 * Report the error message
65 * @param {ASTNode} node node to report
66 * @param {int[]} location range information
67 * @param {int[]} fixRange Range based on the whole program
70 function report(node, location, fixRange) {
73 * Passing node is a bit dirty, because message data will contain big
74 * text in `source`. But... who cares :) ?
75 * One more kludge will not make worse the bloody wizardry of this
81 messageId: "trailingSpace",
83 return fixer.removeRange(fixRange);
89 * Given a list of comment nodes, return the line numbers for those comments.
90 * @param {Array} comments An array of comment nodes.
91 * @returns {number[]} An array of line numbers containing comments.
93 function getCommentLineNumbers(comments) {
94 const lines = new Set();
96 comments.forEach(comment => {
97 const endLine = comment.type === "Block"
98 ? comment.loc.end.line - 1
99 : comment.loc.end.line;
101 for (let i = comment.loc.start.line; i <= endLine; i++) {
109 //--------------------------------------------------------------------------
111 //--------------------------------------------------------------------------
115 Program: function checkTrailingSpaces(node) {
118 * Let's hack. Since Espree does not return whitespace nodes,
119 * fetch the source code and do matching via regexps.
122 const re = new RegExp(NONBLANK, "u"),
123 skipMatch = new RegExp(SKIP_BLANK, "u"),
124 lines = sourceCode.lines,
125 linebreaks = sourceCode.getText().match(astUtils.createGlobalLinebreakMatcher()),
126 comments = sourceCode.getAllComments(),
127 commentLineNumbers = getCommentLineNumbers(comments);
132 for (let i = 0, ii = lines.length; i < ii; i++) {
133 const lineNumber = i + 1;
136 * Always add linebreak length to line length to accommodate for line break (\n or \r\n)
137 * Because during the fix time they also reserve one spot in the array.
138 * Usually linebreak length is 2 for \r\n (CRLF) and 1 for \n (LF)
140 const linebreakLength = linebreaks && linebreaks[i] ? linebreaks[i].length : 1;
141 const lineLength = lines[i].length + linebreakLength;
143 const matches = re.exec(lines[i]);
149 column: matches.index
153 column: lineLength - linebreakLength
157 const rangeStart = totalLength + location.start.column;
158 const rangeEnd = totalLength + location.end.column;
159 const containingNode = sourceCode.getNodeByRangeIndex(rangeStart);
161 if (containingNode && containingNode.type === "TemplateElement" &&
162 rangeStart > containingNode.parent.range[0] &&
163 rangeEnd < containingNode.parent.range[1]) {
164 totalLength += lineLength;
169 * If the line has only whitespace, and skipBlankLines
170 * is true, don't report it
172 if (skipBlankLines && skipMatch.test(lines[i])) {
173 totalLength += lineLength;
177 fixRange = [rangeStart, rangeEnd];
179 if (!ignoreComments || !commentLineNumbers.has(lineNumber)) {
180 report(node, location, fixRange);
184 totalLength += lineLength;