Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
New: function-call-argument-newline (#12024)
  • Loading branch information
finico authored and platinumazure committed Aug 18, 2019
1 parent 30ebf92 commit 8cd00b3
Show file tree
Hide file tree
Showing 5 changed files with 710 additions and 0 deletions.
202 changes: 202 additions & 0 deletions docs/rules/function-call-argument-newline.md
@@ -0,0 +1,202 @@
# enforce line breaks between arguments of a function call (function-call-argument-newline)

A number of style guides require or disallow line breaks between arguments of a function call.

## Rule Details

This rule enforces line breaks between arguments of a function call.

## Options

This rule has a string option:

* `"always"` (default) requires line breaks between arguments
* `"never"` disallows line breaks between arguments
* `"consistent"` requires consistent usage of line breaks between arguments


### always

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

```js
/*eslint function-call-argument-newline: ["error", "always"]*/

foo("one", "two", "three");

bar("one", "two", {
one: 1,
two: 2
});

baz("one", "two", (x) => {
console.log(x);
});
```

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

```js
/*eslint function-call-argument-newline: ["error", "always"]*/

foo(
"one",
"two",
"three"
);

bar(
"one",
"two",
{ one: 1, two: 2 }
);
// or
bar(
"one",
"two",
{
one: 1,
two: 2
}
);

baz(
"one",
"two",
(x) => {
console.log(x);
}
);
```


### never

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

```js
/*eslint function-call-argument-newline: ["error", "never"]*/

foo(
"one",
"two", "three"
);

bar(
"one",
"two", {
one: 1,
two: 2
}
);

baz(
"one",
"two", (x) => {
console.log(x);
}
);
```

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

```js
/*eslint function-call-argument-newline: ["error", "never"]*/

foo("one", "two", "three");
// or
foo(
"one", "two", "three"
);

bar("one", "two", { one: 1, two: 2 });
// or
bar("one", "two", {
one: 1,
two: 2
});

baz("one", "two", (x) => {
console.log(x);
});
```

### consistent

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

```js
/*eslint function-call-argument-newline: ["error", "consistent"]*/

foo("one", "two",
"three");
//or
foo("one",
"two", "three");

bar("one", "two",
{ one: 1, two: 2}
);

baz("one", "two",
(x) => { console.log(x); }
);
```

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

```js
/*eslint function-call-argument-newline: ["error", "consistent"]*/

foo("one", "two", "three");
// or
foo(
"one",
"two",
"three"
);

bar("one", "two", {
one: 1,
two: 2
});
// or
bar(
"one",
"two",
{ one: 1, two: 2 }
);
// or
bar(
"one",
"two",
{
one: 1,
two: 2
}
);

baz("one", "two", (x) => {
console.log(x);
});
// or
baz(
"one",
"two",
(x) => {
console.log(x);
}
);
```


## When Not To Use It

If you don't want to enforce line breaks between arguments, don't enable this rule.

## Related Rules

* [function-paren-newline](function-paren-newline.md)
* [func-call-spacing](func-call-spacing.md)
* [object-property-newline](object-property-newline.md)
* [array-element-newline](array-element-newline.md)
120 changes: 120 additions & 0 deletions lib/rules/function-call-argument-newline.js
@@ -0,0 +1,120 @@
/**
* @fileoverview Rule to enforce line breaks between arguments of a function call
* @author Alexey Gonchar <https://github.com/finico>
*/

"use strict";

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

module.exports = {
meta: {
type: "layout",

docs: {
description: "enforce line breaks between arguments of a function call",
category: "Stylistic Issues",
recommended: false,
url: "https://eslint.org/docs/rules/function-call-argument-newline"
},

fixable: "whitespace",

schema: [
{
enum: ["always", "never", "consistent"]
}
],

messages: {
unexpectedLineBreak: "There should be no line break here.",
missingLineBreak: "There should be a line break after this argument."
}
},

create(context) {
const sourceCode = context.getSourceCode();

const checkers = {
unexpected: {
messageId: "unexpectedLineBreak",
check: (prevToken, currentToken) => prevToken.loc.start.line !== currentToken.loc.start.line,
createFix: (token, tokenBefore) => fixer =>
fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], " ")
},
missing: {
messageId: "missingLineBreak",
check: (prevToken, currentToken) => prevToken.loc.start.line === currentToken.loc.start.line,
createFix: (token, tokenBefore) => fixer =>
fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], "\n")
}
};

/**
* Check all arguments for line breaks in the CallExpression
* @param {CallExpression} node node to evaluate
* @param {{ messageId: string, check: Function }} checker selected checker
* @returns {void}
* @private
*/
function checkArguments(node, checker) {
for (let i = 1; i < node.arguments.length; i++) {
const prevArgToken = sourceCode.getFirstToken(node.arguments[i - 1]);
const currentArgToken = sourceCode.getFirstToken(node.arguments[i]);

if (checker.check(prevArgToken, currentArgToken)) {
const tokenBefore = sourceCode.getTokenBefore(
currentArgToken,
{ includeComments: true }
);

context.report({
node,
loc: {
start: tokenBefore.loc.end,
end: currentArgToken.loc.start
},
messageId: checker.messageId,
fix: checker.createFix(currentArgToken, tokenBefore)
});
}
}
}

/**
* Check if open space is present in a function name
* @param {CallExpression} node node to evaluate
* @returns {void}
* @private
*/
function check(node) {
if (node.arguments.length < 2) {
return;
}

const option = context.options[0] || "always";

if (option === "never") {
checkArguments(node, checkers.unexpected);
} else if (option === "always") {
checkArguments(node, checkers.missing);
} else if (option === "consistent") {
const firstArgToken = sourceCode.getFirstToken(node.arguments[0]);
const secondArgToken = sourceCode.getFirstToken(node.arguments[1]);

if (firstArgToken.loc.start.line === secondArgToken.loc.start.line) {
checkArguments(node, checkers.unexpected);
} else {
checkArguments(node, checkers.missing);
}
}
}

return {
CallExpression: check,
NewExpression: check
};
}
};
1 change: 1 addition & 0 deletions lib/rules/index.js
Expand Up @@ -46,6 +46,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
"func-name-matching": () => require("./func-name-matching"),
"func-names": () => require("./func-names"),
"func-style": () => require("./func-style"),
"function-call-argument-newline": () => require("./function-call-argument-newline"),
"function-paren-newline": () => require("./function-paren-newline"),
"generator-star-spacing": () => require("./generator-star-spacing"),
"getter-return": () => require("./getter-return"),
Expand Down

0 comments on commit 8cd00b3

Please sign in to comment.