diff --git a/CHANGES.md b/CHANGES.md index f73c938016..358c3c081c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Added +- `scalafmt()` now reads the version from the `version` field in the scalafmt config file when no version is explicitly set in the plugin config, falling back to the built-in default only if neither is available. ([#2922](https://github.com/diffplug/spotless/pull/2922)) - Add `javaparserVersion` option to the Cleanthat step, allowing callers to override the JavaParser version pulled in transitively by Cleanthat. ([#2903](https://github.com/diffplug/spotless/pull/2903)) ### Fixed - Fix non-idempotent formatting when `importOrder()` is combined with `greclipse()`: a single catch-all group no longer strips blank lines that `greclipse()` independently inserted between import groups. ([#2914](https://github.com/diffplug/spotless/pull/2914)) diff --git a/lib/src/main/java/com/diffplug/spotless/scala/ScalaFmtStep.java b/lib/src/main/java/com/diffplug/spotless/scala/ScalaFmtStep.java index e0bfa77189..9e045b41d6 100644 --- a/lib/src/main/java/com/diffplug/spotless/scala/ScalaFmtStep.java +++ b/lib/src/main/java/com/diffplug/spotless/scala/ScalaFmtStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2025 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. @@ -20,7 +20,9 @@ import java.io.Serial; import java.io.Serializable; import java.lang.reflect.Constructor; +import java.nio.file.Files; import java.util.Collections; +import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; @@ -54,15 +56,16 @@ public static FormatterStep create(Provisioner provisioner) { return create(defaultVersion(), defaultScalaMajorVersion(), provisioner, null); } - public static FormatterStep create(String version, Provisioner provisioner, @Nullable File configFile) { + public static FormatterStep create(@Nullable String version, Provisioner provisioner, @Nullable File configFile) { return create(version, defaultScalaMajorVersion(), provisioner, configFile); } - public static FormatterStep create(String version, @Nullable String scalaMajorVersion, Provisioner provisioner, @Nullable File configFile) { + public static FormatterStep create(@Nullable String version, @Nullable String scalaMajorVersion, Provisioner provisioner, @Nullable File configFile) { + String finalVersion = version != null ? version : versionFromConfig(configFile).orElse(defaultVersion()); String finalScalaMajorVersion = scalaMajorVersion == null ? DEFAULT_SCALA_MAJOR_VERSION : scalaMajorVersion; return FormatterStep.create(NAME, - new ScalaFmtStep(JarState.promise(() -> JarState.from(MAVEN_COORDINATE + finalScalaMajorVersion + ":" + version, provisioner)), configFile), + new ScalaFmtStep(JarState.promise(() -> JarState.from(MAVEN_COORDINATE + finalScalaMajorVersion + ":" + finalVersion, provisioner)), configFile), ScalaFmtStep::equalityState, State::createFormat); } @@ -75,6 +78,22 @@ public static String defaultScalaMajorVersion() { return DEFAULT_SCALA_MAJOR_VERSION; } + private static Optional versionFromConfig(@Nullable File configFile) { + try { + return configFile == null || !configFile.exists() ? Optional.empty() + : Files.readAllLines(configFile.toPath()) + .stream() + .filter(line -> line.trim().startsWith("version")) + .findFirst() + .flatMap(line -> { + var parts = line.replaceAll("\\s", "").split("="); + return parts.length < 2 ? Optional.empty() : Optional.of(parts[1]); + }); + } catch (IOException e) { + return Optional.empty(); + } + } + private State equalityState() throws IOException { return new State(jarState.get(), configFile); } diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 7598404841..dd7741c3ff 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -4,6 +4,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Added +- `scalafmt()` now reads the version from the `version` field in the scalafmt config file when no version is explicitly set in the plugin config, falling back to the built-in default only if neither is available. ([#2922](https://github.com/diffplug/spotless/pull/2922)) - Add `withIndentStyle` and `withIndentSize` configuration to `tableTestFormatter` for setting the fallback indent when no `.editorconfig` is found. ([#2893](https://github.com/diffplug/spotless/pull/2893)) - Add `javaparserVersion(...)` to `cleanthat`, allowing users to override the JavaParser version pulled in transitively by Cleanthat. ([#2903](https://github.com/diffplug/spotless/pull/2903)) ### Fixed diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/ScalaExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/ScalaExtension.java index 1639f3473c..c11e6ba4ea 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/ScalaExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/ScalaExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 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. @@ -35,22 +35,20 @@ public ScalaExtension(SpotlessExtension spotless) { } public ScalaFmtConfig scalafmt() { - return scalafmt(ScalaFmtStep.defaultVersion()); + return new ScalaFmtConfig(null); } public ScalaFmtConfig scalafmt(String version) { - return new ScalaFmtConfig(version); + return new ScalaFmtConfig(Objects.requireNonNull(version)); } public class ScalaFmtConfig { - final String version; - @Nullable - String scalaMajorVersion; - @Nullable - Object configFile; + @Nullable final String version; + @Nullable String scalaMajorVersion; + @Nullable Object configFile; - ScalaFmtConfig(String version) { - this.version = Objects.requireNonNull(version); + ScalaFmtConfig(@Nullable String version) { + this.version = version; addStep(createStep()); } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ScalaExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ScalaExtensionTest.java index 3474f8e25d..36262e9010 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ScalaExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ScalaExtensionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 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. @@ -20,6 +20,25 @@ import org.junit.jupiter.api.Test; class ScalaExtensionTest extends GradleIntegrationHarness { + @Test + void versionFromConfig() throws IOException { + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "apply plugin: 'scala'", + "spotless {", + " scala {", + " scalafmt().configFile('scalafmt.conf')", + " }", + "}"); + setFile("scalafmt.conf").toResource("scala/scalafmt/scalafmt_newer.conf"); + setFile("src/main/scala/basic.scala").toResource("scala/scalafmt/basic.dirty"); + gradleRunner().withArguments("spotlessApply").build(); + assertFile("src/main/scala/basic.scala").sameAsResource("scala/scalafmt/basic.cleanWithCustomConf_3.0.0"); + } + @Test void integration() throws IOException { setFile("build.gradle").toLines( diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 7fa2b3f5ab..90dbfa5e26 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -4,6 +4,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Added +- `` now reads the version from the `version` field in the scalafmt config file when no `` is explicitly set, falling back to the built-in default only if neither is available. ([#2922](https://github.com/diffplug/spotless/pull/2922)) - Add `` option to ``, allowing users to override the JavaParser version pulled in transitively by Cleanthat. ([#2903](https://github.com/diffplug/spotless/pull/2903)) ### Fixed - Fix non-idempotent formatting when `importOrder()` is combined with `greclipse()`: a single catch-all group no longer strips blank lines that `greclipse()` independently inserted between import groups. ([#2914](https://github.com/diffplug/spotless/pull/2914)) diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/scala/Scalafmt.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/scala/Scalafmt.java index e0dc44bef5..4ceb20d7bc 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/scala/Scalafmt.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/scala/Scalafmt.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 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. @@ -37,9 +37,7 @@ public class Scalafmt implements FormatterStepFactory { @Override public FormatterStep newFormatterStep(FormatterStepConfig config) { - String scalafmtVersion = version != null ? version : ScalaFmtStep.defaultVersion(); - String scalafmtScalaMajorVersion = scalaMajorVersion != null ? scalaMajorVersion : ScalaFmtStep.defaultScalaMajorVersion(); File configFile = config.getFileLocator().locateFile(file); - return ScalaFmtStep.create(scalafmtVersion, scalafmtScalaMajorVersion, config.getProvisioner(), configFile); + return ScalaFmtStep.create(version, scalaMajorVersion, config.getProvisioner(), configFile); } } diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/scala/ScalafmtTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/scala/ScalafmtTest.java index ef36c29d8b..6d90c55bcc 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/scala/ScalafmtTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/scala/ScalafmtTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2021 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. @@ -39,6 +39,18 @@ void testScalafmtWithCustomConfig() throws Exception { runTest("scala/scalafmt/basic.cleanWithCustomConf_3.0.0"); } + @Test + void testScalafmtVersionFromConfig() throws Exception { + setFile("scalafmt.conf").toResource("scala/scalafmt/scalafmt_newer.conf"); + + writePomWithScalaSteps( + "", + " ${project.basedir}/scalafmt.conf", + ""); + + runTest("scala/scalafmt/basic.cleanWithCustomConf_3.0.0"); + } + private void runTest(String s) throws Exception { String path = "src/main/scala/test.scala"; setFile(path).toResource("scala/scalafmt/basic.dirty"); diff --git a/testlib/src/main/resources/scala/scalafmt/scalafmt_newer.conf b/testlib/src/main/resources/scala/scalafmt/scalafmt_newer.conf new file mode 100644 index 0000000000..1970f2f567 --- /dev/null +++ b/testlib/src/main/resources/scala/scalafmt/scalafmt_newer.conf @@ -0,0 +1,5 @@ +version = 3.11.0 +runner.dialect = scala213 +runner.dialectFeatures = [relaxedLambdaSyntax] +style = defaultWithAlign +maxColumn = 20 diff --git a/testlib/src/test/java/com/diffplug/spotless/scala/ScalaFmtStepTest.java b/testlib/src/test/java/com/diffplug/spotless/scala/ScalaFmtStepTest.java index e8485c680d..e04e46db7e 100644 --- a/testlib/src/test/java/com/diffplug/spotless/scala/ScalaFmtStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/scala/ScalaFmtStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2025 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. @@ -88,6 +88,24 @@ protected FormatterStep create() { }.testEquals(); } + @Test + void behaviorVersionFromConfigFile() { + StepHarness.forStep(ScalaFmtStep.create((String) null, TestProvisioner.mavenCentral(), createTestFile("scala/scalafmt/scalafmt.conf"))) + .testResource("scala/scalafmt/basic.dirty", "scala/scalafmt/basic.cleanWithCustomConf_3.0.0"); + } + + @Test + void behaviorVersionFromConfigFileNewerVersion() { + StepHarness.forStep(ScalaFmtStep.create((String) null, TestProvisioner.mavenCentral(), createTestFile("scala/scalafmt/scalafmt_newer.conf"))) + .testResource("scala/scalafmt/basic.dirty", "scala/scalafmt/basic.cleanWithCustomConf_3.0.0"); + } + + @Test + void behaviorDefaultVersionWithoutConfigFile() { + StepHarness.forStep(ScalaFmtStep.create((String) null, TestProvisioner.mavenCentral(), null)) + .testResource("scala/scalafmt/basic.dirty", "scala/scalafmt/basic.clean_3.0.0"); + } + @Test void invalidConfiguration() { File invalidConfFile = createTestFile("scala/scalafmt/scalafmt.invalid.conf");