From 2fa40bd531ee1a9a7b1e37bfc10fe50acdbd10a4 Mon Sep 17 00:00:00 2001 From: Jordan Kiesel Date: Wed, 12 Feb 2025 00:21:10 -0700 Subject: [PATCH] feat: support experimentalOperatorPosition option --- package.json | 2 +- packages/prettier-plugin-java/.gitignore | 1 + .../src/printers/expressions.ts | 7 +- .../src/printers/printer-utils.ts | 26 +- .../binary_expressions-spec.ts | 12 +- .../{ => operator-position-end}/_input.java | 0 .../{ => operator-position-end}/_output.java | 0 .../operator-position-start/_input.java | 138 ++++++++++ .../operator-position-start/_output.java | 249 ++++++++++++++++++ yarn.lock | 8 +- 10 files changed, 425 insertions(+), 18 deletions(-) rename packages/prettier-plugin-java/test/unit-test/binary_expressions/{ => operator-position-end}/_input.java (100%) rename packages/prettier-plugin-java/test/unit-test/binary_expressions/{ => operator-position-end}/_output.java (100%) create mode 100644 packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_input.java create mode 100644 packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_output.java diff --git a/package.json b/package.json index a33baffaf..c30fdb0f4 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "lerna": "8.1.9", "lint-staged": "15.2.10", "mocha": "10.8.2", - "prettier": "3.4.2", + "prettier": "3.5.0", "sinon": "19.0.2" } } diff --git a/packages/prettier-plugin-java/.gitignore b/packages/prettier-plugin-java/.gitignore index 20b1e5fa8..c4b9f53f7 100644 --- a/packages/prettier-plugin-java/.gitignore +++ b/packages/prettier-plugin-java/.gitignore @@ -1,3 +1,4 @@ +dist test-samples/**/* scripts/single-printer-run samples/**/* diff --git a/packages/prettier-plugin-java/src/printers/expressions.ts b/packages/prettier-plugin-java/src/printers/expressions.ts index 6bec3e23c..a48b1e1ce 100644 --- a/packages/prettier-plugin-java/src/printers/expressions.ts +++ b/packages/prettier-plugin-java/src/printers/expressions.ts @@ -301,7 +301,12 @@ export class ExpressionsPrettierVisitor extends BaseCstPrettierPrinter { ); } - const content = binary(nodes, tokens, true); + const content = binary( + nodes, + tokens, + this.prettierOptions.experimentalOperatorPosition, + true + ); return hasTokens && params?.addParenthesisToWrapStatement ? group( diff --git a/packages/prettier-plugin-java/src/printers/printer-utils.ts b/packages/prettier-plugin-java/src/printers/printer-utils.ts index bf63941dd..9aa6464ab 100644 --- a/packages/prettier-plugin-java/src/printers/printer-utils.ts +++ b/packages/prettier-plugin-java/src/printers/printer-utils.ts @@ -606,7 +606,12 @@ export function putIntoBraces( ); } -export function binary(nodes: Doc[], tokens: IToken[], isRoot = false): Doc { +export function binary( + nodes: Doc[], + tokens: IToken[], + operatorPosition?: "start" | "end", + isRoot = false +): Doc { let levelOperator: string | undefined; let levelPrecedence: number | undefined; let level: Doc[] = []; @@ -619,23 +624,24 @@ export function binary(nodes: Doc[], tokens: IToken[], isRoot = false): Doc { ? nextOperator.length : 1; const operator = concat(tokens.splice(0, tokenLength)); + level.push(nodes.shift()!); if ( levelOperator !== undefined && needsParentheses(levelOperator, nextOperator) ) { - level.push(nodes.shift()!); - level = [ - concat(["(", group(indent(join(line, level))), ") ", operator]) - ]; + level = [concat(["(", group(indent(level)), ")"])]; + } + if (operatorPosition === "start") { + level.push(line, operator, " "); } else { - level.push(join(" ", [nodes.shift()!, operator])); + level.push(" ", operator, line); } levelOperator = nextOperator; levelPrecedence = nextPrecedence; } else if (nextPrecedence < levelPrecedence) { level.push(nodes.shift()!); if (isRoot) { - const content = group(indent(join(line, level))); + const content = group(indent(level)); nodes.unshift( levelOperator !== undefined && needsParentheses(levelOperator, nextOperator) @@ -646,10 +652,10 @@ export function binary(nodes: Doc[], tokens: IToken[], isRoot = false): Doc { levelOperator = undefined; levelPrecedence = undefined; } else { - return group(join(line, level)); + return group(level); } } else { - const content = binary(nodes, tokens); + const content = binary(nodes, tokens, operatorPosition); nodes.unshift( levelOperator !== undefined && needsParentheses(nextOperator, levelOperator) @@ -659,7 +665,7 @@ export function binary(nodes: Doc[], tokens: IToken[], isRoot = false): Doc { } } level.push(nodes.shift()!); - return group(join(line, level)); + return group(level); } export function getOperators(ctx: BinaryExpressionCtx) { diff --git a/packages/prettier-plugin-java/test/unit-test/binary_expressions/binary_expressions-spec.ts b/packages/prettier-plugin-java/test/unit-test/binary_expressions/binary_expressions-spec.ts index 90f36ba3f..003ccd460 100644 --- a/packages/prettier-plugin-java/test/unit-test/binary_expressions/binary_expressions-spec.ts +++ b/packages/prettier-plugin-java/test/unit-test/binary_expressions/binary_expressions-spec.ts @@ -1,9 +1,17 @@ import path from "path"; import url from "url"; -import { testSample } from "../../test-utils.js"; +import { testSample, testSampleWithOptions } from "../../test-utils.js"; const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); describe("prettier-java", () => { - testSample(__dirname); + testSampleWithOptions({ + testFolder: path.resolve(__dirname, "operator-position-start"), + prettierOptions: { experimentalOperatorPosition: "start" } + }); + testSampleWithOptions({ + testFolder: path.resolve(__dirname, "operator-position-end"), + prettierOptions: { experimentalOperatorPosition: "end" } + }); + testSample(path.resolve(__dirname, "operator-position-end")); }); diff --git a/packages/prettier-plugin-java/test/unit-test/binary_expressions/_input.java b/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-end/_input.java similarity index 100% rename from packages/prettier-plugin-java/test/unit-test/binary_expressions/_input.java rename to packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-end/_input.java diff --git a/packages/prettier-plugin-java/test/unit-test/binary_expressions/_output.java b/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-end/_output.java similarity index 100% rename from packages/prettier-plugin-java/test/unit-test/binary_expressions/_output.java rename to packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-end/_output.java diff --git a/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_input.java b/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_input.java new file mode 100644 index 000000000..26d9004b9 --- /dev/null +++ b/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_input.java @@ -0,0 +1,138 @@ +public class BinaryOperations { + + public void binaryOperation() { + int alpha = (left) << right; + boolean beta = (left) < right; + } + + @Annotation("This operation with two very long string should break" + "in a very nice way") + public String binaryOperationThatShouldBreak() { + System.out.println("This operation with two very long string should break" + "in a very nice way"); + return "This operation with two very long string should break" + "in a very nice way"; + } + + @Annotation("This operation should" + "not break") + public String binaryOperationThatShouldNotBreak() { + System.out.println("This operation should" + "not break"); + return "This operation should" + "not break"; + } + + public int ternaryOperationThatShouldBreak() { + int shortInteger = thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne ? thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne : thisIsAShortInteger; + return thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne ? thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne : thisIsAShortInteger; + } + + public int ternaryOperationThatShouldBreak2() { + int shortInteger = thisIsAVeryLongInteger ? thisIsAnotherVeryLongOne : thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne; + return thisIsAVeryLongInteger ? thisIsAnotherVeryLongOne : thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne; + } + + public int ternaryOperationThatShouldNotBreak() { + int a = b ? b : c; + return b ? b : c; + } + + public boolean binaryOperationWithComments() { + boolean a = one || two >> 1 // one + // two + // three + || // five + // four + three; + + boolean b = one || two >> 1 // one + // two + // three + || + three; + + boolean c = one || two >> 1 // one + // two + // three + || three; + + return a || b || c; + } + + public void method() { + new Foo(stuff, thing, "auaaaaaaaaa some very long stuff", "some more").bar(10); + foo(stuff, thing, "some very longuuuuuuuuuuuuuu stuff", "some more").bar(10); + + // Issue 381 + new MethodWrappingFollowingContstructor().aLongEnoughMethodNameToForceThingsToWrap(); + } + + public void binaryExpressionWithCast() { + double availability12 = (double) successfulCount / (successfulCount + failureCount); + availability12 = (double) successfulCount / (successfulCount + failureCount); + } + + void declarationVsAssignment() { + var lineLengthInAssignmentMoreThanPrintWidth = "1234567890" + "1234567890" + "1234567890" + "1234567890" + "1234567890" + "1234567890"; + lineLengthInAssignmentMoreThanPrintWidth = "1234567890" + "1234567890" + "1234567890" + "1234567890" + "1234567890" + "1234567890"; + + aaaaaaaaaa += bbbbbbbbbbb + ccccccccccc + ddddddddddd + eeeeeeeeee + ffffffffff + gggggggggg; + aaaaaaaaaa %= bbbbbbbbbbb + ccccccccccc + ddddddddddd + eeeeeeeeee + ffffffffff + gggggggggg; + aaaaaaaaaa <<= bbbbbbbbbbb + ccccccccccc + ddddddddddd + eeeeeeeeee + ffffffffff + gggggggggg; + aaaaaaaaaa &= bbbbbbbbbbb + ccccccccccc + ddddddddddd + eeeeeeeeee + ffffffffff + gggggggggg; + + var aaaaaaaaaa = bbbbbbbbbb || cccccccccc ? dddddddddd + eeeeeeeeee : ffffffffff + gggggggggg; + aaaaaaaaaa = bbbbbbbbbb || cccccccccc ? dddddddddd + eeeeeeeeee : ffffffffff + gggggggggg; + + var something = MyClass.staticFunction(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd); + something = MyClass.staticFunction(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd); + + var something = MyClass.staticFunction(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd) + 0; + something = MyClass.staticFunction(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd) + 0; + + var something12 = new MyClass(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd); + something12 = new MyClass(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd); + } + + void parentheses() { + var result = a + b >>> 1; + var sizeIndex = index - 1 >>> level & MASK; + var from = offset > left ? 0 : left - offset >> level; + var to = right - offset >> level + 1; + if (rawIndex < 1 << list._level + SHIFT) {} + var res = size < SIZE ? 0 : size - 1 >>> SHIFT << SHIFT; + sign = 1 - 2 * b[3] >> 7; + exponent = b[3] << 1 & 0xff | b[2] >> 7 - 127; + mantissa = b[2] & 0x7f << 16 | b[1] << 8 | b[0]; + + 2 / 3 * 10 / 2 + 2; + 2 * 3 * 10 / 2 + 2; + var rotateX = RANGE / rect.height * refY - RANGE / 2 * getXMultiplication(rect.width); + var rotateY = RANGE / rect.width * refX - RANGE / 2 * getYMultiplication(rect.width); + + a % 10 - 5; + a - 10 % 5; + a * b % 10; + a % b * 10; + a % 10 > 5; + a % 10 == 0; + + 1 << 2 >>> 3 >> 4; + 1 >>> 2 >> 3 << 4; + + 1 << 2 + 3; + 1 >> 2 - 3; + 1 >>> 2 * 3; + 1 / 2 << 3; + 1 + 2 >> 3; + 1 - 2 >>> 3; + + x == y == z; + x != y == z; + x == y != z; + x != y != z; + + 1 & 2 == 3; + + if (aaaaaaaaaa + bbbbbbbbbb == cccccccccc + dddddddddd && eeeeeeeeee + ffffffffff == gggggggggg + hhhhhhhhhh || iiiiiiiiii) {} + + if (a * b + c << d < e == f & g ^ h | i && j || k && l | m ^ n & o != p > q >> r - s / t) {} + + if (aaaaaaaaaa + bbbbbbbbbb == cccccccccc + dddddddddd && eeeeeeeeee + ffffffffff == gggggggggg + hhhhhhhhhh || iiiiiiiiii + jjjjjjjjjj == kkkkkkkkkk + llllllllll && mmmmmmmmmm + nnnnnnnnnn == oooooooooo + pppppppppp || qqqqqqqqqq + rrrrrrrrrr == ssssssssss + tttttttttt && uuuuuuuuuu + vvvvvvvvvv == wwwwwwwwww + xxxxxxxxxxx) {} + } +} diff --git a/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_output.java b/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_output.java new file mode 100644 index 000000000..88b62d5d4 --- /dev/null +++ b/packages/prettier-plugin-java/test/unit-test/binary_expressions/operator-position-start/_output.java @@ -0,0 +1,249 @@ +public class BinaryOperations { + + public void binaryOperation() { + int alpha = (left) << right; + boolean beta = (left) < right; + } + + @Annotation( + "This operation with two very long string should break" + + "in a very nice way" + ) + public String binaryOperationThatShouldBreak() { + System.out.println( + "This operation with two very long string should break" + + "in a very nice way" + ); + return ( + "This operation with two very long string should break" + + "in a very nice way" + ); + } + + @Annotation("This operation should" + "not break") + public String binaryOperationThatShouldNotBreak() { + System.out.println("This operation should" + "not break"); + return "This operation should" + "not break"; + } + + public int ternaryOperationThatShouldBreak() { + int shortInteger = thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne + ? thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne + : thisIsAShortInteger; + return thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne + ? thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne + : thisIsAShortInteger; + } + + public int ternaryOperationThatShouldBreak2() { + int shortInteger = thisIsAVeryLongInteger + ? thisIsAnotherVeryLongOne + : thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne; + return thisIsAVeryLongInteger + ? thisIsAnotherVeryLongOne + : thisIsAnotherVeryLongIntegerThatIsEvenLongerThanFirstOne; + } + + public int ternaryOperationThatShouldNotBreak() { + int a = b ? b : c; + return b ? b : c; + } + + public boolean binaryOperationWithComments() { + boolean a = + one + || two >> 1 + || // one + // two// five + // three + // four + three; + + boolean b = + one + || two >> 1 + || // two // one + // three + three; + + boolean c = + one + || two >> 1 + || // two // one + // three + three; + + return a || b || c; + } + + public void method() { + new Foo(stuff, thing, "auaaaaaaaaa some very long stuff", "some more").bar( + 10 + ); + foo(stuff, thing, "some very longuuuuuuuuuuuuuu stuff", "some more").bar( + 10 + ); + + // Issue 381 + new MethodWrappingFollowingContstructor() + .aLongEnoughMethodNameToForceThingsToWrap(); + } + + public void binaryExpressionWithCast() { + double availability12 = + (double) successfulCount / (successfulCount + failureCount); + availability12 = + (double) successfulCount / (successfulCount + failureCount); + } + + void declarationVsAssignment() { + var lineLengthInAssignmentMoreThanPrintWidth = + "1234567890" + + "1234567890" + + "1234567890" + + "1234567890" + + "1234567890" + + "1234567890"; + lineLengthInAssignmentMoreThanPrintWidth = + "1234567890" + + "1234567890" + + "1234567890" + + "1234567890" + + "1234567890" + + "1234567890"; + + aaaaaaaaaa += + bbbbbbbbbbb + + ccccccccccc + + ddddddddddd + + eeeeeeeeee + + ffffffffff + + gggggggggg; + aaaaaaaaaa %= + bbbbbbbbbbb + + ccccccccccc + + ddddddddddd + + eeeeeeeeee + + ffffffffff + + gggggggggg; + aaaaaaaaaa <<= + bbbbbbbbbbb + + ccccccccccc + + ddddddddddd + + eeeeeeeeee + + ffffffffff + + gggggggggg; + aaaaaaaaaa &= + bbbbbbbbbbb + + ccccccccccc + + ddddddddddd + + eeeeeeeeee + + ffffffffff + + gggggggggg; + + var aaaaaaaaaa = bbbbbbbbbb || cccccccccc + ? dddddddddd + eeeeeeeeee + : ffffffffff + gggggggggg; + aaaaaaaaaa = bbbbbbbbbb || cccccccccc + ? dddddddddd + eeeeeeeeee + : ffffffffff + gggggggggg; + + var something = MyClass.staticFunction( + aaaaaaaaaa, + bbbbbbbbbbb, + ccccccccccc, + ddddddddddd + ); + something = MyClass.staticFunction( + aaaaaaaaaa, + bbbbbbbbbbb, + ccccccccccc, + ddddddddddd + ); + + var something = + MyClass.staticFunction(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd) + + 0; + something = + MyClass.staticFunction(aaaaaaaaaa, bbbbbbbbbbb, ccccccccccc, ddddddddddd) + + 0; + + var something12 = new MyClass( + aaaaaaaaaa, + bbbbbbbbbbb, + ccccccccccc, + ddddddddddd + ); + something12 = new MyClass( + aaaaaaaaaa, + bbbbbbbbbbb, + ccccccccccc, + ddddddddddd + ); + } + + void parentheses() { + var result = (a + b) >>> 1; + var sizeIndex = ((index - 1) >>> level) & MASK; + var from = offset > left ? 0 : (left - offset) >> level; + var to = (right - offset) >> (level + 1); + if (rawIndex < 1 << (list._level + SHIFT)) {} + var res = size < SIZE ? 0 : ((size - 1) >>> SHIFT) << SHIFT; + sign = (1 - 2 * b[3]) >> 7; + exponent = ((b[3] << 1) & 0xff) | (b[2] >> (7 - 127)); + mantissa = (b[2] & (0x7f << 16)) | (b[1] << 8) | b[0]; + + ((2 / 3) * 10) / 2 + 2; + (2 * 3 * 10) / 2 + 2; + var rotateX = + (RANGE / rect.height) * refY + - (RANGE / 2) * getXMultiplication(rect.width); + var rotateY = + (RANGE / rect.width) * refX + - (RANGE / 2) * getYMultiplication(rect.width); + + (a % 10) - 5; + a - (10 % 5); + (a * b) % 10; + (a % b) * 10; + a % 10 > 5; + a % 10 == 0; + + ((1 << 2) >>> 3) >> 4; + ((1 >>> 2) >> 3) << 4; + + 1 << (2 + 3); + 1 >> (2 - 3); + 1 >>> (2 * 3); + (1 / 2) << 3; + (1 + 2) >> 3; + (1 - 2) >>> 3; + + (x == y) == z; + (x != y) == z; + (x == y) != z; + (x != y) != z; + + 1 & (2 == 3); + + if ( + (aaaaaaaaaa + bbbbbbbbbb == cccccccccc + dddddddddd + && eeeeeeeeee + ffffffffff == gggggggggg + hhhhhhhhhh) + || iiiiiiiiii + ) {} + + if ( + (((((a * b + c) << d < e == f) & g) ^ h) | i && j) + || (k && l | (m ^ (n & (o != p > q >> (r - s / t))))) + ) {} + + if ( + (aaaaaaaaaa + bbbbbbbbbb == cccccccccc + dddddddddd + && eeeeeeeeee + ffffffffff == gggggggggg + hhhhhhhhhh) + || (iiiiiiiiii + jjjjjjjjjj == kkkkkkkkkk + llllllllll + && mmmmmmmmmm + nnnnnnnnnn == oooooooooo + pppppppppp) + || (qqqqqqqqqq + rrrrrrrrrr == ssssssssss + tttttttttt + && uuuuuuuuuu + vvvvvvvvvv == wwwwwwwwww + xxxxxxxxxxx) + ) {} + } +} diff --git a/yarn.lock b/yarn.lock index b01d1c78d..3854186b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4270,10 +4270,10 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@3.4.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.2.tgz#a5ce1fb522a588bf2b78ca44c6e6fe5aa5a2b13f" - integrity sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ== +prettier@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.0.tgz#50325a28887c6dfdf2ca3f8eaba02b66a8429ca7" + integrity sha512-quyMrVt6svPS7CjQ9gKb3GLEX/rl3BCL2oa/QkNcXv4YNVBC9olt3s+H7ukto06q7B1Qz46PbrKLO34PR6vXcA== pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0"