Assorted UI fixes for installing plugins without restart

GitOrigin-RevId: 6abd7f7b31dfff6e211a70b24767be84dce0ea4e
This commit is contained in:
Dmitry Jemerov
2019-08-28 12:42:07 +00:00
committed by intellij-monorepo-bot
parent 7e65341c31
commit 31e1b50371
9 changed files with 146 additions and 84 deletions
@@ -34,6 +34,7 @@ public final class InstalledPluginsState {
private final Map<PluginId, IdeaPluginDescriptor> myInstalledPlugins = ContainerUtil.newIdentityHashMap();
private final Map<PluginId, IdeaPluginDescriptor> myInstalledWithoutRestartPlugins = ContainerUtil.newIdentityHashMap();
private final Map<PluginId, IdeaPluginDescriptor> myUpdatedPlugins = ContainerUtil.newIdentityHashMap();
private final Map<PluginId, IdeaPluginDescriptor> myUninstalledWithoutRestartPlugins = ContainerUtil.newIdentityHashMap();
private final Set<String> myOutdatedPlugins = new SmartHashSet<>();
@NotNull
@@ -55,6 +56,18 @@ public final class InstalledPluginsState {
}
}
public boolean wasInstalledWithoutRestart(@NotNull PluginId id) {
synchronized (myLock) {
return myInstalledWithoutRestartPlugins.containsKey(id);
}
}
public boolean wasUninstalledWithoutRestart(@NotNull PluginId id) {
synchronized (myLock) {
return myUninstalledWithoutRestartPlugins.containsKey(id);
}
}
public boolean wasUpdated(@NotNull PluginId id) {
synchronized (myLock) {
return myUpdatedPlugins.containsKey(id);
@@ -85,7 +98,6 @@ public final class InstalledPluginsState {
}
}
}
/**
* Should be called whenever a new plugin is installed or an existing one is updated.
*/
@@ -104,4 +116,18 @@ public final class InstalledPluginsState {
}
}
}
public void onPluginUninstall(@NotNull IdeaPluginDescriptor descriptor, boolean restartNeeded) {
PluginId id = descriptor.getPluginId();
synchronized (myLock) {
if (!restartNeeded) {
myUninstalledWithoutRestartPlugins.put(id, descriptor);
}
}
}
public void resetChangesAppliedWithoutRestart() {
myInstalledWithoutRestartPlugins.clear();
}
}
@@ -5,6 +5,7 @@ import com.intellij.CommonBundle;
import com.intellij.ide.startup.StartupActionScriptManager;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
@@ -41,6 +42,8 @@ import java.util.zip.ZipFile;
* @author stathik
*/
public class PluginInstaller {
private static final Logger LOG = Logger.getInstance(PluginInstaller.class);
public static final String UNKNOWN_HOST_MARKER = "__unknown_repository__";
static final Object ourLock = new Object();
@@ -74,7 +77,7 @@ public class PluginInstaller {
else {
boolean needRestart = !DynamicPlugins.isUnloadSafe(pluginDescriptor);
if (needRestart) {
StartupActionScriptManager.addActionCommand(new StartupActionScriptManager.DeleteCommand(pluginDescriptor.getPath()));
uninstallAfterRestart(pluginDescriptor);
}
PluginStateManager.fireState(pluginDescriptor, false);
@@ -85,13 +88,24 @@ public class PluginInstaller {
return false;
}
private static void uninstallAfterRestart(IdeaPluginDescriptor pluginDescriptor) throws IOException {
StartupActionScriptManager.addActionCommand(new StartupActionScriptManager.DeleteCommand(pluginDescriptor.getPath()));
}
public static boolean uninstallDynamicPlugin(IdeaPluginDescriptor pluginDescriptor) {
boolean uninstalledWithoutRestart;
uninstalledWithoutRestart = DynamicPlugins.unloadPlugin((IdeaPluginDescriptorImpl)pluginDescriptor);
boolean uninstalledWithoutRestart = DynamicPlugins.unloadPlugin((IdeaPluginDescriptorImpl)pluginDescriptor);
if (uninstalledWithoutRestart) {
FileUtil.delete(pluginDescriptor.getPath());
PluginManagerCore.setPlugins(ArrayUtil.remove(PluginManagerCore.getPlugins(), pluginDescriptor));
}
else {
try {
uninstallAfterRestart(pluginDescriptor);
}
catch (IOException e) {
LOG.error(e);
}
}
return uninstalledWithoutRestart;
}
@@ -110,7 +124,8 @@ public class PluginInstaller {
commands.add(new StartupActionScriptManager.CopyCommand(sourceFile, new File(pluginsPath, sourceFile.getName())));
}
else {
commands.add(new StartupActionScriptManager.DeleteCommand(new File(pluginsPath, rootEntryName(sourceFile)))); // drops stale directory
commands
.add(new StartupActionScriptManager.DeleteCommand(new File(pluginsPath, rootEntryName(sourceFile)))); // drops stale directory
commands.add(new StartupActionScriptManager.UnzipCommand(sourceFile, new File(pluginsPath)));
}
@@ -232,7 +247,7 @@ public class PluginInstaller {
IdeaPluginDescriptorImpl targetDescriptor = PluginManagerCore.loadDescriptor(targetFile, PluginManagerCore.PLUGIN_XML);
if (targetDescriptor != null) {
DynamicPlugins.loadPlugin(targetDescriptor);
PluginManagerCore.setPlugins(ArrayUtil.mergeArrays(PluginManagerCore.getPlugins(), new IdeaPluginDescriptor[] { targetDescriptor }));
PluginManagerCore.setPlugins(ArrayUtil.mergeArrays(PluginManagerCore.getPlugins(), new IdeaPluginDescriptor[]{targetDescriptor}));
}
}
}
@@ -257,7 +272,8 @@ public class PluginInstaller {
}
if (!notInstalled.isEmpty()) {
String deps = StringUtil.join(notInstalled, PluginId::toString, ", ");
String message = "Plugin " + pluginDescriptor.getName() + " depends on unknown plugin" + (notInstalled.size() > 1 ? "s " : " ") + deps;
String message =
"Plugin " + pluginDescriptor.getName() + " depends on unknown plugin" + (notInstalled.size() > 1 ? "s " : " ") + deps;
MessagesEx.showWarningDialog(parent, message, "Install Plugin");
}
if (!disabledIds.isEmpty()) {
@@ -270,7 +286,9 @@ public class PluginInstaller {
String part = "disabled plugin" + (dependencies.size() > 1 ? "s " : " ");
String deps = StringUtil.join(dependencies, IdeaPluginDescriptor::getName, ", ");
String message = "Plugin " + pluginDescriptor.getName() + " depends on " + part + deps + ". Enable " + part.trim() + "?";
if (Messages.showOkCancelDialog(message, "Install Plugin", "Install", CommonBundle.getCancelButtonText(), Messages.getWarningIcon()) == Messages.OK) {
if (Messages
.showOkCancelDialog(message, "Install Plugin", "Install", CommonBundle.getCancelButtonText(), Messages.getWarningIcon()) ==
Messages.OK) {
model.enableRows(dependencies.toArray(new IdeaPluginDescriptor[0]), Boolean.TRUE);
}
}
@@ -288,7 +306,8 @@ public class PluginInstaller {
descriptor.setTitle("Choose Plugin File");
descriptor.setDescription("JAR and ZIP archives are accepted");
final String oldPath = PropertiesComponent.getInstance().getValue(PLUGINS_PRESELECTION_PATH);
final VirtualFile toSelect = oldPath == null ? null : VfsUtil.findFileByIoFile(new File(FileUtil.toSystemDependentName(oldPath)), false);
final VirtualFile toSelect =
oldPath == null ? null : VfsUtil.findFileByIoFile(new File(FileUtil.toSystemDependentName(oldPath)), false);
FileChooser.chooseFile(descriptor, null, parent, toSelect, virtualFile -> {
File file = VfsUtilCore.virtualToIoFile(virtualFile);
PropertiesComponent.getInstance().setValue(PLUGINS_PRESELECTION_PATH, FileUtil.toSystemIndependentName(file.getParent()));
@@ -1586,6 +1586,8 @@ public class PluginManagerConfigurable
myShutdownCallback.run();
myShutdownCallback = null;
}
InstalledPluginsState.getInstance().resetChangesAppliedWithoutRestart();
}
@Override
@@ -201,7 +201,7 @@ public abstract class CellPluginComponent extends JPanel {
public abstract void showProgress();
public abstract void hideProgress(boolean success);
public abstract void hideProgress(boolean success, boolean restartRequired);
public void clearProgress() {
throw new UnsupportedOperationException();
@@ -177,7 +177,7 @@ public class GridCellPluginComponent extends CellPluginComponent {
}
@Override
public void hideProgress(boolean success) {
public void hideProgress(boolean success, boolean restartRequired) {
myIndicator = null;
JComponent lastComponent = myLastComponent;
if (success) {
@@ -3,6 +3,7 @@ package com.intellij.ide.plugins.newui;
import com.intellij.ide.plugins.PluginManagerConfigurable;
import com.intellij.ui.JBColor;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
@@ -48,13 +49,17 @@ public class InstallButton extends ColorButton {
}
@Override
public void setEnabled(boolean enabled) {
public void setEnabled(boolean b) {
super.setEnabled(b);
}
public void setEnabled(boolean enabled, @Nullable String statusText) {
super.setEnabled(enabled);
if (enabled) {
setTextAndSize();
}
else {
setText("Installed");
setText(statusText);
setWidth(this, 80);
}
}
@@ -45,7 +45,7 @@ public class ListPluginComponent extends CellPluginComponent {
private final JLabel myIconComponent = new JLabel(AllIcons.Plugins.PluginLogo_40);
private final BaselineLayout myLayout = new BaselineLayout();
private JButton myRestartButton;
private JButton myInstallButton;
private InstallButton myInstallButton;
private JButton myUpdateButton;
private JCheckBox myEnableDisableButton;
private JLabel myRating;
@@ -105,7 +105,7 @@ public class ListPluginComponent extends CellPluginComponent {
myLayout.addButtonComponent(myInstallButton = new InstallButton(false));
myInstallButton.addActionListener(e -> myPluginModel.installOrUpdatePlugin(myPlugin, null));
myInstallButton.setEnabled(PluginManagerCore.getPlugin(myPlugin.getPluginId()) == null);
myInstallButton.setEnabled(PluginManagerCore.getPlugin(myPlugin.getPluginId()) == null, "Installed");
ColorButton.setWidth72(myInstallButton);
}
}
@@ -390,12 +390,17 @@ public class ListPluginComponent extends CellPluginComponent {
}
@Override
public void hideProgress(boolean success) {
public void hideProgress(boolean success, boolean restartRequired) {
myIndicator = null;
myLayout.removeProgressComponent();
if (success) {
enableRestart();
if (restartRequired) {
enableRestart();
}
else {
myInstallButton.setEnabled(false, "Installed");
}
}
fullRepaint();
@@ -152,7 +152,7 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM
}
}
return applyEnableDisablePlugins(enabledMap) || needRestartForUninstall;
return applyEnableDisablePlugins(enabledMap) && !needRestartForUninstall;
}
private boolean applyEnableDisablePlugins(Map<PluginId, Boolean> enabledMap) {
@@ -407,13 +407,13 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM
List<CellPluginComponent> gridComponents = myGridMap.get(descriptor);
if (gridComponents != null) {
for (CellPluginComponent gridComponent : gridComponents) {
gridComponent.hideProgress(success);
gridComponent.hideProgress(success, restartRequired);
}
}
List<CellPluginComponent> listComponents = myListMap.get(descriptor);
if (listComponents != null) {
for (CellPluginComponent listComponent : listComponents) {
listComponent.hideProgress(success);
listComponent.hideProgress(success, restartRequired);
}
}
for (PluginDetailsPageComponent panel : myDetailPanels) {
@@ -537,7 +537,7 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM
for (Entry<IdeaPluginDescriptor, List<CellPluginComponent>> entry : myGridMap.entrySet()) {
if (id.equals(entry.getKey().getPluginId().getIdString())) {
for (CellPluginComponent component : entry.getValue()) {
component.hideProgress(true);
component.hideProgress(true, true);
}
break;
}
@@ -769,6 +769,7 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM
try {
((IdeaPluginDescriptorImpl)descriptor).setDeleted(true);
needRestartForUninstall = PluginInstaller.prepareToUninstall(descriptor.getPluginId());
InstalledPluginsState.getInstance().onPluginUninstall(descriptor, needRestartForUninstall);
if (!needRestartForUninstall) {
myDynamicPluginsToUninstall.add(descriptor);
}
@@ -794,8 +795,8 @@ public class MyPluginModel extends InstalledPluginsTableModel implements PluginM
}
for (PluginDetailsPageComponent panel : myDetailPanels) {
if (panel.myPlugin == descriptor && needRestartForUninstall) {
panel.enableRestart();
if (panel.myPlugin == descriptor) {
panel.updateButtons();
}
}
}
@@ -50,7 +50,7 @@ public class PluginDetailsPageComponent extends MultiPanel {
private final JEditorPane myNameComponent = createNameComponent();
private final BaselinePanel myNameAndButtons = new BaselinePanel();
private JButton myRestartButton;
private JButton myInstallButton;
private InstallButton myInstallButton;
private JButton myUpdateButton;
private JButton myEnableDisableButton;
private JBOptionButton myEnableDisableUninstallButton;
@@ -345,56 +345,7 @@ public class PluginDetailsPageComponent extends MultiPanel {
myNameComponent.setText("<html><span>" + myPlugin.getName() + "</span></html>");
updateIcon();
if (myMarketplace) {
boolean installed = InstalledPluginsState.getInstance().wasInstalled(myPlugin.getPluginId());
myRestartButton.setVisible(installed);
myInstallButton.setEnabled(PluginManagerCore.getPlugin(myPlugin.getPluginId()) == null);
myInstallButton.setVisible(!installed);
myUpdateButton.setVisible(false);
myEnableDisableButton.setVisible(false);
myEnableDisableUninstallButton.setVisible(false);
myUninstallButton.setVisible(false);
}
else {
myInstallButton.setVisible(false);
boolean restart = myPlugin instanceof IdeaPluginDescriptorImpl && ((IdeaPluginDescriptorImpl)myPlugin).isDeleted();
if (!restart) {
InstalledPluginsState pluginsState = InstalledPluginsState.getInstance();
PluginId id = myPlugin.getPluginId();
restart = pluginsState.wasInstalled(id) || pluginsState.wasUpdated(id);
}
if (restart) {
myRestartButton.setVisible(true);
myUpdateButton.setVisible(false);
myEnableDisableButton.setVisible(false);
myEnableDisableUninstallButton.setVisible(false);
myUninstallButton.setVisible(false);
}
else {
myRestartButton.setVisible(false);
boolean bundled = myPlugin.isBundled();
String title = myPluginModel.getEnabledTitle(myPlugin);
boolean errors = myPluginModel.hasErrors(myPlugin);
myUpdateButton.setVisible(myUpdateDescriptor != null && !errors);
myEnableDisableButton.setVisible(bundled && !errors);
myEnableDisableButton.setText(title);
myEnableDisableUninstallButton.setVisible(!bundled && !errors);
myEnableDisableUninstallButton.setText(title);
myUninstallButton.setVisible(!bundled && errors);
}
updateEnableForNameAndIcon();
updateErrors();
}
updateButtons();
boolean bundled = myPlugin.isBundled() && !myPlugin.allowBundledUpdate();
String version = bundled ? "bundled" : myPlugin.getVersion();
@@ -500,6 +451,68 @@ public class PluginDetailsPageComponent extends MultiPanel {
}
}
public void updateButtons() {
if (myMarketplace) {
boolean installed = InstalledPluginsState.getInstance().wasInstalled(myPlugin.getPluginId());
boolean installedWithoutRestart = InstalledPluginsState.getInstance().wasInstalledWithoutRestart(myPlugin.getPluginId());
myRestartButton.setVisible(installed);
myInstallButton.setEnabled(PluginManagerCore.getPlugin(myPlugin.getPluginId()) == null && !installedWithoutRestart, "Installed");
myInstallButton.setVisible(!installed);
myUpdateButton.setVisible(false);
myEnableDisableButton.setVisible(false);
myEnableDisableUninstallButton.setVisible(false);
myUninstallButton.setVisible(false);
}
else {
myInstallButton.setVisible(false);
boolean uninstalled = myPlugin instanceof IdeaPluginDescriptorImpl && ((IdeaPluginDescriptorImpl)myPlugin).isDeleted();
boolean uninstalledWithoutRestart = InstalledPluginsState.getInstance().wasUninstalledWithoutRestart(myPlugin.getPluginId());
if (!uninstalled) {
InstalledPluginsState pluginsState = InstalledPluginsState.getInstance();
PluginId id = myPlugin.getPluginId();
uninstalled = pluginsState.wasInstalled(id) || pluginsState.wasUpdated(id);
}
if (uninstalled) {
if (uninstalledWithoutRestart) {
myRestartButton.setVisible(false);
myInstallButton.setVisible(true);
myInstallButton.setEnabled(false, "Uninstalled");
}
else {
myRestartButton.setVisible(true);
}
myUpdateButton.setVisible(false);
myEnableDisableButton.setVisible(false);
myEnableDisableUninstallButton.setVisible(false);
myUninstallButton.setVisible(false);
}
else {
myRestartButton.setVisible(false);
boolean bundled = myPlugin.isBundled();
String title = myPluginModel.getEnabledTitle(myPlugin);
boolean errors = myPluginModel.hasErrors(myPlugin);
myUpdateButton.setVisible(myUpdateDescriptor != null && !errors);
myEnableDisableButton.setVisible(bundled && !errors);
myEnableDisableButton.setText(title);
myEnableDisableUninstallButton.setVisible(!bundled && !errors);
myEnableDisableUninstallButton.setText(title);
myUninstallButton.setVisible(!bundled && errors);
}
updateEnableForNameAndIcon();
updateErrors();
}
}
private void updateIcon() {
boolean jb = PluginManagerConfigurable.isJBPlugin(myPlugin);
boolean errors = myPluginModel.hasErrors(myPlugin);
@@ -551,7 +564,7 @@ public class PluginDetailsPageComponent extends MultiPanel {
myNameAndButtons.removeProgressComponent();
if (success) {
enableRestart();
updateButtons();
}
if (repaint) {
fullRepaint();
@@ -598,15 +611,6 @@ public class PluginDetailsPageComponent extends MultiPanel {
}
}
public void enableRestart() {
myInstallButton.setVisible(false);
myUpdateButton.setVisible(false);
myEnableDisableButton.setVisible(false);
myEnableDisableUninstallButton.setVisible(false);
myUninstallButton.setVisible(false);
myRestartButton.setVisible(true);
}
@Nullable
private String getDescription() {
String description = myPlugin.getDescription();