From a0e751cc6e68840b2728226ba076f2847fe6cc64 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 30 Jan 2020 12:48:41 +0100 Subject: [PATCH] deprecate projectComponentsInitialized in favour of white-listed EP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Another step to protect loading: `ProjectLifecycleListener.projectComponentsInitialized` is deprecated in favour of a new white-listed EP `ProjectServiceContainerInitializedListener`. As for `ProjectServiceContainerCustomizer` (that was introduced for legacy project model), new EP can be extended only by core plugin (runtime error for non-core). Message bus must be not used for any control events, that’s why EP is used here (as for any such solutions in our new code). Even more — EP is a more simpler and suitable here. EP allows you to control order of execution — yes, if you want specific order, it means that your code is bad. But not possible for now to fix RunManager. GitOrigin-RevId: 2493a14678d8604193dfff84008882d5ec4d2a24 --- .../start-up-visualizer/format-changelog.md | 5 +- .../StartUpPerformanceReporter.kt | 4 +- .../ProjectRunConfigurationInitializer.kt | 28 ++-- .../src/com/intellij/execution/RunManager.kt | 3 +- .../module/impl/ModuleManagerComponent.java | 36 ++--- .../ide/ApplicationInitializedListener.java | 4 +- .../ProjectServiceContainerCustomizer.kt | 27 ---- .../impl/ProjectLifecycleListener.java | 6 +- .../lightEdit/project/LightEditProject.java | 7 +- .../openapi/project/impl/ProjectImpl.java | 39 +---- .../project/impl/ProjectManagerImpl.java | 2 +- .../intellij/openapi/project/projectLoader.kt | 108 ++++++++++++++ .../src/META-INF/LangExtensions.xml | 2 + .../src/META-INF/PlatformExtensionPoints.xml | 4 +- .../src/META-INF/PlatformExtensions.xml | 1 + .../src/componentSets/Lang.xml | 4 - .../src/META-INF/WorkspaceModelExtensions.xml | 4 +- .../LegacyBridgeModuleManagerComponent.kt | 133 +++++++++--------- .../LegacyBridgeProjectLifecycleListener.kt | 4 +- 19 files changed, 239 insertions(+), 182 deletions(-) delete mode 100644 platform/platform-api/src/com/intellij/openapi/project/ProjectServiceContainerCustomizer.kt create mode 100644 platform/platform-impl/src/com/intellij/openapi/project/projectLoader.kt diff --git a/platform/built-in-server/start-up-visualizer/format-changelog.md b/platform/built-in-server/start-up-visualizer/format-changelog.md index 5b1d8d4010cb..7777ac9fe3b5 100644 --- a/platform/built-in-server/start-up-visualizer/format-changelog.md +++ b/platform/built-in-server/start-up-visualizer/format-changelog.md @@ -1,7 +1,8 @@ # 16 -* add `run manager initialization` -* add `projectComponentsInitialized event handling` +* add `RunManager initialization` +* add `projectComponentCreated event handling` +* add `projectComponentCreated` ## 15 diff --git a/platform/diagnostic/src/startUpPerformanceReporter/StartUpPerformanceReporter.kt b/platform/diagnostic/src/startUpPerformanceReporter/StartUpPerformanceReporter.kt index d104455af3fc..55ad4e515fc2 100644 --- a/platform/diagnostic/src/startUpPerformanceReporter/StartUpPerformanceReporter.kt +++ b/platform/diagnostic/src/startUpPerformanceReporter/StartUpPerformanceReporter.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2019 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. +// 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.diagnostic.startUpPerformanceReporter import com.fasterxml.jackson.core.JsonGenerator @@ -40,7 +40,7 @@ class StartUpPerformanceReporter : StartupActivity, StartUpPerformanceService { companion object { internal val LOG = logger() - internal const val VERSION = "15" + internal const val VERSION = "16" internal fun sortItems(items: MutableList) { items.sortWith(Comparator { o1, o2 -> diff --git a/platform/execution-impl/src/com/intellij/execution/impl/ProjectRunConfigurationInitializer.kt b/platform/execution-impl/src/com/intellij/execution/impl/ProjectRunConfigurationInitializer.kt index 91391aff130a..924755c05a90 100644 --- a/platform/execution-impl/src/com/intellij/execution/impl/ProjectRunConfigurationInitializer.kt +++ b/platform/execution-impl/src/com/intellij/execution/impl/ProjectRunConfigurationInitializer.kt @@ -1,32 +1,24 @@ // 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.execution.impl +import com.intellij.diagnostic.runActivity import com.intellij.execution.IS_RUN_MANAGER_INITIALIZED import com.intellij.execution.RunManager import com.intellij.openapi.components.service import com.intellij.openapi.project.Project -import com.intellij.openapi.project.impl.ProjectLifecycleListener +import com.intellij.openapi.project.ProjectServiceContainerInitializedListener -internal class ProjectRunConfigurationInitializer(project: Project) { - init { - val connection = project.messageBus.connect() - connection.subscribe(ProjectLifecycleListener.TOPIC, object : ProjectLifecycleListener { - override fun projectComponentsInitialized(eventProject: Project) { - if (project === eventProject) { - requestLoadWorkspaceAndProjectRunConfiguration(project) - } - } - }) - } - - private fun requestLoadWorkspaceAndProjectRunConfiguration(project: Project) { +private class ProjectRunConfigurationInitializer : ProjectServiceContainerInitializedListener { + override fun serviceCreated(project: Project) { if (IS_RUN_MANAGER_INITIALIZED.get(project) == true) { return } - IS_RUN_MANAGER_INITIALIZED.set(project, true) - // we must not fire beginUpdate here, because message bus will fire queued parent message bus messages (and, so, SOE may occur because all other projectOpened will be processed before us) - // simply, you should not listen changes until project opened - project.service() + runActivity("RunManager initialization") { + IS_RUN_MANAGER_INITIALIZED.set(project, true) + // we must not fire beginUpdate here, because message bus will fire queued parent message bus messages (and, so, SOE may occur because all other projectOpened will be processed before us) + // simply, you should not listen changes until project opened + project.service() + } } } \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/execution/RunManager.kt b/platform/lang-api/src/com/intellij/execution/RunManager.kt index 5866b3e28d36..45b2cda5a697 100644 --- a/platform/lang-api/src/com/intellij/execution/RunManager.kt +++ b/platform/lang-api/src/com/intellij/execution/RunManager.kt @@ -2,7 +2,6 @@ package com.intellij.execution import com.intellij.execution.configurations.* -import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key @@ -24,7 +23,7 @@ abstract class RunManager { // https://gist.github.com/develar/5bcf39b3f0ec08f507ec112d73375f2b LOG.debug("Must be not called before project components initialized") } - return ServiceManager.getService(project, RunManager::class.java) + return project.getService(RunManager::class.java) } @JvmStatic diff --git a/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java b/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java index 6f796ea57a12..0112153c6683 100644 --- a/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java +++ b/platform/lang-impl/src/com/intellij/openapi/module/impl/ModuleManagerComponent.java @@ -1,4 +1,4 @@ -// Copyright 2000-2019 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. +// 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.openapi.module.impl; import com.intellij.ProjectTopics; @@ -11,11 +11,12 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.module.UnknownModuleType; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectBundle; -import com.intellij.openapi.project.impl.ProjectLifecycleListener; +import com.intellij.openapi.project.ProjectServiceContainerInitializedListener; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.annotations.NotNull; @@ -25,9 +26,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -/** - * @author yole - */ @State( name = ModuleManagerImpl.COMPONENT_NAME, storages = @Storage("modules.xml"), @@ -48,21 +46,25 @@ public class ModuleManagerComponent extends ModuleManagerImpl { return; } - myMessageBusConnection.subscribe(ProjectLifecycleListener.TOPIC, new ProjectLifecycleListener() { - @Override - public void projectComponentsInitialized(@NotNull final Project project) { - if (project != myProject) return; - - Activity activity = StartUpMeasurer.startMainActivity("module loading"); - loadModules(myModuleModel); - activity.end(); - activity.setDescription("module count: " + myModuleModel.getModules().length); - } - }); - myMessageBusConnection.subscribe(VirtualFileManager.VFS_CHANGES, new ModuleFileListener(this)); } + static class MyProjectServiceContainerInitializedListener implements ProjectServiceContainerInitializedListener { + @Override + public void serviceCreated(@NotNull Project project) { + Activity activity = StartUpMeasurer.startMainActivity("module loading"); + ModuleManager moduleManager = getInstance(project); + if (!(moduleManager instanceof ModuleManagerImpl)) { + return; + } + + ModuleManagerImpl manager = (ModuleManagerImpl)moduleManager; + manager.loadModules(manager.myModuleModel); + activity.end(); + activity.setDescription("module count: " + manager.myModuleModel.getModules().length); + } + } + @Override protected void unloadNewlyAddedModulesIfPossible(@NotNull Set modulesToLoad, @NotNull List modulesToUnload) { UnloadedModulesListChange change = AutomaticModuleUnloader.getInstance(myProject).processNewModules(modulesToLoad, modulesToUnload); diff --git a/platform/platform-api/src/com/intellij/ide/ApplicationInitializedListener.java b/platform/platform-api/src/com/intellij/ide/ApplicationInitializedListener.java index 00a09fec616c..7871940f5d48 100644 --- a/platform/platform-api/src/com/intellij/ide/ApplicationInitializedListener.java +++ b/platform/platform-api/src/com/intellij/ide/ApplicationInitializedListener.java @@ -1,7 +1,8 @@ -// Copyright 2000-2019 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. +// 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.ide; import com.intellij.openapi.extensions.ExtensionPointName; +import org.jetbrains.annotations.ApiStatus; /** * Use extension point {@code com.intellij.applicationInitializedListener} to register listener. @@ -9,6 +10,7 @@ import com.intellij.openapi.extensions.ExtensionPointName; *

* Not part of {@link ApplicationLoadListener} to avoid class loading before application initialization. */ +@ApiStatus.Internal public interface ApplicationInitializedListener { /** * Invoked when all application level components are initialized in the same thread where components are initializing (EDT is not guaranteed). diff --git a/platform/platform-api/src/com/intellij/openapi/project/ProjectServiceContainerCustomizer.kt b/platform/platform-api/src/com/intellij/openapi/project/ProjectServiceContainerCustomizer.kt deleted file mode 100644 index 7ae536e061f7..000000000000 --- a/platform/platform-api/src/com/intellij/openapi/project/ProjectServiceContainerCustomizer.kt +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2000-2019 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.openapi.project - -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.extensions.impl.ExtensionPointImpl -import com.intellij.openapi.extensions.impl.ExtensionsAreaImpl -import org.jetbrains.annotations.ApiStatus - -/** - * Usage requires IJ Platform team approval (including plugin into white-list). - */ -@ApiStatus.Internal -interface ProjectServiceContainerCustomizer { - companion object { - @JvmStatic - fun getEp(): ExtensionPointImpl { - return (ApplicationManager.getApplication().extensionArea as ExtensionsAreaImpl) - .getExtensionPoint("com.intellij.projectServiceContainerCustomizer") - } - } - - /** - * Invoked after implementation classes for project's components were determined (and loaded), - * but before components are instantiated. - */ - fun serviceContainerInitialized(project: Project) -} \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/openapi/project/impl/ProjectLifecycleListener.java b/platform/platform-api/src/com/intellij/openapi/project/impl/ProjectLifecycleListener.java index c3c60561aefe..891ab598c139 100644 --- a/platform/platform-api/src/com/intellij/openapi/project/impl/ProjectLifecycleListener.java +++ b/platform/platform-api/src/com/intellij/openapi/project/impl/ProjectLifecycleListener.java @@ -1,4 +1,4 @@ -// Copyright 2000-2019 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. +// 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.openapi.project.impl; import com.intellij.openapi.project.Project; @@ -13,6 +13,10 @@ import org.jetbrains.annotations.NotNull; public interface ProjectLifecycleListener { Topic TOPIC = Topic.create("Various stages of project lifecycle notifications", ProjectLifecycleListener.class); + /** + * @deprecated Do not use. + */ + @Deprecated default void projectComponentsInitialized(@NotNull Project project) { } diff --git a/platform/platform-impl/src/com/intellij/ide/lightEdit/project/LightEditProject.java b/platform/platform-impl/src/com/intellij/ide/lightEdit/project/LightEditProject.java index 503e489aff64..16e4eb04bd8b 100644 --- a/platform/platform-impl/src/com/intellij/ide/lightEdit/project/LightEditProject.java +++ b/platform/platform-impl/src/com/intellij/ide/lightEdit/project/LightEditProject.java @@ -8,6 +8,7 @@ import com.intellij.openapi.application.PathManager; import com.intellij.openapi.components.ComponentConfig; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.ProjectLoadHelper; import com.intellij.openapi.project.impl.ProjectImpl; import com.intellij.openapi.project.impl.ProjectLifecycleListener; import com.intellij.openapi.roots.ProjectFileIndex; @@ -21,8 +22,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Set; -class LightEditProject extends ProjectImpl { - +final class LightEditProject extends ProjectImpl { private static final Logger LOG = Logger.getInstance(LightEditProject.class); private static final String NAME = "LightEditProject"; private static final Set ALLOWED_CLASSES = ContainerUtil.newHashSet( @@ -38,7 +38,8 @@ class LightEditProject extends ProjectImpl { private LightEditProject(@NotNull Path projectPath) { super(projectPath, NAME); - registerComponents(); + + ProjectLoadHelper.registerComponents(this); customizeRegisteredComponents(); getStateStore().setPath(projectPath, false, null); init(null); diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java index 06d69cd12674..89c2f887b679 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java @@ -2,8 +2,6 @@ package com.intellij.openapi.project.impl; import com.intellij.configurationStore.StoreUtil; -import com.intellij.diagnostic.Activity; -import com.intellij.diagnostic.StartUpMeasurer; import com.intellij.ide.plugins.ContainerDescriptor; import com.intellij.ide.plugins.IdeaPluginDescriptorImpl; import com.intellij.ide.plugins.PluginManagerCore; @@ -22,11 +20,10 @@ import com.intellij.openapi.components.impl.stores.IProjectStore; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.impl.ModuleManagerImpl; -import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectLoadHelper; import com.intellij.openapi.project.ProjectManager; -import com.intellij.openapi.project.ProjectServiceContainerCustomizer; import com.intellij.openapi.project.ex.ProjectEx; import com.intellij.openapi.project.ex.ProjectManagerEx; import com.intellij.openapi.startup.StartupManager; @@ -257,37 +254,6 @@ public class ProjectImpl extends PlatformComponentManagerImpl implements Project return workspaceFilePath == null ? null : LocalFileSystem.getInstance().findFileByPath(workspaceFilePath); } - public final void registerComponents() { - String activityNamePrefix = activityNamePrefix(); - Activity activity = (activityNamePrefix == null || !StartUpMeasurer.isEnabled()) ? null : StartUpMeasurer.startMainActivity(activityNamePrefix + StartUpMeasurer.Activities.REGISTER_COMPONENTS_SUFFIX); - // at this point of time plugins are already loaded by application - no need to pass indicator to getLoadedPlugins call - //noinspection unchecked - registerComponents((List)PluginManagerCore.getLoadedPlugins(), false); - if (activity != null) { - activity = activity.endAndStart("projectComponentRegistered"); - } - - ProjectServiceContainerCustomizer.getEp().processWithPluginDescriptor((customizer, pluginDescriptor) -> { - if (pluginDescriptor.getPluginId() != PluginManagerCore.CORE_ID) { - LOG.error("Plugin " + pluginDescriptor + " is not approved to add ProjectServiceContainerCustomizer"); - } - - try { - customizer.serviceContainerInitialized(this); - } - catch (ProcessCanceledException e) { - throw e; - } - catch (Throwable e) { - LOG.error(e); - } - }); - - if (activity != null) { - activity.end(); - } - } - public void init(@Nullable ProgressIndicator indicator) { Application application = ApplicationManager.getApplication(); @@ -311,7 +277,8 @@ public class ProjectImpl extends PlatformComponentManagerImpl implements Project if (myName == null) { myName = getStateStore().getProjectName(); } - application.getMessageBus().syncPublisher(ProjectLifecycleListener.TOPIC).projectComponentsInitialized(this); + + ProjectLoadHelper.notifyThatComponentCreated(this); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java index f9c9dca11411..bdfdb65d9903 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java @@ -308,7 +308,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable { boolean succeed = false; try { - project.registerComponents(); + ProjectLoadHelper.registerComponents(project); project.getStateStore().setPath(file, isRefreshVfsNeeded, template); project.init(indicator); succeed = true; diff --git a/platform/platform-impl/src/com/intellij/openapi/project/projectLoader.kt b/platform/platform-impl/src/com/intellij/openapi/project/projectLoader.kt new file mode 100644 index 000000000000..3b9b41bf1747 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/project/projectLoader.kt @@ -0,0 +1,108 @@ +// 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.openapi.project + +import com.intellij.diagnostic.Activity +import com.intellij.diagnostic.PluginException +import com.intellij.diagnostic.StartUpMeasurer +import com.intellij.diagnostic.StartUpMeasurer.Activities +import com.intellij.ide.plugins.IdeaPluginDescriptorImpl +import com.intellij.ide.plugins.PluginManagerCore +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.extensions.impl.ExtensionPointImpl +import com.intellij.openapi.extensions.impl.ExtensionsAreaImpl +import com.intellij.openapi.progress.ProcessCanceledException +import com.intellij.openapi.project.impl.ProjectImpl +import com.intellij.openapi.project.impl.ProjectLifecycleListener +import org.jetbrains.annotations.ApiStatus + +// Code maybe located in a ProjectImpl, but it is not possible due to non-technical reasons to convert ProjectImpl into modern language. +// Wrap into class as it is not possible to use internal modifier for top-level functions from Java (but we have to reduce scope). +@ApiStatus.Internal +internal class ProjectLoadHelper { + companion object { + @JvmStatic + fun registerComponents(project: ProjectImpl) { + var activity = createActivity(project) { "project ${Activities.REGISTER_COMPONENTS_SUFFIX}" } + // at this point of time plugins are already loaded by application - no need to pass indicator to getLoadedPlugins call + @Suppress("UNCHECKED_CAST") + project.registerComponents(PluginManagerCore.getLoadedPlugins() as List, notifyListeners = false) + + activity = activity?.endAndStart("projectComponentRegistered") + runHandler(ProjectServiceContainerCustomizer.getEp()) { + it.serviceRegistered(project) + } + activity?.end() + } + + @JvmStatic + fun notifyThatComponentCreated(project: ProjectImpl) { + var activity = createActivity(project) { "projectComponentCreated event handling" } + val app = ApplicationManager.getApplication() + @Suppress("DEPRECATION") + app.messageBus.syncPublisher(ProjectLifecycleListener.TOPIC).projectComponentsInitialized(project) + + activity = activity?.endAndStart("projectComponentCreated") + runHandler((app.extensionArea as ExtensionsAreaImpl).getExtensionPoint("com.intellij.projectServiceContainerInitializedListener")) { + it.serviceCreated(project) + } + activity?.end() + } + } +} + +private val LOG = logger() + +private inline fun createActivity(project: ProjectImpl, message: () -> String): Activity? { + return if (project.isDefault || !StartUpMeasurer.isEnabled()) null else StartUpMeasurer.startActivity(message()) +} + +private inline fun runHandler(ep: ExtensionPointImpl, crossinline executor: (T) -> Unit) { + ep.processWithPluginDescriptor { handler, pluginDescriptor -> + if (pluginDescriptor.pluginId != PluginManagerCore.CORE_ID) { + LOG.error(PluginException("Plugin $pluginDescriptor is not approved to add ${ep.name}", pluginDescriptor.pluginId)) + } + + try { + executor(handler) + } + catch (e: ProcessCanceledException) { + throw e + } + catch (e: Throwable) { + LOG.error(PluginException(e, pluginDescriptor.pluginId)) + } + } +} + +/** + * Usage requires IJ Platform team approval (including plugin into white-list). + */ +@ApiStatus.Internal +interface ProjectServiceContainerCustomizer { + companion object { + @JvmStatic + fun getEp(): ExtensionPointImpl { + return (ApplicationManager.getApplication().extensionArea as ExtensionsAreaImpl) + .getExtensionPoint("com.intellij.projectServiceContainerCustomizer") + } + } + + /** + * Invoked after implementation classes for project's components were determined (and loaded), + * but before components are instantiated. + */ + fun serviceRegistered(project: Project) +} + +/** + * Usage requires IJ Platform team approval (including plugin into white-list). + */ +@ApiStatus.Internal +interface ProjectServiceContainerInitializedListener { + /** + * Invoked after implementation classes for project's components were determined (and loaded), + * but before components are instantiated. + */ + fun serviceCreated(project: Project) +} \ No newline at end of file diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 976bbe07523e..d98ede4bd19d 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -1,5 +1,7 @@ + + diff --git a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml index 5c41a31162dd..928a0b268aee 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml @@ -346,7 +346,9 @@ - + + + diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index b20ba3cedf67..7c372edd6db4 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -533,6 +533,7 @@ + diff --git a/platform/platform-resources/src/componentSets/Lang.xml b/platform/platform-resources/src/componentSets/Lang.xml index 236c82054598..5ba409b5f3a7 100644 --- a/platform/platform-resources/src/componentSets/Lang.xml +++ b/platform/platform-resources/src/componentSets/Lang.xml @@ -21,10 +21,6 @@ com.intellij.psi.search.scope.packageSet.UpdatingScopeOnProjectStructureChangeListener - - com.intellij.execution.impl.ProjectRunConfigurationInitializer - - com.intellij.openapi.fileEditor.FileEditorManager com.intellij.openapi.fileEditor.impl.PsiAwareFileEditorManagerImpl diff --git a/platform/workspaceModel-ide/src/META-INF/WorkspaceModelExtensions.xml b/platform/workspaceModel-ide/src/META-INF/WorkspaceModelExtensions.xml index 973e0bba36f8..0460e2a0e725 100644 --- a/platform/workspaceModel-ide/src/META-INF/WorkspaceModelExtensions.xml +++ b/platform/workspaceModel-ide/src/META-INF/WorkspaceModelExtensions.xml @@ -9,8 +9,8 @@ - + + = ConcurrentHashMap() + internal val unloadedModules: MutableMap = mutableMapOf() + private val newModuleInstances = mutableMapOf() + override fun dispose() { - val modules = modulesMap.values.toList() - modulesMap.clear() + val modules = idToModule.values.toList() + idToModule.clear() for (module in modules) { Disposer.dispose(module) } } - private val modulesMap: ConcurrentMap = ConcurrentHashMap() - internal val unloadedModules: MutableMap = mutableMapOf() - private val newModuleInstances = mutableMapOf() + internal class MyProjectServiceContainerInitializedListener : ProjectServiceContainerInitializedListener { + override fun serviceCreated(project: Project) { + val manager = ModuleManagerComponent.getInstance(project) as? LegacyBridgeModuleManagerComponent ?: return - @ApiStatus.Internal - internal fun setNewModuleInstances(addedInstances: List) { - if (newModuleInstances.isNotEmpty()) error("newModuleInstances are not empty") - for (instance in addedInstances) { - newModuleInstances[instance.moduleEntityId] = instance + val unloadedNames = UnloadedModulesListStorage.getInstance(project).unloadedModuleNames.toSet() + val entities = manager.entityStore.current.entities(ModuleEntity::class.java) + .filter { !unloadedNames.contains(it.name) } + .toList() + manager.loadModules(entities) } } - private fun getModuleRootComponentByLibrary(entity: LibraryEntity): LegacyBridgeModuleRootComponent { - val tableId = entity.tableId as LibraryTableId.ModuleLibraryTableId - val module = modulesMap[tableId.moduleId] ?: error("Could not find module for module library: ${entity.persistentId()}") - return LegacyBridgeModuleRootComponent.getInstance(module) - } - init { // default project doesn't have modules if (!project.isDefault) { - val myMessageBusConnection = project.messageBus.connect(this) - myMessageBusConnection.subscribe(ProjectLifecycleListener.TOPIC, object : ProjectLifecycleListener { - override fun projectComponentsInitialized(listenedProject: Project) { - if (project !== listenedProject) return + val busConnection = project.messageBus.connect(this) + busConnection.subscribe(ProjectManager.TOPIC, object : ProjectManagerListener { + override fun projectOpened(eventProject: Project) { + if (project == eventProject) { + fireModulesAdded() + for (module in idToModule.values) { + (module as ModuleEx).projectOpened() + } + } + } - val unloadedNames = UnloadedModulesListStorage.getInstance(project).unloadedModuleNames.toSet() - - val entities = entityStore.current.entities(ModuleEntity::class.java) - .filter { !unloadedNames.contains(it.name) } - .toList() - loadModules(entities) + override fun projectClosed(eventProject: Project) { + if (project == eventProject) { + for (module in idToModule.values) { + (module as ModuleEx).projectClosed() + } + } } }) - myMessageBusConnection.subscribe(WorkspaceModelTopics.CHANGED, object : WorkspaceModelChangeListener { + busConnection.subscribe(WorkspaceModelTopics.CHANGED, object : WorkspaceModelChangeListener { override fun changed(event: EntityStoreChanged) = LOG.bracket("ModuleManagerComponent.EntityStoreChange") { val moduleLibraryChanges = event.getChanges(LibraryEntity::class.java).filterModuleLibraryChanges() val changes = event.getChanges(ModuleEntity::class.java) @@ -152,7 +157,8 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM alreadyCreatedModule.diff = null addModule(alreadyCreatedModule) alreadyCreatedModule - } else { + } + else { if (change.entity.name in unloadedModules.keys) { // Skip unloaded modules if it was not added via API continue@nextChange @@ -174,7 +180,7 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM unloadedModulesSet.remove(change.newEntity.name) unloadedModules.remove(change.newEntity.name) renameModule(oldId, newId) - oldModuleNames[modulesMap.getValue(newId)] = oldId.name + oldModuleNames[idToModule.getValue(newId)] = oldId.name } } } @@ -237,12 +243,26 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM } } }) - myMessageBusConnection.subscribe(WorkspaceModelTopics.CHANGED, FacetEntityChangeListener(project)) + busConnection.subscribe(WorkspaceModelTopics.CHANGED, FacetEntityChangeListener(project)) } } + @ApiStatus.Internal + internal fun setNewModuleInstances(addedInstances: List) { + if (newModuleInstances.isNotEmpty()) error("newModuleInstances are not empty") + for (instance in addedInstances) { + newModuleInstances[instance.moduleEntityId] = instance + } + } + + private fun getModuleRootComponentByLibrary(entity: LibraryEntity): LegacyBridgeModuleRootComponent { + val tableId = entity.tableId as LibraryTableId.ModuleLibraryTableId + val module = idToModule[tableId.moduleId] ?: error("Could not find module for module library: ${entity.persistentId()}") + return LegacyBridgeModuleRootComponent.getInstance(module) + } + internal fun addModule(moduleEntity: ModuleEntity): LegacyBridgeModule { - if (modulesMap.containsKey(moduleEntity.persistentId())) { + if (idToModule.containsKey(moduleEntity.persistentId())) { error("Module ${moduleEntity.name} (id:'${moduleEntity.persistentId()}') is already added") } @@ -252,27 +272,27 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM } internal fun addModule(module: LegacyBridgeModule) { - val oldValue = modulesMap.put(module.moduleEntityId, module) + val oldValue = idToModule.put(module.moduleEntityId, module) if (oldValue != null) { LOG.warn("Duplicate module name: ${module.name}") } } internal fun removeModuleAndFireEvent(moduleEntityId: ModuleId) { - val moduleImpl = modulesMap.remove(moduleEntityId) ?: error("Module $moduleEntityId does not exist") + val moduleImpl = idToModule.remove(moduleEntityId) ?: error("Module $moduleEntityId does not exist") project.messageBus.syncPublisher(ProjectTopics.MODULES).moduleRemoved(project, moduleImpl) Disposer.dispose(moduleImpl) } internal fun fireBeforeModuleRemoved(moduleEntityId: ModuleId) { - val moduleImpl = modulesMap[moduleEntityId] ?: error("Module $moduleEntityId does not exist") + val moduleImpl = idToModule[moduleEntityId] ?: error("Module $moduleEntityId does not exist") project.messageBus.syncPublisher(ProjectTopics.MODULES).beforeModuleRemoved(project, moduleImpl) } internal fun renameModule(oldId: ModuleId, newId: ModuleId) { - val moduleImpl = modulesMap.remove(oldId) ?: error("Module $oldId does not exist") + val moduleImpl = idToModule.remove(oldId) ?: error("Module $oldId does not exist") - val replacedModuleImplById = modulesMap.put(newId, moduleImpl) + val replacedModuleImplById = idToModule.put(newId, moduleImpl) if (replacedModuleImplById != null) { error("ModuleId $newId already exists") } @@ -289,7 +309,7 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM override fun moduleGraph(): Graph = moduleGraph(includeTests = true) override fun moduleGraph(includeTests: Boolean): Graph { return GraphGenerator.generate(CachingSemiGraph.cache(object : InboundSemiGraph { - override fun getNodes(): Collection = this@LegacyBridgeModuleManagerComponent.modulesMap.values.toMutableList() + override fun getNodes(): Collection = this@LegacyBridgeModuleManagerComponent.idToModule.values.toMutableList() override fun getIn(m: Module): Iterator { val dependentModules = ModuleRootManager.getInstance(m).getDependencies(includeTests) @@ -319,22 +339,8 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM } } - override fun projectOpened() { - fireModulesAdded() - - for (module in modulesMap.values) { - (module as ModuleEx).projectOpened() - } - } - - override fun projectClosed() { - for (module in modulesMap.values) { - (module as ModuleEx).projectClosed() - } - } - private fun fireModulesAdded() { - for (module in modulesMap.values) { + for (module in idToModule.values) { fireModuleAddedInWriteAction(module as ModuleEx) } } @@ -386,7 +392,7 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM ModuleRootManager.getInstance(module).isDependsOn(onModule) override fun getAllModuleDescriptions(): MutableCollection = - (modulesMap.values.map { module -> + (idToModule.values.map { module -> object : ModuleDescription { override fun getName(): String = module.name @@ -427,17 +433,17 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM return getUnloadedModuleDescription(moduleEntity) } - override fun getModules(): Array = modulesMap.values.toTypedArray() + override fun getModules(): Array = idToModule.values.toTypedArray() private val sortedModulesValue = CachedValueWithParameter, Array> { _, _ -> - val allModules = modulesMap.values.toTypedArray() + val allModules = idToModule.values.toTypedArray() Arrays.sort(allModules, moduleDependencyComparator()) return@CachedValueWithParameter allModules } - override fun getSortedModules(): Array = entityStore.cachedValue(sortedModulesValue, modulesMap.keys.toSet()) + override fun getSortedModules(): Array = entityStore.cachedValue(sortedModulesValue, idToModule.keys.toSet()) - override fun findModuleByName(name: String): Module? = modulesMap[ModuleId(name)] + override fun findModuleByName(name: String): Module? = idToModule[ModuleId(name)] override fun disposeModule(module: Module) = ApplicationManager.getApplication().runWriteAction { val modifiableModel = modifiableModel @@ -567,8 +573,9 @@ class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleM companion object { @JvmStatic - fun getInstance(project: Project): LegacyBridgeModuleManagerComponent = - ModuleManagerComponent.getInstance(project) as LegacyBridgeModuleManagerComponent + fun getInstance(project: Project): LegacyBridgeModuleManagerComponent { + return ModuleManagerComponent.getInstance(project) as LegacyBridgeModuleManagerComponent + } private fun List>.filterModuleLibraryChanges() = filter { diff --git a/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/intellij/LegacyBridgeProjectLifecycleListener.kt b/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/intellij/LegacyBridgeProjectLifecycleListener.kt index 888e208e1d97..eb3f698d46a0 100644 --- a/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/intellij/LegacyBridgeProjectLifecycleListener.kt +++ b/platform/workspaceModel-ide/src/com/intellij/workspace/legacyBridge/intellij/LegacyBridgeProjectLifecycleListener.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2019 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. +// 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.workspace.legacyBridge.intellij import com.intellij.ide.plugins.PluginManagerCore @@ -30,7 +30,7 @@ class LegacyBridgeProjectLifecycleListener : ProjectServiceContainerCustomizer { fun enabled(project: Project) = ModuleManager.getInstance(project) is LegacyBridgeModuleManagerComponent } - override fun serviceContainerInitialized(project: Project) { + override fun serviceRegistered(project: Project) { val enabled = Registry.`is`(ENABLED_REGISTRY_KEY) || WorkspaceModelInitialTestContent.peek() != null if (!enabled) { LOG.info("Using legacy project model to open project")