diff --git a/CHANGES.md b/CHANGES.md index 9a80593347..65801d8b59 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,9 +10,10 @@ 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] +### Changed +* Converted `ktlint` integration to use a compile-only source set. ([#524](https://github.com/diffplug/spotless/issues/524)) ## [2.20.1] - 2021-12-01 - ### Changed * Added `named` option to `licenseHeader` to support alternate license header within same format (like java) ([872](https://github.com/diffplug/spotless/issues/872)). * Added `onlyIfContentMatches` option to `licenseHeader` to skip license header application based on source file content pattern ([#650](https://github.com/diffplug/spotless/issues/650)). diff --git a/lib/build.gradle b/lib/build.gradle index 7120c21aea..c456a44fbf 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -7,7 +7,8 @@ apply from: rootProject.file('gradle/java-setup.gradle') apply from: rootProject.file('gradle/java-publish.gradle') def NEEDS_GLUE = [ - 'sortPom' + 'sortPom', + 'ktlint' ] for (glue in NEEDS_GLUE) { sourceSets.register(glue) { @@ -20,12 +21,17 @@ for (glue in NEEDS_GLUE) { dependencies { // zero runtime reqs is a hard requirements for spotless-lib // if you need a dep, put it in lib-extra - testImplementation "org.junit.jupiter:junit-jupiter:${VER_JUNIT}" - testImplementation "org.assertj:assertj-core:${VER_ASSERTJ}" - testImplementation "com.diffplug.durian:durian-testlib:${VER_DURIAN}" + testImplementation "org.junit.jupiter:junit-jupiter:$VER_JUNIT" + testImplementation "org.assertj:assertj-core:$VER_ASSERTJ" + testImplementation "com.diffplug.durian:durian-testlib:$VER_DURIAN" // used for pom sorting sortPomCompileOnly 'com.github.ekryd.sortpom:sortpom-sorter:3.0.0' + + String VER_KTLINT='0.43.1' + ktlintCompileOnly "com.pinterest:ktlint:$VER_KTLINT" + ktlintCompileOnly "com.pinterest.ktlint:ktlint-core:$VER_KTLINT" + ktlintCompileOnly "com.pinterest.ktlint:ktlint-ruleset-standard:$VER_KTLINT" } // we'll hold the core lib to a high standard diff --git a/lib/src/ktlint/java/com/diffplug/spotless/glue/ktlint/KtlintFormatterFunc.java b/lib/src/ktlint/java/com/diffplug/spotless/glue/ktlint/KtlintFormatterFunc.java new file mode 100644 index 0000000000..3c47456dce --- /dev/null +++ b/lib/src/ktlint/java/com/diffplug/spotless/glue/ktlint/KtlintFormatterFunc.java @@ -0,0 +1,70 @@ +/* + * Copyright 2021 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.glue.ktlint; + +import java.io.File; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +import com.pinterest.ktlint.core.KtLint; +import com.pinterest.ktlint.core.KtLint.Params; +import com.pinterest.ktlint.core.LintError; +import com.pinterest.ktlint.core.RuleSet; +import com.pinterest.ktlint.ruleset.standard.StandardRuleSetProvider; + +import com.diffplug.spotless.FormatterFunc; + +import kotlin.Unit; +import kotlin.jvm.functions.Function2; + +public class KtlintFormatterFunc implements FormatterFunc.NeedsFile { + private static final Logger logger = Logger.getLogger(KtlintFormatterFunc.class.getName()); + + private final List rulesets; + private final Map userData; + private final Function2 formatterCallback; + private final boolean isScript; + + public KtlintFormatterFunc(boolean isScript, Map userData) { + rulesets = Collections.singletonList(new StandardRuleSetProvider().get()); + this.userData = userData; + formatterCallback = new Function2() { + @Override + public Unit invoke(LintError lint, Boolean corrected) { + if (!corrected) { + throw new AssertionError("Error on line: " + lint.getLine() + ", column: " + lint.getCol() + "\n" + lint.getDetail()); + } + return null; + } + }; + this.isScript = isScript; + } + + @Override + public String applyWithFile(String unix, File file) throws Exception { + return KtLint.INSTANCE.format(new Params( + file.getName(), + unix, + rulesets, + userData, + formatterCallback, + isScript, + null, + false)); + } +} diff --git a/lib/src/main/java/com/diffplug/spotless/kotlin/KtLintStep.java b/lib/src/main/java/com/diffplug/spotless/kotlin/KtLintStep.java index ebe748882a..f68ce0743a 100644 --- a/lib/src/main/java/com/diffplug/spotless/kotlin/KtLintStep.java +++ b/lib/src/main/java/com/diffplug/spotless/kotlin/KtLintStep.java @@ -103,8 +103,13 @@ static final class State implements Serializable { } FormatterFunc createFormat() throws Exception { - ClassLoader classLoader = jarState.getClassLoader(); + if (useParams) { + Class formatterFunc = jarState.getClassLoader().loadClass("com.diffplug.spotless.glue.ktlint.KtlintFormatterFunc"); + Constructor constructor = formatterFunc.getConstructor(boolean.class, Map.class); + return (FormatterFunc.NeedsFile) constructor.newInstance(isScript, userData); + } + ClassLoader classLoader = jarState.getClassLoader(); // String KtLint::format(String input, Iterable rules, Function2 errorCallback) // first, we get the standard rules @@ -134,57 +139,17 @@ FormatterFunc createFormat() throws Exception { // grab the KtLint singleton Class ktlintClass = classLoader.loadClass(pkg + ".ktlint.core.KtLint"); Object ktlint = ktlintClass.getDeclaredField("INSTANCE").get(null); - FormatterFunc formatterFunc; - if (useParams) { - // - // In KtLint 0.34+ there is a new "format(params: Params)" function. We create an - // instance of the Params class with our configuration and invoke it here. - // - - // grab the Params class - Class paramsClass = classLoader.loadClass(pkg + ".ktlint.core.KtLint$Params"); - // and its constructor - Constructor constructor = paramsClass.getConstructor( - /* fileName, nullable */ String.class, - /* text */ String.class, - /* ruleSets */ Iterable.class, - /* userData */ Map.class, - /* callback */ function2Interface, - /* script */ boolean.class, - /* editorConfigPath, nullable */ String.class, - /* debug */ boolean.class); - Method formatterMethod = ktlintClass.getMethod("format", paramsClass); - FormatterFunc.NeedsFile needsFile = (input, file) -> { - try { - Object params = constructor.newInstance( - /* fileName, nullable */ file.getName(), - /* text */ input, - /* ruleSets */ ruleSets, - /* userData */ userData, - /* callback */ formatterCallback, - /* script */ isScript, - /* editorConfigPath, nullable */ null, - /* debug */ false); - return (String) formatterMethod.invoke(ktlint, params); - } catch (InvocationTargetException e) { - throw ThrowingEx.unwrapCause(e); - } - }; - formatterFunc = FormatterFunc.needsFile(needsFile); - } else { - // and its format method - String formatterMethodName = isScript ? "formatScript" : "format"; - Method formatterMethod = ktlintClass.getMethod(formatterMethodName, String.class, Iterable.class, Map.class, function2Interface); - formatterFunc = input -> { - try { - return (String) formatterMethod.invoke(ktlint, input, ruleSets, userData, formatterCallback); - } catch (InvocationTargetException e) { - throw ThrowingEx.unwrapCause(e); - } - }; - } - return formatterFunc; + // 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); + } + }; } } }