diff --git a/CHANGES.md b/CHANGES.md index 81e67e1416..68764ad9bf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,10 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Changes * Bump default `ktlint` version to latest `1.2.1` -> `1.3.0`. ([#2165](https://github.com/diffplug/spotless/pull/2165)) +* Bump default `ktfmt` version to latest `0.49` -> `0.51`. ([#2172](https://github.com/diffplug/spotless/pull/2172)) +* Renamed property `ktfmt` option `removeUnusedImport` -> `removeUnusedImports` to match `ktfmt`. ([#2172](https://github.com/diffplug/spotless/pull/2172)) +### Fixed +* Fix compatibility issue introduced by `ktfmt` `0.51`. ([#2172](https://github.com/diffplug/spotless/issues/2172)) ## [3.0.0.BETA1] - 2024-06-04 ### Added diff --git a/lib/build.gradle b/lib/build.gradle index c82c648e07..24dddfe396 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -101,7 +101,7 @@ dependencies { jacksonCompileOnly "com.fasterxml.jackson.core:jackson-databind:$VER_JACKSON" jacksonCompileOnly "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$VER_JACKSON" // ktfmt - ktfmtCompileOnly "com.facebook:ktfmt:0.49" + ktfmtCompileOnly "com.facebook:ktfmt:0.51" ktfmtCompileOnly("com.google.googlejavaformat:google-java-format") { version { strictly '1.7' // for JDK 8 compatibility diff --git a/lib/src/ktfmt/java/com/diffplug/spotless/glue/ktfmt/KtfmtFormatterFunc.java b/lib/src/ktfmt/java/com/diffplug/spotless/glue/ktfmt/KtfmtFormatterFunc.java index 1d191c05d5..50925a5a5d 100644 --- a/lib/src/ktfmt/java/com/diffplug/spotless/glue/ktfmt/KtfmtFormatterFunc.java +++ b/lib/src/ktfmt/java/com/diffplug/spotless/glue/ktfmt/KtfmtFormatterFunc.java @@ -15,8 +15,6 @@ */ package com.diffplug.spotless.glue.ktfmt; -import java.lang.reflect.Method; - import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -34,7 +32,7 @@ public final class KtfmtFormatterFunc implements FormatterFunc { private final KtfmtFormattingOptions ktfmtFormattingOptions; public KtfmtFormatterFunc() { - this(KtfmtStyle.DEFAULT, null); + this(KtfmtStyle.META, null); } public KtfmtFormatterFunc(@Nonnull KtfmtStyle style) { @@ -42,7 +40,7 @@ public KtfmtFormatterFunc(@Nonnull KtfmtStyle style) { } public KtfmtFormatterFunc(@Nullable KtfmtFormattingOptions ktfmtFormattingOptions) { - this(KtfmtStyle.DEFAULT, ktfmtFormattingOptions); + this(KtfmtStyle.META, ktfmtFormattingOptions); } public KtfmtFormatterFunc(@Nonnull KtfmtStyle style, @Nullable KtfmtFormattingOptions ktfmtFormattingOptions) { @@ -59,11 +57,8 @@ public String apply(@Nonnull String input) throws Exception { private FormattingOptions createFormattingOptions() throws Exception { FormattingOptions formattingOptions; switch (style) { - case DEFAULT: - formattingOptions = new FormattingOptions(); - break; - case DROPBOX: - formattingOptions = Formatter.DROPBOX_FORMAT; + case META: + formattingOptions = Formatter.META_FORMAT; break; case GOOGLE: formattingOptions = Formatter.GOOGLE_FORMAT; @@ -72,30 +67,17 @@ private FormattingOptions createFormattingOptions() throws Exception { formattingOptions = Formatter.KOTLINLANG_FORMAT; break; default: - throw new IllegalStateException("Unknown formatting option"); + throw new IllegalStateException("Unknown formatting option " + style); } if (ktfmtFormattingOptions != null) { - try { - formattingOptions = formattingOptions.copy( - formattingOptions.getStyle(), - ktfmtFormattingOptions.getMaxWidth().orElse(formattingOptions.getMaxWidth()), - ktfmtFormattingOptions.getBlockIndent().orElse(formattingOptions.getBlockIndent()), - ktfmtFormattingOptions.getContinuationIndent().orElse(formattingOptions.getContinuationIndent()), - ktfmtFormattingOptions.getRemoveUnusedImport().orElse(formattingOptions.getRemoveUnusedImports()), - formattingOptions.getDebuggingPrintOpsAfterFormatting(), - formattingOptions.getManageTrailingCommas()); - } catch (NoSuchMethodError e) { - //noinspection JavaReflectionMemberAccess, ABI change from ktfmt 0.47 - Method copyMethod = formattingOptions.getClass().getMethod("copy", FormattingOptions.Style.class, int.class, int.class, int.class, boolean.class, boolean.class); - formattingOptions = (FormattingOptions) copyMethod.invoke(formattingOptions, - formattingOptions.getStyle(), - ktfmtFormattingOptions.getMaxWidth().orElse(formattingOptions.getMaxWidth()), - ktfmtFormattingOptions.getBlockIndent().orElse(formattingOptions.getBlockIndent()), - ktfmtFormattingOptions.getContinuationIndent().orElse(formattingOptions.getContinuationIndent()), - ktfmtFormattingOptions.getRemoveUnusedImport().orElse(formattingOptions.getRemoveUnusedImports()), - formattingOptions.getDebuggingPrintOpsAfterFormatting()); - } + formattingOptions = formattingOptions.copy( + ktfmtFormattingOptions.getMaxWidth().orElse(formattingOptions.getMaxWidth()), + ktfmtFormattingOptions.getBlockIndent().orElse(formattingOptions.getBlockIndent()), + ktfmtFormattingOptions.getContinuationIndent().orElse(formattingOptions.getContinuationIndent()), + formattingOptions.getManageTrailingCommas(), + ktfmtFormattingOptions.getRemoveUnusedImport().orElse(formattingOptions.getRemoveUnusedImports()), + formattingOptions.getDebuggingPrintOpsAfterFormatting()); } return formattingOptions; diff --git a/lib/src/ktfmt/java/com/diffplug/spotless/glue/ktfmt/KtfmtStyle.java b/lib/src/ktfmt/java/com/diffplug/spotless/glue/ktfmt/KtfmtStyle.java index 34b81175ec..47aea7eedd 100644 --- a/lib/src/ktfmt/java/com/diffplug/spotless/glue/ktfmt/KtfmtStyle.java +++ b/lib/src/ktfmt/java/com/diffplug/spotless/glue/ktfmt/KtfmtStyle.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 DiffPlug + * Copyright 2022-2024 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,5 +16,5 @@ package com.diffplug.spotless.glue.ktfmt; public enum KtfmtStyle { - DEFAULT, DROPBOX, GOOGLE, KOTLIN_LANG + META, GOOGLE, KOTLIN_LANG } diff --git a/lib/src/main/java/com/diffplug/spotless/kotlin/KtfmtStep.java b/lib/src/main/java/com/diffplug/spotless/kotlin/KtfmtStep.java index cf76d5f8ae..5e1cd7f00a 100644 --- a/lib/src/main/java/com/diffplug/spotless/kotlin/KtfmtStep.java +++ b/lib/src/main/java/com/diffplug/spotless/kotlin/KtfmtStep.java @@ -16,12 +16,15 @@ package com.diffplug.spotless.kotlin; import static com.diffplug.spotless.kotlin.KtfmtStep.Style.DEFAULT; +import static com.diffplug.spotless.kotlin.KtfmtStep.Style.DROPBOX; +import static com.diffplug.spotless.kotlin.KtfmtStep.Style.META; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Objects; +import java.util.Optional; import javax.annotation.Nullable; @@ -32,11 +35,11 @@ import com.diffplug.spotless.ThrowingEx; /** - * Wraps up ktfmt as a FormatterStep. + * Wraps up ktfmt as a FormatterStep. */ public class KtfmtStep implements Serializable { private static final long serialVersionUID = 1L; - private static final String DEFAULT_VERSION = "0.49"; + private static final String DEFAULT_VERSION = "0.51"; private static final String NAME = "ktfmt"; private static final String MAVEN_COORDINATE = "com.facebook:ktfmt:"; @@ -64,17 +67,32 @@ private KtfmtStep(String version, /** * Used to allow multiple style option through formatting options and since when is each of them available. * - * @see ktfmt source + * @see ktfmt source */ public enum Style { - DEFAULT("DEFAULT_FORMAT", "0.0"), DROPBOX("DROPBOX_FORMAT", "0.11"), GOOGLE("GOOGLE_FORMAT", "0.21"), KOTLINLANG("KOTLINLANG_FORMAT", "0.21"); + // @formatter:off + DEFAULT("DEFAULT_FORMAT", "0.0", "0.50"), + META("META_FORMAT", "0.51"), + DROPBOX("DROPBOX_FORMAT", "0.16", "0.50"), + GOOGLE("GOOGLE_FORMAT", "0.19"), + KOTLINLANG("KOTLINLANG_FORMAT", "0.21"), + ; + // @formatter:on final private String format; final private String since; + final private @Nullable String until; Style(String format, String since) { this.format = format; this.since = since; + this.until = null; + } + + Style(String format, String since, @Nullable String until) { + this.format = format; + this.since = since; + this.until = until; } String getFormat() { @@ -84,6 +102,12 @@ String getFormat() { String getSince() { return since; } + + /** Last version (inclusive) that supports this style */ + @Nullable + String getUntil() { + return until; + } } public static class KtfmtFormattingOptions implements Serializable { @@ -100,7 +124,7 @@ public static class KtfmtFormattingOptions implements Serializable { private Integer continuationIndent = null; @Nullable - private Boolean removeUnusedImport = null; + private Boolean removeUnusedImports = null; public KtfmtFormattingOptions() {} @@ -108,11 +132,11 @@ public KtfmtFormattingOptions( @Nullable Integer maxWidth, @Nullable Integer blockIndent, @Nullable Integer continuationIndent, - @Nullable Boolean removeUnusedImport) { + @Nullable Boolean removeUnusedImports) { this.maxWidth = maxWidth; this.blockIndent = blockIndent; this.continuationIndent = continuationIndent; - this.removeUnusedImport = removeUnusedImport; + this.removeUnusedImports = removeUnusedImports; } public void setMaxWidth(int maxWidth) { @@ -127,18 +151,11 @@ public void setContinuationIndent(int continuationIndent) { this.continuationIndent = continuationIndent; } - public void setRemoveUnusedImport(boolean removeUnusedImport) { - this.removeUnusedImport = removeUnusedImport; + public void setRemoveUnusedImports(boolean removeUnusedImports) { + this.removeUnusedImports = removeUnusedImports; } } - /** - * The format method is available in the link below. - * - * @see ktfmt source - */ - static final String FORMATTER_METHOD = "format"; - /** Creates a step which formats everything - code, import order, and unused imports. */ public static FormatterStep create(Provisioner provisioner) { return create(defaultVersion(), provisioner); @@ -169,7 +186,6 @@ private State equalityState() { private static final class State implements Serializable { private static final long serialVersionUID = 1L; - private static final String PACKAGE = "com.facebook.ktfmt"; private final String version; @Nullable private final Style style; @@ -185,16 +201,15 @@ private static final class State implements Serializable { this.options = options; this.style = style; this.jarState = jarState; + validateStyle(); + validateOptions(); } FormatterFunc createFormat() throws Exception { final ClassLoader classLoader = jarState.getClassLoader(); - if (BadSemver.version(version) < BadSemver.version(0, 32)) { - if (options != null) { - throw new IllegalStateException("Ktfmt formatting options supported for version 0.32 and later"); - } - return getFormatterFuncFallback(style != null ? style : DEFAULT, classLoader); + if (BadSemver.version(version) < BadSemver.version(0, 51)) { + return new KtfmtFormatterFuncCompat(version, style, options, classLoader).getFormatterFunc(); } final Class formatterFuncClass = classLoader.loadClass("com.diffplug.spotless.glue.ktfmt.KtfmtFormatterFunc"); @@ -215,7 +230,7 @@ FormatterFunc createFormat() throws Exception { final Constructor optionsConstructor = ktfmtFormattingOptionsClass.getConstructor( Integer.class, Integer.class, Integer.class, Boolean.class); final Object ktfmtFormattingOptions = optionsConstructor.newInstance( - options.maxWidth, options.blockIndent, options.continuationIndent, options.removeUnusedImport); + options.maxWidth, options.blockIndent, options.continuationIndent, options.removeUnusedImports); if (style == null) { final Constructor constructor = formatterFuncClass.getConstructor(ktfmtFormattingOptionsClass); return (FormatterFunc) constructor.newInstance(ktfmtFormattingOptions); @@ -225,16 +240,42 @@ FormatterFunc createFormat() throws Exception { return (FormatterFunc) constructor.newInstance(ktfmtStyle, ktfmtFormattingOptions); } + private void validateOptions() { + if (BadSemver.version(version) < BadSemver.version(0, 11)) { + if (options != null) { + throw new IllegalStateException("Ktfmt formatting options supported for version 0.11 and later"); + } + return; + } + + if (BadSemver.version(version) < BadSemver.version(0, 17)) { + if (options != null && options.removeUnusedImports != null) { + throw new IllegalStateException("Ktfmt formatting option `removeUnusedImports` supported for version 0.17 and later"); + } + } + } + + private void validateStyle() { + if (style == null) { + return; + } + + if (BadSemver.version(version) < BadSemver.version(style.since)) { + throw new IllegalStateException(String.format("The style %s is available from version %s (current version: %s)", style.name(), style.since, version)); + } + if (style.until != null && BadSemver.version(version) > BadSemver.version(style.until)) { + throw new IllegalStateException(String.format("The style %s is no longer available from version %s (current version: %s)", style.name(), style.until, version)); + } + } + /** * @param style * @return com.diffplug.spotless.glue.ktfmt.KtfmtStyle enum value name */ private String getKtfmtStyleOption(Style style) { switch (style) { - case DEFAULT: - return "DEFAULT"; - case DROPBOX: - return "DROPBOX"; + case META: + return "META"; case GOOGLE: return "GOOGLE"; case KOTLINLANG: @@ -243,48 +284,117 @@ private String getKtfmtStyleOption(Style style) { throw new IllegalStateException("Unsupported style: " + style); } } + } + + private static final class KtfmtFormatterFuncCompat { + private static final String PACKAGE = "com.facebook.ktfmt"; + + /** + * The format method is available in the link below. + * + * @see ktfmt source + */ + static final String FORMATTER_METHOD = "format"; + + private final String version; + private final Style style; + private final KtfmtFormattingOptions options; + private final ClassLoader classLoader; + + public KtfmtFormatterFuncCompat(String currentVersion, @Nullable Style style, @Nullable KtfmtFormattingOptions options, ClassLoader classLoader) { + this.version = currentVersion; + this.style = style; + this.options = options; + this.classLoader = classLoader; + } - private FormatterFunc getFormatterFuncFallback(Style style, ClassLoader classLoader) { + public FormatterFunc getFormatterFunc() { return input -> { try { - if (style == DEFAULT) { - Method formatterMethod = getFormatterClazz(classLoader).getMethod(FORMATTER_METHOD, String.class); - return (String) formatterMethod.invoke(getFormatterClazz(classLoader), input); - } else { - Method formatterMethod = getFormatterClazz(classLoader).getMethod(FORMATTER_METHOD, - getFormattingOptionsClazz(classLoader), - String.class); - Object formattingOptions = getCustomFormattingOptions(classLoader, style); - return (String) formatterMethod.invoke(getFormatterClazz(classLoader), formattingOptions, input); - } + return applyFormat(input); } catch (InvocationTargetException e) { throw ThrowingEx.unwrapCause(e); } }; } - private Object getCustomFormattingOptions(ClassLoader classLoader, Style style) throws Exception { - if (BadSemver.version(version) < BadSemver.version(style.since)) { - throw new IllegalStateException(String.format("The style %s is available from version %s (current version: %s)", style.name(), style.since, version)); + protected String applyFormat(String input) throws Exception { + Class formatterClass = getFormatterClazz(); + if (style == null && options == null || style == DEFAULT) { + Method formatterMethod = formatterClass.getMethod(FORMATTER_METHOD, String.class); + return (String) formatterMethod.invoke(formatterClass, input); + } else { + Method formatterMethod = formatterClass.getMethod(FORMATTER_METHOD, getFormattingOptionsClazz(), String.class); + Object formattingOptions = getCustomFormattingOptions(formatterClass); + return (String) formatterMethod.invoke(formatterClass, formattingOptions, input); + } + } + + private Object getCustomFormattingOptions(Class formatterClass) throws Exception { + Object formattingOptions = getFormattingOptionsFromStyle(formatterClass); + Class formattingOptionsClass = formattingOptions.getClass(); + + if (options != null) { + if (BadSemver.version(version) < BadSemver.version(0, 17)) { + formattingOptions = formattingOptions.getClass().getConstructor(int.class, int.class, int.class).newInstance( + /* maxWidth = */ Optional.ofNullable(options.maxWidth).orElse((Integer) formattingOptionsClass.getMethod("getMaxWidth").invoke(formattingOptions)), + /* blockIndent = */ Optional.ofNullable(options.blockIndent).orElse((Integer) formattingOptionsClass.getMethod("getBlockIndent").invoke(formattingOptions)), + /* continuationIndent = */ Optional.ofNullable(options.continuationIndent).orElse((Integer) formattingOptionsClass.getMethod("getContinuationIndent").invoke(formattingOptions))); + } else if (BadSemver.version(version) < BadSemver.version(0, 19)) { + formattingOptions = formattingOptions.getClass().getConstructor(int.class, int.class, int.class, boolean.class, boolean.class).newInstance( + /* maxWidth = */ Optional.ofNullable(options.maxWidth).orElse((Integer) formattingOptionsClass.getMethod("getMaxWidth").invoke(formattingOptions)), + /* blockIndent = */ Optional.ofNullable(options.blockIndent).orElse((Integer) formattingOptionsClass.getMethod("getBlockIndent").invoke(formattingOptions)), + /* continuationIndent = */ Optional.ofNullable(options.continuationIndent).orElse((Integer) formattingOptionsClass.getMethod("getContinuationIndent").invoke(formattingOptions)), + /* removeUnusedImports = */ Optional.ofNullable(options.removeUnusedImports).orElse((Boolean) formattingOptionsClass.getMethod("getRemoveUnusedImports").invoke(formattingOptions)), + /* debuggingPrintOpsAfterFormatting = */ (Boolean) formattingOptionsClass.getMethod("getDebuggingPrintOpsAfterFormatting").invoke(formattingOptions)); + } else if (BadSemver.version(version) < BadSemver.version(0, 47)) { + Class styleClass = classLoader.loadClass(formattingOptionsClass.getName() + "$Style"); + formattingOptions = formattingOptions.getClass().getConstructor(styleClass, int.class, int.class, int.class, boolean.class, boolean.class).newInstance( + /* style = */ formattingOptionsClass.getMethod("getStyle").invoke(formattingOptions), + /* maxWidth = */ Optional.ofNullable(options.maxWidth).orElse((Integer) formattingOptionsClass.getMethod("getMaxWidth").invoke(formattingOptions)), + /* blockIndent = */ Optional.ofNullable(options.blockIndent).orElse((Integer) formattingOptionsClass.getMethod("getBlockIndent").invoke(formattingOptions)), + /* continuationIndent = */ Optional.ofNullable(options.continuationIndent).orElse((Integer) formattingOptionsClass.getMethod("getContinuationIndent").invoke(formattingOptions)), + /* removeUnusedImports = */ Optional.ofNullable(options.removeUnusedImports).orElse((Boolean) formattingOptionsClass.getMethod("getRemoveUnusedImports").invoke(formattingOptions)), + /* debuggingPrintOpsAfterFormatting = */ (Boolean) formattingOptionsClass.getMethod("getDebuggingPrintOpsAfterFormatting").invoke(formattingOptions)); + } else { + Class styleClass = classLoader.loadClass(formattingOptionsClass.getName() + "$Style"); + formattingOptions = formattingOptions.getClass().getConstructor(styleClass, int.class, int.class, int.class, boolean.class, boolean.class, boolean.class).newInstance( + /* style = */ formattingOptionsClass.getMethod("getStyle").invoke(formattingOptions), + /* maxWidth = */ Optional.ofNullable(options.maxWidth).orElse((Integer) formattingOptionsClass.getMethod("getMaxWidth").invoke(formattingOptions)), + /* blockIndent = */ Optional.ofNullable(options.blockIndent).orElse((Integer) formattingOptionsClass.getMethod("getBlockIndent").invoke(formattingOptions)), + /* continuationIndent = */ Optional.ofNullable(options.continuationIndent).orElse((Integer) formattingOptionsClass.getMethod("getContinuationIndent").invoke(formattingOptions)), + /* removeUnusedImports = */ Optional.ofNullable(options.removeUnusedImports).orElse((Boolean) formattingOptionsClass.getMethod("getRemoveUnusedImports").invoke(formattingOptions)), + /* debuggingPrintOpsAfterFormatting = */ (Boolean) formattingOptionsClass.getMethod("getDebuggingPrintOpsAfterFormatting").invoke(formattingOptions), + /* manageTrailingCommas */ (Boolean) formattingOptionsClass.getMethod("getManageTrailingCommas").invoke(formattingOptions)); + } } - try { - // ktfmt v0.19 and later - return getFormatterClazz(classLoader).getField(style.getFormat()).get(null); - } catch (NoSuchFieldException ignored) {} + return formattingOptions; + } - // fallback to old, pre-0.19 ktfmt interface. - if (style == Style.DEFAULT || style == Style.DROPBOX) { + private Object getFormattingOptionsFromStyle(Class formatterClass) throws Exception { + Style style = this.style; + if (style == null) { + if (BadSemver.version(version) < BadSemver.version(0, 51)) { + style = DEFAULT; + } else { + style = META; + } + } + if (BadSemver.version(version) < BadSemver.version(0, 19)) { + if (style != DROPBOX) { + throw new IllegalStateException("Invalid style " + style + " for version " + version); + } Class formattingOptionsCompanionClazz = classLoader.loadClass(PACKAGE + ".FormattingOptions$Companion"); Object companion = formattingOptionsCompanionClazz.getConstructors()[0].newInstance((Object) null); Method formattingOptionsMethod = formattingOptionsCompanionClazz.getDeclaredMethod("dropboxStyle"); return formattingOptionsMethod.invoke(companion); } else { - throw new IllegalStateException("Versions pre-0.19 can only use Default and Dropbox styles"); + return formatterClass.getField(style.getFormat()).get(null); } } - private Class getFormatterClazz(ClassLoader classLoader) throws Exception { + private Class getFormatterClazz() throws Exception { Class formatterClazz; if (BadSemver.version(version) >= BadSemver.version(0, 31)) { formatterClazz = classLoader.loadClass(PACKAGE + ".format.Formatter"); @@ -294,7 +404,7 @@ private Class getFormatterClazz(ClassLoader classLoader) throws Exception { return formatterClazz; } - private Class getFormattingOptionsClazz(ClassLoader classLoader) throws Exception { + private Class getFormattingOptionsClazz() throws Exception { Class formattingOptionsClazz; if (BadSemver.version(version) >= BadSemver.version(0, 31)) { formattingOptionsClazz = classLoader.loadClass(PACKAGE + ".format.FormattingOptions"); diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index b09160fd73..2968e11403 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -5,6 +5,10 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Changes * Bump default `ktlint` version to latest `1.2.1` -> `1.3.0`. ([#2165](https://github.com/diffplug/spotless/pull/2165)) +* Bump default `ktfmt` version to latest `0.49` -> `0.51`. ([#2172](https://github.com/diffplug/spotless/pull/2172)) +* Renamed property `ktfmt` option `removeUnusedImport` -> `removeUnusedImports` to match `ktfmt`. ([#2172](https://github.com/diffplug/spotless/pull/2172)) +### Fixed +* Fix compatibility issue introduced by `ktfmt` `0.51`. ([#2172](https://github.com/diffplug/spotless/issues/2172)) ## [7.0.0.BETA1] - 2024-06-04 ### Added diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 117974ba10..e10a333e8c 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -388,12 +388,20 @@ spotless { // if you are using build.gradle.kts, instead of 'spotless {' use: ### ktfmt -[homepage](https://github.com/facebookincubator/ktfmt). [changelog](https://github.com/facebookincubator/ktfmt/releases). +[homepage](https://github.com/facebook/ktfmt). [changelog](https://github.com/facebook/ktfmt/releases). ```kotlin spotless { kotlin { - ktfmt('0.30').dropboxStyle() // version and dropbox style are optional + // version, style and all configurations here are optional + ktfmt("0.51").googleStyle().configure { + it.setMaxWidth(80) + it.setBlockIndent(4) + it.setContinuationIndent(4) + it.setRemoveUnusedImports(false) + } + } +} ``` diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/BaseKotlinExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/BaseKotlinExtension.java index 4be814a470..8d9a36c804 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/BaseKotlinExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/BaseKotlinExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 DiffPlug + * Copyright 2023-2024 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,13 +56,13 @@ public KtlintConfig ktlint(String version) throws IOException { return new KtlintConfig(version, Collections.emptyMap(), Collections.emptyList()); } - /** Uses the ktfmt jar to format source code. */ + /** Uses the ktfmt jar to format source code. */ public KtfmtConfig ktfmt() { return ktfmt(KtfmtStep.defaultVersion()); } /** - * Uses the given version of ktfmt and applies the dropbox style + * Uses the given version of ktfmt and applies the dropbox style * option to format source code. */ public KtfmtConfig ktfmt(String version) { @@ -109,6 +109,10 @@ private KtfmtConfig(String version) { addStep(createStep()); } + public ConfigurableStyle metaStyle() { + return style(KtfmtStep.Style.META); + } + public ConfigurableStyle dropboxStyle() { return style(KtfmtStep.Style.DROPBOX); } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/KotlinExtensionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/KotlinExtensionTest.java index 9720276e15..c2293ab76d 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/KotlinExtensionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/KotlinExtensionTest.java @@ -54,11 +54,11 @@ void integrationKtfmtDropboxStyleWithPublicApi() throws IOException { "repositories { mavenCentral() }", "spotless {", " kotlin {", - " ktfmt().dropboxStyle().configure {", + " ktfmt(\"0.50\").dropboxStyle().configure {", " it.setMaxWidth(4)", " it.setBlockIndent(4)", " it.setContinuationIndent(4)", - " it.setRemoveUnusedImport(false)", + " it.setRemoveUnusedImports(false)", " }", " }", "}"); diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 886dfdf753..d5b511d4c3 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -5,6 +5,10 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Changes * Bump default `ktlint` version to latest `1.2.1` -> `1.3.0`. ([#2165](https://github.com/diffplug/spotless/pull/2165)) +* Bump default `ktfmt` version to latest `0.49` -> `0.51`. ([#2172](https://github.com/diffplug/spotless/pull/2172)) +* Renamed property `ktfmt` option `removeUnusedImport` -> `removeUnusedImports` to match `ktfmt`. ([#2172](https://github.com/diffplug/spotless/pull/2172)) +### Fixed +* Fix compatibility issue introduced by `ktfmt` `0.51`. ([#2172](https://github.com/diffplug/spotless/issues/2172)) ## [2.44.0.BETA1] - 2024-06-04 ### Added diff --git a/plugin-maven/README.md b/plugin-maven/README.md index 12efe3d5bd..5ba1ae7db8 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -392,12 +392,16 @@ Groovy-Eclipse formatting errors/warnings lead per default to a build failure. T ### ktfmt -[homepage](https://github.com/facebookincubator/ktfmt). [changelog](https://github.com/facebookincubator/ktfmt/releases). [code](https://github.com/diffplug/spotless/blob/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/kotlin/Ktfmt.java). +[homepage](https://github.com/facebook/ktfmt). [changelog](https://github.com/facebook/ktfmt/releases). [code](https://github.com/diffplug/spotless/blob/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/kotlin/Ktfmt.java). ```xml - 0.39 - + 0.51 + + 120 + 4 + 8 + false ``` diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/kotlin/Ktfmt.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/kotlin/Ktfmt.java index b917d33a44..7bd420f1ee 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/kotlin/Ktfmt.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/kotlin/Ktfmt.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * Copyright 2016-2024 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,13 +42,13 @@ public class Ktfmt implements FormatterStepFactory { private Integer continuationIndent; @Parameter - private Boolean removeUnusedImport; + private Boolean removeUnusedImports; @Override public FormatterStep newFormatterStep(FormatterStepConfig config) { String version = this.version != null ? this.version : KtfmtStep.defaultVersion(); Style style = this.style != null ? Style.valueOf(this.style) : null; - KtfmtFormattingOptions options = new KtfmtFormattingOptions(maxWidth, blockIndent, continuationIndent, removeUnusedImport); + KtfmtFormattingOptions options = new KtfmtFormattingOptions(maxWidth, blockIndent, continuationIndent, removeUnusedImports); return KtfmtStep.create(version, config.getProvisioner(), style, options); } } diff --git a/plugin-maven/src/test/java/com/diffplug/spotless/maven/kotlin/KtfmtTest.java b/plugin-maven/src/test/java/com/diffplug/spotless/maven/kotlin/KtfmtTest.java index e452c5d56b..48b43bfcb1 100644 --- a/plugin-maven/src/test/java/com/diffplug/spotless/maven/kotlin/KtfmtTest.java +++ b/plugin-maven/src/test/java/com/diffplug/spotless/maven/kotlin/KtfmtTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 DiffPlug + * Copyright 2016-2024 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ void testContinuation() throws Exception { @Test void testKtfmtStyle() throws Exception { - writePomWithKotlinSteps(""); + writePomWithKotlinSteps("0.50"); setFile("src/main/kotlin/main.kt").toResource("kotlin/ktfmt/basic.dirty"); mavenRunner().withArguments("spotless:apply").runNoError(); @@ -65,7 +65,7 @@ void testKtfmtWithMaxWidthOption() throws Exception { @Test void testKtfmtStyleWithMaxWidthOption() throws Exception { - writePomWithKotlinSteps("120"); + writePomWithKotlinSteps("0.17120"); setFile("src/main/kotlin/main.kt").toResource("kotlin/ktfmt/max-width.dirty"); mavenRunner().withArguments("spotless:apply").runNoError(); diff --git a/testlib/build.gradle b/testlib/build.gradle index 38e598369e..08e735bced 100644 --- a/testlib/build.gradle +++ b/testlib/build.gradle @@ -30,9 +30,10 @@ spotbugs { apply from: rootProject.file('gradle/special-tests.gradle') tasks.withType(Test).configureEach { if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_16)) { - // for Antlr4FormatterStepTest and KtLintStepTest + // for Antlr4FormatterStepTest, KtfmtStepTest, and KtLintStepTest def args = [ - '--add-opens=java.base/java.lang=ALL-UNNAMED' + '--add-opens=java.base/java.lang=ALL-UNNAMED', + '--add-opens=java.base/java.util=ALL-UNNAMED', ] jvmArgs args } diff --git a/testlib/src/test/java/com/diffplug/spotless/kotlin/KtfmtStepTest.java b/testlib/src/test/java/com/diffplug/spotless/kotlin/KtfmtStepTest.java index e9458c9d39..f4f6e0b651 100644 --- a/testlib/src/test/java/com/diffplug/spotless/kotlin/KtfmtStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/kotlin/KtfmtStepTest.java @@ -34,9 +34,31 @@ void behaviorWithOptions() { StepHarness.forStep(step).testResource("kotlin/ktfmt/basic.dirty", "kotlin/ktfmt/basic.clean"); } + @Test + void dropboxStyle_0_16() throws Exception { + KtfmtStep.KtfmtFormattingOptions options = new KtfmtStep.KtfmtFormattingOptions(); + FormatterStep step = KtfmtStep.create("0.16", TestProvisioner.mavenCentral(), KtfmtStep.Style.DROPBOX, options); + StepHarness.forStep(step).testResource("kotlin/ktfmt/basic.dirty", "kotlin/ktfmt/basic-dropboxstyle.clean"); + } + @Test void dropboxStyle_0_18() throws Exception { - FormatterStep step = KtfmtStep.create("0.18", TestProvisioner.mavenCentral(), KtfmtStep.Style.DROPBOX, null); + KtfmtStep.KtfmtFormattingOptions options = new KtfmtStep.KtfmtFormattingOptions(); + FormatterStep step = KtfmtStep.create("0.18", TestProvisioner.mavenCentral(), KtfmtStep.Style.DROPBOX, options); + StepHarness.forStep(step).testResource("kotlin/ktfmt/basic.dirty", "kotlin/ktfmt/basic-dropboxstyle.clean"); + } + + @Test + void dropboxStyle_0_22() throws Exception { + KtfmtStep.KtfmtFormattingOptions options = new KtfmtStep.KtfmtFormattingOptions(); + FormatterStep step = KtfmtStep.create("0.22", TestProvisioner.mavenCentral(), KtfmtStep.Style.DROPBOX, options); + StepHarness.forStep(step).testResource("kotlin/ktfmt/basic.dirty", "kotlin/ktfmt/basic-dropboxstyle.clean"); + } + + @Test + void dropboxStyle_0_50() throws Exception { + KtfmtStep.KtfmtFormattingOptions options = new KtfmtStep.KtfmtFormattingOptions(); + FormatterStep step = KtfmtStep.create("0.50", TestProvisioner.mavenCentral(), KtfmtStep.Style.DROPBOX, options); StepHarness.forStep(step).testResource("kotlin/ktfmt/basic.dirty", "kotlin/ktfmt/basic-dropboxstyle.clean"); }