diff --git a/platform/core-api/resources/messages/CoreBundle.properties b/platform/core-api/resources/messages/CoreBundle.properties index c85acd3c45a7..2b6c977f6801 100644 --- a/platform/core-api/resources/messages/CoreBundle.properties +++ b/platform/core-api/resources/messages/CoreBundle.properties @@ -50,8 +50,10 @@ plugin.loading.error.short.incompatible.with.platform=Incompatible: requires hos plugin.loading.error.long.incompatible.with.platform=Plugin ''{0}'' (version ''{1}'') is not compatible with the current host platform, because it requires {2} but the current platform is {3} plugin.loading.error.short.custom.plugin.loading.disabled=Loading of custom plugins is disabled plugin.loading.error.long.custom.plugin.loading.disabled=Plugin ''{0}'' wasn''t loaded because loading of custom plugins is explicitly disabled in the current instance of the IDE -plugin.loading.error.long.kotlin.k2.incompatible=Plugin ''{0}'' wasn''t loaded because it''s incompatible with the Kotlin plugin in K2 mode -plugin.loading.error.short.kotlin.k2.incompatible=Plugin is incompatible with the Kotlin plugin in K2 mode +plugin.loading.error.k1.mode="K1" +plugin.loading.error.k2.mode="K2" +plugin.loading.error.long.kotlin.incompatible=Plugin ''{0}'' wasn''t loaded because it''s incompatible with the Kotlin plugin in {1} mode +plugin.loading.error.short.kotlin.incompatible=Plugin is incompatible with the Kotlin plugin in {0} mode plugin.loading.error.short.plugin.loading.disabled=Loading of plugins is disabled plugin.loading.error.long.plugin.loading.disabled=Plugin ''{0}'' wasn''t loaded because loading of plugins is explicitly disabled in the current instance of the IDE plugin.loading.error.short.marked.as.broken=Marked as incompatible diff --git a/platform/core-impl/src/com/intellij/ide/plugins/ClassLoaderConfigurator.kt b/platform/core-impl/src/com/intellij/ide/plugins/ClassLoaderConfigurator.kt index 252ebef7ad8a..fdf19ead9bff 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/ClassLoaderConfigurator.kt +++ b/platform/core-impl/src/com/intellij/ide/plugins/ClassLoaderConfigurator.kt @@ -189,8 +189,7 @@ class ClassLoaderConfigurator( val subDescriptor = dependency.subDescriptor ?: continue if (!isKotlinPlugin(module.pluginId) && isKotlinPlugin(dependency.pluginId) && - isKotlinPluginK2Mode() && - !pluginCanWorkInK2Mode(module) + isIncompatibleWithKotlinPlugin(module) ) { // disable dependencies which optionally deepened on Kotlin plugin which are incompatible with Kotlin Plugin K2 mode KTIJ-24797 continue diff --git a/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.kt b/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.kt index cc9170b9fa3d..9a63ddb66f17 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.kt +++ b/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.kt @@ -386,12 +386,17 @@ class IdeaPluginDescriptorImpl( } - if (isPluginWhichDependsOnKotlinPluginInK2ModeAndItDoesNotSupportK2Mode(this)) { - // disable plugins which are incompatible with the Kotlin Plugin K2 Mode KTIJ-24797 + if (isPluginWhichDependsOnKotlinPluginAndItsIncompatibleWithIt(this)) { + // disable plugins which are incompatible with the Kotlin Plugin K1/K2 Modes KTIJ-24797, KTIJ-30474 + val mode = if (isKotlinPluginK1Mode()) { + CoreBundle.message("plugin.loading.error.k1.mode") + } else { + CoreBundle.message("plugin.loading.error.k2.mode") + } markAsIncompatible(PluginLoadingError( plugin = this, - detailedMessageSupplier = { CoreBundle.message("plugin.loading.error.long.kotlin.k2.incompatible", getName()) }, - shortMessageSupplier = { CoreBundle.message("plugin.loading.error.short.kotlin.k2.incompatible") }, + detailedMessageSupplier = { CoreBundle.message("plugin.loading.error.long.kotlin.incompatible", getName(), mode) }, + shortMessageSupplier = { CoreBundle.message("plugin.loading.error.short.kotlin.incompatible", mode) }, isNotifyUser = true, )) return diff --git a/platform/core-impl/src/com/intellij/ide/plugins/kotlinK2ModeCompatibilityChecker.kt b/platform/core-impl/src/com/intellij/ide/plugins/kotlinK2ModeCompatibilityChecker.kt index f33dfe188be4..bcef2f529ec7 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/kotlinK2ModeCompatibilityChecker.kt +++ b/platform/core-impl/src/com/intellij/ide/plugins/kotlinK2ModeCompatibilityChecker.kt @@ -1,36 +1,94 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.ide.plugins +import com.intellij.openapi.extensions.ExtensionDescriptor import org.jetbrains.annotations.ApiStatus private val pluginIdsToIgnoreK2KotlinCompatibility: List = System.getProperty("idea.kotlin.plugin.plugin.ids.to.ignore.k2.compatibility")?.split(',')?.map { it.trim() }.orEmpty() + listOf("fleet.backend.kotlin", "fleet.backend.mercury", "fleet.backend.mercury.kotlin.macos") + +/** + * See KTIJ-30474 for semantic + */ internal fun pluginCanWorkInK2Mode(plugin: IdeaPluginDescriptorImpl): Boolean { - return plugin.epNameToExtensions["org.jetbrains.kotlin.supportsKotlinK2Mode"]?.isNotEmpty() == true - || plugin.pluginId.idString in pluginIdsToIgnoreK2KotlinCompatibility + val supportKotlinPluginModeEPs = getSupportKotlinPluginModeEPs(plugin) + + return when { + // explicitly disabled + supportKotlinPluginModeEPs.any { it.element?.attributes.orEmpty()[SUPPORTS_K2_ATTRIBUTE_NAME] == "false" } -> false + plugin.pluginId.idString in pluginIdsToIgnoreK2KotlinCompatibility -> true + // by default, the K2 mode is not supported + else -> supportKotlinPluginModeEPs.any { it.element?.attributes.orEmpty()[SUPPORTS_K2_ATTRIBUTE_NAME] == "true" } + } } + +/** + * See KTIJ-30474 for semantic + */ +internal fun pluginCanWorkInK1Mode(plugin: IdeaPluginDescriptorImpl): Boolean { + val supportKotlinPluginModeEPs = getSupportKotlinPluginModeEPs(plugin) + + return when { + // explicitly disabled + supportKotlinPluginModeEPs.any { it.element?.attributes.orEmpty()[SUPPORTS_K1_ATTRIBUTE_NAME] == "false" } -> false + // by default, the K1 mode is supported + else -> true + } +} + + + +private fun getSupportKotlinPluginModeEPs(plugin: IdeaPluginDescriptorImpl): List { + return plugin.epNameToExtensions[SUPPORTS_KOTLIN_PLUGIN_MODE_EP_NAME] ?: emptyList() +} + + internal fun isKotlinPluginK2Mode(): Boolean { return System.getProperty("idea.kotlin.plugin.use.k2", "false").toBoolean() } - @ApiStatus.Internal -fun isPluginWhichDependsOnKotlinPluginInK2ModeAndItDoesNotSupportK2Mode(plugin: IdeaPluginDescriptorImpl, shouldCheckIfK2modeIsOn: Boolean = true): Boolean { - fun nonOptionallyDependsOnKotlinPlugin(): Boolean { - return plugin.pluginDependencies.any { (isKotlinPlugin(it.pluginId)) && !it.isOptional } || - plugin.dependencies.plugins.any { isKotlinPlugin(it.id) } +fun isKotlinPluginK1Mode(): Boolean { + return !isKotlinPluginK2Mode() +} + +internal fun isIncompatibleWithKotlinPlugin(plugin: IdeaPluginDescriptorImpl): Boolean { + if (isKotlinPluginK1Mode() && !pluginCanWorkInK1Mode(plugin)) { + return true } - if (!shouldCheckIfK2modeIsOn || isKotlinPluginK2Mode()) { - if (!isKotlinPlugin(plugin.pluginId) && nonOptionallyDependsOnKotlinPlugin()) { - if (!pluginCanWorkInK2Mode(plugin)) { - return true - } - } + if (isKotlinPluginK2Mode() && !pluginCanWorkInK2Mode(plugin)) { + return true } return false -} \ No newline at end of file +} + + +@ApiStatus.Internal +fun isPluginWhichDependsOnKotlinPluginAndItsIncompatibleWithIt(plugin: IdeaPluginDescriptorImpl): Boolean { + if (isKotlinPlugin(plugin.pluginId)) return false + if (!nonOptionallyDependsOnKotlinPlugin(plugin)) return false + + return isIncompatibleWithKotlinPlugin(plugin) +} + +@ApiStatus.Internal +fun isPluginWhichDependsOnKotlinPluginInK2ModeAndItDoesNotSupportK2Mode(plugin: IdeaPluginDescriptorImpl): Boolean { + if (isKotlinPlugin(plugin.pluginId)) return false + if (!nonOptionallyDependsOnKotlinPlugin(plugin)) return false + return !pluginCanWorkInK2Mode(plugin) +} + +private fun nonOptionallyDependsOnKotlinPlugin(plugin: IdeaPluginDescriptorImpl): Boolean { + return plugin.pluginDependencies.any { (isKotlinPlugin(it.pluginId)) && !it.isOptional } || + plugin.dependencies.plugins.any { isKotlinPlugin(it.id) } +} + +private const val SUPPORTS_KOTLIN_PLUGIN_MODE_EP_NAME = "org.jetbrains.kotlin.supportsKotlinPluginMode" +private const val SUPPORTS_K1_ATTRIBUTE_NAME = "supportsK1" +private const val SUPPORTS_K2_ATTRIBUTE_NAME = "supportsK2" + diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/DynamicPlugins.kt b/platform/platform-impl/src/com/intellij/ide/plugins/DynamicPlugins.kt index be6fafbc9c5e..7fdf662bb277 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/DynamicPlugins.kt +++ b/platform/platform-impl/src/com/intellij/ide/plugins/DynamicPlugins.kt @@ -289,9 +289,10 @@ object DynamicPlugins { return null } - if (isPluginWhichDependsOnKotlinPluginInK2ModeAndItDoesNotSupportK2Mode(module)) { - // force restarting the IDE in the case the dynamic plugin is incompatible with Kotlin Plugin K2 mode KTIJ-24797 - return "Plugin ${module.pluginId} depends on the Kotlin plugin in K2 Mode, but the plugin does not support K2 Mode" + if (isPluginWhichDependsOnKotlinPluginAndItsIncompatibleWithIt(module)) { + // force restarting the IDE in the case the dynamic plugin is incompatible with Kotlin Plugin K1/K2 modes KTIJ-24797 + val mode = if (isKotlinPluginK1Mode()) "K1" else "K2" + return "Plugin ${module.pluginId} depends on the Kotlin plugin in $mode Mode, but the plugin does not support $mode Mode" } var dependencyMessage: String? = null diff --git a/plugins/kotlin/base/plugin/src/org/jetbrains/kotlin/idea/base/plugin/SupportsKotlinPluginMode.kt b/plugins/kotlin/base/plugin/src/org/jetbrains/kotlin/idea/base/plugin/SupportsKotlinPluginMode.kt new file mode 100644 index 000000000000..8e3f78d3ac57 --- /dev/null +++ b/plugins/kotlin/base/plugin/src/org/jetbrains/kotlin/idea/base/plugin/SupportsKotlinPluginMode.kt @@ -0,0 +1,32 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.kotlin.idea.base.plugin + +import com.intellij.util.xmlb.annotations.Attribute + +/** +* Represents if the Kotlin plugin supports K1 or K2 mode. The following code to `plugin.xml` can be added to support both K1 and K2 modes: + * ```xml + * + * + * + * ``` + * Or the following to support only K2 mode: + * ```xml + * + * + * + * ``` + * To support only K1 mode (this is the default that will be used if no `` is specified): + * ```xml + * + * + * + * ``` + */ +class SupportsKotlinPluginMode { + @Attribute("supportsK1") + var supportsK1: Boolean = true + + @Attribute("supportsK2") + var supportsK2: Boolean = false +} \ No newline at end of file diff --git a/plugins/kotlin/onboarding/src/org/jetbrains/kotlin/onboarding/k2/EnableK2NotificationService.kt b/plugins/kotlin/onboarding/src/org/jetbrains/kotlin/onboarding/k2/EnableK2NotificationService.kt index c2d113c8a5bc..00f3fef12858 100644 --- a/plugins/kotlin/onboarding/src/org/jetbrains/kotlin/onboarding/k2/EnableK2NotificationService.kt +++ b/plugins/kotlin/onboarding/src/org/jetbrains/kotlin/onboarding/k2/EnableK2NotificationService.kt @@ -72,10 +72,7 @@ class EnableK2NotificationService { private fun hasIncompatibleWithK2ModeThirdPartyPluginsEnabled(): Boolean { val allEnabledThirdPartyPlugins = PluginManagerCore.getPluginSet().allPlugins.filter { !it.isBundled && it.isEnabled } return allEnabledThirdPartyPlugins.any { - isPluginWhichDependsOnKotlinPluginInK2ModeAndItDoesNotSupportK2Mode( - it, - shouldCheckIfK2modeIsOn = false - ) + isPluginWhichDependsOnKotlinPluginInK2ModeAndItDoesNotSupportK2Mode(it) } // Code for future: get all incompatible plugins: diff --git a/plugins/kotlin/plugin/resources/META-INF/plugin.xml b/plugins/kotlin/plugin/resources/META-INF/plugin.xml index 4ecea7996de0..54ea966e9a0e 100644 --- a/plugins/kotlin/plugin/resources/META-INF/plugin.xml +++ b/plugins/kotlin/plugin/resources/META-INF/plugin.xml @@ -21,6 +21,13 @@ + + + + + +