From 18141a240d8063fc33cf56980b52149fcd25a1d2 Mon Sep 17 00:00:00 2001 From: nik Date: Mon, 20 Jan 2020 10:54:35 +0300 Subject: [PATCH] [platform] make facetType extension point dynamic (IDEA-225619) GitOrigin-RevId: f5365be5b326b65082f06b14bff1d3fcc810fd30 --- .../com/intellij/facet/FacetTestCase.java | 14 +- .../intellij/facet/FacetTypeUnloadingTest.kt | 132 ++++++++++++++++++ .../src/com/intellij/facet/FacetType.java | 14 +- .../intellij/facet/ModifiableFacetModel.java | 7 + .../com/intellij/facet/FacetManagerBase.java | 24 ++-- .../com/intellij/facet/FacetManagerImpl.java | 62 +++++--- .../intellij/facet/impl/FacetModelImpl.java | 9 ++ .../facet/impl/FacetTypeRegistryImpl.kt | 90 ++++++++++-- .../src/META-INF/LangExtensionPoints.xml | 2 +- .../facet/FacetManagerViaWorkspaceModel.kt | 2 +- .../ModifiableFacetModelViaWorkspaceModel.kt | 5 + 11 files changed, 314 insertions(+), 47 deletions(-) create mode 100644 java/idea-ui/testSrc/com/intellij/facet/FacetTypeUnloadingTest.kt diff --git a/java/idea-ui/testSrc/com/intellij/facet/FacetTestCase.java b/java/idea-ui/testSrc/com/intellij/facet/FacetTestCase.java index ead801ae81be..6d9c321ae7cb 100644 --- a/java/idea-ui/testSrc/com/intellij/facet/FacetTestCase.java +++ b/java/idea-ui/testSrc/com/intellij/facet/FacetTestCase.java @@ -6,6 +6,8 @@ import com.intellij.facet.mock.MockFacetConfiguration; import com.intellij.facet.mock.MockFacetType; import com.intellij.facet.mock.MockSubFacetType; import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.roots.ProjectModelExternalSource; import com.intellij.testFramework.HeavyPlatformTestCase; import org.jetbrains.annotations.Nullable; @@ -32,12 +34,14 @@ public abstract class FacetTestCase extends HeavyPlatformTestCase { } protected void removeAllFacets() { - final FacetManager manager = getFacetManager(); - final ModifiableFacetModel model = manager.createModifiableModel(); - for (Facet facet : manager.getAllFacets()) { - model.removeFacet(facet); + for (Module module : ModuleManager.getInstance(myProject).getModules()) { + FacetManager manager = FacetManager.getInstance(module); + ModifiableFacetModel model = manager.createModifiableModel(); + for (Facet facet : manager.getAllFacets()) { + model.removeFacet(facet); + } + commit(model); } - commit(model); } protected FacetManager getFacetManager() { diff --git a/java/idea-ui/testSrc/com/intellij/facet/FacetTypeUnloadingTest.kt b/java/idea-ui/testSrc/com/intellij/facet/FacetTypeUnloadingTest.kt new file mode 100644 index 000000000000..08b035cab51b --- /dev/null +++ b/java/idea-ui/testSrc/com/intellij/facet/FacetTypeUnloadingTest.kt @@ -0,0 +1,132 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.facet + +import com.intellij.facet.impl.invalid.InvalidFacetManager +import com.intellij.facet.mock.MockFacet +import com.intellij.facet.mock.MockFacetConfiguration +import com.intellij.facet.mock.MockFacetType +import com.intellij.facet.mock.MockSubFacetType +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.runWriteAction +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.JDOMUtil +import com.intellij.testFramework.HeavyPlatformTestCase + +class FacetTypeUnloadingTest : HeavyPlatformTestCase() { + fun `test unload and load facet`() { + val facetManager = FacetManager.getInstance(module) + runWithRegisteredFacetTypes(MockFacetType()) { + runWriteAction { + val configuration = MockFacetConfiguration().apply { + data = "my data" + } + val model = facetManager.createModifiableModel() + model.addFacet(MockFacet(module, "mock", configuration)) + model.commit() + } + assertEquals("mock", facetManager.getFacetsByType(MockFacetType.ID).single().name) + } + + assertTrue(facetManager.getFacetsByType(MockFacetType.ID).isEmpty()) + val invalidFacet = InvalidFacetManager.getInstance(myProject).invalidFacets.single() + assertEquals("mock", invalidFacet.name) + assertEquals("", JDOMUtil.write(invalidFacet.configuration.facetState.configuration)) + + registerFacetType(MockFacetType(), testRootDisposable) + assertTrue(InvalidFacetManager.getInstance(myProject).invalidFacets.isEmpty()) + val mockFacet = facetManager.getFacetsByType(MockFacetType.ID).single() + assertEquals("mock", mockFacet.name) + assertEquals("my data", mockFacet.configuration.data) + } + + fun `test unload and load facet with sub facets`() { + val facetManager = FacetManager.getInstance(module) + runWithRegisteredFacetTypes(MockFacetType(), MockSubFacetType()) { + runWriteAction { + val model = facetManager.createModifiableModel() + val facet = MockFacet(module, "mock") + model.addFacet(facet) + model.addFacet(MockSubFacetType.getInstance().createFacet(module, "sub-facet", MockFacetConfiguration(), facet)) + model.commit() + } + assertEquals("mock", facetManager.getFacetsByType(MockFacetType.ID).single().name) + assertEquals("sub-facet", facetManager.getFacetsByType(MockSubFacetType.ID).single().name) + } + + assertTrue(facetManager.getFacetsByType(MockFacetType.ID).isEmpty()) + assertTrue(facetManager.getFacetsByType(MockSubFacetType.ID).isEmpty()) + val invalidFacet = InvalidFacetManager.getInstance(myProject).invalidFacets.single() + assertEquals("mock", invalidFacet.name) + val subFacetState = invalidFacet.configuration.facetState.subFacets.single() + assertEquals("sub-facet", subFacetState.name) + + registerFacetType(MockFacetType(), testRootDisposable) + registerFacetType(MockSubFacetType(), testRootDisposable) + + assertTrue(InvalidFacetManager.getInstance(myProject).invalidFacets.isEmpty()) + val mockFacet = facetManager.getFacetsByType(MockFacetType.ID).single() + assertEquals("mock", mockFacet.name) + val mockSubFacet = facetManager.getFacetsByType(MockSubFacetType.ID).single() + assertEquals("sub-facet", mockSubFacet.name) + assertSame(mockFacet, mockSubFacet.underlyingFacet) + } + + fun `test unload and load sub facet`() { + val facetManager = FacetManager.getInstance(module) + registerFacetType(MockFacetType(), testRootDisposable) + runWithRegisteredFacetTypes(MockSubFacetType()) { + runWriteAction { + val model = facetManager.createModifiableModel() + val facet = MockFacet(module, "mock") + model.addFacet(facet) + model.addFacet(MockSubFacetType.getInstance().createFacet(module, "sub-facet", MockFacetConfiguration(), facet)) + model.commit() + } + assertEquals("mock", facetManager.getFacetsByType(MockFacetType.ID).single().name) + assertEquals("sub-facet", facetManager.getFacetsByType(MockSubFacetType.ID).single().name) + } + + val mockFacet = facetManager.getFacetsByType(MockFacetType.ID).single() + assertEquals("mock", mockFacet.name) + assertTrue(facetManager.getFacetsByType(MockSubFacetType.ID).isEmpty()) + val invalidFacet = InvalidFacetManager.getInstance(myProject).invalidFacets.single() + assertEquals("sub-facet", invalidFacet.name) + assertSame(mockFacet, invalidFacet.underlyingFacet) + + registerFacetType(MockSubFacetType(), testRootDisposable) + assertTrue(InvalidFacetManager.getInstance(myProject).invalidFacets.isEmpty()) + val mockSubFacet = facetManager.getFacetsByType(MockSubFacetType.ID).single() + assertEquals("sub-facet", mockSubFacet.name) + assertSame(mockFacet, mockSubFacet.underlyingFacet) + } + + private inline fun runWithRegisteredFacetTypes(vararg types: FacetType<*, *>, action: () -> Unit) { + val disposable = Disposer.newDisposable() + for (type in types) { + registerFacetType(type, disposable) + } + + try { + action() + } + finally { + Disposer.dispose(disposable) + } + } + + private fun registerFacetType(type: FacetType<*, *>, disposable: Disposable) { + val facetTypeDisposable = Disposer.newDisposable() + Disposer.register(disposable, Disposable { + runWriteAction { + Disposer.dispose(facetTypeDisposable) + } + }) + FacetType.EP_NAME.getPoint(null).registerExtension(type, facetTypeDisposable) + } + + override fun setUp() { + super.setUp() + //initialize facet types and register listeners + FacetTypeRegistry.getInstance().facetTypes + } +} \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/facet/FacetType.java b/platform/lang-api/src/com/intellij/facet/FacetType.java index ed96360a5aa0..a0bea301bc79 100644 --- a/platform/lang-api/src/com/intellij/facet/FacetType.java +++ b/platform/lang-api/src/com/intellij/facet/FacetType.java @@ -7,6 +7,8 @@ import com.intellij.facet.ui.DefaultFacetSettingsEditor; import com.intellij.facet.ui.FacetEditor; import com.intellij.facet.ui.MultipleFacetSettingsEditor; import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.extensions.PluginAware; +import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.project.Project; @@ -24,13 +26,14 @@ import javax.swing.*; * </extensions> * */ -public abstract class FacetType { +public abstract class FacetType implements PluginAware { public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.facetType"); private final @NotNull FacetTypeId myId; private final @NotNull String myStringId; private final @NotNull String myPresentableName; private final @Nullable FacetTypeId myUnderlyingFacetType; + private PluginDescriptor myPluginDescriptor; public static T findInstance(Class aClass) { return EP_NAME.findExtension(aClass); @@ -90,6 +93,15 @@ public abstract class FacetType { return myUnderlyingFacetType; } + @Override + public void setPluginDescriptor(@NotNull PluginDescriptor pluginDescriptor) { + myPluginDescriptor = pluginDescriptor; + } + + public final PluginDescriptor getPluginDescriptor() { + return myPluginDescriptor; + } + /** * @deprecated this method is not called by IDE core anymore. Use {@link com.intellij.framework.detection.FrameworkDetector} extension * to provide automatic detection for facets diff --git a/platform/lang-api/src/com/intellij/facet/ModifiableFacetModel.java b/platform/lang-api/src/com/intellij/facet/ModifiableFacetModel.java index aabd7c4a099d..0944f436bebc 100644 --- a/platform/lang-api/src/com/intellij/facet/ModifiableFacetModel.java +++ b/platform/lang-api/src/com/intellij/facet/ModifiableFacetModel.java @@ -29,6 +29,13 @@ public interface ModifiableFacetModel extends FacetModel { void addFacet(Facet facet, @Nullable ProjectModelExternalSource externalSource); void removeFacet(Facet facet); + /** + * Replaces {@code original} facet by {@code replacement}. The only difference with {@code removeFacet(original); addFacet(replacement); } + * is that this method preserves order of facets in internal structures to avoid modifications of *.iml files. + */ + @ApiStatus.Internal + void replaceFacet(@NotNull Facet original, @NotNull Facet replacement); + void rename(Facet facet, String newName); @Nullable diff --git a/platform/lang-impl/src/com/intellij/facet/FacetManagerBase.java b/platform/lang-impl/src/com/intellij/facet/FacetManagerBase.java index ad32997d9ea5..5b42667ce1c5 100644 --- a/platform/lang-impl/src/com/intellij/facet/FacetManagerBase.java +++ b/platform/lang-impl/src/com/intellij/facet/FacetManagerBase.java @@ -38,10 +38,10 @@ public abstract class FacetManagerBase extends FacetManager { } @NotNull - private static , C extends FacetConfiguration> F createFacet(@NotNull Module module, @NotNull FacetType type, - @NotNull String name, - @NotNull C configuration, - @Nullable Facet underlying) { + protected static , C extends FacetConfiguration> F createFacet(@NotNull Module module, @NotNull FacetType type, + @NotNull String name, + @NotNull C configuration, + @Nullable Facet underlying) { final F facet = type.createFacet(module, name, configuration, underlying); assertTrue(facet.getModule() == module, facet, "module"); assertTrue(facet.getConfiguration() == configuration, facet, "configuration"); @@ -126,17 +126,19 @@ public abstract class FacetManagerBase extends FacetManager { @NotNull public static InvalidFacet createInvalidFacet(@NotNull Module module, @NotNull FacetState state, @Nullable Facet underlyingFacet, @NotNull String errorMessage, - boolean unknownType) { + boolean unknownType, boolean reportError) { Project project = module.getProject(); - final InvalidFacetManager invalidFacetManager = InvalidFacetManager.getInstance(project); final InvalidFacetType type = InvalidFacetType.getInstance(); final InvalidFacetConfiguration configuration = new InvalidFacetConfiguration(state, errorMessage); final InvalidFacet facet = createFacet(module, type, StringUtil.notNullize(state.getName()), configuration, underlyingFacet); - if (!invalidFacetManager.isIgnored(facet)) { - FacetLoadingErrorDescription description = new FacetLoadingErrorDescription(facet); - ProjectLoadingErrorsNotifier.getInstance(project).registerError(description); - if (unknownType) { - UnknownFeaturesCollector.getInstance(project).registerUnknownFeature("com.intellij.facetType", state.getFacetType(), "Facet"); + if (reportError) { + InvalidFacetManager invalidFacetManager = InvalidFacetManager.getInstance(project); + if (!invalidFacetManager.isIgnored(facet)) { + FacetLoadingErrorDescription description = new FacetLoadingErrorDescription(facet); + ProjectLoadingErrorsNotifier.getInstance(project).registerError(description); + if (unknownType) { + UnknownFeaturesCollector.getInstance(project).registerUnknownFeature("com.intellij.facetType", state.getFacetType(), "Facet"); + } } } return facet; diff --git a/platform/lang-impl/src/com/intellij/facet/FacetManagerImpl.java b/platform/lang-impl/src/com/intellij/facet/FacetManagerImpl.java index 5d4e4d208f55..4905e204bd56 100644 --- a/platform/lang-impl/src/com/intellij/facet/FacetManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/facet/FacetManagerImpl.java @@ -134,7 +134,7 @@ public final class FacetManagerImpl extends FacetManagerBase implements ModuleCo ModifiableFacetModel model, final Facet underlyingFacet, final String errorMessage, boolean unknownType) { - model.addFacet(createInvalidFacet(getModule(), state, underlyingFacet, errorMessage, unknownType)); + model.addFacet(createInvalidFacet(getModule(), state, underlyingFacet, errorMessage, unknownType, true)); } private , C extends FacetConfiguration> void addFacet(final FacetType type, final FacetState state, final Facet underlyingFacet, @@ -161,24 +161,39 @@ public final class FacetManagerImpl extends FacetManagerBase implements ModuleCo } if (facet == null) { - final C configuration = type.createDefaultConfiguration(); - final Element config = state.getConfiguration(); - FacetUtil.loadFacetConfiguration(configuration, config); - String name = state.getName(); - facet = createFacet(type, name, configuration, underlyingFacet); - if (facet instanceof JDOMExternalizable) { - //todo[nik] remove - ((JDOMExternalizable)facet).readExternal(config); - } - String externalSystemId = state.getExternalSystemId(); - if (externalSystemId != null) { - facet.setExternalSource(ExternalProjectSystemRegistry.getInstance().getSourceById(externalSystemId)); - } + facet = createFacetFromState(getModule(), type, state, underlyingFacet); model.addFacet(facet); } addFacets(state.getSubFacets(), facet, model); } + @ApiStatus.Internal + @NotNull + public static Facet createFacetFromStateRaw(@NotNull Module module, @NotNull FacetType type, @NotNull FacetState state, + @Nullable Facet underlyingFacet) { + //noinspection unchecked + return createFacetFromState(module, type, state, underlyingFacet); + } + + @NotNull + private static , C extends FacetConfiguration> F createFacetFromState(Module module, FacetType type, + FacetState state, Facet underlyingFacet) { + C configuration = type.createDefaultConfiguration(); + Element config = state.getConfiguration(); + FacetUtil.loadFacetConfiguration(configuration, config); + String name = state.getName(); + F facet = createFacet(module, type, name, configuration, underlyingFacet); + if (facet instanceof JDOMExternalizable) { + //todo[nik] remove + ((JDOMExternalizable)facet).readExternal(config); + } + String externalSystemId = state.getExternalSystemId(); + if (externalSystemId != null) { + facet.setExternalSource(ExternalProjectSystemRegistry.getInstance().getSourceById(externalSystemId)); + } + return facet; + } + @Override public void noStateLoaded() { doLoadState(null); @@ -224,12 +239,8 @@ public final class FacetManagerImpl extends FacetManagerBase implements ModuleCo if (!filter.test(facet)) continue; final Facet underlyingFacet = facet.getUnderlyingFacet(); - FacetState facetState = createFacetState(facet, myModule.getProject()); - if (!(facet instanceof InvalidFacet)) { - final Element config = FacetUtil.saveFacetConfiguration(facet); - if (config == null) continue; - facetState.setConfiguration(config); - } + FacetState facetState = saveFacetConfiguration(facet); + if (facetState == null) continue; getOrCreateTargetFacetList(underlyingFacet, states, myModule.getProject()).add(facetState); states.put(facet, facetState.getSubFacets()); @@ -237,6 +248,17 @@ public final class FacetManagerImpl extends FacetManagerBase implements ModuleCo return managerState; } + @ApiStatus.Internal + public static @Nullable FacetState saveFacetConfiguration(Facet facet) { + FacetState facetState = createFacetState(facet, facet.getModule().getProject()); + if (!(facet instanceof InvalidFacet)) { + final Element config = FacetUtil.saveFacetConfiguration(facet); + if (config == null) return null; + facetState.setConfiguration(config); + } + return facetState; + } + /** * Configuration of some facet may be stored in one file, but configuration of its underlying facet may be stored in another file. For such * sub-facets we create parent elements which don't store configuration but only name and type. diff --git a/platform/lang-impl/src/com/intellij/facet/impl/FacetModelImpl.java b/platform/lang-impl/src/com/intellij/facet/impl/FacetModelImpl.java index c205fcfd2eac..373597090515 100644 --- a/platform/lang-impl/src/com/intellij/facet/impl/FacetModelImpl.java +++ b/platform/lang-impl/src/com/intellij/facet/impl/FacetModelImpl.java @@ -72,6 +72,15 @@ public class FacetModelImpl extends FacetModelBase implements ModifiableFacetMod facetsChanged(); } + @Override + public void replaceFacet(@NotNull Facet original, @NotNull Facet replacement) { + int index = myFacets.indexOf(original); + if (index != -1) { + myFacets.set(index, replacement); + facetsChanged(); + } + } + @Override public void rename(final Facet facet, final String newName) { if (!newName.equals(facet.getName())) { diff --git a/platform/lang-impl/src/com/intellij/facet/impl/FacetTypeRegistryImpl.kt b/platform/lang-impl/src/com/intellij/facet/impl/FacetTypeRegistryImpl.kt index 816cea5657e9..b5c75cc311aa 100644 --- a/platform/lang-impl/src/com/intellij/facet/impl/FacetTypeRegistryImpl.kt +++ b/platform/lang-impl/src/com/intellij/facet/impl/FacetTypeRegistryImpl.kt @@ -2,9 +2,16 @@ package com.intellij.facet.impl import com.intellij.facet.* +import com.intellij.facet.impl.invalid.InvalidFacetManager +import com.intellij.facet.impl.invalid.InvalidFacetType +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.extensions.ExtensionPointListener import com.intellij.openapi.extensions.PluginDescriptor +import com.intellij.openapi.project.Project +import com.intellij.openapi.project.ProjectManager +import org.jetbrains.jps.model.serialization.facet.FacetState import java.util.* class FacetTypeRegistryImpl : FacetTypeRegistry() { @@ -23,13 +30,72 @@ class FacetTypeRegistryImpl : FacetTypeRegistry() { myTypeIds[id] = typeId } + private fun loadInvalidFacetsOfType(project: Project, facetType: FacetType<*, *>) { + val modulesWithFacets = InvalidFacetManager.getInstance(project).invalidFacets + .filter { it.configuration.facetState.facetType == facetType.stringId } + .mapTo(HashSet()) { it.module } + for (module in modulesWithFacets) { + val model = FacetManager.getInstance(module).createModifiableModel() + val invalidFacets = model.getFacetsByType(InvalidFacetType.TYPE_ID).filter { it.configuration.facetState.facetType == facetType.stringId } + for (invalidFacet in invalidFacets) { + val newFacet = FacetManagerImpl.createFacetFromStateRaw(module, facetType, invalidFacet.configuration.facetState, + invalidFacet.underlyingFacet) + model.replaceFacet(invalidFacet, newFacet) + for (subFacet in invalidFacet.configuration.facetState.subFacets) { + model.addFacet(FacetManagerBase.createInvalidFacet(module, subFacet, newFacet, invalidFacet.configuration.errorMessage, false, false)) + } + } + model.commit() + } + } + @Synchronized override fun unregisterFacetType(facetType: FacetType<*, *>) { - val id = facetType.id - val stringId = facetType.stringId - LOG.assertTrue(myFacetTypes.remove(id) != null, "Facet type '$stringId' is not registered") - myFacetTypes.remove(id) - myTypeIds.remove(stringId) + try { + ProjectManager.getInstance().openProjects.forEach { + convertFacetsToInvalid(it, facetType) + } + } + finally { + val id = facetType.id + val stringId = facetType.stringId + LOG.assertTrue(myFacetTypes.remove(id) != null, "Facet type '$stringId' is not registered") + myFacetTypes.remove(id) + myTypeIds.remove(stringId) + } + } + + private fun convertFacetsToInvalid(project: Project, facetType: FacetType<*, *>) { + val modulesWithFacets = ProjectFacetManager.getInstance(project).getFacets(facetType.id).mapTo(HashSet()) { it.module } + for (module in modulesWithFacets) { + val model = FacetManager.getInstance(module).createModifiableModel() + val subFacets = model.allFacets.groupBy { it.underlyingFacet } + val facets = model.getFacetsByType(facetType.id) + for (facet in facets) { + val facetState = saveFacetWithSubFacets(facet, subFacets) ?: continue + val pluginName = facetType.pluginDescriptor?.name?.let { " '$it'" } ?: "" + val errorMessage = "Plugin$pluginName which provides support for '${facetType.presentableName}' facets is unloaded" + val reportError = !ApplicationManager.getApplication().isUnitTestMode + val invalidFacet = FacetManagerBase.createInvalidFacet(module, facetState, facet.underlyingFacet, errorMessage, true, reportError) + removeAllSubFacets(model, facet, subFacets) + model.replaceFacet(facet, invalidFacet) + } + model.commit() + } + } + + private fun removeAllSubFacets(model: ModifiableFacetModel, facet: Facet<*>, subFacets: Map, List>>) { + val toRemove = subFacets[facet] ?: return + for (subFacet in toRemove) { + removeAllSubFacets(model, subFacet, subFacets) + model.removeFacet(subFacet) + } + } + + private fun saveFacetWithSubFacets(facet: Facet<*>?, subFacets: Map, List>>): FacetState? { + val state = FacetManagerImpl.saveFacetConfiguration(facet) ?: return null + (subFacets[facet] ?: emptyList()).mapNotNullTo(state.subFacets) { saveFacetWithSubFacets(it, subFacets) } + return state } @Synchronized @@ -72,18 +138,26 @@ class FacetTypeRegistryImpl : FacetTypeRegistry() { if (myExtensionsLoaded) { return } - FacetType.EP_NAME.getPoint(null).addExtensionPointListener( + FacetType.EP_NAME.forEachExtensionSafe { + registerFacetType(it) + } + myExtensionsLoaded = true + FacetType.EP_NAME.addExtensionPointListener( object : ExtensionPointListener?> { override fun extensionAdded(extension: FacetType<*, *>, pluginDescriptor: PluginDescriptor) { registerFacetType(extension) + runWriteAction { + ProjectManager.getInstance().openProjects.forEach { + loadInvalidFacetsOfType(it, extension) + } + } } override fun extensionRemoved(extension: FacetType<*, *>, pluginDescriptor: PluginDescriptor) { unregisterFacetType(extension) } - }, true, null) - myExtensionsLoaded = true + }, null) } companion object { diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index e1c771ddb1fa..e48d3d284298 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -422,7 +422,7 @@ - + diff --git a/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/FacetManagerViaWorkspaceModel.kt b/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/FacetManagerViaWorkspaceModel.kt index fdc2cdcec312..a1b7bb7f01ad 100644 --- a/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/FacetManagerViaWorkspaceModel.kt +++ b/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/FacetManagerViaWorkspaceModel.kt @@ -74,7 +74,7 @@ internal open class FacetModelViaWorkspaceModel(protected val legacyBridgeModule name = entity.name setFacetType(entity.facetType) configuration = entity.configurationXmlTag?.let { JDOMUtil.load(it) } - }, underlyingFacet, ProjectBundle.message("error.message.unknown.facet.type.0", entity.facetType), true) + }, underlyingFacet, ProjectBundle.message("error.message.unknown.facet.type.0", entity.facetType), true, true) } val configuration = facetType.createDefaultConfiguration() diff --git a/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/ModifiableFacetModelViaWorkspaceModel.kt b/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/ModifiableFacetModelViaWorkspaceModel.kt index b684f4973a34..b20b76771e7b 100644 --- a/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/ModifiableFacetModelViaWorkspaceModel.kt +++ b/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/facet/ModifiableFacetModelViaWorkspaceModel.kt @@ -47,6 +47,11 @@ internal class ModifiableFacetModelViaWorkspaceModel(private val initialStorage: facetsChanged() } + override fun replaceFacet(original: Facet<*>, replacement: Facet<*>) { + removeFacet(original) + addFacet(replacement) + } + private fun removeFacetEntityWithSubFacets(entity: FacetEntity) { if (entity !in entityToFacet) return