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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Commit, tag and push the choco source files to the chocolatey-bucket repository during the release process
- Added formatter [`clean-that`](https://github.com/diffplug/spotless/tree/main/plugin-gradle#cleanthat)
- Added formatter [`remove-unused-imports`](https://github.com/diffplug/spotless/tree/main/plugin-gradle#removeunusedimports)

## [0.1.1] - 2025-06-02

Expand Down
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,15 @@ or apply the formatting to the files.
-V, --version Print version information and exit.

Available formatting steps:
clang-format Runs clang-format
clean-that CleanThat enables automatic refactoring of Java code.
format-annotations Corrects line break formatting of type annotations in
java files.
google-java-format Runs google java format
license-header Runs license header
palantir-java-format Runs palantir java format
prettier Runs prettier, the opinionated code formatter.
clang-format Runs clang-format
clean-that CleanThat enables automatic refactoring of Java code.
format-annotations Corrects line break formatting of type annotations in
java files.
google-java-format Runs google java format
license-header Runs license header
palantir-java-format Runs palantir java format
prettier Runs prettier, the opinionated code formatter.
remove-unused-imports Removes unused imports from Java files.

Possible exit codes:
0 Successful formatting.
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/java/com/diffplug/spotless/cli/SpotlessCLI.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import com.diffplug.spotless.cli.steps.LicenseHeader;
import com.diffplug.spotless.cli.steps.PalantirJavaFormat;
import com.diffplug.spotless.cli.steps.Prettier;
import com.diffplug.spotless.cli.steps.RemoveUnusedImports;
import com.diffplug.spotless.cli.version.SpotlessCLIVersionProvider;

import picocli.CommandLine;
Expand Down Expand Up @@ -100,7 +101,8 @@
GoogleJavaFormat.class,
LicenseHeader.class,
PalantirJavaFormat.class,
Prettier.class
Prettier.class,
RemoveUnusedImports.class
})
public class SpotlessCLI implements SpotlessAction, SpotlessCommand, SpotlessActionContextProvider {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2025 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.spotless.cli.steps;

import java.util.Collections;
import java.util.List;

import org.jetbrains.annotations.NotNull;

import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.cli.core.SpotlessActionContext;
import com.diffplug.spotless.cli.help.AdditionalInfoLinks;
import com.diffplug.spotless.cli.help.OptionConstants;
import com.diffplug.spotless.cli.help.SupportedFileTypes;
import com.diffplug.spotless.java.RemoveUnusedImportsStep;

import picocli.CommandLine;

@CommandLine.Command(name = "remove-unused-imports", description = "Removes unused imports from Java files.")
@SupportedFileTypes("Java")
@AdditionalInfoLinks("https://github.com/diffplug/spotless/tree/main/plugin-gradle#removeunusedimports")
public class RemoveUnusedImports extends SpotlessFormatterStep {

@CommandLine.Option(
names = {"--engine", "-e"},
defaultValue = "GOOGLE_JAVA_FORMAT",
description = "The backing engine to use for detecting and removing unused imports."
+ OptionConstants.VALID_AND_DEFAULT_VALUES_SUFFIX)
Engine engine;

public enum Engine {
GOOGLE_JAVA_FORMAT {
@Override
String formatterName() {
return RemoveUnusedImportsStep.defaultFormatter();
}
},
CLEAN_THAT {
@Override
String formatterName() {
return "cleanthat-javaparser-unnecessaryimport";
}
};

abstract String formatterName();
}

@Override
public @NotNull List<FormatterStep> prepareFormatterSteps(SpotlessActionContext context) {
return Collections.singletonList(RemoveUnusedImportsStep.create(engine.formatterName(), context.provisioner()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2025 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.spotless.cli.steps;

import org.junit.jupiter.api.Test;

import com.diffplug.spotless.cli.CLIIntegrationHarness;
import com.diffplug.spotless.tag.CliNativeTest;
import com.diffplug.spotless.tag.CliProcessTest;

import static org.junit.jupiter.api.Assertions.*;

@CliProcessTest
@CliNativeTest
class RemoveUnusedImportsTest extends CLIIntegrationHarness {

@Test
void itRemovesUnusedImportsWithDefaultEngine() {
setFile("Java.java").toResource("java/removeunusedimports/JavaCodeWithLicensePackageUnformatted.test");

cliRunner().withTargets("Java.java").withStep(RemoveUnusedImports.class).run();

assertFile("Java.java")
.notSameSasResource("java/removeunusedimports/JavaCodeWithLicensePackageUnformatted.test")
.hasNotContent("Unused");

selfie().expectResource("Java.java").toMatchDisk();
}

@Test
void itRemovesWithExplicitDefaultEngine() {
setFile("Java.java").toResource("java/removeunusedimports/JavaCodeWithLicensePackageUnformatted.test");

cliRunner()
.withTargets("Java.java")
.withStep(RemoveUnusedImports.class)
.withOption("--engine", "GOOGLE_JAVA_FORMAT")
.run();

assertFile("Java.java")
.notSameSasResource("java/removeunusedimports/JavaCodeWithLicensePackageUnformatted.test")
.hasNotContent("Unused");

selfie().expectResource("Java.java").toMatchDisk();
}

@Test
void itRemovesWithExplicitCleanThatEngine() {
setFile("Java.java").toResource("java/removeunusedimports/JavaCodeWithLicensePackageUnformatted.test");

cliRunner()
.withTargets("Java.java")
.withStep(RemoveUnusedImports.class)
.withOption("--engine", "CLEAN_THAT")
.run();

assertFile("Java.java")
.notSameSasResource("java/removeunusedimports/JavaCodeWithLicensePackageUnformatted.test")
.hasNotContent("Unused");

selfie().expectResource("Java.java").toMatchDisk();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
╔═ itRemovesUnusedImportsWithDefaultEngine ═╗
/*
* Some license stuff.
* Very official.
*/
package hello.world;

import mylib.UsedB;
import mylib.UsedA;

public class Java {
public static void main(String[] args) {
System.out.println("hello");
UsedB.someMethod();
UsedA.someMethod();
}
}
╔═ itRemovesWithExplicitCleanThatEngine ═╗
/*
* Some license stuff.
* Very official.
*/
package hello.world;

import mylib.UsedB;
import mylib.UsedA;

public class Java {
public static void main(String[] args) {
System.out.println("hello");
UsedB.someMethod();
UsedA.someMethod();
}
}
╔═ itRemovesWithExplicitDefaultEngine ═╗
/*
* Some license stuff.
* Very official.
*/
package hello.world;

import mylib.UsedB;
import mylib.UsedA;

public class Java {
public static void main(String[] args) {
System.out.println("hello");
UsedB.someMethod();
UsedA.someMethod();
}
}
╔═ [end of file] ═╗
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ enum DocumentedUsages {
GOOGLE_JAVA_FORMAT(),
LICENSE_HEADER(),
PALANTIR_JAVA_FORMAT(),
PRETTIER()
PRETTIER(),
REMOVE_UNUSED_IMPORTS(),

private final String fileName

Expand Down
37 changes: 20 additions & 17 deletions testlib/src/main/java/com/diffplug/spotless/ResourceHarness.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,47 +201,50 @@ private ReadAsserter(File file) {
this.file = file;
}

public void hasContent(String expected) {
hasContent(expected, StandardCharsets.UTF_8);
public ReadAsserter hasContent(String expected) {
return hasContent(expected, StandardCharsets.UTF_8);
}

public void hasNotContent(String notExpected) {
notHasContent(notExpected, StandardCharsets.UTF_8);
public ReadAsserter hasNotContent(String notExpected) {
return notHasContent(notExpected, StandardCharsets.UTF_8);
}

public void hasContent(String expected, Charset charset) {
public ReadAsserter hasContent(String expected, Charset charset) {
assertThat(file).usingCharset(charset).hasContent(expected);
return this;
}

public void notHasContent(String notExpected, Charset charset) {
public ReadAsserter notHasContent(String notExpected, Charset charset) {
assertThat(file).usingCharset(charset).content().isNotEqualTo(notExpected);
return this;
}

public void hasLines(String... lines) {
hasContent(String.join("\n", Arrays.asList(lines)));
public ReadAsserter hasLines(String... lines) {
return hasContent(String.join("\n", Arrays.asList(lines)));
}

public void sameAsResource(String resource) {
hasContent(getTestResource(resource));
public ReadAsserter sameAsResource(String resource) {
return hasContent(getTestResource(resource));
}

public void notSameSasResource(String resource) {
hasNotContent(getTestResource(resource));
public ReadAsserter notSameSasResource(String resource) {
return hasNotContent(getTestResource(resource));
}

public void matches(Consumer<AbstractCharSequenceAssert<?, String>> conditions) throws IOException {
public ReadAsserter matches(Consumer<AbstractCharSequenceAssert<?, String>> conditions) throws IOException {
String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
conditions.accept(assertThat(content));
return this;
}

public void sameAsFile(File otherFile) throws IOException {
public ReadAsserter sameAsFile(File otherFile) throws IOException {
String otherFileContent = Files.readString(otherFile.toPath());
hasContent(otherFileContent, StandardCharsets.UTF_8);
return hasContent(otherFileContent, StandardCharsets.UTF_8);
}

public void notSameAsFile(File otherFile) throws IOException {
public ReadAsserter notSameAsFile(File otherFile) throws IOException {
String otherFileContent = Files.readString(otherFile.toPath());
notHasContent(otherFileContent, StandardCharsets.UTF_8);
return notHasContent(otherFileContent, StandardCharsets.UTF_8);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Some license stuff.
* Very official.
*/
package hello.world;

import mylib.Unused;
import mylib.UsedB;
import mylib.UsedA;

public class Java {
public static void main(String[] args) {
System.out.println("hello");
UsedB.someMethod();
UsedA.someMethod();
}
}
Loading