Skip to content

Commit

Permalink
Ktlint 1.0 support (fixes #706)
Browse files Browse the repository at this point in the history
Allow editorconfig overrides in ktlint 0.49+ (fixes #707)
  • Loading branch information
wakingrufus committed Sep 14, 2023
1 parent 059252a commit 89cd401
Show file tree
Hide file tree
Showing 10 changed files with 218 additions and 29 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

- ktlint 1.0 support [#708](https://github.com/JLLeitschuh/ktlint-gradle/pull/708)
- Allow editorconfig overrides in ktlint 0.49+ [#708](https://github.com/JLLeitschuh/ktlint-gradle/pull/708)
- update latest version text file manually [#700](https://github.com/JLLeitschuh/ktlint-gradle/pull/700)

## [11.5.1] - 2023-08-07
Expand Down
14 changes: 12 additions & 2 deletions plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ sourceSets {
val adapter50 by creating {
compileClasspath += adapter.output
}
val adapter1 by creating {
compileClasspath += adapter.output
}
val adapters = listOf(
adapter,
adapter34,
Expand All @@ -97,7 +100,8 @@ sourceSets {
adapter47,
adapter48,
adapter49,
adapter50
adapter50,
adapter1
)
val main by getting {
kotlin {
Expand All @@ -121,7 +125,8 @@ val adapterSources = listOf(
sourceSets.named("adapter47"),
sourceSets.named("adapter48"),
sourceSets.named("adapter49"),
sourceSets.named("adapter50")
sourceSets.named("adapter50"),
sourceSets.named("adapter1")
)
tasks.named<Jar>("shadowJar") {
this.from(adapterSources.map { sourceSet -> sourceSet.map { it.output.classesDirs } })
Expand Down Expand Up @@ -159,6 +164,11 @@ dependencies {
add("adapter50CompileOnly", "com.pinterest.ktlint:ktlint-ruleset-standard:0.50.0")
add("adapter50CompileOnly", "com.pinterest.ktlint:ktlint-reporter-baseline:0.50.0")

add("adapter1CompileOnly", "com.pinterest.ktlint:ktlint-cli-reporter-core:1.0.0")
add("adapter1CompileOnly", "com.pinterest.ktlint:ktlint-rule-engine:1.0.0")
add("adapter1CompileOnly", "com.pinterest.ktlint:ktlint-ruleset-standard:1.0.0")
add("adapter1CompileOnly", "com.pinterest.ktlint:ktlint-cli-reporter-baseline:1.0.0")

compileOnly(libs.kotlin.gradle.plugin)
compileOnly(libs.android.gradle.plugin)
compileOnly(kotlin("stdlib-jdk8"))
Expand Down
2 changes: 1 addition & 1 deletion plugin/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pluginManagement {
val latestRelease = file("VERSION_LATEST_RELEASE.txt").readText().trim()
plugins {
id("org.jlleitschuh.gradle.ktlint") version latestRelease
id("org.jetbrains.kotlin.jvm") version "1.7.21"
id("org.jetbrains.kotlin.jvm") version "1.9.0"
id("com.gradle.plugin-publish") version "0.15.0"
`java-gradle-plugin`
`maven-publish`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.jlleitschuh.gradle.ktlint.worker

import com.pinterest.ktlint.cli.ruleset.core.api.RuleSetProviderV3
import com.pinterest.ktlint.rule.engine.api.Code
import com.pinterest.ktlint.rule.engine.api.EditorConfigOverride
import com.pinterest.ktlint.rule.engine.api.EditorConfigPropertyRegistry
import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine
import com.pinterest.ktlint.rule.engine.api.LintError
import com.pinterest.ktlint.rule.engine.core.api.RuleProvider
import java.io.File
import java.util.ServiceLoader

class KtLintInvocation1(
private val engine: KtLintRuleEngine
) : KtLintInvocation {
companion object Factory : KtLintInvocationFactory {
fun initialize(editorConfigOverrides: Map<String, String>): KtLintInvocation {
val ruleProviders = loadRuleSetsFromClasspathWithRuleSetProviderV3()
val editorConfigPropertyRegistry = EditorConfigPropertyRegistry(ruleProviders)
val engine = if (editorConfigOverrides.isEmpty()) {
KtLintRuleEngine(ruleProviders = ruleProviders)
} else {
KtLintRuleEngine(
ruleProviders = ruleProviders,
editorConfigOverride = EditorConfigOverride.from(*editorConfigOverrides
.mapKeys { editorConfigPropertyRegistry.find(it.key) }
.entries
.map { it.key to it.value }
.toTypedArray())
)
}
return KtLintInvocation1(engine)
}

private fun loadRuleSetsFromClasspathWithRuleSetProviderV3(): Set<RuleProvider> {
return ServiceLoader
.load(RuleSetProviderV3::class.java)
.flatMap { it.getRuleProviders() }
.toSet()
}
}

override fun invokeLint(file: File): LintErrorResult {
val errors = mutableListOf<Pair<SerializableLintError, Boolean>>()
engine.lint(Code.fromFile(file)) { le: LintError ->
errors.add(le.toSerializable() to false)
}
return LintErrorResult(file, errors)
}

override fun invokeFormat(file: File): Pair<String, LintErrorResult> {
val errors = mutableListOf<Pair<SerializableLintError, Boolean>>()
val newCode =
engine.format(Code.fromFile(file)) { le, boolean ->
errors.add(le.toSerializable() to boolean)
}
return newCode to LintErrorResult(file, errors)
}

override fun trimMemory() {
engine.trimMemory()
}
}

internal fun LintError.toSerializable(): SerializableLintError {
return SerializableLintError(line, col, ruleId.value, detail, canBeAutoCorrected)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.jlleitschuh.gradle.ktlint
import net.swiftzer.semver.SemVer
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
import org.gradle.api.artifacts.component.ModuleComponentSelector
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
Expand Down Expand Up @@ -36,17 +37,29 @@ internal fun createKtlintConfiguration(target: Project, extension: KtlintExtensi
it.attribute(Bundling.BUNDLING_ATTRIBUTE, target.objects.named(Bundling::class.java, Bundling.EXTERNAL))
}

val dependencyProvider = target.provider {
val ktlintVersion = extension.version.get()
target.logger.info("Add dependency: ktlint version $ktlintVersion")
target.dependencies.create("com.pinterest:ktlint:$ktlintVersion")
}
dependencies.addLater(dependencyProvider)
// Workaround for gradle 6 https://github.com/gradle/gradle/issues/13255
val oldProp = target.objects.listProperty(Dependency::class.java)
dependencies.addAllLater(
oldProp.value(extension.version.map {
if (SemVer.parse(it) < SemVer(1, 0, 0)) {
target.logger.info("Add dependency: ktlint version $it")
listOf(target.dependencies.create("com.pinterest:ktlint:$it"))
} else {
target.logger.info("Add dependencies: ktlint version $it")
listOf(
target.dependencies.create("com.pinterest.ktlint:ktlint-cli:$it"),
// this transitive dep was introduced in ktlint 1.0, but for some reason, it is not picked up automatically
target.dependencies.create("io.github.oshai:kotlin-logging:5.1.0")
)
}
})
)
}

internal fun createKtlintRulesetConfiguration(
target: Project,
ktLintConfiguration: Configuration
ktLintConfiguration: Configuration,
extension: KtlintExtension
): Configuration = target
.configurations.maybeCreate(KTLINT_RULESET_CONFIGURATION_NAME).apply {
description = KTLINT_RULESET_CONFIGURATION_DESCRIPTION
Expand All @@ -56,6 +69,12 @@ internal fun createKtlintRulesetConfiguration(
isVisible = false

ensureConsistencyWith(target, ktLintConfiguration)
dependencies.addLater(
target.provider {
val ktlintVersion = extension.version.get()
target.dependencies.create("com.pinterest.ktlint:ktlint-ruleset-standard:$ktlintVersion")
}
)
}

internal fun createKtLintReporterConfiguration(
Expand Down Expand Up @@ -90,6 +109,21 @@ internal fun createKtLintReporterConfiguration(
)
}
}

// Workaround for gradle 6 https://github.com/gradle/gradle/issues/13255
val oldProp = target.objects.listProperty(Dependency::class.java)
dependencies.addAllLater(
oldProp.value(
extension.version.map { version ->
if (SemVer.parse(version) >= SemVer(1, 0, 0)) {
// this transitive dep was introduced in ktlint 1.0, but for some reason, it is not picked up automatically
listOf(target.dependencies.create("io.github.oshai:kotlin-logging:5.1.0"))
} else {
listOf()
}
}
)
)
}

internal fun createKtLintBaselineReporterConfiguration(
Expand All @@ -108,25 +142,33 @@ internal fun createKtLintBaselineReporterConfiguration(

ensureConsistencyWith(target, ktLintConfiguration)

withDependencies {
dependencies.addLater(
target.provider {
// withDependencies {
// Workaround for gradle 6 https://github.com/gradle/gradle/issues/13255
val oldProp = target.objects.listProperty(Dependency::class.java)
dependencies.addAllLater(
oldProp.value(
extension.version.map { version ->
val ktlintVersion = extension.version.get()
// Baseline reporter is only available starting 0.41.0 release
if (SemVer.parse(ktlintVersion) >= SemVer(0, 41, 0)) {
target.dependencies.create(
"com.pinterest.ktlint:ktlint-reporter-baseline:${extension.version.get()}"
val ktlintSemver = SemVer.parse(ktlintVersion)
if (ktlintSemver >= SemVer(1, 0, 0)) {
// Baseline reporter maven coordinates changed in 1.0
listOf(
target.dependencies.create("com.pinterest.ktlint:ktlint-cli-reporter-baseline:$version"),
// this transitive dep was introduced in ktlint 1.0, but for some reason, it is not picked up automatically
target.dependencies.create("io.github.oshai:kotlin-logging:5.1.0")
)
} else if (SemVer.parse(ktlintVersion) >= SemVer(0, 41, 0)) {
// Baseline reporter is only available starting 0.41.0 release
listOf(target.dependencies.create("com.pinterest.ktlint:ktlint-reporter-baseline:$version"))
} else {
// Adding fake plain reporter as addLater() does not accept `null` value
// Generate baseline tasks anyway will not run on KtLint versions < 0.41.0
target.dependencies.create(
"com.pinterest.ktlint:ktlint-reporter-plain:${extension.version.get()}"
)
listOf(target.dependencies.create("com.pinterest.ktlint:ktlint-reporter-plain:$version"))
}
}
)
}
)
// }
}

private fun Configuration.ensureConsistencyWith(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.jlleitschuh.gradle.ktlint.worker.BaselineLoader46
import org.jlleitschuh.gradle.ktlint.worker.BaselineLoader47
import org.jlleitschuh.gradle.ktlint.worker.BaselineLoader48
import org.jlleitschuh.gradle.ktlint.worker.BaselineLoader49
import org.jlleitschuh.gradle.ktlint.worker.KtLintInvocation1
import org.jlleitschuh.gradle.ktlint.worker.KtLintInvocation45
import org.jlleitschuh.gradle.ktlint.worker.KtLintInvocation46
import org.jlleitschuh.gradle.ktlint.worker.KtLintInvocation47
Expand All @@ -38,7 +39,7 @@ internal fun selectInvocation(version: String): KtLintInvocationFactory {
KtLintInvocation50
}
} else {
KtLintInvocation50
KtLintInvocation1
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,11 @@ open class KtlintPlugin : Plugin<Project> {
}

val ktlintConfiguration: Configuration = createKtlintConfiguration(target, extension)
val ktlintRulesetConfiguration: Configuration = createKtlintRulesetConfiguration(target, ktlintConfiguration)
val ktlintRulesetConfiguration: Configuration = createKtlintRulesetConfiguration(
target,
ktlintConfiguration,
extension
)
val ktlintReporterConfiguration: Configuration = createKtLintReporterConfiguration(target, extension, ktlintConfiguration)
val ktlintBaselineReporterConfiguration: Configuration = createKtLintBaselineReporterConfiguration(
target,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ abstract class KtLintWorkAction : WorkAction<KtLintWorkAction.KtLintWorkParamete
is KtLintInvocation50.Factory -> {
ktlintInvokerFactory.initialize(parameters.additionalEditorconfig.get())
}

is KtLintInvocation1.Factory -> {
ktlintInvokerFactory.initialize(parameters.additionalEditorconfig.get())
}

else -> {
throw GradleException("Incompatible ktlint version ${parameters.ktLintVersion}")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,30 +103,39 @@ class KtLintSupportedVersionsTest : AbstractPluginTest() {
}
}

@DisplayName("Lint should use editorconfig override")
@DisplayName("Lint should use editorconfig override (standard rule)")
@ParameterizedTest(name = "{0} with KtLint {1}: {displayName}")
@ArgumentsSource(SupportedKtlintVersionsProvider::class)
internal fun `Lint should use editorconfig override`(
internal fun `Lint should use editorconfig override standard rule`(
gradleVersion: GradleVersion,
ktLintVersion: String
) {
project(gradleVersion) {
//language=Groovy
buildGradle.appendText(
"""
ktlint.version = "$ktLintVersion"
ktlint.additionalEditorconfig = [
"ktlint_standard_no-multi-spaces": "disabled"
]
""".trimIndent()
)
withFailingSources()
if (SemVer.parse(ktLintVersion) < SemVer(0, 49)) {
if (SemVer.parse(ktLintVersion) < SemVer(0, 49, 0)) {
buildAndFail(CHECK_PARENT_TASK_NAME) {
assertThat(task(":$mainSourceSetCheckTaskName")?.outcome).isEqualTo(TaskOutcome.FAILED)
assertThat(task(":$mainSourceSetCheckTaskName")?.outcome)
.`as`("additionalEditorconfig not supported until ktlint 0.49")
.isEqualTo(TaskOutcome.FAILED)
assertThat(output).contains("additionalEditorconfig not supported until ktlint 0.49")
}
} else if (SemVer.parse(ktLintVersion) < SemVer(1, 0)) {
buildAndFail(CHECK_PARENT_TASK_NAME) {
assertThat(task(":runKtlintCheckOverMainSourceSet")?.outcome)
.`as`("standard rules not supported by additionalEditorconfig until 1.0")
.isEqualTo(TaskOutcome.FAILED)
assertThat(output)
.contains("Property with name 'ktlint_standard_no-multi-spaces' is not found")
}
} else {
build(CHECK_PARENT_TASK_NAME) {
assertThat(task(":$mainSourceSetCheckTaskName")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
Expand All @@ -135,6 +144,42 @@ class KtLintSupportedVersionsTest : AbstractPluginTest() {
}
}

@DisplayName("Lint should use editorconfig override")
@ParameterizedTest(name = "{0} with KtLint {1}: {displayName}")
@ArgumentsSource(SupportedKtlintVersionsProvider::class)
internal fun `Lint should use editorconfig override`(
gradleVersion: GradleVersion,
ktLintVersion: String
) {
project(gradleVersion) {
//language=Groovy
buildGradle.appendText(
"""
ktlint.version = "$ktLintVersion"
ktlint.additionalEditorconfig = [
"max_line_length": "20"
]
""".trimIndent()
)
withFailingMaxLineSources()
if (SemVer.parse(ktLintVersion) < SemVer(0, 49)) {
build(CHECK_PARENT_TASK_NAME) {
assertThat(task(":$mainSourceSetCheckTaskName")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
assertThat(output).contains("additionalEditorconfig not supported until ktlint 0.49")
}
} else {
buildAndFail(CHECK_PARENT_TASK_NAME) {
assertThat(task(":$mainSourceSetCheckTaskName")?.outcome)
.`as`("additionalEditorconfig takes effect")
.isEqualTo(TaskOutcome.FAILED)
assertThat(output).doesNotContain("additionalEditorconfig not supported until ktlint 0.49")
assertThat(output).contains("Exceeded max line length (20) (cannot be auto-corrected)")
}
}
}
}

@DisplayName("Format should successfully finish on sources with style violations")
@ParameterizedTest(name = "{0} with KtLint {1}: {displayName}")
@ArgumentsSource(SupportedKtlintVersionsProvider::class)
Expand Down Expand Up @@ -186,7 +231,8 @@ class KtLintSupportedVersionsTest : AbstractPluginTest() {
"0.48.2",
// "0.49.0" did not expose needed baseline classes
"0.49.1",
"0.50.0"
"0.50.0",
"1.0.0"
).also {
// "0.37.0" is failing on Windows machines that is fixed in the next version
if (!OS.WINDOWS.isCurrentOs) it.add("0.37.0")
Expand Down
Loading

0 comments on commit 89cd401

Please sign in to comment.