Group first arg for inline functions (#947)

* Group first arg for inline functions

* Group first arg. Unless there are comments.

* Group first arg. Allow second arg to be empty object/array.

This implementation has a side effect of disallowing empty objects/arrays in the should group last arg heuristic.
master
Royce Townsend 2017-03-19 02:20:07 +08:00 committed by Christopher Chedeau
parent eeae443b43
commit 9eb389b9f4
8 changed files with 466 additions and 35 deletions

View File

@ -1925,25 +1925,43 @@ function printMethod(path, options, print) {
return concat(parts);
}
function couldGroupArg(arg) {
return ((arg.type === "ObjectExpression" && arg.properties.length > 0) ||
(arg.type === "ArrayExpression" && arg.elements.length > 0) ||
arg.type === "FunctionExpression" ||
(arg.type === "ArrowFunctionExpression" &&
(arg.body.type === "BlockStatement" ||
arg.body.type === "ArrowFunctionExpression" ||
arg.body.type === "ObjectExpression" ||
arg.body.type === "ArrayExpression" ||
arg.body.type === "CallExpression" ||
arg.body.type === "JSXElement")));
}
function shouldGroupLastArg(args) {
const lastArg = util.getLast(args);
const penultimateArg = util.getPenultimate(args);
return (!lastArg.comments || !lastArg.comments.length) &&
(lastArg.type === "ObjectExpression" ||
lastArg.type === "ArrayExpression" ||
lastArg.type === "FunctionExpression" ||
(lastArg.type === "ArrowFunctionExpression" &&
(lastArg.body.type === "BlockStatement" ||
lastArg.body.type === "ArrowFunctionExpression" ||
lastArg.body.type === "ObjectExpression" ||
lastArg.body.type === "ArrayExpression" ||
lastArg.body.type === "CallExpression" ||
lastArg.body.type === "JSXElement"))) &&
couldGroupArg(lastArg) &&
// If the last two arguments are of the same type,
// disable last element expansion.
(!penultimateArg || penultimateArg.type !== lastArg.type);
}
function shouldGroupFirstArg(args) {
if (args.length !== 2) {
return false;
}
const firstArg = args[0];
const secondArg = args[1];
return (!firstArg.comments || !firstArg.comments.length) &&
(firstArg.type === 'FunctionExpression' ||
(firstArg.type === 'ArrowFunctionExpression' &&
firstArg.body.type === 'BlockStatement')) &&
!couldGroupArg(secondArg);
}
function printArgumentsList(path, options, print) {
var printed = path.map(print, "arguments");
@ -1959,20 +1977,31 @@ function printArgumentsList(path, options, print) {
// This is just an optimization; I think we could return the
// conditional group for all function calls, but it's more expensive
// so only do it for specific forms.
if (shouldGroupLastArg(args)) {
const shouldBreak = printed.slice(0, -1).some(willBreak);
const shouldGroupFirst = shouldGroupFirstArg(args);
if (shouldGroupFirst || shouldGroupLastArg(args)) {
const shouldBreak = shouldGroupFirst
? printed.slice(1).some(willBreak)
: printed.slice(0, -1).some(willBreak);
return concat([
printed.some(willBreak) ? breakParent : "",
conditionalGroup(
[
concat(["(", join(concat([", "]), printed), ")"]),
concat([
"(",
join(concat([",", line]), printed.slice(0, -1)),
printed.length > 1 ? ", " : "",
group(util.getLast(printed), { shouldBreak: true }),
")"
]),
shouldGroupFirst
? concat([
"(",
group(printed[0], { shouldBreak: true }),
printed.length > 1 ? ", " : "",
join(concat([",", line]), printed.slice(1)),
")"
])
: concat([
"(",
join(concat([",", line]), printed.slice(0, -1)),
printed.length > 1 ? ", " : "",
group(util.getLast(printed), { shouldBreak: true }),
")"
]),
group(
concat([
"(",
@ -2042,8 +2071,8 @@ function printFunctionParams(path, print, options) {
const canHaveTrailingComma = !(lastParam &&
lastParam.type === "RestElement") && !fun.rest;
// If the parent is a call with the last argument expansion and this is the
// params of the last argument, we dont want the arguments to break and instead
// If the parent is a call with the first/last argument expansion and this is the
// params of the first/last argument, we dont want the arguments to break and instead
// want the whole expression to be on a new line.
//
// Good: Bad:
@ -2055,8 +2084,10 @@ function printFunctionParams(path, print, options) {
const parent = path.getParentNode();
if (
(parent.type === "CallExpression" || parent.type === "NewExpression") &&
util.getLast(parent.arguments) === path.getValue() &&
shouldGroupLastArg(parent.arguments)
((util.getLast(parent.arguments) === path.getValue() &&
shouldGroupLastArg(parent.arguments)) ||
(parent.arguments[0] === path.getValue() &&
shouldGroupFirstArg(parent.arguments)))
) {
return concat(["(", join(", ", printed), ")"]);
}

View File

@ -0,0 +1,200 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`test.js 1`] = `
"setTimeout(function() {
thing();
}, 500);
[\\"a\\",\\"b\\",\\"c\\"].reduce(function(item, thing) {
return thing + \\" \\" + item;
}, \\"letters:\\")
func(() => {
thing();
}, identifier);
func(function() {
thing();
}, this.props.timeout * 1000);
func((that) => {
thing();
}, this.props.getTimeout());
func(() => {
thing();
}, true);
func(() => {
thing();
}, null);
func(() => {
thing();
}, undefined);
func(() => {
thing();
}, /regex.*?/);
compose((a) => {
return a.thing;
}, b => b * b);
somthing.reduce(function(item, thing) {
return thing.blah = item;
}, {})
somthing.reduce(function(item, thing) {
return thing.push(item);
}, [])
reallyLongLongLongLongLongLongLongLongLongLongLongLongLongLongMethod((f, g, h) => {
return f.pop();
}, true);
// Don't do the rest of these
func(function() {
thing();
}, true, false);
func(() => {
thing();
}, {yes: true, cats: 5});
compose((a) => {
return a.thing;
}, b => {
return b + \\"\\";
});
compose((a) => {
return a.thing;
}, b => [1, 2, 3, 4, 5]);
renderThing(a =>
<div>Content. So much to say. Oh my. Are we done yet?</div>
,args);
setTimeout(
// Something
function() {
thing();
},
500
);
setTimeout(/* blip */ function() {
thing();
}, 500);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
setTimeout(function() {
thing();
}, 500);
[\\"a\\", \\"b\\", \\"c\\"].reduce(function(item, thing) {
return thing + \\" \\" + item;
}, \\"letters:\\");
func(() => {
thing();
}, identifier);
func(function() {
thing();
}, this.props.timeout * 1000);
func(that => {
thing();
}, this.props.getTimeout());
func(() => {
thing();
}, true);
func(() => {
thing();
}, null);
func(() => {
thing();
}, undefined);
func(() => {
thing();
}, /regex.*?/);
compose(a => {
return a.thing;
}, b => b * b);
somthing.reduce(function(item, thing) {
return (thing.blah = item);
}, {});
somthing.reduce(function(item, thing) {
return thing.push(item);
}, []);
reallyLongLongLongLongLongLongLongLongLongLongLongLongLongLongMethod(
(f, g, h) => {
return f.pop();
},
true
);
// Don't do the rest of these
func(
function() {
thing();
},
true,
false
);
func(
() => {
thing();
},
{ yes: true, cats: 5 }
);
compose(
a => {
return a.thing;
},
b => {
return b + \\"\\";
}
);
compose(
a => {
return a.thing;
},
b => [1, 2, 3, 4, 5]
);
renderThing(
a => <div>Content. So much to say. Oh my. Are we done yet?</div>,
args
);
setTimeout(
// Something
function() {
thing();
},
500
);
setTimeout(
/* blip */ function() {
thing();
},
500
);
"
`;

View File

@ -0,0 +1 @@
run_spec(__dirname);

View File

@ -0,0 +1,87 @@
setTimeout(function() {
thing();
}, 500);
["a","b","c"].reduce(function(item, thing) {
return thing + " " + item;
}, "letters:")
func(() => {
thing();
}, identifier);
func(function() {
thing();
}, this.props.timeout * 1000);
func((that) => {
thing();
}, this.props.getTimeout());
func(() => {
thing();
}, true);
func(() => {
thing();
}, null);
func(() => {
thing();
}, undefined);
func(() => {
thing();
}, /regex.*?/);
compose((a) => {
return a.thing;
}, b => b * b);
somthing.reduce(function(item, thing) {
return thing.blah = item;
}, {})
somthing.reduce(function(item, thing) {
return thing.push(item);
}, [])
reallyLongLongLongLongLongLongLongLongLongLongLongLongLongLongMethod((f, g, h) => {
return f.pop();
}, true);
// Don't do the rest of these
func(function() {
thing();
}, true, false);
func(() => {
thing();
}, {yes: true, cats: 5});
compose((a) => {
return a.thing;
}, b => {
return b + "";
});
compose((a) => {
return a.thing;
}, b => [1, 2, 3, 4, 5]);
renderThing(a =>
<div>Content. So much to say. Oh my. Are we done yet?</div>
,args);
setTimeout(
// Something
function() {
thing();
},
500
);
setTimeout(/* blip */ function() {
thing();
}, 500);

View File

@ -94,12 +94,9 @@ function reduce_test() {
return previousValue + currentValue + array[index];
});
[0, 1, 2, 3, 4].reduce(
function(previousValue, currentValue, index, array) {
return previousValue + currentValue + array[index];
},
10
);
[0, 1, 2, 3, 4].reduce(function(previousValue, currentValue, index, array) {
return previousValue + currentValue + array[index];
}, 10);
var total = [0, 1, 2, 3].reduce(function(a, b) {
return a + b;

View File

@ -1597,12 +1597,9 @@ exports[`test20.js 1`] = `
[0, 1].reduce((x, y, i) => y);
[\\"a\\", \\"b\\"].reduce(
(regex, representation, index) => {
return regex + (index ? \\"|\\" : \\"\\") + \\"(\\" + representation + \\")\\";
},
\\"\\"
);
[\\"a\\", \\"b\\"].reduce((regex, representation, index) => {
return regex + (index ? \\"|\\" : \\"\\") + \\"(\\" + representation + \\")\\";
}, \\"\\");
[\\"\\"].reduce((acc, str) => acc * str.length);
"

View File

@ -154,11 +154,113 @@ exports[`overflow.js 1`] = `
"SuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperLongCall((err, result) => {
// comment
});
func(one, two, three, four, five, six, seven, eig, is, this, too, long, no, []);
func(one, two, three, four, five, six, seven, eig, is, this, too, long, yes, []);
func(one, two, three, four, five, six, seven, eig, is, this, too, long, yes, [
// Comments
]);
func(five, six, seven, eig, is, this, too, long, yes, [
// Comments
]);
func(one, two, three, four, five, six, seven, eig, is, this, too, long, no, {});
func(one, two, three, four, five, six, seven, eig, is, this, too, long, yes, {});
func(one, two, three, four, five, six, seven, eig, is, this, too, long, yes, {
// Comments
});
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperLongCall(
(err, result) => {
// comment
}
);
func(one, two, three, four, five, six, seven, eig, is, this, too, long, no, []);
func(
one,
two,
three,
four,
five,
six,
seven,
eig,
is,
this,
too,
long,
yes,
[]
);
func(
one,
two,
three,
four,
five,
six,
seven,
eig,
is,
this,
too,
long,
yes,
[
// Comments
]
);
func(
five,
six,
seven,
eig,
is,
this,
too,
long,
yes,
[
// Comments
]
);
func(one, two, three, four, five, six, seven, eig, is, this, too, long, no, {});
func(
one,
two,
three,
four,
five,
six,
seven,
eig,
is,
this,
too,
long,
yes,
{}
);
func(
one,
two,
three,
four,
five,
six,
seven,
eig,
is,
this,
too,
long,
yes,
{
// Comments
}
);
"
`;

View File

@ -1,3 +1,19 @@
SuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperSuperLongCall((err, result) => {
// comment
});
func(one, two, three, four, five, six, seven, eig, is, this, too, long, no, []);
func(one, two, three, four, five, six, seven, eig, is, this, too, long, yes, []);
func(one, two, three, four, five, six, seven, eig, is, this, too, long, yes, [
// Comments
]);
func(five, six, seven, eig, is, this, too, long, yes, [
// Comments
]);
func(one, two, three, four, five, six, seven, eig, is, this, too, long, no, {});
func(one, two, three, four, five, six, seven, eig, is, this, too, long, yes, {});
func(one, two, three, four, five, six, seven, eig, is, this, too, long, yes, {
// Comments
});