Stabilize member chain with empty line (#15522)

Co-authored-by: fisker Cheung <lionkay@gmail.com>
This commit is contained in:
Seiyaandfisker Cheung authored and GitHub committed 2023-12-13 16:12:03 +08:00
1 parent be2fe89b43
commit 224f70fb09
6 files changed
+84 -2

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
#### Fix non-idempotent formatting of method chain with empty line (#15522 by @seiyab)
<!-- prettier-ignore -->
```js
// Input
Foo.a()
.b();
// Prettier stable (first format)
Foo.a()
.b();
// Prettier stable (second format)
Foo.a().b();
// Prettier main
Foo.a()
.b();
```
+15 -2
View File
@@ -34,6 +34,11 @@ import {
printBindExpressionCallee, printBindExpressionCallee,
} from "./misc.js"; } from "./misc.js";
/**
* @typedef {import("../../document/builders.js").Doc} Doc
* @typedef {{ node: any, printed: Doc, needsParens?: boolean, shouldInline?: boolean, hasTrailingEmptyLine?: boolean }} PrintedNode
*/
// We detect calls on member expressions specially to format a // We detect calls on member expressions specially to format a
// common pattern better. The pattern we are looking for is this: // common pattern better. The pattern we are looking for is this:
// //
@@ -57,6 +62,7 @@ function printMemberChain(path, options, print) {
// CallExpression(MemberExpression(CallExpression(Identifier))) // CallExpression(MemberExpression(CallExpression(Identifier)))
// and we transform it into // and we transform it into
// [Identifier, CallExpression, MemberExpression, CallExpression] // [Identifier, CallExpression, MemberExpression, CallExpression]
/** @type {PrintedNode[]}} */
const printedNodes = []; const printedNodes = [];
// Here we try to retain one typed empty line after each call expression or // Here we try to retain one typed empty line after each call expression or
@@ -87,8 +93,10 @@ function printMemberChain(path, options, print) {
isCallExpression(node) && isCallExpression(node) &&
(isMemberish(node.callee) || isCallExpression(node.callee)) (isMemberish(node.callee) || isCallExpression(node.callee))
) { ) {
const hasTrailingEmptyLine = shouldInsertEmptyLineAfter(node);
printedNodes.unshift({ printedNodes.unshift({
node, node,
hasTrailingEmptyLine,
printed: [ printed: [
printComments( printComments(
path, path,
@@ -99,7 +107,7 @@ function printMemberChain(path, options, print) {
], ],
options, options,
), ),
shouldInsertEmptyLineAfter(node) ? hardline : "", hasTrailingEmptyLine ? hardline : "",
], ],
}); });
path.call((callee) => rec(callee), "callee"); path.call((callee) => rec(callee), "callee");
@@ -170,6 +178,7 @@ function printMemberChain(path, options, print) {
// < fn()[0][1][2] >.something() // < fn()[0][1][2] >.something()
// - then, as many MemberExpression as possible but the last one // - then, as many MemberExpression as possible but the last one
// < this.items >.something() // < this.items >.something()
/** @type {PrintedNode[][]} */
const groups = []; const groups = [];
let currentGroup = [printedNodes[0]]; let currentGroup = [printedNodes[0]];
let i = 1; let i = 1;
@@ -331,7 +340,11 @@ function printMemberChain(path, options, print) {
// If we only have a single `.`, we shouldn't do anything fancy and just // If we only have a single `.`, we shouldn't do anything fancy and just
// render everything concatenated together. // render everything concatenated together.
if (groups.length <= cutoff && !nodeHasComment) { if (
groups.length <= cutoff &&
!nodeHasComment &&
!groups.some((g) => g.at(-1).hasTrailingEmptyLine)
) {
if (isLongCurriedCallExpression(path)) { if (isLongCurriedCallExpression(path)) {
return oneLine; return oneLine;
} }
@@ -28,6 +28,26 @@ foo(a--).bar().leet();
================================================================================ ================================================================================
`; `;
exports[`assignment-lhs.js format 1`] = `
====================================options=====================================
parsers: ["babel", "flow", "typescript"]
printWidth: 80
| printWidth
=====================================input======================================
document.querySelector("#output-abcdef").textContent = \`metaKey pressed? \${e.metaKey}\`;
document.querySelector("p").textContent = \`Shader program did not link successfully. Error log: \${linkErrLog}\`;
=====================================output=====================================
document.querySelector("#output-abcdef").textContent =
\`metaKey pressed? \${e.metaKey}\`;
document.querySelector("p").textContent =
\`Shader program did not link successfully. Error log: \${linkErrLog}\`;
================================================================================
`;
exports[`bracket_0.js format 1`] = ` exports[`bracket_0.js format 1`] = `
====================================options===================================== ====================================options=====================================
parsers: ["babel", "flow", "typescript"] parsers: ["babel", "flow", "typescript"]
@@ -0,0 +1,3 @@
document.querySelector("#output-abcdef").textContent = `metaKey pressed? ${e.metaKey}`;
document.querySelector("p").textContent = `Shader program did not link successfully. Error log: ${linkErrLog}`;
@@ -529,6 +529,14 @@ const sel = this.connections
.concat(this.activities.concat(this.operators)) .concat(this.activities.concat(this.operators))
.filter(x => x.selected); .filter(x => x.selected);
Object.entries(obj)
.forEach((e) => console.log(e));
this.fetch("/foo")
.then(response => response.json());
=====================================output===================================== =====================================output=====================================
fooBar fooBar
.doSomething("Hello World") .doSomething("Hello World")
@@ -590,6 +598,14 @@ const sel = this.connections
.concat(this.activities.concat(this.operators)) .concat(this.activities.concat(this.operators))
.filter((x) => x.selected); .filter((x) => x.selected);
Object.entries(obj)
.forEach((e) => console.log(e));
this.fetch("/foo")
.then((response) => response.json());
================================================================================ ================================================================================
`; `;
@@ -57,3 +57,11 @@ const sel = this.connections
.concat(this.activities.concat(this.operators)) .concat(this.activities.concat(this.operators))
.filter(x => x.selected); .filter(x => x.selected);
Object.entries(obj)
.forEach((e) => console.log(e));
this.fetch("/foo")
.then(response => response.json());