[plugins] PluginEnabler implementations registered
authorAndrew Kozlov <andrew.kozlov@jetbrains.com>
Tue, 19 Oct 2021 15:28:40 +0000 (17:28 +0200)
committerintellij-monorepo-bot <intellij-monorepo-bot-no-reply@jetbrains.com>
Tue, 19 Oct 2021 19:38:03 +0000 (19:38 +0000)
GitOrigin-RevId: 9ddcd8e2018dee97068a65ab4568852665a78a8d

platform/core-impl/src/com/intellij/ide/plugins/PluginEnabler.java
platform/platform-impl/src/com/intellij/externalDependencies/impl/CheckRequiredPluginsActivity.java
platform/platform-impl/src/com/intellij/ide/plugins/DynamicPluginEnabler.kt
platform/platform-impl/src/com/intellij/ide/plugins/InstalledPluginsTableModel.java
platform/platform-impl/src/com/intellij/ide/plugins/newui/MyPluginModel.java
platform/platform-impl/src/com/intellij/ide/plugins/newui/ProjectDependentPluginEnabledState.kt
platform/platform-impl/src/com/intellij/internal/statistic/collectors/fus/plugins/PluginsUsagesCollector.kt
platform/platform-resources/src/META-INF/PlatformExtensions.xml
platform/platform-tests/testSrc/com/intellij/ide/plugins/PerProjectPluginsTest.kt

index 715033103c010293a2d8d60cde1df3f9a87af743..0f61ed243fe431984bc5635490a8aab952be7233 100644 (file)
@@ -1,6 +1,7 @@
 // Copyright 2000-2021 JetBrains s.r.o. and contributors. 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.plugins;
 
+import com.intellij.diagnostic.LoadingState;
 import com.intellij.openapi.application.Application;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.extensions.PluginId;
@@ -16,12 +17,16 @@ public interface PluginEnabler {
   PluginEnabler HEADLESS = new DisabledPluginsState();
 
   static @NotNull PluginEnabler getInstance() {
+    if (!LoadingState.COMPONENTS_LOADED.isOccurred()) {
+      return HEADLESS;
+    }
+
     Application application = ApplicationManager.getApplication();
-    return application == null ||
-           application.isHeadlessEnvironment() && !application.isUnitTestMode() ||
-           application.isDisposed() ?
-           HEADLESS :
-           application.getService(PluginEnabler.class);
+    if (application == null || application.isDisposed()) {
+      return HEADLESS;
+    }
+
+    return application.getService(PluginEnabler.class);
   }
 
   boolean isDisabled(@NotNull PluginId pluginId);
index b48617febb395b424b45339b4b849ae507ec05de..84128b7e7d2f2373f352cd9dbbc87ccfc2688599 100644 (file)
@@ -55,64 +55,61 @@ final class CheckRequiredPluginsActivity implements StartupActivity.RequiredForS
       return;
     }
 
+    String projectName = project.getName();
+
     final List<@Nls String> errorMessages = new ArrayList<>();
     final List<IdeaPluginDescriptor> disabled = new ArrayList<>();
     final Set<PluginId> notInstalled = new HashSet<>();
     List<IdeaPluginDescriptor> pluginsToEnableWithoutRestart = new ArrayList<>();
-    DynamicPluginEnabler pluginEnabler = DynamicPluginEnabler.getInstance();
-    ProjectPluginTracker pluginTracker = pluginEnabler.getPluginTracker(project);
+
+    ApplicationInfo applicationInfo = ApplicationInfo.getInstance();
+    PluginEnabler pluginEnabler = PluginEnabler.getInstance();
+    ProjectPluginTracker pluginTracker = DynamicPluginEnabler.findPluginTracker(project, pluginEnabler);
 
     for (DependencyOnPlugin dependency : dependencies) {
       PluginId pluginId = PluginId.getId(dependency.getPluginId());
-      IdeaPluginDescriptor plugin = PluginManagerCore.getPlugin(pluginId);
-      if (plugin == null) {
-        errorMessages.add(IdeBundle.message("error.plugin.required.for.project.not.installed", pluginId, project.getName()));
+      IdeaPluginDescriptorImpl descriptor = PluginManagerCore.findPlugin(pluginId);
+      if (descriptor == null) {
+        errorMessages.add(IdeBundle.message("error.plugin.required.for.project.not.installed", pluginId, projectName));
         notInstalled.add(pluginId);
         continue;
       }
 
-      if (!plugin.isEnabled() || pluginTracker.isDisabled(pluginId)) {
-        boolean canEnableWithoutRestart = false;
-        if (Registry.is("ide.plugins.load.automatically")) {
-          String message = DynamicPlugins.INSTANCE.checkCanUnloadWithoutRestart((IdeaPluginDescriptorImpl)plugin);
-          if (message == null) {
-            canEnableWithoutRestart = true;
-            pluginsToEnableWithoutRestart.add(plugin);
-          }
-          else {
-            LOG.info("Required plugin " + plugin.getPluginId() + " can't be enabled without restart: " + message);
-          }
+      String pluginName = descriptor.getName();
+      if (pluginEnabler.isDisabled(pluginId) ||
+          pluginTracker != null && pluginTracker.isDisabled(pluginId)) {
+        boolean canEnableWithoutRestart = Registry.is("ide.plugins.load.automatically") &&
+                                          DynamicPlugins.allowLoadUnloadWithoutRestart(descriptor);
+        if (canEnableWithoutRestart) {
+          pluginsToEnableWithoutRestart.add(descriptor);
         }
-        if (!canEnableWithoutRestart) {
-          errorMessages.add(IdeBundle.message("error.plugin.required.for.project.disabled", plugin.getName(), project.getName()));
-          disabled.add(plugin);
+        else {
+          errorMessages.add(IdeBundle.message("error.plugin.required.for.project.disabled", pluginName, projectName));
+          disabled.add(descriptor);
         }
         continue;
       }
 
       String minVersion = dependency.getMinVersion();
       String maxVersion = dependency.getMaxVersion();
-      String pluginVersion = plugin.getVersion();
-      BuildNumber currentIdeVersion = ApplicationInfo.getInstance().getBuild();
-      if (plugin.isBundled() && !plugin.allowBundledUpdate() && currentIdeVersion.asStringWithoutProductCode().equals(pluginVersion)) {
-        String pluginFromString = PluginManagerCore.CORE_ID.equals(plugin.getPluginId()) ? "" : "plugin '" + plugin.getName() + "' from ";
+      String pluginVersion = descriptor.getVersion();
+
+      BuildNumber currentIdeVersion = applicationInfo.getBuild();
+      if (descriptor.isBundled() && !descriptor.allowBundledUpdate() && currentIdeVersion.asStringWithoutProductCode().equals(pluginVersion)) {
+        String pluginFromString = PluginManagerCore.CORE_ID.equals(descriptor.getPluginId()) ? "" : "plugin '" + pluginName + "' from ";
         if (minVersion != null && currentIdeVersion.compareTo(BuildNumber.fromString(minVersion)) < 0) {
-          errorMessages
-            .add(IdeBundle.message("error.project.requires.newer.ide", project.getName(), pluginFromString, minVersion, pluginVersion));
+          errorMessages.add(IdeBundle.message("error.project.requires.newer.ide", projectName, pluginFromString, minVersion, pluginVersion));
         }
         if (maxVersion != null && currentIdeVersion.compareTo(BuildNumber.fromString(maxVersion)) > 0) {
-          errorMessages
-            .add(IdeBundle.message("error.project.requires.older.ide", project.getName(), pluginFromString, maxVersion, pluginVersion));
+          errorMessages.add(IdeBundle.message("error.project.requires.older.ide", projectName, pluginFromString, maxVersion, pluginVersion));
         }
       }
       else {
         if (minVersion != null && VersionComparatorUtil.compare(pluginVersion, minVersion) < 0) {
-          errorMessages
-            .add(IdeBundle.message("error.project.requires.newer.plugin", project.getName(), plugin.getName(), minVersion, pluginVersion));
+          errorMessages.add(IdeBundle.message("error.project.requires.newer.plugin", projectName, pluginName, minVersion, pluginVersion));
         }
         if (maxVersion != null && VersionComparatorUtil.compare(pluginVersion, maxVersion) > 0) {
-          errorMessages
-            .add(IdeBundle.message("error.project.requires.older.plugin", project.getName(), plugin.getName(), maxVersion, pluginVersion));
+          errorMessages.add(IdeBundle.message("error.project.requires.older.plugin", projectName, pluginName, maxVersion, pluginVersion));
         }
       }
     }
index a9ce2d2c250b9468ea65ef91c1d92f04429ef1ae..394ed0a759858de242f27b1fce3bfe2f9ae1118e 100644 (file)
@@ -33,28 +33,29 @@ class DynamicPluginEnabler : SimplePersistentStateComponent<DynamicPluginEnabler
 
     private val isPerProjectEnabledValue = RegistryManager.getInstance()["ide.plugins.per.project"]
 
-    @JvmStatic
-    fun getInstance(): DynamicPluginEnabler = PluginEnabler.getInstance() as DynamicPluginEnabler
-
     @JvmStatic
     var isPerProjectEnabled: Boolean
       get() = isPerProjectEnabledValue.asBoolean()
       set(value) = isPerProjectEnabledValue.setValue(value)
 
+    @JvmStatic
+    @JvmOverloads
+    fun findPluginTracker(
+      project: Project,
+      pluginEnabler: PluginEnabler = PluginEnabler.getInstance(),
+    ): ProjectPluginTracker? = (pluginEnabler as? DynamicPluginEnabler)?.getPluginTracker(project)
+
     internal class EnableDisablePluginsActivity : StartupActivity {
-      init {
-        if (ApplicationManager.getApplication().isUnitTestMode) {
-          throw ExtensionNotApplicableException.INSTANCE
-        }
-      }
+
+      private val dynamicPluginEnabler = PluginEnabler.getInstance() as? DynamicPluginEnabler
+                                         ?: throw ExtensionNotApplicableException.INSTANCE
 
       override fun runActivity(project: Project) {
-        val pluginEnabler = getInstance()
-        val tracker = pluginEnabler.getPluginTracker(project)
+        val tracker = dynamicPluginEnabler.getPluginTracker(project)
         val projects = openProjectsExcludingCurrent(project)
 
         val pluginIdsToLoad = tracker.enabledPluginsIds
-          .union(pluginEnabler.locallyDisabledAndGloballyEnabledPlugins(projects))
+          .union(dynamicPluginEnabler.locallyDisabledAndGloballyEnabledPlugins(projects))
 
         val pluginIdsToUnload = tracker.disabledPluginsIds
 
@@ -70,7 +71,7 @@ class DynamicPluginEnabler : SimplePersistentStateComponent<DynamicPluginEnabler
             indicator?.let {
               it.text = IdeBundle.message("plugins.progress.unloading.plugins.for.current.project.title", project.name)
             }
-            pluginEnabler.unloadPlugins(
+            dynamicPluginEnabler.unloadPlugins(
               pluginIdsToUnload.toPluginDescriptors(),
               project,
               projects,
@@ -87,44 +88,38 @@ class DynamicPluginEnabler : SimplePersistentStateComponent<DynamicPluginEnabler
 
   init {
     val connection = ApplicationManager.getApplication().messageBus.connect()
-    connection.subscribe(
-      ProjectManager.TOPIC,
-      object : ProjectManagerListener {
-        override fun projectClosing(project: Project) {
-          if (applicationShuttingDown) return
-
-          val tracker = getPluginTracker(project)
-          val projects = openProjectsExcludingCurrent(project)
-
-          val descriptorsToLoad = if (projects.isNotEmpty())
-            emptyList()
-          else
-            tracker.disabledPluginsIds
-              .filterNot { isDisabled(it) }
-              .toPluginDescriptors()
-          DynamicPlugins.loadPlugins(descriptorsToLoad)
-
-          val descriptorsToUnload = tracker.enabledPluginsIds
-            .union(locallyDisabledPlugins(projects))
+    connection.subscribe(ProjectManager.TOPIC, object : ProjectManagerListener {
+      override fun projectClosing(project: Project) {
+        if (applicationShuttingDown) return
+
+        val tracker = getPluginTracker(project)
+        val projects = openProjectsExcludingCurrent(project)
+
+        val descriptorsToLoad = if (projects.isNotEmpty())
+          emptyList()
+        else
+          tracker.disabledPluginsIds
+            .filterNot { isDisabled(it) }
             .toPluginDescriptors()
+        DynamicPlugins.loadPlugins(descriptorsToLoad)
 
-          unloadPlugins(
-            descriptorsToUnload,
-            project,
-            projects,
-          )
-        }
+        val descriptorsToUnload = tracker.enabledPluginsIds
+          .union(locallyDisabledPlugins(projects))
+          .toPluginDescriptors()
+
+        unloadPlugins(
+          descriptorsToUnload,
+          project,
+          projects,
+        )
       }
-    )
+    })
 
-    connection.subscribe(
-      AppLifecycleListener.TOPIC,
-      object : AppLifecycleListener {
-        override fun appWillBeClosed(isRestart: Boolean) {
-          applicationShuttingDown = true
-        }
+    connection.subscribe(AppLifecycleListener.TOPIC, object : AppLifecycleListener {
+      override fun appWillBeClosed(isRestart: Boolean) {
+        applicationShuttingDown = true
       }
-    )
+    })
   }
 
   fun getPluginTracker(project: Project): ProjectPluginTracker = state.findStateByProject(project)
index a4140d1fad462c1c1d705381c818b9fc298ac285..cfd57aac10945c3c6a8479b57c430e98fdda5ac9 100644 (file)
@@ -25,13 +25,9 @@ public class InstalledPluginsTableModel {
   protected final List<IdeaPluginDescriptor> view = new ArrayList<>();
   private final Map<PluginId, PluginEnabledState> myEnabled = new HashMap<>();
   private final @Nullable Project myProject;
-  private final @Nullable ProjectPluginTracker myPluginTracker;
 
   public InstalledPluginsTableModel(@Nullable Project project) {
     myProject = project;
-    myPluginTracker = myProject != null ?
-                      DynamicPluginEnabler.getInstance().getPluginTracker(myProject) :
-                      null;
 
     ApplicationInfoEx appInfo = ApplicationInfoEx.getInstanceEx();
     for (IdeaPluginDescriptor plugin : PluginManagerCore.getPlugins()) {
@@ -45,8 +41,9 @@ public class InstalledPluginsTableModel {
     }
     view.addAll(InstalledPluginsState.getInstance().getInstalledPlugins());
 
+    ProjectPluginTracker pluginTracker = myProject != null ? DynamicPluginEnabler.findPluginTracker(myProject) : null;
     for (IdeaPluginDescriptor descriptor : view) {
-      setEnabled(descriptor);
+      setEnabled(descriptor, pluginTracker);
     }
   }
 
@@ -58,12 +55,13 @@ public class InstalledPluginsTableModel {
     return isLoaded(pluginId, getEnabledMap());
   }
 
-  protected final void setEnabled(@NotNull IdeaPluginDescriptor ideaPluginDescriptor) {
+  private void setEnabled(@NotNull IdeaPluginDescriptor ideaPluginDescriptor,
+                          @Nullable ProjectPluginTracker pluginTracker) {
     PluginId pluginId = ideaPluginDescriptor.getPluginId();
 
-    PluginEnabledState enabled = myPluginTracker != null && myPluginTracker.isEnabled(pluginId) ?
+    PluginEnabledState enabled = pluginTracker != null && pluginTracker.isEnabled(pluginId) ?
                                  PluginEnabledState.ENABLED_FOR_PROJECT :
-                                 myPluginTracker != null && myPluginTracker.isDisabled(pluginId) ?
+                                 pluginTracker != null && pluginTracker.isDisabled(pluginId) ?
                                  PluginEnabledState.DISABLED_FOR_PROJECT :
                                  PluginManagerCore.isDisabled(pluginId) ?
                                  PluginEnabledState.DISABLED :
index cce67f2b290605c2b017a136ccba1af747d62c2b..cbd775b86590a3db4d0434828c80a53072ae2f04 100644 (file)
@@ -128,7 +128,10 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginE
     updatePluginDependencies(pluginIdMap);
     assertCanApply(pluginIdMap);
 
-    DynamicPluginEnablerState pluginEnablerState = DynamicPluginEnabler.getInstance().getState();
+    PluginEnabler pluginEnabler = PluginEnabler.getInstance();
+    DynamicPluginEnablerState pluginEnablerState = pluginEnabler instanceof DynamicPluginEnabler ?
+                                                   ((DynamicPluginEnabler)pluginEnabler).getState() :
+                                                   null;
     Set<PluginId> uninstallsRequiringRestart = new HashSet<>();
     for (IdeaPluginDescriptorImpl pluginDescriptor : myDynamicPluginsToUninstall) {
       myDiff.remove(pluginDescriptor);
@@ -141,7 +144,9 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginE
         getEnabledMap().remove(pluginId);
       }
 
-      pluginEnablerState.stopTracking(List.of(pluginId));
+      if (pluginEnablerState != null) {
+        pluginEnablerState.stopTracking(List.of(pluginId));
+      }
     }
 
     boolean installsRequiringRestart = myInstallsRequiringRestart;
@@ -173,7 +178,7 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginE
     myDynamicPluginsToInstall.clear();
     myPluginsToRemoveOnCancel.clear();
 
-    boolean enableDisableAppliedWithoutRestart = applyEnableDisablePlugins(parent);
+    boolean enableDisableAppliedWithoutRestart = applyEnableDisablePlugins(pluginEnabler, parent);
     myDynamicPluginsToUninstall.clear();
     myDiff.clear();
 
@@ -201,7 +206,8 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginE
     myPluginsToRemoveOnCancel.clear();
   }
 
-  private boolean applyEnableDisablePlugins(@Nullable JComponent parentComponent) {
+  private boolean applyEnableDisablePlugins(@NotNull PluginEnabler pluginEnabler,
+                                            @Nullable JComponent parentComponent) {
     EnumMap<PluginEnableDisableAction, List<IdeaPluginDescriptor>> descriptorsByAction = new EnumMap<>(PluginEnableDisableAction.class);
 
     for (Map.Entry<IdeaPluginDescriptor, Pair<PluginEnableDisableAction, PluginEnabledState>> entry : myDiff.entrySet()) {
@@ -226,15 +232,21 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginE
       }
     }
 
-    boolean result = true;
-    DynamicPluginEnabler pluginEnabler = DynamicPluginEnabler.getInstance();
+    boolean appliedWithoutRestart = true;
     for (Map.Entry<PluginEnableDisableAction, List<IdeaPluginDescriptor>> entry : descriptorsByAction.entrySet()) {
-      result &= pluginEnabler.updatePluginsState(entry.getValue(),
-                                                 entry.getKey(),
-                                                 getProject(),
-                                                 parentComponent);
-    }
-    return result;
+      PluginEnableDisableAction action = entry.getKey();
+      List<IdeaPluginDescriptor> descriptors = entry.getValue();
+
+      appliedWithoutRestart &= pluginEnabler instanceof DynamicPluginEnabler ?
+                               ((DynamicPluginEnabler)pluginEnabler).updatePluginsState(descriptors,
+                                                                                        action,
+                                                                                        getProject(),
+                                                                                        parentComponent) :
+                               action.isEnable() ?
+                               pluginEnabler.enable(descriptors) :
+                               pluginEnabler.disable(descriptors);
+    }
+    return appliedWithoutRestart;
   }
 
   public void pluginInstalledFromDisk(@NotNull PluginInstallCallbackData callbackData) {
index d35c0f241a74d8a96f4fc84cc4c527ab40e1b25c..6bc8ac5cddfb15064dcb4febfb83677983aede49 100644 (file)
@@ -22,15 +22,16 @@ class ProjectDependentPluginEnabledState(
       emptyList()
     }
     else {
-      val pluginEnabler = DynamicPluginEnabler.getInstance()
-      ProjectManager.getInstance()
-        .openProjects
-        .asSequence()
-        .filterNot { it == project }
-        .map { pluginEnabler.getPluginTracker(it) }
-        .filter { !PluginEnabler.HEADLESS.isDisabled(pluginId) || it.isEnabled(pluginId) }
-        .map { it.projectName }
-        .toList()
+      (PluginEnabler.getInstance() as? DynamicPluginEnabler)?.let { pluginEnabler ->
+        ProjectManager.getInstance()
+          .openProjects
+          .asSequence()
+          .filterNot { it == project }
+          .map { pluginEnabler.getPluginTracker(it) }
+          .filter { !pluginEnabler.isDisabled(pluginId) || it.isEnabled(pluginId) }
+          .map { it.projectName }
+          .toList()
+      } ?: emptyList()
     }
   }
 
index 2d9b721cb40cfbbac4c6c811975c832137519fbb..074fd83874fd44390303a0c3b539c99a0b071b6b 100644 (file)
@@ -1,10 +1,7 @@
 // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 package com.intellij.internal.statistic.collectors.fus.plugins
 
-import com.intellij.ide.plugins.DisabledPluginsState
-import com.intellij.ide.plugins.DynamicPluginEnabler
-import com.intellij.ide.plugins.PluginManagerCore
-import com.intellij.ide.plugins.ProjectPluginTracker
+import com.intellij.ide.plugins.*
 import com.intellij.internal.statistic.beans.MetricEvent
 import com.intellij.internal.statistic.eventLog.EventLogGroup
 import com.intellij.internal.statistic.eventLog.events.EventFields
@@ -51,11 +48,18 @@ class PluginsUsagesCollector : ApplicationUsagesCollector() {
   private fun getPerProjectPlugins(
     eventId: EventId1<Int>,
     countProducer: (ProjectPluginTracker) -> Set<PluginId>
-  ) = DynamicPluginEnabler.getInstance().trackers.values
-    .map { countProducer(it) }
-    .filter { it.isNotEmpty() }
-    .map { eventId.metric(it.size) }
-    .toSet()
+  ): Set<MetricEvent> {
+    return when (val pluginEnabler = PluginEnabler.getInstance()) {
+      is DynamicPluginEnabler ->
+        pluginEnabler.trackers.values
+          .map { countProducer(it) }
+          .filter { it.isNotEmpty() }
+          .map { eventId.metric(it.size) }
+          .toSet()
+      else ->
+        emptySet()
+    }
+  }
 
   private fun getNotBundledPlugins() = PluginManagerCore
     .getPlugins().asSequence()
index 0277e48970010310dde8cf0a62870181663471b1..0c622a2aaf7f732f878903d5523ca9e6c4aa651b 100644 (file)
 
     <applicationService serviceInterface="com.intellij.ide.plugins.PluginEnabler"
                         serviceImplementation="com.intellij.ide.plugins.DynamicPluginEnabler"
+                        headlessImplementation="com.intellij.ide.plugins.DisabledPluginsState"
+                        testServiceImplementation="com.intellij.ide.plugins.DynamicPluginEnabler"
                         preload="true"/>
     <startupActivity implementation="com.intellij.ide.plugins.DynamicPluginEnabler$Companion$EnableDisablePluginsActivity"
                      order="first"/>
index 7335aa1449a22dbe1bf1b856c900983bfeee2799..886d8af069c6910c91f55eaa6f946be359927d51 100644 (file)
@@ -37,7 +37,7 @@ class PerProjectPluginsTest {
     assertThat(descriptor).isNotNull
 
     val project = projectRule.project
-    val pluginEnabler = DynamicPluginEnabler.getInstance()
+    val pluginEnabler = PluginEnabler.getInstance() as DynamicPluginEnabler
 
     val loaded = pluginEnabler.updatePluginsState(
       listOf(descriptor),