diff --git a/CHANGES.md b/CHANGES.md index e99fecee87..733abfd3d9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,8 +10,11 @@ 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 `groupArtifact` option to `PalantirJavaFormatStep`. ([#3179](https://github.com/diffplug/spotless/pull/3179)) ### Fixed - GrEclipse no longer leaves a `file%3A...` directory in the working directory. ([#2019](https://github.com/diffplug/spotless/issues/2019)) +- DBeaver SQL formatter no longer prints an `IndexOutOfBoundsException` when a file starts with a keyword like `SET`. ([#1552](https://github.com/diffplug/spotless/issues/1552)) - `ExpandWildcardImportsStep` no longer fails with `Symbol resolution not configured` on records and other syntax newer than Java 11, and leaves files JavaParser cannot validate unchanged. ([#2946](https://github.com/diffplug/spotless/issues/2946)) - `removeSemicolons()` no longer removes semicolons inside dollar-slashy strings (`$/ ... /$`), after escaped triple quotes in multiline strings, or in multi-line `for (init; cond; update)` headers. ([#2780](https://github.com/diffplug/spotless/issues/2780)) - `RemoveUnusedImportsStep` and `GoogleJavaFormatStep` no longer remove imports that are only used by links in Markdown doc comments (`///`, JDK 23+). ([#3161](https://github.com/diffplug/spotless/issues/3161)) diff --git a/lib/src/main/java/com/diffplug/spotless/java/PalantirJavaFormatStep.java b/lib/src/main/java/com/diffplug/spotless/java/PalantirJavaFormatStep.java index 5840cd4073..cb658bbcb2 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/PalantirJavaFormatStep.java +++ b/lib/src/main/java/com/diffplug/spotless/java/PalantirJavaFormatStep.java @@ -33,7 +33,8 @@ public final class PalantirJavaFormatStep implements Serializable { private static final boolean DEFAULT_FORMAT_JAVADOC = false; private static final String DEFAULT_STYLE = "PALANTIR"; private static final String NAME = "palantir-java-format"; - public static final String MAVEN_COORDINATE = "com.palantir.javaformat:palantir-java-format:"; + private static final String DEFAULT_GROUP_ARTIFACT = "com.palantir.javaformat:palantir-java-format"; + public static final String MAVEN_COORDINATE = DEFAULT_GROUP_ARTIFACT + ":"; public static final String DEFAULT_VERSION = BuildConfig.VERSION_PALANTIR_JAVA_FORMAT; // compatible with Java 11+ /** The jar that contains the formatter. */ @@ -77,16 +78,33 @@ public static FormatterStep create(String version, String style, Provisioner pro * format style. */ public static FormatterStep create(String version, String style, boolean formatJavadoc, Provisioner provisioner) { + return create(defaultGroupArtifact(), version, style, formatJavadoc, provisioner); + } + + /** + * Creates a step which formats everything - groupArtifact, code, import order, unused imports, and Java docs. And with the given + * format style. + */ + public static FormatterStep create(String groupArtifact, String version, String style, boolean formatJavadoc, Provisioner provisioner) { + Objects.requireNonNull(groupArtifact, "groupArtifact"); + if (groupArtifact.chars().filter(ch -> ch == ':').count() != 1) { + throw new IllegalArgumentException("groupArtifact must be in the form 'groupId:artifactId'"); + } Objects.requireNonNull(version, "version"); Objects.requireNonNull(style, "style"); Objects.requireNonNull(provisioner, "provisioner"); return FormatterStep.create(NAME, - new PalantirJavaFormatStep(JarState.promise(() -> JarState.from(MAVEN_COORDINATE + version, provisioner)), version, style, formatJavadoc), + new PalantirJavaFormatStep(JarState.promise(() -> JarState.from(groupArtifact + ":" + version, provisioner)), version, style, formatJavadoc), PalantirJavaFormatStep::equalityState, State::createFormat); } + /** Get default formatter group and artifact */ + public static String defaultGroupArtifact() { + return DEFAULT_GROUP_ARTIFACT; + } + /** Get default formatter version */ public static String defaultVersion() { return DEFAULT_VERSION; diff --git a/lib/src/main/java/com/diffplug/spotless/sql/dbeaver/SQLTokenizedFormatter.java b/lib/src/main/java/com/diffplug/spotless/sql/dbeaver/SQLTokenizedFormatter.java index bad5d4a64a..af38b1631b 100644 --- a/lib/src/main/java/com/diffplug/spotless/sql/dbeaver/SQLTokenizedFormatter.java +++ b/lib/src/main/java/com/diffplug/spotless/sql/dbeaver/SQLTokenizedFormatter.java @@ -431,6 +431,10 @@ && isCommentLine(SQL_DIALECT, token.getString()) boolean isDelimiter = statementDelimiters.contains(token.getString().toUpperCase(Locale.ENGLISH)); if (!isDelimiter) { + if (argIndex == 0) { + // nothing precedes the first token, so there is no line to break + return 0; + } token = argList.get(argIndex - 1); if (token.getType() == TokenType.SPACE) { token.setString(s.toString()); diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 5549a0da75..27c78dde65 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -4,11 +4,13 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Added +- Add `groupArtifact(String)` to `palantirJavaFormat()`. ([#3179](https://github.com/diffplug/spotless/pull/3179)) - `SpotlessApply` writes a report of the files it rewrote to `build/spotless/-changed-files.txt`, configurable via `changedFilesReport`. The report is a task output, so tasks which take it as an input automatically depend on the apply task. ([#2715](https://github.com/diffplug/spotless/issues/2715)) ### Changes - `SpotlessApply` now only reports that it did work (and is otherwise `UP-TO-DATE`) when it actually rewrote a file. ([#2715](https://github.com/diffplug/spotless/issues/2715)) ### Fixed - GrEclipse no longer leaves a `file%3A...` directory in the working directory. ([#2019](https://github.com/diffplug/spotless/issues/2019)) +- DBeaver SQL formatter no longer prints an `IndexOutOfBoundsException` when a file starts with a keyword like `SET`. ([#1552](https://github.com/diffplug/spotless/issues/1552)) - `expandWildcardImports()` no longer fails with `Symbol resolution not configured` on records and other syntax newer than Java 11, and leaves files JavaParser cannot validate unchanged. ([#2946](https://github.com/diffplug/spotless/issues/2946)) - String `targetExclude` patterns ending in `/**` (e.g. `**/build/**`) are now applied as exclude patterns on String `target`s, so excluded directories are skipped while walking the target instead of walking the whole project directory, which made configuration cache store and reuse scale with the size of the project. ([#3147](https://github.com/diffplug/spotless/issues/3147)) - `removeSemicolons()` no longer removes semicolons inside dollar-slashy strings (`$/ ... /$`), after escaped triple quotes in multiline strings, or in multi-line `for (init; cond; update)` headers. ([#2780](https://github.com/diffplug/spotless/issues/2780)) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index a38dc6fc92..e762f1d5c5 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -397,6 +397,8 @@ spotless { palantirJavaFormat('2.9.0').style("GOOGLE") // optional: you can also format Javadocs, requires at least Palantir 2.39.0 palantirJavaFormat('2.39.0').formatJavadoc(true) + // optional: you can also use custom group artifact (you probably don't need this) + palantirJavaFormat('2.39.0').groupArtifact('com.palantir.javaformat:palantir-java-format') ``` ### prince-of-space diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java index f065c14f1b..ed7fa4151f 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java @@ -312,15 +312,23 @@ public PalantirJavaFormatConfig palantirJavaFormat(String version) { public class PalantirJavaFormatConfig { final String version; + String groupArtifact; String style; boolean formatJavadoc; PalantirJavaFormatConfig(String version) { this.version = Objects.requireNonNull(version); + this.groupArtifact = PalantirJavaFormatStep.defaultGroupArtifact(); this.style = PalantirJavaFormatStep.defaultStyle(); addStep(createStep()); } + public PalantirJavaFormatConfig groupArtifact(String groupArtifact) { + this.groupArtifact = Objects.requireNonNull(groupArtifact); + replaceStep(createStep()); + return this; + } + public PalantirJavaFormatConfig style(String style) { this.style = Objects.requireNonNull(style); replaceStep(createStep()); @@ -334,7 +342,7 @@ public PalantirJavaFormatConfig formatJavadoc(boolean formatJavadoc) { } private FormatterStep createStep() { - return PalantirJavaFormatStep.create(version, style, formatJavadoc, provisioner()); + return PalantirJavaFormatStep.create(groupArtifact, version, style, formatJavadoc, provisioner()); } } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PalantirJavaFormatIntegrationTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PalantirJavaFormatIntegrationTest.java index 02538ff763..ba582e9fab 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PalantirJavaFormatIntegrationTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PalantirJavaFormatIntegrationTest.java @@ -15,9 +15,12 @@ */ package com.diffplug.gradle.spotless; +import static org.junit.jupiter.api.condition.JRE.JAVA_21; + import java.io.IOException; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; class PalantirJavaFormatIntegrationTest extends GradleIntegrationHarness { @Test @@ -67,4 +70,27 @@ void formatJavaDoc() throws IOException { checkRunsThenUpToDate(); } + + @Test + @EnabledForJreRange(min = JAVA_21) + void customGroupArtifact() throws IOException { + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "", + "spotless {", + " java {", + " target file('test.java')", + " palantirJavaFormat('21.0.2').groupArtifact('dev.openjavaformat:open-java-format')", + " }", + "}"); + + setFile("test.java").toResource("java/palantirjavaformat/JavaCodeUnformatted.test"); + gradleRunner().withArguments("spotlessApply").build(); + assertFile("test.java").sameAsResource("java/palantirjavaformat/JavaCodeFormatted.test"); + + checkRunsThenUpToDate(); + } } diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 9e50104b9e..7120684723 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -4,10 +4,12 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Added +- Add a `` parameter to ``. ([#3179](https://github.com/diffplug/spotless/pull/3179)) - Add a `spotless:report` goal that puts a Spotless page in the Maven site. ([#3175](https://github.com/diffplug/spotless/pull/3175)) List `spotless-maven-plugin` under `` and run `mvn site`: the page shows which files need formatting, with a colored diff for each, and their lint problems. It works with `maven-site-plugin` 3.12.1 and 3.20.0 or later. ### Fixed - GrEclipse no longer leaves a `file%3A...` directory in the working directory. ([#2019](https://github.com/diffplug/spotless/issues/2019)) +- DBeaver SQL formatter no longer prints an `IndexOutOfBoundsException` when a file starts with a keyword like `SET`. ([#1552](https://github.com/diffplug/spotless/issues/1552)) - `` no longer fails with `Symbol resolution not configured` on records and other syntax newer than Java 11, and leaves files JavaParser cannot validate unchanged. ([#2946](https://github.com/diffplug/spotless/issues/2946)) - `` no longer removes semicolons inside dollar-slashy strings (`$/ ... /$`), after escaped triple quotes in multiline strings, or in multi-line `for (init; cond; update)` headers. ([#2780](https://github.com/diffplug/spotless/issues/2780)) - `` and `` no longer remove imports that are only used by links in Markdown doc comments (`///`, JDK 23+). ([#3161](https://github.com/diffplug/spotless/issues/3161)) diff --git a/plugin-maven/README.md b/plugin-maven/README.md index 0065c0cfe6..cb94daa3de 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -364,6 +364,8 @@ Note: when enabling `AOSP` style, `reorderImports` needs to be enabled to apply 2.39.0 false + + com.palantir.javaformat:palantir-java-format ``` diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/PalantirJavaFormat.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/PalantirJavaFormat.java index 674238d2e8..49bf13b893 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/PalantirJavaFormat.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/PalantirJavaFormat.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 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. @@ -23,6 +23,8 @@ import com.diffplug.spotless.maven.FormatterStepFactory; public class PalantirJavaFormat implements FormatterStepFactory { + @Parameter + private String groupArtifact; @Parameter private String version; @@ -35,9 +37,10 @@ public class PalantirJavaFormat implements FormatterStepFactory { @Override public FormatterStep newFormatterStep(FormatterStepConfig config) { + String groupArtifact = this.groupArtifact != null ? this.groupArtifact : PalantirJavaFormatStep.defaultGroupArtifact(); String version = this.version != null ? this.version : PalantirJavaFormatStep.defaultVersion(); String style = this.style != null ? this.style : PalantirJavaFormatStep.defaultStyle(); boolean formatJavadoc = this.formatJavadoc != null ? this.formatJavadoc : PalantirJavaFormatStep.defaultFormatJavadoc(); - return PalantirJavaFormatStep.create(version, style, formatJavadoc, config.getProvisioner()); + return PalantirJavaFormatStep.create(groupArtifact, version, style, formatJavadoc, config.getProvisioner()); } } diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/PalantirJavaFormatTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/PalantirJavaFormatTest.java index ffac52c6af..821766f9e9 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/PalantirJavaFormatTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/java/PalantirJavaFormatTest.java @@ -15,8 +15,11 @@ */ package com.diffplug.spotless.maven.java; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + import org.junit.jupiter.api.Test; +import com.diffplug.spotless.Jvm; import com.diffplug.spotless.java.PalantirJavaFormatStep; import com.diffplug.spotless.maven.MavenIntegrationHarness; @@ -52,6 +55,19 @@ void formatJavaDoc() throws Exception { runTest("java/palantirjavaformat/JavaCodeWithJavaDocFormatted.test", "java/palantirjavaformat/JavaCodeWithJavaDocUnformatted.test"); } + @Test + void customGroupArtifact() throws Exception { + assumeTrue(Jvm.version() >= 21, "open-java-format 21.0.2 requires JVM 21+"); + + writePomWithJavaSteps( + "", + " dev.openjavaformat:open-java-format", + " 21.0.2", + ""); + + runTest("java/palantirjavaformat/JavaCodeFormatted.test", "java/palantirjavaformat/JavaCodeUnformatted.test"); + } + private void runTest(String targetResource, String sourceResource) throws Exception { String path = "src/main/java/test.java"; setFile(path).toResource(sourceResource); diff --git a/testlib/src/main/resources/sql/dbeaver/set-mode.clean b/testlib/src/main/resources/sql/dbeaver/set-mode.clean new file mode 100644 index 0000000000..30e7fdc679 --- /dev/null +++ b/testlib/src/main/resources/sql/dbeaver/set-mode.clean @@ -0,0 +1,2 @@ +SET +MODE PostgreSQL; diff --git a/testlib/src/main/resources/sql/dbeaver/set-mode.dirty b/testlib/src/main/resources/sql/dbeaver/set-mode.dirty new file mode 100644 index 0000000000..81ae754394 --- /dev/null +++ b/testlib/src/main/resources/sql/dbeaver/set-mode.dirty @@ -0,0 +1 @@ +SET MODE PostgreSQL; diff --git a/testlib/src/test/java/com/diffplug/spotless/java/PalantirJavaFormatStepTest.java b/testlib/src/test/java/com/diffplug/spotless/java/PalantirJavaFormatStepTest.java index 2f37e53326..03a82cd68e 100644 --- a/testlib/src/test/java/com/diffplug/spotless/java/PalantirJavaFormatStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/java/PalantirJavaFormatStepTest.java @@ -15,7 +15,11 @@ */ package com.diffplug.spotless.java; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.condition.JRE.JAVA_21; + import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.ResourceHarness; @@ -67,9 +71,28 @@ void behaviorWithGoogleStyle() throws Exception { .testResource("java/palantirjavaformat/JavaCodeWithPackageUnformatted.test", "java/palantirjavaformat/JavaCodeWithPackageFormattedGoogle.test"); } + @Test + @EnabledForJreRange(min = JAVA_21) + void behaviorWithCustomGroupArtifact() throws Exception { + FormatterStep step = PalantirJavaFormatStep.create("dev.openjavaformat:open-java-format", "21.0.2", PalantirJavaFormatStep.defaultStyle(), PalantirJavaFormatStep.defaultFormatJavadoc(), TestProvisioner.mavenCentral()); + StepHarness.forStep(step) + .testResource("java/palantirjavaformat/JavaCodeUnformatted.test", "java/palantirjavaformat/JavaCodeFormatted.test") + .testResource("java/palantirjavaformat/JavaCodeWithLicenseUnformatted.test", "java/palantirjavaformat/JavaCodeWithLicenseFormatted.test") + .testResource("java/palantirjavaformat/JavaCodeWithPackageUnformatted.test", "java/palantirjavaformat/JavaCodeWithPackageFormatted.test"); + } + + @Test + void invalidGroupArtifact() { + assertThrows(IllegalArgumentException.class, + () -> PalantirJavaFormatStep.create("com.palantir.javaformat", "1.1.0", "PALANTIR", false, TestProvisioner.mavenCentral())); + assertThrows(IllegalArgumentException.class, + () -> PalantirJavaFormatStep.create("com.palantir.javaformat:palantir-java-format:1.1.0", "1.1.0", "PALANTIR", false, TestProvisioner.mavenCentral())); + } + @Test void equality() { new SerializableEqualityTester() { + String groupArtifact = PalantirJavaFormatStep.defaultGroupArtifact(); String version = "1.1.0"; String style = ""; boolean formatJavadoc; @@ -93,12 +116,17 @@ protected void setupTest(API api) { formatJavadoc = true; api.areDifferentThan(); formatJavadoc = false; + + // change the groupArtifact, and it's different + groupArtifact = "com.palantir.javaformat:palantir-java-format-spi"; + api.areDifferentThan(); + groupArtifact = PalantirJavaFormatStep.defaultGroupArtifact(); } @Override protected FormatterStep create() { String finalVersion = this.version; - return PalantirJavaFormatStep.create(finalVersion, style, formatJavadoc, TestProvisioner.mavenCentral()); + return PalantirJavaFormatStep.create(groupArtifact, finalVersion, style, formatJavadoc, TestProvisioner.mavenCentral()); } }.testEquals(); } diff --git a/testlib/src/test/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStepTest.java b/testlib/src/test/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStepTest.java index 648ddc6669..24c6be0b19 100644 --- a/testlib/src/test/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 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. @@ -15,7 +15,11 @@ */ package com.diffplug.spotless.sql; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.PrintStream; import java.util.Collections; import java.util.List; @@ -39,6 +43,22 @@ void behavior() { .testResource("sql/dbeaver/jdbi-params.dirty", "sql/dbeaver/jdbi-params.clean"); } + @Test + void keywordAtStartOfFileDoesNotThrow() { + // https://github.com/diffplug/spotless/issues/1552 + PrintStream originalErr = System.err; + ByteArrayOutputStream err = new ByteArrayOutputStream(); + System.setErr(new PrintStream(err, true)); + try { + FormatterStep step = DBeaverSQLFormatterStep.create(Collections.emptySet()); + StepHarness.forStep(step) + .testResource("sql/dbeaver/set-mode.dirty", "sql/dbeaver/set-mode.clean"); + } finally { + System.setErr(originalErr); + } + assertThat(err.toString()).isEmpty(); + } + @Test void behaviorWithConfigFile() { FormatterStep step = DBeaverSQLFormatterStep.create(createTestFiles("sql/dbeaver/sqlConfig.properties"));