Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ nullaway {

tasks.withType(JavaCompile).configureEach {
options.errorprone {
// Annotation processor output (derive4j, Immutables) is not ours to fix.
// Annotation processor output (Immutables) is not ours to fix.
excludedPaths = '.*/build/generated/.*'
}
}
Expand Down
2 changes: 0 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ compileTesting = { module = "com.google.testing.compile:compile-testing", versio
errorprone-core = { module = "com.google.errorprone:error_prone_core", version = "2.50.0" }
errorProneSupport-contrib = { module = "tech.picnic.error-prone-support:error-prone-contrib", version = "0.30.0" }
nullaway = { module = "com.uber.nullaway:nullaway", version = "0.14.2" }
derive4j = { module = "org.derive4j:derive4j", version = "1.1.1" }
derive4j-annotation = { module = "org.derive4j:derive4j-annotation", version = "1.1.1" }
functionaljava = { module = "org.functionaljava:functionaljava", version = "5.0" }
guava = { module = "com.google.guava:guava", version = "33.7.1-jre" }
guava-testlib = { module = "com.google.guava:guava-testlib", version = "33.5.0-jre" }
Expand Down
2 changes: 0 additions & 2 deletions open-java-format/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ dependencies {
compileOnly variantOf(libs.immutables.value) { classifier('annotations') }
annotationProcessor libs.immutables.value

compileOnly libs.derive4j.annotation
annotationProcessor libs.derive4j
implementation libs.jsr305
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,96 +23,99 @@
import com.palantir.javaformat.doc.Doc;
import com.palantir.javaformat.doc.Level;
import java.io.IOException;
import java.io.UncheckedIOException;
import org.derive4j.ArgOption;
import org.derive4j.Data;

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

R breakThisLevel();
static BreakBehaviour breakThisLevel() {
return new BreakThisLevel();
}

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

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

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

public abstract <R> R match(Cases<R> cases);
/** Break this level. */
record BreakThisLevel() implements BreakBehaviour {
@Override
public String toString() {
return "breakThisLevel()";
}
}

/** For {@link com.palantir.javaformat.doc.LevelDelimitedFlatValueDocVisitor}. */
@Override
public abstract String toString();
/**
* If the last level is breakable, prefer breaking it if it will keep the rest of this level on line line.
*
* @param keepIndentWhenInlined whether to keep this level's indent when inlined as a recursive level (when
* reached via a previous `preferBreakingLastInnerLevel` whose breakability was
* {@link LastLevelBreakability#CHECK_INNER})
*/
record PreferBreakingLastInnerLevel(boolean keepIndentWhenInlined) implements BreakBehaviour {
@Override
public String toString() {
return "preferBreakingLastInnerLevel(" + keepIndentWhenInlined + ")";
}
}

/**
* Attempt to inline the suffix of this level (which must be a {@link Level} and the last doc), recursing into the
* {@link Level} just before the last {@link Level} (if there is such a level) to see if that can be broken instead.
*
* <p>This behaves like {@link BreakThisLevel} if we couldn't recurse into such an inner level, or if the suffix
* level doesn't fit on the last line.
*/
record InlineSuffix() implements BreakBehaviour {
@Override
public String toString() {
return "inlineSuffix()";
}
}

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

/** Writes the case as {@code type} and its flag, if it has one. */
final class Json extends JsonSerializer<BreakBehaviour> {
@Override
public void serialize(BreakBehaviour value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
gen.writeStartObject();
BreakBehaviours.caseOf(value)
.breakThisLevel(() -> {
try {
gen.writeObjectField("type", "breakThisLevel");
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return null;
})
.preferBreakingLastInnerLevel(keepIndentWhenInlined -> {
try {
gen.writeObjectField("type", "preferBreakingLastInnerLevel");
gen.writeObjectField("keepIndentWhenInlined", keepIndentWhenInlined);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return null;
})
.inlineSuffix(() -> {
try {
gen.writeObjectField("type", "inlineSuffix");
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return null;
})
.breakOnlyIfInnerLevelsThenFitOnOneLine(keepIndentWhenInlined -> {
try {
gen.writeObjectField("type", "breakOnlyIfInnerLevelsThenFitOnOneLine");
gen.writeObjectField("keepIndentWhenInlined", keepIndentWhenInlined);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return null;
});
switch (value) {
case BreakThisLevel() -> gen.writeObjectField("type", "breakThisLevel");
case PreferBreakingLastInnerLevel(boolean keepIndentWhenInlined) -> {
gen.writeObjectField("type", "preferBreakingLastInnerLevel");
gen.writeObjectField("keepIndentWhenInlined", keepIndentWhenInlined);
}
case InlineSuffix() -> gen.writeObjectField("type", "inlineSuffix");
case BreakOnlyIfInnerLevelsThenFitOnOneLine(boolean keepIndentWhenInlined) -> {
gen.writeObjectField("type", "breakOnlyIfInnerLevelsThenFitOnOneLine");
gen.writeObjectField("keepIndentWhenInlined", keepIndentWhenInlined);
}
}
gen.writeEndObject();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,25 @@

/**
* How to decide whether to break the last inner level ("this level") of a parent level with
* {@link BreakBehaviour.Cases#preferBreakingLastInnerLevel}.
* {@link BreakBehaviour.PreferBreakingLastInnerLevel}.
*/
public enum LastLevelBreakability {
/**
* Default behaviour. When processing a {@link BreakBehaviour.Cases#preferBreakingLastInnerLevel} chain, if we've
* Default behaviour. When processing a {@link BreakBehaviour.PreferBreakingLastInnerLevel} chain, if we've
* arrived at a level with this breakability, then we should abort the chain.
*/
ABORT,
/**
* Unconditionally allow ending an inline chain at this level, after which this level may be broken as usual, or a
* prefix thereof could be inlined further (if it has the appropriate break behaviour of
* {@link BreakBehaviour.Cases#preferBreakingLastInnerLevel}). This should only be used when you know that the first
* {@link BreakBehaviour.PreferBreakingLastInnerLevel}). This should only be used when you know that the first
* non-Level {@link Doc} inside this level, if you flatten it, is a {@link Break}.
*/
ACCEPT_INLINE_CHAIN,
/**
* Delegate to the {@link LastLevelBreakability} of _this_ level's last inner level. Typically, this will be true if
* this level is not immediately followed by a break (see StartsWithBreakVisitor). Behaves the same as
* {@link #ABORT} if this level is not {@link BreakBehaviour.Cases#preferBreakingLastInnerLevel}.
* {@link #ABORT} if this level is not {@link BreakBehaviour.PreferBreakingLastInnerLevel}.
*/
CHECK_INNER,
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public abstract class OpenOp extends HasUniqueId implements Op {
*/
@Default
public BreakBehaviour breakBehaviour() {
return BreakBehaviours.breakThisLevel();
return BreakBehaviour.breakThisLevel();
}

/** If it's the last level of its parent, when to inline this level rather than break the parent. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

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