[plugin model] process dependencies on modules in 'processAllNonOptionalDependencies' (IJPL-192427, IJPL-192738)

Before, only dependencies on plugins were processed. So if a plugin depends on a module from another plugin, such dependencies weren't handled in Settings | Plugins, for example. Now `processAllNonOptionalDependencies` processes dependencies on modules as well. To find a descriptor by the module ID (name), a corresponding mapping is built by `PluginSet::buildContentModuleIdMap` and propagated to `processAllNonOptionalDependencies` along with `pluginIdMap`.

'getNonOptionalDependenciesIds' is deprecated since it doesn't return dependencies on modules. It's now used only in the performance testing plugin, so this shouldn't cause problems.

(cherry picked from commit 5ea349497169077cfe33cea4895ce853600fe856, IJ-CR-166457)

GitOrigin-RevId: b8853da20b71a82fbdf1cc26abf2f019c06a4db7
This commit is contained in:
Nikolay Chashnikov
2025-06-20 22:58:09 +03:00
committed by intellij-monorepo-bot
parent 1e83cd1933
commit 5b70765b8c
9 changed files with 167 additions and 52 deletions

View File

@@ -7,6 +7,7 @@ import com.intellij.diagnostic.CoroutineTracerShim
import com.intellij.diagnostic.LoadingState
import com.intellij.ide.plugins.DisabledPluginsState.Companion.invalidate
import com.intellij.ide.plugins.PluginManagerCore.ULTIMATE_PLUGIN_ID
import com.intellij.ide.plugins.PluginManagerCore.getPluginSet
import com.intellij.ide.plugins.PluginManagerCore.isDisabled
import com.intellij.ide.plugins.PluginManagerCore.loadedPlugins
import com.intellij.ide.plugins.PluginManagerCore.processAllNonOptionalDependencies
@@ -483,7 +484,13 @@ object PluginManagerCore {
loadingResult.getIncompleteIdMap().flatMap { (_, value) ->
value.pluginAliases.map { it to value }
}.toMap()
val fullContentModuleIdMap = HashMap<String, ContentModuleDescriptor>()
for (descriptor in loadingResult.getIncompleteIdMap().values) {
descriptor.contentModules.associateByTo(fullContentModuleIdMap) { it.moduleName }
}
for (descriptor in idMap.values) {
descriptor.contentModules.associateByTo(fullContentModuleIdMap) { it.moduleName }
}
if (initContext.checkEssentialPlugins && !idMap.containsKey(CORE_ID)) {
throw EssentialPluginMissingException(listOf("$CORE_ID (platform prefix: ${System.getProperty(PlatformUtils.PLATFORM_PREFIX_KEY)})"))
@@ -513,7 +520,7 @@ object PluginManagerCore {
incompletePlugins = loadingResult.getIncompleteIdMap().values,
currentProductModeEvaluator = initContext::currentProductModeId,
disabler = { descriptor, disabledModuleToProblematicPlugin ->
val loadingError = pluginSetBuilder.initEnableState(descriptor, idMap, fullIdMap, initContext::isPluginDisabled, pluginErrorsById, disabledModuleToProblematicPlugin)
val loadingError = pluginSetBuilder.initEnableState(descriptor, idMap, fullIdMap, fullContentModuleIdMap, initContext::isPluginDisabled, pluginErrorsById, disabledModuleToProblematicPlugin)
if (loadingError != null) {
registerLoadingError(loadingError)
}
@@ -560,7 +567,7 @@ object PluginManagerCore {
}
private fun selectPluginsForLoading(
descriptors: Collection<IdeaPluginDescriptorImpl>,
descriptors: Collection<PluginMainDescriptor>,
idMap: Map<PluginId, IdeaPluginDescriptorImpl>,
errors: MutableMap<PluginId, PluginNonLoadReason>,
initContext: PluginInitializationContext,
@@ -568,10 +575,14 @@ object PluginManagerCore {
if (initContext.explicitPluginSubsetToLoad != null) {
val rootPluginsToLoad: Set<PluginId> = initContext.explicitPluginSubsetToLoad!!.toHashSet() + initContext.essentialPlugins
val pluginsToLoad = LinkedHashSet<IdeaPluginDescriptorImpl>(rootPluginsToLoad.size)
val contentModuleIdMap = HashMap<String, ContentModuleDescriptor>()
for (descriptor in descriptors) {
descriptor.contentModules.associateByTo(contentModuleIdMap) { it.moduleName }
}
for (id in rootPluginsToLoad) {
val descriptor = idMap[id] ?: continue
pluginsToLoad.add(descriptor)
processAllNonOptionalDependencies(descriptor, idMap) { dependency ->
processAllNonOptionalDependencies(descriptor, idMap, contentModuleIdMap) { dependency ->
pluginsToLoad.add(dependency)
FileVisitResult.CONTINUE
}
@@ -758,8 +769,12 @@ object PluginManagerCore {
}
@ApiStatus.Internal
fun processAllNonOptionalDependencyIds(rootDescriptor: IdeaPluginDescriptorImpl, pluginIdMap: Map<PluginId, IdeaPluginDescriptorImpl>, consumer: (PluginId) -> FileVisitResult) {
processAllNonOptionalDependencies(rootDescriptor, depProcessed = HashSet(), pluginIdMap, consumer = { pluginId, _ -> consumer(pluginId) })
fun processAllNonOptionalDependencyIds(rootDescriptor: IdeaPluginDescriptorImpl, pluginIdMap: Map<PluginId, IdeaPluginDescriptorImpl>,
contentModuleIdMap: Map<String, ContentModuleDescriptor>,
consumer: (PluginId) -> FileVisitResult) {
processAllNonOptionalDependencies(rootDescriptor, depProcessed = HashSet(), pluginIdMap, contentModuleIdMap) { pluginId, _ ->
if (pluginId == null) FileVisitResult.CONTINUE else consumer(pluginId)
}
}
/**
@@ -770,8 +785,9 @@ object PluginManagerCore {
fun processAllNonOptionalDependencies(
rootDescriptor: IdeaPluginDescriptorImpl,
pluginIdMap: Map<PluginId, IdeaPluginDescriptorImpl>,
contentModuleIdMap: Map<String, ContentModuleDescriptor>,
consumer: (IdeaPluginDescriptorImpl) -> FileVisitResult,
): Boolean = processAllNonOptionalDependencies(rootDescriptor, depProcessed = HashSet(), pluginIdMap, consumer = { _, descriptor ->
): Boolean = processAllNonOptionalDependencies(rootDescriptor, depProcessed = HashSet(), pluginIdMap, contentModuleIdMap, consumer = { _, descriptor ->
if (descriptor == null) FileVisitResult.CONTINUE else consumer(descriptor)
})
@@ -779,25 +795,53 @@ object PluginManagerCore {
rootDescriptor: IdeaPluginDescriptorImpl,
depProcessed: MutableSet<in IdeaPluginDescriptorImpl>,
pluginIdMap: Map<PluginId, IdeaPluginDescriptorImpl>,
consumer: (PluginId, IdeaPluginDescriptorImpl?) -> FileVisitResult,
contentModuleIdMap: Map<String, ContentModuleDescriptor>,
consumer: (PluginId?, IdeaPluginDescriptorImpl?) -> FileVisitResult,
): Boolean {
for (dependencyId in getNonOptionalDependenciesIds(rootDescriptor)) {
val descriptor = pluginIdMap[dependencyId]
val pluginId = descriptor?.getPluginId() ?: dependencyId
fun processDependency(pluginId: PluginId?, moduleId: String?): Boolean {
val descriptor = if (pluginId != null) pluginIdMap[pluginId] else contentModuleIdMap[moduleId]
val pluginId = descriptor?.getPluginId() ?: pluginId
when (consumer(pluginId, descriptor)) {
FileVisitResult.TERMINATE -> return false
FileVisitResult.CONTINUE -> {
if (descriptor != null && depProcessed.add(descriptor)) {
if (!processAllNonOptionalDependencies(descriptor, depProcessed, pluginIdMap, consumer)) return false
if (!processAllNonOptionalDependencies(descriptor, depProcessed, pluginIdMap, contentModuleIdMap, consumer)) return false
}
}
FileVisitResult.SKIP_SUBTREE -> {}
FileVisitResult.SKIP_SIBLINGS -> throw UnsupportedOperationException("FileVisitResult.SKIP_SIBLINGS is not supported")
}
return true
}
fun processModuleDependencies(moduleDependencies: ModuleDependencies): Boolean {
for (plugin in moduleDependencies.plugins) {
if (!processDependency(plugin.id, null)) return false
}
for (module in moduleDependencies.modules) {
if (!processDependency(null, module.name)) return false
}
return true
}
for (dependency in rootDescriptor.dependencies) {
if (!dependency.isOptional) {
if (!processDependency(dependency.pluginId, null)) return false
}
}
if (!processModuleDependencies(rootDescriptor.moduleDependencies)) return false
if (rootDescriptor is PluginMainDescriptor) {
for (contentModule in rootDescriptor.contentModules) {
if (contentModule.moduleLoadingRule.required && !processModuleDependencies(contentModule.moduleDependencies)) {
return false
}
}
}
return true
}
@Deprecated("Use [processAllNonOptionalDependencyIds] instead, this function doesn't process dependencies on modules")
@ApiStatus.Internal
fun getNonOptionalDependenciesIds(descriptor: IdeaPluginDescriptorImpl): Set<PluginId> {
val dependencies = LinkedHashSet<PluginId>()
@@ -857,9 +901,10 @@ object PluginManagerCore {
@ApiStatus.Internal
fun dependsOnUltimateOptionally(pluginDescriptor: IdeaPluginDescriptor?): Boolean {
if (pluginDescriptor == null || pluginDescriptor !is IdeaPluginDescriptorImpl || !isDisabled(ULTIMATE_PLUGIN_ID)) return false
val idMap = buildPluginIdMap()
val pluginIdMap = buildPluginIdMap()
val contentModuleIdMap = getPluginSet().buildContentModuleIdMap()
return pluginDescriptor.contentModules.any { contentModule ->
!contentModule.moduleLoadingRule.required && !processAllNonOptionalDependencies(contentModule, idMap) { descriptorImpl ->
!contentModule.moduleLoadingRule.required && !processAllNonOptionalDependencies(contentModule, pluginIdMap, contentModuleIdMap) { descriptorImpl ->
when (descriptorImpl.pluginId) {
ULTIMATE_PLUGIN_ID -> FileVisitResult.TERMINATE
else -> FileVisitResult.CONTINUE
@@ -928,20 +973,25 @@ fun getPluginDistDirByClass(aClass: Class<*>): Path? {
@ApiStatus.Internal
fun pluginRequiresUltimatePluginButItsDisabled(plugin: PluginId): Boolean {
val idMap = PluginManagerCore.buildPluginIdMap()
return pluginRequiresUltimatePluginButItsDisabled(plugin, idMap)
val contentModuleIdMap = getPluginSet().buildContentModuleIdMap()
return pluginRequiresUltimatePluginButItsDisabled(plugin, idMap, contentModuleIdMap)
}
@ApiStatus.Internal
fun pluginRequiresUltimatePluginButItsDisabled(plugin: PluginId, pluginMap: Map<PluginId, IdeaPluginDescriptorImpl>): Boolean {
fun pluginRequiresUltimatePluginButItsDisabled(plugin: PluginId, pluginMap: Map<PluginId, IdeaPluginDescriptorImpl>,
contentModuleIdMap: Map<String, ContentModuleDescriptor>): Boolean {
if (!isDisabled(ULTIMATE_PLUGIN_ID)) return false
return pluginRequiresUltimatePlugin(plugin, pluginMap)
return pluginRequiresUltimatePlugin(plugin, pluginMap, contentModuleIdMap)
}
@ApiStatus.Internal
fun pluginRequiresUltimatePlugin(plugin: PluginId, pluginMap: Map<PluginId, IdeaPluginDescriptorImpl> = PluginManagerCore.buildPluginIdMap()): Boolean {
fun pluginRequiresUltimatePlugin(plugin: PluginId,
pluginMap: Map<PluginId, IdeaPluginDescriptorImpl>,
contentModuleMap: Map<String, ContentModuleDescriptor>,
): Boolean {
val rootDescriptor = pluginMap[plugin]
if (rootDescriptor == null) return false
return !processAllNonOptionalDependencies(rootDescriptor, pluginMap) { descriptorImpl ->
return !processAllNonOptionalDependencies(rootDescriptor, pluginMap, contentModuleMap) { descriptorImpl ->
when (descriptorImpl.pluginId) {
ULTIMATE_PLUGIN_ID -> FileVisitResult.TERMINATE
else -> FileVisitResult.CONTINUE

View File

@@ -80,4 +80,21 @@ class PluginSet internal constructor(
// FIXME this is a bad way to treat ambiguous plugin ids
return pluginIdResolutionMap.asSequence().filter { it.value.size == 1 }.associateTo(HashMap()) { it.key to it.value[0] }
}
/**
* Returns a map from content module ID (name) to the corresponding descriptor from all plugins, not only enabled.
*/
fun buildContentModuleIdMap(): Map<String, ContentModuleDescriptor> {
val result = HashMap<String, ContentModuleDescriptor>()
val enabledPluginIds = enabledPlugins.mapTo(HashSet()) { it.pluginId }
for (plugin in allPlugins) {
if (plugin.pluginId !in enabledPluginIds) {
plugin.contentModules.associateByTo(result, ContentModuleDescriptor::moduleName)
}
}
for (plugin in enabledPlugins) {
plugin.contentModules.associateByTo(result, ContentModuleDescriptor::moduleName)
}
return result
}
}

View File

@@ -286,11 +286,12 @@ class PluginSetBuilder(@JvmField val unsortedPlugins: Set<PluginMainDescriptor>)
descriptor: IdeaPluginDescriptorImpl,
idMap: Map<PluginId, IdeaPluginDescriptorImpl>,
fullIdMap: Map<PluginId, IdeaPluginDescriptorImpl>,
fullContentModuleIdMap: Map<String, ContentModuleDescriptor>,
isPluginDisabled: (PluginId) -> Boolean,
errors: MutableMap<PluginId, PluginNonLoadReason>,
disabledModuleToProblematicPlugin: Map<String, PluginId>,
): PluginNonLoadReason? {
val isNotifyUser = !descriptor.isImplementationDetail && !pluginRequiresUltimatePluginButItsDisabled(descriptor.pluginId, fullIdMap)
val isNotifyUser = !descriptor.isImplementationDetail && !pluginRequiresUltimatePluginButItsDisabled(descriptor.pluginId, fullIdMap, fullContentModuleIdMap)
for (incompatibleId in descriptor.incompatiblePlugins) {
if (!enabledPluginIds.containsKey(incompatibleId) || isPluginDisabled(incompatibleId)) {
continue

View File

@@ -67,11 +67,13 @@ internal object DisablePluginsDialog {
@JvmStatic
private fun morePluginsAffected(pluginIdsToDisable: Set<PluginId>): Boolean {
val pluginIdMap = PluginManagerCore.buildPluginIdMap()
val contentModuleIdMap = PluginManagerCore.getPluginSet().buildContentModuleIdMap()
for (rootDescriptor in PluginManagerCore.plugins) {
if (!rootDescriptor.isEnabled || pluginIdsToDisable.contains(rootDescriptor.pluginId)) {
continue
}
if (!PluginManagerCore.processAllNonOptionalDependencies((rootDescriptor as IdeaPluginDescriptorImpl), pluginIdMap) { descriptor ->
if (!PluginManagerCore.processAllNonOptionalDependencies((rootDescriptor as IdeaPluginDescriptorImpl), pluginIdMap, contentModuleIdMap) { descriptor ->
when {
descriptor.isEnabled -> if (pluginIdsToDisable.contains(descriptor.pluginId)) FileVisitResult.TERMINATE
else FileVisitResult.CONTINUE

View File

@@ -75,13 +75,14 @@ class DynamicPaidPluginsService(private val cs: CoroutineScope) {
val disabledPlugins = DisabledPluginsState.getDisabledIds()
val pluginSet = PluginManagerCore.getPluginSet()
val pluginIdMap = PluginManagerCore.buildPluginIdMap()
val contentModuleIdMap = pluginSet.buildContentModuleIdMap()
val loadedPlugins = pluginSet.enabledPlugins.toSet()
val pluginsToEnable = pluginSet.allPlugins.filter {
!disabledPlugins.contains(it.pluginId) &&
!loadedPlugins.contains(it) &&
pluginRequiresUltimatePlugin(it.pluginId, pluginIdMap) &&
!pluginRequiresDisabledPlugin(it.pluginId, pluginIdMap, disabledPlugins)
pluginRequiresUltimatePlugin(it.pluginId, pluginIdMap, contentModuleIdMap) &&
!pluginRequiresDisabledPlugin(it.pluginId, pluginIdMap, contentModuleIdMap, disabledPlugins)
}
if (pluginsToEnable.isEmpty()) {
@@ -177,10 +178,11 @@ class DynamicPaidPluginsService(private val cs: CoroutineScope) {
}
}
private fun pluginRequiresDisabledPlugin(plugin: PluginId, pluginMap: Map<PluginId, IdeaPluginDescriptorImpl>, disabledPluginIds: Set<PluginId>): Boolean {
private fun pluginRequiresDisabledPlugin(plugin: PluginId, pluginMap: Map<PluginId, IdeaPluginDescriptorImpl>,
contentModuleIdMap: Map<String, ContentModuleDescriptor>, disabledPluginIds: Set<PluginId>): Boolean {
if (disabledPluginIds.isEmpty()) return false
val rootDescriptor = pluginMap[plugin] ?: return false
return !processAllNonOptionalDependencies(rootDescriptor, pluginMap) { descriptorImpl ->
return !processAllNonOptionalDependencies(rootDescriptor, pluginMap, contentModuleIdMap) { descriptorImpl ->
if (disabledPluginIds.contains(descriptorImpl.pluginId)) FileVisitResult.TERMINATE
else FileVisitResult.CONTINUE
}

View File

@@ -125,7 +125,8 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
needRestart = true
}
val pluginIdMap = buildPluginIdMap()
val pluginsToEnable = updatePluginDependencies(session, pluginIdMap)
val contentModuleIdMap = getPluginSet().buildContentModuleIdMap()
val pluginsToEnable = updatePluginDependencies(session, pluginIdMap, contentModuleIdMap)
assertCanApply(session, pluginIdMap)
val pluginEnabler = PluginEnabler.getInstance()
@@ -342,7 +343,8 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
override fun prepareToUninstall(pluginsToUninstall: List<PluginId>): PrepareToUninstallResult {
val applicationInfo = ApplicationInfoEx.getInstanceEx()
val idMap = buildPluginIdMap()
val dependentsMap = pluginsToUninstall.associateWith { getDependents(it, applicationInfo, idMap).map { it.name } }
val contentModuleIdMap = getPluginSet().buildContentModuleIdMap()
val dependentsMap = pluginsToUninstall.associateWith { getDependents(it, applicationInfo, idMap, contentModuleIdMap).map { it.name } }
val bundledPlugins = pluginsToUninstall
.mapNotNull { idMap[it] }
.filter { isBundledUpdate(it) }
@@ -369,10 +371,11 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
setNewEnabled(descriptors, tempEnabled, action)
val pluginIdMap: Map<PluginId, IdeaPluginDescriptorImpl> = buildPluginIdMap()
val contentModuleIdMap = getPluginSet().buildContentModuleIdMap()
val descriptorsToUpdate = if (action.isEnable) {
getDependenciesToEnable(descriptors, tempEnabled, pluginIdMap)
getDependenciesToEnable(descriptors, tempEnabled, pluginIdMap, contentModuleIdMap)
}
else getDependentsToDisable(descriptorIds, tempEnabled, pluginIdMap)
else getDependentsToDisable(descriptorIds, tempEnabled, pluginIdMap, contentModuleIdMap)
val pluginNamesToUpdate = descriptorsToUpdate
.filter { !InstalledPluginsTableModel.isHiddenImplementationDetail(it) }
@@ -384,7 +387,7 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
return SetEnabledStateResult(pluginNamesToUpdate, allDescriptorsToUpdate.map { it.pluginId }.toSet())
}
else {
return enableDependencies(session, allDescriptorsToUpdate, action, pluginIdMap)
return enableDependencies(session, allDescriptorsToUpdate, action, pluginIdMap, contentModuleIdMap)
}
}
@@ -396,7 +399,7 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
val session = findSession(sessionId) ?: return SetEnabledStateResult()
val descriptors = descriptorIds.toPluginDescriptors()
val action = if (enable) PluginEnableDisableAction.ENABLE_GLOBALLY else PluginEnableDisableAction.DISABLE_GLOBALLY
return enableDependencies(session, descriptors, action, buildPluginIdMap())
return enableDependencies(session, descriptors, action, buildPluginIdMap(), getPluginSet().buildContentModuleIdMap())
}
override fun enableRequiredPlugins(sessionId: String, pluginId: PluginId): Set<PluginId> {
@@ -441,22 +444,26 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
override fun hasPluginsAvailableForEnableDisable(pluginIds: List<PluginId>): Boolean {
val idMap = buildPluginIdMap()
return pluginIds.any { !pluginRequiresUltimatePluginButItsDisabled(it, idMap) }
val contentModuleIdMap = getPluginSet().buildContentModuleIdMap()
return pluginIds.any { !pluginRequiresUltimatePluginButItsDisabled(it, idMap, contentModuleIdMap) }
}
override fun isPluginRequiresUltimateButItIsDisabled(pluginId: PluginId): Boolean {
val idMap = buildPluginIdMap()
return pluginRequiresUltimatePluginButItsDisabled(pluginId, idMap)
val contentModuleIdMap = getPluginSet().buildContentModuleIdMap()
return pluginRequiresUltimatePluginButItsDisabled(pluginId, idMap, contentModuleIdMap)
}
override fun hasPluginRequiresUltimateButItsDisabled(pluginIds: List<PluginId>): Boolean {
val idMap = buildPluginIdMap()
return pluginIds.any { pluginRequiresUltimatePluginButItsDisabled(it, idMap) }
val contentModuleIdMap = getPluginSet().buildContentModuleIdMap()
return pluginIds.any { pluginRequiresUltimatePluginButItsDisabled(it, idMap, contentModuleIdMap) }
}
override fun filterPluginsRequiringUltimateButItsDisabled(pluginIds: List<PluginId>): List<PluginId> {
val idMap = buildPluginIdMap()
return pluginIds.filter { pluginRequiresUltimatePluginButItsDisabled(it, idMap) }
val contentModuleIdMap = getPluginSet().buildContentModuleIdMap()
return pluginIds.filter { pluginRequiresUltimatePluginButItsDisabled(it, idMap, contentModuleIdMap) }
}
override fun allowLoadUnloadWithoutRestart(pluginId: PluginId): Boolean {
@@ -471,7 +478,7 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
override fun updatePluginDependencies(sessionId: String): Set<PluginId> {
val session = findSession(sessionId) ?: return emptySet()
return updatePluginDependencies(session, null)
return updatePluginDependencies(session, null, null)
}
override fun executePluginsSearch(query: String, count: Int, includeIncompatible: Boolean): PluginSearchResult {
@@ -604,10 +611,11 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
allDescriptorsToUpdate: List<IdeaPluginDescriptor>,
action: PluginEnableDisableAction,
pluginIdMap: Map<PluginId, IdeaPluginDescriptorImpl>,
contentModuleIdMap: Map<String, ContentModuleDescriptor>,
): SetEnabledStateResult {
val changedStates = setNewEnabled(allDescriptorsToUpdate, session.pluginStates, action,
{ descriptor, pair -> handleBeforeChangeEnableState(session, descriptor, pair) })
val pluginsToEnable = updatePluginDependencies(session, pluginIdMap)
val pluginsToEnable = updatePluginDependencies(session, pluginIdMap, contentModuleIdMap)
pluginsToEnable.forEach { changedStates[it] = true }
return SetEnabledStateResult(changedStates = changedStates)
}
@@ -792,10 +800,11 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
descriptors: List<IdeaPluginDescriptorImpl>,
enabledMap: Map<PluginId, PluginEnabledState>,
pluginIdMap: Map<PluginId, IdeaPluginDescriptorImpl>,
contentModuleIdMap: Map<String, ContentModuleDescriptor>,
): List<IdeaPluginDescriptor> {
val result = mutableListOf<IdeaPluginDescriptor>()
for (descriptor in descriptors) {
PluginManagerCore.processAllNonOptionalDependencies(descriptor, pluginIdMap) { dependency ->
PluginManagerCore.processAllNonOptionalDependencies(descriptor, pluginIdMap, contentModuleIdMap) { dependency ->
val dependencyId = dependency.pluginId
val state = enabledMap[dependencyId]
@@ -812,9 +821,11 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
private fun updatePluginDependencies(
session: PluginManagerSession,
pluginIdMap: Map<PluginId, IdeaPluginDescriptorImpl>?,
contentModuleIdMap: Map<String, ContentModuleDescriptor>?,
): Set<PluginId> {
val pluginsToEnable = mutableSetOf<PluginId>()
var pluginIdMap = pluginIdMap
var contentModuleIdMap = contentModuleIdMap
session.dependentToRequiredListMap.clear()
val pluginsState = InstalledPluginsState.getInstance()
@@ -828,10 +839,13 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
if (pluginIdMap == null) {
pluginIdMap = buildPluginIdMap()
}
if (contentModuleIdMap == null) {
contentModuleIdMap = getPluginSet().buildContentModuleIdMap()
}
val loaded: Boolean = session.pluginStates.contains(pluginId)
if (rootDescriptor is IdeaPluginDescriptorImpl) {
PluginManagerCore.processAllNonOptionalDependencyIds(rootDescriptor, pluginIdMap) { depId: PluginId ->
PluginManagerCore.processAllNonOptionalDependencyIds(rootDescriptor, pluginIdMap, contentModuleIdMap) { depId: PluginId ->
if (depId == pluginId) {
return@processAllNonOptionalDependencyIds FileVisitResult.CONTINUE
}
@@ -877,6 +891,7 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
pluginIds: List<PluginId>,
enabledMap: MutableMap<PluginId, PluginEnabledState>,
pluginIdMap: Map<PluginId, IdeaPluginDescriptorImpl>,
contentModuleIdMap: Map<String, ContentModuleDescriptor>,
): List<IdeaPluginDescriptor> {
val result = mutableListOf<IdeaPluginDescriptor>()
@@ -887,7 +902,7 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
continue
}
PluginManagerCore.processAllNonOptionalDependencies(descriptor, pluginIdMap) { dependency: IdeaPluginDescriptorImpl? ->
PluginManagerCore.processAllNonOptionalDependencies(descriptor, pluginIdMap, contentModuleIdMap) { dependency: IdeaPluginDescriptorImpl? ->
val dependencyId = dependency!!.getPluginId()
if (!enabledMap.contains(dependencyId)) {
return@processAllNonOptionalDependencies FileVisitResult.TERMINATE
@@ -908,6 +923,7 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
rootId: PluginId,
applicationInfo: ApplicationInfoEx,
pluginIdMap: Map<PluginId, IdeaPluginDescriptorImpl>,
contentModuleIdMap: Map<String, ContentModuleDescriptor>,
): List<IdeaPluginDescriptorImpl> {
val result = mutableListOf<IdeaPluginDescriptorImpl>()
for (entry in pluginIdMap.entries) {
@@ -920,7 +936,7 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
continue
}
PluginManagerCore.processAllNonOptionalDependencies(descriptor, pluginIdMap) {
PluginManagerCore.processAllNonOptionalDependencies(descriptor, pluginIdMap, contentModuleIdMap) {
if (it.pluginId == rootId) {
result.add(descriptor)
return@processAllNonOptionalDependencies FileVisitResult.TERMINATE

View File

@@ -63,9 +63,10 @@ public final class PluginBooleanOptionDescriptor extends BooleanOptionDescriptio
}
Map<PluginId, IdeaPluginDescriptorImpl> pluginIdMap = PluginManagerCore.INSTANCE.buildPluginIdMap();
Map<@NotNull String, @NotNull ContentModuleDescriptor> contentModuleIdMap = PluginManagerCore.getPluginSet().buildContentModuleIdMap();
Collection<? extends IdeaPluginDescriptor> autoSwitchedDescriptors = enable ?
getDependenciesToEnable(descriptors, pluginIdMap) :
getDependentsToDisable(descriptors, pluginIdMap);
getDependenciesToEnable(descriptors, pluginIdMap, contentModuleIdMap) :
getDependentsToDisable(descriptors, pluginIdMap, contentModuleIdMap);
PluginEnabler pluginEnabler = PluginEnabler.getInstance();
boolean appliedWithoutRestart = enable ?
@@ -131,7 +132,8 @@ public final class PluginBooleanOptionDescriptor extends BooleanOptionDescriptio
}
private static @NotNull Collection<? extends IdeaPluginDescriptor> getDependenciesToEnable(@NotNull Collection<? extends IdeaPluginDescriptor> descriptors,
@NotNull Map<PluginId, IdeaPluginDescriptorImpl> pluginIdMap) {
@NotNull Map<PluginId, IdeaPluginDescriptorImpl> pluginIdMap,
@NotNull Map<String, ContentModuleDescriptor> contentModuleIdMap) {
Set<IdeaPluginDescriptor> result = new LinkedHashSet<>();
for (IdeaPluginDescriptor descriptor : descriptors) {
@@ -141,7 +143,7 @@ public final class PluginBooleanOptionDescriptor extends BooleanOptionDescriptio
continue;
}
PluginManagerCore.INSTANCE.processAllNonOptionalDependencies((IdeaPluginDescriptorImpl)descriptor, pluginIdMap, dependency ->
PluginManagerCore.INSTANCE.processAllNonOptionalDependencies((IdeaPluginDescriptorImpl)descriptor, pluginIdMap, contentModuleIdMap, dependency ->
PluginManagerCore.CORE_ID.equals(dependency.getPluginId()) ||
(PluginManagerCore.ULTIMATE_PLUGIN_ID.equals(dependency.getPluginId()) &&
PluginManagerCore.isDisabled(PluginManagerCore.ULTIMATE_PLUGIN_ID)) ||
@@ -155,14 +157,15 @@ public final class PluginBooleanOptionDescriptor extends BooleanOptionDescriptio
}
private static @NotNull Collection<? extends IdeaPluginDescriptor> getDependentsToDisable(@NotNull Collection<? extends IdeaPluginDescriptor> descriptors,
@NotNull Map<PluginId, IdeaPluginDescriptorImpl> pluginIdMap) {
@NotNull Map<PluginId, IdeaPluginDescriptorImpl> pluginIdMap,
@NotNull Map<String, ContentModuleDescriptor> contentModuleIdMap) {
Set<IdeaPluginDescriptor> result = new LinkedHashSet<>();
ApplicationInfoEx applicationInfo = ApplicationInfoEx.getInstanceEx();
for (IdeaPluginDescriptor descriptor : descriptors) {
result.add(descriptor);
result.addAll(DefaultUiPluginManagerController.INSTANCE.getDependents(descriptor.getPluginId(), applicationInfo, pluginIdMap));
result.addAll(DefaultUiPluginManagerController.INSTANCE.getDependents(descriptor.getPluginId(), applicationInfo, pluginIdMap, contentModuleIdMap));
}
return Collections.unmodifiableSet(result);

View File

@@ -172,10 +172,31 @@ internal class PluginDependenciesTest {
assertFirstErrorContains("sample.plugin", "requires plugin", "bar")
assertNonOptionalDependenciesIds(result, "sample.plugin", "bar")
}
@Test
fun `plugin is not loaded if required module depends on a module from disabled plugin`() {
`bar-plugin with module bar`()
plugin("sample.plugin") {
content {
module("required.module", ModuleLoadingRule.REQUIRED) {
packagePrefix = "required"
dependencies {
module("bar")
}
}
}
}.buildDir(pluginDirPath.resolve("sample-plugin"))
val result = buildPluginSet(disabledPluginIds = arrayOf("bar-plugin"))
assertThat(result).doesNotHaveEnabledPlugins()
assertFirstErrorContains("sample.plugin", "requires plugin", "bar-plugin"/*, "to be enabled"*/) //todo fix not loading reason
assertNonOptionalDependenciesIds(result, "sample.plugin", "bar-plugin")
}
private fun assertNonOptionalDependenciesIds(result: PluginSet, pluginId: String, vararg dependencyPluginId: String) {
val actualDependencies = HashSet<String>()
PluginManagerCore.processAllNonOptionalDependencyIds(result.getPlugin(pluginId), result.buildPluginIdMap()) {
val actualDependencies = HashSet<String>()
val pluginIdMap = result.buildPluginIdMap()
val contentModuleIdMap = result.buildContentModuleIdMap()
PluginManagerCore.processAllNonOptionalDependencyIds(result.getPlugin(pluginId), pluginIdMap, contentModuleIdMap) {
actualDependencies.add(it.idString)
FileVisitResult.CONTINUE
}

View File

@@ -1,5 +1,6 @@
package com.intellij.settingsSync.core.plugins
import com.intellij.ide.plugins.ContentModuleDescriptor
import com.intellij.ide.plugins.IdeaPluginDescriptor
import com.intellij.ide.plugins.IdeaPluginDescriptorImpl
import com.intellij.ide.plugins.PluginEnableStateChangedListener
@@ -88,7 +89,8 @@ internal class SettingsSyncPluginManager(private val cs: CoroutineScope) : Dispo
}
val pluginIdMap = PluginManagerCore.buildPluginIdMap()
val contentModuleIdMap = PluginManagerCore.getPluginSet().buildContentModuleIdMap()
for (plugin in currentIdePlugins) {
val id = plugin.pluginId
if (!isPluginSynceable(id) || PluginManagerProxy.getInstance().isIncompatible(plugin)) {
@@ -102,7 +104,7 @@ internal class SettingsSyncPluginManager(private val cs: CoroutineScope) : Dispo
}
else if (
PluginManagerCore.isDisabled(PluginManagerCore.ULTIMATE_PLUGIN_ID) &&
isUltimate(plugin, pluginIdMap)
isUltimate(plugin, pluginIdMap, contentModuleIdMap)
) {
if (LOG.isDebugEnabled) {
LOG.debug("Skipped syncing ultimate plugin ${plugin.pluginId}")
@@ -125,9 +127,10 @@ internal class SettingsSyncPluginManager(private val cs: CoroutineScope) : Dispo
private fun isUltimate(
plugin: IdeaPluginDescriptor,
pluginIdMap: Map<PluginId, IdeaPluginDescriptorImpl>,
contentModuleIdMap: Map<String, ContentModuleDescriptor>,
): Boolean {
var isUltimate = false
PluginManagerCore.processAllNonOptionalDependencyIds(plugin as IdeaPluginDescriptorImpl, pluginIdMap) {
PluginManagerCore.processAllNonOptionalDependencyIds(plugin as IdeaPluginDescriptorImpl, pluginIdMap, contentModuleIdMap) {
if (it == PluginManagerCore.ULTIMATE_PLUGIN_ID) {
isUltimate = true
}