Skip to content

Commit

Permalink
feat: improve binary expression formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
jtkiesel authored and clementdessoude committed Aug 15, 2023
1 parent 9f2687d commit b98cf67
Show file tree
Hide file tree
Showing 7 changed files with 376 additions and 244 deletions.
106 changes: 80 additions & 26 deletions packages/prettier-plugin-java/src/printers/comments/handle-comments.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,98 @@
import { hasLeadingComments } from "./comments-utils";
import { BinaryExpressionCtx, IToken } from "java-parser";
import { hasLeadingComments, hasTrailingComments } from "./comments-utils";
import {
BinaryExpressionCtx,
IToken,
UnaryExpressionCstNode
} from "java-parser";

export function handleCommentsBinaryExpression(ctx: BinaryExpressionCtx) {
moveOperatorLeadingCommentsToNextExpression(ctx);
moveExpressionTrailingCommentsToNextOperator(ctx);
}

function moveOperatorLeadingCommentsToNextExpression(ctx: BinaryExpressionCtx) {
let unaryExpressionIndex = 1;
if (ctx.BinaryOperator !== undefined) {
ctx.BinaryOperator.forEach(binaryOperator => {
if (hasLeadingComments(binaryOperator)) {
ctx.BinaryOperator?.forEach(binaryOperator => {
if (hasLeadingComments(binaryOperator)) {
while (
ctx.unaryExpression[unaryExpressionIndex].location.startOffset <
binaryOperator.endOffset
) {
unaryExpressionIndex++;
}

// Adapt the position of the operator and its leading comments
const shiftUp =
binaryOperator.leadingComments[0].startLine -
1 -
binaryOperator.startLine;

if (
binaryOperator.startLine !==
ctx.unaryExpression[unaryExpressionIndex].location.startLine
) {
binaryOperator.leadingComments.forEach(comment => {
comment.startLine += 1;
comment.endLine += 1;
});
}
binaryOperator.startLine += shiftUp;
binaryOperator.endLine += shiftUp;

// Move binaryOperator's leading comments to the following
// unaryExpression
ctx.unaryExpression[unaryExpressionIndex].leadingComments =
ctx.unaryExpression[unaryExpressionIndex].leadingComments || [];
ctx.unaryExpression[unaryExpressionIndex].leadingComments!.unshift(
...binaryOperator.leadingComments
);
delete (binaryOperator as IToken).leadingComments;
}
});
}

function moveExpressionTrailingCommentsToNextOperator(
ctx: BinaryExpressionCtx
) {
const binaryOperators = ctx.BinaryOperator;
let binaryOperatorIndex = 1;
if (binaryOperators?.length) {
ctx.unaryExpression.forEach(unaryExpression => {
if (hasTrailingComments(unaryExpression)) {
while (
ctx.unaryExpression[unaryExpressionIndex].location.startOffset <
binaryOperator.endOffset
binaryOperatorIndex < binaryOperators.length &&
unaryExpression.location.endOffset &&
binaryOperators[binaryOperatorIndex].startOffset <
unaryExpression.location.endOffset
) {
unaryExpressionIndex++;
binaryOperatorIndex++;
}
const binaryOperator = binaryOperators[binaryOperatorIndex];

// Adapt the position of the operator and its leading comments
// Adapt the position of the expression and its trailing comments
const shiftUp =
binaryOperator.leadingComments[0].startLine -
unaryExpression.trailingComments[0].startLine -
1 -
binaryOperator.startLine;
unaryExpression.location.startLine;

if (
binaryOperator.startLine !==
ctx.unaryExpression[unaryExpressionIndex].location.startLine
) {
binaryOperator.leadingComments.forEach(comment => {
if (unaryExpression.location.startLine !== binaryOperator.startLine) {
unaryExpression.trailingComments.forEach(comment => {
comment.startLine += 1;
comment.endLine += 1;
});
}
binaryOperator.startLine += shiftUp;
binaryOperator.endLine += shiftUp;

// Assign the leading comments & trailing comments of the binaryOperator
// to the following unaryExpression as leading comments
ctx.unaryExpression[unaryExpressionIndex].leadingComments =
ctx.unaryExpression[unaryExpressionIndex].leadingComments || [];
ctx.unaryExpression[unaryExpressionIndex].leadingComments!.unshift(
...binaryOperator.leadingComments
unaryExpression.location.startLine += shiftUp;
if (unaryExpression.location.endLine !== undefined) {
unaryExpression.location.endLine += shiftUp;
}

// Move unaryExpression's trailing comments to the following
// binaryOperator
binaryOperator.trailingComments = binaryOperator.trailingComments ?? [];
binaryOperator.trailingComments.unshift(
...unaryExpression.trailingComments
);
delete (binaryOperator as IToken).leadingComments;
delete (unaryExpression as UnaryExpressionCstNode).trailingComments;
}
});
}
Expand Down
150 changes: 52 additions & 98 deletions packages/prettier-plugin-java/src/printers/expressions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import {
ReferenceTypeCastExpressionCtx,
RegularLambdaParameterCtx,
TernaryExpressionCtx,
TypeArgumentListCtx,
TypeArgumentsOrDiamondCtx,
TypePatternCtx,
UnaryExpressionCtx,
Expand All @@ -50,7 +49,6 @@ import {
} from "java-parser/api";

import forEach from "lodash/forEach";
import { Doc } from "prettier";
import { builders } from "prettier/doc";
import { BaseCstPrettierPrinter } from "../base-cst-printer";
import { isAnnotationCstNode } from "../types/utils";
Expand All @@ -63,20 +61,18 @@ import { printTokenWithComments } from "./comments/format-comments";
import { handleCommentsBinaryExpression } from "./comments/handle-comments";
import { concat, dedent, group, indent } from "./prettier-builder";
import {
binary,
findDeepElementInPartsArray,
isExplicitLambdaParameter,
isShiftOperator,
isUniqueMethodInvocation,
matchCategory,
putIntoBraces,
rejectAndConcat,
rejectAndJoin,
rejectAndJoinSeps,
separateTokensIntoGroups,
sortAnnotationIdentifier,
sortNodes
sortNodes,
sortTokens
} from "./printer-utils";
import join = builders.join;

const { ifBreak, line, softline, indentIfBreak } = builders;

Expand Down Expand Up @@ -244,104 +240,54 @@ export class ExpressionsPrettierVisitor extends BaseCstPrettierPrinter {
return binaryExpression;
}

binaryExpression(ctx: BinaryExpressionCtx, params: any) {
binaryExpression(
ctx: BinaryExpressionCtx,
params?: { addParenthesisToWrapStatement?: boolean }
) {
handleCommentsBinaryExpression(ctx);

const instanceofReferences = this.mapVisit(
sortNodes([ctx.pattern, ctx.referenceType])
);
const expression = this.mapVisit(ctx.expression);
const unaryExpression = this.mapVisit(ctx.unaryExpression);

const { groupsOfOperator, sortedBinaryOperators } =
separateTokensIntoGroups(ctx);
const segmentsSplitByBinaryOperator: any[] = [];
let currentSegment = [];
const sortedNodes = sortNodes([
ctx.pattern,
ctx.referenceType,
ctx.expression,
ctx.unaryExpression
]);

if (groupsOfOperator.length === 1 && groupsOfOperator[0].length === 0) {
return unaryExpression.shift();
}
const nodes = this.mapVisit(
sortedNodes,
sortedNodes.length === 1 ? params : undefined
);
const tokens = sortTokens([
ctx.Instanceof,
ctx.AssignmentOperator,
ctx.Less,
ctx.Greater,
ctx.BinaryOperator
]);
const hasTokens = tokens.length > 0;

groupsOfOperator.forEach(subgroup => {
currentSegment = [unaryExpression.shift()];
for (let i = 0; i < subgroup.length; i++) {
const token = subgroup[i];
const shiftOperator = isShiftOperator(subgroup, i);
if (token.tokenType.name === "Instanceof") {
currentSegment.push(
rejectAndJoin(" ", [
ctx.Instanceof![0],
instanceofReferences.shift()
])
);
} else if (matchCategory(token, "'AssignmentOperator'")) {
currentSegment.push(
indent(rejectAndJoin(line, [token, expression.shift()]))
);
} else if (
shiftOperator === "leftShift" ||
shiftOperator === "rightShift"
) {
currentSegment.push(
rejectAndJoin(" ", [
rejectAndConcat([token, subgroup[i + 1]]),
unaryExpression.shift()
])
);
i++;
} else if (shiftOperator === "doubleRightShift") {
currentSegment.push(
rejectAndJoin(" ", [
rejectAndConcat([token, subgroup[i + 1], subgroup[i + 2]]),
unaryExpression.shift()
])
);
i += 2;
} else if (matchCategory(token, "'BinaryOperator'")) {
currentSegment.push(
rejectAndJoin(line, [token, unaryExpression.shift()])
);
}
}
segmentsSplitByBinaryOperator.push(
group(rejectAndJoin(" ", currentSegment))
);
});
const content = binary(nodes, tokens, true);

if (params !== undefined && params.addParenthesisToWrapStatement) {
return group(
concat([
ifBreak("(", ""),
indent(
concat([
softline,
group(
rejectAndJoinSeps(
sortedBinaryOperators.map(elt => concat([" ", elt, line])),
segmentsSplitByBinaryOperator
)
)
])
),
softline,
ifBreak(")")
])
);
}

return group(
rejectAndJoinSeps(
sortedBinaryOperators.map(elt => concat([" ", elt, line])),
segmentsSplitByBinaryOperator
)
);
return hasTokens && params?.addParenthesisToWrapStatement
? group(
concat([
ifBreak("("),
indent(concat([softline, content])),
softline,
ifBreak(")")
])
)
: content;
}

unaryExpression(ctx: UnaryExpressionCtx) {
unaryExpression(
ctx: UnaryExpressionCtx,
params?: { addParenthesisToWrapStatement?: boolean }
) {
const unaryPrefixOperator = ctx.UnaryPrefixOperator
? ctx.UnaryPrefixOperator
: [];
const primary = this.visit(ctx.primary);
const primary = this.visit(ctx.primary, params);
const unarySuffixOperator = ctx.UnarySuffixOperator
? ctx.UnarySuffixOperator
: [];
Expand Down Expand Up @@ -369,10 +315,14 @@ export class ExpressionsPrettierVisitor extends BaseCstPrettierPrinter {
]);
}

primary(ctx: PrimaryCtx) {
primary(
ctx: PrimaryCtx,
params?: { addParenthesisToWrapStatement?: boolean }
) {
const countMethodInvocation = isUniqueMethodInvocation(ctx.primarySuffix);

const primaryPrefix = this.visit(ctx.primaryPrefix, {
...params,
shouldBreakBeforeFirstMethodInvocation: countMethodInvocation > 1
});

Expand Down Expand Up @@ -561,9 +511,13 @@ export class ExpressionsPrettierVisitor extends BaseCstPrettierPrinter {
return rejectAndConcat([keyWord, typeArguments]);
}

parenthesisExpression(ctx: ParenthesisExpressionCtx) {
parenthesisExpression(
ctx: ParenthesisExpressionCtx,
params?: { addParenthesisToWrapStatement?: boolean }
) {
const expression = this.visit(ctx.expression);
return putIntoBraces(expression, softline, ctx.LBrace[0], ctx.RBrace[0]);
const separator = params?.addParenthesisToWrapStatement ? softline : "";
return putIntoBraces(expression, separator, ctx.LBrace[0], ctx.RBrace[0]);
}

castExpression(ctx: CastExpressionCtx) {
Expand Down
Loading

0 comments on commit b98cf67

Please sign in to comment.