diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginDropHandler.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginDropHandler.java index 60e363d918af..41d338a409de 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginDropHandler.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginDropHandler.java @@ -27,7 +27,7 @@ public class PluginDropHandler extends CustomFileDropHandler { public boolean handleDrop(@NotNull Transferable t, @Nullable Editor editor, Project project) { File file = getFile(t); if (file == null) return false; - return PluginInstaller.install(new InstalledPluginsTableModel(), file, PluginInstallCallbackDataKt::installPluginFromCallbackData, null); + return PluginInstaller.installFromDisk(new InstalledPluginsTableModel(), file, PluginInstallCallbackDataKt::installPluginFromCallbackData, null); } @Nullable diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstallOperation.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstallOperation.java index 24fa47dfcc04..79399d6d4951 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstallOperation.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstallOperation.java @@ -19,7 +19,6 @@ import com.intellij.openapi.updateSettings.impl.PluginDownloader; import com.intellij.openapi.updateSettings.impl.UpdateSettings; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.ArrayUtil; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.ApiStatus; @@ -37,7 +36,7 @@ public class PluginInstallOperation { private final PluginManagerMain.PluginEnabler myPluginEnabler; private final ProgressIndicator myIndicator; private boolean mySuccess = true; - private final Set myDependant = new HashSet<>(); + private final Set myDependant = new HashSet<>(); private boolean myAllowInstallWithoutRestart = false; private final List myPendingDynamicPluginInstalls = new ArrayList<>(); private boolean myRestartRequired = false; @@ -87,7 +86,7 @@ public class PluginInstallOperation { return mySuccess; } - public Set getInstalledDependentPlugins() { + public Set getInstalledDependentPlugins() { return myDependant; } @@ -160,16 +159,53 @@ public class PluginInstallOperation { private boolean prepareToInstall(PluginNode pluginNode, List pluginIds) throws IOException { - myDependant.add(pluginNode); + Ref toDisable = checkDependenciesAndReplacements(pluginNode, pluginIds); + if (toDisable == null) return false; + myShownErrors = false; + + PluginDownloader downloader = PluginDownloader.createDownloader(pluginNode, pluginNode.getRepositoryName(), null); + + IdeaPluginDescriptorImpl descriptor = downloader.prepareToInstallAndLoadDescriptor(myIndicator); + if (descriptor != null) { + boolean allowNoRestart = myAllowInstallWithoutRestart && DynamicPlugins.allowLoadUnloadWithoutRestart(descriptor); + if (allowNoRestart) { + myPendingDynamicPluginInstalls.add(new PendingDynamicPluginInstall(downloader.getFile(), descriptor)); + InstalledPluginsState state = InstalledPluginsState.getInstanceIfLoaded(); + if (state != null) { + state.onPluginInstall(downloader.getDescriptor(), false, false); + } + } + else { + myRestartRequired = true; + synchronized (PluginInstaller.ourLock) { + downloader.install(); + } + } + myDependant.add(new PluginInstallCallbackData(downloader.getFile(), descriptor, !allowNoRestart)); + pluginNode.setStatus(PluginNode.Status.DOWNLOADED); + if (!toDisable.isNull()) { + myPluginEnabler.disablePlugins(Collections.singleton(toDisable.get())); + } + } + else { + myShownErrors = downloader.isShownErrors(); + return false; + } + + return true; + } + + @Nullable + public Ref checkDependenciesAndReplacements(IdeaPluginDescriptor pluginNode, @Nullable List pluginIds) { // check for dependent plugins at first. - if (pluginNode.getDepends() != null && !pluginNode.getDepends().isEmpty()) { + List dependencies = pluginNode.getDependencies(); + if (!dependencies.isEmpty()) { // prepare plugins list for install - final PluginId[] optionalDependentPluginIds = pluginNode.getOptionalDependentPluginIds(); final List depends = new ArrayList<>(); final List optionalDeps = new ArrayList<>(); - for (int i = 0; i < pluginNode.getDepends().size(); i++) { - PluginId depPluginId = pluginNode.getDepends().get(i); + for (IdeaPluginDependency dependency : dependencies) { + PluginId depPluginId = dependency.getPluginId(); if (PluginManagerCore.isModuleDependency(depPluginId)) { PluginId pluginIdByModule = PluginModulesHelper.getInstance().getMarketplacePluginIdByModule(depPluginId); @@ -194,7 +230,7 @@ public class PluginInstallOperation { } if (depPluginDescriptor != null) { - if (ArrayUtil.indexOf(optionalDependentPluginIds, depPluginId) != -1) { + if (dependency.isOptional()) { optionalDeps.add(depPlugin); } else { @@ -215,10 +251,10 @@ public class PluginInstallOperation { }, ModalityState.any()); } catch (Exception e) { - return false; + return null; } if (!proceed[0] || !prepareToInstall(depends)) { - return false; + return null; } } @@ -234,10 +270,10 @@ public class PluginInstallOperation { }, ModalityState.any()); } catch (Exception e) { - return false; + return null; } if (proceed[0] && !prepareToInstall(optionalDeps)) { - return false; + return null; } } } @@ -262,37 +298,7 @@ public class PluginInstallOperation { }, ModalityState.any()); } } - - myShownErrors = false; - - PluginDownloader downloader = PluginDownloader.createDownloader(pluginNode, pluginNode.getRepositoryName(), null); - - IdeaPluginDescriptorImpl descriptor = downloader.prepareToInstallAndLoadDescriptor(myIndicator); - if (descriptor != null) { - if (myAllowInstallWithoutRestart && DynamicPlugins.allowLoadUnloadWithoutRestart(descriptor)) { - myPendingDynamicPluginInstalls.add(new PendingDynamicPluginInstall(downloader.getFile(), descriptor)); - InstalledPluginsState state = InstalledPluginsState.getInstanceIfLoaded(); - if (state != null) { - state.onPluginInstall(downloader.getDescriptor(), false, false); - } - } - else { - myRestartRequired = true; - synchronized (PluginInstaller.ourLock) { - downloader.install(); - } - } - pluginNode.setStatus(PluginNode.Status.DOWNLOADED); - if (!toDisable.isNull()) { - myPluginEnabler.disablePlugins(Collections.singleton(toDisable.get())); - } - } - else { - myShownErrors = downloader.isShownErrors(); - return false; - } - - return true; + return toDisable; } @NotNull diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstaller.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstaller.java index 2c50ae69bbd9..f8b3f05d6087 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstaller.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginInstaller.java @@ -25,8 +25,8 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ArrayUtilRt; import com.intellij.util.Consumer; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.io.Decompressor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -230,10 +230,10 @@ public final class PluginInstaller { PluginStateManager.addStateListener(listener); } - public static boolean install(@NotNull InstalledPluginsTableModel model, - @NotNull File file, - @NotNull Consumer callback, - @Nullable Component parent) { + public static boolean installFromDisk(@NotNull InstalledPluginsTableModel model, + @NotNull File file, + @NotNull Consumer callback, + @Nullable Component parent) { try { IdeaPluginDescriptorImpl pluginDescriptor = PluginDescriptorLoader.loadDescriptorFromArtifact(file.toPath(), null); if (pluginDescriptor == null) { @@ -273,19 +273,55 @@ public final class PluginInstaller { return false; } + PluginManagerMain.PluginEnabler pluginEnabler = model instanceof PluginManagerMain.PluginEnabler + ? (PluginManagerMain.PluginEnabler)model + : new PluginManagerMain.PluginEnabler.HEADLESS(); + Ref cancel = Ref.create(false); + Ref toDisable = new Ref<>(); + Ref dependenciesRequireRestart = Ref.create(false); + Set installedDependencies = new HashSet<>(); + boolean success = ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { + PluginInstallOperation dependencyInstallOperation = new PluginInstallOperation( + Collections.emptyList(), + CustomPluginRepositoryService.getInstance().getCustomRepositoryPlugins(), + pluginEnabler, + ProgressManager.getInstance().getProgressIndicator()); + dependencyInstallOperation.setAllowInstallWithoutRestart(true); + Ref ref = dependencyInstallOperation.checkDependenciesAndReplacements(pluginDescriptor, null); + if (ref == null) { + cancel.set(true); + } + else { + dependenciesRequireRestart.set(dependencyInstallOperation.isRestartRequired()); + installedDependencies.addAll(dependencyInstallOperation.getInstalledDependentPlugins()); + toDisable.set(ref.get()); + } + }, IdeBundle.message("progress.title.checking.plugin.dependencies"), true, null, (JComponent) parent); + + if (!success || cancel.get()) return false; + Path oldFile = null; if (installedPlugin != null && !installedPlugin.isBundled()) { oldFile = installedPlugin.getPluginPath(); } - boolean installWithoutRestart = oldFile == null && DynamicPlugins.allowLoadUnloadWithoutRestart(pluginDescriptor); + boolean installWithoutRestart = oldFile == null && DynamicPlugins.allowLoadUnloadWithoutRestart(pluginDescriptor) && !dependenciesRequireRestart.get(); if (!installWithoutRestart) { installAfterRestart(file, false, oldFile, pluginDescriptor); } - ourState.onPluginInstall(pluginDescriptor, installedPlugin != null, !installWithoutRestart); - checkInstalledPluginDependencies(model, pluginDescriptor, parent); + if (!toDisable.isNull()) { + // TODO[yole] unload and check for restart + pluginEnabler.disablePlugins(Collections.singleton(toDisable.get())); + } + checkInstalledPluginDependencies(model, pluginDescriptor, parent, + ContainerUtil.map2Set(installedDependencies, (callbackData) -> callbackData.getPluginDescriptor().getPluginId())); callback.consume(new PluginInstallCallbackData(file, pluginDescriptor, !installWithoutRestart)); + for (PluginInstallCallbackData callbackData: installedDependencies) { + if (!callbackData.getPluginDescriptor().getPluginId().equals(pluginDescriptor.getPluginId())) { + callback.consume(callbackData); + } + } return true; } catch (IOException ex) { @@ -309,13 +345,14 @@ public final class PluginInstaller { private static void checkInstalledPluginDependencies(@NotNull InstalledPluginsTableModel model, @NotNull IdeaPluginDescriptorImpl pluginDescriptor, - @Nullable Component parent) { + @Nullable Component parent, + Set installedDependencies) { final Set notInstalled = new HashSet<>(); final Set disabledIds = new HashSet<>(); - final PluginId[] dependentPluginIds = pluginDescriptor.getDependentPluginIds(); - final PluginId[] optionalDependentPluginIds = pluginDescriptor.getOptionalDependentPluginIds(); - for (PluginId id : dependentPluginIds) { - if (ArrayUtilRt.find(optionalDependentPluginIds, id) > -1) continue; + for (IdeaPluginDependency dep : pluginDescriptor.getDependencies()) { + if (dep.isOptional()) continue; + PluginId id = dep.getPluginId(); + if (installedDependencies.contains(id)) continue; final boolean disabled = model.isDisabled(id); final boolean enabled = model.isEnabled(id); if (!enabled && !disabled && !PluginManagerCore.isModuleDependency(id)) { @@ -364,7 +401,7 @@ public final class PluginInstaller { FileChooser.chooseFile(descriptor, null, parent, toSelect, virtualFile -> { File file = VfsUtilCore.virtualToIoFile(virtualFile); PropertiesComponent.getInstance().setValue(PLUGINS_PRESELECTION_PATH, FileUtil.toSystemIndependentName(file.getParent())); - install(model, file, callback, parent); + installFromDisk(model, file, callback, parent); }); } } \ No newline at end of file