diff --git a/CHANGES.md b/CHANGES.md index 93e693ebf..a8ae11c80 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,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 Step to remove semicolons from Groovy code. ([#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..cfe46a139 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/groovy/RemoveSemicolonsStep.java @@ -0,0 +1,77 @@ +/* + * 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 RemoveSemicolonsStep() { + // prevent instantiation + } + + static final String NAME = "Remove unnecessary semicolons"; + + 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/src/main/java/com/diffplug/gradle/spotless/GroovyExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GroovyExtension.java index a7f3d98ce..29cf5fb05 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GroovyExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GroovyExtension.java @@ -33,6 +33,7 @@ import com.diffplug.spotless.extra.EquoBasedStepBuilder; import com.diffplug.spotless.extra.groovy.GrEclipseFormatterStep; import com.diffplug.spotless.generic.LicenseHeaderStep; +import com.diffplug.spotless.groovy.RemoveSemicolonsStep; import com.diffplug.spotless.java.ImportOrderStep; public class GroovyExtension extends FormatExtension implements HasBuiltinDelimiterForLicense, JvmLang { @@ -69,6 +70,10 @@ public void importOrder(String... importOrder) { addStep(ImportOrderStep.forGroovy().createFrom(importOrder)); } + public void removeSemicolons() { + addStep(RemoveSemicolonsStep.create()); + } + public void importOrderFile(Object importOrderFile) { Objects.requireNonNull(importOrderFile); addStep(ImportOrderStep.forGroovy().createFrom(getProject().file(importOrderFile))); diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GroovyGradleExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GroovyGradleExtension.java index 30cb75a2a..c6520ba8e 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GroovyGradleExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GroovyGradleExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 DiffPlug + * 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. @@ -20,6 +20,7 @@ import javax.inject.Inject; import com.diffplug.spotless.extra.groovy.GrEclipseFormatterStep; +import com.diffplug.spotless.groovy.RemoveSemicolonsStep; import com.diffplug.spotless.java.ImportOrderStep; public class GroovyGradleExtension extends FormatExtension { @@ -40,6 +41,10 @@ public void importOrderFile(Object importOrderFile) { addStep(ImportOrderStep.forGroovy().createFrom(getProject().file(importOrderFile))); } + public void removeSemicolons(String... arguments) { + addStep(RemoveSemicolonsStep.create()); + } + public GroovyExtension.GrEclipseConfig greclipse() { return new GroovyExtension.GrEclipseConfig(GrEclipseFormatterStep.defaultVersion(), this); } 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..d7a24b424 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,31 @@ 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/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..af3846e1d --- /dev/null +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/groovy/RemoveSemicolonsTest.java @@ -0,0 +1,53 @@ +/* + * 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