Skip to content

Commit 3d673f9

Browse files
Fix: lambda formatting (#497)
* feat: improve lambda formatting in method invocations and assignations * feat: improve lambda formatting in constructors and enums * refactor: extract printSingleLambdaInvocation method to regroup common logic
1 parent 53c6d2b commit 3d673f9

File tree

8 files changed

+579
-86
lines changed

8 files changed

+579
-86
lines changed

packages/prettier-plugin-java/src/printers/classes.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ import {
9191
} from "java-parser";
9292
import { Doc } from "prettier";
9393
import { isAnnotationCstNode, isTypeArgumentsCstNode } from "../types/utils";
94+
import { printArgumentListWithBraces } from "../utils";
9495

9596
const { line, softline, hardline } = builders;
9697

@@ -734,16 +735,16 @@ export class ClassesPrettierVisitor extends BaseCstPrettierPrinter {
734735
) {
735736
const typeArguments = this.visit(ctx.typeArguments);
736737
const keyWord = ctx.This ? ctx.This[0] : ctx.Super![0];
737-
const argumentList = this.visit(ctx.argumentList);
738+
const argumentList = printArgumentListWithBraces.call(
739+
this,
740+
ctx.argumentList,
741+
ctx.RBrace![0],
742+
ctx.LBrace[0]
743+
);
738744
return rejectAndConcat([
739745
typeArguments,
740746
keyWord,
741-
group(
742-
rejectAndConcat([
743-
putIntoBraces(argumentList, softline, ctx.LBrace[0], ctx.RBrace[0]),
744-
ctx.Semicolon[0]
745-
])
746-
)
747+
group(rejectAndConcat([argumentList, ctx.Semicolon[0]]))
747748
]);
748749
}
749750

@@ -752,19 +753,19 @@ export class ClassesPrettierVisitor extends BaseCstPrettierPrinter {
752753
) {
753754
const expressionName = this.visit(ctx.expressionName);
754755
const typeArguments = this.visit(ctx.typeArguments);
755-
const argumentList = this.visit(ctx.argumentList);
756+
const argumentList = printArgumentListWithBraces.call(
757+
this,
758+
ctx.argumentList,
759+
ctx.RBrace![0],
760+
ctx.LBrace[0]
761+
);
756762

757763
return rejectAndConcat([
758764
expressionName,
759765
ctx.Dot[0],
760766
typeArguments,
761767
ctx.Super[0],
762-
group(
763-
rejectAndConcat([
764-
putIntoBraces(argumentList, softline, ctx.LBrace[0], ctx.RBrace[0]),
765-
ctx.Semicolon[0]
766-
])
767-
)
768+
group(rejectAndConcat([argumentList, ctx.Semicolon[0]]))
768769
]);
769770
}
770771

@@ -842,18 +843,22 @@ export class ClassesPrettierVisitor extends BaseCstPrettierPrinter {
842843
const otherModifiers = this.mapVisit(modifiers[1]);
843844

844845
const identifier = ctx.Identifier[0];
845-
const argumentList = this.visit(ctx.argumentList);
846846
const classBody = this.visit(ctx.classBody);
847847

848-
const optionnalBracesAndArgumentList = ctx.LBrace
849-
? putIntoBraces(argumentList, softline, ctx.LBrace[0], ctx.RBrace![0])
848+
const optionalBracesAndArgumentList = ctx.LBrace
849+
? printArgumentListWithBraces.call(
850+
this,
851+
ctx.argumentList,
852+
ctx.RBrace![0],
853+
ctx.LBrace[0]
854+
)
850855
: "";
851856

852857
return rejectAndJoin(hardline, [
853858
rejectAndJoin(hardline, firstAnnotations),
854859
rejectAndJoin(" ", [
855860
rejectAndJoin(" ", otherModifiers),
856-
rejectAndConcat([identifier, optionnalBracesAndArgumentList]),
861+
rejectAndConcat([identifier, optionalBracesAndArgumentList]),
857862
classBody
858863
])
859864
]);

packages/prettier-plugin-java/src/printers/expressions.ts

Lines changed: 89 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"use strict";
22

3-
import { BaseCstPrettierPrinter } from "../base-cst-printer";
43
import {
54
ArgumentListCtx,
65
ArrayAccessSuffixCtx,
@@ -50,40 +49,64 @@ import {
5049
} from "java-parser/api";
5150

5251
import forEach from "lodash/forEach";
53-
import { concat, group, indent, dedent } from "./prettier-builder";
52+
import { Doc } from "prettier";
53+
import { builders } from "prettier/doc";
54+
import { BaseCstPrettierPrinter } from "../base-cst-printer";
55+
import { isAnnotationCstNode } from "../types/utils";
56+
import { isArgumentListSingleLambda } from "../utils/expressions-utils";
57+
import {
58+
printSingleLambdaInvocation,
59+
printArgumentListWithBraces
60+
} from "../utils";
5461
import { printTokenWithComments } from "./comments/format-comments";
5562
import { handleCommentsBinaryExpression } from "./comments/handle-comments";
63+
import { concat, dedent, group, indent } from "./prettier-builder";
5664
import {
57-
matchCategory,
58-
rejectAndJoin,
59-
rejectAndConcat,
60-
sortAnnotationIdentifier,
61-
sortNodes,
62-
rejectAndJoinSeps,
6365
findDeepElementInPartsArray,
6466
isExplicitLambdaParameter,
67+
isShiftOperator,
68+
isUniqueMethodInvocation,
69+
matchCategory,
6570
putIntoBraces,
71+
rejectAndConcat,
72+
rejectAndJoin,
73+
rejectAndJoinSeps,
6674
separateTokensIntoGroups,
67-
isShiftOperator,
68-
isUniqueMethodInvocation
75+
sortAnnotationIdentifier,
76+
sortNodes
6977
} from "./printer-utils";
70-
import { builders } from "prettier/doc";
71-
import { Doc } from "prettier";
72-
import { isAnnotationCstNode, isCstNode } from "../types/utils";
73-
const { ifBreak, line, softline } = builders;
78+
79+
const { ifBreak, line, softline, indentIfBreak } = builders;
7480

7581
export class ExpressionsPrettierVisitor extends BaseCstPrettierPrinter {
7682
expression(ctx: ExpressionCtx, params: any) {
7783
return this.visitSingle(ctx, params);
7884
}
7985

80-
lambdaExpression(ctx: LambdaExpressionCtx) {
81-
const lambdaParameters = this.visit(ctx.lambdaParameters);
86+
lambdaExpression(
87+
ctx: LambdaExpressionCtx,
88+
params?: {
89+
lambdaParametersGroupId: symbol;
90+
isInsideMethodInvocationSuffix: boolean;
91+
}
92+
) {
93+
const lambdaParameters = group(
94+
this.visit(ctx.lambdaParameters, params),
95+
params ? { id: params.lambdaParametersGroupId } : undefined
96+
);
8297
const lambdaBody = this.visit(ctx.lambdaBody);
8398

8499
const isLambdaBodyABlock = ctx.lambdaBody[0].children.block !== undefined;
85100
if (isLambdaBodyABlock) {
86-
return rejectAndJoin(" ", [lambdaParameters, ctx.Arrow[0], lambdaBody]);
101+
return rejectAndJoin(" ", [
102+
lambdaParameters,
103+
ctx.Arrow[0],
104+
params?.lambdaParametersGroupId !== undefined
105+
? indentIfBreak(lambdaBody, {
106+
groupId: params.lambdaParametersGroupId
107+
})
108+
: lambdaBody
109+
]);
87110
}
88111

89112
return group(
@@ -96,23 +119,35 @@ export class ExpressionsPrettierVisitor extends BaseCstPrettierPrinter {
96119
);
97120
}
98121

99-
lambdaParameters(ctx: LambdaParametersCtx) {
122+
lambdaParameters(
123+
ctx: LambdaParametersCtx,
124+
params?: { isInsideMethodInvocationSuffix: boolean }
125+
) {
100126
if (ctx.lambdaParametersWithBraces) {
101-
return this.visitSingle(ctx);
127+
return this.visitSingle(ctx, params);
102128
}
103129

104130
return printTokenWithComments(this.getSingle(ctx) as IToken);
105131
}
106132

107-
lambdaParametersWithBraces(ctx: LambdaParametersWithBracesCtx) {
133+
lambdaParametersWithBraces(
134+
ctx: LambdaParametersWithBracesCtx,
135+
params?: { isInsideMethodInvocationSuffix: boolean }
136+
) {
108137
const lambdaParameterList = this.visit(ctx.lambdaParameterList);
109138

110139
if (findDeepElementInPartsArray(lambdaParameterList, ",")) {
111-
return rejectAndConcat([
112-
ctx.LBrace[0],
140+
const content = putIntoBraces(
113141
lambdaParameterList,
142+
softline,
143+
ctx.LBrace[0],
114144
ctx.RBrace[0]
115-
]);
145+
);
146+
if (params?.isInsideMethodInvocationSuffix === true) {
147+
return indent(concat([softline, content]));
148+
}
149+
150+
return content;
116151
}
117152

118153
// removing braces when only no comments attached
@@ -142,7 +177,7 @@ export class ExpressionsPrettierVisitor extends BaseCstPrettierPrinter {
142177
inferredLambdaParameterList(ctx: InferredLambdaParameterListCtx) {
143178
const commas = ctx.Comma
144179
? ctx.Comma.map(elt => {
145-
return concat([elt, " "]);
180+
return concat([elt, line]);
146181
})
147182
: [];
148183

@@ -153,7 +188,7 @@ export class ExpressionsPrettierVisitor extends BaseCstPrettierPrinter {
153188
const lambdaParameter = this.mapVisit(ctx.lambdaParameter);
154189
const commas = ctx.Comma
155190
? ctx.Comma.map(elt => {
156-
return concat([elt, " "]);
191+
return concat([elt, line]);
157192
})
158193
: [];
159194
return rejectAndJoinSeps(commas, lambdaParameter);
@@ -595,15 +630,22 @@ export class ExpressionsPrettierVisitor extends BaseCstPrettierPrinter {
595630
const classOrInterfaceTypeToInstantiate = this.visit(
596631
ctx.classOrInterfaceTypeToInstantiate
597632
);
598-
const argumentList = this.visit(ctx.argumentList);
633+
634+
let content = printArgumentListWithBraces.call(
635+
this,
636+
ctx.argumentList,
637+
ctx.RBrace[0],
638+
ctx.LBrace[0]
639+
);
640+
599641
const classBody = this.visit(ctx.classBody);
600642

601643
return rejectAndJoin(" ", [
602644
ctx.New[0],
603645
rejectAndConcat([
604646
typeArguments,
605647
classOrInterfaceTypeToInstantiate,
606-
putIntoBraces(argumentList, softline, ctx.LBrace[0], ctx.RBrace[0])
648+
content
607649
]),
608650
classBody
609651
]);
@@ -642,17 +684,35 @@ export class ExpressionsPrettierVisitor extends BaseCstPrettierPrinter {
642684
}
643685

644686
methodInvocationSuffix(ctx: MethodInvocationSuffixCtx, params: any) {
687+
const isSingleLambda = isArgumentListSingleLambda(ctx.argumentList);
688+
if (isSingleLambda) {
689+
return printSingleLambdaInvocation.call(
690+
this,
691+
ctx.argumentList,
692+
ctx.RBrace[0],
693+
ctx.LBrace[0]
694+
);
695+
}
696+
645697
const argumentList = this.visit(ctx.argumentList);
698+
646699
if (params && params.shouldDedent) {
647700
return dedent(
648701
putIntoBraces(argumentList, softline, ctx.LBrace[0], ctx.RBrace[0])
649702
);
650703
}
704+
651705
return putIntoBraces(argumentList, softline, ctx.LBrace[0], ctx.RBrace[0]);
652706
}
653707

654-
argumentList(ctx: ArgumentListCtx) {
655-
const expressions = this.mapVisit(ctx.expression);
708+
argumentList(
709+
ctx: ArgumentListCtx,
710+
params?: {
711+
lambdaParametersGroupId: symbol;
712+
isInsideMethodInvocationSuffix: boolean;
713+
}
714+
) {
715+
const expressions = this.mapVisit(ctx.expression, params);
656716
const commas = ctx.Comma ? ctx.Comma.map(elt => concat([elt, line])) : [];
657717
return rejectAndJoinSeps(commas, expressions);
658718
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { ArgumentListCstNode } from "java-parser";
2+
3+
export function isArgumentListSingleLambda(
4+
argumentList: ArgumentListCstNode[] | undefined
5+
) {
6+
if (argumentList === undefined) {
7+
return false;
8+
}
9+
10+
const args = argumentList[0].children.expression;
11+
if (args.length !== 1) {
12+
return false;
13+
}
14+
15+
const argument = args[0];
16+
return argument.children.lambdaExpression !== undefined;
17+
}
18+
19+
export const isSingleArgumentLambdaExpressionWithBlock = (
20+
argumentList: ArgumentListCstNode[] | undefined
21+
) => {
22+
if (argumentList === undefined) {
23+
return false;
24+
}
25+
26+
const args = argumentList[0].children.expression;
27+
if (args.length !== 1) {
28+
return false;
29+
}
30+
31+
const argument = args[0];
32+
return (
33+
argument.children.lambdaExpression !== undefined &&
34+
argument.children.lambdaExpression[0].children.lambdaBody[0].children
35+
.block !== undefined
36+
);
37+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default as printArgumentListWithBraces } from "./printArgumentListWithBraces";
2+
export { default as printSingleLambdaInvocation } from "./printSingleLambdaInvocation";
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { ArgumentListCstNode, IToken } from "java-parser";
2+
import { builders } from "prettier/doc";
3+
import { isArgumentListSingleLambda } from "./expressions-utils";
4+
import { putIntoBraces } from "../printers/printer-utils";
5+
import printSingleLambdaInvocation from "./printSingleLambdaInvocation";
6+
7+
const { softline } = builders;
8+
9+
export default function printArgumentListWithBraces(
10+
argumentListCtx: ArgumentListCstNode[] | undefined,
11+
rBrace: IToken,
12+
lBrace: IToken
13+
) {
14+
const isSingleLambda = isArgumentListSingleLambda(argumentListCtx);
15+
if (isSingleLambda) {
16+
return printSingleLambdaInvocation.call(
17+
this,
18+
argumentListCtx,
19+
rBrace,
20+
lBrace
21+
);
22+
}
23+
24+
const argumentList = this.visit(argumentListCtx, {
25+
isInsideMethodInvocationSuffix: true
26+
});
27+
return putIntoBraces(argumentList, softline, lBrace, rBrace);
28+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { ArgumentListCstNode, IToken } from "java-parser";
2+
import { builders } from "prettier/doc";
3+
import { isSingleArgumentLambdaExpressionWithBlock } from "./expressions-utils";
4+
import { printTokenWithComments } from "../printers/comments/format-comments";
5+
import { concat, dedent, indent } from "../printers/prettier-builder";
6+
import { putIntoBraces } from "../printers/printer-utils";
7+
8+
const { softline, ifBreak } = builders;
9+
10+
export default function printSingleLambdaInvocation(
11+
argumentListCtx: ArgumentListCstNode[] | undefined,
12+
rBrace: IToken,
13+
lBrace: IToken
14+
) {
15+
const lambdaParametersGroupId = Symbol("lambdaParameters");
16+
const argumentList = this.visit(argumentListCtx, {
17+
lambdaParametersGroupId,
18+
isInsideMethodInvocationSuffix: true
19+
});
20+
21+
const formattedRBrace = isSingleArgumentLambdaExpressionWithBlock(
22+
argumentListCtx
23+
)
24+
? ifBreak(
25+
indent(concat([softline, rBrace])),
26+
printTokenWithComments(rBrace),
27+
{ groupId: lambdaParametersGroupId }
28+
)
29+
: indent(concat([softline, rBrace]));
30+
return dedent(putIntoBraces(argumentList, "", lBrace, formattedRBrace));
31+
}

0 commit comments

Comments
 (0)