Skip to content

Commit

Permalink
fix: hug lambdas without blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
jtkiesel committed Mar 5, 2024
1 parent 74609d2 commit c6f18b9
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 69 deletions.
93 changes: 41 additions & 52 deletions packages/prettier-plugin-java/src/printers/expressions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ import { isAnnotationCstNode } from "../types/utils.js";
import { printArgumentListWithBraces } from "../utils/index.js";
import { printTokenWithComments } from "./comments/format-comments.js";
import { handleCommentsBinaryExpression } from "./comments/handle-comments.js";
import { concat, dedent, fill, group, indent } from "./prettier-builder.js";
import { concat, dedent, group, indent, join } from "./prettier-builder.js";
import {
binary,
findDeepElementInPartsArray,
Expand All @@ -77,39 +77,31 @@ import {
sortTokens
} from "./printer-utils.js";

const { hardline, ifBreak, line, lineSuffixBoundary, softline } = builders;
const { ifBreak, line, lineSuffixBoundary, softline } = builders;

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

lambdaExpression(
ctx: LambdaExpressionCtx,
params?: { shouldBreak?: boolean }
) {
lambdaExpression(ctx: LambdaExpressionCtx, params?: { flat?: boolean }) {
const lambdaParameters = group(this.visit(ctx.lambdaParameters, params));
const lambdaBody = this.visit(ctx.lambdaBody);

const isLambdaBodyABlock = ctx.lambdaBody[0].children.block !== undefined;
if (isLambdaBodyABlock) {
return rejectAndJoin(" ", [lambdaParameters, ctx.Arrow[0], lambdaBody]);
return join(" ", [lambdaParameters, ctx.Arrow[0], lambdaBody]);
}

return group(
indent(
rejectAndJoin(line, [
rejectAndJoin(" ", [lambdaParameters, ctx.Arrow[0]]),
lambdaBody
])
)
);
return group([
lambdaParameters,
" ",
ctx.Arrow[0],
indent([line, lambdaBody])
]);
}

lambdaParameters(
ctx: LambdaParametersCtx,
params?: { shouldBreak?: boolean }
) {
lambdaParameters(ctx: LambdaParametersCtx, params?: { flat?: boolean }) {
if (ctx.lambdaParametersWithBraces) {
return this.visitSingle(ctx, params);
}
Expand All @@ -119,19 +111,18 @@ export class ExpressionsPrettierVisitor extends BaseCstPrettierPrinter {

lambdaParametersWithBraces(
ctx: LambdaParametersWithBracesCtx,
params?: { shouldBreak?: boolean }
params?: { flat?: boolean }
) {
const lambdaParameterList = this.visit(ctx.lambdaParameterList, params);

if (findDeepElementInPartsArray(lambdaParameterList, ",")) {
const separator = params?.shouldBreak === false ? "" : softline;
const content = putIntoBraces(
lambdaParameterList,
separator,
return [
ctx.LBrace[0],
...(params?.flat
? [lambdaParameterList]
: [indent([softline, lambdaParameterList]), softline]),
ctx.RBrace[0]
);
return content;
];
}

// removing braces when only no comments attached
Expand All @@ -156,26 +147,26 @@ export class ExpressionsPrettierVisitor extends BaseCstPrettierPrinter {

lambdaParameterList(
ctx: LambdaParameterListCtx,
params?: { shouldBreak?: boolean }
params?: { flat?: boolean }
) {
return this.visitSingle(ctx, params);
}

inferredLambdaParameterList(
ctx: InferredLambdaParameterListCtx,
params?: { shouldBreak?: boolean }
params?: { flat?: boolean }
) {
const commaSuffix = params?.shouldBreak === false ? " " : line;
const commaSuffix = params?.flat ? " " : line;
const commas = ctx.Comma?.map(comma => concat([comma, commaSuffix]));
return rejectAndJoinSeps(commas, ctx.Identifier);
}

explicitLambdaParameterList(
ctx: ExplicitLambdaParameterListCtx,
params?: { shouldBreak?: boolean }
params?: { flat?: boolean }
) {
const lambdaParameter = this.mapVisit(ctx.lambdaParameter);
const commaSuffix = params?.shouldBreak === false ? " " : line;
const commaSuffix = params?.flat ? " " : line;
const commas = ctx.Comma?.map(comma => concat([comma, commaSuffix]));
return rejectAndJoinSeps(commas, lambdaParameter);
}
Expand Down Expand Up @@ -628,29 +619,27 @@ export class ExpressionsPrettierVisitor extends BaseCstPrettierPrinter {

argumentList(
ctx: ArgumentListCtx,
params?: { isHuggable?: boolean; shouldBreak?: boolean }
params: { flat?: boolean; hugged?: boolean } = {}
) {
const shouldBreak = params?.shouldBreak;
const expressions = this.mapVisit(ctx.expression, params);
const { flat, hugged } = params;
const expressions = this.mapVisit(ctx.expression, {
flat: flat || hugged
});

const lastArgument = expressions.pop();
const commaSuffix =
shouldBreak === true ? hardline : shouldBreak === false ? " " : line;
const commas = ctx.Comma?.map(comma => concat([comma, commaSuffix]));
const otherArguments = rejectAndJoinSeps(commas, expressions);

if (lastArgument && params?.isHuggable) {
const argumentListGroupId = Symbol("argumentList");
const separator =
shouldBreak === true ? hardline : shouldBreak === false ? "" : softline;
return concat([
group([separator, otherArguments], { id: argumentListGroupId }),
ifBreak([lastArgument, dedent(separator)], dedent(lastArgument), {
groupId: argumentListGroupId
})
]);
}
return rejectAndConcat([otherArguments, lastArgument]);
const commaSuffix = flat || hugged ? " " : line;
const lastExpression = expressions.at(-1)!;
return concat([
...expressions
.slice(0, -1)
.flatMap((expression, index) => [
expression,
ctx.Comma![index],
commaSuffix
]),
hugged
? group([lastExpression, softline], { shouldBreak: true })
: lastExpression
]);
}

arrayCreationExpression(ctx: ArrayCreationExpressionCtx) {
Expand Down
4 changes: 2 additions & 2 deletions packages/prettier-plugin-java/src/printers/printer-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -650,11 +650,11 @@ export function binary(nodes: Doc[], tokens: IToken[], isRoot = false): Doc {
return group(join(line, level));
}
} else {
const content = indent(binary(nodes, tokens));
const content = binary(nodes, tokens);
nodes.unshift(
levelOperator !== undefined &&
needsParentheses(nextOperator, levelOperator)
? concat(["(", content, ")"])
? concat(["(", indent(content), ")"])
: content
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ArgumentListCstNode, ArgumentListCtx, IToken } from "java-parser";
import { builders, utils } from "prettier/doc";
import { dedent } from "../printers/prettier-builder.js";
import { putIntoBraces } from "../printers/printer-utils.js";

const { breakParent, conditionalGroup, softline } = builders;
Expand All @@ -16,17 +17,21 @@ export default function printArgumentListWithBraces(
!rBrace.leadingComments &&
isArgumentListHuggable(argumentListNodes[0].children)
) {
const [flat, expanded] = [false, true].map(shouldBreak => {
const argumentList = this.visit(argumentListNodes, {
isHuggable: true,
shouldBreak
});
return putIntoBraces(argumentList, "", lBrace, rBrace);
});
return [
willBreak(flat) ? breakParent : "",
conditionalGroup([flat, expanded])
];
const [flat, hugged, expanded] = [{ flat: true }, { hugged: true }, {}].map(
params => {
const argumentList = this.visit(argumentListNodes, params);
const flat = params.flat || params.hugged;
const separator = flat ? "" : softline;
return putIntoBraces(
flat ? dedent(argumentList) : argumentList,
separator,
lBrace,
rBrace
);
}
);
const alternatives = conditionalGroup([flat, hugged, expanded]);
return willBreak(flat) ? [breakParent, alternatives] : alternatives;
}

const argumentList = this.visit(argumentListNodes);
Expand All @@ -35,10 +40,17 @@ export default function printArgumentListWithBraces(

function isArgumentListHuggable(argumentList: ArgumentListCtx) {
const expressions = argumentList.expression;
const lastArgumentLambdaBodyExpression =
expressions.at(-1)?.children.lambdaExpression?.[0].children.lambdaBody[0]
.children.expression?.[0].children;
const lastArgumentLambdaBodyTernaryExpression =
lastArgumentLambdaBodyExpression?.ternaryExpression?.[0].children;
return (
(expressions.length === 1 ||
expressions[expressions.length - 1].children.lambdaExpression?.[0]
.children.lambdaBody[0].children.block !== undefined) &&
expressions.filter(({ children }) => children.lambdaExpression).length === 1
(!lastArgumentLambdaBodyExpression ||
lastArgumentLambdaBodyTernaryExpression?.QuestionMark !== undefined ||
lastArgumentLambdaBodyTernaryExpression?.binaryExpression[0].children
.unaryExpression.length === 1) &&
expressions.findIndex(({ children }) => children.lambdaExpression) ===
expressions.length - 1
);
}
30 changes: 30 additions & 0 deletions packages/prettier-plugin-java/test/unit-test/lambda/_input.java
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,36 @@ void argumentAfterLambdaWithBlock() {
}, g);
}

void huggableArguments() {
aaaaaaaaaaaaaaaaaaaaaaaa((bbbbbbbbbbbbbbbbbbbbbbbb, cccccccccccccccccccccccc, dddddddddddddddddddddddd) -> eeeeeeeeeeeeeeeeeeeeeeee.ffffffffffffffffffffffff());

a.b(c -> d -> eeeeeeeeee.ffffffffff(gggggggggg, hhhhhhhhhh, iiiiiiiiii, jjjjjjjjjj, kkkkkkkkkk));

a.b(c -> d && eeeeeeeeee.ffffffffff() ? g && hhhhhhhhhh.iiiiiiiiii() : j && kkkkkkkkkk.llllllllll());

a.b(c -> d && eeeeeeeeee.ffffffffff(gggggggggg, hhhhhhhhhh, iiiiiiiiii, jjjjjjjjjj, kkkkkkkkkk) > 0);

a.b(c, (c0, c1) -> d && eeeeeeeeee.ffffffffff(gggggggggg, hhhhhhhhhh, iiiiiiiiii, jjjjjjjjjj, kkkkkkkkkk) > 0);

a.b(c -> eeeeeeeeee.ffffffffff(gggggggggg, hhhhhhhhhh, iiiiiiiiii, jjjjjjjjjj, kkkkkkkkkk) > 0);

a.b(c, (c0, c1) -> eeeeeeeeee.ffffffffff(gggggggggg, hhhhhhhhhh, iiiiiiiiii, jjjjjjjjjj, kkkkkkkkkk) > 0);

a.b(c -> d && eeeeeeeeee.ffffffffff(gggggggggg, hhhhhhhhhh, iiiiiiiiii, jjjjjjjjjj, kkkkkkkkkk));

a.b(c, (c0, c1) -> d && eeeeeeeeee.ffffffffff(gggggggggg, hhhhhhhhhh, iiiiiiiiii, jjjjjjjjjj, kkkkkkkkkk));

a.b(c -> eeeeeeeeee.ffffffffff(gggggggggg, hhhhhhhhhh, iiiiiiiiii, jjjjjjjjjj, kkkkkkkkkk));

a.b(c -> {
eeeeeeeeee.ffffffffff(gggggggggg, hhhhhhhhhh, iiiiiiiiii, jjjjjjjjjj, kkkkkkkkkk);
});

a.b((c0, c1) -> eeeeeeeeee.ffffffffff(gggggggggg, hhhhhhhhhh, iiiiiiiiii, jjjjjjjjjj, kkkkkkkkkk));

a.b(c, (c0, c1) -> eeeeeeeeee.ffffffffff(gggggggggg, hhhhhhhhhh, iiiiiiiiii, jjjjjjjjjj, kkkkkkkkkk));
}

void lambdaWithLeadingComments() {
System.out.println(
List.of(1, 2, 3).stream().map(
Expand Down
Loading

0 comments on commit c6f18b9

Please sign in to comment.