Skip to content

Commit d1cde3c

Browse files
authored
Format annotated fields in records (#1427)
Format annotated fields in records
1 parent de003ef commit d1cde3c

File tree

3 files changed

+372
-13
lines changed

3 files changed

+372
-13
lines changed

palantir-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java

Lines changed: 116 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
157157
*/
158158
private static final int METHOD_CHAIN_COLUMN_LIMIT = 80;
159159

160+
/**
161+
* Maximum column at which the annotated parameter of a record should start. This exists in particular to improve
162+
* readability when one or multiple annotations are added to the parameters of a record.
163+
*/
164+
private static final int ANNOTATION_RECORD_PARAMETER_COLUMN_LIMIT = 60;
165+
160166
/** Direction for Annotations (usually VERTICAL). */
161167
protected enum Direction {
162168
VERTICAL,
@@ -2212,13 +2218,23 @@ protected List<Op> visitModifiers(
22122218
ModifiersTree modifiersTree,
22132219
Direction annotationsDirection,
22142220
Optional<BreakTag> declarationAnnotationBreak) {
2215-
return visitModifiers(modifiersTree.getAnnotations(), annotationsDirection, declarationAnnotationBreak);
2221+
boolean isRecordParameter = modifiersTree instanceof JCTree.JCModifiers && isInRecord(modifiersTree);
2222+
return visitModifiers(
2223+
modifiersTree.getAnnotations(), annotationsDirection, declarationAnnotationBreak, isRecordParameter);
22162224
}
22172225

22182226
private List<Op> visitModifiers(
22192227
List<? extends AnnotationTree> annotationTrees,
22202228
Direction annotationsDirection,
22212229
Optional<BreakTag> declarationAnnotationBreak) {
2230+
return visitModifiers(annotationTrees, annotationsDirection, declarationAnnotationBreak, false);
2231+
}
2232+
2233+
private List<Op> visitModifiers(
2234+
List<? extends AnnotationTree> annotationTrees,
2235+
Direction annotationsDirection,
2236+
Optional<BreakTag> declarationAnnotationBreak,
2237+
boolean isRecordParameter) {
22222238
if (annotationTrees.isEmpty() && !nextIsModifier()) {
22232239
return EMPTY_LIST;
22242240
}
@@ -2241,9 +2257,19 @@ private List<Op> visitModifiers(
22412257
lastWasAnnotation = true;
22422258
}
22432259
builder.close();
2244-
ImmutableList<Op> trailingBreak = annotationsDirection.isVertical()
2245-
? forceBreakList(declarationAnnotationBreak)
2246-
: breakList(declarationAnnotationBreak);
2260+
2261+
// pjf specific: record params should take into consideration the columnLimit
2262+
ImmutableList<Op> trailingBreak = isRecordParameter
2263+
? ImmutableList.of(Break.builder()
2264+
.fillMode(FillMode.UNIFIED)
2265+
.flat(" ")
2266+
.plusIndent(ZERO)
2267+
.hasColumnLimit(true)
2268+
.optTag(declarationAnnotationBreak)
2269+
.build())
2270+
: annotationsDirection.isVertical()
2271+
? forceBreakList(declarationAnnotationBreak)
2272+
: breakList(declarationAnnotationBreak);
22472273
if (annotations.isEmpty() && !nextIsModifier()) {
22482274
return trailingBreak;
22492275
}
@@ -2387,6 +2413,11 @@ protected void visitFormals(Optional<VariableTree> receiver, List<? extends Vari
23872413
if (!receiver.isPresent() && parameters.isEmpty()) {
23882414
return;
23892415
}
2416+
boolean isRecordParams = false;
2417+
if (!parameters.isEmpty() && parameters.get(0) instanceof JCTree.JCVariableDecl) {
2418+
isRecordParams = isInRecord(((JCTree.JCVariableDecl) parameters.get(0)).mods);
2419+
}
2420+
23902421
builder.open(ZERO);
23912422
boolean first = true;
23922423
if (receiver.isPresent()) {
@@ -2410,13 +2441,26 @@ protected void visitFormals(Optional<VariableTree> receiver, List<? extends Vari
24102441
if (!first) {
24112442
builder.breakOp(" ");
24122443
}
2444+
BreakTag declarationAnnotationBreak = new BreakTag();
2445+
2446+
// pfj specific: add a conditional blank line if the annotated parameter was broken (only for records)
2447+
if (isRecordParams && !first) {
2448+
builder.blankLineWanted(BlankLineWanted.conditional(declarationAnnotationBreak));
2449+
}
2450+
24132451
visitToDeclare(
24142452
DeclarationKind.PARAMETER,
24152453
Direction.HORIZONTAL,
24162454
parameter,
24172455
/* initializer= */ Optional.empty(),
24182456
"=",
2419-
i < parameters.size() - 1 ? Optional.of(",") : /* a= */ Optional.empty());
2457+
i < parameters.size() - 1 ? Optional.of(",") : /* a= */ Optional.empty(),
2458+
Optional.of(declarationAnnotationBreak));
2459+
2460+
// pfj specific: add a conditional blank line if the annotated parameter was broken (only for records)
2461+
if (isRecordParams && i < parameters.size() - 1) {
2462+
builder.blankLineWanted(BlankLineWanted.conditional(declarationAnnotationBreak));
2463+
}
24202464
first = false;
24212465
}
24222466
builder.close();
@@ -2586,6 +2630,17 @@ private void visitToDeclare(
25862630
Optional<ExpressionTree> initializer,
25872631
String equals,
25882632
Optional<String> trailing) {
2633+
visitToDeclare(kind, annotationsDirection, node, initializer, equals, trailing, Optional.empty());
2634+
}
2635+
2636+
private void visitToDeclare(
2637+
DeclarationKind kind,
2638+
Direction annotationsDirection,
2639+
VariableTree node,
2640+
Optional<ExpressionTree> initializer,
2641+
String equals,
2642+
Optional<String> trailing,
2643+
Optional<BreakTag> annotationBreakForRecords) {
25892644
sync(node);
25902645
declareOne(
25912646
kind,
@@ -2598,7 +2653,8 @@ private void visitToDeclare(
25982653
initializer,
25992654
trailing,
26002655
/* receiverExpression= */ Optional.empty(),
2601-
/* typeWithDims= */ Optional.empty());
2656+
/* typeWithDims= */ Optional.empty(),
2657+
annotationBreakForRecords);
26022658
}
26032659

26042660
/** Does not omit the leading '<', which should be associated with the type name. */
@@ -3402,9 +3458,39 @@ int declareOne(
34023458
Optional<String> trailing,
34033459
Optional<ExpressionTree> receiverExpression,
34043460
Optional<TypeWithDims> typeWithDims) {
3461+
return declareOne(
3462+
kind,
3463+
annotationsDirection,
3464+
modifiers,
3465+
type,
3466+
name,
3467+
op,
3468+
equals,
3469+
initializer,
3470+
trailing,
3471+
receiverExpression,
3472+
typeWithDims,
3473+
Optional.empty());
3474+
}
3475+
3476+
/** Declare one variable or variable-like thing. */
3477+
@SuppressWarnings("TooManyArguments")
3478+
int declareOne(
3479+
DeclarationKind kind,
3480+
Direction annotationsDirection,
3481+
Optional<ModifiersTree> modifiers,
3482+
Tree type,
3483+
Name name,
3484+
String op,
3485+
String equals,
3486+
Optional<ExpressionTree> initializer,
3487+
Optional<String> trailing,
3488+
Optional<ExpressionTree> receiverExpression,
3489+
Optional<TypeWithDims> typeWithDims,
3490+
Optional<BreakTag> verticalAnnotationBreakForRecords) {
34053491

34063492
BreakTag typeBreak = new BreakTag();
3407-
BreakTag verticalAnnotationBreak = new BreakTag();
3493+
BreakTag verticalAnnotationBreak = verticalAnnotationBreakForRecords.orElseGet(BreakTag::new);
34083494

34093495
// If the node is a field declaration, try to output any declaration
34103496
// annotations in-line. If the entire declaration doesn't fit on a single
@@ -3419,12 +3505,22 @@ int declareOne(
34193505
new ArrayDeque<>(typeWithDims.isPresent() ? typeWithDims.get().dims : Collections.emptyList());
34203506
int baseDims = 0;
34213507

3422-
builder.open(
3423-
kind == DeclarationKind.PARAMETER
3424-
&& (modifiers.isPresent()
3425-
&& !modifiers.get().getAnnotations().isEmpty())
3426-
? plusFour
3427-
: ZERO);
3508+
if (kind == DeclarationKind.PARAMETER
3509+
&& modifiers.isPresent()
3510+
&& !modifiers.get().getAnnotations().isEmpty()) {
3511+
if (!isInRecord(modifiers.get())) {
3512+
builder.open(plusFour);
3513+
} else {
3514+
// pjf specific: we are enforcing the column limit for annotated parameters of records
3515+
builder.open(OpenOp.builder()
3516+
.debugName("visitParam")
3517+
.plusIndent(ZERO)
3518+
.columnLimitBeforeLastBreak(ANNOTATION_RECORD_PARAMETER_COLUMN_LIMIT)
3519+
.build());
3520+
}
3521+
} else {
3522+
builder.open(ZERO);
3523+
}
34283524
{
34293525
if (modifiers.isPresent()) {
34303526
visitAndBreakModifiers(modifiers.get(), annotationsDirection, Optional.of(verticalAnnotationBreak));
@@ -3794,6 +3890,13 @@ private Direction inlineAnnotationDirection(ModifiersTree modifiers) {
37943890
return Direction.HORIZONTAL;
37953891
}
37963892

3893+
/**
3894+
* Checks if the modifiers are parameters of a record definition.
3895+
*/
3896+
private boolean isInRecord(ModifiersTree modifiers) {
3897+
return (((JCTree.JCModifiers) modifiers).flags & RECORD) == RECORD;
3898+
}
3899+
37973900
/**
37983901
* Emit a {@link Token}.
37993902
*
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
@Schema(description = "Type of quote being requested", example = "NEW_BUSINESS", somethingElse= "new", other="my other long string")
2+
public record QuoteRequest(
3+
@Schema(description = "US state of the product being quoted", example = "TX")
4+
RegulatoryState regulatoryState,
5+
@Schema(description = "Reason for a quote", example = "New Business") String amendmentReason,
6+
@Schema(description = "Type of quote being requested", example = "NEW_BUSINESS", somethingElse= "new", other="my other long string") QuoteType quoteType,
7+
@Schema(description = "Date the quoted changes become active", example = "2023-06-25")
8+
LocalDate quoteEffectiveDate
9+
) {}
10+
11+
public record QuoteRequest(
12+
13+
int value,
14+
15+
@SomeInput RegulatoryState regulatoryState,
16+
17+
@SomeInput
18+
@NotNull
19+
@Deprecated
20+
@JsonValue(name = "something") @Schema(description = "US state of the product being quoted", example = "TX") RegulatoryState regulatoryState,
21+
@Schema(description = "Reason for a quote", example = "New Business") String amendmentReason,
22+
int x,
23+
int j) {}
24+
25+
public record Url(
26+
int var1,
27+
@JsonValue("value") String var2,
28+
@SomeInput @JsonValue String var3,
29+
int var3,
30+
int var4,
31+
int var5,
32+
int var6) {}
33+
34+
public record Url(
35+
@NotNull
36+
@JsonValue
37+
@Deprecated
38+
@Annotation1
39+
@Annotation2
40+
@Annotation3
41+
@Annotation4
42+
@Annotation5
43+
@Annotation6
44+
@Annotation7
45+
@Annotation8
46+
@Annotation9
47+
@Annotation10
48+
@Annotation11
49+
@Annotation12
50+
String value) {}
51+
52+
public record Url(@NotNull String value, int number) {}
53+
54+
public record Url(
55+
int number,
56+
@JsonValue("value") String value) {}
57+
58+
public record Url(@New @JsonValue("value") String value) {}
59+
60+
@JsonIgnoreProperties(ignoreUnknown = true)
61+
@JsonInclude(JsonInclude.Include.NON_NULL)
62+
public record Game(
63+
@JsonProperty("accounting_group") @JsonPropertyDescription("The accounting group of the game.")
64+
String accountingGroup,
65+
@JsonProperty("accumulating")
66+
@JsonPropertyDescription("Marks which games with accumulating bonuses.") Boolean accumulating,
67+
@JsonProperty("bonus_buy") @JsonPropertyDescription("Games with purchasable bonuses.") Boolean bonusBuy,
68+
@JsonProperty("category")
69+
@JsonPropertyDescription(
70+
"Game's category. Allowed values: slots, roulette, card, "
71+
+ "casual, lottery, poker, craps, video_poker")
72+
String category,
73+
@JsonProperty("hd") @JsonPropertyDescription("HD format games.") Boolean hd,
74+
75+
@JsonProperty("new") @JsonPropertyDescription("New format games.") Boolean assa,
76+
@JsonProperty("hit_rate") @JsonPropertyDescription(
77+
"Frequency of wins per 100 bets. The higher, the hit rate the lower the volatility "
78+
+ "rating, and vice versa. Positive value. For slots only. "
79+
+ "Match pattern: ^\\d{1,18}(\\.\\d{1,12})?$") String hitRate,
80+
@JsonProperty("params") @JsonPropertyDescription("Game's custom parameters.")
81+
Map<String, String> params) {
82+
83+
private void get(@JsonNode("value") String value) {}
84+
85+
private void get2(@JsonNode(value="value") String value) {}
86+
}
87+
88+
record ApiEndpoint(@JsonValue("name") String name, HttpMethod method, String url) {
89+
ApiEndpoint {
90+
name = name.toUpperCase();
91+
}
92+
}
93+
94+
record ApiEndpoint(@JsonProperty("bonus_buy") @JsonValue(some_other_name_that_is_here="value", name_which_is_super_long_that_goes_beyond_the_limit="name", other="other") String name, HttpMethod method, String url) {
95+
ApiEndpoint {
96+
name = name.toUpperCase();
97+
}
98+
}
99+
100+
record ApiEndpoint(@JsonValue(name="name") String name, HttpMethod method, String url) {
101+
ApiEndpoint {
102+
name = name.toUpperCase();
103+
}
104+
}
105+
106+
record ApiEndpoint(@JsonValue(name="name") String name, HttpMethod method, String url, @JsonValue(name="name") String name) {
107+
ApiEndpoint {
108+
name = name.toUpperCase();
109+
}
110+
}

0 commit comments

Comments
 (0)