Skip to content

Commit

Permalink
Update ktlint to 0.46.1 and remove support for older versions (#1250)
Browse files Browse the repository at this point in the history
  • Loading branch information
nedtwigg authored Jun 30, 2022
2 parents d951b89 + 4befeaf commit aa9bb11
Show file tree
Hide file tree
Showing 14 changed files with 66 additions and 346 deletions.
8 changes: 5 additions & 3 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
## [Unreleased]
### Added
* Support for `MAC_CLASSIC` (`\r`) line ending ([#1243](https://github.com/diffplug/spotless/pull/1243) fixes [#1196](https://github.com/diffplug/spotless/issues/1196))
### Changed
* Minimum required `diktat` version bumped to `1.2.0` ([#1246](https://github.com/diffplug/spotless/pull/1246))
* Default bumped from `1.1.0` -> `1.2.0`
### Changes
* Bump default `ktlint` version to latest `0.45.2` -> `0.46.1` ([#1239](https://github.com/diffplug/spotless/issues/1239))
* Minimum supported version also bumped to `0.46.0` (we have abandoned strong backward compatibility for `ktlint`, from here on out Spotless will only support the most-recent breaking change).
* Bump default `diktat` version to latest `1.1.0` -> `1.2.0` ([#1246](https://github.com/diffplug/spotless/pull/1246))
* Minimum supported version also bumped to `1.2.0` (diktat is based on ktlint and has the same backward compatibility issues).

## [2.26.2] - 2022-06-11
### Fixed
Expand Down
2 changes: 1 addition & 1 deletion lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ dependencies {
}
}

String VER_KTLINT='0.45.2'
String VER_KTLINT='0.46.1'
ktlintCompileOnly "com.pinterest:ktlint:$VER_KTLINT"
ktlintCompileOnly "com.pinterest.ktlint:ktlint-core:$VER_KTLINT"
ktlintCompileOnly "com.pinterest.ktlint:ktlint-ruleset-experimental:$VER_KTLINT"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.jetbrains.annotations.NotNull;

import com.pinterest.ktlint.core.KtLint;
import com.pinterest.ktlint.core.KtLint.ExperimentalParams;
import com.pinterest.ktlint.core.LintError;
Expand All @@ -46,6 +48,7 @@ public class KtlintFormatterFunc implements FormatterFunc.NeedsFile {
private final Map<String, String> userData;
private final Function2<? super LintError, ? super Boolean, Unit> formatterCallback;
private final boolean isScript;
@NotNull
private final EditorConfigOverride editorConfigOverride;

/**
Expand All @@ -64,7 +67,7 @@ public KtlintFormatterFunc(boolean isScript, boolean useExperimental, Map<String
this.isScript = isScript;

if (editorConfigOverrideMap.isEmpty()) {
this.editorConfigOverride = null;
this.editorConfigOverride = EditorConfigOverride.Companion.getEmptyEditorConfigOverride();
} else {
this.editorConfigOverride = createEditorConfigOverride(editorConfigOverrideMap);
}
Expand All @@ -83,7 +86,7 @@ private EditorConfigOverride createEditorConfigOverride(Map<String, Object> edit

// Create a mapping of properties to their names based on rule properties and default properties
Map<String, UsesEditorConfigProperties.EditorConfigProperty<?>> supportedProperties = Stream
.concat(ruleProperties, DefaultEditorConfigProperties.INSTANCE.getDefaultEditorConfigProperties().stream())
.concat(ruleProperties, DefaultEditorConfigProperties.INSTANCE.getEditorConfigProperties().stream())
.distinct()
.collect(Collectors.toMap(property -> property.getType().getName(), property -> property));

Expand Down Expand Up @@ -116,31 +119,16 @@ public Unit invoke(LintError lint, Boolean corrected) {

@Override
public String applyWithFile(String unix, File file) throws Exception {

if (editorConfigOverride != null) {
// Use ExperimentalParams with EditorConfigOverride which requires KtLint 0.45.2
return KtLint.INSTANCE.format(new ExperimentalParams(
file.getName(),
unix,
rulesets,
userData,
formatterCallback,
isScript,
null,
false,
editorConfigOverride,
false));
} else {
// Use Params for backward compatibility
return KtLint.INSTANCE.format(new KtLint.Params(
file.getName(),
unix,
rulesets,
userData,
formatterCallback,
isScript,
null,
false));
}
return KtLint.INSTANCE.format(new ExperimentalParams(
file.getName(),
unix,
rulesets,
userData,
formatterCallback,
isScript,
null,
false,
editorConfigOverride,
false));
}
}
86 changes: 7 additions & 79 deletions lib/src/main/java/com/diffplug/spotless/kotlin/KtLintStep.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
Expand All @@ -31,18 +27,15 @@
import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.JarState;
import com.diffplug.spotless.Provisioner;
import com.diffplug.spotless.ThrowingEx;

/** Wraps up <a href="https://github.com/pinterest/ktlint">ktlint</a> as a FormatterStep. */
public class KtLintStep {
// prevent direct instantiation
private KtLintStep() {}

private static final String DEFAULT_VERSION = "0.45.2";
private static final String DEFAULT_VERSION = "0.46.1";
static final String NAME = "ktlint";
static final String PACKAGE_PRE_0_32 = "com.github.shyiko";
static final String PACKAGE = "com.pinterest";
static final String MAVEN_COORDINATE_PRE_0_32 = PACKAGE_PRE_0_32 + ":ktlint:";
static final String MAVEN_COORDINATE = PACKAGE + ":ktlint:";

public static FormatterStep create(Provisioner provisioner) {
Expand Down Expand Up @@ -85,95 +78,30 @@ static final class State implements Serializable {

/** Are the files being linted Kotlin script files. */
private final boolean isScript;
private final String pkg;
/** The jar that contains the formatter. */
final JarState jarState;
private final boolean useExperimental;
private final TreeMap<String, String> userData;
private final TreeMap<String, Object> editorConfigOverride;
private final boolean useParams;

State(String version, Provisioner provisioner, boolean isScript, boolean useExperimental,
Map<String, String> userData, Map<String, Object> editorConfigOverride) throws IOException {

if (!editorConfigOverride.isEmpty() &&
BadSemver.version(version) < BadSemver.version(0, 45, 2)) {
throw new IllegalStateException("KtLint editorConfigOverride supported for version 0.45.2 and later");
if (BadSemver.version(version) < BadSemver.version(0, 46, 0)) {
throw new IllegalStateException("KtLint versions < 0.46.0 not supported!");
}

this.useExperimental = useExperimental;
this.userData = new TreeMap<>(userData);
this.editorConfigOverride = new TreeMap<>(editorConfigOverride);
String coordinate;
if (BadSemver.version(version) < BadSemver.version(0, 32)) {
coordinate = MAVEN_COORDINATE_PRE_0_32;
this.pkg = PACKAGE_PRE_0_32;
} else {
coordinate = MAVEN_COORDINATE;
this.pkg = PACKAGE;
}
this.useParams = BadSemver.version(version) >= BadSemver.version(0, 34);
this.jarState = JarState.from(coordinate + version, provisioner);
this.jarState = JarState.from(MAVEN_COORDINATE + version, provisioner);
this.isScript = isScript;
}

FormatterFunc createFormat() throws Exception {
if (useParams) {
Class<?> formatterFunc = jarState.getClassLoader().loadClass("com.diffplug.spotless.glue.ktlint.KtlintFormatterFunc");
Constructor<?> constructor = formatterFunc.getConstructor(boolean.class, boolean.class, Map.class, Map.class);
return (FormatterFunc.NeedsFile) constructor.newInstance(isScript, useExperimental, userData, editorConfigOverride);
}

ClassLoader classLoader = jarState.getClassLoader();
// String KtLint::format(String input, Iterable<RuleSet> rules, Function2 errorCallback)

ArrayList<Object> ruleSets = new ArrayList<>();

// first, we get the standard rules
Class<?> standardRuleSetProviderClass = classLoader.loadClass(pkg + ".ktlint.ruleset.standard.StandardRuleSetProvider");
Object standardRuleSet = standardRuleSetProviderClass.getMethod("get").invoke(standardRuleSetProviderClass.newInstance());
ruleSets.add(standardRuleSet);

// second, we get the experimental rules if desired
if (useExperimental) {
Class<?> experimentalRuleSetProviderClass = classLoader.loadClass(pkg + ".ktlint.ruleset.experimental.ExperimentalRuleSetProvider");
Object experimentalRuleSet = experimentalRuleSetProviderClass.getMethod("get").invoke(experimentalRuleSetProviderClass.newInstance());
ruleSets.add(experimentalRuleSet);
}

// next, we create an error callback which throws an assertion error when the format is bad
Class<?> function2Interface = classLoader.loadClass("kotlin.jvm.functions.Function2");
Class<?> lintErrorClass = classLoader.loadClass(pkg + ".ktlint.core.LintError");
Method detailGetter = lintErrorClass.getMethod("getDetail");
Method lineGetter = lintErrorClass.getMethod("getLine");
Method colGetter = lintErrorClass.getMethod("getCol");
Object formatterCallback = Proxy.newProxyInstance(classLoader, new Class[]{function2Interface},
(proxy, method, args) -> {
Object lintError = args[0]; //ktlint.core.LintError
boolean corrected = (Boolean) args[1];
if (!corrected) {
String detail = (String) detailGetter.invoke(lintError);
int line = (Integer) lineGetter.invoke(lintError);
int col = (Integer) colGetter.invoke(lintError);
throw new AssertionError("Error on line: " + line + ", column: " + col + "\n" + detail);
}
return null;
});

// grab the KtLint singleton
Class<?> ktlintClass = classLoader.loadClass(pkg + ".ktlint.core.KtLint");
Object ktlint = ktlintClass.getDeclaredField("INSTANCE").get(null);

// and its format method
String formatterMethodName = isScript ? "formatScript" : "format";
Method formatterMethod = ktlintClass.getMethod(formatterMethodName, String.class, Iterable.class, Map.class, function2Interface);
return input -> {
try {
return (String) formatterMethod.invoke(ktlint, input, ruleSets, userData, formatterCallback);
} catch (InvocationTargetException e) {
throw ThrowingEx.unwrapCause(e);
}
};
Class<?> formatterFunc = jarState.getClassLoader().loadClass("com.diffplug.spotless.glue.ktlint.KtlintFormatterFunc");
Constructor<?> constructor = formatterFunc.getConstructor(boolean.class, boolean.class, Map.class, Map.class);
return (FormatterFunc.NeedsFile) constructor.newInstance(isScript, useExperimental, userData, editorConfigOverride);
}
}
}
8 changes: 5 additions & 3 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
## [Unreleased]
### Added
* Support for `MAC_CLASSIC` (`\r`) line ending ([#1243](https://github.com/diffplug/spotless/pull/1243) fixes [#1196](https://github.com/diffplug/spotless/issues/1196))
### Changed
* Minimum required `diktat` version bumped to `1.2.0` ([#1246](https://github.com/diffplug/spotless/pull/1246))
* Default bumped from `1.1.0` -> `1.2.0`
### Changes
* Bump default `ktlint` version to latest `0.45.2` -> `0.46.1` ([#1239](https://github.com/diffplug/spotless/issues/1239))
* Minimum supported version also bumped to `0.46.0` (we have abandoned strong backward compatibility for `ktlint`, from here on out Spotless will only support the most-recent breaking change).
* Bump default `diktat` version to latest `1.1.0` -> `1.2.0` ([#1246](https://github.com/diffplug/spotless/pull/1246))
* Minimum supported version also bumped to `1.2.0` (diktat is based on ktlint and has the same backward compatibility issues).

## [6.7.2] - 2022-06-11
### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,10 @@
*/
package com.diffplug.gradle.spotless;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.condition.JRE.JAVA_11;

import java.io.IOException;

import org.gradle.testkit.runner.BuildResult;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledForJreRange;

Expand All @@ -42,9 +39,9 @@ void integration() throws IOException {
" ktlint()",
" }",
"}");
setFile("src/main/kotlin/basic.kt").toResource("kotlin/ktlint/basic.dirty");
setFile("src/main/kotlin/Main.kt").toResource("kotlin/ktlint/basic.dirty");
gradleRunner().withArguments("spotlessApply").build();
assertFile("src/main/kotlin/basic.kt").sameAsResource("kotlin/ktlint/basic.clean");
assertFile("src/main/kotlin/Main.kt").sameAsResource("kotlin/ktlint/basic.clean");
}

@Test
Expand Down Expand Up @@ -132,12 +129,12 @@ void testWithIndentation() throws IOException {
"repositories { mavenCentral() }",
"spotless {",
" kotlin {",
" ktlint('0.32.0').userData(['indent_size': '6'])",
" ktlint().editorConfigOverride(['indent_size': '6'])",
" }",
"}");
setFile("src/main/kotlin/basic.kt").toResource("kotlin/ktlint/basic.dirty");
BuildResult result = gradleRunner().withArguments("spotlessApply").buildAndFail();
assertThat(result.getOutput()).contains("Unexpected indentation (4) (it should be 6)");
setFile("src/main/kotlin/Main.kt").toResource("kotlin/ktlint/basic.dirty");
gradleRunner().withArguments("spotlessApply").build();
assertFile("src/main/kotlin/Main.kt").sameAsResource("kotlin/ktlint/basic.clean-indent6");
}

@Test
Expand All @@ -153,27 +150,9 @@ void withExperimental() throws IOException {
" ktlint().setUseExperimental(true)",
" }",
"}");
setFile("src/main/kotlin/experimental.kt").toResource("kotlin/ktlint/experimental.dirty");
setFile("src/main/kotlin/Main.kt").toResource("kotlin/ktlint/experimental.dirty");
gradleRunner().withArguments("spotlessApply").build();
assertFile("src/main/kotlin/experimental.kt").sameAsResource("kotlin/ktlint/experimental.clean");
}

@Test
void withExperimental_0_32() throws IOException {
setFile("build.gradle").toLines(
"plugins {",
" id 'org.jetbrains.kotlin.jvm' version '1.5.31'",
" id 'com.diffplug.spotless'",
"}",
"repositories { mavenCentral() }",
"spotless {",
" kotlin {",
" ktlint('0.32.0').setUseExperimental(true)",
" }",
"}");
setFile("src/main/kotlin/basic.kt").toResource("kotlin/ktlint/basic.dirty");
gradleRunner().withArguments("spotlessApply").build();
assertFile("src/main/kotlin/basic.kt").sameAsResource("kotlin/ktlint/basic.clean");
assertFile("src/main/kotlin/Main.kt").sameAsResource("kotlin/ktlint/experimental.clean");
}

@Test
Expand All @@ -193,56 +172,9 @@ void withExperimentalEditorConfigOverride() throws IOException {
" ])",
" }",
"}");
setFile("src/main/kotlin/experimental.kt").toResource("kotlin/ktlint/experimentalEditorConfigOverride.dirty");
gradleRunner().withArguments("spotlessApply").build();
assertFile("src/main/kotlin/experimental.kt").sameAsResource("kotlin/ktlint/experimentalEditorConfigOverride.clean");
}

@Test
void withEditorConfigOverride_0_45_1() throws IOException {
setFile("build.gradle").toLines(
"plugins {",
" id 'org.jetbrains.kotlin.jvm' version '1.5.31'",
" id 'com.diffplug.spotless'",
"}",
"repositories { mavenCentral() }",
"spotless {",
" kotlin {",
" ktlint('0.45.1')",
" .editorConfigOverride([",
" indent_size: 5",
" ])",
" }",
"}");
setFile("src/main/kotlin/basic.kt").toResource("kotlin/ktlint/basic.dirty");
Throwable error = assertThrows(Throwable.class,
() -> gradleRunner().withArguments("spotlessApply").build());
assertThat(error).hasMessageContaining("KtLint editorConfigOverride supported for version 0.45.2 and later");
}

/**
* Check that the sample used to verify the experimental ruleset is untouched by the default ruleset, to verify
* that enabling the experimental ruleset is actually doing something.
*
* If this test fails, it's likely that the experimental rule being used as a test graduated into the standard
* ruleset, and therefore a new experimental rule should be used to verify functionality.
*/
@Test
void experimentalSampleUnchangedWithDefaultRuleset() throws IOException {
setFile("build.gradle").toLines(
"plugins {",
" id 'org.jetbrains.kotlin.jvm' version '1.5.31'",
" id 'com.diffplug.spotless'",
"}",
"repositories { mavenCentral() }",
"spotless {",
" kotlin {",
" ktlint()",
" }",
"}");
setFile("src/main/kotlin/experimental.kt").toResource("kotlin/ktlint/experimental.dirty");
setFile("src/main/kotlin/Main.kt").toResource("kotlin/ktlint/experimentalEditorConfigOverride.dirty");
gradleRunner().withArguments("spotlessApply").build();
assertFile("src/main/kotlin/experimental.kt").sameAsResource("kotlin/ktlint/experimental.dirty");
assertFile("src/main/kotlin/Main.kt").sameAsResource("kotlin/ktlint/experimentalEditorConfigOverride.clean");
}

@Test
Expand Down
Loading

0 comments on commit aa9bb11

Please sign in to comment.