diff --git a/CHANGES.md b/CHANGES.md
index 404363e19..7d7c3dc35 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -16,6 +16,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
* Fix Eclipse JDT on some settings files. ([#1864](https://github.com/diffplug/spotless/pull/1864) fixes [#1638](https://github.com/diffplug/spotless/issues/1638))
### Changes
* Bump default `ktlint` version to latest `1.0.0` -> `1.0.1`. ([#1855](https://github.com/diffplug/spotless/pull/1855))
+* Add a Step to remove semicolons from Groovy files. ([#1881](https://github.com/diffplug/spotless/pull/1881))
## [2.42.0] - 2023-09-28
### Added
diff --git a/lib/src/main/java/com/diffplug/spotless/groovy/RemoveSemicolonsStep.java b/lib/src/main/java/com/diffplug/spotless/groovy/RemoveSemicolonsStep.java
new file mode 100644
index 000000000..e24feb5cd
--- /dev/null
+++ b/lib/src/main/java/com/diffplug/spotless/groovy/RemoveSemicolonsStep.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2023 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.groovy;
+
+import java.io.BufferedReader;
+import java.io.Serializable;
+import java.io.StringReader;
+
+import com.diffplug.spotless.FormatterFunc;
+import com.diffplug.spotless.FormatterStep;
+
+/**
+ * Removes all semicolons from the end of lines.
+ *
+ * @author Jose Luis Badano
+ */
+public final class RemoveSemicolonsStep {
+ private static final String NAME = "Remove unnecessary semicolons";
+
+ private RemoveSemicolonsStep() {
+ // do not instantiate
+ }
+
+ public static FormatterStep create() {
+ return FormatterStep.createLazy(NAME,
+ State::new,
+ RemoveSemicolonsStep.State::toFormatter);
+ }
+
+ private static final class State implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ FormatterFunc toFormatter() {
+ return raw -> {
+ try (BufferedReader reader = new BufferedReader(new StringReader(raw))) {
+ StringBuilder result = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ result.append(removeSemicolon(line));
+ result.append(System.lineSeparator());
+ }
+ return result.toString();
+ }
+ };
+ }
+
+ /**
+ * Removes the last semicolon in a line if it exists.
+ *
+ * @param line the line to remove the semicolon from
+ * @return the line without the last semicolon
+ */
+ private String removeSemicolon(String line) {
+ // find last semicolon in a string a remove it
+ int lastSemicolon = line.lastIndexOf(";");
+ if (lastSemicolon != -1 && lastSemicolon == line.length() - 1) {
+ return line.substring(0, lastSemicolon);
+ } else {
+ return line;
+ }
+ }
+ }
+}
diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md
index 32bf1bd7c..03ddf370f 100644
--- a/plugin-gradle/CHANGES.md
+++ b/plugin-gradle/CHANGES.md
@@ -10,6 +10,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
* Check if EditorConfig file exist for Ktlint in KotlinGradleExtension. ([#1889](https://github.com/diffplug/spotless/pull/1889))
### Changes
* Bump default `ktlint` version to latest `1.0.0` -> `1.0.1`. ([#1855](https://github.com/diffplug/spotless/pull/1855))
+* Add a Step to remove semicolons from Groovy files. ([#1881](https://github.com/diffplug/spotless/pull/1881))
### Fixed
* Fix `GoogleJavaFormatConfig.reorderImports` not working. ([#1872](https://github.com/diffplug/spotless/issues/1872))
diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md
index 6232f9c0e..6b707a1ea 100644
--- a/plugin-gradle/README.md
+++ b/plugin-gradle/README.md
@@ -320,6 +320,8 @@ spotless {
// optional: instead of specifying import groups directly you can specify a config file
// export config file: https://github.com/diffplug/spotless/blob/main/ECLIPSE_SCREENSHOTS.md#creating-spotlessimportorder
importOrderFile('eclipse-import-order.txt') // import order file as exported from eclipse
+ // removes semicolons at the end of lines
+ removeSemicolons()
excludeJava() // excludes all Java sources within the Groovy source dirs from formatting
// the Groovy Eclipse formatter extends the Java Eclipse formatter,
diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/BaseGroovyExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/BaseGroovyExtension.java
index 6aa28d185..657ef06a6 100644
--- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/BaseGroovyExtension.java
+++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/BaseGroovyExtension.java
@@ -24,6 +24,7 @@
import com.diffplug.spotless.extra.EquoBasedStepBuilder;
import com.diffplug.spotless.extra.groovy.GrEclipseFormatterStep;
+import com.diffplug.spotless.groovy.RemoveSemicolonsStep;
import com.diffplug.spotless.java.ImportOrderStep;
abstract class BaseGroovyExtension extends FormatExtension {
@@ -40,6 +41,10 @@ public void importOrderFile(Object importOrderFile) {
addStep(ImportOrderStep.forGroovy().createFrom(getProject().file(importOrderFile)));
}
+ public void removeSemicolons() {
+ addStep(RemoveSemicolonsStep.create());
+ }
+
public GrEclipseConfig greclipse() {
return greclipse(GrEclipseFormatterStep.defaultVersion());
}
diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GroovyExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GroovyExtensionTest.java
index c38df40d5..7f87d8793 100644
--- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GroovyExtensionTest.java
+++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GroovyExtensionTest.java
@@ -88,6 +88,27 @@ void excludeJavaWithCustomTarget() throws IOException {
assertThat(error).hasMessageContaining("'excludeJava' is not supported");
}
+ @Test
+ void removeSemicolons() throws IOException {
+ setFile("build.gradle").toLines(
+ "plugins {",
+ " id 'com.diffplug.spotless'",
+ "}",
+ "apply plugin: 'groovy'",
+ "",
+ "spotless {",
+ " groovy {",
+ " removeSemicolons()",
+ " }",
+ "}");
+
+ String withSemicolons = getTestResource("groovy/removeSemicolons/GroovyCodeWithSemicolons.test");
+ String withoutSemicolons = getTestResource("groovy/removeSemicolons/GroovyCodeWithSemicolonsFormatted.test");
+ setFile("src/main/groovy/test.groovy").toContent(withSemicolons);
+ gradleRunner().withArguments("spotlessApply").build();
+ assertFile("src/main/groovy/test.groovy").hasContent(withoutSemicolons);
+ }
+
@Test
void groovyPluginMissingCheck() throws IOException {
setFile("build.gradle").toLines(
diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md
index 512240201..78fb6f84c 100644
--- a/plugin-maven/CHANGES.md
+++ b/plugin-maven/CHANGES.md
@@ -11,6 +11,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
* Fix Eclipse JDT on some settings files. ([#1864](https://github.com/diffplug/spotless/pull/1864) fixes [#1638](https://github.com/diffplug/spotless/issues/1638))
### Changes
* Bump default `ktlint` version to latest `1.0.0` -> `1.0.1`. ([#1855](https://github.com/diffplug/spotless/pull/1855))
+* Add a Step to remove semicolons from Groovy files. ([#1881](https://github.com/diffplug/spotless/pull/1881))
## [2.40.0] - 2023-09-28
### Added
diff --git a/plugin-maven/README.md b/plugin-maven/README.md
index f7849fbdf..5eb523eca 100644
--- a/plugin-maven/README.md
+++ b/plugin-maven/README.md
@@ -336,6 +336,8 @@ These mechanisms already exist for the Gradle plugin.
java|javax,org,com,com.diffplug,,\#com.diffplug,\#
+
+
diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/groovy/Groovy.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/groovy/Groovy.java
index 35b91f367..db4b35dfd 100644
--- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/groovy/Groovy.java
+++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/groovy/Groovy.java
@@ -52,4 +52,8 @@ public void addGreclipse(GrEclipse greclipse) {
public void addImportOrder(ImportOrder importOrder) {
addStepFactory(importOrder);
}
+
+ public void addRemoveSemicolons(RemoveSemicolons removeSemicolons) {
+ addStepFactory(removeSemicolons);
+ }
}
diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/groovy/RemoveSemicolons.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/groovy/RemoveSemicolons.java
new file mode 100644
index 000000000..a96a07869
--- /dev/null
+++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/groovy/RemoveSemicolons.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2016-2023 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.maven.groovy;
+
+import com.diffplug.spotless.FormatterStep;
+import com.diffplug.spotless.groovy.RemoveSemicolonsStep;
+import com.diffplug.spotless.maven.FormatterStepConfig;
+import com.diffplug.spotless.maven.FormatterStepFactory;
+
+public class RemoveSemicolons implements FormatterStepFactory {
+
+ @Override
+ public FormatterStep newFormatterStep(FormatterStepConfig config) {
+ return RemoveSemicolonsStep.create();
+ }
+}
diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/groovy/RemoveSemicolonsTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/groovy/RemoveSemicolonsTest.java
new file mode 100644
index 000000000..fc78c87a2
--- /dev/null
+++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/groovy/RemoveSemicolonsTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 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.maven.groovy;
+
+import org.junit.jupiter.api.Test;
+
+import com.diffplug.spotless.maven.MavenIntegrationHarness;
+
+class RemoveSemicolonsTest extends MavenIntegrationHarness {
+
+ @Test
+ void testRemoveSemicolonsString() throws Exception {
+ writePomWithGroovySteps("");
+ runTest("Hello World;", "Hello World");
+ }
+
+ @Test
+ void testNotRemoveSemicolonsString() throws Exception {
+ writePomWithGroovySteps("");
+ runTest("Hello;World", "Hello;World");
+ }
+
+ @Test
+ void testRemoveSemicolons() throws Exception {
+ writePomWithGroovySteps("");
+
+ String path = "src/main/groovy/test.groovy";
+ setFile(path).toResource("groovy/removeSemicolons/GroovyCodeWithSemicolons.test");
+ mavenRunner().withArguments("spotless:apply").runNoError();
+ assertFile(path).sameAsResource("groovy/removeSemicolons/GroovyCodeWithSemicolonsFormatted.test");
+ }
+
+ private void runTest(String sourceContent, String targetContent) throws Exception {
+ String path = "src/main/groovy/test.groovy";
+ setFile(path).toContent(sourceContent);
+ mavenRunner().withArguments("spotless:apply").runNoError();
+ assertFile(path).hasContent(targetContent);
+ }
+}
diff --git a/testlib/src/main/resources/groovy/removeSemicolons/GroovyCodeWithSemicolons.test b/testlib/src/main/resources/groovy/removeSemicolons/GroovyCodeWithSemicolons.test
new file mode 100644
index 000000000..b66af4e82
--- /dev/null
+++ b/testlib/src/main/resources/groovy/removeSemicolons/GroovyCodeWithSemicolons.test
@@ -0,0 +1,10 @@
+import mylib.Unused;
+import mylib.UsedB;
+import mylib.UsedA;
+
+public class SomeClass {
+System.out.println("hello");
+UsedB.someMethod();
+UsedA.someMethod();
+}
+}
\ No newline at end of file
diff --git a/testlib/src/main/resources/groovy/removeSemicolons/GroovyCodeWithSemicolonsFormatted.test b/testlib/src/main/resources/groovy/removeSemicolons/GroovyCodeWithSemicolonsFormatted.test
new file mode 100644
index 000000000..434addfa6
--- /dev/null
+++ b/testlib/src/main/resources/groovy/removeSemicolons/GroovyCodeWithSemicolonsFormatted.test
@@ -0,0 +1,10 @@
+import mylib.Unused
+import mylib.UsedB
+import mylib.UsedA
+
+public class SomeClass {
+System.out.println("hello")
+UsedB.someMethod()
+UsedA.someMethod()
+}
+}
\ No newline at end of file