From c570e228dfd3068cb16331877dc686f00e7eda5b Mon Sep 17 00:00:00 2001 From: Nikolay Chashnikov Date: Tue, 26 May 2020 12:25:28 +0300 Subject: [PATCH] [workspace model] support loading and unloading plugins defining custom module root types Since properties of custom source roots are stored in xml format in the workspace model entities it's enough to just drop caches if type is changed. However it's also required to unregister plugin which provides root type after root types are changed to 'unknown' (otherwise when ModifiableRootModel is created it'll already use UnknownSourceRootType for these roots), and collect affected roots before creating ModifiableRootModel when a new root type is registered. GitOrigin-RevId: b39e769a1a42b5119d3b7fff61156f7e9bf19882 --- .../jps/impl/JpsIdePluginManagerImpl.java | 63 ++++++++++++------- .../intellij/openapi/roots/SourceFolder.java | 5 ++ .../LegacyBridgeModuleRootComponent.kt | 8 ++- .../module/ContentFolderViaTypedEntity.kt | 5 +- 4 files changed, 53 insertions(+), 28 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/jps/impl/JpsIdePluginManagerImpl.java b/platform/platform-impl/src/com/intellij/jps/impl/JpsIdePluginManagerImpl.java index 659fad9edb7a..c63869113911 100644 --- a/platform/platform-impl/src/com/intellij/jps/impl/JpsIdePluginManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/jps/impl/JpsIdePluginManagerImpl.java @@ -7,8 +7,10 @@ import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.roots.ContentEntry; +import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.roots.SourceFolder; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.util.containers.ContainerUtil; import org.jdom.Element; @@ -32,6 +34,7 @@ import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; public final class JpsIdePluginManagerImpl extends JpsPluginManager { private final List myExternalBuildPlugins = new CopyOnWriteArrayList<>(); @@ -89,6 +92,8 @@ public final class JpsIdePluginManagerImpl extends JpsPluginManager { } private void handlePluginRemoved(@NotNull PluginDescriptor pluginDescriptor) { + if (!myExternalBuildPlugins.contains(pluginDescriptor)) return; + Map, JpsModuleSourceRootPropertiesSerializer> removed = new HashMap<>(); for (JpsModelSerializerExtension extension : loadExtensions(JpsModelSerializerExtension.class)) { for (JpsModuleSourceRootPropertiesSerializer serializer : extension.getModuleSourceRootPropertiesSerializers()) { @@ -96,14 +101,7 @@ public final class JpsIdePluginManagerImpl extends JpsPluginManager { } } - if (myExternalBuildPlugins.remove(pluginDescriptor)) { - myModificationStamp.incrementAndGet(); - } - else{ - return; - } - - for (JpsModelSerializerExtension extension : loadExtensions(JpsModelSerializerExtension.class)) { + for (JpsModelSerializerExtension extension : loadExtensions(JpsModelSerializerExtension.class, descriptor -> !descriptor.equals(pluginDescriptor))) { for (JpsModuleSourceRootPropertiesSerializer serializer : extension.getModuleSourceRootPropertiesSerializers()) { removed.remove(serializer.getType()); } @@ -114,6 +112,8 @@ public final class JpsIdePluginManagerImpl extends JpsPluginManager { replaceWithUnknownRootType(project, removed.values()); } } + myExternalBuildPlugins.remove(pluginDescriptor); + myModificationStamp.incrementAndGet(); } private void handlePluginAdded(@NotNull PluginDescriptor pluginDescriptor) { @@ -183,24 +183,32 @@ public final class JpsIdePluginManagerImpl extends JpsPluginManager { serializers.put(ser.getTypeId(), ser); } for (Module module : ModuleManager.getInstance(project).getModules()) { - ModuleRootModificationUtil.modifyModel(module, model -> { - boolean shouldCommit = false; - for (ContentEntry contentEntry : model.getContentEntries()) { - for (SourceFolder folder : contentEntry.getSourceFolders()) { - if (folder.getRootType() instanceof UnknownSourceRootType) { - UnknownSourceRootType type = (UnknownSourceRootType)folder.getRootType(); - JpsModuleSourceRootPropertiesSerializer serializer = serializers.get(type.getUnknownTypeId()); - if (serializer != null) { - UnknownSourceRootTypeProperties properties = folder.getJpsElement().getProperties(type); - Object data = properties != null? properties.getPropertiesData() : null; - changeType(folder, serializer, data instanceof Element ? (Element)data : null); - shouldCommit = true; - } + Map, Element>> foldersToUpdate = new HashMap<>(); + for (ContentEntry contentEntry : ModuleRootManager.getInstance(module).getContentEntries()) { + for (SourceFolder folder : contentEntry.getSourceFolders()) { + if (folder.getRootType() instanceof UnknownSourceRootType) { + UnknownSourceRootType type = (UnknownSourceRootType)folder.getRootType(); + JpsModuleSourceRootPropertiesSerializer serializer = serializers.get(type.getUnknownTypeId()); + if (serializer != null) { + UnknownSourceRootTypeProperties properties = folder.getJpsElement().getProperties(type); + Object data = properties != null ? properties.getPropertiesData() : null; + foldersToUpdate.put(folder, new Pair<>(serializer, data instanceof Element ? (Element)data : null)); } } } - return shouldCommit; - }); + } + if (!foldersToUpdate.isEmpty()) { + ModuleRootModificationUtil.updateModel(module, model -> { + for (ContentEntry contentEntry : model.getContentEntries()) { + for (SourceFolder folder : contentEntry.getSourceFolders()) { + Pair, Element> pair = foldersToUpdate.get(folder); + if (pair != null) { + changeType(folder, pair.first, pair.second); + } + } + } + }); + } } } @@ -230,9 +238,16 @@ public final class JpsIdePluginManagerImpl extends JpsPluginManager { @NotNull @Override public Collection loadExtensions(@NotNull Class extensionClass) { + return loadExtensions(extensionClass, null); + } + + @NotNull + private Collection loadExtensions(@NotNull Class extensionClass, @Nullable Predicate filter) { Set loaders = new LinkedHashSet<>(); for (PluginDescriptor plugin : myExternalBuildPlugins) { - ContainerUtil.addIfNotNull(loaders, plugin.getPluginClassLoader()); + if (filter == null || filter.test(plugin)) { + ContainerUtil.addIfNotNull(loaders, plugin.getPluginClassLoader()); + } } if (loaders.isEmpty()) { loaders.add(getClass().getClassLoader()); diff --git a/platform/projectModel-api/src/com/intellij/openapi/roots/SourceFolder.java b/platform/projectModel-api/src/com/intellij/openapi/roots/SourceFolder.java index 9d73f6d6b7dc..2985f50dc820 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/roots/SourceFolder.java +++ b/platform/projectModel-api/src/com/intellij/openapi/roots/SourceFolder.java @@ -57,5 +57,10 @@ public interface SourceFolder extends ContentFolder { @NotNull JpsModuleSourceRoot getJpsElement(); + /** + * This method is used internally to change root type to 'unknown' and back when the plugin which provides the custom root type is + * unloaded or loader. It isn't intended to change root type to some other arbitrary type and must not be used in plugins. + */ + @ApiStatus.Internal

void changeType(JpsModuleSourceRootType

newType, P properties); } diff --git a/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/intellij/LegacyBridgeModuleRootComponent.kt b/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/intellij/LegacyBridgeModuleRootComponent.kt index fd99cf9a883e..dca76002cdf9 100644 --- a/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/intellij/LegacyBridgeModuleRootComponent.kt +++ b/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/intellij/LegacyBridgeModuleRootComponent.kt @@ -53,11 +53,11 @@ class LegacyBridgeModuleRootComponent( init { MODULE_EXTENSION_NAME.getPoint(legacyBridgeModule).addExtensionPointListener(object : ExtensionPointListener { override fun extensionAdded(extension: ModuleExtension, pluginDescriptor: PluginDescriptor) { - modelValue.dropCache() + dropRootModelCache() } override fun extensionRemoved(extension: ModuleExtension, pluginDescriptor: PluginDescriptor) { - modelValue.dropCache() + dropRootModelCache() } }, false, null) } @@ -75,6 +75,10 @@ class LegacyBridgeModuleRootComponent( override fun dropCaches() { orderRootsCache.clearCache() + dropRootModelCache() + } + + internal fun dropRootModelCache() { modelValue.dropCache() } diff --git a/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/typedModel/module/ContentFolderViaTypedEntity.kt b/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/typedModel/module/ContentFolderViaTypedEntity.kt index 44c76f8de948..83a68fc42e4c 100644 --- a/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/typedModel/module/ContentFolderViaTypedEntity.kt +++ b/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/typedModel/module/ContentFolderViaTypedEntity.kt @@ -3,12 +3,14 @@ package com.intellij.workspace.legacyBridge.typedModel.module import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.roots.ContentFolder import com.intellij.openapi.roots.ExcludeFolder +import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.SourceFolder import com.intellij.openapi.util.JDOMUtil import com.intellij.openapi.vfs.VirtualFile import com.intellij.workspace.api.* import com.intellij.workspace.legacyBridge.intellij.LegacyBridgeFilePointerProvider import com.intellij.workspace.legacyBridge.intellij.LegacyBridgeFilePointerScope +import com.intellij.workspace.legacyBridge.intellij.LegacyBridgeModuleRootComponent import org.jetbrains.jps.model.JpsElement import org.jetbrains.jps.model.JpsElementFactory import org.jetbrains.jps.model.java.JpsJavaExtensionService @@ -100,7 +102,7 @@ internal class SourceFolderViaTypedEntity(private val entry: ContentEntryViaType } override fun

changeType(newType: JpsModuleSourceRootType

, properties: P) { - sourceRootType = newType + (ModuleRootManager.getInstance(contentEntry.rootModel.module) as LegacyBridgeModuleRootComponent).dropRootModelCache() } override fun hashCode() = entry.url.hashCode() @@ -140,7 +142,6 @@ internal class SourceFolderViaTypedEntity(private val entry: ContentEntryViaType if (javaResourceRoot != null) return updater { diff -> - // TODO Replace TempEntitySource with sourceRootEntity.source diff.addJavaSourceRootEntity(sourceRootEntity, false, packagePrefix, sourceRootEntity.entitySource) } } else {