1 | /**
|
2 | * @fileoverview Disallow trailing spaces at the end of lines.
|
3 | * @author Nodeca Team <https://github.com/nodeca>
|
4 | * @copyright 2015 Greg Cochard
|
5 | */
|
6 | ;
|
7 |
|
8 | //------------------------------------------------------------------------------
|
9 | // Rule Definition
|
10 | //------------------------------------------------------------------------------
|
11 |
|
12 | module.exports = function(context) {
|
13 |
|
14 | var BLANK_CLASS = "[ \t\u00a0\u2000-\u200b\u2028\u2029\u3000]",
|
15 | SKIP_BLANK = "^" + BLANK_CLASS + "*$",
|
16 | NONBLANK = BLANK_CLASS + "+$";
|
17 |
|
18 | var options = context.options[0] || {},
|
19 | skipBlankLines = options.skipBlankLines || false;
|
20 |
|
21 | /**
|
22 | * Report the error message
|
23 | * @param {ASTNode} node node to report
|
24 | * @param {int[]} location range information
|
25 | * @param {int[]} fixRange Range based on the whole program
|
26 | * @returns {void}
|
27 | */
|
28 | function report(node, location, fixRange) {
|
29 | // Passing node is a bit dirty, because message data will contain
|
30 | // big text in `source`. But... who cares :) ?
|
31 | // One more kludge will not make worse the bloody wizardry of this plugin.
|
32 | context.report({
|
33 | node: node,
|
34 | loc: location,
|
35 | message: "Trailing spaces not allowed.",
|
36 | fix: function(fixer) {
|
37 | return fixer.removeRange(fixRange);
|
38 | }
|
39 | });
|
40 | }
|
41 |
|
42 |
|
43 | //--------------------------------------------------------------------------
|
44 | // Public
|
45 | //--------------------------------------------------------------------------
|
46 |
|
47 | return {
|
48 |
|
49 | "Program": function checkTrailingSpaces(node) {
|
50 |
|
51 | // Let's hack. Since Espree does not return whitespace nodes,
|
52 | // fetch the source code and do matching via regexps.
|
53 |
|
54 | var src = context.getSource(),
|
55 | re = new RegExp(NONBLANK),
|
56 | skipMatch = new RegExp(SKIP_BLANK),
|
57 | matches, lines = src.split(/\r?\n/),
|
58 | linebreaks = context.getSource().match(/\r\n|\r|\n|\u2028|\u2029/g),
|
59 | location,
|
60 | totalLength = 0,
|
61 | fixRange = [];
|
62 |
|
63 | for (var i = 0, ii = lines.length; i < ii; i++) {
|
64 | matches = re.exec(lines[i]);
|
65 |
|
66 | // Always add linebreak length to line length to accommodate for line break (\n or \r\n)
|
67 | // Because during the fix time they also reserve one spot in the array.
|
68 | // Usually linebreak length is 2 for \r\n (CRLF) and 1 for \n (LF)
|
69 | var linebreakLength = linebreaks && linebreaks[i] ? linebreaks[i].length : 1;
|
70 | var lineLength = lines[i].length + linebreakLength;
|
71 |
|
72 | if (matches) {
|
73 |
|
74 | // If the line has only whitespace, and skipBlankLines
|
75 | // is true, don't report it
|
76 | if (skipBlankLines && skipMatch.test(lines[i])) {
|
77 | continue;
|
78 | }
|
79 | location = {
|
80 | line: i + 1,
|
81 | column: matches.index
|
82 | };
|
83 |
|
84 | fixRange = [totalLength + location.column, totalLength + lineLength - linebreakLength];
|
85 |
|
86 | report(node, location, fixRange);
|
87 | }
|
88 |
|
89 | totalLength += lineLength;
|
90 | }
|
91 | }
|
92 |
|
93 | };
|
94 | };
|
95 |
|
96 | module.exports.schema = [
|
97 | {
|
98 | "type": "object",
|
99 | "properties": {
|
100 | "skipBlankLines": {
|
101 | "type": "boolean"
|
102 | }
|
103 | },
|
104 | "additionalProperties": false
|
105 | }
|
106 | ];
|