Skip to content

Commit 2fbf175

Browse files
authored
Merge pull request #99 from openjavaformat/break-behaviour-without-derive4j
Replace derive4j with a sealed interface and records
2 parents 26f6755 + 4c647aa commit 2fbf175

10 files changed

Lines changed: 194 additions & 186 deletions

File tree

‎buildSrc/src/main/groovy/open-java-format.error-prone-conventions.gradle‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ nullaway {
1616

1717
tasks.withType(JavaCompile).configureEach {
1818
options.errorprone {
19-
// Annotation processor output (derive4j, Immutables) is not ours to fix.
19+
// Annotation processor output (Immutables) is not ours to fix.
2020
excludedPaths = '.*/build/generated/.*'
2121
}
2222
}

‎gradle/libs.versions.toml‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ compileTesting = { module = "com.google.testing.compile:compile-testing", versio
2626
errorprone-core = { module = "com.google.errorprone:error_prone_core", version = "2.50.0" }
2727
errorProneSupport-contrib = { module = "tech.picnic.error-prone-support:error-prone-contrib", version = "0.30.0" }
2828
nullaway = { module = "com.uber.nullaway:nullaway", version = "0.14.2" }
29-
derive4j = { module = "org.derive4j:derive4j", version = "1.1.1" }
30-
derive4j-annotation = { module = "org.derive4j:derive4j-annotation", version = "1.1.1" }
3129
functionaljava = { module = "org.functionaljava:functionaljava", version = "5.0" }
3230
guava = { module = "com.google.guava:guava", version = "33.7.1-jre" }
3331
guava-testlib = { module = "com.google.guava:guava-testlib", version = "33.5.0-jre" }

‎open-java-format/build.gradle‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ dependencies {
2626
compileOnly variantOf(libs.immutables.value) { classifier('annotations') }
2727
annotationProcessor libs.immutables.value
2828

29-
compileOnly libs.derive4j.annotation
30-
annotationProcessor libs.derive4j
3129
implementation libs.jsr305
3230
}
3331

‎open-java-format/src/main/java/com/palantir/javaformat/BreakBehaviour.java‎

Lines changed: 75 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -23,96 +23,99 @@
2323
import com.palantir.javaformat.doc.Doc;
2424
import com.palantir.javaformat.doc.Level;
2525
import java.io.IOException;
26-
import java.io.UncheckedIOException;
27-
import org.derive4j.ArgOption;
28-
import org.derive4j.Data;
2926

30-
@Data(arguments = ArgOption.checkedNotNull)
27+
/**
28+
* How a {@link Level} is broken when it does not fit on one line. Each case's {@code toString()} is what
29+
* {@link com.palantir.javaformat.doc.LevelDelimitedFlatValueDocVisitor} prints for it.
30+
*/
3131
@Immutable
3232
@JsonSerialize(using = BreakBehaviour.Json.class)
33-
public abstract class BreakBehaviour {
34-
public interface Cases<R> {
33+
public sealed interface BreakBehaviour {
3534

36-
R breakThisLevel();
35+
static BreakBehaviour breakThisLevel() {
36+
return new BreakThisLevel();
37+
}
3738

38-
/**
39-
* If the last level is breakable, prefer breaking it if it will keep the rest of this level on line line.
40-
*
41-
* @param keepIndentWhenInlined whether to keep this level's indent when inlined as a recursive level (when
42-
* reached via a previous `preferBreakingLastInnerLevel` whose breakability was
43-
* {@link LastLevelBreakability#CHECK_INNER})
44-
*/
45-
R preferBreakingLastInnerLevel(boolean keepIndentWhenInlined);
39+
static BreakBehaviour preferBreakingLastInnerLevel(boolean keepIndentWhenInlined) {
40+
return new PreferBreakingLastInnerLevel(keepIndentWhenInlined);
41+
}
4642

47-
/**
48-
* Attempt to inline the suffix of this level (which must be a {@link Level} and the last doc), recursing into
49-
* the {@link Level} just before the last {@link Level} (if there is such a level) to see if that can be broken
50-
* instead.
51-
*
52-
* <p>This behaves like {@link #breakThisLevel()} if we couldn't recurse into such an inner level, or if the
53-
* suffix level doesn't fit on the last line.
54-
*/
55-
R inlineSuffix();
43+
static BreakBehaviour inlineSuffix() {
44+
return new InlineSuffix();
45+
}
5646

57-
/**
58-
* Break if by doing so all inner levels then fit on a single line. However, don't break if we can fit in the
59-
* {@link Doc docs} up to the first break (which might be nested inside the next doc if it's a {@link Level}),
60-
* in order to prevent exceeding the maxLength accidentally.
61-
*/
62-
R breakOnlyIfInnerLevelsThenFitOnOneLine(boolean keepIndentWhenInlined);
47+
static BreakBehaviour breakOnlyIfInnerLevelsThenFitOnOneLine(boolean keepIndentWhenInlined) {
48+
return new BreakOnlyIfInnerLevelsThenFitOnOneLine(keepIndentWhenInlined);
6349
}
6450

65-
public abstract <R> R match(Cases<R> cases);
51+
/** Break this level. */
52+
record BreakThisLevel() implements BreakBehaviour {
53+
@Override
54+
public String toString() {
55+
return "breakThisLevel()";
56+
}
57+
}
6658

67-
/** For {@link com.palantir.javaformat.doc.LevelDelimitedFlatValueDocVisitor}. */
68-
@Override
69-
public abstract String toString();
59+
/**
60+
* If the last level is breakable, prefer breaking it if it will keep the rest of this level on line line.
61+
*
62+
* @param keepIndentWhenInlined whether to keep this level's indent when inlined as a recursive level (when
63+
* reached via a previous `preferBreakingLastInnerLevel` whose breakability was
64+
* {@link LastLevelBreakability#CHECK_INNER})
65+
*/
66+
record PreferBreakingLastInnerLevel(boolean keepIndentWhenInlined) implements BreakBehaviour {
67+
@Override
68+
public String toString() {
69+
return "preferBreakingLastInnerLevel(" + keepIndentWhenInlined + ")";
70+
}
71+
}
72+
73+
/**
74+
* Attempt to inline the suffix of this level (which must be a {@link Level} and the last doc), recursing into the
75+
* {@link Level} just before the last {@link Level} (if there is such a level) to see if that can be broken instead.
76+
*
77+
* <p>This behaves like {@link BreakThisLevel} if we couldn't recurse into such an inner level, or if the suffix
78+
* level doesn't fit on the last line.
79+
*/
80+
record InlineSuffix() implements BreakBehaviour {
81+
@Override
82+
public String toString() {
83+
return "inlineSuffix()";
84+
}
85+
}
7086

7187
/**
72-
* This is gross but just wanted to get something working. See https://github.com/derive4j/derive4j/issues/51 for a
73-
* potential better implementation.
88+
* Break if by doing so all inner levels then fit on a single line. However, don't break if we can fit in the
89+
* {@link Doc docs} up to the first break (which might be nested inside the next doc if it's a {@link Level}), in
90+
* order to prevent exceeding the maxLength accidentally.
91+
*
92+
* @param keepIndentWhenInlined whether to keep this level's indent when inlined as a recursive level
7493
*/
75-
static class Json extends JsonSerializer<BreakBehaviour> {
94+
record BreakOnlyIfInnerLevelsThenFitOnOneLine(boolean keepIndentWhenInlined) implements BreakBehaviour {
95+
@Override
96+
public String toString() {
97+
return "breakOnlyIfInnerLevelsThenFitOnOneLine(" + keepIndentWhenInlined + ")";
98+
}
99+
}
76100

101+
/** Writes the case as {@code type} and its flag, if it has one. */
102+
final class Json extends JsonSerializer<BreakBehaviour> {
77103
@Override
78104
public void serialize(BreakBehaviour value, JsonGenerator gen, SerializerProvider serializers)
79105
throws IOException {
80106
gen.writeStartObject();
81-
BreakBehaviours.caseOf(value)
82-
.breakThisLevel(() -> {
83-
try {
84-
gen.writeObjectField("type", "breakThisLevel");
85-
} catch (IOException e) {
86-
throw new UncheckedIOException(e);
87-
}
88-
return null;
89-
})
90-
.preferBreakingLastInnerLevel(keepIndentWhenInlined -> {
91-
try {
92-
gen.writeObjectField("type", "preferBreakingLastInnerLevel");
93-
gen.writeObjectField("keepIndentWhenInlined", keepIndentWhenInlined);
94-
} catch (IOException e) {
95-
throw new UncheckedIOException(e);
96-
}
97-
return null;
98-
})
99-
.inlineSuffix(() -> {
100-
try {
101-
gen.writeObjectField("type", "inlineSuffix");
102-
} catch (IOException e) {
103-
throw new UncheckedIOException(e);
104-
}
105-
return null;
106-
})
107-
.breakOnlyIfInnerLevelsThenFitOnOneLine(keepIndentWhenInlined -> {
108-
try {
109-
gen.writeObjectField("type", "breakOnlyIfInnerLevelsThenFitOnOneLine");
110-
gen.writeObjectField("keepIndentWhenInlined", keepIndentWhenInlined);
111-
} catch (IOException e) {
112-
throw new UncheckedIOException(e);
113-
}
114-
return null;
115-
});
107+
switch (value) {
108+
case BreakThisLevel() -> gen.writeObjectField("type", "breakThisLevel");
109+
case PreferBreakingLastInnerLevel(boolean keepIndentWhenInlined) -> {
110+
gen.writeObjectField("type", "preferBreakingLastInnerLevel");
111+
gen.writeObjectField("keepIndentWhenInlined", keepIndentWhenInlined);
112+
}
113+
case InlineSuffix() -> gen.writeObjectField("type", "inlineSuffix");
114+
case BreakOnlyIfInnerLevelsThenFitOnOneLine(boolean keepIndentWhenInlined) -> {
115+
gen.writeObjectField("type", "breakOnlyIfInnerLevelsThenFitOnOneLine");
116+
gen.writeObjectField("keepIndentWhenInlined", keepIndentWhenInlined);
117+
}
118+
}
116119
gen.writeEndObject();
117120
}
118121
}

‎open-java-format/src/main/java/com/palantir/javaformat/LastLevelBreakability.java‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,25 @@
2020

2121
/**
2222
* How to decide whether to break the last inner level ("this level") of a parent level with
23-
* {@link BreakBehaviour.Cases#preferBreakingLastInnerLevel}.
23+
* {@link BreakBehaviour.PreferBreakingLastInnerLevel}.
2424
*/
2525
public enum LastLevelBreakability {
2626
/**
27-
* Default behaviour. When processing a {@link BreakBehaviour.Cases#preferBreakingLastInnerLevel} chain, if we've
27+
* Default behaviour. When processing a {@link BreakBehaviour.PreferBreakingLastInnerLevel} chain, if we've
2828
* arrived at a level with this breakability, then we should abort the chain.
2929
*/
3030
ABORT,
3131
/**
3232
* Unconditionally allow ending an inline chain at this level, after which this level may be broken as usual, or a
3333
* prefix thereof could be inlined further (if it has the appropriate break behaviour of
34-
* {@link BreakBehaviour.Cases#preferBreakingLastInnerLevel}). This should only be used when you know that the first
34+
* {@link BreakBehaviour.PreferBreakingLastInnerLevel}). This should only be used when you know that the first
3535
* non-Level {@link Doc} inside this level, if you flatten it, is a {@link Break}.
3636
*/
3737
ACCEPT_INLINE_CHAIN,
3838
/**
3939
* Delegate to the {@link LastLevelBreakability} of _this_ level's last inner level. Typically, this will be true if
4040
* this level is not immediately followed by a break (see StartsWithBreakVisitor). Behaves the same as
41-
* {@link #ABORT} if this level is not {@link BreakBehaviour.Cases#preferBreakingLastInnerLevel}.
41+
* {@link #ABORT} if this level is not {@link BreakBehaviour.PreferBreakingLastInnerLevel}.
4242
*/
4343
CHECK_INNER,
4444
/**

‎open-java-format/src/main/java/com/palantir/javaformat/OpenOp.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public abstract class OpenOp extends HasUniqueId implements Op {
4141
*/
4242
@Default
4343
public BreakBehaviour breakBehaviour() {
44-
return BreakBehaviours.breakThisLevel();
44+
return BreakBehaviour.breakThisLevel();
4545
}
4646

4747
/** If it's the last level of its parent, when to inline this level rather than break the parent. */

‎open-java-format/src/main/java/com/palantir/javaformat/PartialInlineability.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
/**
2222
* What are the conditions for a level to be partially inlineable. Partial inlining refers to the behaviour of
23-
* {@link BreakBehaviours#breakOnlyIfInnerLevelsThenFitOnOneLine} where a level is too large to fit on the current line,
23+
* {@link BreakBehaviour.BreakOnlyIfInnerLevelsThenFitOnOneLine} where a level is too large to fit on the current line,
2424
* but a prefix thereof is partially inlined onto the current line.
2525
*
2626
* <p>Specifically, when inlining a level with the above behaviour, the partial inlineability of its first

0 commit comments

Comments
 (0)