Skip to content

Commit

Permalink
New: func-call-spacing rule (fixes #6080) (#6749)
Browse files Browse the repository at this point in the history
  • Loading branch information
btmills authored and ilyavolodin committed Aug 9, 2016
1 parent be68f0b commit f8ab8f1
Show file tree
Hide file tree
Showing 11 changed files with 792 additions and 6 deletions.
1 change: 1 addition & 0 deletions conf/eslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@
"dot-notation": "off",
"eol-last": "off",
"eqeqeq": "off",
"func-call-spacing": "off",
"func-names": "off",
"func-style": "off",
"generator-star-spacing": "off",
Expand Down
107 changes: 107 additions & 0 deletions docs/rules/func-call-spacing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# require or disallow spacing between `function` identifiers and their invocations (func-call-spacing)

(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fix) automatically fixes problems reported by this rule.

When calling a function, developers may insert optional whitespace between the function's name and the parentheses that invoke it. The following pairs of function calls are equivalent:

```js
alert('Hello');
alert ('Hello');

console.log(42);
console.log (42);

new Date();
new Date ();
```

## Rule Details

This rule requires or disallows spaces between the function name and the opening parenthesis that calls it.

## options

This rule has a string option:

- `"never"` (default) disallows space between the function name and the opening parenthesis.
- `"always"` requires space between the function name and the opening parenthesis.

Further, in `"always"` mode, a second object option is available that contains a single boolean `allowNewlines` property.

### never

Examples of **incorrect** code for this rule with the default `"never"` option:

```js
/*eslint func-call-spacing: ["error", "never"]*/

fn ();

fn
();
```

Examples of **correct** code for this rule with the default `"never"` option:

```js
/*eslint func-call-spacing: ["error", "never"]*/

fn();
```

### always

Examples of **incorrect** code for this rule with the `"always"` option:

```js
/*eslint func-call-spacing: ["error", "always"]*/

fn();

fn
();
```

Examples of **correct** code for this rule with the `"always"` option:

```js
/*eslint func-call-spacing: ["error", "always"]*/

fn ();
```

#### allowNewlines

By default, `"always"` does not allow newlines. To permit newlines when in `"always"` mode, set the `allowNewlines` option to `true`. Newlines are never required.

Examples of **incorrect** code for this rule with `allowNewlines` option enabled:

```js
/*eslint func-call-spacing: ["error", "always", { "allowNewlines": true }]*/

fn();
```

Examples of **correct** code for this rule with the `allowNewlines` option enabled:

```js
/*eslint func-call-spacing: ["error", "always", { "allowNewlines": true }]*/

fn (); // Newlines are never required.

fn
();
```

## When Not To Use It

This rule can safely be turned off if your project does not care about enforcing a consistent style for spacing within function calls.

## Related Rules

- [no-spaced-func](no-spaced-func.md) (deprecated)

## Compatibility

- **JSCS**: [disallowSpacesInCallExpression](http://jscs.info/rule/disallowSpacesInCallExpression)
- **JSCS**: [requireSpacesInCallExpression](http://jscs.info/rule/requireSpacesInCallExpression)
2 changes: 1 addition & 1 deletion docs/rules/keyword-spacing.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ let obj = {
foo:function() {}
};

// not conflict with `no-spaced-func`
// not conflict with `func-call-spacing`
class A {
constructor() {
super();
Expand Down
2 changes: 2 additions & 0 deletions docs/rules/no-spaced-func.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# disallow spacing between `function` identifiers and their applications (no-spaced-func)

This rule was **deprecated** in ESLint v3.3.0 and replaced by the [func-call-spacing](func-call-spacing.md) rule.

(fixable) The `--fix` option on the [command line](../user-guide/command-line-interface#fix) automatically fixes problems reported by this rule.

While it's possible to have whitespace between the name of a function and the parentheses that execute it, such patterns tend to look more like errors.
Expand Down
2 changes: 1 addition & 1 deletion docs/rules/no-unexpected-multiline.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,6 @@ Note that the patterns considered problems are **not** flagged by the [semi](sem

## Related Rules

* [func-call-spacing](func-call-spacing.md)
* [semi](semi.md)
* [no-spaced-func](no-spaced-func.md)
* [space-unary-ops](space-unary-ops.md)
153 changes: 153 additions & 0 deletions lib/rules/func-call-spacing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/**
* @fileoverview Rule to control spacing within function calls
* @author Matt DuVall <http://www.mattduvall.com>
*/

"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description: "require or disallow spacing between `function` identifiers and their invocations",
category: "Stylistic Issues",
recommended: false
},

fixable: "whitespace",
schema: {
anyOf: [
{
type: "array",
items: [
{
enum: ["never"]
}
],
minItems: 0,
maxItems: 1
},
{
type: "array",
items: [
{
enum: ["always"]
},
{
type: "object",
properties: {
allowNewlines: {
type: "boolean"
}
},
additionalProperties: false
}
],
minItems: 0,
maxItems: 2
}
]
}
},

create: function(context) {

const never = context.options[0] !== "always";
const allowNewlines = !never && context.options[1] && context.options[1].allowNewlines;
const sourceCode = context.getSourceCode();
const text = sourceCode.getText();

/**
* Check if open space is present in a function name
* @param {ASTNode} node node to evaluate
* @returns {void}
* @private
*/
function checkSpacing(node) {
const lastCalleeToken = sourceCode.getLastToken(node.callee);
let prevToken = lastCalleeToken;
let parenToken = sourceCode.getTokenAfter(lastCalleeToken);

// advances to an open parenthesis.
while (
parenToken &&
parenToken.range[1] < node.range[1] &&
parenToken.value !== "("
) {
prevToken = parenToken;
parenToken = sourceCode.getTokenAfter(parenToken);
}

// Parens in NewExpression are optional
if (!(parenToken && parenToken.range[1] < node.range[1])) {
return;
}

const hasWhitespace = sourceCode.isSpaceBetweenTokens(prevToken, parenToken);
const hasNewline = hasWhitespace &&
/\n/.test(text.slice(prevToken.range[1], parenToken.range[0]).replace(/\/\*.*?\*\//g, ""));

/*
* never allowNewlines hasWhitespace hasNewline message
* F F F F Missing space between function name and paren.
* F F F T (Invalid `!hasWhitespace && hasNewline`)
* F F T T Unexpected newline between function name and paren.
* F F T F (OK)
* F T T F (OK)
* F T T T (OK)
* F T F T (Invalid `!hasWhitespace && hasNewline`)
* F T F F Missing space between function name and paren.
* T T F F (Invalid `never && allowNewlines`)
* T T F T (Invalid `!hasWhitespace && hasNewline`)
* T T T T (Invalid `never && allowNewlines`)
* T T T F (Invalid `never && allowNewlines`)
* T F T F Unexpected space between function name and paren.
* T F T T Unexpected space between function name and paren.
* T F F T (Invalid `!hasWhitespace && hasNewline`)
* T F F F (OK)
*
* T T Unexpected space between function name and paren.
* F F Missing space between function name and paren.
* F F T Unexpected newline between function name and paren.
*/

if (never && hasWhitespace) {
context.report({
node: node,
loc: lastCalleeToken.loc.start,
message: "Unexpected space between function name and paren.",
fix: function(fixer) {
return fixer.removeRange([prevToken.range[1], parenToken.range[0]]);
}
});
} else if (!never && !hasWhitespace) {
context.report({
node: node,
loc: lastCalleeToken.loc.start,
message: "Missing space between function name and paren.",
fix: function(fixer) {
return fixer.insertTextBefore(parenToken, " ");
}
});
} else if (!never && !allowNewlines && hasNewline) {
context.report({
node: node,
loc: lastCalleeToken.loc.start,
message: "Unexpected newline between function name and paren.",
fix: function(fixer) {
return fixer.replaceTextRange([prevToken.range[1], parenToken.range[0]], " ");
}
});
}
}

return {
CallExpression: checkSpacing,
NewExpression: checkSpacing
};

}
};
8 changes: 6 additions & 2 deletions lib/rules/no-spaced-func.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* @fileoverview Rule to check that spaced function application
* @author Matt DuVall <http://www.mattduvall.com>
* @deprecated in ESLint v3.3.0
*/

"use strict";
Expand All @@ -12,11 +13,14 @@
module.exports = {
meta: {
docs: {
description: "disallow spacing between `function` identifiers and their applications",
description: "disallow spacing between `function` identifiers and their applications (deprecated)",
category: "Stylistic Issues",
recommended: false
recommended: false,
replacedBy: ["func-call-spacing"]
},

deprecated: true,

fixable: "whitespace",
schema: []
},
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-config-eslint/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ rules:
dot-notation: ["error", { allowKeywords: true }]
eol-last: "error"
eqeqeq: "error"
func-call-spacing: "error"
func-style: ["error", "declaration"]
generator-star-spacing: "error"
guard-for-in: "error"
Expand Down Expand Up @@ -72,7 +73,6 @@ rules:
no-sequences: "error"
no-shadow: "error"
no-shadow-restricted-names: "error"
no-spaced-func: "error"
no-tabs: "error"
no-trailing-spaces: "error"
no-undef: "error"
Expand Down
Loading

0 comments on commit f8ab8f1

Please sign in to comment.