Skip to content

Commit 21be091

Browse files
authored
feat: add remove unused imports (#6)
2 parents e8d321d + 84d0b2b commit 21be091

File tree

9 files changed

+245
-27
lines changed

9 files changed

+245
-27
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

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

1920
## [0.1.1] - 2025-06-02
2021

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,15 @@ or apply the formatting to the files.
152152
-V, --version Print version information and exit.
153153
154154
Available formatting steps:
155-
clang-format Runs clang-format
156-
clean-that CleanThat enables automatic refactoring of Java code.
157-
format-annotations Corrects line break formatting of type annotations in
158-
java files.
159-
google-java-format Runs google java format
160-
license-header Runs license header
161-
palantir-java-format Runs palantir java format
162-
prettier Runs prettier, the opinionated code formatter.
155+
clang-format Runs clang-format
156+
clean-that CleanThat enables automatic refactoring of Java code.
157+
format-annotations Corrects line break formatting of type annotations in
158+
java files.
159+
google-java-format Runs google java format
160+
license-header Runs license header
161+
palantir-java-format Runs palantir java format
162+
prettier Runs prettier, the opinionated code formatter.
163+
remove-unused-imports Removes unused imports from Java files.
163164
164165
Possible exit codes:
165166
0 Successful formatting.

app/src/main/java/com/diffplug/spotless/cli/SpotlessCLI.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import com.diffplug.spotless.cli.steps.LicenseHeader;
5151
import com.diffplug.spotless.cli.steps.PalantirJavaFormat;
5252
import com.diffplug.spotless.cli.steps.Prettier;
53+
import com.diffplug.spotless.cli.steps.RemoveUnusedImports;
5354
import com.diffplug.spotless.cli.version.SpotlessCLIVersionProvider;
5455

5556
import picocli.CommandLine;
@@ -100,7 +101,8 @@
100101
GoogleJavaFormat.class,
101102
LicenseHeader.class,
102103
PalantirJavaFormat.class,
103-
Prettier.class
104+
Prettier.class,
105+
RemoveUnusedImports.class
104106
})
105107
public class SpotlessCLI implements SpotlessAction, SpotlessCommand, SpotlessActionContextProvider {
106108

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2025 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.cli.steps;
17+
18+
import java.util.Collections;
19+
import java.util.List;
20+
21+
import org.jetbrains.annotations.NotNull;
22+
23+
import com.diffplug.spotless.FormatterStep;
24+
import com.diffplug.spotless.cli.core.SpotlessActionContext;
25+
import com.diffplug.spotless.cli.help.AdditionalInfoLinks;
26+
import com.diffplug.spotless.cli.help.OptionConstants;
27+
import com.diffplug.spotless.cli.help.SupportedFileTypes;
28+
import com.diffplug.spotless.java.RemoveUnusedImportsStep;
29+
30+
import picocli.CommandLine;
31+
32+
@CommandLine.Command(name = "remove-unused-imports", description = "Removes unused imports from Java files.")
33+
@SupportedFileTypes("Java")
34+
@AdditionalInfoLinks("https://github.com/diffplug/spotless/tree/main/plugin-gradle#removeunusedimports")
35+
public class RemoveUnusedImports extends SpotlessFormatterStep {
36+
37+
@CommandLine.Option(
38+
names = {"--engine", "-e"},
39+
defaultValue = "GOOGLE_JAVA_FORMAT",
40+
description = "The backing engine to use for detecting and removing unused imports."
41+
+ OptionConstants.VALID_AND_DEFAULT_VALUES_SUFFIX)
42+
Engine engine;
43+
44+
public enum Engine {
45+
GOOGLE_JAVA_FORMAT {
46+
@Override
47+
String formatterName() {
48+
return RemoveUnusedImportsStep.defaultFormatter();
49+
}
50+
},
51+
CLEAN_THAT {
52+
@Override
53+
String formatterName() {
54+
return "cleanthat-javaparser-unnecessaryimport";
55+
}
56+
};
57+
58+
abstract String formatterName();
59+
}
60+
61+
@Override
62+
public @NotNull List<FormatterStep> prepareFormatterSteps(SpotlessActionContext context) {
63+
return Collections.singletonList(RemoveUnusedImportsStep.create(engine.formatterName(), context.provisioner()));
64+
}
65+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2025 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.cli.steps;
17+
18+
import org.junit.jupiter.api.Test;
19+
20+
import com.diffplug.spotless.cli.CLIIntegrationHarness;
21+
import com.diffplug.spotless.tag.CliNativeTest;
22+
import com.diffplug.spotless.tag.CliProcessTest;
23+
24+
import static org.junit.jupiter.api.Assertions.*;
25+
26+
@CliProcessTest
27+
@CliNativeTest
28+
class RemoveUnusedImportsTest extends CLIIntegrationHarness {
29+
30+
@Test
31+
void itRemovesUnusedImportsWithDefaultEngine() {
32+
setFile("Java.java").toResource("java/removeunusedimports/JavaCodeWithLicensePackageUnformatted.test");
33+
34+
cliRunner().withTargets("Java.java").withStep(RemoveUnusedImports.class).run();
35+
36+
assertFile("Java.java")
37+
.notSameSasResource("java/removeunusedimports/JavaCodeWithLicensePackageUnformatted.test")
38+
.hasNotContent("Unused");
39+
40+
selfie().expectResource("Java.java").toMatchDisk();
41+
}
42+
43+
@Test
44+
void itRemovesWithExplicitDefaultEngine() {
45+
setFile("Java.java").toResource("java/removeunusedimports/JavaCodeWithLicensePackageUnformatted.test");
46+
47+
cliRunner()
48+
.withTargets("Java.java")
49+
.withStep(RemoveUnusedImports.class)
50+
.withOption("--engine", "GOOGLE_JAVA_FORMAT")
51+
.run();
52+
53+
assertFile("Java.java")
54+
.notSameSasResource("java/removeunusedimports/JavaCodeWithLicensePackageUnformatted.test")
55+
.hasNotContent("Unused");
56+
57+
selfie().expectResource("Java.java").toMatchDisk();
58+
}
59+
60+
@Test
61+
void itRemovesWithExplicitCleanThatEngine() {
62+
setFile("Java.java").toResource("java/removeunusedimports/JavaCodeWithLicensePackageUnformatted.test");
63+
64+
cliRunner()
65+
.withTargets("Java.java")
66+
.withStep(RemoveUnusedImports.class)
67+
.withOption("--engine", "CLEAN_THAT")
68+
.run();
69+
70+
assertFile("Java.java")
71+
.notSameSasResource("java/removeunusedimports/JavaCodeWithLicensePackageUnformatted.test")
72+
.hasNotContent("Unused");
73+
74+
selfie().expectResource("Java.java").toMatchDisk();
75+
}
76+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
╔═ itRemovesUnusedImportsWithDefaultEngine ═╗
2+
/*
3+
* Some license stuff.
4+
* Very official.
5+
*/
6+
package hello.world;
7+
8+
import mylib.UsedB;
9+
import mylib.UsedA;
10+
11+
public class Java {
12+
public static void main(String[] args) {
13+
System.out.println("hello");
14+
UsedB.someMethod();
15+
UsedA.someMethod();
16+
}
17+
}
18+
╔═ itRemovesWithExplicitCleanThatEngine ═╗
19+
/*
20+
* Some license stuff.
21+
* Very official.
22+
*/
23+
package hello.world;
24+
25+
import mylib.UsedB;
26+
import mylib.UsedA;
27+
28+
public class Java {
29+
public static void main(String[] args) {
30+
System.out.println("hello");
31+
UsedB.someMethod();
32+
UsedA.someMethod();
33+
}
34+
}
35+
╔═ itRemovesWithExplicitDefaultEngine ═╗
36+
/*
37+
* Some license stuff.
38+
* Very official.
39+
*/
40+
package hello.world;
41+
42+
import mylib.UsedB;
43+
import mylib.UsedA;
44+
45+
public class Java {
46+
public static void main(String[] args) {
47+
System.out.println("hello");
48+
UsedB.someMethod();
49+
UsedA.someMethod();
50+
}
51+
}
52+
╔═ [end of file] ═╗

build-logic/src/main/groovy/com/diffplug/spotless/cli/picocli/usage/DocumentedUsages.groovy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ enum DocumentedUsages {
99
GOOGLE_JAVA_FORMAT(),
1010
LICENSE_HEADER(),
1111
PALANTIR_JAVA_FORMAT(),
12-
PRETTIER()
12+
PRETTIER(),
13+
REMOVE_UNUSED_IMPORTS(),
1314

1415
private final String fileName
1516

testlib/src/main/java/com/diffplug/spotless/ResourceHarness.java

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -201,47 +201,50 @@ private ReadAsserter(File file) {
201201
this.file = file;
202202
}
203203

204-
public void hasContent(String expected) {
205-
hasContent(expected, StandardCharsets.UTF_8);
204+
public ReadAsserter hasContent(String expected) {
205+
return hasContent(expected, StandardCharsets.UTF_8);
206206
}
207207

208-
public void hasNotContent(String notExpected) {
209-
notHasContent(notExpected, StandardCharsets.UTF_8);
208+
public ReadAsserter hasNotContent(String notExpected) {
209+
return notHasContent(notExpected, StandardCharsets.UTF_8);
210210
}
211211

212-
public void hasContent(String expected, Charset charset) {
212+
public ReadAsserter hasContent(String expected, Charset charset) {
213213
assertThat(file).usingCharset(charset).hasContent(expected);
214+
return this;
214215
}
215216

216-
public void notHasContent(String notExpected, Charset charset) {
217+
public ReadAsserter notHasContent(String notExpected, Charset charset) {
217218
assertThat(file).usingCharset(charset).content().isNotEqualTo(notExpected);
219+
return this;
218220
}
219221

220-
public void hasLines(String... lines) {
221-
hasContent(String.join("\n", Arrays.asList(lines)));
222+
public ReadAsserter hasLines(String... lines) {
223+
return hasContent(String.join("\n", Arrays.asList(lines)));
222224
}
223225

224-
public void sameAsResource(String resource) {
225-
hasContent(getTestResource(resource));
226+
public ReadAsserter sameAsResource(String resource) {
227+
return hasContent(getTestResource(resource));
226228
}
227229

228-
public void notSameSasResource(String resource) {
229-
hasNotContent(getTestResource(resource));
230+
public ReadAsserter notSameSasResource(String resource) {
231+
return hasNotContent(getTestResource(resource));
230232
}
231233

232-
public void matches(Consumer<AbstractCharSequenceAssert<?, String>> conditions) throws IOException {
234+
public ReadAsserter matches(Consumer<AbstractCharSequenceAssert<?, String>> conditions) throws IOException {
233235
String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
234236
conditions.accept(assertThat(content));
237+
return this;
235238
}
236239

237-
public void sameAsFile(File otherFile) throws IOException {
240+
public ReadAsserter sameAsFile(File otherFile) throws IOException {
238241
String otherFileContent = Files.readString(otherFile.toPath());
239-
hasContent(otherFileContent, StandardCharsets.UTF_8);
242+
return hasContent(otherFileContent, StandardCharsets.UTF_8);
240243
}
241244

242-
public void notSameAsFile(File otherFile) throws IOException {
245+
public ReadAsserter notSameAsFile(File otherFile) throws IOException {
243246
String otherFileContent = Files.readString(otherFile.toPath());
244-
notHasContent(otherFileContent, StandardCharsets.UTF_8);
247+
return notHasContent(otherFileContent, StandardCharsets.UTF_8);
245248
}
246249
}
247250

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Some license stuff.
3+
* Very official.
4+
*/
5+
package hello.world;
6+
7+
import mylib.Unused;
8+
import mylib.UsedB;
9+
import mylib.UsedA;
10+
11+
public class Java {
12+
public static void main(String[] args) {
13+
System.out.println("hello");
14+
UsedB.someMethod();
15+
UsedA.someMethod();
16+
}
17+
}

0 commit comments

Comments
 (0)