eslint 1.4.1 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +1 -1
  2. package/bin/eslint.js +32 -3
  3. package/lib/ast-utils.js +17 -11
  4. package/lib/cli-engine.js +72 -11
  5. package/lib/config.js +54 -55
  6. package/lib/eslint.js +7 -56
  7. package/lib/options.js +7 -0
  8. package/lib/rules/array-bracket-spacing.js +6 -5
  9. package/lib/rules/block-spacing.js +4 -3
  10. package/lib/rules/comma-dangle.js +25 -10
  11. package/lib/rules/comma-spacing.js +4 -29
  12. package/lib/rules/computed-property-spacing.js +5 -4
  13. package/lib/rules/eol-last.js +8 -1
  14. package/lib/rules/func-style.js +29 -9
  15. package/lib/rules/id-length.js +3 -3
  16. package/lib/rules/indent.js +100 -47
  17. package/lib/rules/jsx-quotes.js +1 -1
  18. package/lib/rules/key-spacing.js +7 -1
  19. package/lib/rules/no-dupe-args.js +30 -46
  20. package/lib/rules/no-extra-semi.js +7 -1
  21. package/lib/rules/no-inline-comments.js +3 -1
  22. package/lib/rules/no-spaced-func.js +18 -5
  23. package/lib/rules/no-trailing-spaces.js +34 -6
  24. package/lib/rules/no-unused-vars.js +4 -4
  25. package/lib/rules/no-warning-comments.js +7 -0
  26. package/lib/rules/object-curly-spacing.js +7 -9
  27. package/lib/rules/semi-spacing.js +29 -4
  28. package/lib/rules/space-after-keywords.js +27 -5
  29. package/lib/rules/space-before-blocks.js +64 -7
  30. package/lib/rules/space-before-function-paren.js +19 -13
  31. package/lib/rules/space-before-keywords.js +45 -17
  32. package/lib/rules/space-infix-ops.js +22 -1
  33. package/lib/rules/space-return-throw-case.js +7 -1
  34. package/lib/testers/event-generator-tester.js +63 -0
  35. package/lib/util/comment-event-generator.js +116 -0
  36. package/lib/util/node-event-generator.js +55 -0
  37. package/lib/util/source-code.js +14 -7
  38. package/package.json +2 -2
@@ -13,6 +13,8 @@ var astUtils = require("../ast-utils");
13
13
 
14
14
  module.exports = function(context) {
15
15
 
16
+ var sourceCode = context.getSourceCode();
17
+
16
18
  var options = {
17
19
  before: context.options[0] ? !!context.options[0].before : false,
18
20
  after: context.options[0] ? !!context.options[0].after : true
@@ -29,33 +31,6 @@ module.exports = function(context) {
29
31
  // list of comma tokens to ignore for the check of leading whitespace
30
32
  var commaTokensToIgnore = [];
31
33
 
32
- /**
33
- * Determines the length of comment between 2 tokens
34
- * @param {Object} left - The left token object.
35
- * @param {Object} right - The right token object.
36
- * @returns {number} Length of comment in between tokens
37
- */
38
- function getCommentLengthBetweenTokens(left, right) {
39
- return allComments.reduce(function(val, comment) {
40
- if (left.range[1] <= comment.range[0] && comment.range[1] <= right.range[0]) {
41
- val = val + comment.range[1] - comment.range[0];
42
- }
43
- return val;
44
- }, 0);
45
- }
46
-
47
- /**
48
- * Determines whether two adjacent tokens have whitespace between them.
49
- * @param {Object} left - The left token object.
50
- * @param {Object} right - The right token object.
51
- * @returns {boolean} Whether or not there is space between the tokens.
52
- */
53
- function isSpaced(left, right) {
54
- var punctuationLength = context.getTokensBetween(left, right).length; // the length of any parenthesis
55
- var commentLenth = getCommentLengthBetweenTokens(left, right);
56
- return (left.range[1] + punctuationLength + commentLenth) < right.range[0];
57
- }
58
-
59
34
  /**
60
35
  * Determines if a given token is a comma operator.
61
36
  * @param {ASTNode} token The token to check.
@@ -91,12 +66,12 @@ module.exports = function(context) {
91
66
  */
92
67
  function validateCommaItemSpacing(tokens, reportItem) {
93
68
  if (tokens.left && astUtils.isTokenOnSameLine(tokens.left, tokens.comma) &&
94
- (options.before !== isSpaced(tokens.left, tokens.comma))
69
+ (options.before !== sourceCode.isSpaceBetweenTokens(tokens.left, tokens.comma))
95
70
  ) {
96
71
  report(reportItem, "before");
97
72
  }
98
73
  if (tokens.right && astUtils.isTokenOnSameLine(tokens.comma, tokens.right) &&
99
- (options.after !== isSpaced(tokens.comma, tokens.right))
74
+ (options.after !== sourceCode.isSpaceBetweenTokens(tokens.comma, tokens.right))
100
75
  ) {
101
76
  report(reportItem, "after");
102
77
  }
@@ -12,6 +12,7 @@ var astUtils = require("../ast-utils");
12
12
  //------------------------------------------------------------------------------
13
13
 
14
14
  module.exports = function(context) {
15
+ var sourceCode = context.getSourceCode();
15
16
  var propertyNameMustBeSpaced = context.options[0] === "always"; // default is "never"
16
17
 
17
18
  //--------------------------------------------------------------------------
@@ -83,11 +84,11 @@ module.exports = function(context) {
83
84
 
84
85
  if (astUtils.isTokenOnSameLine(before, first)) {
85
86
  if (propertyNameMustBeSpaced) {
86
- if (!astUtils.isTokenSpaced(before, first) && astUtils.isTokenOnSameLine(before, first)) {
87
+ if (!sourceCode.isSpaceBetweenTokens(before, first) && astUtils.isTokenOnSameLine(before, first)) {
87
88
  reportRequiredBeginningSpace(node, before);
88
89
  }
89
90
  } else {
90
- if (astUtils.isTokenSpaced(before, first)) {
91
+ if (sourceCode.isSpaceBetweenTokens(before, first)) {
91
92
  reportNoBeginningSpace(node, before);
92
93
  }
93
94
  }
@@ -95,11 +96,11 @@ module.exports = function(context) {
95
96
 
96
97
  if (astUtils.isTokenOnSameLine(last, after)) {
97
98
  if (propertyNameMustBeSpaced) {
98
- if (!astUtils.isTokenSpaced(last, after) && astUtils.isTokenOnSameLine(last, after)) {
99
+ if (!sourceCode.isSpaceBetweenTokens(last, after) && astUtils.isTokenOnSameLine(last, after)) {
99
100
  reportRequiredEndingSpace(node, after);
100
101
  }
101
102
  } else {
102
- if (astUtils.isTokenSpaced(last, after)) {
103
+ if (sourceCode.isSpaceBetweenTokens(last, after)) {
103
104
  reportNoEndingSpace(node, after);
104
105
  }
105
106
  }
@@ -27,7 +27,14 @@ module.exports = function(context) {
27
27
  if (src[src.length - 1] !== "\n") {
28
28
  // file is not newline-terminated
29
29
  location.line = src.split(/\n/g).length;
30
- context.report(node, location, "Newline required at end of file but not found.");
30
+ context.report({
31
+ node: node,
32
+ loc: location,
33
+ message: "Newline required at end of file but not found.",
34
+ fix: function(fixer) {
35
+ return fixer.insertTextAfterRange([0, src.length], "\n");
36
+ }
37
+ });
31
38
  }
32
39
  }
33
40
 
@@ -12,32 +12,52 @@
12
12
  module.exports = function(context) {
13
13
 
14
14
  var style = context.options[0],
15
- enforceDeclarations = (style === "declaration");
15
+ enforceDeclarations = (style === "declaration"),
16
+ stack = [];
16
17
 
17
18
  return {
19
+ "Program": function() {
20
+ stack = [];
21
+ },
18
22
 
19
23
  "FunctionDeclaration": function(node) {
24
+ stack.push(false);
25
+
20
26
  if (!enforceDeclarations) {
21
27
  context.report(node, "Expected a function expression.");
22
28
  }
23
29
  },
30
+ "FunctionDeclaration:exit": function() {
31
+ stack.pop();
32
+ },
24
33
 
25
- "FunctionExpression": function() {
26
- var parent = context.getAncestors().pop();
34
+ "FunctionExpression": function(node) {
35
+ stack.push(false);
27
36
 
28
- if (enforceDeclarations && parent.type === "VariableDeclarator") {
29
- context.report(parent, "Expected a function declaration.");
37
+ if (enforceDeclarations && node.parent.type === "VariableDeclarator") {
38
+ context.report(node.parent, "Expected a function declaration.");
30
39
  }
31
40
  },
41
+ "FunctionExpression:exit": function() {
42
+ stack.pop();
43
+ },
32
44
 
33
45
  "ArrowFunctionExpression": function() {
34
- var parent = context.getAncestors().pop();
46
+ stack.push(false);
47
+ },
48
+ "ArrowFunctionExpression:exit": function(node) {
49
+ var hasThisExpr = stack.pop();
35
50
 
36
- if (enforceDeclarations && parent.type === "VariableDeclarator") {
37
- context.report(parent, "Expected a function declaration.");
51
+ if (enforceDeclarations && !hasThisExpr && node.parent.type === "VariableDeclarator") {
52
+ context.report(node.parent, "Expected a function declaration.");
38
53
  }
39
- }
54
+ },
40
55
 
56
+ "ThisExpression": function() {
57
+ if (stack.length > 0) {
58
+ stack[stack.length - 1] = true;
59
+ }
60
+ }
41
61
  };
42
62
 
43
63
  };
@@ -19,10 +19,10 @@ module.exports = function(context) {
19
19
  var properties = options.properties !== "never";
20
20
  var exceptions = (options.exceptions ? options.exceptions : [])
21
21
  .reduce(function(obj, item) {
22
- obj[item] = true;
22
+ obj[item] = true;
23
23
 
24
- return obj;
25
- }, {});
24
+ return obj;
25
+ }, {});
26
26
 
27
27
  var SUPPORTED_EXPRESSIONS = {
28
28
  "MemberExpression": function(parent) {
@@ -83,20 +83,78 @@ module.exports = function(context) {
83
83
  * @param {int} needed Expected indentation character count
84
84
  * @param {int} gotten Indentation character count in the actual node/code
85
85
  * @param {Object=} loc Error line and column location
86
+ * @param {boolean} isLastNodeCheck Is the error for last node check
86
87
  * @returns {void}
87
88
  */
88
- function report(node, needed, gotten, loc) {
89
+ function report(node, needed, gotten, loc, isLastNodeCheck) {
89
90
  var msgContext = {
90
91
  needed: needed,
91
92
  type: indentType,
92
93
  characters: needed === 1 ? "character" : "characters",
93
94
  gotten: gotten
94
95
  };
96
+ var indentChar = indentType === "space" ? " " : "\t";
97
+
98
+ /**
99
+ * Responsible for fixing the indentation issue fix
100
+ * @returns {Function} function to be executed by the fixer
101
+ * @private
102
+ */
103
+ function getFixerFunction() {
104
+ var rangeToFix = [];
105
+
106
+ if (needed > gotten) {
107
+ var spaces = "" + new Array(needed - gotten + 1).join(indentChar); // replace with repeat in future
108
+
109
+ if (isLastNodeCheck === true) {
110
+ rangeToFix = [
111
+ node.range[1] - 1,
112
+ node.range[1] - 1
113
+ ];
114
+ } else {
115
+ rangeToFix = [
116
+ node.range[0],
117
+ node.range[0]
118
+ ];
119
+ }
120
+
121
+ return function(fixer) {
122
+ return fixer.insertTextBeforeRange(rangeToFix, spaces);
123
+ };
124
+ } else {
125
+ if (isLastNodeCheck === true) {
126
+ rangeToFix = [
127
+ node.range[1] - (gotten - needed) - 1,
128
+ node.range[1] - 1
129
+ ];
130
+ } else {
131
+ rangeToFix = [
132
+ node.range[0] - (gotten - needed),
133
+ node.range[0]
134
+ ];
135
+ }
136
+
137
+ return function(fixer) {
138
+ return fixer.removeRange(rangeToFix);
139
+ };
140
+ }
141
+ }
95
142
 
96
143
  if (loc) {
97
- context.report(node, loc, MESSAGE, msgContext);
144
+ context.report({
145
+ node: node,
146
+ loc: loc,
147
+ message: MESSAGE,
148
+ data: msgContext,
149
+ fix: getFixerFunction()
150
+ });
98
151
  } else {
99
- context.report(node, MESSAGE, msgContext);
152
+ context.report({
153
+ node: node,
154
+ message: MESSAGE,
155
+ data: msgContext,
156
+ fix: getFixerFunction()
157
+ });
100
158
  }
101
159
  }
102
160
 
@@ -172,7 +230,8 @@ module.exports = function(context) {
172
230
  node,
173
231
  lastLineIndent,
174
232
  endIndent,
175
- { line: lastToken.loc.start.line, column: lastToken.loc.start.column }
233
+ { line: lastToken.loc.start.line, column: lastToken.loc.start.column },
234
+ true
176
235
  );
177
236
  }
178
237
  }
@@ -195,6 +254,35 @@ module.exports = function(context) {
195
254
  }
196
255
  }
197
256
 
257
+ /**
258
+ * Returns the VariableDeclarator based on the current node
259
+ * if not present then return null
260
+ * @param {ASTNode} node node to examine
261
+ * @returns {ASTNode|void} if found then node otherwise null
262
+ */
263
+ function getVariableDeclaratorNode(node) {
264
+ var parent = node.parent;
265
+
266
+ while (parent.type !== "VariableDeclarator" && parent.type !== "Program") {
267
+ parent = parent.parent;
268
+ }
269
+
270
+ return parent.type === "VariableDeclarator" ? parent : null;
271
+ }
272
+
273
+ /**
274
+ * Check to see if the node is part of the multi-line variable declaration.
275
+ * Also if its on the same line as the varNode
276
+ * @param {ASTNode} node node to check
277
+ * @param {ASTNode} varNode variable declaration node to check against
278
+ * @returns {boolean} True if all the above condition satisfy
279
+ */
280
+ function isNodeInVarOnTop(node, varNode) {
281
+ return varNode &&
282
+ varNode.parent.loc.start.line === node.loc.start.line &&
283
+ varNode.parent.declarations.length > 1;
284
+ }
285
+
198
286
  /**
199
287
  * Check indent for function block content
200
288
  * @param {ASTNode} node node to examine
@@ -214,11 +302,6 @@ module.exports = function(context) {
214
302
  //
215
303
  // Looks for 'Models'
216
304
  var calleeNode = node.parent; // FunctionExpression
217
- while (calleeNode.parent &&
218
- calleeNode.parent.type === "CallExpression") {
219
- calleeNode = calleeNode.parent;
220
- }
221
-
222
305
  var indent;
223
306
 
224
307
  if (calleeNode.parent &&
@@ -244,17 +327,16 @@ module.exports = function(context) {
244
327
  indent += indentSize;
245
328
  }
246
329
 
330
+ // function body indent should be indent + indent size
247
331
  indent += indentSize;
248
- // If function content is not empty
249
- var bodyIndent;
250
- if (node.body.length > 0) {
251
- bodyIndent = getNodeIndent(node.body[0]);
252
- // Calculate left shift position don't require strict indent
253
- // allow function body align to (indentSize * X)
254
- while (bodyIndent > indent) {
255
- indent += indentSize;
256
- }
257
332
 
333
+ // check if the node is inside a variable
334
+ var parentVarNode = getVariableDeclaratorNode(node);
335
+ if (parentVarNode && isNodeInVarOnTop(node, parentVarNode)) {
336
+ indent += indentSize * options.VariableDeclarator[parentVarNode.parent.kind];
337
+ }
338
+
339
+ if (node.body.length > 0) {
258
340
  checkNodesIndent(node.body, indent);
259
341
  }
260
342
 
@@ -275,35 +357,6 @@ module.exports = function(context) {
275
357
  return startLine === endLine;
276
358
  }
277
359
 
278
- /**
279
- * Returns the VariableDeclarator based on the current node
280
- * if not present then return null
281
- * @param {ASTNode} node node to examine
282
- * @returns {ASTNode|void} if found then node otherwise null
283
- */
284
- function getVariableDeclaratorNode(node) {
285
- var parent = node.parent;
286
-
287
- while (parent.type !== "VariableDeclarator" && parent.type !== "Program") {
288
- parent = parent.parent;
289
- }
290
-
291
- return parent.type === "VariableDeclarator" ? parent : null;
292
- }
293
-
294
- /**
295
- * Check to see if the node is part of the multi-line variable declaration.
296
- * Also if its on the same line as the varNode
297
- * @param {ASTNode} node node to check
298
- * @param {ASTNode} varNode variable declaration node to check against
299
- * @returns {boolean} True if all the above condition satisfy
300
- */
301
- function isNodeInVarOnTop(node, varNode) {
302
- return varNode &&
303
- varNode.parent.loc.start.line === node.loc.start.line &&
304
- varNode.parent.declarations.length > 1;
305
- }
306
-
307
360
  /**
308
361
  * Check to see if the first element inside an array is an object and on the same line as the node
309
362
  * If the node is not an array then it will return false.
@@ -49,7 +49,7 @@ module.exports = function(context) {
49
49
  "JSXAttribute": function(node) {
50
50
  var attributeValue = node.value;
51
51
 
52
- if (astUtils.isStringLiteral(attributeValue) && !usesExpectedQuotes(attributeValue)) {
52
+ if (attributeValue && astUtils.isStringLiteral(attributeValue) && !usesExpectedQuotes(attributeValue)) {
53
53
  context.report(attributeValue, "Unexpected usage of {{description}}.", setting);
54
54
  }
55
55
  }
@@ -94,6 +94,7 @@ module.exports = function(context) {
94
94
 
95
95
  var options = context.options[0] || {},
96
96
  align = options.align,
97
+ mode = options.mode || "strict",
97
98
  beforeColon = +!!options.beforeColon, // Defaults to false
98
99
  afterColon = +!(options.afterColon === false); // Defaults to true
99
100
 
@@ -162,7 +163,9 @@ module.exports = function(context) {
162
163
  firstTokenAfterColon = context.getTokenAfter(key, 1),
163
164
  location = side === "key" ? key.loc.start : firstTokenAfterColon.loc.start;
164
165
 
165
- if (diff && !(expected && containsLineTerminator(whitespace))) {
166
+ if ((diff && mode === "strict" || diff < 0 && mode === "minimum") &&
167
+ !(expected && containsLineTerminator(whitespace))
168
+ ) {
166
169
  context.report(property[side], location, messages[side], {
167
170
  error: diff > 0 ? "Extra" : "Missing",
168
171
  computed: property.computed ? "computed " : "",
@@ -333,6 +336,9 @@ module.exports.schema = [
333
336
  "align": {
334
337
  "enum": ["colon", "value"]
335
338
  },
339
+ "mode": {
340
+ "enum": ["strict", "minimum"]
341
+ },
336
342
  "beforeColon": {
337
343
  "type": "boolean"
338
344
  },
@@ -2,6 +2,8 @@
2
2
  * @fileoverview Rule to flag duplicate arguments
3
3
  * @author Jamund Ferguson
4
4
  * @copyright 2015 Jamund Ferguson. All rights reserved.
5
+ * @copyright 2015 Toru Nagashima. All rights reserved.
6
+ * See LICENSE file in root directory for full license.
5
7
  */
6
8
 
7
9
  "use strict";
@@ -16,6 +18,15 @@ module.exports = function(context) {
16
18
  // Helpers
17
19
  //--------------------------------------------------------------------------
18
20
 
21
+ /**
22
+ * Checks whether or not a given definition is a parameter's.
23
+ * @param {escope.DefEntry} def - A definition to check.
24
+ * @returns {boolean} `true` if the definition is a parameter's.
25
+ */
26
+ function isParameter(def) {
27
+ return def.type === "Parameter";
28
+ }
29
+
19
30
  /**
20
31
  * Determines if a given node has duplicate parameters.
21
32
  * @param {ASTNode} node The node to check.
@@ -23,56 +34,29 @@ module.exports = function(context) {
23
34
  * @private
24
35
  */
25
36
  function checkParams(node) {
26
- var params = {},
27
- dups = {};
37
+ var variables = context.getDeclaredVariables(node);
38
+ var keyMap = Object.create(null);
28
39
 
40
+ for (var i = 0; i < variables.length; ++i) {
41
+ var variable = variables[i];
29
42
 
30
- /**
31
- * Marks a given param as either seen or duplicated.
32
- * @param {string} name The name of the param to mark.
33
- * @returns {void}
34
- * @private
35
- */
36
- function markParam(name) {
37
- if (params.hasOwnProperty(name)) {
38
- dups[name] = 1;
39
- } else {
40
- params[name] = 1;
43
+ // TODO(nagashima): Remove this duplication check after https://github.com/estools/escope/pull/79
44
+ var key = "$" + variable.name; // to avoid __proto__.
45
+ if (!isParameter(variable.defs[0]) || keyMap[key]) {
46
+ continue;
41
47
  }
42
- }
43
-
44
- // loop through and find each duplicate param
45
- node.params.forEach(function(param) {
46
-
47
- switch (param.type) {
48
- case "Identifier":
49
- markParam(param.name);
50
- break;
51
-
52
- case "ObjectPattern":
53
- param.properties.forEach(function(property) {
54
- markParam(property.key.name);
55
- });
56
- break;
57
-
58
- case "ArrayPattern":
59
- param.elements.forEach(function(element) {
60
-
61
- // Arrays can be sparse (unwanted arguments)
62
- if (element !== null) {
63
- markParam(element.name);
64
- }
65
- });
66
- break;
67
-
68
- // no default
48
+ keyMap[key] = true;
49
+
50
+ // Checks and reports duplications.
51
+ var defs = variable.defs.filter(isParameter);
52
+ if (defs.length >= 2) {
53
+ context.report({
54
+ node: node,
55
+ message: "Duplicate param '{{name}}'.",
56
+ data: {name: variable.name}
57
+ });
69
58
  }
70
- });
71
-
72
- // log an error for each duplicate (not 2 for each)
73
- Object.keys(dups).forEach(function(currentParam) {
74
- context.report(node, "Duplicate param '{{key}}'.", { key: currentParam });
75
- });
59
+ }
76
60
  }
77
61
 
78
62
  //--------------------------------------------------------------------------
@@ -17,7 +17,13 @@ module.exports = function(context) {
17
17
  * @returns {void}
18
18
  */
19
19
  function report(nodeOrToken) {
20
- context.report(nodeOrToken, "Unnecessary semicolon.");
20
+ context.report({
21
+ node: nodeOrToken,
22
+ message: "Unnecessary semicolon.",
23
+ fix: function(fixer) {
24
+ return fixer.remove(nodeOrToken);
25
+ }
26
+ });
21
27
  }
22
28
 
23
29
  /**
@@ -5,6 +5,8 @@
5
5
  */
6
6
  "use strict";
7
7
 
8
+ var astUtils = require("../ast-utils");
9
+
8
10
  //------------------------------------------------------------------------------
9
11
  // Rule Definition
10
12
  //------------------------------------------------------------------------------
@@ -29,7 +31,7 @@ module.exports = function(context) {
29
31
  var postamble = endLine.slice(node.loc.end.column).trim();
30
32
 
31
33
  // Check that this comment isn't an ESLint directive
32
- var isDirective = node.value.trim().indexOf("eslint-") === 0;
34
+ var isDirective = astUtils.isDirectiveComment(node);
33
35
 
34
36
  // Should be empty if there was only whitespace around the comment
35
37
  if (!isDirective && (preamble || postamble)) {
@@ -11,6 +11,8 @@
11
11
 
12
12
  module.exports = function(context) {
13
13
 
14
+ var sourceCode = context.getSourceCode();
15
+
14
16
  /**
15
17
  * Check if open space is present in a function name
16
18
  * @param {ASTNode} node node to evaluate
@@ -18,18 +20,29 @@ module.exports = function(context) {
18
20
  * @private
19
21
  */
20
22
  function detectOpenSpaces(node) {
21
- var lastCalleeToken = context.getLastToken(node.callee);
22
- var tokens = context.getTokens(node);
23
- var i = tokens.indexOf(lastCalleeToken), l = tokens.length;
23
+ var lastCalleeToken = sourceCode.getLastToken(node.callee),
24
+ tokens = sourceCode.getTokens(node),
25
+ i = tokens.indexOf(lastCalleeToken),
26
+ l = tokens.length;
27
+
24
28
  while (i < l && tokens[i].value !== "(") {
25
29
  ++i;
26
30
  }
31
+
27
32
  if (i >= l) {
28
33
  return;
29
34
  }
35
+
30
36
  // look for a space between the callee and the open paren
31
- if (tokens[i - 1].range[1] !== tokens[i].range[0]) {
32
- context.report(node, lastCalleeToken.loc.start, "Unexpected space between function name and paren.");
37
+ if (sourceCode.isSpaceBetweenTokens(tokens[i - 1], tokens[i])) {
38
+ context.report({
39
+ node: node,
40
+ loc: lastCalleeToken.loc.start,
41
+ message: "Unexpected space between function name and paren.",
42
+ fix: function(fixer) {
43
+ return fixer.removeRange([tokens[i - 1].range[1], tokens[i].range[0]]);
44
+ }
45
+ });
33
46
  }
34
47
  }
35
48
 
@@ -18,6 +18,27 @@ module.exports = function(context) {
18
18
  var options = context.options[0] || {},
19
19
  skipBlankLines = options.skipBlankLines || false;
20
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
+
21
42
 
22
43
  //--------------------------------------------------------------------------
23
44
  // Public
@@ -33,10 +54,12 @@ module.exports = function(context) {
33
54
  var src = context.getSource(),
34
55
  re = new RegExp(NONBLANK),
35
56
  skipMatch = new RegExp(SKIP_BLANK),
36
- matches, lines = src.split(/\r?\n/), location;
57
+ matches, lines = src.split(/\r?\n/),
58
+ location,
59
+ totalLength = 0,
60
+ fixRange = [];
37
61
 
38
62
  for (var i = 0, ii = lines.length; i < ii; i++) {
39
-
40
63
  matches = re.exec(lines[i]);
41
64
  if (matches) {
42
65
 
@@ -50,11 +73,16 @@ module.exports = function(context) {
50
73
  column: matches.index
51
74
  };
52
75
 
53
- // Passing node is a bit dirty, because message data will contain
54
- // big text in `source`. But... who cares :) ?
55
- // One more kludge will not make worse the bloody wizardry of this plugin.
56
- context.report(node, location, "Trailing spaces not allowed.");
76
+ if (i === ii - 1) {
77
+ fixRange = [totalLength + location.column + 1, totalLength + lines[i].length + 1];
78
+ } else {
79
+ fixRange = [totalLength + location.column, totalLength + lines[i].length];
80
+ }
81
+
82
+ report(node, location, fixRange);
57
83
  }
84
+
85
+ totalLength += lines[i].length;
58
86
  }
59
87
  }
60
88