mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Allow enabling/disabling plugins from Find Action without restart (IDEA-231814)
GitOrigin-RevId: 80e6b6e09943944301064b48dfa8b4a72b542fff
This commit is contained in:
committed by
intellij-monorepo-bot
parent
002f266c66
commit
a37dafcbad
@@ -347,6 +347,9 @@ object DynamicPlugins {
|
||||
} finally {
|
||||
IdeEventQueue.getInstance().flushQueue()
|
||||
|
||||
if (ApplicationManager.getApplication().isUnitTestMode && !(loadedPluginDescriptor.pluginClassLoader is PluginClassLoader)) {
|
||||
return true
|
||||
}
|
||||
val classLoaderUnloaded = loadedPluginDescriptor.unloadClassLoader()
|
||||
if (!classLoaderUnloaded) {
|
||||
if (Registry.`is`("ide.plugins.snapshot.on.unload.fail") && MemoryDumpHelper.memoryDumpAvailable() && !ApplicationManager.getApplication().isUnitTestMode) {
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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.plugins;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.PluginDescriptor;
|
||||
import com.intellij.openapi.extensions.PluginId;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PluginEnabler {
|
||||
private static final Logger LOG = Logger.getInstance(PluginEnabler.class);
|
||||
|
||||
public static boolean enablePlugins(Collection<IdeaPluginDescriptor> plugins, boolean enable) {
|
||||
return updatePluginEnabledState(enable ? plugins : Collections.emptyList(),
|
||||
enable ? Collections.emptyList() : plugins,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the requested enabled state was applied without restart, false if restart is required
|
||||
*/
|
||||
public static boolean updatePluginEnabledState(Collection<IdeaPluginDescriptor> pluginsToEnable,
|
||||
Collection<IdeaPluginDescriptor> pluginsToDisable,
|
||||
@Nullable JComponent parentComponent) {
|
||||
List<IdeaPluginDescriptorImpl> pluginDescriptorsToEnable = ContainerUtil.map(pluginsToEnable, PluginEnabler::loadFullDescriptor);
|
||||
List<IdeaPluginDescriptorImpl> pluginDescriptorsToDisable = ContainerUtil.map(pluginsToDisable, PluginEnabler::loadFullDescriptor);
|
||||
|
||||
Set<PluginId> disabledIds = PluginManagerCore.getDisabledIds();
|
||||
for (PluginDescriptor descriptor : pluginsToEnable) {
|
||||
descriptor.setEnabled(true);
|
||||
disabledIds.remove(descriptor.getPluginId());
|
||||
}
|
||||
for (PluginDescriptor descriptor : pluginsToDisable) {
|
||||
descriptor.setEnabled(false);
|
||||
disabledIds.add(descriptor.getPluginId());
|
||||
}
|
||||
|
||||
try {
|
||||
PluginManagerCore.saveDisabledPlugins(disabledIds, false);
|
||||
}
|
||||
catch (IOException e) {
|
||||
PluginManagerMain.LOG.error(e);
|
||||
}
|
||||
|
||||
if (ContainerUtil.all(pluginDescriptorsToDisable, (plugin) -> DynamicPlugins.allowLoadUnloadWithoutRestart(plugin)) &&
|
||||
ContainerUtil.all(pluginDescriptorsToEnable, (plugin) -> DynamicPlugins.allowLoadUnloadWithoutRestart(plugin))) {
|
||||
boolean needRestart = false;
|
||||
for (IdeaPluginDescriptor descriptor : pluginDescriptorsToDisable) {
|
||||
if (!DynamicPlugins.unloadPluginWithProgress(parentComponent, (IdeaPluginDescriptorImpl)descriptor, true)) {
|
||||
needRestart = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!needRestart) {
|
||||
for (IdeaPluginDescriptor descriptor : pluginDescriptorsToEnable) {
|
||||
DynamicPlugins.loadPlugin((IdeaPluginDescriptorImpl)descriptor, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static IdeaPluginDescriptorImpl loadFullDescriptor(PluginDescriptor descriptor) {
|
||||
// PluginDescriptor fields are cleaned after the plugin is loaded, so we need to reload the descriptor to check if it's dynamic
|
||||
IdeaPluginDescriptorImpl fullDescriptor =
|
||||
PluginManager.loadDescriptor(((IdeaPluginDescriptorImpl)descriptor).getPluginPath(), PluginManagerCore.PLUGIN_XML, Collections
|
||||
.emptySet());
|
||||
if (fullDescriptor == null) {
|
||||
LOG.error("Could not load full descriptor for plugin " + descriptor.getPath());
|
||||
fullDescriptor = (IdeaPluginDescriptorImpl)descriptor;
|
||||
}
|
||||
return fullDescriptor;
|
||||
}
|
||||
}
|
||||
@@ -190,8 +190,8 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM
|
||||
}
|
||||
|
||||
private boolean applyEnableDisablePlugins(JComponent parentComponent, Map<PluginId, Boolean> enabledMap) {
|
||||
List<IdeaPluginDescriptorImpl> pluginDescriptorsToDisable = new ArrayList<>();
|
||||
List<IdeaPluginDescriptorImpl> pluginDescriptorsToEnable = new ArrayList<>();
|
||||
List<IdeaPluginDescriptor> pluginDescriptorsToDisable = new ArrayList<>();
|
||||
List<IdeaPluginDescriptor> pluginDescriptorsToEnable = new ArrayList<>();
|
||||
|
||||
int rowCount = getRowCount();
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
@@ -202,55 +202,16 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM
|
||||
|
||||
boolean enabled = isEnabled(descriptor.getPluginId());
|
||||
if (enabled != descriptor.isEnabled()) {
|
||||
// PluginDescriptor fields are cleaned after the plugin is loaded, so we need to reload the descriptor to check if it's dynamic
|
||||
IdeaPluginDescriptorImpl fullDescriptor = PluginManager.loadDescriptor(((IdeaPluginDescriptorImpl)descriptor).getPluginPath(), PluginManagerCore.PLUGIN_XML, Collections.emptySet());
|
||||
if (fullDescriptor == null) {
|
||||
LOG.error("Could not load full descriptor for plugin " + descriptor.getPath());
|
||||
fullDescriptor = (IdeaPluginDescriptorImpl)descriptor;
|
||||
}
|
||||
if (!enabled) {
|
||||
pluginDescriptorsToDisable.add(fullDescriptor);
|
||||
pluginDescriptorsToDisable.add(descriptor);
|
||||
}
|
||||
else {
|
||||
pluginDescriptorsToEnable.add(fullDescriptor);
|
||||
pluginDescriptorsToEnable.add(descriptor);
|
||||
}
|
||||
}
|
||||
descriptor.setEnabled(enabled);
|
||||
}
|
||||
|
||||
List<PluginId> disableIds = new ArrayList<>();
|
||||
for (Map.Entry<PluginId, Boolean> entry : enabledMap.entrySet()) {
|
||||
Boolean enabled = entry.getValue();
|
||||
if (enabled != null && !enabled) {
|
||||
disableIds.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
PluginManagerCore.saveDisabledPlugins(disableIds, false);
|
||||
}
|
||||
catch (IOException e) {
|
||||
PluginManagerMain.LOG.error(e);
|
||||
}
|
||||
|
||||
if (ContainerUtil.all(pluginDescriptorsToDisable, (plugin) -> DynamicPlugins.allowLoadUnloadWithoutRestart(plugin)) &&
|
||||
ContainerUtil.all(pluginDescriptorsToEnable, (plugin) -> DynamicPlugins.allowLoadUnloadWithoutRestart(plugin))) {
|
||||
boolean needRestart = false;
|
||||
for (IdeaPluginDescriptor descriptor : pluginDescriptorsToDisable) {
|
||||
if (!DynamicPlugins.unloadPluginWithProgress(parentComponent, (IdeaPluginDescriptorImpl)descriptor, true)) {
|
||||
needRestart = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!needRestart) {
|
||||
for (IdeaPluginDescriptor descriptor : pluginDescriptorsToEnable) {
|
||||
DynamicPlugins.loadPlugin((IdeaPluginDescriptorImpl)descriptor, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return PluginEnabler.updatePluginEnabledState(pluginDescriptorsToDisable, pluginDescriptorsToEnable, parentComponent);
|
||||
}
|
||||
|
||||
public void pluginInstalledFromDisk(@NotNull PluginInstallCallbackData callbackData) {
|
||||
|
||||
@@ -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.ide.ui;
|
||||
|
||||
import com.intellij.ide.IdeBundle;
|
||||
@@ -47,12 +47,14 @@ final class PluginBooleanOptionDescriptor extends BooleanOptionDescription {
|
||||
@Override
|
||||
public void setOptionState(boolean enabled) {
|
||||
Collection<IdeaPluginDescriptor> autoSwitchedIds = enabled ? getPluginsIdsToEnable(plugin) : getPluginsIdsToDisable(plugin);
|
||||
PluginManager.getInstance().enablePlugins(autoSwitchedIds, enabled);
|
||||
boolean enabledWithoutRestart = PluginEnabler.enablePlugins(autoSwitchedIds, enabled);
|
||||
if (autoSwitchedIds.size() > 1) {
|
||||
showAutoSwitchNotification(autoSwitchedIds, enabled);
|
||||
}
|
||||
|
||||
ourRestartNeededNotifier.showNotification();
|
||||
if (!enabledWithoutRestart) {
|
||||
ourRestartNeededNotifier.showNotification();
|
||||
}
|
||||
}
|
||||
|
||||
private void showAutoSwitchNotification(@NotNull Collection<IdeaPluginDescriptor> autoSwitchedPlugins, boolean enabled) {
|
||||
|
||||
@@ -32,12 +32,10 @@ import com.intellij.testFramework.assertions.Assertions.assertThat
|
||||
import com.intellij.testFramework.rules.InMemoryFsRule
|
||||
import com.intellij.ui.switcher.ShowQuickActionPopupAction
|
||||
import com.intellij.util.KeyedLazyInstanceEP
|
||||
import com.intellij.util.SystemProperties
|
||||
import com.intellij.util.io.write
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.intellij.util.xmlb.annotations.Attribute
|
||||
import org.junit.*
|
||||
import org.junit.Assume.assumeFalse
|
||||
import java.io.File
|
||||
|
||||
@RunsInEdt
|
||||
@@ -52,12 +50,6 @@ class DynamicPluginsTest {
|
||||
val projectRule = ProjectRule()
|
||||
|
||||
val receivedNotifications = mutableListOf<UISettings>()
|
||||
|
||||
@JvmStatic
|
||||
@BeforeClass
|
||||
fun check() {
|
||||
assumeFalse(SystemProperties.getBooleanProperty("skip.DynamicPluginsTest", true))
|
||||
}
|
||||
}
|
||||
|
||||
@Rule
|
||||
@@ -361,6 +353,31 @@ class DynamicPluginsTest {
|
||||
Disposer.dispose(disposable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun disableWithoutRestart() {
|
||||
val pluginId = "disableWithoutRestart" + System.currentTimeMillis()
|
||||
val disposable = loadPluginWithText("""
|
||||
<idea-plugin>
|
||||
<id>$pluginId</id>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<applicationService serviceImplementation="${MyPersistentComponent::class.java.name}"/>
|
||||
</extensions>
|
||||
</idea-plugin>""".trimIndent(), DynamicPlugins::class.java.classLoader)
|
||||
assertThat(ServiceManager.getService(MyPersistentComponent::class.java)).isNotNull()
|
||||
|
||||
val pluginDescriptor = PluginManagerCore.getPlugin(PluginId.getId(pluginId))!!
|
||||
val success = PluginEnabler.updatePluginEnabledState(emptyList(), listOf(pluginDescriptor), null)
|
||||
assertThat(success).isTrue()
|
||||
assertThat(pluginDescriptor.isEnabled).isFalse()
|
||||
assertThat(ServiceManager.getService(MyPersistentComponent::class.java)).isNull()
|
||||
|
||||
assertThat(PluginEnabler.updatePluginEnabledState(listOf(pluginDescriptor), emptyList(), null)).isTrue()
|
||||
assertThat(pluginDescriptor.isEnabled).isTrue()
|
||||
assertThat(ServiceManager.getService(MyPersistentComponent::class.java)).isNotNull()
|
||||
|
||||
Disposer.dispose(disposable)
|
||||
}
|
||||
|
||||
private fun loadPluginWithOptionalDependency(pluginXmlText: String, optionalDependencyDescriptorText: String): Disposable {
|
||||
val directory = FileUtil.createTempDirectory("test", "test", true)
|
||||
val plugin = File(directory, "/plugin/META-INF/plugin.xml")
|
||||
|
||||
@@ -29,7 +29,11 @@ fun loadDescriptorInTest(dir: Path, disabledPlugins: Set<PluginId> = emptySet())
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun loadExtensionWithText(extensionTag: String, loader: ClassLoader, ns: String = "com.intellij"): Disposable {
|
||||
fun loadExtensionWithText(
|
||||
extensionTag: String,
|
||||
loader: ClassLoader = DynamicPlugins::class.java.classLoader,
|
||||
ns: String = "com.intellij"
|
||||
): Disposable {
|
||||
val name = "test" + abs(extensionTag.hashCode())
|
||||
val text = """<idea-plugin>
|
||||
<name>$name</name>
|
||||
|
||||
Reference in New Issue
Block a user