diff --git a/packages/prettier-plugin-java/src/cst-printer.js b/packages/prettier-plugin-java/src/cst-printer.js index 68f93886d..d061622b1 100644 --- a/packages/prettier-plugin-java/src/cst-printer.js +++ b/packages/prettier-plugin-java/src/cst-printer.js @@ -32,13 +32,13 @@ class CstPrettierPrinter extends BaseJavaCstVisitor { // TODO: this methods should be defined on the prototype // defining as instance members **after** the validations to avoid // false positive errors on redundant methods - this.mapVisit = elements => { + this.mapVisit = (elements, params) => { if (elements === undefined) { // TODO: can optimize this by returning an immutable empty array singleton. return []; } - return elements.map(this.visit, this); + return elements.map(element => this.visit(element, params), this); }; this.getSingle = function(ctx) { diff --git a/packages/prettier-plugin-java/src/printers/blocks-and-statements.js b/packages/prettier-plugin-java/src/printers/blocks-and-statements.js index 5480c526f..13d8f0bc5 100644 --- a/packages/prettier-plugin-java/src/printers/blocks-and-statements.js +++ b/packages/prettier-plugin-java/src/printers/blocks-and-statements.js @@ -325,7 +325,10 @@ class BlocksAndStatementPrettierVisitor { returnStatement(ctx) { if (ctx.expression) { - const expression = this.visit(ctx.expression); + const expression = this.visit(ctx.expression, { + addParenthesisToWrapStatement: true, + shouldIndentBinaryOperationInExpression: false + }); return rejectAndConcat([ concat([ctx.Return[0], " "]), diff --git a/packages/prettier-plugin-java/src/printers/classes.js b/packages/prettier-plugin-java/src/printers/classes.js index d5ab13c08..3f20260c2 100644 --- a/packages/prettier-plugin-java/src/printers/classes.js +++ b/packages/prettier-plugin-java/src/printers/classes.js @@ -194,11 +194,78 @@ class ClassesPrettierVisitor { const variableDeclaratorId = this.visit(ctx.variableDeclaratorId); if (ctx.Equals) { const variableInitializer = this.visit(ctx.variableInitializer); - return rejectAndJoin(" ", [ - variableDeclaratorId, - ctx.Equals[0], - variableInitializer - ]); + + if ( + // Array Initialisation + ctx.variableInitializer[0].children.arrayInitializer !== undefined || + // Ternary Expression + (ctx.variableInitializer[0].children.expression[0].children + .ternaryExpression !== undefined && + ctx.variableInitializer[0].children.expression[0].children + .ternaryExpression[0].children.QuestionMark !== undefined) + ) { + return rejectAndJoin(" ", [ + variableDeclaratorId, + ctx.Equals[0], + variableInitializer + ]); + } + + if ( + ctx.variableInitializer[0].children.expression[0].children + .ternaryExpression !== undefined + ) { + const firstPrimary = + ctx.variableInitializer[0].children.expression[0].children + .ternaryExpression[0].children.binaryExpression[0].children + .unaryExpression[0].children.primary[0]; + + // Cast Expression + if ( + firstPrimary.children.primaryPrefix[0].children.castExpression !== + undefined + ) { + return rejectAndJoin(" ", [ + variableDeclaratorId, + ctx.Equals[0], + variableInitializer + ]); + } + + // New Expression + if ( + firstPrimary.children.primaryPrefix[0].children.newExpression !== + undefined + ) { + return rejectAndJoin(" ", [ + variableDeclaratorId, + ctx.Equals[0], + variableInitializer + ]); + } + + // Method Invocation + if ( + firstPrimary.children.primarySuffix !== undefined && + firstPrimary.children.primarySuffix[0].children + .methodInvocationSuffix !== undefined + ) { + return rejectAndJoin(" ", [ + variableDeclaratorId, + ctx.Equals[0], + variableInitializer + ]); + } + } + + return group( + indent( + rejectAndJoin(line, [ + rejectAndJoin(" ", [variableDeclaratorId, ctx.Equals[0]]), + variableInitializer + ]) + ) + ); } return variableDeclaratorId; } diff --git a/packages/prettier-plugin-java/src/printers/expressions.js b/packages/prettier-plugin-java/src/printers/expressions.js index ee7004a8b..5ab1e0b65 100644 --- a/packages/prettier-plugin-java/src/printers/expressions.js +++ b/packages/prettier-plugin-java/src/printers/expressions.js @@ -2,7 +2,7 @@ /* eslint-disable no-unused-vars */ const _ = require("lodash"); -const { line, softline } = require("prettier").doc.builders; +const { ifBreak, line, softline, hardline } = require("prettier").doc.builders; const { concat, group, @@ -19,7 +19,8 @@ const { isExplicitLambdaParameter, putIntoBraces, separateTokensIntoGroups, - isShiftOperator + isShiftOperator, + isUniqueMethodInvocation } = require("./printer-utils"); class ExpressionsPrettierVisitor { @@ -27,8 +28,8 @@ class ExpressionsPrettierVisitor { return this.visitSingle(ctx); } - expression(ctx) { - return this.visitSingle(ctx); + expression(ctx, params) { + return this.visitSingle(ctx, params); } lambdaExpression(ctx) { @@ -128,8 +129,8 @@ class ExpressionsPrettierVisitor { return this.visitSingle(ctx); } - ternaryExpression(ctx) { - const binaryExpression = this.visit(ctx.binaryExpression); + ternaryExpression(ctx, params) { + const binaryExpression = this.visit(ctx.binaryExpression, params); if (ctx.QuestionMark) { const expression1 = this.visit(ctx.expression[0]); const expression2 = this.visit(ctx.expression[1]); @@ -153,7 +154,7 @@ class ExpressionsPrettierVisitor { return binaryExpression; } - binaryExpression(ctx) { + binaryExpression(ctx, params) { const referenceType = this.mapVisit(ctx.referenceType); const expression = this.mapVisit(ctx.expression); const unaryExpression = this.mapVisit(ctx.unaryExpression); @@ -165,6 +166,10 @@ class ExpressionsPrettierVisitor { const segmentsSplittedByBinaryOperator = []; let currentSegment = []; + if (groupsOfOperator.length === 1 && groupsOfOperator[0].length === 0) { + return unaryExpression.shift(); + } + groupsOfOperator.forEach(subgroup => { currentSegment = [unaryExpression.shift()]; for (let i = 0; i < subgroup.length; i++) { @@ -198,18 +203,44 @@ class ExpressionsPrettierVisitor { ); i += 2; } else if (matchCategory(token, "'BinaryOperator'")) { - currentSegment.push( - indent(rejectAndJoin(line, [token, unaryExpression.shift()])) - ); + const binaryOperation = rejectAndJoin(line, [ + token, + unaryExpression.shift() + ]); + if ( + params !== undefined && + !params.shouldIndentBinaryOperationInExpression + ) { + currentSegment.push(binaryOperation); + } else { + currentSegment.push(indent(binaryOperation)); + } } } segmentsSplittedByBinaryOperator.push( group(rejectAndJoin(" ", currentSegment)) ); }); - if (groupsOfOperator.length === 0) { - return unaryExpression.shift(); + + if (params !== undefined && params.addParenthesisToWrapStatement) { + return group( + concat([ + ifBreak("(", ""), + indent( + concat([ + softline, + rejectAndJoinSeps( + sortedBinaryOperators.map(elt => concat([" ", elt, line])), + segmentsSplittedByBinaryOperator + ) + ]) + ), + softline, + ifBreak(")") + ]) + ); } + return group( rejectAndJoinSeps( sortedBinaryOperators.map(elt => concat([" ", elt, line])), @@ -251,52 +282,56 @@ class ExpressionsPrettierVisitor { } primary(ctx) { - const primaryPrefix = this.visit(ctx.primaryPrefix); + const countMethodInvocation = isUniqueMethodInvocation(ctx.primarySuffix); + + const primaryPrefix = this.visit(ctx.primaryPrefix, { + shouldBreakBeforeFirstMethodInvocation: countMethodInvocation > 1 + }); const primarySuffixes = this.mapVisit(ctx.primarySuffix); const suffixes = []; - let addIndent = false; - for (let i = 0; i < primarySuffixes.length; i++) { - if (ctx.primarySuffix[i].children.Dot !== undefined) { - suffixes.push(indent(softline), primarySuffixes[i]); - addIndent = true; - } else if ( - ctx.primarySuffix[i].children.methodInvocationSuffix === undefined + + if (ctx.primarySuffix !== undefined) { + if ( + ctx.primarySuffix[0].children.Dot !== undefined && + ctx.primaryPrefix[0].children.newExpression !== undefined ) { - suffixes.push(softline, primarySuffixes[i]); - } else { - if (addIndent) { - suffixes.push(indent(primarySuffixes[i])); - addIndent = false; - } else { - suffixes.push(primarySuffixes[i]); + suffixes.push(softline); + } + suffixes.push(primarySuffixes[0]); + + for (let i = 1; i < primarySuffixes.length; i++) { + if ( + ctx.primarySuffix[i].children.Dot !== undefined && + ctx.primarySuffix[i - 1].children.methodInvocationSuffix !== undefined + ) { + suffixes.push(softline); } + suffixes.push(primarySuffixes[i]); } - } - let firstSeparator = suffixes.shift(); - if ( - ctx.primaryPrefix[0].children.This !== undefined || - firstSeparator === undefined - ) { - firstSeparator = ""; + if (countMethodInvocation === 1) { + return group( + rejectAndConcat([ + primaryPrefix, + suffixes[0], + indent(rejectAndConcat(suffixes.slice(1))) + ]) + ); + } } return group( - rejectAndConcat([ - primaryPrefix, - firstSeparator, - rejectAndConcat(suffixes) - ]) + rejectAndConcat([primaryPrefix, indent(rejectAndConcat(suffixes))]) ); } - primaryPrefix(ctx) { + primaryPrefix(ctx, params) { if (ctx.This || ctx.Void || ctx.Boolean) { return getImageWithComments(this.getSingle(ctx)); } - return this.visitSingle(ctx); + return this.visitSingle(ctx, params); } primarySuffix(ctx) { @@ -319,10 +354,25 @@ class ExpressionsPrettierVisitor { return this.visitSingle(ctx); } - fqnOrRefType(ctx) { + fqnOrRefType(ctx, params) { const fqnOrRefTypePart = this.mapVisit(ctx.fqnOrRefTypePart); const dims = this.visit(ctx.dims); const dots = ctx.Dot ? ctx.Dot : []; + + if ( + params !== undefined && + params.shouldBreakBeforeFirstMethodInvocation === true + ) { + return rejectAndConcat([ + indent( + rejectAndJoin(concat([softline, dots[0]]), [ + fqnOrRefTypePart[0], + rejectAndJoinSeps(dots.slice(1), fqnOrRefTypePart.slice(1)), + dims + ]) + ) + ]); + } return rejectAndConcat([rejectAndJoinSeps(dots, fqnOrRefTypePart), dims]); } @@ -375,8 +425,10 @@ class ExpressionsPrettierVisitor { } parenthesisExpression(ctx) { - const expression = this.visit(ctx.expression); - return rejectAndConcat([ctx.LBrace[0], expression, ctx.RBrace[0]]); + const expression = this.visit(ctx.expression, { + shouldIndentBinaryOperationInExpression: false + }); + return putIntoBraces(expression, softline, ctx.LBrace[0], ctx.RBrace[0]); } castExpression(ctx) { diff --git a/packages/prettier-plugin-java/src/printers/printer-utils.js b/packages/prettier-plugin-java/src/printers/printer-utils.js index 5840da09d..79b5265ca 100644 --- a/packages/prettier-plugin-java/src/printers/printer-utils.js +++ b/packages/prettier-plugin-java/src/printers/printer-utils.js @@ -583,6 +583,25 @@ function compareFqn(packageOrTypeNameFirst, packageOrTypeNameSecond) { return 0; } +function isUniqueMethodInvocation(primarySuffixes) { + if (primarySuffixes === undefined) { + return 0; + } + + let count = 0; + primarySuffixes.forEach(primarySuffix => { + if (primarySuffix.children.methodInvocationSuffix !== undefined) { + count++; + + if (count > 1) { + return 2; + } + } + }); + + return count; +} + module.exports = { buildFqn, reject, @@ -609,5 +628,6 @@ module.exports = { buildOriginalText, getCSTNodeStartEndToken, isStatementEmptyStatement, - sortImports + sortImports, + isUniqueMethodInvocation }; diff --git a/packages/prettier-plugin-java/test/unit-test/member_chain/_output.java b/packages/prettier-plugin-java/test/unit-test/member_chain/_output.java index 473310853..7d5c7a788 100644 --- a/packages/prettier-plugin-java/test/unit-test/member_chain/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/member_chain/_output.java @@ -43,7 +43,8 @@ public void doSomethingLongNew2() { } public void doSomethingLongStatic() { - return Object.something() + return Object + .something() .more() .and() .that() diff --git a/packages/prettier-plugin-java/test/unit-test/prettier-ignore/multiple-ignore/_output.java b/packages/prettier-plugin-java/test/unit-test/prettier-ignore/multiple-ignore/_output.java index c175b2818..b99e1abb8 100644 --- a/packages/prettier-plugin-java/test/unit-test/prettier-ignore/multiple-ignore/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/prettier-ignore/multiple-ignore/_output.java @@ -14,7 +14,8 @@ public void myPrettierIgnoreMethod( int param10 ) { for (int i = 0; i < param1; i++) { - param2.methodcall() + param2 + .methodcall() .methodcall() .methodcall() .methodcall() diff --git a/packages/prettier-plugin-java/test/unit-test/return/_input.java b/packages/prettier-plugin-java/test/unit-test/return/_input.java index 463ce96b0..15e5d02c4 100644 --- a/packages/prettier-plugin-java/test/unit-test/return/_input.java +++ b/packages/prettier-plugin-java/test/unit-test/return/_input.java @@ -16,4 +16,24 @@ Object returnCast() { return (BeanItemContainer) super.getContainerDataSource(); } + Object returnSomethingWhichDoNotBreak() { + return oneVariable + secondVariable; + } + + Object returnSomethingWhichBreak() { + return oneVariable + secondVariable + thirdVariable + fourthVariable + fifthVariable + sixthVariable + seventhVariable; + } + + Object returnSomethingWhichBreakAndAlreadyInParenthesis() { + return ( + oneVariable + + secondVariable + + thirdVariable + + fourthVariable + + fifthVariable + + sixthVariable + + seventhVariable + ); + } + } diff --git a/packages/prettier-plugin-java/test/unit-test/return/_output.java b/packages/prettier-plugin-java/test/unit-test/return/_output.java index f30b31fea..436b18c3a 100644 --- a/packages/prettier-plugin-java/test/unit-test/return/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/return/_output.java @@ -15,4 +15,32 @@ void exit() { Object returnCast() { return (BeanItemContainer) super.getContainerDataSource(); } + + Object returnSomethingWhichDoNotBreak() { + return oneVariable + secondVariable; + } + + Object returnSomethingWhichBreak() { + return ( + oneVariable + + secondVariable + + thirdVariable + + fourthVariable + + fifthVariable + + sixthVariable + + seventhVariable + ); + } + + Object returnSomethingWhichBreakAndAlreadyInParenthesis() { + return ( + oneVariable + + secondVariable + + thirdVariable + + fourthVariable + + fifthVariable + + sixthVariable + + seventhVariable + ); + } } diff --git a/packages/prettier-plugin-java/test/unit-test/variables/_input.java b/packages/prettier-plugin-java/test/unit-test/variables/_input.java index d6297e174..86e2788e5 100644 --- a/packages/prettier-plugin-java/test/unit-test/variables/_input.java +++ b/packages/prettier-plugin-java/test/unit-test/variables/_input.java @@ -73,4 +73,23 @@ public boolean localVariableDeclarationWhichDoNotBreak() { BackupStatus lastStatus = value; } + public void breakVariables() { + Obj newObject = new Object().something().more().and().that().as().well().but().not().something(); + + Object.test.creation thisObject = classWithName.invocationOne().invocationTwo(); + + Object.test.creation thisObject1 = classWithName.invocationOne(argument1, argument2, argument3); + + Object.test.creation thisObject2 = classWithName.invocationOne(argument1, argument2, argument3).invocationTwo(); + + Object.test.creation thisObject3 = classWithName.invocationOne().invocationTwo(argument1, argument2, argument3); + + Object.test.creation thisObject4 = classWithName.invocationOne(argument1, argument2, argument3).invocationTwo(argument1, argument2); + + Object.test.creation thisObject5 = classWithName.invocationOne(argument1WithAVeryVeryVeryVeryLongName, argument2, argument3).attributeOne.attributeTwo + .invocationTwo(argument1, argument2).attributeThree.invocationThree(); + + Object.test.creation thisObject6 = classWithName.invocationOne(argument1, argument2, + argument3).attributeOne.attributeTwo.invocationTwo(argument1, argument2).attributeThree.invocationThree(); + } } diff --git a/packages/prettier-plugin-java/test/unit-test/variables/_output.java b/packages/prettier-plugin-java/test/unit-test/variables/_output.java index bf29079ee..457a26db5 100644 --- a/packages/prettier-plugin-java/test/unit-test/variables/_output.java +++ b/packages/prettier-plugin-java/test/unit-test/variables/_output.java @@ -37,8 +37,8 @@ public class Variables { private Range creator1 = this.dateRangeField.new Range(from, to); private Range creator2 = this.dateRangeField.new Range(from, to); - private Range creator3 = this.dateRangeField - .new Range<>(from, to); + private Range creator3 = + this.dateRangeField.new Range<>(from, to); private Range creator3 = new Range<>(from, to); private int hexLiteral = 0x0000; @@ -97,4 +97,53 @@ public boolean localVariableDeclarationWhichDoNotBreak() { BackupStatus lastStatus = value; } + + public void breakVariables() { + Obj newObject = new Object() + .something() + .more() + .and() + .that() + .as() + .well() + .but() + .not() + .something(); + + Object.test.creation thisObject = classWithName + .invocationOne() + .invocationTwo(); + + Object.test.creation thisObject1 = classWithName.invocationOne( + argument1, + argument2, + argument3 + ); + + Object.test.creation thisObject2 = classWithName + .invocationOne(argument1, argument2, argument3) + .invocationTwo(); + + Object.test.creation thisObject3 = classWithName + .invocationOne() + .invocationTwo(argument1, argument2, argument3); + + Object.test.creation thisObject4 = classWithName + .invocationOne(argument1, argument2, argument3) + .invocationTwo(argument1, argument2); + + Object.test.creation thisObject5 = classWithName + .invocationOne( + argument1WithAVeryVeryVeryVeryLongName, + argument2, + argument3 + ) + .attributeOne.attributeTwo.invocationTwo(argument1, argument2) + .attributeThree.invocationThree(); + + Object.test.creation thisObject6 = classWithName + .invocationOne(argument1, argument2, argument3) + .attributeOne.attributeTwo.invocationTwo(argument1, argument2) + .attributeThree.invocationThree(); + } }