InstalledPackagesPanel moved to webide-api

This commit is contained in:
Dmitry Jemerov
2013-05-03 21:25:56 +02:00
parent 6a87f08cb9
commit a2962fd55e
5 changed files with 7 additions and 505 deletions
@@ -1,5 +1,6 @@
package com.jetbrains.python.packaging;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.webcore.packaging.InstalledPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -36,6 +37,6 @@ public class PyPackage extends InstalledPackage {
@Nullable
@Override
public String getTooltipText() {
return myLocation;
return FileUtil.getLocationRelativeToUserHome(myLocation);
}
}
@@ -1,6 +1,5 @@
package com.jetbrains.python.packaging;
import com.google.common.collect.ImmutableMap;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.util.io.FileUtil;
@@ -8,6 +7,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Function;
import com.intellij.webcore.packaging.PackageVersionComparator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -123,73 +123,6 @@ public class PyRequirement {
@Nullable private final String myURL;
private final boolean myEditable;
public static final Comparator<String> VERSION_COMPARATOR = new Comparator<String>() {
@Override
public int compare(String version1, String version2) {
final List<String> vs1 = parse(version1);
final List<String> vs2 = parse(version2);
int result = 0;
for (int i = 0; i < vs1.size() && i < vs2.size(); i++) {
result = vs1.get(i).compareTo(vs2.get(i));
if (result != 0) {
break;
}
}
if (result == 0) {
return vs1.size() - vs2.size();
}
return result;
}
@Nullable
private String replace(@NotNull String s) {
final Map<String, String> sub = ImmutableMap.of("pre", "c",
"preview", "c",
"rc", "c",
"dev", "@");
final String tmp = sub.get(s);
if (tmp != null) {
s = tmp;
}
if (s.equals(".") || s.equals("-")) {
return null;
}
if (s.matches("[0-9]+")) {
final long value = Long.parseLong(s);
return String.format("%08d", value);
}
return "*" + s;
}
@NotNull
private List<String> parse(@Nullable String s) {
// Version parsing from pkg_resources ensures that all the "pre", "alpha", "rc", etc. are sorted correctly
if (s == null) {
return Collections.emptyList();
}
final Pattern COMPONENT_RE = Pattern.compile("\\d+|[a-z]+|\\.|-|.+");
final List<String> results = new ArrayList<String>();
final Matcher matcher = COMPONENT_RE.matcher(s);
while (matcher.find()) {
final String component = replace(matcher.group());
if (component == null) {
continue;
}
results.add(component);
}
for (int i = results.size() - 1; i > 0; i--) {
if ("00000000".equals(results.get(i))) {
results.remove(i);
}
else {
break;
}
}
results.add("*final");
return results;
}
};
public PyRequirement(@NotNull String name) {
this(name, Collections.<VersionSpec>emptyList());
}
@@ -285,7 +218,7 @@ public class PyRequirement {
for (PyPackage pkg : packages) {
if (normalizeName(myName).equalsIgnoreCase(pkg.getName())) {
for (VersionSpec spec : myVersionSpecs) {
final int cmp = VERSION_COMPARATOR.compare(pkg.getVersion(), spec.getVersion());
final int cmp = PackageVersionComparator.VERSION_COMPARATOR.compare(pkg.getVersion(), spec.getVersion());
final Relation relation = spec.getRelation();
if (!relation.isSuccessful(cmp)) {
return null;
@@ -1,434 +0,0 @@
package com.jetbrains.python.packaging.ui;
import com.google.common.collect.Lists;
import com.intellij.icons.AllIcons;
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.openapi.ui.Messages;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.ui.DoubleClickListener;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.table.JBTable;
import com.intellij.util.CatchingConsumer;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.webcore.packaging.*;
import com.jetbrains.python.packaging.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.*;
import java.util.List;
public class InstalledPackagesPanel extends JPanel {
protected final JButton myInstallButton;
private final JButton myUninstallButton;
private final JButton myUpgradeButton;
protected final JBTable myPackagesTable;
private DefaultTableModel myPackagesTableModel;
protected PackageManagementService myPackageManagementService;
protected final Project myProject;
protected final PackagesNotificationPanel myNotificationArea;
protected final List<Consumer<Sdk>> myPathChangedListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private Set<String> currentlyInstalling = new HashSet<String>();
public InstalledPackagesPanel(Project project, PackagesNotificationPanel area) {
super(new GridBagLayout());
myProject = project;
myNotificationArea = area;
myInstallButton = new JButton("Install");
myUninstallButton = new JButton("Uninstall");
myUpgradeButton = new JButton("Upgrade");
myInstallButton.setMnemonic('I');
myUninstallButton.setMnemonic('U');
myUpgradeButton.setMnemonic('p');
myInstallButton.setEnabled(false);
myUninstallButton.setEnabled(false);
myUpgradeButton.setEnabled(false);
myPackagesTableModel = new DefaultTableModel(new String[]{"Package", "Version", "Latest"}, 0) {
@Override
public boolean isCellEditable(int i, int i1) {
return false;
}
};
final TableCellRenderer tableCellRenderer = new MyTableCellRenderer();
myPackagesTable = new JBTable(myPackagesTableModel) {
@Override
public TableCellRenderer getCellRenderer(int row, int column) {
return tableCellRenderer;
}
};
Insets anInsets = new Insets(2, 2, 2, 2);
JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myPackagesTable,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setPreferredSize(new Dimension(500, 500));
add(scrollPane, new GridBagConstraints(0, 0, 1, 8, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
anInsets, 0, 0));
addUninstallAction();
addUpgradeAction();
add(myInstallButton,
new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
anInsets, 0, 0));
add(myUninstallButton,
new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
anInsets, 0, 0));
add(myUpgradeButton,
new GridBagConstraints(1, 2, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, anInsets, 0, 0));
myPackagesTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent event) {
updateUninstallUpgrade();
}
});
myInstallButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (myPackageManagementService != null) {
ManagePackagesDialog dialog = createManagePackagesDialog();
dialog.show();
}
}
});
new DoubleClickListener() {
@Override
protected boolean onDoubleClick(MouseEvent e) {
if (myPackageManagementService != null && myInstallButton.isEnabled()) {
ManagePackagesDialog dialog = createManagePackagesDialog();
Point p = e.getPoint();
int row = myPackagesTable.rowAtPoint(p);
int column = myPackagesTable.columnAtPoint(p);
if (row >= 0 && column >= 0) {
Object pyPackage = myPackagesTable.getValueAt(row, 0);
if (pyPackage instanceof PyPackage) {
dialog.setSelected(((PyPackage)pyPackage).getName());
}
}
dialog.show();
return true;
}
return false;
}
}.installOn(myPackagesTable);
}
private ManagePackagesDialog createManagePackagesDialog() {
return new ManagePackagesDialog(myProject,
myPackageManagementService,
new PackageManagementService.Listener() {
@Override
public void operationStarted(String packageName) {
myPackagesTable.setPaintBusy(true);
}
@Override
public void operationFinished(String packageName,
@Nullable String errorDescription) {
myNotificationArea.showResult(packageName, errorDescription);
myPackagesTable.clearSelection();
doUpdatePackages(myPackageManagementService);
}
});
}
public void addPathChangedListener(Consumer<Sdk> consumer) {
myPathChangedListeners.add(consumer);
}
private void addUpgradeAction() {
myUpgradeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final int[] rows = myPackagesTable.getSelectedRows();
if (myPackageManagementService != null) {
for (int row : rows) {
final Object pyPackage = myPackagesTableModel.getValueAt(row, 0);
if (pyPackage instanceof InstalledPackage) {
final String packageName = ((InstalledPackage)pyPackage).getName();
final String currentVersion = (String)myPackagesTableModel.getValueAt(row, 1);
upgradePackage(packageName, currentVersion);
}
}
}
}
});
}
private void upgradePackage(final String packageName, final String currentVersion) {
final PackageManagementService selPackageManagementService = myPackageManagementService;
myPackageManagementService.fetchPackageVersions(packageName, new CatchingConsumer<List<String>, Exception>() {
@Override
public void consume(List<String> releases) {
if (!releases.isEmpty() &&
PyRequirement.VERSION_COMPARATOR.compare(currentVersion, releases.get(0)) >= 0)
return;
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
final PackageManagementService.Listener listener = new PackageManagementService.Listener() {
@Override
public void operationStarted(String packageName) {
myPackagesTable.setPaintBusy(true);
currentlyInstalling.add(packageName);
}
@Override
public void operationFinished(String packageName, @Nullable String errorDescription) {
myPackagesTable.clearSelection();
updatePackages(selPackageManagementService);
myPackagesTable.setPaintBusy(false);
currentlyInstalling.remove(packageName);
if (errorDescription == null) {
myNotificationArea.showSuccess("Package " + packageName + " successfully upgraded");
}
else {
myNotificationArea.showError("Upgrade packages failed. <a href=\"xxx\">Details...</a>",
"Upgrade Packages Failed",
"Upgrade packages failed.\n" + errorDescription);
}
}
};
myPackageManagementService.installPackage(new RepoPackage(packageName, null /* TODO? */), null, true, null, listener, false);
myUpgradeButton.setEnabled(false);
}
}, ModalityState.any());
}
@Override
public void consume(Exception e) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
Messages.showErrorDialog("Error occurred. Please, check your internet connection.",
"Upgrade Package Failed.");
}
}, ModalityState.any());
}
});
}
private void updateUninstallUpgrade() {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
final int[] selected = myPackagesTable.getSelectedRows();
boolean upgradeAvailable = false;
boolean canUninstall = true;
boolean canUpgrade = true;
if (myPackageManagementService != null && selected.length != 0) {
for (int i = 0; i != selected.length; ++i) {
final int index = selected[i];
if (index >= myPackagesTable.getRowCount()) continue;
final Object value = myPackagesTable.getValueAt(index, 0);
if (value instanceof InstalledPackage) {
final InstalledPackage pkg = (InstalledPackage)value;
if (!canUninstallPackage(pkg)) {
canUninstall = false;
}
if (!canUpgradePackage(pkg)) {
canUpgrade = false;
}
final String pyPackageName = pkg.getName();
final String availableVersion = (String)myPackagesTable.getValueAt(index, 2);
if (!upgradeAvailable) {
upgradeAvailable = PyRequirement.VERSION_COMPARATOR.compare(pkg.getVersion(), availableVersion) < 0 &&
!currentlyInstalling.contains(pyPackageName);
}
if (!canUninstall && !canUpgrade) break;
}
}
}
myUninstallButton.setEnabled(canUninstall);
myUpgradeButton.setEnabled(upgradeAvailable && canUpgrade);
}
}, ModalityState.any());
}
protected boolean canUninstallPackage(InstalledPackage pyPackage) {
return true;
}
protected boolean canUpgradePackage(InstalledPackage pyPackage) {
return true;
}
private void addUninstallAction() {
myUninstallButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
final List<InstalledPackage> packages = getSelectedPackages();
final PackageManagementService selPackageManagementService = myPackageManagementService;
if (selPackageManagementService != null) {
PackageManagementService.Listener listener = new PackageManagementService.Listener() {
@Override
public void operationStarted(String packageName) {
myPackagesTable.setPaintBusy(true);
}
@Override
public void operationFinished(String packageName, @Nullable String errorDescription) {
myPackagesTable.clearSelection();
updatePackages(selPackageManagementService);
myPackagesTable.setPaintBusy(false);
if (errorDescription == null) {
if (packageName != null) {
myNotificationArea.showSuccess("Package '" + packageName + "' successfully uninstalled");
}
else {
myNotificationArea.showSuccess("Packages successfully uninstalled");
}
}
else {
myNotificationArea.showError("Uninstall packages failed. <a href=\"xxx\">Details...</a>",
"Uninstall Packages Failed",
"Uninstall packages failed.\n" + errorDescription);
}
}
};
myPackageManagementService.uninstallPackages(packages, listener);
}
}
});
}
@NotNull
private List<InstalledPackage> getSelectedPackages() {
final List<InstalledPackage> results = new ArrayList<InstalledPackage>();
final int[] rows = myPackagesTable.getSelectedRows();
for (int row : rows) {
final Object packageName = myPackagesTableModel.getValueAt(row, 0);
if (packageName instanceof InstalledPackage) {
results.add((InstalledPackage)packageName);
}
}
return results;
}
public void updatePackages(@Nullable PackageManagementService packageManagementService) {
myPackageManagementService = packageManagementService;
myPackagesTable.clearSelection();
myPackagesTableModel.getDataVector().clear();
doUpdatePackages(packageManagementService);
}
public void doUpdatePackages(final PackageManagementService packageManagementService) {
myPackagesTable.setPaintBusy(true);
final Application application = ApplicationManager.getApplication();
application.executeOnPooledThread(new Runnable() {
@Override
public void run() {
Collection<InstalledPackage> packages = Lists.newArrayList();
if (packageManagementService != null) {
try {
packages = packageManagementService.getInstalledPackages();
}
catch (IOException e) {
// do nothing, we already have an empty list
}
finally {
final Collection<InstalledPackage> finalPackages = packages;
final Map<String, RepoPackage> cache = buildNameToPackageMap(packageManagementService.getAllPackagesCached());
if (cache.isEmpty()) {
refreshLatestVersions();
}
application.invokeLater(new Runnable() {
@Override
public void run() {
if (packageManagementService == myPackageManagementService) {
myPackagesTableModel.getDataVector().clear();
for (InstalledPackage pkg : finalPackages) {
RepoPackage repoPackage = cache.get(pkg.getName());
final String version = repoPackage != null ? repoPackage.getLatestVersion() : null;
myPackagesTableModel
.addRow(new Object[]{pkg, pkg.getVersion(), version == null ? "" : version});
}
if (!cache.isEmpty()) {
myPackagesTable.setPaintBusy(false);
}
}
}
}, ModalityState.any());
}
}
}
});
}
private void refreshLatestVersions() {
final Application application = ApplicationManager.getApplication();
application.executeOnPooledThread(new Runnable() {
@Override
public void run() {
try {
List<RepoPackage> packages = myPackageManagementService.reloadAllPackages();
final Map<String, RepoPackage> packageMap = buildNameToPackageMap(packages);
application.invokeLater(new Runnable() {
@Override
public void run() {
for (int i = 0; i != myPackagesTableModel.getRowCount(); ++i) {
final InstalledPackage pyPackage = (PyPackage)myPackagesTableModel.getValueAt(i, 0);
final RepoPackage repoPackage = packageMap.get(pyPackage.getName());
myPackagesTableModel.setValueAt(repoPackage == null ? null : repoPackage.getLatestVersion(), i, 2);
}
myPackagesTable.setPaintBusy(false);
}
}, ModalityState.stateForComponent(myPackagesTable));
}
catch (IOException ignored) {
myPackagesTable.setPaintBusy(false);
}
}
});
}
private static Map<String, RepoPackage> buildNameToPackageMap(List<RepoPackage> packages) {
final Map<String, RepoPackage> packageMap = new HashMap<String, RepoPackage>();
for (RepoPackage aPackage : packages) {
packageMap.put(aPackage.getName(), aPackage);
}
return packageMap;
}
private static class MyTableCellRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected,
final boolean hasFocus, final int row, final int column) {
final JLabel cell = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
final String version = (String)table.getValueAt(row, 1);
final String availableVersion = (String)table.getValueAt(row, 2);
cell.setIcon(PyRequirement.VERSION_COMPARATOR.compare(version, availableVersion) < 0 && column == 2 ?
AllIcons.Vcs.Arrow_right : null);
final Object pyPackage = table.getValueAt(row, 0);
if (pyPackage instanceof PyPackage) {
cell.setToolTipText(FileUtil.getLocationRelativeToUserHome(((PyPackage)pyPackage).getLocation()));
}
return cell;
}
}
}
@@ -7,6 +7,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.util.Consumer;
import com.intellij.webcore.packaging.InstalledPackage;
import com.intellij.webcore.packaging.InstalledPackagesPanel;
import com.intellij.webcore.packaging.PackagesNotificationPanel;
import com.jetbrains.python.packaging.PyExternalProcessException;
import com.jetbrains.python.packaging.PyPackage;
@@ -17,6 +17,7 @@ import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFileSystemItem;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.webcore.packaging.PackageVersionComparator;
import com.jetbrains.python.packaging.*;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFile;
@@ -75,7 +76,7 @@ public class PyTestConfigurationProducer extends RuntimeConfigurationProducer {
final PyPackageManagerImpl packageManager = (PyPackageManagerImpl)PyPackageManager.getInstance(sdk);
try {
final PyPackage pytestPackage = packageManager.findPackage("pytest");
if (pytestPackage != null && PyRequirement.VERSION_COMPARATOR.compare(pytestPackage.getVersion(), "2.3.3") >= 0) {
if (pytestPackage != null && PackageVersionComparator.VERSION_COMPARATOR.compare(pytestPackage.getVersion(), "2.3.3") >= 0) {
name = pyClass.getName() + " and " + name;
}
else {