diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/EditorConfigUtils.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/EditorConfigUtils.kt index f78e559e..a415f1cb 100644 --- a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/EditorConfigUtils.kt +++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/EditorConfigUtils.kt @@ -1,8 +1,11 @@ package org.jmailen.gradle.kotlinter.support +import com.pinterest.ktlint.core.KtLint import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties import com.pinterest.ktlint.core.api.EditorConfigOverride +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.ProjectLayout +import org.gradle.api.logging.Logger import java.io.File internal fun editorConfigOverride(ktLintParams: KtLintParams): EditorConfigOverride { @@ -15,6 +18,17 @@ internal fun editorConfigOverride(ktLintParams: KtLintParams): EditorConfigOverr } } +internal fun resetEditorconfigCacheIfNeeded( + changedEditorconfigFiles: ConfigurableFileCollection, + logger: Logger, +) { + val changedFiles = changedEditorconfigFiles.files + if (changedFiles.any()) { + logger.info("Editorconfig changed, resetting KtLint caches") + changedFiles.map(File::toPath).forEach(KtLint::reloadEditorConfigFile) + } +} + internal fun ProjectLayout.findApplicableEditorConfigFiles(): Sequence { val projectEditorConfig = projectDirectory.file(".editorconfig").asFile diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/ConfigurableKtLintTask.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/ConfigurableKtLintTask.kt index 716b19f0..f8b46c35 100644 --- a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/ConfigurableKtLintTask.kt +++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/ConfigurableKtLintTask.kt @@ -13,6 +13,9 @@ import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.SourceTask import org.gradle.internal.exceptions.MultiCauseException +import org.gradle.work.FileChange +import org.gradle.work.Incremental +import org.gradle.work.InputChanges import org.jmailen.gradle.kotlinter.KotlinterExtension.Companion.DEFAULT_DISABLED_RULES import org.jmailen.gradle.kotlinter.KotlinterExtension.Companion.DEFAULT_EXPERIMENTAL_RULES import org.jmailen.gradle.kotlinter.support.KtLintParams @@ -31,6 +34,7 @@ abstract class ConfigurableKtLintTask( @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) + @get:Incremental internal val editorconfigFiles: FileCollection = objectFactory.fileCollection().apply { from(projectLayout.findApplicableEditorConfigFiles().toList()) } @@ -40,6 +44,13 @@ abstract class ConfigurableKtLintTask( experimentalRules = experimentalRules.get(), disabledRules = disabledRules.get(), ) + + protected fun getChangedEditorconfigFiles(inputChanges: InputChanges) = + if (inputChanges.isIncremental) { + inputChanges.getFileChanges(editorconfigFiles).map(FileChange::getFile) + } else { + emptyList() + } } internal inline fun ObjectFactory.property(default: T? = null): Property = diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/FormatTask.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/FormatTask.kt index cae4f0c6..53ba5c2d 100644 --- a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/FormatTask.kt +++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/FormatTask.kt @@ -7,6 +7,7 @@ import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction +import org.gradle.work.InputChanges import org.gradle.workers.WorkerExecutor import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty import org.jmailen.gradle.kotlinter.support.KotlinterError @@ -31,7 +32,7 @@ open class FormatTask @Inject constructor( } @TaskAction - fun run() { + fun run(inputChanges: InputChanges) { val result = with(workerExecutor.noIsolation()) { submit(FormatWorkerAction::class.java) { p -> p.name.set(name) @@ -39,6 +40,7 @@ open class FormatTask @Inject constructor( p.projectDirectory.set(projectLayout.projectDirectory.asFile) p.ktLintParams.set(getKtLintParams()) p.output.set(report) + p.changedEditorConfigFiles.from(getChangedEditorconfigFiles(inputChanges)) } runCatching { await() } } diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/LintTask.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/LintTask.kt index c0aa674d..ba03305b 100644 --- a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/LintTask.kt +++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/LintTask.kt @@ -13,6 +13,7 @@ import org.gradle.api.tasks.OutputFiles import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction +import org.gradle.work.InputChanges import org.gradle.workers.WorkerExecutor import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty import org.jmailen.gradle.kotlinter.KotlinterExtension.Companion.DEFAULT_IGNORE_FAILURES @@ -43,7 +44,7 @@ open class LintTask @Inject constructor( val ignoreFailures: Property = objectFactory.property(default = DEFAULT_IGNORE_FAILURES) @TaskAction - fun run() { + fun run(inputChanges: InputChanges) { val result = with(workerExecutor.noIsolation()) { submit(LintWorkerAction::class.java) { p -> p.name.set(name) @@ -51,6 +52,7 @@ open class LintTask @Inject constructor( p.projectDirectory.set(projectLayout.projectDirectory.asFile) p.reporters.putAll(reports) p.ktLintParams.set(getKtLintParams()) + p.changedEditorconfigFiles.from(getChangedEditorconfigFiles(inputChanges)) } runCatching { await() } } diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerAction.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerAction.kt index cbde073f..5b411fa6 100644 --- a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerAction.kt +++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerAction.kt @@ -12,6 +12,7 @@ import org.jmailen.gradle.kotlinter.support.KotlinterError import org.jmailen.gradle.kotlinter.support.KtLintParams import org.jmailen.gradle.kotlinter.support.defaultRuleSetProviders import org.jmailen.gradle.kotlinter.support.editorConfigOverride +import org.jmailen.gradle.kotlinter.support.resetEditorconfigCacheIfNeeded import org.jmailen.gradle.kotlinter.support.resolveRuleProviders import org.jmailen.gradle.kotlinter.tasks.FormatTask import java.io.File @@ -25,6 +26,11 @@ abstract class FormatWorkerAction : WorkAction { private val output: File? = parameters.output.asFile.orNull override fun execute() { + resetEditorconfigCacheIfNeeded( + changedEditorconfigFiles = parameters.changedEditorConfigFiles, + logger = logger, + ) + val fixes = mutableListOf() try { files.forEach { file -> diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerParameters.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerParameters.kt index 684b1c2e..d8a4b64b 100644 --- a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerParameters.kt +++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/format/FormatWorkerParameters.kt @@ -8,6 +8,7 @@ import org.jmailen.gradle.kotlinter.support.KtLintParams interface FormatWorkerParameters : WorkParameters { val name: Property + val changedEditorConfigFiles: ConfigurableFileCollection val files: ConfigurableFileCollection val projectDirectory: RegularFileProperty val ktLintParams: Property diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/lint/LintWorkerAction.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/lint/LintWorkerAction.kt index 9cd2ce0a..914792a0 100644 --- a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/lint/LintWorkerAction.kt +++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/lint/LintWorkerAction.kt @@ -15,6 +15,7 @@ import org.jmailen.gradle.kotlinter.support.defaultRuleSetProviders import org.jmailen.gradle.kotlinter.support.editorConfigOverride import org.jmailen.gradle.kotlinter.support.reporterFor import org.jmailen.gradle.kotlinter.support.reporterPathFor +import org.jmailen.gradle.kotlinter.support.resetEditorconfigCacheIfNeeded import org.jmailen.gradle.kotlinter.support.resolveRuleProviders import org.jmailen.gradle.kotlinter.tasks.LintTask import java.io.File @@ -30,6 +31,10 @@ abstract class LintWorkerAction : WorkAction { private val ktLintParams: KtLintParams = parameters.ktLintParams.get() override fun execute() { + resetEditorconfigCacheIfNeeded( + changedEditorconfigFiles = parameters.changedEditorconfigFiles, + logger = logger, + ) var hasError = false try { diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/lint/LintWorkerParameters.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/lint/LintWorkerParameters.kt index cd7def3b..82b375b8 100644 --- a/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/lint/LintWorkerParameters.kt +++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/tasks/lint/LintWorkerParameters.kt @@ -10,6 +10,7 @@ import java.io.File interface LintWorkerParameters : WorkParameters { val name: Property + val changedEditorconfigFiles: ConfigurableFileCollection val files: ConfigurableFileCollection val projectDirectory: RegularFileProperty val reporters: MapProperty diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/EditorConfigTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/EditorConfigTest.kt index 1215d979..0bac4dcf 100644 --- a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/EditorConfigTest.kt +++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/EditorConfigTest.kt @@ -1,10 +1,12 @@ package org.jmailen.gradle.kotlinter.functional import org.gradle.testkit.runner.TaskOutcome +import org.jmailen.gradle.kotlinter.functional.utils.editorConfig import org.jmailen.gradle.kotlinter.functional.utils.kotlinClass import org.jmailen.gradle.kotlinter.functional.utils.resolve import org.jmailen.gradle.kotlinter.functional.utils.settingsFile import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test @@ -55,9 +57,10 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() { fun `plugin respects disabled_rules set in editorconfig`() { projectRoot.resolve(".editorconfig") { appendText( + // language=editorconfig """ [*.{kt,kts}] - disabled_rules=filename + ktlint_disabled_rules = filename """.trimIndent(), ) } @@ -71,9 +74,10 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() { } @Test - fun `plugin respects 'indent_size' set in editorconfig`() { + fun `plugin respects 'indent_size' set in editorconfig`() { projectRoot.resolve(".editorconfig") { appendText( + // language=editorconfig """ [*.{kt,kts}] indent_size = 6 @@ -99,4 +103,75 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() { assertTrue(output.contains("[indent] Unexpected indentation (2) (should be 6)")) } } + + @Test + fun `editorconfig changes are taken into account on lint task re-runs`() { + projectRoot.resolve(".editorconfig") { + writeText( + // language=editorconfig + """ + [*.{kt,kts}] + ktlint_disabled_rules = filename + """.trimIndent(), + ) + } + projectRoot.resolve("src/main/kotlin/FileName.kt") { + writeText(kotlinClass("DifferentClassName")) + } + build("lintKotlin").apply { + assertEquals(TaskOutcome.SUCCESS, task(":lintKotlinMain")?.outcome) + assertFalse(output.contains("resetting KtLint caches")) + } + + projectRoot.resolve(".editorconfig") { + writeText(editorConfig) + } + buildAndFail("lintKotlin", "--info").apply { + assertEquals(TaskOutcome.FAILED, task(":lintKotlinMain")?.outcome) + assertTrue(output.contains("[filename] File 'FileName.kt' contains a single top level declaration")) + assertTrue(output.contains("resetting KtLint caches")) + } + + projectRoot.resolve("src/main/kotlin/FileName.kt") { + writeText(kotlinClass("FileName")) + } + build("lintKotlin").apply { + assertEquals(TaskOutcome.SUCCESS, task(":lintKotlinMain")?.outcome) + assertFalse(output.contains("resetting KtLint caches")) + } + build("lintKotlin").apply { + assertEquals(TaskOutcome.UP_TO_DATE, task(":lintKotlinMain")?.outcome) + assertFalse(output.contains("resetting KtLint caches")) + } + } + + @Test + fun `editorconfig changes are ignored for format task re-runs`() { + projectRoot.resolve(".editorconfig") { + writeText(editorConfig) + } + + projectRoot.resolve("src/main/kotlin/FileName.kt") { + writeText(kotlinClass("DifferentClassName")) + } + build("formatKotlin").apply { + assertEquals(TaskOutcome.SUCCESS, task(":formatKotlinMain")?.outcome) + assertTrue(output.contains("Format could not fix > [filename] File 'FileName.kt' contains a single top level declaration")) + } + + projectRoot.resolve(".editorconfig") { + writeText( + // language=editorconfig + """ + [*.{kt,kts}] + ktlint_disabled_rules = filename + """.trimIndent(), + ) + } + build("formatKotlin", "--info").apply { + assertEquals(TaskOutcome.SUCCESS, task(":formatKotlinMain")?.outcome) + assertTrue(output.contains("Format could not fix")) + assertFalse(output.contains("resetting KtLint caches")) + } + } }