diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java index 148834789e93..4e4a21cdabb7 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java @@ -17,6 +17,7 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.AnActionButton; import com.intellij.ui.DoubleClickListener; +import com.intellij.ui.TableSpeedSearch; import com.intellij.ui.ToolbarDecorator; import com.intellij.ui.table.JBTable; import com.intellij.util.CatchingConsumer; @@ -83,6 +84,7 @@ public class InstalledPackagesPanel extends JPanel { myPackagesTable.setPreferredScrollableViewportSize(null); myPackagesTable.setStriped(true); myPackagesTable.getTableHeader().setReorderingAllowed(false); + new TableSpeedSearch(myPackagesTable); myUpgradeButton = new AnActionButton("Upgrade", IconUtil.getMoveUpIcon()) { @Override @@ -111,6 +113,7 @@ public class InstalledPackagesPanel extends JPanel { .addExtraAction(myUninstallButton) .addExtraAction(myUpgradeButton); + decorator.addExtraActions(getExtraActions()); add(decorator.createPanel()); myInstallButton.setEnabled(false); myUninstallButton.setEnabled(false); @@ -145,6 +148,10 @@ public class InstalledPackagesPanel extends JPanel { }.installOn(myPackagesTable); } + protected AnActionButton[] getExtraActions() { + return new AnActionButton[0]; + } + @NotNull protected ManagePackagesDialog createManagePackagesDialog() { return new ManagePackagesDialog(myProject, diff --git a/python/gen/icons/PythonIcons.java b/python/gen/icons/PythonIcons.java index 069bd69ad9d0..656b78bee289 100644 --- a/python/gen/icons/PythonIcons.java +++ b/python/gen/icons/PythonIcons.java @@ -36,6 +36,7 @@ public class PythonIcons { } public static class Python { + public static final Icon Anaconda = load("/icons/com/jetbrains/python/anaconda.png"); // 16x16 public static class Buildout { public static final Icon Buildout = load("/icons/com/jetbrains/python/buildout/buildout.png"); // 16x16 diff --git a/python/resources/icons/com/jetbrains/python/anaconda.png b/python/resources/icons/com/jetbrains/python/anaconda.png new file mode 100644 index 000000000000..285fdd78c40e Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/anaconda.png differ diff --git a/python/resources/icons/com/jetbrains/python/anaconda@2x.png b/python/resources/icons/com/jetbrains/python/anaconda@2x.png new file mode 100644 index 000000000000..1fc952cd5902 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/anaconda@2x.png differ diff --git a/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java index 7b6c3b5859d1..5d644de8b5af 100644 --- a/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java @@ -40,38 +40,38 @@ import java.util.Set; public class PyCondaPackageManagerImpl extends PyPackageManagerImpl { public static final String PYTHON = "python"; + public boolean useConda = true; + + public boolean useConda() { + return useConda; + } + + public void useConda(boolean conda) { + useConda = conda; + } PyCondaPackageManagerImpl(@NotNull final Sdk sdk) { super(sdk); } - @Override - public void installManagement() throws ExecutionException { - } - - @Override - public boolean hasManagement() throws ExecutionException { - final Sdk sdk = getSdk(); - return isCondaVEnv(sdk); - } - - @Override - protected void installManagement(@NotNull String name) throws ExecutionException { - } - @Override public void install(@NotNull List requirements, @NotNull List extraArgs) throws ExecutionException { - final ArrayList arguments = new ArrayList<>(); - for (PyRequirement requirement : requirements) { - arguments.add(requirement.toString()); - } - arguments.add("-y"); - if (extraArgs.contains("-U")) { - getCondaOutput("update", arguments); + if (useConda) { + final ArrayList arguments = new ArrayList<>(); + for (PyRequirement requirement : requirements) { + arguments.add(requirement.toString()); + } + arguments.add("-y"); + if (extraArgs.contains("-U")) { + getCondaOutput("update", arguments); + } + else { + arguments.addAll(extraArgs); + getCondaOutput("install", arguments); + } } else { - arguments.addAll(extraArgs); - getCondaOutput("install", arguments); + super.install(requirements, extraArgs); } } @@ -109,27 +109,41 @@ public class PyCondaPackageManagerImpl extends PyPackageManagerImpl { @Override public void install(@NotNull String requirementString) throws ExecutionException { - getCondaOutput("install", Lists.newArrayList(requirementString, "-y")); + if (useConda) { + super.install(requirementString); + } + else { + getCondaOutput("install", Lists.newArrayList(requirementString, "-y")); + } } @Override public void uninstall(@NotNull List packages) throws ExecutionException { - final ArrayList arguments = new ArrayList<>(); - for (PyPackage aPackage : packages) { - arguments.add(aPackage.getName()); - } - arguments.add("-y"); + if (useConda) { + final ArrayList arguments = new ArrayList<>(); + for (PyPackage aPackage : packages) { + arguments.add(aPackage.getName()); + } + arguments.add("-y"); - getCondaOutput("remove", arguments); + getCondaOutput("remove", arguments); + } + else { + super.uninstall(packages); + } } @NotNull @Override protected List collectPackages() throws ExecutionException { - final ProcessOutput output = getCondaOutput("list", Lists.newArrayList("-e")); - final Set packages = Sets.newConcurrentHashSet(parseCondaToolOutput(output.getStdout())); - packages.addAll(super.collectPackages()); - return Lists.newArrayList(packages); + if (useConda) { + final ProcessOutput output = getCondaOutput("list", Lists.newArrayList("-e")); + final Set packages = Sets.newConcurrentHashSet(parseCondaToolOutput(output.getStdout())); + return Lists.newArrayList(packages); + } + else { + return super.collectPackages(); + } } @NotNull @@ -162,7 +176,7 @@ public class PyCondaPackageManagerImpl extends PyPackageManagerImpl { final VirtualFile homeDirectory = sdk.getHomeDirectory(); if (homeDirectory == null) return false; final VirtualFile condaMeta = SystemInfo.isWindows ? homeDirectory.getParent().findChild(condaName) : - homeDirectory.getParent().getParent().findChild(condaName); + homeDirectory.getParent().getParent().findChild(condaName); return condaMeta != null; } diff --git a/python/src/com/jetbrains/python/packaging/PyCondaPackageService.java b/python/src/com/jetbrains/python/packaging/PyCondaPackageService.java index ee320ad782f8..f44b500552dc 100644 --- a/python/src/com/jetbrains/python/packaging/PyCondaPackageService.java +++ b/python/src/com/jetbrains/python/packaging/PyCondaPackageService.java @@ -227,9 +227,7 @@ public class PyCondaPackageService implements PersistentStateComponent lines = output.getStdoutLines(); - for (String line : lines) { - CONDA_CHANNELS.add(line); - } + CONDA_CHANNELS.addAll(lines); LAST_TIME_CHECKED = System.currentTimeMillis(); } } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyCondaManagementService.java b/python/src/com/jetbrains/python/packaging/ui/PyCondaManagementService.java index 14177418eff6..298fe87fe6ad 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyCondaManagementService.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyCondaManagementService.java @@ -27,9 +27,12 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.CatchingConsumer; import com.intellij.webcore.packaging.PackageVersionComparator; import com.intellij.webcore.packaging.RepoPackage; +import com.jetbrains.python.packaging.PyCondaPackageManagerImpl; import com.jetbrains.python.packaging.PyCondaPackageService; +import com.jetbrains.python.packaging.PyPackageManager; import org.jetbrains.annotations.NotNull; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -40,86 +43,102 @@ public class PyCondaManagementService extends PyPackageManagementService { super(project, sdk); } + private boolean useConda() { + return PyPackageManager.getInstance(mySdk) instanceof PyCondaPackageManagerImpl && + ((PyCondaPackageManagerImpl)PyPackageManager.getInstance(mySdk)).useConda(); + } + @Override @NotNull public List getAllPackagesCached() { - return versionMapToPackageList(PyCondaPackageService.getInstance().getCondaPackages()); + return useConda() ? versionMapToPackageList(PyCondaPackageService.getInstance().getCondaPackages()) : super.getAllPackagesCached(); } @Override @NotNull - public List getAllPackages() { - return versionMapToPackageList(PyCondaPackageService.getInstance().loadAndGetPackages()); + public List getAllPackages() throws IOException { + return useConda() ? versionMapToPackageList(PyCondaPackageService.getInstance().loadAndGetPackages()) : super.getAllPackages(); } @Override @NotNull - public List reloadAllPackages() { - return getAllPackages(); + public List reloadAllPackages() throws IOException { + return useConda() ? getAllPackages() : super.reloadAllPackages(); } @Override public List getAllRepositories() { - List result = new ArrayList<>(); - result.addAll(PyCondaPackageService.getInstance().loadAndGetChannels()); - return result; + return useConda() ? Lists.newArrayList(PyCondaPackageService.getInstance().loadAndGetChannels()) : super.getAllRepositories(); } @Override public void addRepository(String repositoryUrl) { - final String conda = PyCondaPackageService.getCondaExecutable(mySdk.getHomeDirectory()); - final ArrayList parameters = Lists.newArrayList(conda, "config", "--add", "channels", repositoryUrl, "--force"); - final GeneralCommandLine commandLine = new GeneralCommandLine(parameters); + if (useConda()) { + final String conda = PyCondaPackageService.getCondaExecutable(mySdk.getHomeDirectory()); + final ArrayList parameters = Lists.newArrayList(conda, "config", "--add", "channels", repositoryUrl, "--force"); + final GeneralCommandLine commandLine = new GeneralCommandLine(parameters); - try { - final CapturingProcessHandler handler = new CapturingProcessHandler(commandLine); - final ProcessOutput result = handler.runProcess(); - final int exitCode = result.getExitCode(); - if (exitCode != 0) { - final String message = StringUtil.isEmptyOrSpaces(result.getStdout()) && StringUtil.isEmptyOrSpaces(result.getStderr()) ? - "Permission denied" : "Non-zero exit code"; - LOG.warn("Failed to add repository " + message); + try { + final CapturingProcessHandler handler = new CapturingProcessHandler(commandLine); + final ProcessOutput result = handler.runProcess(); + final int exitCode = result.getExitCode(); + if (exitCode != 0) { + final String message = StringUtil.isEmptyOrSpaces(result.getStdout()) && StringUtil.isEmptyOrSpaces(result.getStderr()) ? + "Permission denied" : "Non-zero exit code"; + LOG.warn("Failed to add repository " + message); + } + PyCondaPackageService.getInstance().addChannel(repositoryUrl); + } + catch (ExecutionException e) { + LOG.warn("Failed to add repository"); } - PyCondaPackageService.getInstance().addChannel(repositoryUrl); } - catch (ExecutionException e) { - LOG.warn("Failed to add repository"); + else { + super.addRepository(repositoryUrl); } - } @Override public void removeRepository(String repositoryUrl) { - final String conda = PyCondaPackageService.getCondaExecutable(mySdk.getHomeDirectory()); - final ArrayList parameters = Lists.newArrayList(conda, "config", "--remove", "channels", repositoryUrl, "--force"); - final GeneralCommandLine commandLine = new GeneralCommandLine(parameters); + if (useConda()) { + final String conda = PyCondaPackageService.getCondaExecutable(mySdk.getHomeDirectory()); + final ArrayList parameters = Lists.newArrayList(conda, "config", "--remove", "channels", repositoryUrl, "--force"); + final GeneralCommandLine commandLine = new GeneralCommandLine(parameters); - try { - final CapturingProcessHandler handler = new CapturingProcessHandler(commandLine); - final ProcessOutput result = handler.runProcess(); - final int exitCode = result.getExitCode(); - if (exitCode != 0) { - final String message = StringUtil.isEmptyOrSpaces(result.getStdout()) && StringUtil.isEmptyOrSpaces(result.getStderr()) ? - "Permission denied" : "Non-zero exit code"; - LOG.warn("Failed to remove repository " + message); + try { + final CapturingProcessHandler handler = new CapturingProcessHandler(commandLine); + final ProcessOutput result = handler.runProcess(); + final int exitCode = result.getExitCode(); + if (exitCode != 0) { + final String message = StringUtil.isEmptyOrSpaces(result.getStdout()) && StringUtil.isEmptyOrSpaces(result.getStderr()) ? + "Permission denied" : "Non-zero exit code"; + LOG.warn("Failed to remove repository " + message); + } + PyCondaPackageService.getInstance().removeChannel(repositoryUrl); + } + catch (ExecutionException e) { + LOG.warn("Failed to remove repository"); } - PyCondaPackageService.getInstance().removeChannel(repositoryUrl); } - catch (ExecutionException e) { - LOG.warn("Failed to remove repository"); + else { + super.removeRepository(repositoryUrl); } } @Override public boolean canInstallToUser() { - return false; + return !useConda() && super.canInstallToUser(); } @Override public void fetchPackageVersions(String packageName, CatchingConsumer, Exception> consumer) { - final List versions = PyCondaPackageService.getInstance().getPackageVersions(packageName); - Collections.sort(versions, Collections.reverseOrder(new PackageVersionComparator())); - consumer.consume(versions); + if (useConda()) { + final List versions = PyCondaPackageService.getInstance().getPackageVersions(packageName); + Collections.sort(versions, Collections.reverseOrder(new PackageVersionComparator())); + consumer.consume(versions); + } + else { + super.fetchPackageVersions(packageName, consumer); + } } - } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java index d7704a26b6ac..0a8ad9ad59c2 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java @@ -18,11 +18,13 @@ package com.jetbrains.python.packaging.ui; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.intellij.execution.ExecutionException; +import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.ui.ToggleActionButton; import com.intellij.webcore.packaging.InstalledPackage; import com.intellij.webcore.packaging.InstalledPackagesPanel; import com.intellij.webcore.packaging.PackageManagementService; @@ -30,6 +32,7 @@ import com.intellij.webcore.packaging.PackagesNotificationPanel; import com.jetbrains.python.packaging.*; import com.jetbrains.python.sdk.PySdkUtil; import com.jetbrains.python.sdk.PythonSdkType; +import icons.PythonIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -183,4 +186,34 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { return myHasManagement && !PyCondaPackageManagerImpl.PYTHON.equals(pyPackage.getName()); } + + @Override + @NotNull + protected ToggleActionButton[] getExtraActions() { + return new ToggleActionButton[]{new ToggleActionButton("Use Conda Package Manager", PythonIcons.Python.Anaconda) { + @Override + public boolean isSelected(AnActionEvent e) { + final Sdk sdk = getSelectedSdk(); + return sdk != null && PyPackageManager.getInstance(sdk) instanceof PyCondaPackageManagerImpl && + ((PyCondaPackageManagerImpl)PyPackageManager.getInstance(sdk)).useConda(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + final Sdk sdk = getSelectedSdk(); + if (sdk == null) return; + final PyPackageManager manager = PyPackageManager.getInstance(sdk); + if (manager instanceof PyCondaPackageManagerImpl) { + ((PyCondaPackageManagerImpl)manager).useConda(state); + } + updatePackages(myPackageManagementService); + } + + @Override + public boolean isVisible() { + final Sdk sdk = getSelectedSdk(); + return sdk != null && PythonSdkType.isCondaVirtualEnv(sdk); + } + }}; + } } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java index 8f4e28017aba..3bf889d15deb 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java @@ -174,7 +174,7 @@ public class PyPackageManagementService extends PackageManagementServiceEx { catch (ExecutionException e) { throw new IOException(e); } - Collections.sort(packages, (pkg1, pkg2) -> pkg1.getName().compareTo(pkg2.getName())); + Collections.sort(packages, Comparator.comparing(InstalledPackage::getName)); return new ArrayList<>(packages); }