Skip to content
Open
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
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ This document is intended for Spotless developers.
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).

## [Unreleased]
### Added
- Add a `cleanup` option to `GoogleJavaFormatStep` to disable google-java-format's non-whitespace cleanups. ([#3157](https://github.com/diffplug/spotless/pull/3157))
### Changes
- Bump default `google-java-format` version `1.36.1` -> `2.0.0`. ([#3157](https://github.com/diffplug/spotless/pull/3157))

## [4.10.4] - 2026-10-08

Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ flexmark-all = "com.vladsch.flexmark:flexmark-all:0.64.8"
freshmark = "com.diffplug.freshmark:freshmark:1.3.1"
cucumber-gherkin = "io.cucumber:gherkin:42.0.1"
gherkin-utils = "io.cucumber:gherkin-utils:12.0.2"
google-java-format = "com.google.googlejavaformat:google-java-format:1.36.1"
google-java-format = "com.google.googlejavaformat:google-java-format:2.0.0"
gson = "com.google.code.gson:gson:2.14.0"
json-simple = "org.json:json:20210307"
ktfmt = "org.jetbrains.kotlinx:ktfmt:0.65"
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -15,6 +15,7 @@
*/
package com.diffplug.spotless.glue.java;

import java.lang.reflect.Method;
import java.util.Objects;

import javax.annotation.Nonnull;
Expand Down Expand Up @@ -46,7 +47,7 @@ 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, boolean cleanup) {
this.version = Objects.requireNonNull(version);
this.formatterStyle = Style.valueOf(Objects.requireNonNull(style));
this.reflowStrings = reflowStrings;
Expand All @@ -56,9 +57,29 @@ public GoogleJavaFormatFormatterFunc(@Nonnull String version, @Nonnull String st
if (!formatJavadoc) {
builder = builder.formatJavadoc(false);
}
if (!cleanup) {
builder = disableCleanup(builder);
}
this.formatter = new Formatter(builder.build());
}

// JavaFormatterOptions.Builder.cleanup(boolean) was added in google-java-format 2.0.0, and this
// glue is compiled against an older version.
private static JavaFormatterOptions.Builder disableCleanup(JavaFormatterOptions.Builder builder) {
Method cleanup;
try {
cleanup = JavaFormatterOptions.Builder.class.getMethod("cleanup", boolean.class);
} catch (NoSuchMethodException e) {
// google-java-format versions before 2.0.0 don't have cleanups
return builder;
}
try {
return (JavaFormatterOptions.Builder) cleanup.invoke(builder, false);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException(e);
}
}

@Override
@Nonnull
public String apply(@Nonnull String input) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ 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;
private static final boolean DEFAULT_CLEANUP = true;
private static final String NAME = "google-java-format";
private static final String MAVEN_COORDINATE = "com.google.googlejavaformat:google-java-format";

Expand All @@ -44,19 +45,22 @@ public final class GoogleJavaFormatStep implements Serializable {
private final boolean reflowLongStrings;
private final boolean reorderImports;
private final boolean formatJavadoc;
private final boolean cleanup;

private GoogleJavaFormatStep(JarState.Promised jarState,
String version,
String style,
boolean reflowLongStrings,
boolean reorderImports,
boolean formatJavadoc) {
boolean formatJavadoc,
boolean cleanup) {
this.jarState = jarState;
this.version = version;
this.style = style;
this.reflowLongStrings = reflowLongStrings;
this.reorderImports = reorderImports;
this.formatJavadoc = formatJavadoc;
this.cleanup = cleanup;
}

/** Creates a step which formats everything - code, import order, and unused imports. */
Expand Down Expand Up @@ -89,14 +93,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_CLEANUP);
}

/**
* Creates a step which formats everything - groupArtifact, code, import order, and unused imports - and optionally reflows long strings.
* If {@code cleanup} is false, google-java-format 2.0.0 and later skip their non-whitespace cleanups (older versions don't have any).
*/
public static FormatterStep create(String groupArtifact, String version, String style, Provisioner provisioner, boolean reflowLongStrings, boolean reorderImports, boolean formatJavadoc, boolean cleanup) {
return createInternally(NAME, groupArtifact, version, style, provisioner, reflowLongStrings, reorderImports, formatJavadoc, cleanup, 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(), defaultCleanup(), 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, boolean cleanup, 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'");
Expand All @@ -105,7 +117,7 @@ private static FormatterStep createInternally(String name, String groupArtifact,
Objects.requireNonNull(style, "style");
Objects.requireNonNull(provisioner, "provisioner");

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, cleanup);
if (removeImports) {
return FormatterStep.create(name,
step,
Expand Down Expand Up @@ -150,8 +162,12 @@ public static boolean defaultFormatJavadoc() {
return DEFAULT_FORMAT_JAVADOC;
}

public static boolean defaultCleanup() {
return DEFAULT_CLEANUP;
}

private State equalityState() {
return new State(version, style, jarState.get(), reflowLongStrings, reorderImports, formatJavadoc);
return new State(version, style, jarState.get(), reflowLongStrings, reorderImports, formatJavadoc, cleanup);
}

private static final class State implements Serializable {
Expand All @@ -164,13 +180,15 @@ private static final class State implements Serializable {
private final boolean reflowLongStrings;
private final boolean reorderImports;
private final boolean formatJavadoc;
private final boolean cleanup;

State(String version,
String style,
JarState jarState,
boolean reflowLongStrings,
boolean reorderImports,
boolean formatJavadoc) {
boolean formatJavadoc,
boolean cleanup) {
JVM_SUPPORT.assertFormatterSupported(version);
ModuleHelper.doOpenInternalPackagesIfRequired();
this.jarState = jarState;
Expand All @@ -179,13 +197,14 @@ private static final class State implements Serializable {
this.reflowLongStrings = reflowLongStrings;
this.reorderImports = reorderImports;
this.formatJavadoc = formatJavadoc;
this.cleanup = cleanup;
}

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, boolean.class);
FormatterFunc googleJavaFormatFormatterFunc = (FormatterFunc) constructor.newInstance(version, style, reflowLongStrings, reorderImports, formatJavadoc, cleanup);

return JVM_SUPPORT.suggestLaterVersionOnError(version, googleJavaFormatFormatterFunc);
}
Expand Down
5 changes: 5 additions & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`).

## [Unreleased]
### Added
- Add `cleanup(false)` to `googleJavaFormat()` to disable google-java-format's non-whitespace cleanups. ([#3157](https://github.com/diffplug/spotless/pull/3157))
### Changes
- Bump default `google-java-format` version `1.36.1` -> `2.0.0`. ([#3157](https://github.com/diffplug/spotless/pull/3157))
This version applies some small non-whitespace cleanups by default (removing redundant semicolons, empty annotation parentheses, and parentheses around single lambda parameters), which can be disabled with `cleanup(false)`.

## [8.10.4] - 2026-10-08

Expand Down
3 changes: 2 additions & 1 deletion plugin-gradle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 disable non-whitespace cleanups (google-java-format >= 2.0.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().formatJavadoc(false).reorderImports(false).cleanup(false).groupArtifact('com.google.googlejavaformat:google-java-format')
```

### palantir-java-format
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ public class GoogleJavaFormatConfig {
boolean reflowLongStrings;
boolean reorderImports;
boolean formatJavadoc = true;
boolean cleanup = true;

GoogleJavaFormatConfig(String version) {
this.version = Objects.requireNonNull(version);
Expand Down Expand Up @@ -261,6 +262,13 @@ public GoogleJavaFormatConfig formatJavadoc(boolean formatJavadoc) {
return this;
}

/** Enables or disables google-java-format's non-whitespace cleanups (google-java-format 2.0.0 and later), which are enabled by default. */
public GoogleJavaFormatConfig cleanup(boolean cleanup) {
this.cleanup = cleanup;
replaceStep(createStep());
return this;
}

private FormatterStep createStep() {
return GoogleJavaFormatStep.create(
groupArtifact,
Expand All @@ -269,7 +277,8 @@ private FormatterStep createStep() {
provisioner(),
reflowLongStrings,
reorderImports,
formatJavadoc);
formatJavadoc,
cleanup);
}
}

Expand Down
5 changes: 5 additions & 0 deletions plugin-maven/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).

## [Unreleased]
### Added
- Add `<cleanup>false</cleanup>` to `<googleJavaFormat>` to disable google-java-format's non-whitespace cleanups. ([#3157](https://github.com/diffplug/spotless/pull/3157))
### Changes
- Bump default `google-java-format` version `1.36.1` -> `2.0.0`. ([#3157](https://github.com/diffplug/spotless/pull/3157))
This version applies some small non-whitespace cleanups by default (removing redundant semicolons, empty annotation parentheses, and parentheses around single lambda parameters), which can be disabled with `<cleanup>false</cleanup>`.

## [3.10.4] - 2026-10-08

Expand Down
1 change: 1 addition & 0 deletions plugin-maven/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ Types in `java.lang` and in the file's own package are shortened without adding
<reflowLongStrings>true</reflowLongStrings> <!-- optional -->
<formatJavadoc>false</formatJavadoc> <!-- optional -->
<reorderImports>false</reorderImports>. <!-- optional -->
<cleanup>false</cleanup> <!-- optional, disables non-whitespace cleanups in google-java-format >= 2.0.0 -->
<!-- optional: custom group artifact (you probably don't need this) -->
<groupArtifact>com.google.googlejavaformat:google-java-format</groupArtifact>
</googleJavaFormat>
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -41,6 +41,9 @@ public class GoogleJavaFormat implements FormatterStepFactory {
@Parameter
private Boolean formatJavadoc;

@Parameter
private Boolean cleanup;

@Override
public FormatterStep newFormatterStep(FormatterStepConfig config) {
String groupArtifact = this.groupArtifact != null ? this.groupArtifact : GoogleJavaFormatStep.defaultGroupArtifact();
Expand All @@ -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);
boolean cleanup = this.cleanup != null ? this.cleanup : GoogleJavaFormatStep.defaultCleanup();
return GoogleJavaFormatStep.create(groupArtifact, version, style, config.getProvisioner(), reflowLongStrings, reorderImports, formatJavadoc, cleanup);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import mylib.UsedA;
import mylib.UsedB;

public class Java {
public static void main(String[] args) {
var a =
"""
Howdy
Partner!
""";
System.out.println(a);
UsedB.someMethod();
UsedA.someMethod();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class GoogleJavaFormatStepTest extends ResourceHarness {
@Test
void jvm13Features() throws Exception {
try (StepHarness step = StepHarness.forStep(GoogleJavaFormatStep.create(TestProvisioner.mavenCentral()))) {
step.testResource("java/googlejavaformat/TextBlock.dirty", "java/googlejavaformat/TextBlock.clean");
// google-java-format 2.0.0 indents text block contents even when long strings aren't reflowed
step.testResource("java/googlejavaformat/TextBlock.dirty", defaultVersionIsAtLeast2() ? "java/googlejavaformat/TextBlockGjf2.clean" : "java/googlejavaformat/TextBlock.clean");
}
}

Expand Down Expand Up @@ -117,6 +118,29 @@ void behaviorWithSkipFormatJavadoc() throws Exception {
}
}

@Test
void behaviorWithCleanup() throws Exception {
String unformatted = "class T {\n @Deprecated() void f() { g((x) -> x);; }\n}\n";
String withoutCleanups = "class T {\n @Deprecated()\n void f() {\n g((x) -> x);\n ;\n }\n}\n";
// google-java-format versions before 2.0.0 don't have cleanups
String withCleanups = defaultVersionIsAtLeast2() ? "class T {\n @Deprecated\n void f() {\n g(x -> x);\n }\n}\n" : withoutCleanups;
try (StepHarness step = StepHarness.forStep(createWithCleanup(true))) {
step.test(unformatted, withCleanups);
}
try (StepHarness step = StepHarness.forStep(createWithCleanup(false))) {
step.test(unformatted, withoutCleanups);
}
}

/** The default version depends on the JVM, e.g. google-java-format 2.0.0 requires Java 21. */
private static boolean defaultVersionIsAtLeast2() {
return !GoogleJavaFormatStep.defaultVersion().startsWith("1.");
}

private static FormatterStep createWithCleanup(boolean cleanup) {
return GoogleJavaFormatStep.create(GoogleJavaFormatStep.defaultGroupArtifact(), GoogleJavaFormatStep.defaultVersion(), GoogleJavaFormatStep.defaultStyle(), TestProvisioner.mavenCentral(), GoogleJavaFormatStep.defaultReflowLongStrings(), GoogleJavaFormatStep.defaultReorderImports(), GoogleJavaFormatStep.defaultFormatJavadoc(), cleanup);
}

@Test
void behaviorWithCustomGroupArtifact() throws Exception {
FormatterStep step = GoogleJavaFormatStep.create(GoogleJavaFormatStep.defaultGroupArtifact(), GoogleJavaFormatStep.defaultVersion(), GoogleJavaFormatStep.defaultStyle(), TestProvisioner.mavenCentral(), false);
Expand Down