From 646ba3d0be70bbd19ebaaa3ceb3a86a31255d5be Mon Sep 17 00:00:00 2001 From: xzxiaoshan <365384722@qq.com> Date: Fri, 23 Oct 2026 17:18:46 +0800 Subject: [PATCH 1/2] feat: add maxLineLength option to googleJavaFormat step Expose the column-limit setting that google-java-format 1.37.0 added via JavaFormatterOptions.Style.builder().maxLineLength(int): - Gradle: googleJavaFormat('1.37.0').maxLineLength(240) - Maven: 1.37.0240 The step rejects maxLineLength with gjf older than 1.37.0 at configuration time with a clear error message. The glue code keeps compiling against gjf 1.28.0 (so older versions and JDK 17 keep working) and calls the 1.37.0 builder API reflectively. google-java-format 1.37.0 turned JavaFormatterOptions.Style from an enum into a record, which removed Style.valueOf(String) and broke the existing glue with a NoSuchMethodError. Style names are now resolved against the GOOGLE/AOSP constants, which exist as static fields in both shapes, so the step works again on 1.37.0+ regardless of maxLineLength. Fixes #3151 --- CHANGES.md | 3 + .../java/GoogleJavaFormatFormatterFunc.java | 56 +++++++++++++-- .../spotless/java/GoogleJavaFormatStep.java | 71 ++++++++++++++++--- plugin-gradle/CHANGES.md | 3 + plugin-gradle/README.md | 3 +- .../gradle/spotless/JavaExtension.java | 14 +++- .../GoogleJavaFormatIntegrationTest.java | 26 +++++++ plugin-maven/CHANGES.md | 3 + plugin-maven/README.md | 1 + .../spotless/maven/java/GoogleJavaFormat.java | 8 ++- .../maven/java/GoogleJavaFormatTest.java | 16 +++++ .../JavaCodeFormattedMaxLineLength240.test | 11 +++ .../java/GoogleJavaFormatStepTest.java | 33 +++++++++ 13 files changed, 231 insertions(+), 17 deletions(-) create mode 100644 testlib/src/main/resources/java/googlejavaformat/JavaCodeFormattedMaxLineLength240.test diff --git a/CHANGES.md b/CHANGES.md index 9929ccf7a6..3c0974788b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,9 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] +### Features +- `googleJavaFormat` gained a `maxLineLength` option, which overrides the 100-column limit prescribed by the GOOGLE and AOSP styles. It requires `google-java-format` `1.37.0` or later, and the step now also works at all with `1.37.0+` (upstream changed `JavaFormatterOptions.Style` from an enum to a record in `1.37.0`). ([#3151](https://github.com/diffplug/spotless/issues/3151)) + ## [4.10.4] - 2026-10-08 ### Changes diff --git a/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatFormatterFunc.java b/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatFormatterFunc.java index d13f8d9d40..501db11974 100644 --- a/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatFormatterFunc.java +++ b/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatFormatterFunc.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 DiffPlug + * Copyright 2023-2026 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ */ package com.diffplug.spotless.glue.java; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Objects; import javax.annotation.Nonnull; @@ -46,19 +48,65 @@ public class GoogleJavaFormatFormatterFunc implements FormatterFunc { private final boolean reorderImports; - public GoogleJavaFormatFormatterFunc(@Nonnull String version, @Nonnull String style, boolean reflowStrings, boolean reorderImports, boolean formatJavadoc) { + public GoogleJavaFormatFormatterFunc(@Nonnull String version, @Nonnull String style, boolean reflowStrings, boolean reorderImports, boolean formatJavadoc, int maxLineLength) { this.version = Objects.requireNonNull(version); - this.formatterStyle = Style.valueOf(Objects.requireNonNull(style)); + this.formatterStyle = toStyle(style); this.reflowStrings = reflowStrings; this.reorderImports = reorderImports; - JavaFormatterOptions.Builder builder = JavaFormatterOptions.builder().style(formatterStyle); + Style effectiveStyle = formatterStyle; + if (maxLineLength > 0) { + effectiveStyle = withMaxLineLength(formatterStyle, maxLineLength, version); + } + JavaFormatterOptions.Builder builder = JavaFormatterOptions.builder().style(effectiveStyle); if (!formatJavadoc) { builder = builder.formatJavadoc(false); } this.formatter = new Formatter(builder.build()); } + /** + * Parses a style name without calling {@code Style.valueOf}, which existed only while + * {@code Style} was an enum (google-java-format {@literal <=} 1.36); since 1.37 it is a record, + * but the {@code GOOGLE} and {@code AOSP} constants are present in both. + */ + @Nonnull + private static Style toStyle(@Nonnull String style) { + switch (Objects.requireNonNull(style)) { + case "GOOGLE": + return Style.GOOGLE; + case "AOSP": + return Style.AOSP; + default: + throw new IllegalArgumentException("style must be GOOGLE or AOSP, but was: " + style); + } + } + + /** + * Sets the column limit on the style's builder. That builder exists only in google-java-format + * 1.37.0+, while this glue code is compiled against 1.28.0 so that it keeps working on older + * versions, hence the reflection (https://github.com/diffplug/spotless/issues/3151). + * Methods are looked up on the public {@code Style$Builder} type rather than on the runtime + * class of the builder, because the runtime class generated by AutoBuilder is package-private. + */ + @Nonnull + private static Style withMaxLineLength(@Nonnull Style style, int maxLineLength, @Nonnull String version) { + try { + Method createBuilder = Style.class.getMethod("builder"); + Class builderType = createBuilder.getReturnType(); + Object builder = createBuilder.invoke(null); + builder = builderType.getMethod(style == Style.AOSP ? "aosp" : "google").invoke(builder); + builder = builderType.getMethod("maxLineLength", int.class).invoke(builder, maxLineLength); + return (Style) builderType.getMethod("build").invoke(builder); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException( + "maxLineLength requires google-java-format 1.37.0 or later (you are using " + version + ")", e); + } catch (IllegalAccessException | InvocationTargetException e) { + Throwable cause = e instanceof InvocationTargetException ? e.getCause() : e; + throw new IllegalArgumentException("Failed to apply maxLineLength " + maxLineLength, cause); + } + } + @Override @Nonnull public String apply(@Nonnull String input) throws Exception { diff --git a/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java b/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java index 079955a731..aeacd3a1f1 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java +++ b/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java @@ -18,6 +18,7 @@ import java.io.Serial; import java.io.Serializable; import java.lang.reflect.Constructor; +import java.util.Arrays; import java.util.Objects; import com.diffplug.spotless.FormatterFunc; @@ -34,6 +35,10 @@ public final class GoogleJavaFormatStep implements Serializable { private static final boolean DEFAULT_REFLOW_LONG_STRINGS = false; private static final boolean DEFAULT_REORDER_IMPORTS = false; private static final boolean DEFAULT_FORMAT_JAVADOC = true; + /** 0 means "not set", i.e. use whatever column limit the formatter's style prescribes. */ + private static final int DEFAULT_MAX_LINE_LENGTH = 0; + /** The first version which made `JavaFormatterOptions.Style` configurable, see [#3151](https://github.com/diffplug/spotless/issues/3151). */ + static final String MIN_VERSION_FOR_MAX_LINE_LENGTH = "1.37.0"; private static final String NAME = "google-java-format"; private static final String MAVEN_COORDINATE = "com.google.googlejavaformat:google-java-format"; @@ -44,19 +49,22 @@ public final class GoogleJavaFormatStep implements Serializable { private final boolean reflowLongStrings; private final boolean reorderImports; private final boolean formatJavadoc; + private final int maxLineLength; private GoogleJavaFormatStep(JarState.Promised jarState, String version, String style, boolean reflowLongStrings, boolean reorderImports, - boolean formatJavadoc) { + boolean formatJavadoc, + int maxLineLength) { this.jarState = jarState; this.version = version; this.style = style; this.reflowLongStrings = reflowLongStrings; this.reorderImports = reorderImports; this.formatJavadoc = formatJavadoc; + this.maxLineLength = maxLineLength; } /** Creates a step which formats everything - code, import order, and unused imports. */ @@ -89,14 +97,22 @@ public static FormatterStep create(String groupArtifact, String version, String /** Creates a step which formats everything - groupArtifact, code, import order, and unused imports - and optionally reflows long strings. */ public static FormatterStep create(String groupArtifact, String version, String style, Provisioner provisioner, boolean reflowLongStrings, boolean reorderImports, boolean formatJavadoc) { - return createInternally(NAME, groupArtifact, version, style, provisioner, reflowLongStrings, reorderImports, formatJavadoc, false); + return create(groupArtifact, version, style, provisioner, reflowLongStrings, reorderImports, formatJavadoc, DEFAULT_MAX_LINE_LENGTH); + } + + /** + * Creates a step which formats everything - groupArtifact, code, import order, and unused imports - and optionally reflows long strings. + * A positive {@code maxLineLength} overrides the column limit prescribed by the style, and requires google-java-format {@code 1.37.0} or later. + */ + public static FormatterStep create(String groupArtifact, String version, String style, Provisioner provisioner, boolean reflowLongStrings, boolean reorderImports, boolean formatJavadoc, int maxLineLength) { + return createInternally(NAME, groupArtifact, version, style, provisioner, reflowLongStrings, reorderImports, formatJavadoc, maxLineLength, false); } static FormatterStep createRemoveUnusedImportsOnly(Provisioner provisioner) { - return createInternally(RemoveUnusedImportsStep.NAME, MAVEN_COORDINATE, defaultVersion(), defaultStyle(), provisioner, defaultReflowLongStrings(), defaultReorderImports(), defaultFormatJavadoc(), true); + return createInternally(RemoveUnusedImportsStep.NAME, MAVEN_COORDINATE, defaultVersion(), defaultStyle(), provisioner, defaultReflowLongStrings(), defaultReorderImports(), defaultFormatJavadoc(), DEFAULT_MAX_LINE_LENGTH, true); } - private static FormatterStep createInternally(String name, String groupArtifact, String version, String style, Provisioner provisioner, boolean reflowLongStrings, boolean reorderImports, boolean formatJavadoc, boolean removeImports) { + private static FormatterStep createInternally(String name, String groupArtifact, String version, String style, Provisioner provisioner, boolean reflowLongStrings, boolean reorderImports, boolean formatJavadoc, int maxLineLength, boolean removeImports) { Objects.requireNonNull(groupArtifact, "groupArtifact"); if (groupArtifact.chars().filter(ch -> ch == ':').count() != 1) { throw new IllegalArgumentException("groupArtifact must be in the form 'groupId:artifactId'"); @@ -104,8 +120,14 @@ private static FormatterStep createInternally(String name, String groupArtifact, Objects.requireNonNull(version, "version"); Objects.requireNonNull(style, "style"); Objects.requireNonNull(provisioner, "provisioner"); + if (maxLineLength < 0) { + throw new IllegalArgumentException("maxLineLength must be positive, but was: " + maxLineLength); + } + if (maxLineLength > 0 && compareVersions(version, MIN_VERSION_FOR_MAX_LINE_LENGTH) < 0) { + throw new IllegalArgumentException("maxLineLength requires google-java-format " + MIN_VERSION_FOR_MAX_LINE_LENGTH + " or later (you are using " + version + ")"); + } - GoogleJavaFormatStep step = new GoogleJavaFormatStep(JarState.promise(() -> JarState.from(groupArtifact + ":" + version, provisioner)), version, style, reflowLongStrings, reorderImports, formatJavadoc); + GoogleJavaFormatStep step = new GoogleJavaFormatStep(JarState.promise(() -> JarState.from(groupArtifact + ":" + version, provisioner)), version, style, reflowLongStrings, reorderImports, formatJavadoc, maxLineLength); if (removeImports) { return FormatterStep.create(name, step, @@ -150,8 +172,36 @@ public static boolean defaultFormatJavadoc() { return DEFAULT_FORMAT_JAVADOC; } + public static int defaultMaxLineLength() { + return DEFAULT_MAX_LINE_LENGTH; + } + + /** Compares dotted numeric versions, e.g. {@code compareVersions("1.36.1", "1.37.0") < 0}. */ + private static int compareVersions(String left, String right) { + int[] lhs = parseVersion(left); + int[] rhs = parseVersion(right); + int numberOfElements = Math.max(lhs.length, rhs.length); + lhs = Arrays.copyOf(lhs, numberOfElements); + rhs = Arrays.copyOf(rhs, numberOfElements); + for (int i = 0; i < numberOfElements; i++) { + if (lhs[i] != rhs[i]) { + return lhs[i] > rhs[i] ? 1 : -1; + } + } + return 0; + } + + private static int[] parseVersion(String version) { + try { + String withoutSuffix = version.endsWith("-SNAPSHOT") ? version.substring(0, version.length() - "-SNAPSHOT".length()) : version; + return Arrays.stream(withoutSuffix.split("\\.")).mapToInt(Integer::parseInt).toArray(); + } catch (Exception e) { + throw new IllegalArgumentException("Not a semantic version: " + version, e); + } + } + private State equalityState() { - return new State(version, style, jarState.get(), reflowLongStrings, reorderImports, formatJavadoc); + return new State(version, style, jarState.get(), reflowLongStrings, reorderImports, formatJavadoc, maxLineLength); } private static final class State implements Serializable { @@ -164,13 +214,15 @@ private static final class State implements Serializable { private final boolean reflowLongStrings; private final boolean reorderImports; private final boolean formatJavadoc; + private final int maxLineLength; State(String version, String style, JarState jarState, boolean reflowLongStrings, boolean reorderImports, - boolean formatJavadoc) { + boolean formatJavadoc, + int maxLineLength) { JVM_SUPPORT.assertFormatterSupported(version); ModuleHelper.doOpenInternalPackagesIfRequired(); this.jarState = jarState; @@ -179,13 +231,14 @@ private static final class State implements Serializable { this.reflowLongStrings = reflowLongStrings; this.reorderImports = reorderImports; this.formatJavadoc = formatJavadoc; + this.maxLineLength = maxLineLength; } FormatterFunc createFormat() throws Exception { final ClassLoader classLoader = jarState.getClassLoader(); Class formatterFunc = classLoader.loadClass("com.diffplug.spotless.glue.java.GoogleJavaFormatFormatterFunc"); - Constructor constructor = formatterFunc.getConstructor(String.class, String.class, boolean.class, boolean.class, boolean.class); - FormatterFunc googleJavaFormatFormatterFunc = (FormatterFunc) constructor.newInstance(version, style, reflowLongStrings, reorderImports, formatJavadoc); + Constructor constructor = formatterFunc.getConstructor(String.class, String.class, boolean.class, boolean.class, boolean.class, int.class); + FormatterFunc googleJavaFormatFormatterFunc = (FormatterFunc) constructor.newInstance(version, style, reflowLongStrings, reorderImports, formatJavadoc, maxLineLength); return JVM_SUPPORT.suggestLaterVersionOnError(version, googleJavaFormatFormatterFunc); } diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index d3249bf262..6c821ca4d1 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -4,6 +4,9 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] +### Features +- `googleJavaFormat` gained `maxLineLength(int)`, which overrides the 100-column limit prescribed by the GOOGLE and AOSP styles. It requires `google-java-format` `1.37.0` or later. ([#3151](https://github.com/diffplug/spotless/issues/3151)) + ## [8.10.4] - 2026-10-08 ### Changes diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 258fd6450b..886bb7248d 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -380,8 +380,9 @@ spotless { googleJavaFormat() // optional: you can specify a specific version (>= 1.8) and/or switch to AOSP style // and/or reflow long strings + // and/or override the 100-column limit (requires google-java-format 1.37.0+) // and/or use custom group artifact (you probably don't need this) - googleJavaFormat('1.8').aosp().reflowLongStrings().formatJavadoc(false).reorderImports(false).groupArtifact('com.google.googlejavaformat:google-java-format') + googleJavaFormat('1.8').aosp().reflowLongStrings().maxLineLength(120).formatJavadoc(false).reorderImports(false).groupArtifact('com.google.googlejavaformat:google-java-format') ``` ### palantir-java-format diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java index 9411811322..8fa5c20b88 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java @@ -211,6 +211,7 @@ public class GoogleJavaFormatConfig { boolean reflowLongStrings; boolean reorderImports; boolean formatJavadoc = true; + int maxLineLength = GoogleJavaFormatStep.defaultMaxLineLength(); GoogleJavaFormatConfig(String version) { this.version = Objects.requireNonNull(version); @@ -261,6 +262,16 @@ public GoogleJavaFormatConfig formatJavadoc(boolean formatJavadoc) { return this; } + /** + * Overrides the column limit prescribed by the style (100 for both GOOGLE and AOSP). + * Requires google-java-format 1.37.0 or later. + */ + public GoogleJavaFormatConfig maxLineLength(int maxLineLength) { + this.maxLineLength = maxLineLength; + replaceStep(createStep()); + return this; + } + private FormatterStep createStep() { return GoogleJavaFormatStep.create( groupArtifact, @@ -269,7 +280,8 @@ private FormatterStep createStep() { provisioner(), reflowLongStrings, reorderImports, - formatJavadoc); + formatJavadoc, + maxLineLength); } } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GoogleJavaFormatIntegrationTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GoogleJavaFormatIntegrationTest.java index dde872d3e5..869126a170 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GoogleJavaFormatIntegrationTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GoogleJavaFormatIntegrationTest.java @@ -15,6 +15,8 @@ */ package com.diffplug.gradle.spotless; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + import java.io.IOException; import org.junit.jupiter.api.Test; @@ -101,4 +103,28 @@ void integrationWithSkipJavadocFormatting() throws IOException { "googleJavaFormat()"); checkRunsThenUpToDate(); } + + @Test + void integrationWithMaxLineLength() throws IOException { + assumeTrue(Jvm.version() >= 21, "google-java-format 1.37.0 requires JVM 21+"); + + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "", + "spotless {", + " java {", + " target file('test.java')", + " googleJavaFormat('1.37.0').maxLineLength(240)", + " }", + "}"); + + setFile("test.java").toResource("java/googlejavaformat/JavaCodeUnformatted.test"); + gradleRunner().withArguments("spotlessApply").build(); + assertFile("test.java").sameAsResource("java/googlejavaformat/JavaCodeFormattedMaxLineLength240.test"); + + checkRunsThenUpToDate(); + } } diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 46c259e6e9..3576459072 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -4,6 +4,9 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] +### Features +- `` gained a `` parameter, which overrides the 100-column limit prescribed by the GOOGLE and AOSP styles. It requires `google-java-format` `1.37.0` or later. ([#3151](https://github.com/diffplug/spotless/issues/3151)) + ## [3.10.4] - 2026-10-08 ### Changes diff --git a/plugin-maven/README.md b/plugin-maven/README.md index de25140289..f034c527de 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -343,6 +343,7 @@ Types in `java.lang` and in the file's own package are shortened without adding 1.8 true + 120 false false. diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/GoogleJavaFormat.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/GoogleJavaFormat.java index d0a2b24954..8b88ab3d57 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/GoogleJavaFormat.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/GoogleJavaFormat.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 DiffPlug + * Copyright 2016-2026 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,9 @@ public class GoogleJavaFormat implements FormatterStepFactory { @Parameter private Boolean formatJavadoc; + @Parameter + private Integer maxLineLength; + @Override public FormatterStep newFormatterStep(FormatterStepConfig config) { String groupArtifact = this.groupArtifact != null ? this.groupArtifact : GoogleJavaFormatStep.defaultGroupArtifact(); @@ -49,6 +52,7 @@ public FormatterStep newFormatterStep(FormatterStepConfig config) { boolean reflowLongStrings = this.reflowLongStrings != null ? this.reflowLongStrings : GoogleJavaFormatStep.defaultReflowLongStrings(); boolean reorderImports = this.reorderImports != null ? this.reorderImports : GoogleJavaFormatStep.defaultReorderImports(); boolean formatJavadoc = this.formatJavadoc != null ? this.formatJavadoc : GoogleJavaFormatStep.defaultFormatJavadoc(); - return GoogleJavaFormatStep.create(groupArtifact, version, style, config.getProvisioner(), reflowLongStrings, reorderImports, formatJavadoc); + int maxLineLength = this.maxLineLength != null ? this.maxLineLength : GoogleJavaFormatStep.defaultMaxLineLength(); + return GoogleJavaFormatStep.create(groupArtifact, version, style, config.getProvisioner(), reflowLongStrings, reorderImports, formatJavadoc, maxLineLength); } } diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/GoogleJavaFormatTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/GoogleJavaFormatTest.java index 0723d73c0e..eed60d531b 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/GoogleJavaFormatTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/GoogleJavaFormatTest.java @@ -15,8 +15,11 @@ */ package com.diffplug.spotless.maven.java; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + import org.junit.jupiter.api.Test; +import com.diffplug.spotless.Jvm; import com.diffplug.spotless.java.GoogleJavaFormatStep; import com.diffplug.spotless.maven.MavenIntegrationHarness; @@ -76,6 +79,19 @@ void specificVersionSkipJavadocFormatting() throws Exception { runTest("java/googlejavaformat/JavaCodeFormattedSkipJavadocFormatting.test"); } + @Test + void specificVersionMaxLineLength() throws Exception { + assumeTrue(Jvm.version() >= 21, "google-java-format 1.37.0 requires JVM 21+"); + + writePomWithJavaSteps( + "", + " 1.37.0", + " 240", + ""); + + runTest("java/googlejavaformat/JavaCodeFormattedMaxLineLength240.test"); + } + private void runTest(String targetResource) throws Exception { runTest(targetResource, "java/googlejavaformat/JavaCodeUnformatted.test"); } diff --git a/testlib/src/main/resources/java/googlejavaformat/JavaCodeFormattedMaxLineLength240.test b/testlib/src/main/resources/java/googlejavaformat/JavaCodeFormattedMaxLineLength240.test new file mode 100644 index 0000000000..2000494af2 --- /dev/null +++ b/testlib/src/main/resources/java/googlejavaformat/JavaCodeFormattedMaxLineLength240.test @@ -0,0 +1,11 @@ +import mylib.UsedA; +import mylib.UsedB; + +public class Java { + /** Some javadoc. */ + public static void main(String[] args) { + System.out.println("A very very very very very very very very very very very very very very very very very very very very very long string that goes beyond the 100-character line length."); + UsedB.someMethod(); + UsedA.someMethod(); + } +} diff --git a/testlib/src/test/java/com/diffplug/spotless/java/GoogleJavaFormatStepTest.java b/testlib/src/test/java/com/diffplug/spotless/java/GoogleJavaFormatStepTest.java index cae19f4bca..ebd992490b 100644 --- a/testlib/src/test/java/com/diffplug/spotless/java/GoogleJavaFormatStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/java/GoogleJavaFormatStepTest.java @@ -15,6 +15,8 @@ */ package com.diffplug.spotless.java; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.condition.JRE.JAVA_20; import static org.junit.jupiter.api.condition.JRE.JAVA_21; import static org.junit.jupiter.api.condition.JRE.JAVA_25; @@ -140,6 +142,37 @@ void behaviorWithReorderImports() throws Exception { } } + @Test + @EnabledForJreRange(min = JAVA_21) + void behavior137() throws Exception { + FormatterStep step = GoogleJavaFormatStep.create("1.37.0", TestProvisioner.mavenCentral()); + StepHarness.forStep(step) + .testResource("java/googlejavaformat/JavaCodeUnformatted.test", "java/googlejavaformat/JavaCodeFormatted.test"); + } + + @Test + @EnabledForJreRange(min = JAVA_21) + void behaviorWithMaxLineLength() throws Exception { + FormatterStep step = GoogleJavaFormatStep.create( + GoogleJavaFormatStep.defaultGroupArtifact(), + "1.37.0", + GoogleJavaFormatStep.defaultStyle(), + TestProvisioner.mavenCentral(), + GoogleJavaFormatStep.defaultReflowLongStrings(), + GoogleJavaFormatStep.defaultReorderImports(), + GoogleJavaFormatStep.defaultFormatJavadoc(), + 240); + StepHarness.forStep(step) + .testResource("java/googlejavaformat/JavaCodeUnformatted.test", "java/googlejavaformat/JavaCodeFormattedMaxLineLength240.test"); + } + + @Test + void maxLineLengthRequiresVersion1Dot37OrLater() { + IllegalArgumentException error = assertThrows(IllegalArgumentException.class, + () -> GoogleJavaFormatStep.create(GoogleJavaFormatStep.defaultGroupArtifact(), "1.36.1", GoogleJavaFormatStep.defaultStyle(), TestProvisioner.mavenCentral(), false, false, true, 120)); + assertTrue(error.getMessage().contains("maxLineLength requires google-java-format 1.37.0 or later"), error.getMessage()); + } + @Test @EnabledForJreRange(max = JAVA_20) void equality() throws Exception { From ab9a64d09e74d0bb4fee8d1a5ecc8251eb638b09 Mon Sep 17 00:00:00 2001 From: xzxiaoshan <365384722@qq.com> Date: Mon, 26 Oct 2026 09:26:09 +0800 Subject: [PATCH 2/2] refactor: base maxLineLength on gjf 1.38.0 instead of carrying a 1.37.0 workaround google/google-java-format#1477 (merged, to be released as gjf 1.38.0) restores the enum-compatible Style.valueOf(String)/values()/name() methods on the record Style, so the glue no longer needs toStyle() to parse style names. Revert the glue to Style.valueOf and raise the maxLineLength gate to 1.38.0, the first version that combines a configurable Style with those restored methods. The short-lived 1.37.0 remains unsupported for Spotless, in line with the upstream fix. The behavior tests now target gjf 1.38.0 and will turn green in CI once that version is published. --- CHANGES.md | 2 +- .../java/GoogleJavaFormatFormatterFunc.java | 27 +++++-------------- .../spotless/java/GoogleJavaFormatStep.java | 10 ++++--- plugin-gradle/CHANGES.md | 2 +- plugin-gradle/README.md | 2 +- .../gradle/spotless/JavaExtension.java | 2 +- .../GoogleJavaFormatIntegrationTest.java | 4 +-- plugin-maven/CHANGES.md | 2 +- plugin-maven/README.md | 2 +- .../maven/java/GoogleJavaFormatTest.java | 4 +-- .../java/GoogleJavaFormatStepTest.java | 16 +++-------- 11 files changed, 27 insertions(+), 46 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3c0974788b..b500216169 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,7 +12,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Features -- `googleJavaFormat` gained a `maxLineLength` option, which overrides the 100-column limit prescribed by the GOOGLE and AOSP styles. It requires `google-java-format` `1.37.0` or later, and the step now also works at all with `1.37.0+` (upstream changed `JavaFormatterOptions.Style` from an enum to a record in `1.37.0`). ([#3151](https://github.com/diffplug/spotless/issues/3151)) +- `googleJavaFormat` gained a `maxLineLength` option, which overrides the 100-column limit prescribed by the GOOGLE and AOSP styles. It requires `google-java-format` `1.38.0` or later (the first version which combines a configurable `JavaFormatterOptions.Style` with the enum-compatible methods restored by [google/google-java-format#1477](https://github.com/google/google-java-format/pull/1477)). ([#3151](https://github.com/diffplug/spotless/issues/3151)) ## [4.10.4] - 2026-10-08 diff --git a/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatFormatterFunc.java b/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatFormatterFunc.java index 501db11974..cd61af3e4a 100644 --- a/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatFormatterFunc.java +++ b/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatFormatterFunc.java @@ -50,7 +50,7 @@ public class GoogleJavaFormatFormatterFunc implements FormatterFunc { public GoogleJavaFormatFormatterFunc(@Nonnull String version, @Nonnull String style, boolean reflowStrings, boolean reorderImports, boolean formatJavadoc, int maxLineLength) { this.version = Objects.requireNonNull(version); - this.formatterStyle = toStyle(style); + this.formatterStyle = Style.valueOf(Objects.requireNonNull(style)); this.reflowStrings = reflowStrings; this.reorderImports = reorderImports; @@ -65,27 +65,12 @@ public GoogleJavaFormatFormatterFunc(@Nonnull String version, @Nonnull String st this.formatter = new Formatter(builder.build()); } - /** - * Parses a style name without calling {@code Style.valueOf}, which existed only while - * {@code Style} was an enum (google-java-format {@literal <=} 1.36); since 1.37 it is a record, - * but the {@code GOOGLE} and {@code AOSP} constants are present in both. - */ - @Nonnull - private static Style toStyle(@Nonnull String style) { - switch (Objects.requireNonNull(style)) { - case "GOOGLE": - return Style.GOOGLE; - case "AOSP": - return Style.AOSP; - default: - throw new IllegalArgumentException("style must be GOOGLE or AOSP, but was: " + style); - } - } - /** * Sets the column limit on the style's builder. That builder exists only in google-java-format - * 1.37.0+, while this glue code is compiled against 1.28.0 so that it keeps working on older - * versions, hence the reflection (https://github.com/diffplug/spotless/issues/3151). + * 1.37.0+ (1.38.0+ also restores the enum-compatible methods this glue relies on, see + * google/google-java-format#1477), while this glue code is compiled against 1.28.0 so that it + * keeps working on older versions, hence the reflection + * (https://github.com/diffplug/spotless/issues/3151). * Methods are looked up on the public {@code Style$Builder} type rather than on the runtime * class of the builder, because the runtime class generated by AutoBuilder is package-private. */ @@ -100,7 +85,7 @@ private static Style withMaxLineLength(@Nonnull Style style, int maxLineLength, return (Style) builderType.getMethod("build").invoke(builder); } catch (NoSuchMethodException e) { throw new IllegalArgumentException( - "maxLineLength requires google-java-format 1.37.0 or later (you are using " + version + ")", e); + "maxLineLength requires google-java-format 1.38.0 or later (you are using " + version + ")", e); } catch (IllegalAccessException | InvocationTargetException e) { Throwable cause = e instanceof InvocationTargetException ? e.getCause() : e; throw new IllegalArgumentException("Failed to apply maxLineLength " + maxLineLength, cause); diff --git a/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java b/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java index aeacd3a1f1..e726284254 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java +++ b/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java @@ -37,8 +37,12 @@ public final class GoogleJavaFormatStep implements Serializable { private static final boolean DEFAULT_FORMAT_JAVADOC = true; /** 0 means "not set", i.e. use whatever column limit the formatter's style prescribes. */ private static final int DEFAULT_MAX_LINE_LENGTH = 0; - /** The first version which made `JavaFormatterOptions.Style` configurable, see [#3151](https://github.com/diffplug/spotless/issues/3151). */ - static final String MIN_VERSION_FOR_MAX_LINE_LENGTH = "1.37.0"; + /** + * The first version which combines a configurable {@code JavaFormatterOptions.Style} with the + * enum-compatible methods restored by [google/google-java-format#1477](https://github.com/google/google-java-format/pull/1477), + * see [#3151](https://github.com/diffplug/spotless/issues/3151). + */ + static final String MIN_VERSION_FOR_MAX_LINE_LENGTH = "1.38.0"; private static final String NAME = "google-java-format"; private static final String MAVEN_COORDINATE = "com.google.googlejavaformat:google-java-format"; @@ -102,7 +106,7 @@ public static FormatterStep create(String groupArtifact, String version, String /** * Creates a step which formats everything - groupArtifact, code, import order, and unused imports - and optionally reflows long strings. - * A positive {@code maxLineLength} overrides the column limit prescribed by the style, and requires google-java-format {@code 1.37.0} or later. + * A positive {@code maxLineLength} overrides the column limit prescribed by the style, and requires google-java-format {@code 1.38.0} or later. */ public static FormatterStep create(String groupArtifact, String version, String style, Provisioner provisioner, boolean reflowLongStrings, boolean reorderImports, boolean formatJavadoc, int maxLineLength) { return createInternally(NAME, groupArtifact, version, style, provisioner, reflowLongStrings, reorderImports, formatJavadoc, maxLineLength, false); diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 6c821ca4d1..789234e47b 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -5,7 +5,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Features -- `googleJavaFormat` gained `maxLineLength(int)`, which overrides the 100-column limit prescribed by the GOOGLE and AOSP styles. It requires `google-java-format` `1.37.0` or later. ([#3151](https://github.com/diffplug/spotless/issues/3151)) +- `googleJavaFormat` gained `maxLineLength(int)`, which overrides the 100-column limit prescribed by the GOOGLE and AOSP styles. It requires `google-java-format` `1.38.0` or later. ([#3151](https://github.com/diffplug/spotless/issues/3151)) ## [8.10.4] - 2026-10-08 diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 886bb7248d..88a94c7d4f 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -380,7 +380,7 @@ spotless { googleJavaFormat() // optional: you can specify a specific version (>= 1.8) and/or switch to AOSP style // and/or reflow long strings - // and/or override the 100-column limit (requires google-java-format 1.37.0+) + // and/or override the 100-column limit (requires google-java-format 1.38.0+) // and/or use custom group artifact (you probably don't need this) googleJavaFormat('1.8').aosp().reflowLongStrings().maxLineLength(120).formatJavadoc(false).reorderImports(false).groupArtifact('com.google.googlejavaformat:google-java-format') ``` diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java index 8fa5c20b88..8d86d8ba73 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java @@ -264,7 +264,7 @@ public GoogleJavaFormatConfig formatJavadoc(boolean formatJavadoc) { /** * Overrides the column limit prescribed by the style (100 for both GOOGLE and AOSP). - * Requires google-java-format 1.37.0 or later. + * Requires google-java-format 1.38.0 or later. */ public GoogleJavaFormatConfig maxLineLength(int maxLineLength) { this.maxLineLength = maxLineLength; diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GoogleJavaFormatIntegrationTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GoogleJavaFormatIntegrationTest.java index 869126a170..bc5419ca3b 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GoogleJavaFormatIntegrationTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GoogleJavaFormatIntegrationTest.java @@ -106,7 +106,7 @@ void integrationWithSkipJavadocFormatting() throws IOException { @Test void integrationWithMaxLineLength() throws IOException { - assumeTrue(Jvm.version() >= 21, "google-java-format 1.37.0 requires JVM 21+"); + assumeTrue(Jvm.version() >= 21, "google-java-format 1.38.0 requires JVM 21+"); setFile("build.gradle").toLines( "plugins {", @@ -117,7 +117,7 @@ void integrationWithMaxLineLength() throws IOException { "spotless {", " java {", " target file('test.java')", - " googleJavaFormat('1.37.0').maxLineLength(240)", + " googleJavaFormat('1.38.0').maxLineLength(240)", " }", "}"); diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 3576459072..fba813b848 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -5,7 +5,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Features -- `` gained a `` parameter, which overrides the 100-column limit prescribed by the GOOGLE and AOSP styles. It requires `google-java-format` `1.37.0` or later. ([#3151](https://github.com/diffplug/spotless/issues/3151)) +- `` gained a `` parameter, which overrides the 100-column limit prescribed by the GOOGLE and AOSP styles. It requires `google-java-format` `1.38.0` or later. ([#3151](https://github.com/diffplug/spotless/issues/3151)) ## [3.10.4] - 2026-10-08 diff --git a/plugin-maven/README.md b/plugin-maven/README.md index f034c527de..21dc4ac218 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -343,7 +343,7 @@ Types in `java.lang` and in the file's own package are shortened without adding 1.8 true - 120 + 120 false false. diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/GoogleJavaFormatTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/GoogleJavaFormatTest.java index eed60d531b..a1da8a423e 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/GoogleJavaFormatTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/GoogleJavaFormatTest.java @@ -81,11 +81,11 @@ void specificVersionSkipJavadocFormatting() throws Exception { @Test void specificVersionMaxLineLength() throws Exception { - assumeTrue(Jvm.version() >= 21, "google-java-format 1.37.0 requires JVM 21+"); + assumeTrue(Jvm.version() >= 21, "google-java-format 1.38.0 requires JVM 21+"); writePomWithJavaSteps( "", - " 1.37.0", + " 1.38.0", " 240", ""); diff --git a/testlib/src/test/java/com/diffplug/spotless/java/GoogleJavaFormatStepTest.java b/testlib/src/test/java/com/diffplug/spotless/java/GoogleJavaFormatStepTest.java index ebd992490b..fb9ed0993c 100644 --- a/testlib/src/test/java/com/diffplug/spotless/java/GoogleJavaFormatStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/java/GoogleJavaFormatStepTest.java @@ -142,20 +142,12 @@ void behaviorWithReorderImports() throws Exception { } } - @Test - @EnabledForJreRange(min = JAVA_21) - void behavior137() throws Exception { - FormatterStep step = GoogleJavaFormatStep.create("1.37.0", TestProvisioner.mavenCentral()); - StepHarness.forStep(step) - .testResource("java/googlejavaformat/JavaCodeUnformatted.test", "java/googlejavaformat/JavaCodeFormatted.test"); - } - @Test @EnabledForJreRange(min = JAVA_21) void behaviorWithMaxLineLength() throws Exception { FormatterStep step = GoogleJavaFormatStep.create( GoogleJavaFormatStep.defaultGroupArtifact(), - "1.37.0", + "1.38.0", GoogleJavaFormatStep.defaultStyle(), TestProvisioner.mavenCentral(), GoogleJavaFormatStep.defaultReflowLongStrings(), @@ -167,10 +159,10 @@ void behaviorWithMaxLineLength() throws Exception { } @Test - void maxLineLengthRequiresVersion1Dot37OrLater() { + void maxLineLengthRequiresVersion1Dot38OrLater() { IllegalArgumentException error = assertThrows(IllegalArgumentException.class, - () -> GoogleJavaFormatStep.create(GoogleJavaFormatStep.defaultGroupArtifact(), "1.36.1", GoogleJavaFormatStep.defaultStyle(), TestProvisioner.mavenCentral(), false, false, true, 120)); - assertTrue(error.getMessage().contains("maxLineLength requires google-java-format 1.37.0 or later"), error.getMessage()); + () -> GoogleJavaFormatStep.create(GoogleJavaFormatStep.defaultGroupArtifact(), "1.37.0", GoogleJavaFormatStep.defaultStyle(), TestProvisioner.mavenCentral(), false, false, true, 120)); + assertTrue(error.getMessage().contains("maxLineLength requires google-java-format 1.38.0 or later"), error.getMessage()); } @Test