Merge branch 'pycharm-interpreters-rework'

This commit is contained in:
Ekaterina Tuzova
2014-02-17 12:41:12 +04:00
37 changed files with 781 additions and 706 deletions
@@ -54,7 +54,7 @@ public class SdkConfigurationUtil {
public static void createSdk(@Nullable final Project project,
final Sdk[] existingSdks,
final NullableConsumer<Sdk> onSdkCreatedCallBack,
final NullableConsumer<Sdk> onSdkCreatedCallBack, final boolean createIfExists,
final SdkType... sdkTypes) {
if (sdkTypes.length == 0) {
onSdkCreatedCallBack.consume(null);
@@ -72,8 +72,19 @@ public class SdkConfigurationUtil {
@Override
public void consume(List<VirtualFile> selectedFiles) {
for (SdkType sdkType : sdkTypes) {
if (sdkType.isValidSdkHome(selectedFiles.get(0).getPath())) {
onSdkCreatedCallBack.consume(setupSdk(existingSdks, selectedFiles.get(0), sdkType, false, null, null));
final String path = selectedFiles.get(0).getPath();
if (sdkType.isValidSdkHome(path)) {
Sdk newSdk = null;
if (!createIfExists) {
for (Sdk sdk : existingSdks) {
if (path.equals(sdk.getHomePath())) {
newSdk = sdk;
}
}
}
if (newSdk == null)
newSdk = setupSdk(existingSdks, selectedFiles.get(0), sdkType, false, null, null);
onSdkCreatedCallBack.consume(newSdk);
return;
}
}
@@ -85,6 +96,14 @@ public class SdkConfigurationUtil {
onSdkCreatedCallBack.consume(null);
}
});
}
public static void createSdk(@Nullable final Project project,
final Sdk[] existingSdks,
final NullableConsumer<Sdk> onSdkCreatedCallBack,
final SdkType... sdkTypes) {
createSdk(project, existingSdks, onSdkCreatedCallBack, true, sdkTypes);
}
private static FileChooserDescriptor createCompositeDescriptor(final SdkType... sdkTypes) {
@@ -140,7 +159,7 @@ public class SdkConfigurationUtil {
final ProjectJdkImpl sdk;
try {
String sdkPath = sdkType.sdkPath(homeDir);
final String sdkName = customSdkSuggestedName == null
? createUniqueSdkName(sdkType, sdkPath, sdksList)
: createUniqueSdkName(customSdkSuggestedName, sdksList);
@@ -313,8 +332,9 @@ public class SdkConfigurationUtil {
@Nullable
private static Sdk findByPath(SdkType sdkType, Sdk[] sdks, String sdkHome) {
for (Sdk sdk : sdks) {
if (sdk.getSdkType() == sdkType &&
FileUtil.pathsEqual(FileUtil.toSystemIndependentName(sdk.getHomePath()), FileUtil.toSystemIndependentName(sdkHome))) {
final String path = sdk.getHomePath();
if (sdk.getSdkType() == sdkType && path != null &&
FileUtil.pathsEqual(FileUtil.toSystemIndependentName(path), FileUtil.toSystemIndependentName(sdkHome))) {
return sdk;
}
}
@@ -2,6 +2,7 @@ package com.intellij.webcore.packaging;
import com.google.common.collect.Lists;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
@@ -9,13 +10,13 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.DoubleClickListener;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.*;
import com.intellij.ui.table.JBTable;
import com.intellij.util.CatchingConsumer;
import com.intellij.util.Consumer;
import com.intellij.util.IconUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.*;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -26,20 +27,16 @@ 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.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class InstalledPackagesPanel extends JPanel {
protected final JButton myInstallButton;
private final JButton myUninstallButton;
private final JButton myUpgradeButton;
private final AnActionButton myUpgradeButton;
protected final AnActionButton myInstallButton;
private final AnActionButton myUninstallButton;
protected final JBTable myPackagesTable;
private DefaultTableModel myPackagesTableModel;
@@ -51,19 +48,9 @@ public class InstalledPackagesPanel extends JPanel {
private final Set<InstalledPackage> myWaitingToUpgrade = ContainerUtil.newHashSet();
public InstalledPackagesPanel(Project project, PackagesNotificationPanel area) {
super(new GridBagLayout());
super(new BorderLayout());
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
@@ -80,25 +67,41 @@ public class InstalledPackagesPanel extends JPanel {
};
myPackagesTable.getTableHeader().setReorderingAllowed(false);
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, 3, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
anInsets, 0, 0));
myUpgradeButton = new AnActionButton("Upgrade", IconUtil.getMoveUpIcon()) {
@Override
public void actionPerformed(AnActionEvent e) {
upgradeAction();
}
};
final ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myPackagesTable).disableUpDownActions()
.setAddAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
if (myPackageManagementService != null) {
ManagePackagesDialog dialog = createManagePackagesDialog();
dialog.show();
}
}
})
.setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
uninstallAction();
}
})
.addExtraAction(myUpgradeButton);
addUninstallAction();
addUpgradeAction();
decorator.setPreferredSize(new Dimension(500, 500));
add(decorator.createPanel());
myInstallButton = decorator.getActionsPanel().getAnActionButton(CommonActionsPanel.Buttons.ADD);
myUninstallButton = decorator.getActionsPanel().getAnActionButton(CommonActionsPanel.Buttons.REMOVE);
myInstallButton.setEnabled(false);
myUninstallButton.setEnabled(false);
myUpgradeButton.setEnabled(false);
myInstallButton.getTemplatePresentation().setText("Install");
myUninstallButton.getTemplatePresentation().setText("Uninstall");
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, 1.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, anInsets, 0, 0));
myPackagesTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
@@ -107,16 +110,6 @@ public class InstalledPackagesPanel extends JPanel {
}
});
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) {
@@ -162,38 +155,33 @@ public class InstalledPackagesPanel extends JPanel {
myPathChangedListeners.add(consumer);
}
private void addUpgradeAction() {
myUpgradeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final int[] rows = myPackagesTable.getSelectedRows();
if (myPackageManagementService != null) {
final Set<String> upgradedPackages = new HashSet<String>();
final Set<String> packagesShouldBePostponed = getPackagesToPostpone();
for (int row : rows) {
final Object packageObj = myPackagesTableModel.getValueAt(row, 0);
if (packageObj instanceof InstalledPackage) {
InstalledPackage pkg = (InstalledPackage)packageObj;
final String packageName = pkg.getName();
final String currentVersion = pkg.getVersion();
final String availableVersion = (String)myPackagesTableModel.getValueAt(row, 2);
private void upgradeAction() {
final int[] rows = myPackagesTable.getSelectedRows();
if (myPackageManagementService != null) {
final Set<String> upgradedPackages = new HashSet<String>();
final Set<String> packagesShouldBePostponed = getPackagesToPostpone();
for (int row : rows) {
final Object packageObj = myPackagesTableModel.getValueAt(row, 0);
if (packageObj instanceof InstalledPackage) {
InstalledPackage pkg = (InstalledPackage)packageObj;
final String packageName = pkg.getName();
final String currentVersion = pkg.getVersion();
final String availableVersion = (String)myPackagesTableModel.getValueAt(row, 2);
if (packagesShouldBePostponed.contains(packageName)) {
myWaitingToUpgrade.add((InstalledPackage)packageObj);
}
else if (PackageVersionComparator.VERSION_COMPARATOR.compare(currentVersion, availableVersion) < 0) {
upgradePackage(pkg, availableVersion);
upgradedPackages.add(packageName);
}
}
if (packagesShouldBePostponed.contains(packageName)) {
myWaitingToUpgrade.add((InstalledPackage)packageObj);
}
if (myCurrentlyInstalling.isEmpty() && upgradedPackages.isEmpty() && !myWaitingToUpgrade.isEmpty()) {
upgradePostponedPackages();
else if (PackageVersionComparator.VERSION_COMPARATOR.compare(currentVersion, availableVersion) < 0) {
upgradePackage(pkg, availableVersion);
upgradedPackages.add(packageName);
}
}
}
});
if (myCurrentlyInstalling.isEmpty() && upgradedPackages.isEmpty() && !myWaitingToUpgrade.isEmpty()) {
upgradePostponedPackages();
}
}
}
private void upgradePostponedPackages() {
@@ -283,7 +271,7 @@ public class InstalledPackagesPanel extends JPanel {
public void run() {
final int[] selected = myPackagesTable.getSelectedRows();
boolean upgradeAvailable = false;
boolean canUninstall = true;
boolean canUninstall = selected.length != 0;
boolean canUpgrade = true;
if (myPackageManagementService != null && selected.length != 0) {
for (int i = 0; i != selected.length; ++i) {
@@ -322,43 +310,38 @@ public class InstalledPackagesPanel extends JPanel {
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);
private void uninstallAction() {
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
@@ -189,7 +189,7 @@ public class PlatformProjectOpenProcessor extends ProjectOpenProcessor {
if (project == null) return null;
ProjectBaseDirectory.getInstance(project).setBaseDir(baseDir);
final Module module = runConfigurators ? runDirectoryProjectConfigurators(baseDir, project) : null;
final Module module = runConfigurators ? runDirectoryProjectConfigurators(baseDir, project) : ModuleManager.getInstance(project).getModules()[0];
openFileFromCommandLine(project, virtualFile, line);
if (!projectManager.openProject(project)) {
@@ -204,7 +204,7 @@ public class PlatformProjectOpenProcessor extends ProjectOpenProcessor {
return project;
}
if (callback != null && runConfigurators) {
if (callback != null) {
callback.projectOpened(project, module);
}
@@ -224,7 +224,7 @@ public class PyIdeCommonOptionsForm implements AbstractPyCommonOptionsForm {
}
public void updateSdkList(boolean preserveSelection, PyConfigurableInterpreterList myInterpreterList) {
myPythonSdks = myInterpreterList.getAllPythonSdks();
myPythonSdks = myInterpreterList.getAllPythonSdks(myProject);
Sdk selection = preserveSelection ? (Sdk)myInterpreterComboBox.getSelectedItem() : null;
if (!myPythonSdks.contains(selection)) {
selection = null;
@@ -266,7 +266,7 @@ public class PyIdeCommonOptionsForm implements AbstractPyCommonOptionsForm {
return myPathMappingsComponent.getMappingSettings();
}
else {
return null;
return new PathMappingSettings();
}
}
@@ -15,18 +15,21 @@
*/
package com.jetbrains.python;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkType;
import com.intellij.openapi.projectRoots.impl.SdkListCellRenderer;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.IconLoader;
import com.intellij.ui.CollectionComboBoxModel;
import com.intellij.ui.ComboboxWithBrowseButton;
import com.intellij.util.NullableConsumer;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.configuration.PythonSdkConfigurable;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
import com.jetbrains.python.configuration.PythonSdkDetailsDialog;
import com.jetbrains.python.sdk.PyDetectedSdk;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.ActionEvent;
@@ -39,10 +42,7 @@ import java.util.List;
public class PythonSdkChooserCombo extends ComboboxWithBrowseButton {
private final List<ActionListener> myChangedListeners = ContainerUtil.createLockFreeCopyOnWriteList();
//public PythonSdkChooserCombo(final Condition<Sdk> acceptableSdkCondition) {
// this(PythonSdkType.getAllSdks(), acceptableSdkCondition);
//}
@SuppressWarnings("unchecked")
public PythonSdkChooserCombo(final Project project, List<Sdk> sdks, final Condition<Sdk> acceptableSdkCondition) {
Sdk initialSelection = null;
for (Sdk sdk : sdks) {
@@ -51,26 +51,30 @@ public class PythonSdkChooserCombo extends ComboboxWithBrowseButton {
break;
}
}
getComboBox().setModel(new CollectionComboBoxModel(sdks, initialSelection));
getComboBox().setRenderer(new SdkListCellRenderer("<no interpreter>") {
final JComboBox comboBox = getComboBox();
comboBox.setModel(new CollectionComboBoxModel(sdks, initialSelection));
comboBox.setRenderer(new SdkListCellRenderer("<no interpreter>") {
@Override
protected Icon getSdkIcon(Sdk sdk) {
final PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(sdk);
return flavor != null ? flavor.getIcon() : ((SdkType)sdk.getSdkType()).getIcon();
final Icon icon = flavor != null ? flavor.getIcon() : ((SdkType)sdk.getSdkType()).getIcon();
return sdk instanceof PyDetectedSdk ? IconLoader.getTransparentIcon(icon) : icon;
}
});
addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
final PythonSdkConfigurable configurable = new PythonSdkConfigurable(project);
configurable.setNewProject(true);
ShowSettingsUtil.getInstance().editConfigurable(PythonSdkChooserCombo.this, configurable);
Sdk selection = configurable.getRealSelectedSdk();
final List<Sdk> sdks = PythonSdkType.getAllSdks();
getComboBox().setModel(new CollectionComboBoxModel(sdks, selection));
PythonSdkDetailsDialog dialog = new PythonSdkDetailsDialog(project, new NullableConsumer<Sdk>() {
@Override
public void consume(@Nullable Sdk sdk) {
comboBox.setModel(new CollectionComboBoxModel(sdks, sdk));
}
});
dialog.show();
notifyChanged(e);
}
});
getComboBox().addActionListener(new ActionListener() {
comboBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
notifyChanged(e);
}
@@ -83,6 +87,7 @@ public class PythonSdkChooserCombo extends ComboboxWithBrowseButton {
}
}
@SuppressWarnings("UnusedDeclaration")
public void addChangedListener(ActionListener listener) {
myChangedListeners.add(listener);
}
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.configuration.AddVEnvOptionsDialog">
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
@@ -8,23 +8,14 @@
<properties/>
<border type="none"/>
<children>
<component id="6ea43" class="com.intellij.ui.components.JBCheckBox" binding="myUseForThisProjectJBCheckBox" default-binding="true">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<selected value="true"/>
<text value="Set as project interpreter for &amp;this project"/>
</properties>
</component>
<vspacer id="46c2b">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="b3884" class="com.intellij.ui.components.JBCheckBox" binding="myMakeAvailableToAllJBCheckBox" default-binding="true">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Make available to &amp;all projects"/>
@@ -25,7 +25,6 @@ import java.awt.*;
* @author yole
*/
public class AddVEnvOptionsDialog extends DialogWrapper {
private JBCheckBox myUseForThisProjectJBCheckBox;
private JBCheckBox myMakeAvailableToAllJBCheckBox;
private JPanel myMainPanel;
@@ -40,10 +39,6 @@ public class AddVEnvOptionsDialog extends DialogWrapper {
return myMainPanel;
}
public boolean useForThisProject() {
return myUseForThisProjectJBCheckBox.isSelected();
}
public boolean makeAvailableToAll() {
return myMakeAvailableToAllJBCheckBox.isSelected();
}
@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.configuration.PyActiveSdkConfigurable">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="70598" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Project Interpreter:"/>
</properties>
</component>
<vspacer id="3aae9">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="7421c" class="javax.swing.JComboBox" binding="mySdkCombo">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<grid id="19f49" binding="myConfigureInterpretersPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<grid row="1" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</grid>
</children>
</grid>
</form>
@@ -16,6 +16,7 @@
package com.jetbrains.python.configuration;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.ex.EditorEx;
@@ -27,68 +28,218 @@ import com.intellij.openapi.options.UnnamedConfigurable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkModel;
import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
import com.intellij.openapi.projectRoots.impl.SdkListCellRenderer;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.CollectionComboBoxModel;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.NullableConsumer;
import com.intellij.util.ui.UIUtil;
import com.intellij.webcore.packaging.PackagesNotificationPanel;
import com.jetbrains.python.packaging.ui.PyInstalledPackagesPanel;
import com.jetbrains.python.packaging.ui.PyPackageManagementService;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.sdk.PyDetectedSdk;
import com.jetbrains.python.sdk.PySdkListCellRenderer;
import com.jetbrains.python.sdk.PythonSdkDetailsStep;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
import icons.PythonIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
/**
* @author yole
*/
public class PyActiveSdkConfigurable implements UnnamedConfigurable {
private JPanel myPanel;
private JPanel myMainPanel;
private final Project myProject;
@Nullable private final Module myModule;
private JComboBox mySdkCombo;
private JPanel myConfigureInterpretersPanel;
private MySdkModelListener mySdkModelListener;
private Sdk myAddedSdk = null;
private PyConfigurableInterpreterList myInterpreterList;
private ProjectSdksModel myProjectSdksModel;
private MyListener myListener;
private ComboBox mySdkCombo;
private PyInstalledPackagesPanel myPackagesPanel;
private JButton myDetailsButton;
private static final String SHOW_ALL = "Show All";
private NullableConsumer<Sdk> myDetailsCallback;
public PyActiveSdkConfigurable(Project project) {
public PyActiveSdkConfigurable(@NotNull Project project) {
myModule = null;
myProject = project;
init();
layoutPanel();
initContent();
}
public PyActiveSdkConfigurable(@NotNull Module module) {
myModule = module;
myProject = module.getProject();
init();
layoutPanel();
initContent();
}
private void init() {
private void initContent() {
myInterpreterList = PyConfigurableInterpreterList.getInstance(myProject);
myInterpreterList.setSdkCombo(mySdkCombo);
myProjectSdksModel = myInterpreterList.getModel();
mySdkModelListener = new MySdkModelListener(this);
myProjectSdksModel.addListener(mySdkModelListener);
mySdkCombo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final Sdk selectedSdk = (Sdk)mySdkCombo.getSelectedItem();
myPackagesPanel.updatePackages(selectedSdk != null ? new PyPackageManagementService(myProject, selectedSdk) : null);
}
});
myDetailsCallback = new NullableConsumer<Sdk>() {
@Override
public void consume(@Nullable Sdk sdk) {
if (sdk instanceof PyDetectedSdk) {
final Sdk addedSdk = SdkConfigurationUtil.setupSdk(myProjectSdksModel.getSdks(), sdk.getHomeDirectory(),
PythonSdkType.getInstance(), true,
null, null);
myAddedSdk = addedSdk;
myProjectSdksModel.addSdk(addedSdk);
myProjectSdksModel.removeSdk(sdk);
mySdkCombo.setSelectedItem(addedSdk);
}
else if (getSdk() != sdk) {
mySdkCombo.setSelectedItem(sdk);
}
}
};
myDetailsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
PythonSdkDetailsStep
.show(myProject, myProjectSdksModel.getSdks(),
myModule == null ? new PythonSdkDetailsDialog(myProject, myDetailsCallback) :
new PythonSdkDetailsDialog(myModule, myDetailsCallback),
RelativePoint.fromScreen(myDetailsButton.getLocationOnScreen()), true,
new NullableConsumer<Sdk>() {
@Override
public void consume(Sdk sdk) {
if (sdk == null) return;
if (myProjectSdksModel.findSdk(sdk) == null) {
myProjectSdksModel.addSdk(sdk);
myAddedSdk = sdk;
}
updateSdkList(false);
mySdkCombo.getModel().setSelectedItem(sdk);
myPackagesPanel.updatePackages(new PyPackageManagementService(myProject, sdk));
}
}
);
}
}
);
}
private void layoutPanel() {
final GridBagLayout layout = new GridBagLayout();
myMainPanel = new JPanel(layout);
final JLabel interpreterLabel = new JLabel("Project Interpreter:");
final JLabel emptyLabel = new JLabel(" ");
mySdkCombo = new ComboBox() {
@Override
public void setSelectedItem(Object item) {
if (SHOW_ALL.equals(item)) {
PythonSdkDetailsDialog options = myModule == null ? new PythonSdkDetailsDialog(myProject, myDetailsCallback) :
new PythonSdkDetailsDialog(myModule, myDetailsCallback);
options.show();
return;
}
if (!PySdkListCellRenderer.SEPARATOR.equals(item))
super.setSelectedItem(item);
}
};
mySdkCombo.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
mySdkCombo.setRenderer(new SdkListCellRenderer("<None>"));
myInterpreterList = PyConfigurableInterpreterList.getInstance(myProject);
myProjectSdksModel = myInterpreterList.getModel();
myListener = new MyListener(this);
myProjectSdksModel.addListener(myListener);
myConfigureInterpretersPanel.add(new PyConfigureInterpretersLinkPanel(myPanel), BorderLayout.CENTER);
myInterpreterList.setSdkCombo(mySdkCombo);
final PackagesNotificationPanel notificationsArea = new PackagesNotificationPanel(myProject);
final JComponent notificationsComponent = notificationsArea.getComponent();
notificationsComponent.setPreferredSize(new Dimension(500, 29));
myDetailsButton = new JButton();
myDetailsButton.setIcon(PythonIcons.Python.InterpreterGear);
myDetailsButton.setIconTextGap(0);
myDetailsButton.setPreferredSize(new Dimension(29, 29));
if (((UIUtil.isUnderAquaLookAndFeel())) || UIUtil.isUnderIntelliJLaF() || UIUtil.isUnderDarcula()) {
myDetailsButton.putClientProperty("JButton.buttonType", "square");
}
myPackagesPanel = new PyInstalledPackagesPanel(myProject, notificationsArea);
mySdkCombo.setPreferredSize(new Dimension(250, 29));
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(2,2,2,2);
c.gridx = 0;
c.gridy = 0;
myMainPanel.add(interpreterLabel, c);
c.gridx = 1;
c.gridy = 0;
c.weightx = 0.1;
myMainPanel.add(mySdkCombo, c);
c.insets = new Insets(0,5,0,2);
c.gridx = 2;
c.gridy = 0;
c.weightx = 0.0;
myMainPanel.add(myDetailsButton, c);
c.insets = new Insets(2,2,2,2);
c.gridx = 0;
c.gridy = 1;
c.gridwidth = 3;
myMainPanel.add(emptyLabel, c);
c.gridx = 0;
c.gridy = 2;
c.weighty = 0.5;
c.gridwidth = 3;
c.gridheight = GridBagConstraints.RELATIVE;
c.fill = GridBagConstraints.BOTH;
myMainPanel.add(myPackagesPanel, c);
c.gridheight = GridBagConstraints.REMAINDER;
c.gridx = 0;
c.gridy = 3;
c.gridwidth = 3;
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.SOUTH;
myMainPanel.add(notificationsComponent, c);
}
@Override
public JComponent createComponent() {
return myPanel;
return myMainPanel;
}
@Override
public boolean isModified() {
final Sdk sdk = getSdk();
return sdk != myProjectSdksModel.findSdk((Sdk)mySdkCombo.getSelectedItem());
final Sdk selectedItem = (Sdk)mySdkCombo.getSelectedItem();
return myAddedSdk != null || selectedItem instanceof PyDetectedSdk || sdk != myProjectSdksModel.findSdk(selectedItem);
}
@Nullable
@@ -102,28 +253,31 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable {
@Override
public void apply() throws ConfigurationException {
final Sdk selectedSdk = myProjectSdksModel.findSdk((Sdk)mySdkCombo.getSelectedItem());
if (myModule == null) {
final ProjectRootManager rootManager = ProjectRootManager.getInstance(myProject);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
final Sdk item = (Sdk)mySdkCombo.getSelectedItem();
if (item instanceof PyDetectedSdk) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
rootManager.setProjectSdk(selectedSdk);
final Sdk sdk = SdkConfigurationUtil.createAndAddSDK(item.getName(), PythonSdkType.getInstance());
myProjectSdksModel.removeSdk(item);
myProjectSdksModel.addSdk(sdk);
updateSdkList(true);
mySdkCombo.setSelectedItem(sdk);
setSdk(sdk);
}
});
}, ModalityState.any());
}
else {
ModuleRootModificationUtil.setModuleSdk(myModule, selectedSdk);
if (item != null) {
myProjectSdksModel.addSdk(item);
myProjectSdksModel.apply(null, true);
mySdkCombo.setSelectedItem(item);
}
else if (myAddedSdk != null) {
myProjectSdksModel.apply(null, true);
}
final Sdk prevSdk = ProjectRootManager.getInstance(myProject).getProjectSdk();
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
ProjectRootManager.getInstance(myProject).setProjectSdk(selectedSdk);
}
});
myProjectSdksModel.setProjectSdk(selectedSdk);
final Sdk prevSdk = ProjectRootManager.getInstance(myProject).getProjectSdk();
final Sdk selectedSdk = setSdk(item);
// update string literals if different LanguageLevel was selected
if (prevSdk != null && selectedSdk != null) {
@@ -141,6 +295,24 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable {
rehighlightStrings(myProject);
}
private Sdk setSdk(Sdk item) {
myAddedSdk = null;
final Sdk selectedSdk = myProjectSdksModel.findSdk(item);
if (myModule == null) {
final ProjectRootManager rootManager = ProjectRootManager.getInstance(myProject);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
rootManager.setProjectSdk(selectedSdk);
}
});
}
else {
ModuleRootModificationUtil.setModuleSdk(myModule, selectedSdk);
}
return selectedSdk;
}
public static void rehighlightStrings(final @NotNull Project project) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
@@ -161,6 +333,14 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable {
@Override
public void reset() {
if (myAddedSdk != null) {
myProjectSdksModel.removeSdk(myAddedSdk);
myAddedSdk = null;
}
resetSdkList();
}
private void resetSdkList() {
updateSdkList(false);
final Sdk sdk = getSdk();
@@ -168,37 +348,64 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable {
}
private void updateSdkList(boolean preserveSelection) {
final List<Sdk> sdkList = myInterpreterList.getAllPythonSdks();
final List<Sdk> sdkList = myInterpreterList.getAllPythonSdks(myProject);
Sdk selection = preserveSelection ? (Sdk)mySdkCombo.getSelectedItem() : null;
if (!sdkList.contains(selection)) {
selection = null;
}
VirtualEnvProjectFilter.removeNotMatching(myProject, sdkList);
final boolean showAll = VirtualEnvProjectFilter.removeNotMatching(myProject, sdkList);
// if the selection is a non-matching virtualenv, show it anyway
if (selection != null && !sdkList.contains(selection)) {
sdkList.add(0, selection);
}
sdkList.add(0, null);
List<Object> items = new ArrayList<Object>();
items.add(null);
boolean remoteSeparator = true;
boolean separator = true;
boolean detectedSeparator = true;
for (Sdk sdk : sdkList) {
if (!PythonSdkType.isVirtualEnv(sdk) && !PythonSdkType.isRemote(sdk) && !(sdk instanceof PyDetectedSdk) && separator) {
items.add(PySdkListCellRenderer.SEPARATOR);
separator = false;
}
if (PythonSdkType.isRemote(sdk) && remoteSeparator) {
items.add(PySdkListCellRenderer.SEPARATOR);
remoteSeparator = false;
}
if (sdk instanceof PyDetectedSdk && detectedSeparator) {
items.add(PySdkListCellRenderer.SEPARATOR);
detectedSeparator = false;
}
items.add(sdk);
}
if (showAll) {
items.add(PySdkListCellRenderer.SEPARATOR);
items.add(SHOW_ALL);
}
mySdkCombo.setRenderer(new PySdkListCellRenderer());
mySdkCombo.setModel(new CollectionComboBoxModel(sdkList, selection));
//noinspection unchecked
mySdkCombo.setModel(new CollectionComboBoxModel(items, selection));
}
@Override
public void disposeUIResources() {
myProjectSdksModel.removeListener(myListener);
myProjectSdksModel.removeListener(mySdkModelListener);
myInterpreterList.disposeModel();
}
private static class MyListener implements SdkModel.Listener {
private static class MySdkModelListener implements SdkModel.Listener {
private final PyActiveSdkConfigurable myConfigurable;
public MyListener(PyActiveSdkConfigurable configurable) {
public MySdkModelListener(PyActiveSdkConfigurable configurable) {
myConfigurable = configurable;
}
@Override
public void sdkAdded(Sdk sdk) {
myConfigurable.reset();
myConfigurable.resetSdkList();
}
@Override
@@ -18,7 +18,6 @@ package com.jetbrains.python.configuration;
import com.intellij.application.options.ModuleAwareProjectConfigurable;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.UnnamedConfigurable;
import com.intellij.openapi.project.Project;
@@ -28,17 +27,12 @@ import com.jetbrains.python.sdk.PythonSdkUpdater;
import com.jetbrains.python.testing.VFSTestFrameworkListener;
import org.jetbrains.annotations.NotNull;
/**
* @author yole
*/
public class PyActiveSdkModuleConfigurable extends ModuleAwareProjectConfigurable implements Configurable.Composite {
public class PyActiveSdkModuleConfigurable extends ModuleAwareProjectConfigurable {
private final Project myProject;
private final Configurable mySdkConfigurable;
public PyActiveSdkModuleConfigurable(Project project) {
super(project, "Project Interpreter", "reference.settings.project.interpreter");
myProject = project;
mySdkConfigurable = new PythonSdkConfigurable(project);
}
@NotNull
@@ -67,9 +61,4 @@ public class PyActiveSdkModuleConfigurable extends ModuleAwareProjectConfigurabl
}
PythonSdkUpdater.getInstance().updateActiveSdks(myProject, 0);
}
@Override
public Configurable[] getConfigurables() {
return new Configurable[] { mySdkConfigurable };
}
}
@@ -18,15 +18,20 @@ package com.jetbrains.python.configuration;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
import com.intellij.openapi.util.Comparing;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.sdk.PyDetectedSdk;
import com.jetbrains.python.sdk.PySdkUtil;
import com.jetbrains.python.sdk.PythonSdkAdditionalData;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
import com.jetbrains.python.sdk.flavors.VirtualEnvSdkFlavor;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.*;
/**
* Manages the SDK model shared between PythonSdkConfigurable and PyActiveSdkConfigurable.
@@ -65,19 +70,77 @@ public class PyConfigurableInterpreterList {
}
}
public List<Sdk> getAllPythonSdks() {
public List<Sdk> getAllPythonSdks(@Nullable final Project project) {
List<Sdk> result = new ArrayList<Sdk>();
for (Sdk sdk : getModel().getSdks()) {
if (sdk.getSdkType() instanceof PythonSdkType) {
result.add(sdk);
}
}
Collections.sort(result, new Comparator<Sdk>() {
@Override
public int compare(Sdk o1, Sdk o2) {
if (!(o1.getSdkType() instanceof PythonSdkType) ||
!(o2.getSdkType() instanceof PythonSdkType))
return -Comparing.compare(o1.getName(), o2.getName());
final boolean isVEnv1 = PythonSdkType.isVirtualEnv(o1);
final boolean isVEnv2 = PythonSdkType.isVirtualEnv(o2);
final boolean isRemote1 = PySdkUtil.isRemote(o1);
final boolean isRemote2 = PySdkUtil.isRemote(o2);
final PythonSdkFlavor flavor1 = PythonSdkFlavor.getFlavor(o1);
final PythonSdkFlavor flavor2 = PythonSdkFlavor.getFlavor(o2);
final LanguageLevel level1 = flavor1 != null ? flavor1.getLanguageLevel(o1) : LanguageLevel.getDefault();
final LanguageLevel level2 = flavor2 != null ? flavor2.getLanguageLevel(o2) : LanguageLevel.getDefault();
if (isVEnv1) {
if (project != null && associatedWithCurrent(o1, project)) return -1;
if (isVEnv2) {
final int compare = Comparing.compare(level1, level2);
if (compare != 0) return -compare;
return Comparing.compare(o1.getName(), o2.getName());
}
return -1;
}
if (isVEnv2) {
return 1;
}
if (isRemote1) return 1;
if (isRemote2) return -1;
final int compare = Comparing.compare(level1, level2);
if (compare != 0) return -compare;
return Comparing.compare(o1.getName(), o2.getName());
}
});
final Collection<String> sdkHomes = new ArrayList<String>();
sdkHomes.addAll(VirtualEnvSdkFlavor.INSTANCE.suggestHomePaths());
for (PythonSdkFlavor flavor : PythonSdkFlavor.getApplicableFlavors()) {
if (flavor instanceof VirtualEnvSdkFlavor) continue;
sdkHomes.addAll(flavor.suggestHomePaths());
}
for (String sdkHome : SdkConfigurationUtil.filterExistingPaths(PythonSdkType.getInstance(), sdkHomes, getModel().getSdks())) {
result.add(new PyDetectedSdk(sdkHome));
}
return result;
}
private static boolean associatedWithCurrent(Sdk o1, Project project) {
final PythonSdkAdditionalData data = (PythonSdkAdditionalData)o1.getSdkAdditionalData();
if (data != null) {
final String path = data.getAssociatedProjectPath();
final String projectBasePath = project.getBasePath();
if (path != null && path.equals(projectBasePath)) {
return true;
}
}
return false;
}
public List<Sdk> getAllPythonSdks() {
return getAllPythonSdks(null);
}
}
@@ -16,6 +16,7 @@
package com.jetbrains.python.configuration;
import com.intellij.ide.DataManager;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.options.newEditor.OptionsEditor;
import com.intellij.ui.ClickListener;
import com.intellij.ui.components.JBLabel;
@@ -41,7 +42,7 @@ public class PyConfigureInterpretersLinkPanel extends JPanel {
if (clickCount == 1) {
final OptionsEditor optionsEditor = OptionsEditor.KEY.getData(DataManager.getInstance().getDataContext(parentPanel));
if (optionsEditor != null) {
PythonSdkConfigurable configurable = optionsEditor.findConfigurable(PythonSdkConfigurable.class);
SearchableConfigurable configurable = optionsEditor.findConfigurableById(PyActiveSdkModuleConfigurable.class.getName());
if (configurable != null) {
optionsEditor.clearSearchAndSelect(configurable);
}
@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.configuration.PythonSdkConfigurable">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="611" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="feff7" binding="mySplitterHolder" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints>
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="6feb9" binding="myNotificationsPlaceholder" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints border-constraint="South"/>
<properties>
<background swing-color="ArrowButton.background"/>
<minimumSize width="6" height="6"/>
<preferredSize width="6" height="23"/>
</properties>
<border type="none"/>
<children/>
</grid>
</children>
</grid>
</children>
</grid>
</form>
@@ -15,41 +15,31 @@
*/
package com.jetbrains.python.configuration;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.CommonBundle;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.*;
import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkModel;
import com.intellij.openapi.projectRoots.SdkModificator;
import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
import com.intellij.openapi.ui.DialogBuilder;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.remotesdk.RemoteCredentials;
import com.intellij.ui.*;
import com.intellij.ui.components.JBList;
import com.intellij.ui.components.JBTabbedPane;
import com.intellij.util.Consumer;
import com.intellij.util.NullableConsumer;
import com.intellij.util.NullableFunction;
import com.intellij.util.containers.FactoryMap;
import com.intellij.webcore.packaging.PackagesNotificationPanel;
import com.jetbrains.python.packaging.ui.PyInstalledPackagesPanel;
import com.jetbrains.python.packaging.ui.PyPackageManagementService;
import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
import com.jetbrains.python.sdk.*;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
@@ -63,19 +53,12 @@ import java.awt.*;
import java.util.*;
import java.util.List;
public class PythonSdkConfigurable implements Configurable, Configurable.NoScroll {
private JPanel myPanel;
public class PythonSdkDetailsDialog extends DialogWrapper {
private JPanel myMainPanel;
private JList mySdkList;
private JPanel mySplitterHolder;
private PackagesNotificationPanel myNotificationsArea;
private JPanel myNotificationsPlaceholder;
private PythonPathEditor myPathEditor;
private boolean mySdkListChanged = false;
private boolean myMakeActiveAdded = false;
private Sdk myAddedSdk;
private final PyConfigurableInterpreterList myInterpreterList;
private final ProjectSdksModel myProjectSdksModel;
private final PyInstalledPackagesPanel myPackagesPanel;
private Map<Sdk, SdkModificator> myModificators = new FactoryMap<Sdk, SdkModificator>() {
@Override
@@ -84,25 +67,43 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol
}
};
private Set<SdkModificator> myModifiedModificators = new HashSet<SdkModificator>();
private Sdk myPreviousSelection;
private boolean myFirstReset;
private final Project myProject;
private boolean myNewProject = false;
private boolean myShowOtherProjectVirtualenvs = false;
private boolean myShowOtherProjectVirtualenvs = true;
private final Module myModule;
private NullableConsumer<Sdk> myShowMoreCallback;
public void setNewProject(final boolean newProject) {
myNewProject = newProject;
}
public PythonSdkDetailsDialog(Project project, NullableConsumer<Sdk> showMoreCallback) {
super(project);
myModule = null;
public PythonSdkConfigurable(Project project) {
setTitle("Project Interpreters");
myShowMoreCallback = showMoreCallback;
myProject = project;
myInterpreterList = PyConfigurableInterpreterList.getInstance(myProject);
myProjectSdksModel = myInterpreterList.getModel();
myFirstReset = true;
init();
updateOkButton();
}
public PythonSdkDetailsDialog(Module module, NullableConsumer<Sdk> showMoreCallback) {
super(module.getProject());
myModule = module;
setTitle("Project Interpreters");
myShowMoreCallback = showMoreCallback;
myProject = module.getProject();
myInterpreterList = PyConfigurableInterpreterList.getInstance(myProject);
myProjectSdksModel = myInterpreterList.getModel();
init();
updateOkButton();
}
@Nullable
@Override
protected JComponent createCenterPanel() {
mySdkList = new JBList();
//noinspection unchecked
mySdkList.setCellRenderer(new PySdkListCellRenderer("", myModificators));
mySdkList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
@@ -111,65 +112,32 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol
@Override
public void run(AnActionButton button) {
addSdk(button);
updateOkButton();
}
})
.setEditAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
editSdk();
updateOkButton();
}
})
.setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
removeSdk();
updateOkButton();
}
})
.addExtraAction(new CreateVirtualEnvButton())
.addExtraAction(new ToggleVirtualEnvFilterButton());
final Splitter splitter = new Splitter(true);
/*
final JScrollPane sdkListPane = ScrollPaneFactory.createScrollPane(mySdkList,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
sdkListPane.setPreferredSize(new Dimension(10, 10));
*/
splitter.setFirstComponent(decorator.createPanel());
myPathEditor =
new PythonPathEditor("Classes", OrderRootType.CLASSES, FileChooserDescriptorFactory.createAllButJarContentsDescriptor()) {
@Override
protected void onReloadButtonClicked() {
reloadSdk();
}
};
myNotificationsArea = new PackagesNotificationPanel(project);
myNotificationsPlaceholder.add(myNotificationsArea.getComponent(), BorderLayout.CENTER);
final JBTabbedPane tabbedPane = new JBTabbedPane(SwingConstants.TOP);
myPackagesPanel = new PyInstalledPackagesPanel(project, myNotificationsArea);
tabbedPane.addTab("Packages", myPackagesPanel);
JPanel panel1 = new JPanel(new GridBagLayout());
Insets anInsets1 = new Insets(2, 2, 2, 2);
JScrollPane scrollPane1 = ScrollPaneFactory.createScrollPane(myPathEditor.createComponent(),
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane1.setPreferredSize(new Dimension(500, 500));
panel1.add(scrollPane1, new GridBagConstraints(0, 0, 1, 8, 1.0, 1.0,
GridBagConstraints.CENTER,
GridBagConstraints.BOTH,
anInsets1, 0, 0));
tabbedPane.addTab("Paths", panel1);
splitter.setSecondComponent(tabbedPane);
mySplitterHolder.add(splitter, BorderLayout.CENTER);
.addExtraAction(new ToggleVirtualEnvFilterButton())
.addExtraAction(new ShowPathButton())
.addExtraAction(new GenerateSkeletonsButton());
decorator.setPreferredSize(new Dimension(600, 500));
myMainPanel = decorator.createPanel();
refreshSdkList();
addListeners();
return myMainPanel;
}
private void addListeners() {
@@ -191,99 +159,49 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol
public void sdkHomeSelected(Sdk sdk, String newSdkHome) {
}
});
myPackagesPanel.addPathChangedListener(new Consumer<Sdk>() {
@Override
public void consume(Sdk sdk) {
updateSdkPaths(sdk);
}
});
myNotificationsArea.addLinkHandler(PyInstalledPackagesPanel.CREATE_VENV, new Runnable() {
@Override
public void run() {
createVirtualEnv(getSelectedSdk());
}
});
mySdkList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent event) {
updateOkButton();
updateUI(getSelectedSdk());
}
});
}
private void updateUI(final Sdk selectedSdk) {
if (myPreviousSelection != null) {
saveSdkPaths(myPreviousSelection);
}
myProjectSdksModel.setProjectSdk(selectedSdk);
updateSdkPaths(selectedSdk);
myPreviousSelection = selectedSdk;
myPackagesPanel.updatePackages(selectedSdk == null ? null : new PyPackageManagementService(myProject, selectedSdk));
if (selectedSdk != null) {
myPackagesPanel.updateNotifications(selectedSdk);
}
}
private void createVirtualEnv(Sdk sdk) {
CreateVirtualEnvDialog.VirtualEnvCallback callback = new CreateVirtualEnvDialog.VirtualEnvCallback() {
@Override
public void virtualEnvCreated(Sdk sdk, boolean associateWithProject, boolean setAsProjectInterpreter) {
PythonSdkType.setupSdkPaths(sdk, myProject, null);
if (associateWithProject) {
SdkAdditionalData additionalData = sdk.getSdkAdditionalData();
if (additionalData == null) {
additionalData = new PythonSdkAdditionalData(PythonSdkFlavor.getFlavor(sdk.getHomePath()));
((ProjectJdkImpl)sdk).setSdkAdditionalData(additionalData);
}
if (myNewProject) {
((PythonSdkAdditionalData)additionalData).associateWithNewProject();
}
else {
((PythonSdkAdditionalData)additionalData).associateWithProject(myProject);
}
}
myMakeActiveAdded = setAsProjectInterpreter;
addCreatedSdk(sdk, true);
}
};
final List<Sdk> allSdks = PyConfigurableInterpreterList.getInstance(myProject).getAllPythonSdks();
final CreateVirtualEnvDialog dialog = new CreateVirtualEnvDialog(myProject, myNewProject, allSdks, sdk);
dialog.show();
if (dialog.isOK()) {
dialog.createVirtualEnv(allSdks, callback);
}
}
public String getDisplayName() {
return "Python Interpreters";
}
public String getHelpTopic() {
return "python_interpreter";
}
public JComponent createComponent() {
return myPanel;
}
public boolean isModified() {
return mySdkListChanged ||
myProjectSdksModel.isModified() ||
myPathEditor.isModified() ||
!myModifiedModificators.isEmpty();
}
@Nullable
private String getSelectedSdkName() {
final Sdk selectedSdk = (Sdk)mySdkList.getSelectedValue();
return selectedSdk == null ? null : selectedSdk.getName();
@Override
public JComponent getPreferredFocusedComponent() {
return mySdkList;
}
public boolean isModified() {
Sdk projectSdk = getSdk();
if (projectSdk != null) {
projectSdk = myProjectSdksModel.findSdk(projectSdk.getName());
}
return getSelectedSdk() != projectSdk || mySdkListChanged ||
myProjectSdksModel.isModified() ||
!myModifiedModificators.isEmpty();
}
protected void updateOkButton() {
super.setOKActionEnabled(isModified());
}
@Override
protected void doOKAction() {
try {
apply();
}
catch (ConfigurationException ignored) {
}
super.doOKAction();
}
public void apply() throws ConfigurationException {
if (myPreviousSelection != null) {
saveSdkPaths(myPreviousSelection);
}
for (SdkModificator modificator : myModifiedModificators) {
modificator.commitChanges();
}
@@ -291,21 +209,7 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol
myModifiedModificators.clear();
myProjectSdksModel.apply();
mySdkListChanged = false;
if (myMakeActiveAdded) {
SdkConfigurationUtil.setDirectoryProjectSdk(myProject, myAddedSdk);
myProjectSdksModel.setProjectSdk(myAddedSdk);
myInterpreterList.setSelectedSdk(myAddedSdk);
}
}
/**
* Returns the stable copy of the SDK currently selected in the SDK table.
*
* @return the selected SDK, or null if there's no selection
*/
@Nullable
public Sdk getRealSelectedSdk() {
return ProjectJdkTable.getInstance().findJdk(getSelectedSdkName());
myShowMoreCallback.consume(getSelectedSdk());
}
@Nullable
@@ -313,38 +217,17 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol
return (Sdk)mySdkList.getSelectedValue();
}
public void reset() {
clearModificators();
if (myFirstReset) {
myFirstReset = false;
}
else {
myProjectSdksModel.reset(null);
}
refreshSdkList();
final Sdk selectedSdk = getRealSelectedSdk();
if (selectedSdk != null) {
myPackagesPanel.updateNotifications(selectedSdk);
}
}
private void clearModificators() {
myModificators.clear();
myModifiedModificators.clear();
myPreviousSelection = null;
}
private void refreshSdkList() {
final List<Sdk> pythonSdks = myInterpreterList.getAllPythonSdks();
Sdk projectSdk = myProjectSdksModel.getProjectSdk();
final List<Sdk> pythonSdks = myInterpreterList.getAllPythonSdks(myProject);
Sdk projectSdk = getSdk();
if (!myShowOtherProjectVirtualenvs) {
VirtualEnvProjectFilter.removeNotMatching(myProject, pythonSdks);
}
Collections.sort(pythonSdks, new PreferredSdkComparator());
//noinspection unchecked
mySdkList.setModel(new CollectionListModel<Sdk>(pythonSdks));
mySdkListChanged = false;
if (projectSdk == null) projectSdk = getSdk();
if (projectSdk != null) {
projectSdk = myProjectSdksModel.findSdk(projectSdk.getName());
mySdkList.clearSelection();
@@ -352,24 +235,20 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol
mySdkList.updateUI();
}
}
@Nullable
private Sdk getSdk() {
final Module[] modules = ModuleManager.getInstance(myProject).getModules();
if (modules.length > 0) {
final Module module = modules[0];
final ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
return rootManager.getSdk();
if (myModule == null) {
return ProjectRootManager.getInstance(myProject).getProjectSdk();
}
return ProjectRootManager.getInstance(myProject).getProjectSdk();
final ModuleRootManager rootManager = ModuleRootManager.getInstance(myModule);
return rootManager.getSdk();
}
private void addSdk(AnActionButton button) {
InterpreterPathChooser
.show(myProject, myProjectSdksModel.getSdks(), button.getPreferredPopupPoint(), false, new NullableConsumer<Sdk>() {
PythonSdkDetailsStep
.show(myProject, myProjectSdksModel.getSdks(), this, button.getPreferredPopupPoint(), false, new NullableConsumer<Sdk>() {
@Override
public void consume(Sdk sdk) {
myMakeActiveAdded = false;
addCreatedSdk(sdk, false);
}
});
@@ -377,11 +256,9 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol
private void addCreatedSdk(@Nullable final Sdk sdk, boolean newVirtualEnv) {
if (sdk != null) {
myAddedSdk = sdk;
boolean isVirtualEnv = PythonSdkType.isVirtualEnv(sdk);
boolean askSetAsProjectInterpreter = !myProject.isDefault() && !myNewProject;
if (askSetAsProjectInterpreter && isVirtualEnv && !newVirtualEnv) {
AddVEnvOptionsDialog dialog = new AddVEnvOptionsDialog(myPanel);
if (isVirtualEnv && !newVirtualEnv) {
AddVEnvOptionsDialog dialog = new AddVEnvOptionsDialog(myMainPanel);
dialog.show();
if (dialog.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
return;
@@ -389,20 +266,13 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol
SdkModificator modificator = myModificators.get(sdk);
setSdkAssociated(modificator, !dialog.makeAvailableToAll());
myModifiedModificators.add(modificator);
myMakeActiveAdded = dialog.useForThisProject();
}
myProjectSdksModel.addSdk(sdk);
final Sdk oldSdk = myProjectSdksModel.findSdk(sdk);
if (oldSdk == null)
myProjectSdksModel.addSdk(sdk);
refreshSdkList();
mySdkList.setSelectedValue(sdk, true);
mySdkListChanged = true;
if (askSetAsProjectInterpreter && !isVirtualEnv && !PythonSdkType.isInvalid(sdk) && !PythonSdkType.isIncompleteRemote(sdk)) {
//TODO: make native mac dialog work
myMakeActiveAdded = Messages.showIdeaMessageDialog(myProject, "Do you want to set this interpreter as Project Interpreter?",
"Project Interpreter",
new String[]{CommonBundle.getYesButtonText(), CommonBundle.getNoButtonText()}, 0, null,
null
) == Messages.YES;
}
}
}
@@ -521,55 +391,6 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol
private void reloadSdk(Sdk currentSdk) {
PythonSdkType.setupSdkPaths(myProject, null, currentSdk, myModificators.get(currentSdk)); // or must it be a RunWriteAction?
reloadSdkPaths(currentSdk);
}
public void disposeUIResources() {
myInterpreterList.disposeModel();
clearModificators();
myFirstReset = true;
}
private void saveSdkPaths(Sdk selection) {
SdkModificator modificator = myModificators.get(selection);
if (myPathEditor.isModified()) {
myPathEditor.apply(modificator);
myModifiedModificators.add(modificator);
}
}
private void reloadSdkPaths(Sdk selection) {
List<VirtualFile> rootPaths = Lists.newArrayList();
if (selection != null) {
Collections.addAll(rootPaths, selection.getRootProvider().getFiles(OrderRootType.CLASSES));
myPathEditor.reload(myModificators.get(selection));
}
else {
myPathEditor.reload(null);
}
}
private void updateSdkPaths(final Sdk selection) {
final List<VirtualFile> rootPaths = Lists.newArrayList();
if (selection != null) {
Collections.addAll(rootPaths, selection.getRootProvider().getFiles(OrderRootType.CLASSES));
myPathEditor.reset(myModificators.get(selection));
}
else {
myPathEditor.reset(null);
}
}
private class CreateVirtualEnvButton extends AnActionButton implements DumbAware {
public CreateVirtualEnvButton() {
super("Create Virtual Environment", PythonIcons.Python.Virtualenv);
}
@Override
public void actionPerformed(AnActionEvent e) {
Sdk selectedSdk = getSelectedSdk();
createVirtualEnv(selectedSdk);
}
}
private class ToggleVirtualEnvFilterButton extends ToggleActionButton implements DumbAware {
@@ -586,6 +407,73 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol
public void setSelected(AnActionEvent e, boolean state) {
myShowOtherProjectVirtualenvs = state;
refreshSdkList();
updateOkButton();
}
}
private class ShowPathButton extends AnActionButton implements DumbAware {
public ShowPathButton() {
super("Show path for the selected interpreter", AllIcons.Actions.ShowAsTree);
}
@Override
public boolean isEnabled() {
return !(getSelectedSdk() instanceof PyDetectedSdk);
}
@Override
public void actionPerformed(AnActionEvent e) {
DialogBuilder dialog = new DialogBuilder(myProject);
final PythonPathEditor editor =
new PythonPathEditor("Classes", OrderRootType.CLASSES, FileChooserDescriptorFactory.createAllButJarContentsDescriptor()) {
@Override
protected void onReloadButtonClicked() {
reloadSdk();
}
};
final JComponent component = editor.createComponent();
component.setPreferredSize(new Dimension(600, 400));
component.setBorder(IdeBorderFactory.createBorder(SideBorder.ALL));
dialog.setCenterPanel(component);
final Sdk sdk = getSelectedSdk();
editor.reload(sdk != null ? sdk.getSdkModificator(): null);
dialog.setTitle("Interpreter Paths");
dialog.show();
updateOkButton();
}
}
private class GenerateSkeletonsButton extends AnActionButton implements DumbAware {
public GenerateSkeletonsButton() {
super("Generate skeletons for the selected interpreter", PythonIcons.Python.Skeleton);
}
@Override
public boolean isEnabled() {
return (getSelectedSdk() instanceof PyDetectedSdk);
}
@Override
public void actionPerformed(AnActionEvent e) {
final Sdk sdk = getSelectedSdk();
if (sdk instanceof PyDetectedSdk) {
try {
myProjectSdksModel.apply();
}
catch (ConfigurationException ignored) {
}
final Sdk addedSdk = SdkConfigurationUtil.setupSdk(myProjectSdksModel.getSdks(), sdk.getHomeDirectory(),
PythonSdkType.getInstance(), true,
null, null);
myProjectSdksModel.addSdk(addedSdk);
myProjectSdksModel.removeSdk(sdk);
refreshSdkList();
mySdkList.setSelectedValue(addedSdk, true);
updateOkButton();
}
}
}
}
@@ -50,13 +50,14 @@ public class VirtualEnvProjectFilter implements Predicate<Sdk> {
return false;
}
public static void removeNotMatching(Project project, List<Sdk> sdks) {
public static boolean removeNotMatching(Project project, List<Sdk> sdks) {
if (project != null) {
final String basePath = project.getBasePath();
if (basePath != null) {
Iterables.removeIf(sdks, new VirtualEnvProjectFilter(FileUtil.toSystemIndependentName(basePath)));
return Iterables.removeIf(sdks, new VirtualEnvProjectFilter(FileUtil.toSystemIndependentName(basePath)));
}
}
return false;
}
public static void removeAllAssociated(List<Sdk> sdks) {
@@ -27,6 +27,7 @@ import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.platform.NewDirectoryProjectAction;
import com.jetbrains.python.sdk.PyDetectedSdk;
import com.jetbrains.python.sdk.PythonSdkAdditionalData;
import com.jetbrains.python.sdk.PythonSdkType;
@@ -48,6 +49,9 @@ public class PythonNewDirectoryProjectAction extends NewDirectoryProjectAction {
dlg.show();
if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) return;
mySdk = dlg.getSdk();
if (mySdk instanceof PyDetectedSdk) {
mySdk = SdkConfigurationUtil.createAndAddSDK(mySdk.getName(), PythonSdkType.getInstance());
}
myInstallFramework = dlg.installFramework();
Project newProject = generateProject(project, dlg);
if (newProject != null) {
@@ -31,6 +31,7 @@ import com.intellij.ui.ComboboxWithBrowseButton;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.jetbrains.python.PythonSdkChooserCombo;
import com.jetbrains.python.configuration.PyConfigurableInterpreterList;
import com.jetbrains.python.configuration.VirtualEnvProjectFilter;
import com.jetbrains.python.packaging.PyExternalProcessException;
import com.jetbrains.python.packaging.PyPackage;
@@ -38,7 +39,6 @@ import com.jetbrains.python.packaging.PyPackageManager;
import com.jetbrains.python.packaging.PyPackageManagerImpl;
import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
import com.jetbrains.python.remote.RemoteProjectSettings;
import com.jetbrains.python.sdk.PreferredSdkComparator;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.sdk.flavors.JythonSdkFlavor;
import com.jetbrains.python.sdk.flavors.PyPySdkFlavor;
@@ -53,7 +53,6 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.List;
/**
@@ -69,9 +68,8 @@ public class PythonNewDirectoryProjectDialog extends NewDirectoryProjectDialog {
super(project);
myProject = project;
final List<Sdk> sdks = PythonSdkType.getAllSdks();
final List<Sdk> sdks = PyConfigurableInterpreterList.getInstance(myProject).getAllPythonSdks();
VirtualEnvProjectFilter.removeAllAssociated(sdks);
Collections.sort(sdks, PreferredSdkComparator.INSTANCE);
final Sdk preferred = sdks.isEmpty() ? null : sdks.iterator().next();
mySdkCombo = new PythonSdkChooserCombo(project, sdks, new Condition<Sdk>() {
@Override
+2 -1
View File
@@ -1,2 +1,3 @@
# Only has artwork and tips stuff
skip: *
skip: *.png
skip: tips
Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1019 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

-1
View File
@@ -31,7 +31,6 @@
<projectAttachProcessor implementation="com.intellij.platform.ModuleAttachProcessor"/>
<!--<applicationConfigurable instance="com.jetbrains.python.configuration.PythonSdkConfigurable"/>-->
<projectConfigurable instance="com.jetbrains.python.configuration.PythonContentEntriesConfigurable"/>
<projectConfigurable instance="com.jetbrains.python.buildout.BuildoutModulesConfigurable"/>
<projectConfigurable instance="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable"/>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.sdk.CreateVirtualEnvDialog">
<grid id="cbd77" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="7" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="cbd77" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="6" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="48" y="54" width="550" height="342"/>
@@ -16,7 +16,7 @@
</component>
<vspacer id="b277d">
<constraints>
<grid row="6" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="5" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="48fd" class="com.intellij.ui.components.JBLabel">
@@ -60,30 +60,22 @@
</component>
<component id="41e08" class="com.intellij.ui.components.JBCheckBox" binding="myMakeAvailableToAllProjectsCheckbox">
<constraints>
<grid row="5" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
<grid row="4" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<selected value="false"/>
<text value="Make available to &amp;all projects"/>
</properties>
</component>
<component id="9835d" class="com.intellij.ui.components.JBCheckBox" binding="mySitePackagesCheckBox">
<component id="d10a9" class="com.intellij.ui.components.JBCheckBox" binding="mySitePackagesCheckBox">
<constraints>
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="3" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<selected value="false"/>
<text value="&amp;Inherit global site-packages"/>
</properties>
</component>
<component id="fd5f3" class="com.intellij.ui.components.JBCheckBox" binding="mySetAsProjectInterpreterCheckbox" default-binding="true">
<constraints>
<grid row="4" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<selected value="true"/>
<text value="&amp;Set as project interpreter for this project"/>
</properties>
</component>
</children>
</grid>
</form>
@@ -15,6 +15,8 @@
*/
package com.jetbrains.python.sdk;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.intellij.facet.ui.FacetEditorValidator;
import com.intellij.facet.ui.FacetValidatorsManager;
import com.intellij.openapi.application.Application;
@@ -67,18 +69,16 @@ public class CreateVirtualEnvDialog extends IdeaDialog {
private JTextField myName;
private JBCheckBox mySitePackagesCheckBox;
private JBCheckBox myMakeAvailableToAllProjectsCheckbox;
private JBCheckBox mySetAsProjectInterpreterCheckbox;
@Nullable private Project myProject;
private String myInitialPath;
public interface VirtualEnvCallback {
void virtualEnvCreated(Sdk sdk, boolean associateWithProject, boolean setAsProjectInterpreter);
void virtualEnvCreated(Sdk sdk, boolean associateWithProject);
}
private static void setupVirtualEnvSdk(List<Sdk> allSdks,
private void setupVirtualEnvSdk(List<Sdk> allSdks,
final String path,
boolean associateWithProject,
final boolean makeActive,
VirtualEnvCallback callback) {
final VirtualFile sdkHome =
ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
@@ -92,46 +92,45 @@ public class CreateVirtualEnvDialog extends IdeaDialog {
SdkConfigurationUtil.createUniqueSdkName(PythonSdkType.getInstance(), sdkHome.getPath(), allSdks);
final ProjectJdkImpl sdk = new ProjectJdkImpl(name, PythonSdkType.getInstance());
sdk.setHomePath(sdkHome.getPath());
callback.virtualEnvCreated(sdk, associateWithProject, makeActive);
callback.virtualEnvCreated(sdk, associateWithProject);
PythonSdkType.setupSdkPaths(sdk, myProject, null);
}
}
public CreateVirtualEnvDialog(Project project,
boolean isNewProject,
final List<Sdk> allSdks,
@Nullable Sdk suggestedBaseSdk) {
super(project);
setupDialog(project, isNewProject, allSdks, suggestedBaseSdk);
setupDialog(project, allSdks, suggestedBaseSdk);
}
public CreateVirtualEnvDialog(Component owner,
boolean isNewProject,
final List<Sdk> allSdks,
@Nullable Sdk suggestedBaseSdk) {
super(owner);
setupDialog(null, isNewProject, allSdks, suggestedBaseSdk);
setupDialog(null, allSdks, suggestedBaseSdk);
}
private void setupDialog(Project project, boolean isNewProject, List<Sdk> allSdks, @Nullable Sdk suggestedBaseSdk) {
private void setupDialog(Project project, List<Sdk> allSdks, @Nullable Sdk suggestedBaseSdk) {
myProject = project;
init();
setTitle("Create Virtual Environment");
if (suggestedBaseSdk == null && allSdks.size() > 0) {
Iterables.removeIf(allSdks, new Predicate<Sdk>() {
@Override
public boolean apply(Sdk s) {
return PythonSdkType.isInvalid(s) || PythonSdkType.isVirtualEnv(s) || RemoteSdkDataHolder.isRemoteSdk(s.getHomePath());
}
});
List<Sdk> sortedSdks = new ArrayList<Sdk>(allSdks);
Collections.sort(sortedSdks, new PreferredSdkComparator());
suggestedBaseSdk = sortedSdks.get(0);
}
updateSdkList(allSdks, suggestedBaseSdk);
myMakeAvailableToAllProjectsCheckbox.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0));
if (project == null || project.isDefault() || !PlatformUtils.isPyCharm()) {
myMakeAvailableToAllProjectsCheckbox.setSelected(true);
myMakeAvailableToAllProjectsCheckbox.setVisible(false);
mySetAsProjectInterpreterCheckbox.setSelected(false);
mySetAsProjectInterpreterCheckbox.setVisible(false);
}
else if (isNewProject) {
mySetAsProjectInterpreterCheckbox.setText("Set as project interpreter for the project being created");
}
setOKActionEnabled(false);
@@ -238,17 +237,7 @@ public class CreateVirtualEnvDialog extends IdeaDialog {
private void updateSdkList(final List<Sdk> allSdks, @Nullable Sdk initialSelection) {
mySdkCombo.setRenderer(new PySdkListCellRenderer());
List<Sdk> baseSdks = new ArrayList<Sdk>();
for (Sdk s : allSdks) {
if (!PythonSdkType.isInvalid(s) && !PythonSdkType.isVirtualEnv(s) && !RemoteSdkDataHolder.isRemoteSdk(s.getHomePath())) {
baseSdks.add(s);
}
else if (s.equals(initialSelection)){
initialSelection = null;
}
}
mySdkCombo.setModel(new CollectionComboBoxModel(baseSdks, initialSelection));
mySdkCombo.setModel(new CollectionComboBoxModel(allSdks, initialSelection));
}
@Override
@@ -288,10 +277,6 @@ public class CreateVirtualEnvDialog extends IdeaDialog {
return !myMakeAvailableToAllProjectsCheckbox.isSelected();
}
public boolean setAsProjectInterpreter() {
return mySetAsProjectInterpreterCheckbox.isSelected();
}
@Override
public JComponent getPreferredFocusedComponent() {
return myName;
@@ -326,7 +311,7 @@ public class CreateVirtualEnvDialog extends IdeaDialog {
application.invokeLater(new Runnable() {
@Override
public void run() {
setupVirtualEnvSdk(allSdks, myPath, associateWithProject(), setAsProjectInterpreter(), callback);
setupVirtualEnvSdk(allSdks, myPath, associateWithProject(), callback);
}
}, ModalityState.any());
}
@@ -37,6 +37,12 @@ public class PreferredSdkComparator implements Comparator<Sdk> {
if (remote1Weight != remote2Weight) {
return remote2Weight - remote1Weight;
}
int detectedWeight1 = o1 instanceof PyDetectedSdk ? 0 : 1;
int detectedWeight2 = o2 instanceof PyDetectedSdk ? 0 : 1;
if (detectedWeight1 != detectedWeight2) {
return detectedWeight2 - detectedWeight1;
}
int venv1weight = PythonSdkType.isVirtualEnv(o1) ? 0 : 1;
int venv2weight = PythonSdkType.isVirtualEnv(o2) ? 0 : 1;
if (venv1weight != venv2weight) {
@@ -47,6 +53,7 @@ public class PreferredSdkComparator implements Comparator<Sdk> {
if (flavor1weight != flavor2weight) {
return flavor2weight - flavor1weight;
}
return -Comparing.compare(o1.getVersionString(), o2.getVersionString());
}
}
@@ -0,0 +1,11 @@
package com.jetbrains.python.sdk;
import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
public class PyDetectedSdk extends ProjectJdkImpl {
public PyDetectedSdk(String name) {
super(name, PythonSdkType.getInstance());
setHomePath(name);
}
}
@@ -19,6 +19,7 @@ import com.intellij.icons.AllIcons;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkModificator;
import com.intellij.openapi.projectRoots.SdkType;
import com.intellij.openapi.util.IconLoader;
import com.intellij.ui.LayeredIcon;
import com.intellij.ui.ListCellRendererWrapper;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
@@ -27,9 +28,10 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Map;
public class PySdkListCellRenderer extends ListCellRendererWrapper<Sdk> {
public class PySdkListCellRenderer extends ListCellRendererWrapper<Object> {
private final String myNullText;
private final Map<Sdk, SdkModificator> mySdkModifiers;
public static final String SEPARATOR = "separator";
public PySdkListCellRenderer() {
myNullText = "";
@@ -42,8 +44,9 @@ public class PySdkListCellRenderer extends ListCellRendererWrapper<Sdk> {
}
@Override
public void customize(JList list, Sdk sdk, int index, boolean selected, boolean hasFocus) {
if (sdk != null) {
public void customize(JList list, Object item, int index, boolean selected, boolean hasFocus) {
if (item instanceof Sdk) {
Sdk sdk = (Sdk)item;
final PythonSdkFlavor flavor = PythonSdkFlavor.getPlatformIndependentFlavor(sdk.getHomePath());
final Icon icon = flavor != null ? flavor.getIcon() : ((SdkType)sdk.getSdkType()).getIcon();
@@ -54,7 +57,6 @@ public class PySdkListCellRenderer extends ListCellRendererWrapper<Sdk> {
else {
name = sdk.getName();
}
if (PythonSdkType.isInvalid(sdk)) {
setText("[invalid] " + name);
setIcon(wrapIconWithWarningDecorator(icon));
@@ -63,17 +65,22 @@ public class PySdkListCellRenderer extends ListCellRendererWrapper<Sdk> {
setText("[incomplete] " + name);
setIcon(wrapIconWithWarningDecorator(icon));
}
else if (sdk instanceof PyDetectedSdk){
setText(name);
setIcon(IconLoader.getTransparentIcon(icon));
}
else {
setText(name);
setIcon(icon);
}
}
else {
else if (SEPARATOR.equals(item))
setSeparator();
else if (item == null)
setText(myNullText);
}
}
private LayeredIcon wrapIconWithWarningDecorator(Icon icon) {
private static LayeredIcon wrapIconWithWarningDecorator(Icon icon) {
final LayeredIcon layered = new LayeredIcon(2);
layered.setIcon(icon, 0);
// TODO: Create a separate invalid SDK overlay icon (DSGN-497)
@@ -21,102 +21,80 @@ import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkAdditionalData;
import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.ui.popup.ListPopupStep;
import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.*;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.NullableConsumer;
import com.intellij.util.SystemProperties;
import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* @author yole
*/
public class InterpreterPathChooser extends BaseListPopupStep<String> {
public class PythonSdkDetailsStep extends BaseListPopupStep<String> {
private static DialogWrapper myMore;
private final Project myProject;
private final Component myOwnerComponent;
private final Sdk[] myExistingSdks;
private final NullableConsumer<Sdk> myCallback;
private static final String LOCAL = "Local...";
private static final String REMOTE = "Remote...";
private static final String VIRTUALENV = "Create VirtualEnv...";
private static final String LOCAL = "Add Local";
private static final String REMOTE = "Add Remote";
private static final String VIRTUALENV = "Create VirtualEnv";
private static final String MORE = "More...";
public static void show(final Project project,
final Sdk[] existingSdks,
final RelativePoint popupPoint,
final boolean showVirtualEnv,
DialogWrapper component, final RelativePoint popupPoint,
final boolean showMore,
final NullableConsumer<Sdk> callback) {
ListPopupStep sdkHomesStep = new InterpreterPathChooser(project, popupPoint.getComponent(), existingSdks, showVirtualEnv, callback);
myMore = component;
final ListPopupStep sdkHomesStep = new PythonSdkDetailsStep(project, popupPoint.getComponent(), existingSdks, showMore, callback);
final ListPopup popup = JBPopupFactory.getInstance().createListPopup(sdkHomesStep);
popup.show(popupPoint);
}
public InterpreterPathChooser(Project project,
Component ownerComponent,
Sdk[] existingSdks,
boolean showVirtualEnv,
NullableConsumer<Sdk> callback) {
super("Select Interpreter Path", getSuggestedPythonSdkPaths(existingSdks, showVirtualEnv));
public PythonSdkDetailsStep(Project project,
Component ownerComponent,
Sdk[] existingSdks,
boolean showMore,
NullableConsumer<Sdk> callback) {
super(null, getAvailableOptions(showMore));
myProject = project;
myOwnerComponent = ownerComponent;
myExistingSdks = existingSdks;
myCallback = callback;
}
private static List<String> getSuggestedPythonSdkPaths(Sdk[] existingSdks, boolean showVirtualEnv) {
List<String> paths = new ArrayList<String>();
Collection<String> sdkHomes = PythonSdkType.getInstance().suggestHomePaths();
for (String sdkHome : SdkConfigurationUtil.filterExistingPaths(PythonSdkType.getInstance(), sdkHomes, existingSdks)) {
paths.add(FileUtil.getLocationRelativeToUserHome(sdkHome));
}
paths.add(LOCAL);
private static List<String> getAvailableOptions(boolean showMore) {
final List<String> options = new ArrayList<String>();
options.add(LOCAL);
if (PythonRemoteInterpreterManager.getInstance() != null) {
paths.add(REMOTE);
options.add(REMOTE);
}
if (showVirtualEnv) {
paths.add(VIRTUALENV);
options.add(VIRTUALENV);
if (showMore) {
options.add(MORE);
}
return paths;
return options;
}
@Nullable
@Override
public Icon getIconFor(String aValue) {
if (LOCAL.equals(aValue) || REMOTE.equals(aValue) || VIRTUALENV.equals(aValue)) return null;
String filePath = aValue;
if (StringUtil.startsWithChar(filePath, '~')) {
String home = SystemProperties.getUserHome();
filePath = home + filePath.substring(1);
}
final PythonSdkFlavor flavor = PythonSdkFlavor.getPlatformIndependentFlavor(filePath);
return flavor != null ? flavor.getIcon() : PythonSdkType.getInstance().getIcon();
public ListSeparator getSeparatorAbove(String value) {
return MORE.equals(value) ? new ListSeparator() : null;
}
@NotNull
@Override
public String getTextFor(String value) {
return FileUtil.toSystemDependentName(value);
}
private void sdkSelected(final String selectedValue) {
private void optionSelected(final String selectedValue) {
if (LOCAL.equals(selectedValue)) {
createLocalSdk();
}
@@ -127,7 +105,7 @@ public class InterpreterPathChooser extends BaseListPopupStep<String> {
createVirtualEnvSdk();
}
else {
createSdkFromPath(selectedValue);
myMore.show();
}
}
@@ -135,7 +113,7 @@ public class InterpreterPathChooser extends BaseListPopupStep<String> {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
SdkConfigurationUtil.createSdk(myProject, myExistingSdks, myCallback, PythonSdkType.getInstance());
SdkConfigurationUtil.createSdk(myProject, myExistingSdks, myCallback, false, PythonSdkType.getInstance());
}
}, ModalityState.any());
}
@@ -152,35 +130,43 @@ public class InterpreterPathChooser extends BaseListPopupStep<String> {
}
}
private void createSdkFromPath(String selectedPath) {
String filePath = selectedPath;
if (StringUtil.startsWithChar(filePath, '~')) {
String home = SystemProperties.getUserHome();
filePath = home + filePath.substring(1);
}
Sdk sdk = SdkConfigurationUtil.setupSdk(myExistingSdks,
LocalFileSystem.getInstance().findFileByPath(filePath),
PythonSdkType.getInstance(), false, null, null);
myCallback.consume(sdk);
}
private void createVirtualEnvSdk() {
CreateVirtualEnvDialog.VirtualEnvCallback callback = new CreateVirtualEnvDialog.VirtualEnvCallback() {
@Override
public void virtualEnvCreated(Sdk sdk, boolean associateWithProject) {
PythonSdkType.setupSdkPaths(sdk, myProject, null);
if (associateWithProject) {
SdkAdditionalData additionalData = sdk.getSdkAdditionalData();
if (additionalData == null) {
additionalData = new PythonSdkAdditionalData(PythonSdkFlavor.getFlavor(sdk.getHomePath()));
((ProjectJdkImpl)sdk).setSdkAdditionalData(additionalData);
}
((PythonSdkAdditionalData)additionalData).associateWithProject(myProject);
}
myCallback.consume(sdk);
}
};
final CreateVirtualEnvDialog dialog;
final List<Sdk> allSdks = Arrays.asList(myExistingSdks);
final List<Sdk> allSdks = Lists.newArrayList(myExistingSdks);
final List<PythonSdkFlavor> flavors = PythonSdkFlavor.getApplicableFlavors(false);
for (PythonSdkFlavor flavor : flavors) {
final Collection<String> strings = flavor.suggestHomePaths();
for (String string : strings) {
allSdks.add(new PyDetectedSdk(string));
}
}
if (myProject != null) {
dialog = new CreateVirtualEnvDialog(myProject, false, allSdks, null);
dialog = new CreateVirtualEnvDialog(myProject, allSdks, null);
}
else {
dialog = new CreateVirtualEnvDialog(myOwnerComponent, false, allSdks, null);
dialog = new CreateVirtualEnvDialog(myOwnerComponent, allSdks, null);
}
dialog.show();
if (dialog.isOK()) {
dialog.createVirtualEnv(allSdks, new CreateVirtualEnvDialog.VirtualEnvCallback() {
@Override
public void virtualEnvCreated(Sdk sdk, boolean associateWithProject, boolean setAsProjectInterpreter) {
myCallback.consume(sdk);
}
});
dialog.createVirtualEnv(allSdks, callback);
}
}
@@ -193,7 +179,7 @@ public class InterpreterPathChooser extends BaseListPopupStep<String> {
public PopupStep onChosen(final String selectedValue, boolean finalChoice) {
return doFinalStep(new Runnable() {
public void run() {
sdkSelected(selectedValue);
optionSelected(selectedValue);
}
});
}
@@ -45,7 +45,10 @@ import com.intellij.openapi.util.io.FileSystemUtil;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.CharFilter;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.reference.SoftReference;
import com.intellij.remotesdk.RemoteSdkData;
@@ -277,15 +280,16 @@ public class PythonSdkType extends SdkType {
public void showCustomCreateUI(SdkModel sdkModel, final JComponent parentComponent, final Consumer<Sdk> sdkCreatedCallback) {
Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(parentComponent));
InterpreterPathChooser.show(project, sdkModel.getSdks(), RelativePoint.getCenterOf(parentComponent), true, new NullableConsumer<Sdk>() {
@Override
public void consume(@Nullable Sdk sdk) {
if (sdk != null) {
sdk.putUserData(SDK_CREATOR_COMPONENT_KEY, new WeakReference<Component>(parentComponent));
sdkCreatedCallback.consume(sdk);
PythonSdkDetailsStep
.show(project, sdkModel.getSdks(), null, RelativePoint.getCenterOf(parentComponent), true, new NullableConsumer<Sdk>() {
@Override
public void consume(@Nullable Sdk sdk) {
if (sdk != null) {
sdk.putUserData(SDK_CREATOR_COMPONENT_KEY, new WeakReference<Component>(parentComponent));
sdkCreatedCallback.consume(sdk);
}
}
}
});
});
}
public static boolean isVirtualEnv(Sdk sdk) {
@@ -16,6 +16,7 @@
package com.jetbrains.python.sdk.flavors;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VFileProperty;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
@@ -57,7 +58,7 @@ public class MacPythonSdkFlavor extends CPythonSdkFlavor {
if (binDir != null && binDir.isDirectory()) {
for (String name : POSSIBLE_BINARY_NAMES) {
final VirtualFile child = binDir.findChild(name);
if (child != null) {
if (child != null && !child.is(VFileProperty.SYMLINK)) {
candidates.add(child.getPath());
break;
}
@@ -72,6 +72,10 @@ public abstract class PythonSdkFlavor {
}
public static List<PythonSdkFlavor> getApplicableFlavors() {
return getApplicableFlavors(true);
}
public static List<PythonSdkFlavor> getApplicableFlavors(boolean addPlatformIndependent) {
List<PythonSdkFlavor> result = new ArrayList<PythonSdkFlavor>();
if (SystemInfo.isWindows) {
@@ -84,7 +88,8 @@ public abstract class PythonSdkFlavor {
result.add(UnixPythonSdkFlavor.INSTANCE);
}
result.addAll(getPlatformIndependentFlavors());
if (addPlatformIndependent)
result.addAll(getPlatformIndependentFlavors());
return result;
}
@@ -16,6 +16,7 @@
package com.jetbrains.python.sdk.flavors;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VFileProperty;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
@@ -47,14 +48,15 @@ public class UnixPythonSdkFlavor extends CPythonSdkFlavor {
if (rootDir instanceof NewVirtualFile) {
((NewVirtualFile)rootDir).markDirty();
}
rootDir.refresh(false, false);
rootDir.refresh(true, false);
VirtualFile[] suspects = rootDir.getChildren();
for (VirtualFile child : suspects) {
if (!child.isDirectory()) {
final String childName = child.getName();
for (String name : NAMES) {
if (childName.startsWith(name)) {
if (!childName.endsWith("-config") && !childName.startsWith("pythonw")) {
if (!childName.endsWith("-config") && !childName.startsWith("pythonw") &&
!childName.endsWith("m") && !child.is(VFileProperty.SYMLINK)) {
candidates.add(child.getPath());
}
break;
@@ -83,7 +83,7 @@ public class VirtualEnvSdkFlavor extends CPythonSdkFlavor {
public static Collection<String> findInDirectory(VirtualFile rootDir) {
List<String> candidates = new ArrayList<String>();
if (rootDir != null) {
rootDir.refresh(false, false);
rootDir.refresh(true, false);
VirtualFile[] suspects = rootDir.getChildren();
for (VirtualFile child : suspects) {
if (child.isDirectory()) {
+2
View File
@@ -32,6 +32,7 @@ public class PythonIcons {
}
public static final Icon Dotnet = load("/icons/com/jetbrains/python/dotnet.png"); // 16x16
public static final Icon InterpreterGear = load("/icons/com/jetbrains/python/interpreterGear.png"); // 16x16
public static final Icon Jython = load("/icons/com/jetbrains/python/jython.png"); // 16x16
public static class Nodes {
@@ -48,6 +49,7 @@ public class PythonIcons {
public static final Icon Python_24 = load("/icons/com/jetbrains/python/python_24.png"); // 24x24
public static final Icon PythonClosed = load("/icons/com/jetbrains/python/pythonClosed.png"); // 16x16
public static final Icon PythonTests = load("/icons/com/jetbrains/python/pythonTests.png"); // 16x16
public static final Icon Skeleton = load("/icons/com/jetbrains/python/skeleton.png"); // 16x16
public static final Icon Virtualenv = load("/icons/com/jetbrains/python/virtualenv.png"); // 16x16
}