From 7c9d0b33bcf8808b51222bac9474ba2d06dd3dd0 Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Sun, 24 Mar 2024 17:57:16 +0100 Subject: [PATCH] Support multiple versions of ktlint standard ruleset (#487) * Support multiple versions of ktlint standard ruleset The StandardRulesetProvider from each supported version of ktlint is transformed with shadow plugin to a unique class so that multiple versions of the StandardRulesetProvider class can be compiled into the plugin. Closes #426 Closes #35 * Increase build memory (cherry picked from commit 516955b7e030d004848f2e8e99ca398ce2f6d230) --- README.md | 2 +- gradle.properties | 4 +- gradle/libs.versions.toml | 1 - ktlint-lib/README.md | 11 ++++ {lib => ktlint-lib/core}/build.gradle.kts | 1 - ktlint-lib/ruleset-0-50-0/build.gradle.kts | 46 ++++++++++++++++ ktlint-lib/ruleset-1-0-1/build.gradle.kts | 39 ++++++++++++++ ktlint-lib/ruleset-1-1-1/build.gradle.kts | 39 ++++++++++++++ ktlint-lib/ruleset-1-2-1/build.gradle.kts | 39 ++++++++++++++ lib/README.md | 10 ---- plugin/build.gradle.kts | 20 +++++-- .../com/nbadal/ktlint/KtlintConfigForm.form | 33 ++++++++++-- .../com/nbadal/ktlint/KtlintConfigForm.kt | 25 ++++++++- .../com/nbadal/ktlint/KtlintConfigStorage.kt | 50 +++++++++++------ .../nbadal/ktlint/KtlintLoadRuleProviders.kt | 16 ++---- .../com/nbadal/ktlint/KtlintRulesetVersion.kt | 53 +++++++++++++++++++ plugin/src/main/resources/strings.properties | 1 + settings.gradle.kts | 9 +++- 18 files changed, 343 insertions(+), 56 deletions(-) create mode 100644 ktlint-lib/README.md rename {lib => ktlint-lib/core}/build.gradle.kts (98%) create mode 100644 ktlint-lib/ruleset-0-50-0/build.gradle.kts create mode 100644 ktlint-lib/ruleset-1-0-1/build.gradle.kts create mode 100644 ktlint-lib/ruleset-1-1-1/build.gradle.kts create mode 100644 ktlint-lib/ruleset-1-2-1/build.gradle.kts delete mode 100644 lib/README.md create mode 100644 plugin/src/main/kotlin/com/nbadal/ktlint/KtlintRulesetVersion.kt diff --git a/README.md b/README.md index 8a00bfbb..c50a37d8 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Contrary to the [default plugin setup](https://github.com/JetBrains/intellij-platform-plugin-template) this plugin is set up as a multi-module project. This is required as the Ktlint artifact for the KtlintRuleEngine encloses the embeddable Kotlin compiler which conflicts with the IDE compiler. -The "lib" project relocates the conflicting classes. The "plugin" project uses the "lib" to include the (modified) Ktlint artifacts. +The "ktlint-lib" project relocates the conflicting classes, and provides the different versions of the rulesets. The "plugin" project uses the "lib" to include the (modified) Ktlint artifacts. ## Installation diff --git a/gradle.properties b/gradle.properties index b2dcb22c..aaefd8d5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ pluginRepositoryUrl = https://github.com/nbadal/ktlint-intellij-plugin # other value. See https://plugins.jetbrains.com/docs/intellij/publishing-plugin.html#specifying-a-release-channel # Users need to specify an additional repository to pick up publications from the non default channel. For example: # - https://plugins.jetbrains.com/plugins/list?channel=beta&pluginId=com.nbadal.ktlint -pluginVersion = 0.22.0 +pluginVersion = 0.23.0-beta-1 # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html pluginSinceBuild = 213 @@ -38,3 +38,5 @@ org.gradle.caching = true # Enable Gradle Kotlin DSL Lazy Property Assignment -> https://docs.gradle.org/current/userguide/kotlin_dsl.html#kotdsl:assignment systemProp.org.gradle.unsafe.kotlin.assignment = true + +org.gradle.jvmargs=-Xmx4g -Xms1g "-XX:MaxMetaspaceSize=1g" \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cab3126c..45140c4b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,7 +18,6 @@ ktlintRuleEngine = { group = "com.pinterest.ktlint", name = "ktlint-rule-engine ktlintCliRulesetCore = { group = "com.pinterest.ktlint", name = "ktlint-cli-ruleset-core", version.ref = "ktlint" } ktlintCliReporterCore = { group = "com.pinterest.ktlint", name = "ktlint-cli-reporter-core", version.ref = "ktlint" } ktlintCliReporterBaselineCore = { group = "com.pinterest.ktlint", name = "ktlint-cli-reporter-baseline", version.ref = "ktlint" } -ktlintRulesetStandard = { group = "com.pinterest.ktlint", name = "ktlint-ruleset-standard", version.ref = "ktlint" } [plugins] buildconfig = { id = "com.github.gmazzo.buildconfig", version.ref = "buildconfig" } # BuildConfig - read more: https://github.com/gmazzo/gradle-buildconfig-plugin diff --git a/ktlint-lib/README.md b/ktlint-lib/README.md new file mode 100644 index 00000000..2281265b --- /dev/null +++ b/ktlint-lib/README.md @@ -0,0 +1,11 @@ +# Why is this a module? + +The `ktlint-lib` contains several libraries to isolate functionalities from Ktlint in such a way that it does not conflict with the plugin. + +## Core + +The Ktlint RuleEngine core module requires certain elements of the kotlin compiler. As of that it includes a dependency on the embedded kotlin compiler library. This clashes and conflicts with classes we use in the JetBrains Kotlin plugin. + +## Ruleset + +Each ruleset library transforms the StandardRuleSetProvider of that version to a unique class name so that multiple versions of the Standard rule sets can be supported by the plugin. The plugin allows the user to configure one of the supported ktlint versios. In this way, the user can keep the configuration of the ktlint intellij plugin in sync with other plugins like the ktlint gradle plugin or kotlinter. diff --git a/lib/build.gradle.kts b/ktlint-lib/core/build.gradle.kts similarity index 98% rename from lib/build.gradle.kts rename to ktlint-lib/core/build.gradle.kts index ba3d73d9..be5d8f92 100644 --- a/lib/build.gradle.kts +++ b/ktlint-lib/core/build.gradle.kts @@ -16,7 +16,6 @@ dependencies { api(libs.ktlintCliRulesetCore) api(libs.ktlintCliReporterCore) api(libs.ktlintCliReporterBaselineCore) - implementation(libs.ktlintRulesetStandard) } tasks { diff --git a/ktlint-lib/ruleset-0-50-0/build.gradle.kts b/ktlint-lib/ruleset-0-50-0/build.gradle.kts new file mode 100644 index 00000000..63f1715c --- /dev/null +++ b/ktlint-lib/ruleset-0-50-0/build.gradle.kts @@ -0,0 +1,46 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("java") // Java support + alias(libs.plugins.kotlin) // Kotlin support + alias(libs.plugins.shadow) +} + +repositories { + mavenCentral() +} + +dependencies { + // Until version 0.50.0, the "mu.Kotlin" logger was used. In 1.x version this has been replaced with + // "io.github.oshai.kotlinlogging.KLogger". + implementation("com.pinterest.ktlint:ktlint-logger:0.50.0") + implementation("com.pinterest.ktlint:ktlint-ruleset-standard:0.50.0") +} + +tasks { + // Set the compatibility versions to 11 + withType { + sourceCompatibility = "11" + targetCompatibility = "11" + } + withType { + kotlinOptions.jvmTarget = "11" + } + + withType { + val api = project.configurations.api.get() + val impl = project.configurations.implementation.get() + + configurations = listOf(api, impl).map { it.apply { isCanBeResolved = true } } + + relocate( + "com.pinterest.ktlint.logger", + "shadow.com.pinterest.ktlint-0-50-0.logger", + ) + relocate( + "com.pinterest.ktlint.ruleset.standard.StandardRuleSetProvider", + "shadow.com.pinterest.ktlint.ruleset.standard.StandardRuleSetProviderV0_50_0", + ) + } +} diff --git a/ktlint-lib/ruleset-1-0-1/build.gradle.kts b/ktlint-lib/ruleset-1-0-1/build.gradle.kts new file mode 100644 index 00000000..ba6afea5 --- /dev/null +++ b/ktlint-lib/ruleset-1-0-1/build.gradle.kts @@ -0,0 +1,39 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("java") // Java support + alias(libs.plugins.kotlin) // Kotlin support + alias(libs.plugins.shadow) +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("com.pinterest.ktlint:ktlint-ruleset-standard:1.0.1") +} + +tasks { + // Set the compatibility versions to 11 + withType { + sourceCompatibility = "11" + targetCompatibility = "11" + } + withType { + kotlinOptions.jvmTarget = "11" + } + + withType { + val api = project.configurations.api.get() + val impl = project.configurations.implementation.get() + + configurations = listOf(api, impl).map { it.apply { isCanBeResolved = true } } + + relocate( + "com.pinterest.ktlint.ruleset.standard.StandardRuleSetProvider", + "shadow.com.pinterest.ktlint.ruleset.standard.StandardRuleSetProviderV1_00_1", + ) + } +} diff --git a/ktlint-lib/ruleset-1-1-1/build.gradle.kts b/ktlint-lib/ruleset-1-1-1/build.gradle.kts new file mode 100644 index 00000000..5c3c67f3 --- /dev/null +++ b/ktlint-lib/ruleset-1-1-1/build.gradle.kts @@ -0,0 +1,39 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("java") // Java support + alias(libs.plugins.kotlin) // Kotlin support + alias(libs.plugins.shadow) +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("com.pinterest.ktlint:ktlint-ruleset-standard:1.1.1") +} + +tasks { + // Set the compatibility versions to 11 + withType { + sourceCompatibility = "11" + targetCompatibility = "11" + } + withType { + kotlinOptions.jvmTarget = "11" + } + + withType { + val api = project.configurations.api.get() + val impl = project.configurations.implementation.get() + + configurations = listOf(api, impl).map { it.apply { isCanBeResolved = true } } + + relocate( + "com.pinterest.ktlint.ruleset.standard.StandardRuleSetProvider", + "shadow.com.pinterest.ktlint.ruleset.standard.StandardRuleSetProviderV1_01_1", + ) + } +} diff --git a/ktlint-lib/ruleset-1-2-1/build.gradle.kts b/ktlint-lib/ruleset-1-2-1/build.gradle.kts new file mode 100644 index 00000000..b905aaf3 --- /dev/null +++ b/ktlint-lib/ruleset-1-2-1/build.gradle.kts @@ -0,0 +1,39 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("java") // Java support + alias(libs.plugins.kotlin) // Kotlin support + alias(libs.plugins.shadow) +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("com.pinterest.ktlint:ktlint-ruleset-standard:1.2.1") +} + +tasks { + // Set the compatibility versions to 11 + withType { + sourceCompatibility = "11" + targetCompatibility = "11" + } + withType { + kotlinOptions.jvmTarget = "11" + } + + withType { + val api = project.configurations.api.get() + val impl = project.configurations.implementation.get() + + configurations = listOf(api, impl).map { it.apply { isCanBeResolved = true } } + + relocate( + "com.pinterest.ktlint.ruleset.standard.StandardRuleSetProvider", + "shadow.com.pinterest.ktlint.ruleset.standard.StandardRuleSetProviderV1_02_1", + ) + } +} diff --git a/lib/README.md b/lib/README.md deleted file mode 100644 index 83f940ec..00000000 --- a/lib/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Why is this a module? - -Because ktlint requires certain elements of the kotlin compiler, it includes a -dependency on the embedded kotlin compiler library. - -Unfortunately, this clashes and conflicts with classes we use in the JetBrains -Kotlin plugin. - -Separating these dependencies out allow us to create a "shadowed" JAR internally -that relocates the conflicting package so that ktlint works, and so does our code! diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 6df4e64e..eeb5a679 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -48,16 +48,28 @@ repositories { dependencies { // implementation(libs.annotations) - // Shadow lib (see: ../lib/README.md) - compileOnly(project(":lib")) // Required for IDE - implementation(project(":lib", "shadow")) + // Shadow lib (see: ../ktlint-lib/README.md) + compileOnly(project(":ktlint-lib:core")) // Required for IDE + implementation(project(":ktlint-lib:core", "shadow")) + + compileOnly(project(":ktlint-lib:ruleset-0-50-0")) // Required for IDE + implementation(project(":ktlint-lib:ruleset-0-50-0", "shadow")) + + compileOnly(project(":ktlint-lib:ruleset-1-0-1")) // Required for IDE + implementation(project(":ktlint-lib:ruleset-1-0-1", "shadow")) + + compileOnly(project(":ktlint-lib:ruleset-1-1-1")) // Required for IDE + implementation(project(":ktlint-lib:ruleset-1-1-1", "shadow")) + + compileOnly(project(":ktlint-lib:ruleset-1-2-1")) // Required for IDE + implementation(project(":ktlint-lib:ruleset-1-2-1", "shadow")) implementation("com.rollbar:rollbar-java:1.10.0") { exclude(group = "org.slf4j") // Duplicated in IDE environment } // Tests: - testImplementation(project(":lib")) + testImplementation(project(":ktlint-lib:core")) testImplementation("org.junit.jupiter:junit-jupiter:5.10.2") testImplementation("org.junit.platform:junit-platform-launcher:1.10.2") testImplementation("io.mockk:mockk:1.13.10") diff --git a/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintConfigForm.form b/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintConfigForm.form index 63f2e0e7..76b074b2 100644 --- a/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintConfigForm.form +++ b/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintConfigForm.form @@ -1,6 +1,6 @@
- + @@ -10,12 +10,12 @@ - + - + @@ -24,7 +24,7 @@ - + @@ -84,7 +84,7 @@ - + @@ -112,6 +112,29 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintConfigForm.kt b/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintConfigForm.kt index 10fdfe39..532efe72 100644 --- a/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintConfigForm.kt +++ b/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintConfigForm.kt @@ -3,8 +3,10 @@ package com.nbadal.ktlint import com.intellij.openapi.fileChooser.FileChooser import com.intellij.openapi.fileChooser.FileChooserDescriptor import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory +import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.project.Project import com.intellij.openapi.ui.TextFieldWithBrowseButton +import com.intellij.psi.PsiManager import com.intellij.ui.IdeBorderFactory import com.nbadal.ktlint.KtlintMode.DISABLED import com.nbadal.ktlint.KtlintMode.DISTRACT_FREE @@ -15,6 +17,7 @@ import java.net.URI import java.util.Objects import javax.swing.JButton import javax.swing.JCheckBox +import javax.swing.JComboBox import javax.swing.JComponent import javax.swing.JLabel import javax.swing.JPanel @@ -31,6 +34,8 @@ class KtlintConfigForm( private set lateinit var disabledMode: JRadioButton private set + lateinit var rulesetVersion: JComboBox + private set private lateinit var formatLabel: JLabel private set lateinit var formatOnSave: JCheckBox @@ -87,9 +92,8 @@ class KtlintConfigForm( } fun apply() { - project.resetKtlintAnnotator() - ktlintConfigStorage.ktlintMode = ktlintMode + ktlintConfigStorage.ktlintRulesetVersion = ktlintRulesetVersion ktlintConfigStorage.formatOnSave = formatOnSave.isSelected ktlintConfigStorage.externalJarPaths = externalJarPaths @@ -102,6 +106,18 @@ class KtlintConfigForm( .text .trim() .let { it.ifBlank { null } } + + project.resetKtlintAnnotator() + + FileEditorManager + .getInstance(project) + .openFiles + .forEach { virtualFile -> + PsiManager + .getInstance(project) + .findFile(virtualFile) + ?.let { psiFile -> ktlintFormat(psiFile, "KtlintActionOnSave") } + } } fun reset() { @@ -111,6 +127,7 @@ class KtlintConfigForm( DISABLED -> disabledMode.isSelected = true else -> Unit } + rulesetVersion.selectedItem = ktlintConfigStorage.ktlintRulesetVersion.label formatOnSave.isSelected = ktlintConfigStorage.formatOnSave baselinePath.text = ktlintConfigStorage.baselinePath.orEmpty() externalJarPaths.text = ktlintConfigStorage.externalJarPaths.joinToString(", ") @@ -125,10 +142,14 @@ class KtlintConfigForm( else -> NOT_INITIALIZED } + private val ktlintRulesetVersion + get() = KtlintRulesetVersion.findByLabelOrDefault(rulesetVersion.selectedItem as String) + val isModified get() = !( Objects.equals(ktlintConfigStorage.ktlintMode, ktlintMode) && + Objects.equals(ktlintConfigStorage.ktlintRulesetVersion, ktlintRulesetVersion) && Objects.equals(ktlintConfigStorage.formatOnSave, formatOnSave.isSelected) && Objects.equals(ktlintConfigStorage.baselinePath, baselinePath.text) && Objects.equals(ktlintConfigStorage.externalJarPaths, externalJarPaths.text) diff --git a/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintConfigStorage.kt b/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintConfigStorage.kt index 14ec6206..a4fa30d0 100644 --- a/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintConfigStorage.kt +++ b/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintConfigStorage.kt @@ -33,6 +33,9 @@ class KtlintConfigStorage : PersistentStateComponent { @Tag var ktlintMode: KtlintMode = NOT_INITIALIZED + @Tag + var ktlintRulesetVersion: KtlintRulesetVersion = KtlintRulesetVersion.DEFAULT + @Tag var formatOnSave: Boolean = true @@ -46,13 +49,19 @@ class KtlintConfigStorage : PersistentStateComponent { * Keeps the state of the last loaded set of rule set jars. It serves as a cache so that the rule set providers do not need to be * reloaded from the file system on each invocation of ktlint format. */ - private var _ruleSetProviders: RuleSetProviders? = null + private lateinit var _ruleSetProviders: RuleSetProviders val ruleSetProviders: RuleSetProviders - get() = - _ruleSetProviders - ?.takeIf { it.externalJarPaths == externalJarPaths } - ?: RuleSetProviders(externalJarPaths).also { _ruleSetProviders = it } + get() { + if (!::_ruleSetProviders.isInitialized || + _ruleSetProviders.ktlintRulesetVersion != ktlintRulesetVersion || + _ruleSetProviders.externalJarPaths != externalJarPaths + ) { + _ruleSetProviders = RuleSetProviders(ktlintRulesetVersion, externalJarPaths) + _ktlintRuleEngine = null + } + return _ruleSetProviders + } /** * Keeps the state of the last loaded baseline. It serves as a cache so that the baseline does not need to be reloaded from the file @@ -64,21 +73,23 @@ class KtlintConfigStorage : PersistentStateComponent { val ktlintRuleEngine: KtLintRuleEngine? get() { - ruleSetProviders - .takeIf { it.isChanged() } - ?.ruleProviders - ?.let { ruleProviders -> - _ktlintRuleEngine = - KtLintRuleEngine( - editorConfigOverride = EditorConfigOverride.EMPTY_EDITOR_CONFIG_OVERRIDE, - ruleProviders = ruleProviders, - ) - } + if (_ktlintRuleEngine == null || + _ruleSetProviders.ktlintRulesetVersion != ktlintRulesetVersion || + _ruleSetProviders.externalJarPaths != externalJarPaths + ) { + _ruleSetProviders + .ruleProviders + ?.let { ruleProviders -> + _ktlintRuleEngine = + KtLintRuleEngine( + editorConfigOverride = EditorConfigOverride.EMPTY_EDITOR_CONFIG_OVERRIDE, + ruleProviders = ruleProviders, + ) + } + } return _ktlintRuleEngine } - private fun RuleSetProviders.isChanged() = externalJarPaths == this@KtlintConfigStorage.externalJarPaths - /** * Clears the ".editorconfig" cache so that it gets reloaded. This should only be called after saving a modified ".editorconfig". */ @@ -92,12 +103,16 @@ class KtlintConfigStorage : PersistentStateComponent { @Suppress("USELESS_ELVIS") this.ktlintMode = state.ktlintMode ?: NOT_INITIALIZED + this.ktlintRulesetVersion = state.ktlintRulesetVersion this.formatOnSave = state.formatOnSave this.baselinePath = state.baselinePath this.externalJarPaths = state.externalJarPaths + + this._ruleSetProviders = RuleSetProviders(ktlintRulesetVersion, externalJarPaths) } data class RuleSetProviders( + val ktlintRulesetVersion: KtlintRulesetVersion, val externalJarPaths: List, ) { private var _error: String? = null @@ -117,6 +132,7 @@ class KtlintConfigStorage : PersistentStateComponent { externalJarPaths .map { File(it).toURI().toURL() } .loadRuleProviders() + .plus(ktlintRulesetVersion.ruleProviders()) } catch (throwable: Throwable) { _isLoaded = false _error = throwable.toString() diff --git a/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintLoadRuleProviders.kt b/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintLoadRuleProviders.kt index a52cfac3..1b5a384f 100644 --- a/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintLoadRuleProviders.kt +++ b/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintLoadRuleProviders.kt @@ -13,32 +13,22 @@ private val logger = KtlintLogger("com.nbdal.ktlint.KtlintLoadRuleProviders") // See: LoadRuleProviders.kt internal fun List.loadRuleProviders(): Set = RuleSetProviderV3::class.java - .loadFromJarFiles(this, providerId = { it.id.value }) + .loadFromJarFiles(this) .flatMap { it.getRuleProviders() } .toSet() // See: KtlintServiceLoader.kt -private fun Class.loadFromJarFiles( - urls: List, - providerId: (T) -> String, -): Set { - val providersFromKtlintJars = this.loadProvidersFromJars(null) - logger.debug { "Loaded ${providersFromKtlintJars.size} providers from ktlint jars" } - val providerIdsFromKtlintJars = providersFromKtlintJars.map { providerId(it) } +private fun Class.loadFromJarFiles(urls: List): Set { val providersFromCustomJars = urls .distinct() .flatMap { url -> loadProvidersFromJars(url) - .filterNot { providerId(it) in providerIdsFromKtlintJars } .also { providers -> logger.debug { "Loaded ${providers.size} custom ruleset providers from $url" } } .filterNotNull() .ifEmpty { throw EmptyRuleSetJarException("Custom rule set '$url' does not contain a custom ktlint rule set provider") } }.toSet() - return providersFromKtlintJars - .plus(providersFromCustomJars) - .filterNotNull() - .toSet() + return providersFromCustomJars.toSet() } class EmptyRuleSetJarException( diff --git a/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintRulesetVersion.kt b/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintRulesetVersion.kt new file mode 100644 index 00000000..710a7678 --- /dev/null +++ b/plugin/src/main/kotlin/com/nbadal/ktlint/KtlintRulesetVersion.kt @@ -0,0 +1,53 @@ +package com.nbadal.ktlint + +import com.pinterest.ktlint.cli.ruleset.core.api.RuleSetProviderV3 +import shadow.com.pinterest.ktlint.ruleset.standard.StandardRuleSetProviderV0_50_0 +import shadow.com.pinterest.ktlint.ruleset.standard.StandardRuleSetProviderV1_00_1 +import shadow.com.pinterest.ktlint.ruleset.standard.StandardRuleSetProviderV1_01_1 +import shadow.com.pinterest.ktlint.ruleset.standard.StandardRuleSetProviderV1_02_1 + +/** + * Policies for supporting rulesets from older versions: + * * Only support for RuleSetProviderV3 + * * Only latest patch version of a minor release is supported + */ +enum class KtlintRulesetVersion( + val label: String, + private val ruleSetProvider: RuleSetProviderV3?, +) { + DEFAULT("default (recommended)", null), + V1_2_1("1.2.1", StandardRuleSetProviderV1_02_1()), + V1_1_1("1.1.1", StandardRuleSetProviderV1_01_1()), + V1_0_1("1.0.1", StandardRuleSetProviderV1_00_1()), + V0_50_0("0.50.0", StandardRuleSetProviderV0_50_0()), + + // Older versions are not compatible with the plugin and are therefore not supported. + // * V49 is incompatible as the RuleSet class was defined as value/data class which can not be used from Java environment + // * V48 and before use the RulesetProviderV2 instead of RulesetProviderV3 + + // User must at least import one custom ruleset as ktlint plugin otherwise will throw errors + NONE("none", null), + ; + + fun ruleProviders() = + ruleSetProvider?.getRuleProviders() + ?: default.ruleSetProvider?.getRuleProviders().orEmpty() + + companion object { + fun findByLabelOrDefault(label: String) = entries.firstOrNull { it.label == label } ?: DEFAULT + + private val default = + entries + .filterNot { it == DEFAULT } + .map { ktlintRulesetVersion -> + ktlintRulesetVersion to + ktlintRulesetVersion + .label + .split(".") + .joinToString(separator = ".") { it.format("%3d") } + }.sortedBy { it.second } + .reversed() + .map { it.first } + .first() + } +} diff --git a/plugin/src/main/resources/strings.properties b/plugin/src/main/resources/strings.properties index c2c50de1..b8d8fc2e 100644 --- a/plugin/src/main/resources/strings.properties +++ b/plugin/src/main/resources/strings.properties @@ -13,3 +13,4 @@ manualModeTooltip=Displays all ktlint violations including violations that can b disabledMode=D&isabled disabledModeTooltip=Disable ktlint entirely for this project. formatLabel=Format +rulesetVersionLabel=Ruleset version diff --git a/settings.gradle.kts b/settings.gradle.kts index 6e89d2d4..789d384f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,4 +4,11 @@ plugins { rootProject.name = "ktlint-intellij-plugin" -include("lib", "plugin") +include( + "ktlint-lib:core", + "ktlint-lib:ruleset-0-50-0", + "ktlint-lib:ruleset-1-0-1", + "ktlint-lib:ruleset-1-1-1", + "ktlint-lib:ruleset-1-2-1", + "plugin", +)