Repository: jeremymailen/kotlinter-gradle
Pull request #330 (2023-05-25 06:54:34)

Upgrade to ktlint-0.49.1
A few twists and turns in the migration, but functionality unaffected for the most part.

Also
- Upgrade Kotlin and Gradle
- Fix docs issue #320


Patch:
diff --git a/.editorconfig b/.editorconfig
index 3902112..43deb25 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -6,4 +6,4 @@ indent_size = 4
 insert_final_newline = true
 ij_kotlin_allow_trailing_comma = true
 ij_kotlin_allow_trailing_comma_on_call_site = true
-ktlint_disabled_rules = filename
+ktlint_standard_filename = disabled
diff --git a/README.md b/README.md
index 4abe302..6ac7e6b 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ Available on the Gradle Plugins Portal: https://plugins.gradle.org/plugin/org.jm
 
 ```kotlin
 plugins {
-    id("org.jmailen.kotlinter") version "3.14.0"
+    id("org.jmailen.kotlinter") version "3.15.0"
 }
 ```
 
@@ -31,7 +31,7 @@ plugins {
 
 ```groovy
 plugins {
-    id "org.jmailen.kotlinter" version "3.14.0"
+    id "org.jmailen.kotlinter" version "3.15.0"
 }
 ```
 
@@ -45,7 +45,7 @@ Root `build.gradle.kts`
 
 ```kotlin
 plugins {
-    id("org.jmailen.kotlinter") version "3.14.0" apply false
+    id("org.jmailen.kotlinter") version "3.15.0" apply false
 }
 ```
 
@@ -65,7 +65,7 @@ Root `build.gradle`
 
 ```groovy
 plugins {
-    id 'org.jmailen.kotlinter' version "3.14.0" apply false
+    id 'org.jmailen.kotlinter' version "3.15.0" apply false
 }
 ```
 
@@ -81,14 +81,15 @@ plugins {
 
 ### Compatibility
 
-| kotlinter version  | min kotlin version | max kotlin version | min gradle version |
-|--------------------|--------------------|--------------------|--------------------|
-| 3.11.0+            | 1.7.0              | -                  | 7.0                |
-| 3.10.0+            | 1.6.20             | 1.6.21             | 7.0                |
-| 3.7.0+             | 1.5.31             | 1.6.10             | 7.0                |
-| 3.5.0+             | 1.5.0              | 1.5.30             | 6.8                |
-| 3.0.0+             | 1.4.0              | 1.4.30             | 6.8                |
-| 2.0.0+             | 1.3.0              | 1.3.30             | -                  |
+| kotlinter version | min kotlin version | max kotlin version | min gradle version |
+|------------------|--------------------|--------------------|--------------------|
+| 3.14.0+          | 1.8.0              | -                  | 7.6                |
+| 3.11.0+          | 1.7.0              | 1.7.22             | 7.0                |
+| 3.10.0+          | 1.6.20             | 1.6.21             | 7.0                |
+| 3.7.0+           | 1.5.31             | 1.6.10             | 7.0                |
+| 3.5.0+           | 1.5.0              | 1.5.30             | 6.8                |
+| 3.0.0+           | 1.4.0              | 1.4.30             | 6.8                |
+| 2.0.0+           | 1.3.0              | 1.3.30             | -                  |
 
 ### Features
 
@@ -282,14 +283,16 @@ buildscript {
     configurations.classpath {
         resolutionStrategy {
             force(
-                "com.pinterest.ktlint:ktlint-core:0.48.0",
-                "com.pinterest.ktlint:ktlint-reporter-checkstyle:0.48.0",
-                "com.pinterest.ktlint:ktlint-reporter-json:0.48.0",
-                "com.pinterest.ktlint:ktlint-reporter-html:0.48.0",
-                "com.pinterest.ktlint:ktlint-reporter-plain:0.48.0",
-                "com.pinterest.ktlint:ktlint-reporter-sarif:0.48.0",
-                "com.pinterest.ktlint:ktlint-ruleset-experimental:0.48.0",
-                "com.pinterest.ktlint:ktlint-ruleset-standard:0.48.0"
+                "com.pinterest.ktlint:ktlint-core:0.49.0",
+                "com.pinterest.ktlint:ktlint-rule-engine:0.49.0",
+                "com.pinterest.ktlint:ktlint-rule-engine-core:0.49.0",
+                "com.pinterest.ktlint:ktlint-cli-reporter:0.49.0",
+                "com.pinterest.ktlint:ktlint-reporter-checkstyle:0.49.0",
+                "com.pinterest.ktlint:ktlint-reporter-json:0.49.0",
+                "com.pinterest.ktlint:ktlint-reporter-html:0.49.0",
+                "com.pinterest.ktlint:ktlint-reporter-plain:0.49.0",
+                "com.pinterest.ktlint:ktlint-reporter-sarif:0.49.0",
+                "com.pinterest.ktlint:ktlint-ruleset-standard:0.49.0"
             )
         }
     }
@@ -306,14 +309,16 @@ buildscript {
     configurations.classpath {
         resolutionStrategy {
             force(
-                "com.pinterest.ktlint:ktlint-core:0.48.0",
-                "com.pinterest.ktlint:ktlint-reporter-checkstyle:0.48.0",
-                "com.pinterest.ktlint:ktlint-reporter-json:0.48.0",
-                "com.pinterest.ktlint:ktlint-reporter-html:0.48.0",
-                "com.pinterest.ktlint:ktlint-reporter-plain:0.48.0",
-                "com.pinterest.ktlint:ktlint-reporter-sarif:0.48.0",
-                "com.pinterest.ktlint:ktlint-ruleset-experimental:0.48.0",
-                "com.pinterest.ktlint:ktlint-ruleset-standard:0.48.0"
+                "com.pinterest.ktlint:ktlint-core:0.49.0",
+                "com.pinterest.ktlint:ktlint-rule-engine:0.49.0", 
+                "com.pinterest.ktlint:ktlint-rule-engine-core:0.49.0",
+                "com.pinterest.ktlint:ktlint-cli-reporter:0.49.0",
+                "com.pinterest.ktlint:ktlint-reporter-checkstyle:0.49.0",
+                "com.pinterest.ktlint:ktlint-reporter-json:0.49.0",
+                "com.pinterest.ktlint:ktlint-reporter-html:0.49.0",
+                "com.pinterest.ktlint:ktlint-reporter-plain:0.49.0",
+                "com.pinterest.ktlint:ktlint-reporter-sarif:0.49.0",
+                "com.pinterest.ktlint:ktlint-ruleset-standard:0.49.0"
             )
         }
     }
diff --git a/build.gradle.kts b/build.gradle.kts
index c900eed..530b683 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -2,11 +2,11 @@ import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
-    kotlin("jvm") version "1.8.10"
+    kotlin("jvm") version "1.8.21"
     id("com.gradle.plugin-publish") version "1.1.0"
     `java-gradle-plugin`
     `maven-publish`
-    id("org.jmailen.kotlinter") version "3.13.0"
+    id("org.jmailen.kotlinter") version "3.14.0"
     idea
 }
 
@@ -20,14 +20,14 @@ val githubUrl = "https://github.com/jeremymailen/kotlinter-gradle"
 val webUrl = "https://github.com/jeremymailen/kotlinter-gradle"
 val projectDescription = "Lint and formatting for Kotlin using ktlint with configuration-free setup on JVM and Android projects"
 
-version = "3.14.0"
+version = "3.15.0"
 group = "org.jmailen.gradle"
 description = projectDescription
 
 object Versions {
     const val androidTools = "7.3.1"
-    const val junit = "5.9.1"
-    const val ktlint = "0.48.2"
+    const val junit = "5.9.3"
+    const val ktlint = "0.49.1"
     const val mockitoKotlin = "4.1.0"
 }
 
@@ -55,12 +55,14 @@ dependencies {
 
     listOf(
         "ktlint-core",
+        "ktlint-rule-engine",
+        "ktlint-rule-engine-core",
+        "ktlint-cli-reporter",
         "ktlint-reporter-checkstyle",
         "ktlint-reporter-json",
         "ktlint-reporter-html",
         "ktlint-reporter-plain",
         "ktlint-reporter-sarif",
-        "ktlint-ruleset-experimental",
         "ktlint-ruleset-standard"
     ).forEach { module ->
         implementation("com.pinterest.ktlint:$module:${Versions.ktlint}")
@@ -68,7 +70,7 @@ dependencies {
 
     testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.junit}")
     testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.junit}")
-    testImplementation("commons-io:commons-io:2.11.0")
+    testImplementation("commons-io:commons-io:2.12.0")
     testImplementation("org.mockito.kotlin:mockito-kotlin:${Versions.mockitoKotlin}")
 }
 
@@ -113,7 +115,7 @@ tasks {
     }
 
     wrapper {
-        gradleVersion = "7.6"
+        gradleVersion = "8.1.1"
     }
 }
 
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f398c33..37aef8d 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
 networkTimeout=10000
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KtlintEngineUtils.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KtlintEngineUtils.kt
index 9985178..7c869d4 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KtlintEngineUtils.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/KtlintEngineUtils.kt
@@ -1,13 +1,11 @@
 package org.jmailen.gradle.kotlinter.support
 
-import com.pinterest.ktlint.core.KtLintRuleEngine
+import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine
 import org.gradle.api.file.ConfigurableFileCollection
 import org.gradle.api.logging.Logger
 import java.io.File
 
-// You must create a new KtLintRuleEngine per file with fresh rule providers.
-// Otherwise, KtLint errors on resolving rule enable/disable statements in .editorconfig
-internal fun createKtlintEngine() = KtLintRuleEngine(
+internal val ktlintEngine = KtLintRuleEngine(
     ruleProviders = resolveRuleProviders(defaultRuleSetProviders),
 )
 
@@ -15,12 +13,11 @@ internal fun resetEditorconfigCacheIfNeeded(
     changedEditorconfigFiles: ConfigurableFileCollection,
     logger: Logger,
 ) {
-    val engine = createKtlintEngine()
     val changedFiles = changedEditorconfigFiles.files
     if (changedFiles.any()) {
         logger.info("Editorconfig changed, resetting KtLint caches")
         changedFiles.map(File::toPath).forEach {
-            engine.reloadEditorConfigFile(it)
+            ktlintEngine.reloadEditorConfigFile(it)
         }
     }
 }
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/Reporters.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/Reporters.kt
index 840ee11..829f7ae 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/Reporters.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/Reporters.kt
@@ -1,11 +1,11 @@
 package org.jmailen.gradle.kotlinter.support
 
-import com.pinterest.ktlint.core.Reporter
-import com.pinterest.ktlint.reporter.checkstyle.CheckStyleReporter
-import com.pinterest.ktlint.reporter.html.HtmlReporter
-import com.pinterest.ktlint.reporter.json.JsonReporter
-import com.pinterest.ktlint.reporter.plain.PlainReporter
-import com.pinterest.ktlint.reporter.sarif.SarifReporter
+import com.pinterest.ktlint.cli.reporter.checkstyle.CheckStyleReporter
+import com.pinterest.ktlint.cli.reporter.core.api.ReporterV2
+import com.pinterest.ktlint.cli.reporter.html.HtmlReporter
+import com.pinterest.ktlint.cli.reporter.json.JsonReporter
+import com.pinterest.ktlint.cli.reporter.plain.PlainReporter
+import com.pinterest.ktlint.cli.reporter.sarif.SarifReporter
 import java.io.File
 import java.io.PrintStream
 
@@ -19,7 +19,7 @@ enum class ReporterType(val fileExtension: String) {
 }
 /* ktlint-enable enum-entry-name-case */
 
-fun reporterFor(reporterName: String, output: File): Reporter {
+fun reporterFor(reporterName: String, output: File): ReporterV2 {
     val out = PrintStream(output)
     return SortedThreadSafeReporterWrapper(
         when (ReporterType.valueOf(reporterName)) {
@@ -32,7 +32,7 @@ fun reporterFor(reporterName: String, output: File): Reporter {
     )
 }
 
-fun reporterPathFor(reporter: Reporter, output: File, projectDir: File): String {
+fun reporterPathFor(reporter: ReporterV2, output: File, projectDir: File): String {
     val unwrappedReporter = (reporter as? SortedThreadSafeReporterWrapper)?.unwrap() ?: reporter
     return when (unwrappedReporter) {
         is SarifReporter -> output.absolutePath
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/RuleSets.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/RuleSets.kt
index 7b3a156..66877c3 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/RuleSets.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/RuleSets.kt
@@ -1,26 +1,26 @@
 package org.jmailen.gradle.kotlinter.support
 
-import com.pinterest.ktlint.core.RuleProvider
-import com.pinterest.ktlint.core.RuleSetProviderV2
+import com.pinterest.ktlint.cli.ruleset.core.api.RuleSetProviderV3
+import com.pinterest.ktlint.rule.engine.core.api.RuleProvider
 import java.util.ServiceLoader
 
 internal fun resolveRuleProviders(
-    providers: Iterable<RuleSetProviderV2>,
+    providers: Iterable<RuleSetProviderV3>,
 ): Set<RuleProvider> = providers
     .asSequence()
     .sortedWith(
         compareBy {
-            when (it.id) {
+            when (it.id.value) {
                 "standard" -> 0
                 else -> 1
             }
         },
     )
-    .map(RuleSetProviderV2::getRuleProviders)
+    .map(RuleSetProviderV3::getRuleProviders)
     .flatten()
     .toSet()
 
 // statically resolve providers from plugin classpath. ServiceLoader#load alone resolves classes lazily which fails when run in parallel
 // https://github.com/jeremymailen/kotlinter-gradle/issues/101
-val defaultRuleSetProviders: List<RuleSetProviderV2> =
-    ServiceLoader.load(RuleSetProviderV2::class.java).toList()
+val defaultRuleSetProviders: List<RuleSetProviderV3> =
+    ServiceLoader.load(RuleSetProviderV3::class.java).toList()
diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/SortedThreadSafeReporterWrapper.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/SortedThreadSafeReporterWrapper.kt
index e5d5d26..c6dbe50 100644
--- a/src/main/kotlin/org/jmailen/gradle/kotlinter/support/SortedThreadSafeReporterWrapper.kt
+++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/support/SortedThreadSafeReporterWrapper.kt
@@ -1,7 +1,7 @@
 package org.jmailen.gradle.kotlinter.support
 
-import com.pinterest.ktlint.core.LintError
-import com.pinterest.ktlint.core.Reporter
+import com.pinterest.ktlint.cli.reporter.core.api.KtlintCliError
+import com.pinterest.ktlint.cli.reporter.core.api.ReporterV2
 import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.ConcurrentMap
 import java.util.concurrent.ConcurrentSkipListSet
@@ -11,11 +11,11 @@ import java.util.concurrent.ConcurrentSkipListSet
  * As a downside, the calls to the wrapped reporter are delayed until the end of the execution.
  */
 class SortedThreadSafeReporterWrapper(
-    private val wrapped: Reporter,
-) : Reporter {
+    private val wrapped: ReporterV2,
+) : ReporterV2 {
 
     private val callsToBefore: ConcurrentMap<String, Unit> = ConcurrentHashMap()
-    private val lintErrorReports: ConcurrentMap<String, ConcurrentSkipListSet<LintErrorReport>> = ConcurrentHashMap()
+    private val lintErrorReports: ConcurrentMap<String, ConcurrentSkipListSet<KtlintCliError>> = ConcurrentHashMap()
     private val callsToAfter: ConcurrentMap<String, Unit> = ConcurrentHashMap()
 
     override fun beforeAll() {
@@ -26,9 +26,17 @@ class SortedThreadSafeReporterWrapper(
         callsToBefore[file] = Unit
     }
 
-    override fun onLintError(file: String, err: LintError, corrected: Boolean) {
-        lintErrorReports.putIfAbsent(file, ConcurrentSkipListSet())
-        lintErrorReports[file]!!.add(LintErrorReport(err, corrected))
+    override fun onLintError(file: String, ktlintCliError: KtlintCliError) {
+        lintErrorReports.putIfAbsent(
+            file,
+            ConcurrentSkipListSet() { o1, o2 ->
+                when (o1.line == o2.line) {
+                    true -> o1.col.compareTo(o2.col)
+                    false -> o1.line.compareTo(o2.line)
+                }
+            },
+        )
+        lintErrorReports[file]!!.add(ktlintCliError)
     }
 
     override fun after(file: String) {
@@ -44,7 +52,7 @@ class SortedThreadSafeReporterWrapper(
                 }
                 lintErrorReports[fileName]?.let { lintErrorReports ->
                     lintErrorReports.forEach {
-                        wrapped.onLintError(fileName, it.lintError, it.corrected)
+                        wrapped.onLintError(fileName, it)
                     }
                 }
                 if (callsToAfter.contains(fileName)) {
@@ -56,14 +64,4 @@ class SortedThreadSafeReporterWrapper(
     }
 
     fun unwrap() = wrapped
-
-    private data class LintErrorReport(
-        val lintError: LintError,
-        val corrected: Boolean,
-    ) : Comparable<LintErrorReport> {
-        override fun compareTo(other: LintErrorReport) = when (lintError.line == other.lintError.line) {
-            true -> lintError.col.compareTo(other.lintError.col)
-            false -> lintError.line.compareTo(other.lintError.line)
-        }
-    }
 }
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 37e344a..ae88f57 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
@@ -1,13 +1,13 @@
 package org.jmailen.gradle.kotlinter.tasks.format
 
-import com.pinterest.ktlint.core.Code
+import com.pinterest.ktlint.rule.engine.api.Code
 import org.gradle.api.logging.LogLevel
 import org.gradle.api.logging.Logger
 import org.gradle.api.logging.Logging
 import org.gradle.internal.logging.slf4j.DefaultContextAwareTaskLogger
 import org.gradle.workers.WorkAction
 import org.jmailen.gradle.kotlinter.support.KotlinterError
-import org.jmailen.gradle.kotlinter.support.createKtlintEngine
+import org.jmailen.gradle.kotlinter.support.ktlintEngine
 import org.jmailen.gradle.kotlinter.support.resetEditorconfigCacheIfNeeded
 import org.jmailen.gradle.kotlinter.tasks.FormatTask
 import java.io.File
@@ -28,7 +28,6 @@ abstract class FormatWorkerAction : WorkAction<FormatWorkerParameters> {
 
         try {
             files.forEach { file ->
-                val ktLintEngine = createKtlintEngine()
 
                 val sourceText = file.readText()
                 val relativePath = file.toRelativeString(projectDirectory)
@@ -40,10 +39,10 @@ abstract class FormatWorkerAction : WorkAction<FormatWorkerParameters> {
                     return@forEach
                 }
 
-                val formattedText = ktLintEngine.format(Code.CodeFile(file)) { error, corrected ->
+                val formattedText = ktlintEngine.format(Code.fromFile(file)) { error, corrected ->
                     val msg = when (corrected) {
-                        true -> "${file.path}:${error.line}:${error.col}: Format fixed > [${error.ruleId}] ${error.detail}"
-                        false -> "${file.path}:${error.line}:${error.col}: Format could not fix > [${error.ruleId}] ${error.detail}"
+                        true -> "${file.path}:${error.line}:${error.col}: Format fixed > [${error.ruleId.value}] ${error.detail}"
+                        false -> "${file.path}:${error.line}:${error.col}: Format could not fix > [${error.ruleId.value}] ${error.detail}"
                     }
                     logger.warn(msg)
                     fixes.add(msg)
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 1ba6b76..5b8bf8f 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
@@ -1,14 +1,16 @@
 package org.jmailen.gradle.kotlinter.tasks.lint
 
-import com.pinterest.ktlint.core.Code
-import com.pinterest.ktlint.core.Reporter
+import com.pinterest.ktlint.cli.reporter.core.api.KtlintCliError
+import com.pinterest.ktlint.cli.reporter.core.api.ReporterV2
+import com.pinterest.ktlint.rule.engine.api.Code
+import com.pinterest.ktlint.rule.engine.api.LintError
 import org.gradle.api.logging.Logger
 import org.gradle.api.logging.Logging
 import org.gradle.internal.logging.slf4j.DefaultContextAwareTaskLogger
 import org.gradle.workers.WorkAction
 import org.jmailen.gradle.kotlinter.support.KotlinterError
 import org.jmailen.gradle.kotlinter.support.LintFailure
-import org.jmailen.gradle.kotlinter.support.createKtlintEngine
+import org.jmailen.gradle.kotlinter.support.ktlintEngine
 import org.jmailen.gradle.kotlinter.support.reporterFor
 import org.jmailen.gradle.kotlinter.support.reporterPathFor
 import org.jmailen.gradle.kotlinter.support.resetEditorconfigCacheIfNeeded
@@ -17,7 +19,7 @@ import java.io.File
 
 abstract class LintWorkerAction : WorkAction<LintWorkerParameters> {
     private val logger: Logger = DefaultContextAwareTaskLogger(Logging.getLogger(LintTask::class.java))
-    private val reporters: List<Reporter> = parameters.reporters.get().map { (reporterName, outputPath) ->
+    private val reporters: List<ReporterV2> = parameters.reporters.get().map { (reporterName, outputPath) ->
         reporterFor(reporterName, outputPath)
     }
     private val files: List<File> = parameters.files.toList()
@@ -34,7 +36,6 @@ abstract class LintWorkerAction : WorkAction<LintWorkerParameters> {
         try {
             reporters.onEach { it.beforeAll() }
             files.forEach { file ->
-                val ktLintEngine = createKtlintEngine()
 
                 val relativePath = file.toRelativeString(projectDirectory)
                 reporters.onEach { it.before(relativePath) }
@@ -45,14 +46,14 @@ abstract class LintWorkerAction : WorkAction<LintWorkerParameters> {
                     return@forEach
                 }
 
-                ktLintEngine.lint(Code.CodeFile(file)) { error ->
+                ktlintEngine.lint(Code.fromFile(file)) { error: LintError ->
                     hasError = true
                     reporters.onEach { reporter ->
                         // some reporters want relative paths, some want absolute
                         val filePath = reporterPathFor(reporter, file, projectDirectory)
-                        reporter.onLintError(filePath, error, false)
+                        reporter.onLintError(filePath, error.toCliError())
                     }
-                    logger.error("${file.path}:${error.line}:${error.col}: Lint error > [${error.ruleId}] ${error.detail}")
+                    logger.error("${file.path}:${error.line}:${error.col}: Lint error > [${error.ruleId.value}] ${error.detail}")
                 }
                 reporters.onEach { it.after(relativePath) }
             }
@@ -68,3 +69,15 @@ abstract class LintWorkerAction : WorkAction<LintWorkerParameters> {
 }
 
 private val supportedExtensions = setOf("kt", "kts")
+
+internal fun LintError.toCliError() = KtlintCliError(
+    line,
+    col,
+    ruleId.value,
+    detail,
+    if (canBeAutoCorrected) {
+        KtlintCliError.Status.LINT_CAN_BE_AUTOCORRECTED
+    } else {
+        KtlintCliError.Status.LINT_CAN_NOT_BE_AUTOCORRECTED
+    },
+)


Test patch:
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/CustomTaskTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/CustomTaskTest.kt
index de1266e..db8d151 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/CustomTaskTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/CustomTaskTest.kt
@@ -178,12 +178,12 @@ class CustomTaskTest : WithGradleTest.Kotlin() {
 
         buildAndFail("reportsEmpty").apply {
             assertEquals(TaskOutcome.FAILED, task(":reportsEmpty")?.outcome)
-            assertTrue(output.contains("[final-newline] File must end with a newline (\\n)"))
+            assertTrue(output.contains("[standard:final-newline] File must end with a newline (\\n)"))
             assertArrayEquals(emptyArray<String>(), projectRoot.resolve("build/reports/ktlint").list().orEmpty())
         }
         buildAndFail("reportsNotConfigured").apply {
             assertEquals(TaskOutcome.FAILED, task(":reportsNotConfigured")?.outcome)
-            assertTrue(output.contains("[final-newline] File must end with a newline (\\n)"))
+            assertTrue(output.contains("[standard:final-newline] File must end with a newline (\\n)"))
             assertArrayEquals(emptyArray<String>(), projectRoot.resolve("build/reports/ktlint").list().orEmpty())
         }
     }
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 9791281..5c6c3a3 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/EditorConfigTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/EditorConfigTest.kt
@@ -100,7 +100,7 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() {
 
         buildAndFail("lintKotlin").apply {
             assertEquals(TaskOutcome.FAILED, task(":lintKotlinMain")?.outcome)
-            assertTrue(output.contains("[indent] Unexpected indentation (2) (should be 6)"))
+            assertTrue(output.contains("[standard:indent] Unexpected indentation (2) (should be 6)"))
         }
     }
 
@@ -128,7 +128,7 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() {
         }
         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("[standard:filename] File 'FileName.kt' contains a single top level declaration"))
             assertTrue(output.contains("resetting KtLint caches"))
         }
 
@@ -156,7 +156,9 @@ internal class EditorConfigTest : WithGradleTest.Kotlin() {
         }
         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"))
+            assertTrue(
+                output.contains("Format could not fix > [standard:filename] File 'FileName.kt' contains a single top level declaration"),
+            )
         }
 
         projectRoot.resolve(".editorconfig") {
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinJsProjectTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinJsProjectTest.kt
index e7e6992..61a7908 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinJsProjectTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinJsProjectTest.kt
@@ -67,9 +67,13 @@ class KotlinJsProjectTest : WithGradleTest.Kotlin() {
 
         buildAndFail("lintKotlin", "--continue").apply {
             assertEquals(TaskOutcome.FAILED, task(":lintKotlinMain")?.outcome)
-            assertTrue(output.contains("Lint error > [filename] File 'FixtureFileName.kt' contains a single top level declaration"))
+            assertTrue(
+                output.contains("Lint error > [standard:filename] File 'FixtureFileName.kt' contains a single top level declaration"),
+            )
             assertEquals(TaskOutcome.FAILED, task(":lintKotlinTest")?.outcome)
-            assertTrue(output.contains("Lint error > [filename] File 'FixtureTestFileName.kt' contains a single top level declaration"))
+            assertTrue(
+                output.contains("Lint error > [standard:filename] File 'FixtureTestFileName.kt' contains a single top level declaration"),
+            )
         }
     }
 
@@ -105,11 +109,11 @@ class KotlinJsProjectTest : WithGradleTest.Kotlin() {
         }
         build("formatKotlin").apply {
             assertEquals(TaskOutcome.SUCCESS, task(":formatKotlinMain")?.outcome)
-            assertTrue(output.contains("FixtureClass.kt:3:19: Format fixed > [curly-spacing] Missing spacing before \"{\""))
-            assertTrue(output.contains("FixtureClass.kt:1:1: Format could not fix > [no-wildcard-imports] Wildcard import"))
+            assertTrue(output.contains("FixtureClass.kt:3:19: Format fixed > [standard:curly-spacing] Missing spacing before \"{\""))
+            assertTrue(output.contains("FixtureClass.kt:1:1: Format could not fix > [standard:no-wildcard-imports] Wildcard import"))
             assertEquals(TaskOutcome.SUCCESS, task(":formatKotlinTest")?.outcome)
-            assertTrue(output.contains("FixtureTestClass.kt:3:23: Format fixed > [curly-spacing] Missing spacing before \"{\""))
-            assertTrue(output.contains("FixtureTestClass.kt:1:1: Format could not fix > [no-wildcard-imports] Wildcard import"))
+            assertTrue(output.contains("FixtureTestClass.kt:3:23: Format fixed > [standard:curly-spacing] Missing spacing before \"{\""))
+            assertTrue(output.contains("FixtureTestClass.kt:1:1: Format could not fix > [standard:no-wildcard-imports] Wildcard import"))
         }
     }
 }
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinProjectTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinProjectTest.kt
index 8c70f0d..98db498 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinProjectTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/KotlinProjectTest.kt
@@ -45,8 +45,8 @@ internal class KotlinProjectTest : WithGradleTest.Kotlin() {
         )
 
         buildAndFail("lintKotlinMain").apply {
-            assertTrue(output.contains(".*$className.kt.* Lint error > \\[.*] Missing spacing before \"\\{\"".toRegex()))
-            assertTrue(output.contains(".*$className.kt.* Lint error > \\[.*] Unexpected spacing before \"\\(\"".toRegex()))
+            assertTrue(output.contains("$className.kt:2:21: Lint error > [standard:curly-spacing] Missing spacing before"))
+            assertTrue(output.contains("$className.kt:3:16: Lint error > [standard:paren-spacing] Unexpected spacing before"))
             output.lines().filter { it.contains("Lint error") }.forEach { line ->
                 val filePath = pathPattern.find(line)?.groups?.get(1)?.value.orEmpty()
                 assertTrue(File(filePath).exists())
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/support/RuleSetsTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/support/RuleSetsTest.kt
index b06e86d..08b50d5 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/support/RuleSetsTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/support/RuleSetsTest.kt
@@ -1,10 +1,12 @@
 package org.jmailen.gradle.kotlinter.support
 
-import com.pinterest.ktlint.core.Code
-import com.pinterest.ktlint.core.KtLintRuleEngine
-import com.pinterest.ktlint.core.Rule
-import com.pinterest.ktlint.core.RuleProvider
-import com.pinterest.ktlint.core.RuleSetProviderV2
+import com.pinterest.ktlint.cli.ruleset.core.api.RuleSetProviderV3
+import com.pinterest.ktlint.rule.engine.api.Code
+import com.pinterest.ktlint.rule.engine.api.KtLintRuleEngine
+import com.pinterest.ktlint.rule.engine.core.api.Rule
+import com.pinterest.ktlint.rule.engine.core.api.RuleId
+import com.pinterest.ktlint.rule.engine.core.api.RuleProvider
+import com.pinterest.ktlint.rule.engine.core.api.RuleSetId
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Assertions.assertTrue
 import org.junit.jupiter.api.Test
@@ -20,9 +22,9 @@ class RuleSetsTest {
 
     @Test
     fun `resolveRuleSets puts standard rules first`() {
-        val standard = TestRuleSetProvider("standard", setOf(TestRule("one")))
-        val extra1 = TestRuleSetProvider("extra-one", setOf(TestRule("two")))
-        val extra2 = TestRuleSetProvider("extra-two", setOf(TestRule("three")))
+        val standard = TestRuleSetProvider("standard", setOf(TestRule("custom:one")))
+        val extra1 = TestRuleSetProvider("extra-one", setOf(TestRule("custom:two")))
+        val extra2 = TestRuleSetProvider("extra-two", setOf(TestRule("custom:three")))
 
         val result = resolveRuleProviders(providers = listOf(extra2, standard, extra1)).map { it.createNewRuleInstance() }
 
@@ -36,7 +38,7 @@ class RuleSetsTest {
         KtLintRuleEngine(
             ruleProviders = resolveRuleProviders(defaultRuleSetProviders),
         ).lint(
-            Code.CodeSnippet(
+            Code.fromSnippet(
                 """
                 package test
 
@@ -53,17 +55,8 @@ class RuleSetsTest {
     }
 }
 
-class TestRuleSetProvider(id: String, val ruleSet: Set<Rule>) : RuleSetProviderV2(
-    id = id,
-    about = About(
-        maintainer = "stub-maintainer",
-        description = "stub-description",
-        license = "stub-license",
-        repositoryUrl = "stub-repositoryUrl",
-        issueTrackerUrl = "stub-issueTrackerUrl",
-    ),
-) {
+class TestRuleSetProvider(id: String, val ruleSet: Set<Rule>) : RuleSetProviderV3(RuleSetId(id)) {
     override fun getRuleProviders() = ruleSet.map { rule -> RuleProvider(provider = { rule }) }.toSet()
 }
 
-class TestRule(id: String) : Rule(id)
+class TestRule(id: String) : Rule(RuleId(id), About("maintainer"), emptySet(), emptySet())
diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/support/SortedThreadSafeReporterWrapperTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/support/SortedThreadSafeReporterWrapperTest.kt
index c1b32a2..588b852 100644
--- a/src/test/kotlin/org/jmailen/gradle/kotlinter/support/SortedThreadSafeReporterWrapperTest.kt
+++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/support/SortedThreadSafeReporterWrapperTest.kt
@@ -1,7 +1,9 @@
 package org.jmailen.gradle.kotlinter.support
 
-import com.pinterest.ktlint.core.LintError
-import com.pinterest.ktlint.core.Reporter
+import com.pinterest.ktlint.cli.reporter.core.api.ReporterV2
+import com.pinterest.ktlint.rule.engine.api.LintError
+import com.pinterest.ktlint.rule.engine.core.api.RuleId
+import org.jmailen.gradle.kotlinter.tasks.lint.toCliError
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
 import org.mockito.Mockito.verify
@@ -11,7 +13,7 @@ import org.mockito.kotlin.never
 
 class SortedThreadSafeReporterWrapperTest {
 
-    private lateinit var wrapped: Reporter
+    private lateinit var wrapped: ReporterV2
 
     private lateinit var reporter: SortedThreadSafeReporterWrapper
 
@@ -51,24 +53,25 @@ class SortedThreadSafeReporterWrapperTest {
     @Test
     fun onLintErrorIsNotDirectlyDelegated() {
         val fileName = "fileName"
-        val lintError = LintError(0, 0, "", "")
-        val corrected = false
+        val lintError = LintError(0, 0, RuleId("custom:x"), "", false)
+        val cliError = lintError.toCliError()
 
-        reporter.onLintError(fileName, lintError, corrected)
+        reporter.onLintError(fileName, cliError)
 
-        verify(wrapped, never()).onLintError(fileName, lintError, corrected)
+        verify(wrapped, never()).onLintError(fileName, cliError)
     }
 
     @Test
     fun onLintErrorIsDelegatedInAfterAll() {
         val fileName = "fileName"
-        val lintError = LintError(0, 0, "", "")
-        val corrected = false
-        reporter.onLintError(fileName, lintError, corrected)
+        val lintError = LintError(0, 0, RuleId("custom:x"), "", false)
+        val cliError = lintError.toCliError()
+
+        reporter.onLintError(fileName, cliError)
 
         reporter.afterAll()
 
-        verify(wrapped).onLintError(fileName, lintError, corrected)
+        verify(wrapped).onLintError(fileName, cliError)
     }
 
     @Test
@@ -93,32 +96,34 @@ class SortedThreadSafeReporterWrapperTest {
     @Test
     fun afterAllDelegatesInSortedOrder() {
         val firstFileName = "b"
-        val firstLintError = LintError(0, 0, "", "")
-        val firstCorrected = true
+        val firstLintError = LintError(0, 0, RuleId("custom:x"), "", true)
+            .toCliError()
         val secondFileName = "a"
-        val secondLintError = LintError(1, 0, "", "")
-        val secondLintError2 = LintError(3, 2, "", "")
-        val secondLintError3 = LintError(2, 6, "", "")
-        val secondCorrected = false
+        val secondLintError = LintError(1, 0, RuleId("custom:x"), "", false)
+            .toCliError()
+        val secondLintError2 = LintError(3, 2, RuleId("custom:x"), "", false)
+            .toCliError()
+        val secondLintError3 = LintError(2, 6, RuleId("custom:x"), "", false)
+            .toCliError()
         reporter.before(firstFileName)
-        reporter.onLintError(firstFileName, firstLintError, firstCorrected)
+        reporter.onLintError(firstFileName, firstLintError)
         reporter.after(firstFileName)
         reporter.before(secondFileName)
-        reporter.onLintError(secondFileName, secondLintError, secondCorrected)
-        reporter.onLintError(secondFileName, secondLintError2, secondCorrected)
-        reporter.onLintError(secondFileName, secondLintError3, secondCorrected)
+        reporter.onLintError(secondFileName, secondLintError)
+        reporter.onLintError(secondFileName, secondLintError2)
+        reporter.onLintError(secondFileName, secondLintError3)
         reporter.after(secondFileName)
 
         reporter.afterAll()
 
         val inOrder = inOrder(wrapped)
         inOrder.verify(wrapped).before(secondFileName)
-        inOrder.verify(wrapped).onLintError(secondFileName, secondLintError, secondCorrected)
-        inOrder.verify(wrapped).onLintError(secondFileName, secondLintError3, secondCorrected)
-        inOrder.verify(wrapped).onLintError(secondFileName, secondLintError2, secondCorrected)
+        inOrder.verify(wrapped).onLintError(secondFileName, secondLintError)
+        inOrder.verify(wrapped).onLintError(secondFileName, secondLintError3)
+        inOrder.verify(wrapped).onLintError(secondFileName, secondLintError2)
         inOrder.verify(wrapped).after(secondFileName)
         inOrder.verify(wrapped).before(firstFileName)
-        inOrder.verify(wrapped).onLintError(firstFileName, firstLintError, firstCorrected)
+        inOrder.verify(wrapped).onLintError(firstFileName, firstLintError)
         inOrder.verify(wrapped).after(firstFileName)
         inOrder.verify(wrapped).afterAll()
         inOrder.verifyNoMoreInteractions()