Break lines before binary operators (#7111)

* Add additional binary expression comment tests

Refs #3806

These cover a few edge cases I encountered while maintaining
`@btmills/prettier` over the last few years. I'm adding them up front to
demonstrate that the `experimentalOperatorPosition` changes in a later
commit have no effect on this formatting when the flag is not enabled.

* Define experimentalOperatorPosition flag

Refs #3806

This merely defines the flag. A future commit will use it in updated
logic.

* Add experimentalOperatorPosition default tests

Right now, this flag isn't implemented, so these tests duplicate the
existing behavior. I'm adding them now so that when I implement the
flag's logic in a future commit, the changed formatting will be obvious
in the diff and easier to review.

* Implement experimentalOperatorPosition flag

Fixes #3806 as requested in https://github.com/prettier/prettier/pull/7111#issuecomment-2252623779

The flag's tests were already added in a previous commit, so the test
changes you see in this commit are specifically due to the new flag's
implementation.

* Document experimentalOperatorPosition flag

* Change operator position option start|end

In response to https://github.com/prettier/prettier/pull/7111#discussion_r1729382119

* Add TS intersection line wrapping tests

This clones TypeScript's type union operator tests in `union-parens.ts`
and swaps the operators (& <-> |) to test the the type interesection
operator. It also runs intersection tests with the new
`experimentalOperatorPosition: "start"` flag. This does _not_ yet update
the implementation to consider the flag; to ease review, I'll make that
change in the next commit to isolate the impact of the change.

* Apply experimentalOperatorPosition: start to type intersections

The type union operator was already formatted at the beginning of lines
by default, so that logic didn't need to be updated.

* Update 7111.md

* Update tests after rebasing

I rebased from 1dfe28f85 to c7066780e to fix test failures related to
https://github.com/prettier/prettier/pull/16601. The rebase applied
cleanly, and this was the only change to tests. Since the PR has
already been approved, I'll keep this as a separate commit rather than
squashing into the prior commits.

---------

Co-authored-by: fisker Cheung <lionkay@gmail.com>
Co-authored-by: SUZUKI Sosuke <aosukeke@gmail.com>
This commit is contained in:
authored and GitHub committed 2025-01-04 01:30:51 +09:00
1 parent d9d0999aac
commit eed53fc29a
17 files changed
+2342 -21

No files matched your search

+16
View File
@@ -36,6 +36,22 @@ const options = {
oppositeDescription:
"Do not print semicolons, except at the beginning of lines which may need them.",
},
experimentalOperatorPosition: {
category: CATEGORY_JAVASCRIPT,
type: "choice",
default: "end",
description: "Where to print operators when binary expressions wrap lines.",
choices: [
{
value: "start",
description: "Print operators at the start of new lines.",
},
{
value: "end",
description: "Print operators at the end of previous lines.",
},
],
},
experimentalTernaries: {
category: CATEGORY_JAVASCRIPT,
type: "boolean",
+46 -13
View File
@@ -7,8 +7,13 @@ import {
line,
softline,
} from "../../document/builders.js";
import { DOC_TYPE_FILL, DOC_TYPE_GROUP } from "../../document/constants.js";
import { cleanDoc } from "../../document/utils.js";
import {
DOC_TYPE_ARRAY,
DOC_TYPE_FILL,
DOC_TYPE_GROUP,
DOC_TYPE_LABEL,
} from "../../document/constants.js";
import { cleanDoc, getDocType } from "../../document/utils.js";
import { printComments } from "../../main/comments/print.js";
import {
CommentCheckFlags,
@@ -23,6 +28,7 @@ import {
isObjectProperty,
shouldFlatten,
} from "../utils/index.js";
import isTypeCastComment from "../utils/is-type-cast-comment.js";
/** @import {Doc} from "../../document/builders.js" */
@@ -231,6 +237,14 @@ function printBinaryishExpressions(
node.type === "NGPipeExpression" ||
isVueFilterSequenceExpression(path, options)) &&
!hasLeadingOwnLineComment(options.originalText, node.right);
const hasTypeCastComment = hasComment(
node.right,
CommentCheckFlags.Leading,
isTypeCastComment,
);
const commentBeforeOperator =
!hasTypeCastComment &&
hasLeadingOwnLineComment(options.originalText, node.right);
const operator = node.type === "NGPipeExpression" ? "|" : node.operator;
const rightSuffix =
@@ -267,13 +281,28 @@ function printBinaryishExpressions(
"right",
)
: print("right");
right = [
lineBeforeOperator ? line : "",
operator,
lineBeforeOperator ? " " : line,
rightContent,
rightSuffix,
];
if (options.experimentalOperatorPosition === "start") {
let comment = "";
if (commentBeforeOperator) {
switch (getDocType(rightContent)) {
case DOC_TYPE_ARRAY:
comment = rightContent.splice(0, 1)[0];
break;
case DOC_TYPE_LABEL:
comment = rightContent.contents.splice(0, 1)[0];
break;
}
}
right = [line, comment, operator, " ", rightContent, rightSuffix];
} else {
right = [
lineBeforeOperator ? line : "",
operator,
lineBeforeOperator ? " " : line,
rightContent,
rightSuffix,
];
}
}
// If there's only a single binary expression, we want to create a group
@@ -289,11 +318,15 @@ function printBinaryishExpressions(
parent.type !== node.type &&
node.left.type !== node.type &&
node.right.type !== node.type);
if (shouldGroup) {
right = group(right, { shouldBreak });
}
parts.push(
lineBeforeOperator ? "" : " ",
shouldGroup ? group(right, { shouldBreak }) : right,
);
if (options.experimentalOperatorPosition === "start") {
parts.push(shouldInline || commentBeforeOperator ? " " : "", right);
} else {
parts.push(lineBeforeOperator ? "" : " ", right);
}
// The root comments are already printed, but we need to manually print
// the other ones since we don't call the normal print on BinaryExpression,
+3
View File
@@ -142,6 +142,9 @@ function printIntersectionType(path, options, print) {
// If no object is involved, go to the next line if it breaks
if (!previousIsObjectType && !currentIsObjectType) {
if (options.experimentalOperatorPosition === "start") {
return indent([line, "& ", doc]);
}
return indent([" &", line, doc]);
}