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
49 const sourceCode = context.getSourceCode();
51 const BLANK_CLASS = "[ \t\u00a0\u2000-\u200b\u3000]",
52 SKIP_BLANK = `^${BLANK_CLASS}*$`,
53 NONBLANK = `${BLANK_CLASS}+$`;
55 const options = context.options[0] || {},
56 skipBlankLines = options.skipBlankLines || false,
57 ignoreComments = options.ignoreComments || false;
60 * Report the error message
61 * @param {ASTNode} node node to report
62 * @param {int[]} location range information
63 * @param {int[]} fixRange Range based on the whole program
66 function report(node, location, fixRange) {
69 * Passing node is a bit dirty, because message data will contain big
70 * text in `source`. But... who cares :) ?
71 * One more kludge will not make worse the bloody wizardry of this
77 message: "Trailing spaces not allowed.",
79 return fixer.removeRange(fixRange);
85 * Given a list of comment nodes, return the line numbers for those comments.
86 * @param {Array} comments An array of comment nodes.
87 * @returns {number[]} An array of line numbers containing comments.
89 function getCommentLineNumbers(comments) {
90 const lines = new Set();
92 comments.forEach(comment => {
93 const endLine = comment.type === "Block"
94 ? comment.loc.end.line - 1
95 : comment.loc.end.line;
97 for (let i = comment.loc.start.line; i <= endLine; i++) {
105 //--------------------------------------------------------------------------
107 //--------------------------------------------------------------------------
111 Program: function checkTrailingSpaces(node) {
114 * Let's hack. Since Espree does not return whitespace nodes,
115 * fetch the source code and do matching via regexps.
118 const re = new RegExp(NONBLANK, "u"),
119 skipMatch = new RegExp(SKIP_BLANK, "u"),
120 lines = sourceCode.lines,
121 linebreaks = sourceCode.getText().match(astUtils.createGlobalLinebreakMatcher()),
122 comments = sourceCode.getAllComments(),
123 commentLineNumbers = getCommentLineNumbers(comments);
128 for (let i = 0, ii = lines.length; i < ii; i++) {
129 const lineNumber = i + 1;
132 * Always add linebreak length to line length to accommodate for line break (\n or \r\n)
133 * Because during the fix time they also reserve one spot in the array.
134 * Usually linebreak length is 2 for \r\n (CRLF) and 1 for \n (LF)
136 const linebreakLength = linebreaks && linebreaks[i] ? linebreaks[i].length : 1;
137 const lineLength = lines[i].length + linebreakLength;
139 const matches = re.exec(lines[i]);
145 column: matches.index
149 column: lineLength - linebreakLength
153 const rangeStart = totalLength + location.start.column;
154 const rangeEnd = totalLength + location.end.column;
155 const containingNode = sourceCode.getNodeByRangeIndex(rangeStart);
157 if (containingNode && containingNode.type === "TemplateElement" &&
158 rangeStart > containingNode.parent.range[0] &&
159 rangeEnd < containingNode.parent.range[1]) {
160 totalLength += lineLength;
165 * If the line has only whitespace, and skipBlankLines
166 * is true, don't report it
168 if (skipBlankLines && skipMatch.test(lines[i])) {
169 totalLength += lineLength;
173 fixRange = [rangeStart, rangeEnd];
175 if (!ignoreComments || !commentLineNumbers.has(lineNumber)) {
176 report(node, location, fixRange);
180 totalLength += lineLength;