Repository: jeremymailen/kotlinter-gradle (kotlin)

Issue:
Regression:  3.11.1 doesn't seem to read `disabled_rules` from .editorconfig anymore
After upgrading from 3.10.0 I had to specify `disabledRules` in the gradle `kotlinter { }` block instead of relying on `disabled_rules` in .editorconfig.

Pull request description:
Fixes #262

It seems like this was the scenario I mentioned in: https://github.com/jeremymailen/kotlinter-gradle/pull/246#discussion_r841261149 😅 

My proposal is to do basically the same thing as ktlint-gradle does 🤷 Maaybe if https://github.com/pinterest/ktlint/issues/1446 gets addressed we'll get appropriate api to know when something changes (or ktlint will be smart enough to track `.editorconfig` changes)

Patch:
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<File> {
     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 <reified T> ObjectFactory.property(default: T? = null): Property<T> =
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<Boolean> = 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<FormatWorkerParameters> {
     private val output: File? = parameters.output.asFile.orNull
 
     override fun execute() {
+        resetEditorconfigCacheIfNeeded(
+            changedEditorconfigFiles = parameters.changedEditorConfigFiles,
+            logger = logger,
+        )
+
         val fixes = mutableListOf<String>()
         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<String>
+    val changedEditorConfigFiles: ConfigurableFileCollection
     val files: ConfigurableFileCollection
     val projectDirectory: RegularFileProperty
     val ktLintParams: Property<KtLintParams>
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<LintWorkerParameters> {
     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<String>
+    val changedEditorconfigFiles: ConfigurableFileCollection
     val files: ConfigurableFileCollection
     val projectDirectory: RegularFileProperty
     val reporters: MapProperty<String, File>


Test patch:
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"))
+        }
+    }
 }