mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
extract community part out of python-ide module
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="platform-impl" />
|
||||
<orderEntry type="module" module-name="python-community" />
|
||||
<orderEntry type="module" module-name="lang-api" />
|
||||
<orderEntry type="module" module-name="lang-impl" />
|
||||
<orderEntry type="library" name="Guava" level="project" />
|
||||
<orderEntry type="module" module-name="xml-psi-impl" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.ide.util.projectWizard.EmptyModuleBuilder;
|
||||
import com.intellij.openapi.module.ModuleType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PlatformPythonModuleType extends PythonModuleTypeBase<EmptyModuleBuilder> {
|
||||
@NotNull
|
||||
@Override
|
||||
public EmptyModuleBuilder createModuleBuilder() {
|
||||
return new EmptyModuleBuilder() {
|
||||
@Override
|
||||
public ModuleType getModuleType() {
|
||||
return getInstance();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.application.options.InitialConfigurationDialog;
|
||||
import com.intellij.codeInsight.CodeInsightSettings;
|
||||
import com.intellij.ide.AppLifecycleListener;
|
||||
import com.intellij.ide.OpenJdkNotifier;
|
||||
import com.intellij.ide.ui.UISettings;
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.wm.WindowManager;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
||||
import com.intellij.util.messages.MessageBus;
|
||||
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
@SuppressWarnings({"UtilityClassWithoutPrivateConstructor", "UtilityClassWithPublicConstructor"})
|
||||
public class PyCharmInitialConfigurator {
|
||||
@NonNls private static final String DISPLAYED_PROPERTY = "PyCharm.initialConfigurationShown";
|
||||
|
||||
public PyCharmInitialConfigurator(MessageBus bus, final PropertiesComponent propertiesComponent, final FileTypeManager fileTypeManager) {
|
||||
OpenJdkNotifier.checkJdk(bus);
|
||||
if (!propertiesComponent.getBoolean("PyCharm.InitialConfiguration", false)) {
|
||||
propertiesComponent.setValue("PyCharm.InitialConfiguration", "true");
|
||||
EditorSettingsExternalizable.getInstance().setVirtualSpace(false);
|
||||
}
|
||||
if (!propertiesComponent.getBoolean("PyCharm.InitialConfiguration.V2", false)) {
|
||||
propertiesComponent.setValue("PyCharm.InitialConfiguration.V2", "true");
|
||||
final CodeStyleSettings settings = CodeStyleSettingsManager.getInstance().getCurrentSettings();
|
||||
settings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true;
|
||||
settings.getCommonSettings(PythonLanguage.getInstance()).ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true;
|
||||
UISettings.getInstance().SHOW_DIRECTORY_FOR_NON_UNIQUE_FILENAMES = true;
|
||||
}
|
||||
if (!propertiesComponent.getBoolean("PyCharm.InitialConfiguration.V3", false)) {
|
||||
propertiesComponent.setValue("PyCharm.InitialConfiguration.V3", "true");
|
||||
UISettings.getInstance().SHOW_MEMORY_INDICATOR = false;
|
||||
final String ignoredFilesList = fileTypeManager.getIgnoredFilesList();
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FileTypeManager.getInstance().setIgnoredFilesList(ignoredFilesList + ";*$py.class");
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!propertiesComponent.getBoolean("PyCharm.InitialConfiguration.V4", false)) {
|
||||
propertiesComponent.setValue("PyCharm.InitialConfiguration.V4", "true");
|
||||
PyCodeInsightSettings.getInstance().SHOW_IMPORT_POPUP = false;
|
||||
}
|
||||
if (!propertiesComponent.getBoolean("PyCharm.InitialConfiguration.V5", false)) {
|
||||
propertiesComponent.setValue("PyCharm.InitialConfiguration.V5", "true");
|
||||
CodeInsightSettings.getInstance().REFORMAT_ON_PASTE = CodeInsightSettings.NO_REFORMAT;
|
||||
}
|
||||
if (!propertiesComponent.isValueSet(DISPLAYED_PROPERTY)) {
|
||||
bus.connect().subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() {
|
||||
@Override
|
||||
public void welcomeScreenDisplayed() {
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
propertiesComponent.setValue(DISPLAYED_PROPERTY, "true");
|
||||
showInitialConfigurationDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void showInitialConfigurationDialog() {
|
||||
final JFrame frame = WindowManager.getInstance().findVisibleFrame();
|
||||
new InitialConfigurationDialog(frame, "Python").show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.PyIdeCommonOptionsForm">
|
||||
<grid id="464af" binding="myHideablePanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="522" height="400"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="10" 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 border-constraint="Center"/>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<vspacer id="a7e1d">
|
||||
<constraints>
|
||||
<grid row="9" 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="62f63" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="&Interpreter options:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="47414" class="com.intellij.ui.RawCommandLineEditor" binding="myInterpreterOptionsTextField">
|
||||
<constraints>
|
||||
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<dialogCaption value="Enter interpreter options"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="32056" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="&Working directory:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="a0dd" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myWorkingDirectoryTextField">
|
||||
<constraints>
|
||||
<grid row="4" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="58eee" class="com.intellij.ui.components.JBLabel" binding="myPythonInterpreterJBLabel">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="39bcf"/>
|
||||
<text value="&Python interpreter:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="39bcf" class="javax.swing.JComboBox" binding="myInterpreterComboBox" custom-create="true">
|
||||
<constraints>
|
||||
<grid row="2" 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>
|
||||
<component id="a0aae" class="com.intellij.execution.configuration.EnvironmentVariablesComponent" binding="myEnvsComponent">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelLocation value="West"/>
|
||||
<text value="&Environment variables"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="fec2b" class="javax.swing.JLabel" binding="myProjectLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Pro&ject:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="195cd" class="javax.swing.JComboBox" binding="myModuleCombo">
|
||||
<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="ded2d" binding="myConfigureInterpretersPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<grid row="6" 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>
|
||||
<component id="c633e" class="com.intellij.execution.util.PathMappingsComponent" binding="myPathMappingsComponent">
|
||||
<constraints>
|
||||
<grid row="5" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelLocation value="West"/>
|
||||
<text value="Path mappings"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="7ebfd" class="com.intellij.ui.components.JBCheckBox" binding="myAddContentRootsCheckbox">
|
||||
<constraints>
|
||||
<grid row="7" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<selected value="true"/>
|
||||
<text value="Add content roots to PYTHONPATH"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="507b" class="com.intellij.ui.components.JBCheckBox" binding="myAddSourceRootsCheckbox">
|
||||
<constraints>
|
||||
<grid row="8" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<selected value="true"/>
|
||||
<text value="Add source roots to PYTHONPATH"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
@@ -0,0 +1,352 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.execution.configuration.EnvironmentVariablesComponent;
|
||||
import com.intellij.execution.util.PathMappingsComponent;
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.projectRoots.SdkModel;
|
||||
import com.intellij.openapi.projectRoots.impl.SdkListCellRenderer;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.ui.CollectionComboBoxModel;
|
||||
import com.intellij.ui.HideableDecorator;
|
||||
import com.intellij.ui.RawCommandLineEditor;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.PathMappingSettings;
|
||||
import com.jetbrains.python.configuration.PyConfigurableInterpreterList;
|
||||
import com.jetbrains.python.configuration.PyConfigureInterpretersLinkPanel;
|
||||
import com.jetbrains.python.run.AbstractPyCommonOptionsForm;
|
||||
import com.intellij.application.options.ModuleListCellRenderer;
|
||||
import com.jetbrains.python.run.PyCommonOptionsFormData;
|
||||
import com.jetbrains.python.sdk.PySdkUtil;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyIdeCommonOptionsForm implements AbstractPyCommonOptionsForm {
|
||||
private JPanel myMainPanel;
|
||||
private EnvironmentVariablesComponent myEnvsComponent;
|
||||
private RawCommandLineEditor myInterpreterOptionsTextField;
|
||||
private TextFieldWithBrowseButton myWorkingDirectoryTextField;
|
||||
private JComboBox myInterpreterComboBox;
|
||||
private JBLabel myPythonInterpreterJBLabel;
|
||||
private JLabel myProjectLabel;
|
||||
private JComboBox myModuleCombo;
|
||||
private JPanel myConfigureInterpretersPanel;
|
||||
private String mySelectedSdkHome = null;
|
||||
private PathMappingsComponent myPathMappingsComponent;
|
||||
private JPanel myHideablePanel;
|
||||
private JBCheckBox myAddContentRootsCheckbox;
|
||||
private JBCheckBox myAddSourceRootsCheckbox;
|
||||
|
||||
private JComponent labelAnchor;
|
||||
private final Project myProject;
|
||||
private List<Sdk> myPythonSdks;
|
||||
private boolean myInterpreterRemote;
|
||||
private final HideableDecorator myDecorator;
|
||||
|
||||
public PyIdeCommonOptionsForm(PyCommonOptionsFormData data) {
|
||||
myProject = data.getProject();
|
||||
myWorkingDirectoryTextField.addBrowseFolderListener("Select Working Directory", "", data.getProject(),
|
||||
FileChooserDescriptorFactory.createSingleFolderDescriptor());
|
||||
|
||||
myPythonSdks = new ArrayList<Sdk>(PythonSdkType.getAllSdks());
|
||||
myPythonSdks.add(0, null);
|
||||
|
||||
myInterpreterComboBox.setModel(new CollectionComboBoxModel(myPythonSdks, null));
|
||||
|
||||
final Module[] modules = ModuleManager.getInstance(data.getProject()).getModules();
|
||||
if (modules.length == 1) {
|
||||
setModule(modules[0]);
|
||||
myProjectLabel.setVisible(false);
|
||||
myModuleCombo.setVisible(false);
|
||||
}
|
||||
else {
|
||||
final List<Module> validModules = data.getValidModules();
|
||||
Module selection = validModules.size() > 0 ? validModules.get(0) : null;
|
||||
myModuleCombo.setModel(new CollectionComboBoxModel(validModules, selection));
|
||||
myModuleCombo.setRenderer(new ModuleListCellRenderer());
|
||||
myModuleCombo.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
updateDefaultInterpreter((Module)myModuleCombo.getSelectedItem());
|
||||
}
|
||||
});
|
||||
updateDefaultInterpreter((Module)myModuleCombo.getSelectedItem());
|
||||
}
|
||||
|
||||
setAnchor(myEnvsComponent.getLabel());
|
||||
myPathMappingsComponent.setAnchor(myEnvsComponent.getLabel());
|
||||
|
||||
if (data.showConfigureInterpretersLink()) {
|
||||
myConfigureInterpretersPanel.add(new PyConfigureInterpretersLinkPanel(myMainPanel));
|
||||
}
|
||||
|
||||
addInterpreterComboBoxActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
updateRemoteInterpreterMode();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
updateRemoteInterpreterMode();
|
||||
|
||||
myDecorator = new HideableDecorator(myHideablePanel, "Environment", false) {
|
||||
@Override
|
||||
protected void on() {
|
||||
super.on();
|
||||
storeState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void off() {
|
||||
super.off();
|
||||
storeState();
|
||||
}
|
||||
private void storeState() {
|
||||
PropertiesComponent.getInstance().setValue(EXPAND_PROPERTY_KEY, String.valueOf(isExpanded()));
|
||||
}
|
||||
};
|
||||
myDecorator.setOn(PropertiesComponent.getInstance().getBoolean(EXPAND_PROPERTY_KEY, true));
|
||||
myDecorator.setContentComponent(myMainPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getMainPanel() {
|
||||
return myHideablePanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe() {
|
||||
PyConfigurableInterpreterList myInterpreterList = PyConfigurableInterpreterList.getInstance(myProject);
|
||||
ProjectSdksModel myProjectSdksModel = myInterpreterList.getModel();
|
||||
myProjectSdksModel.addListener(new MyListener(this, myInterpreterList));
|
||||
updateSdkList(true, myInterpreterList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getAnchor() {
|
||||
return labelAnchor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAnchor(JComponent anchor) {
|
||||
labelAnchor = anchor;
|
||||
myEnvsComponent.setAnchor(anchor);
|
||||
myPythonInterpreterJBLabel.setAnchor(anchor);
|
||||
}
|
||||
|
||||
public String getInterpreterOptions() {
|
||||
return myInterpreterOptionsTextField.getText().trim();
|
||||
}
|
||||
|
||||
public void setInterpreterOptions(String interpreterOptions) {
|
||||
myInterpreterOptionsTextField.setText(interpreterOptions);
|
||||
}
|
||||
|
||||
public String getWorkingDirectory() {
|
||||
return FileUtil.toSystemIndependentName(myWorkingDirectoryTextField.getText().trim());
|
||||
}
|
||||
|
||||
public void setWorkingDirectory(String workingDirectory) {
|
||||
myWorkingDirectoryTextField.setText(workingDirectory == null ? "" : FileUtil.toSystemDependentName(workingDirectory));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSdkHome() {
|
||||
final Sdk selectedSdk = (Sdk)myInterpreterComboBox.getSelectedItem();
|
||||
return selectedSdk == null ? null : selectedSdk.getHomePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSdkHome(String sdkHome) {
|
||||
mySelectedSdkHome = sdkHome;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Module getModule() {
|
||||
final Module selectedItem = (Module)myModuleCombo.getSelectedItem();
|
||||
if (selectedItem != null) {
|
||||
return selectedItem;
|
||||
}
|
||||
final Module[] modules = ModuleManager.getInstance(myProject).getModules();
|
||||
if (modules.length == 1) {
|
||||
return modules[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModule(Module module) {
|
||||
myModuleCombo.setSelectedItem(module);
|
||||
updateDefaultInterpreter(module);
|
||||
}
|
||||
|
||||
private void updateDefaultInterpreter(Module module) {
|
||||
final Sdk sdk = module == null ? null : ModuleRootManager.getInstance(module).getSdk();
|
||||
String projectSdkName = sdk == null ? "none" : sdk.getName();
|
||||
myInterpreterComboBox.setRenderer(new SdkListCellRenderer("Project Default (" + projectSdkName + ")"));
|
||||
}
|
||||
|
||||
public void updateSdkList(boolean preserveSelection, PyConfigurableInterpreterList myInterpreterList) {
|
||||
myPythonSdks = myInterpreterList.getAllPythonSdks();
|
||||
Sdk selection = preserveSelection ? (Sdk)myInterpreterComboBox.getSelectedItem() : null;
|
||||
if (!myPythonSdks.contains(selection)) {
|
||||
selection = null;
|
||||
}
|
||||
myPythonSdks.add(0, null);
|
||||
myInterpreterComboBox.setModel(new CollectionComboBoxModel(myPythonSdks, selection));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseModuleSdk() {
|
||||
return myInterpreterComboBox.getSelectedItem() == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseModuleSdk(boolean useModuleSdk) {
|
||||
myInterpreterComboBox.setSelectedItem(useModuleSdk ? null : PythonSdkType.findSdkByPath(myPythonSdks, mySelectedSdkHome));
|
||||
}
|
||||
|
||||
public boolean isPassParentEnvs() {
|
||||
return myEnvsComponent.isPassParentEnvs();
|
||||
}
|
||||
|
||||
public void setPassParentEnvs(boolean passParentEnvs) {
|
||||
myEnvsComponent.setPassParentEnvs(passParentEnvs);
|
||||
}
|
||||
|
||||
public Map<String, String> getEnvs() {
|
||||
return myEnvsComponent.getEnvs();
|
||||
}
|
||||
|
||||
public void setEnvs(Map<String, String> envs) {
|
||||
myEnvsComponent.setEnvs(envs);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PathMappingSettings getMappingSettings() {
|
||||
if (myInterpreterRemote) {
|
||||
return myPathMappingsComponent.getMappingSettings();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMappingSettings(@Nullable PathMappingSettings mappingSettings) {
|
||||
myPathMappingsComponent.setMappingSettings(mappingSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addContentRoots() {
|
||||
return myAddContentRootsCheckbox.isSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addSourceRoots() {
|
||||
return myAddSourceRootsCheckbox.isSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addContentRoots(boolean add) {
|
||||
myAddContentRootsCheckbox.setSelected(add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSourceRoots(boolean add) {
|
||||
myAddSourceRootsCheckbox.setSelected(add);
|
||||
}
|
||||
|
||||
private void createUIComponents() {
|
||||
myInterpreterComboBox = new ComboBox(100);
|
||||
}
|
||||
|
||||
private void setRemoteInterpreterMode(boolean isInterpreterRemote) {
|
||||
myInterpreterRemote = isInterpreterRemote;
|
||||
myPathMappingsComponent.setVisible(isInterpreterRemote);
|
||||
}
|
||||
|
||||
private void updateRemoteInterpreterMode() {
|
||||
setRemoteInterpreterMode(PySdkUtil.isRemote(getSdkSelected()));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Sdk getSdkSelected() {
|
||||
String sdkHome = getSdkHome();
|
||||
if (StringUtil.isEmptyOrSpaces(sdkHome)) {
|
||||
final Sdk projectJdk = PythonSdkType.findPythonSdk(getModule());
|
||||
if (projectJdk != null) {
|
||||
sdkHome = projectJdk.getHomePath();
|
||||
}
|
||||
}
|
||||
|
||||
return PythonSdkType.findSdkByPath(sdkHome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterpreterComboBoxActionListener(ActionListener listener) {
|
||||
myInterpreterComboBox.addActionListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeInterpreterComboBoxActionListener(ActionListener listener) {
|
||||
myInterpreterComboBox.removeActionListener(listener);
|
||||
}
|
||||
|
||||
private static class MyListener implements SdkModel.Listener {
|
||||
private final PyIdeCommonOptionsForm myForm;
|
||||
private PyConfigurableInterpreterList myInterpreterList;
|
||||
|
||||
public MyListener(PyIdeCommonOptionsForm form, PyConfigurableInterpreterList interpreterList) {
|
||||
myForm = form;
|
||||
myInterpreterList = interpreterList;
|
||||
}
|
||||
|
||||
|
||||
private void update() {
|
||||
myForm.updateSdkList(true, myInterpreterList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sdkAdded(Sdk sdk) {
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeSdkRemove(Sdk sdk) {
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sdkChanged(Sdk sdk, String previousName) {
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sdkHomeSelected(Sdk sdk, String newSdkHome) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.jetbrains.python.run.AbstractPyCommonOptionsForm;
|
||||
import com.jetbrains.python.run.PyCommonOptionsFormData;
|
||||
import com.jetbrains.python.run.PyCommonOptionsFormFactory;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyIdeCommonOptionsFormFactory extends PyCommonOptionsFormFactory {
|
||||
@Override
|
||||
public PyIdeCommonOptionsForm createForm(PyCommonOptionsFormData data) {
|
||||
return new PyIdeCommonOptionsForm(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.ProjectTopics;
|
||||
import com.intellij.execution.RunManagerEx;
|
||||
import com.intellij.execution.RunnerAndConfigurationSettings;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.ModuleAdapter;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.messages.MessageBus;
|
||||
import com.jetbrains.python.run.AbstractPythonRunConfiguration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyModuleListener {
|
||||
public PyModuleListener(MessageBus messageBus) {
|
||||
messageBus.connect().subscribe(ProjectTopics.MODULES, new ModuleAdapter() {
|
||||
@Override
|
||||
public void beforeModuleRemoved(Project project, Module module) {
|
||||
final RunManagerEx runManager = RunManagerEx.getInstanceEx(project);
|
||||
final Collection<RunnerAndConfigurationSettings> configurations = new ArrayList<RunnerAndConfigurationSettings>(runManager.getSortedConfigurations());
|
||||
for (RunnerAndConfigurationSettings configuration : configurations) {
|
||||
if (configuration.getConfiguration() instanceof AbstractPythonRunConfiguration) {
|
||||
final Module configModule = ((AbstractPythonRunConfiguration)configuration.getConfiguration()).getModule();
|
||||
if (configModule == module) {
|
||||
runManager.removeConfiguration(configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.application.options.OptionId;
|
||||
import com.intellij.application.options.OptionsApplicabilityFilter;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyOptionsApplicabilityFilter extends OptionsApplicabilityFilter {
|
||||
@Override
|
||||
public boolean isOptionApplicable(OptionId optionId) {
|
||||
return optionId == OptionId.RENAME_IN_PLACE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.ide.highlighter.XmlFileType;
|
||||
import com.intellij.openapi.fileTypes.FileTypeConsumer;
|
||||
import com.intellij.openapi.fileTypes.FileTypeFactory;
|
||||
import com.jetbrains.pyqt.QtUIFileType;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* IDE version of file type factory - does not use UltimateVerifier.
|
||||
*
|
||||
* @author yole
|
||||
*/
|
||||
public class PythonFileTypeFactory extends FileTypeFactory {
|
||||
public void createFileTypes(@NonNls @NotNull final FileTypeConsumer consumer) {
|
||||
consumer.consume(PythonFileType.INSTANCE, "py;pyw;");
|
||||
consumer.consume(QtUIFileType.INSTANCE, "ui");
|
||||
consumer.consume(XmlFileType.INSTANCE, "qrc");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.openapi.module.ModuleType;
|
||||
import com.intellij.openapi.module.impl.ModuleTypeManagerImpl;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PythonModuleTypeManager extends ModuleTypeManagerImpl {
|
||||
@Override
|
||||
public ModuleType getDefaultModuleType() {
|
||||
return new PlatformPythonModuleType();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
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.ui.CollectionComboBoxModel;
|
||||
import com.intellij.ui.ComboboxWithBrowseButton;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.configuration.PythonSdkConfigurable;
|
||||
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PythonSdkChooserCombo extends ComboboxWithBrowseButton {
|
||||
private final List<ActionListener> myChangedListeners = ContainerUtil.createLockFreeCopyOnWriteList();
|
||||
|
||||
//public PythonSdkChooserCombo(final Condition<Sdk> acceptableSdkCondition) {
|
||||
// this(PythonSdkType.getAllSdks(), acceptableSdkCondition);
|
||||
//}
|
||||
|
||||
public PythonSdkChooserCombo(final Project project, List<Sdk> sdks, final Condition<Sdk> acceptableSdkCondition) {
|
||||
Sdk initialSelection = null;
|
||||
for (Sdk sdk : sdks) {
|
||||
if (acceptableSdkCondition.value(sdk)) {
|
||||
initialSelection = sdk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
getComboBox().setModel(new CollectionComboBoxModel(sdks, initialSelection));
|
||||
getComboBox().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();
|
||||
}
|
||||
});
|
||||
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));
|
||||
notifyChanged(e);
|
||||
}
|
||||
});
|
||||
getComboBox().addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
notifyChanged(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void notifyChanged(ActionEvent e) {
|
||||
for (ActionListener changedListener : myChangedListeners) {
|
||||
changedListener.actionPerformed(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addChangedListener(ActionListener listener) {
|
||||
myChangedListeners.add(listener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
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.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.platform.DirectoryProjectConfigurator;
|
||||
import com.intellij.util.SystemProperties;
|
||||
import com.jetbrains.python.sdk.PreferredSdkComparator;
|
||||
import com.jetbrains.python.sdk.PythonSdkAdditionalData;
|
||||
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import com.jetbrains.python.sdk.flavors.VirtualEnvSdkFlavor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dcheryasov
|
||||
*/
|
||||
public class PythonSdkConfigurator implements DirectoryProjectConfigurator {
|
||||
|
||||
public void configureProject(final Project project, @NotNull final VirtualFile baseDir, Ref<Module> moduleRef) {
|
||||
// it it a virtualenv?
|
||||
final PythonSdkType py_sdk_type = PythonSdkType.getInstance();
|
||||
//find virtualEnv in project directory
|
||||
final List<String> candidates = new ArrayList<String>();
|
||||
if (project != null) {
|
||||
final VirtualFile rootDir = project.getBaseDir();
|
||||
if (rootDir != null)
|
||||
candidates.addAll(VirtualEnvSdkFlavor.findInDirectory(rootDir));
|
||||
if (!candidates.isEmpty()) {
|
||||
String filePath = candidates.get(0);
|
||||
if (StringUtil.startsWithChar(filePath, '~')) {
|
||||
final String home = SystemProperties.getUserHome();
|
||||
filePath = home + filePath.substring(1);
|
||||
}
|
||||
final Sdk virtualEnvSdk = SdkConfigurationUtil.createAndAddSDK(filePath, py_sdk_type);
|
||||
if (virtualEnvSdk != null) {
|
||||
SdkConfigurationUtil.setDirectoryProjectSdk(project, virtualEnvSdk);
|
||||
SdkAdditionalData additionalData = virtualEnvSdk.getSdkAdditionalData();
|
||||
if (additionalData == null) {
|
||||
additionalData = new PythonSdkAdditionalData(PythonSdkFlavor.getFlavor(virtualEnvSdk.getHomePath()));
|
||||
((ProjectJdkImpl)virtualEnvSdk).setSdkAdditionalData(additionalData);
|
||||
}
|
||||
((PythonSdkAdditionalData) additionalData).associateWithProject(project);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final Sdk existing_sdk = ProjectRootManager.getInstance(project).getProjectSdk();
|
||||
if (existing_sdk != null && existing_sdk.getSdkType() == py_sdk_type) return; // SdkConfigurationUtil does the same
|
||||
final File py_exe = PythonSdkType.findExecutableFile(new File(project.getBasePath(), "bin"), "python");
|
||||
if (py_exe != null) {
|
||||
final File env_root = PythonSdkType.getVirtualEnvRoot(py_exe.getPath());
|
||||
if (env_root != null) {
|
||||
// yes, an unknown virtualenv; set it up as SDK
|
||||
final Sdk an_sdk = SdkConfigurationUtil.createAndAddSDK(py_exe.getPath(), py_sdk_type);
|
||||
if (an_sdk != null) {
|
||||
SdkConfigurationUtil.setDirectoryProjectSdk(project, an_sdk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// default
|
||||
SdkConfigurationUtil.configureDirectoryProjectSdk(project, new PreferredSdkComparator(), py_sdk_type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ContentEntry;
|
||||
import com.intellij.openapi.roots.ModifiableRootModel;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.SourceFolder;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.platform.DirectoryProjectConfigurator;
|
||||
import com.intellij.psi.PsiFileFactory;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PythonSourceRootConfigurator implements DirectoryProjectConfigurator {
|
||||
@NonNls private static final String SETUP_PY = "setup.py";
|
||||
|
||||
@Override
|
||||
public void configureProject(Project project, @NotNull final VirtualFile baseDir, Ref<Module> moduleRef) {
|
||||
VirtualFile setupPy = baseDir.findChild(SETUP_PY);
|
||||
if (setupPy != null) {
|
||||
final CharSequence content = LoadTextUtil.loadText(setupPy);
|
||||
PyFile setupPyFile = (PyFile) PsiFileFactory.getInstance(project).createFileFromText(SETUP_PY, content.toString());
|
||||
final SetupCallVisitor visitor = new SetupCallVisitor();
|
||||
setupPyFile.accept(visitor);
|
||||
String dir = visitor.getRootPackageDir();
|
||||
if (dir != null) {
|
||||
final VirtualFile rootPackageVFile = baseDir.findFileByRelativePath(dir);
|
||||
addSourceRoot(project, baseDir, rootPackageVFile, true);
|
||||
}
|
||||
}
|
||||
|
||||
VirtualFile web2Py = baseDir.findChild("web2py");
|
||||
if (web2Py != null) {
|
||||
addSourceRoot(project, baseDir, web2Py, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addSourceRoot(Project project, final VirtualFile baseDir, final VirtualFile root, final boolean unique) {
|
||||
final Module[] modules = ModuleManager.getInstance(project).getModules();
|
||||
if (modules.length > 0 && root != null) {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
public void run() {
|
||||
final ModifiableRootModel model = ModuleRootManager.getInstance(modules[0]).getModifiableModel();
|
||||
final ContentEntry[] contentEntries = model.getContentEntries();
|
||||
for (ContentEntry contentEntry : contentEntries) {
|
||||
if (Comparing.equal(contentEntry.getFile(), baseDir)) {
|
||||
final SourceFolder[] sourceFolders = contentEntry.getSourceFolders();
|
||||
if (!unique || sourceFolders.length == 0) {
|
||||
contentEntry.addSourceFolder(root, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
model.commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static class SetupCallVisitor extends PyRecursiveElementVisitor {
|
||||
private String myRootPackageDir = null;
|
||||
|
||||
@Override
|
||||
public void visitPyCallExpression(PyCallExpression node) {
|
||||
final PyArgumentList argList = node.getArgumentList();
|
||||
if (node.isCalleeText("setup") && argList != null) {
|
||||
final PyKeywordArgument packageDirArg = argList.getKeywordArgument("package_dir");
|
||||
if (packageDirArg != null) {
|
||||
final PyExpression valueExpression = packageDirArg.getValueExpression();
|
||||
if (valueExpression instanceof PyDictLiteralExpression) {
|
||||
PyDictLiteralExpression packageDirDict = (PyDictLiteralExpression)valueExpression;
|
||||
for (PyKeyValueExpression keyValue : packageDirDict.getElements()) {
|
||||
final PyExpression keyExpr = keyValue.getKey();
|
||||
final PyExpression valueExpr = keyValue.getValue();
|
||||
if (keyExpr instanceof PyStringLiteralExpression && valueExpr instanceof PyStringLiteralExpression) {
|
||||
String key = ((PyStringLiteralExpression) keyExpr).getStringValue();
|
||||
String value = ((PyStringLiteralExpression)valueExpr).getStringValue();
|
||||
if (key.length() == 0) {
|
||||
myRootPackageDir = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
super.visitPyCallExpression(node);
|
||||
}
|
||||
}
|
||||
|
||||
public String getRootPackageDir() {
|
||||
return myRootPackageDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.buildout.BuildoutConfigurable">
|
||||
<grid id="27dc6" binding="myMainPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="541" height="400"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<grid id="9337d" layout-manager="GridLayoutManager" row-count="1" 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 border-constraint="North"/>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="287e6" class="javax.swing.JCheckBox" binding="myEnabledCheckbox">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<enabled value="false"/>
|
||||
<text value="&Enable buildout support"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
<grid id="88f56" binding="myPlaceholder" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints border-constraint="Center"/>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children/>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
@@ -0,0 +1,171 @@
|
||||
package com.jetbrains.python.buildout;
|
||||
|
||||
import com.intellij.facet.FacetManager;
|
||||
import com.intellij.facet.ModifiableFacetModel;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.options.NonDefaultProjectConfigurable;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A silly configurable to add buildout facet configurator to PyCharm
|
||||
* User: dcheryasov
|
||||
* Date: Jul 28, 2010 4:34:58 PM
|
||||
*/
|
||||
public class BuildoutConfigurable implements Configurable, NonDefaultProjectConfigurable {
|
||||
|
||||
private JCheckBox myEnabledCheckbox;
|
||||
private JPanel myPlaceholder;
|
||||
private JPanel myMainPanel;
|
||||
private BuildoutConfigPanel mySettingsPanel;
|
||||
private final Module myModule;
|
||||
|
||||
public BuildoutConfigurable(@NotNull Module module) {
|
||||
myModule = module;
|
||||
BuildoutFacetConfiguration config = null;
|
||||
BuildoutFacet facet = BuildoutFacet.getInstance(myModule);
|
||||
if (facet != null) config = facet.getConfiguration();
|
||||
if (config == null) config = new BuildoutFacetConfiguration(null);
|
||||
mySettingsPanel = new BuildoutConfigPanel(myModule, config);
|
||||
myPlaceholder.add(mySettingsPanel, BorderLayout.CENTER);
|
||||
final boolean isEnabled = !StringUtil.isEmptyOrSpaces(config.getScriptName());
|
||||
myEnabledCheckbox.setSelected(isEnabled);
|
||||
updateFacetEnabled(isEnabled);
|
||||
myEnabledCheckbox.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final boolean enabled = myEnabledCheckbox.isSelected();
|
||||
updateFacetEnabled(enabled);
|
||||
}
|
||||
});
|
||||
mySettingsPanel.addFocusListener(
|
||||
new FocusListener() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent focusEvent) {
|
||||
switchNoticeText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusLost(FocusEvent focusEvent) { }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void updateFacetEnabled(boolean enabled) {
|
||||
mySettingsPanel.setFacetEnabled(enabled);
|
||||
UIUtil.setEnabled(mySettingsPanel, enabled, true);
|
||||
}
|
||||
|
||||
@Nls
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Buildout Support";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpTopic() {
|
||||
return "reference-python-buildout";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent createComponent() {
|
||||
switchNoticeText();
|
||||
return myMainPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
if (! myEnabledCheckbox.isEnabled()) return false;
|
||||
final BuildoutFacet facet = myModule != null? BuildoutFacet.getInstance(myModule) : null;
|
||||
final boolean got_facet = facet != null;
|
||||
if (myEnabledCheckbox.isSelected() != got_facet) return true;
|
||||
if (got_facet && myEnabledCheckbox.isSelected()) {
|
||||
return mySettingsPanel.isModified(facet.getConfiguration());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws ConfigurationException {
|
||||
final BuildoutFacet facet = myModule != null? BuildoutFacet.getInstance(myModule) : null;
|
||||
final boolean got_facet = facet != null;
|
||||
boolean facet_is_desired = myEnabledCheckbox.isSelected();
|
||||
|
||||
mySettingsPanel.apply();
|
||||
List<String> paths_from_script = null;
|
||||
if (facet_is_desired) {
|
||||
String script_name = mySettingsPanel.getScriptName();
|
||||
VirtualFile script_file = BuildoutConfigPanel.getScriptFile(script_name);
|
||||
paths_from_script = BuildoutFacet.extractBuildoutPaths(script_file);
|
||||
if (paths_from_script == null) {
|
||||
throw new ConfigurationException("Failed to extract paths from '" + script_file.getPresentableName() + "'");
|
||||
}
|
||||
mySettingsPanel.getConfiguration().setPaths(paths_from_script);
|
||||
|
||||
}
|
||||
if (facet_is_desired && ! got_facet) addFacet(mySettingsPanel.getConfiguration());
|
||||
if (! facet_is_desired && got_facet) removeFacet(facet);
|
||||
if (facet_is_desired) BuildoutFacet.attachLibrary(myModule);
|
||||
else BuildoutFacet.detachLibrary(myModule);
|
||||
}
|
||||
|
||||
private void addFacet(BuildoutFacetConfiguration config) {
|
||||
BuildoutFacetConfigurator.setupFacet(myModule, config);
|
||||
}
|
||||
|
||||
private void removeFacet(BuildoutFacet facet) {
|
||||
final ModifiableFacetModel model = FacetManager.getInstance(myModule).createModifiableModel();
|
||||
model.removeFacet(facet);
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
public void run() {
|
||||
model.commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
// TODO: refactor, see AppEngineConfigurable
|
||||
if (myModule == null) {
|
||||
myEnabledCheckbox.setSelected(false);
|
||||
myEnabledCheckbox.setEnabled(false);
|
||||
}
|
||||
else {
|
||||
myEnabledCheckbox.setEnabled(true);
|
||||
switchNoticeText();
|
||||
final BuildoutFacet instance = BuildoutFacet.getInstance(myModule);
|
||||
if (instance != null) {
|
||||
boolean selected = ! StringUtil.isEmptyOrSpaces(instance.getConfiguration().getScriptName());
|
||||
myEnabledCheckbox.setSelected(selected);
|
||||
mySettingsPanel.setEnabled(selected);
|
||||
mySettingsPanel.reset();
|
||||
}
|
||||
else {
|
||||
myEnabledCheckbox.setSelected(false);
|
||||
mySettingsPanel.setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void switchNoticeText() {
|
||||
mySettingsPanel.showNoticeText(/*DjangoFacet.getInstance(myModule) != null*/ false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeUIResources() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.jetbrains.python.buildout;
|
||||
|
||||
import com.intellij.facet.FacetManager;
|
||||
import com.intellij.facet.ModifiableFacetModel;
|
||||
import com.intellij.facet.ProjectFacetManager;
|
||||
import com.intellij.openapi.application.Result;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.platform.DirectoryProjectConfigurator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Detects and configures a buildout facet.
|
||||
* User: dcheryasov
|
||||
* Date: Jul 26, 2010 6:10:39 PM
|
||||
*/
|
||||
public class BuildoutFacetConfigurator implements DirectoryProjectConfigurator {
|
||||
@Override
|
||||
public void configureProject(Project project, @NotNull VirtualFile baseDir, Ref<Module> moduleRef) {
|
||||
final Module[] modules = ModuleManager.getInstance(project).getModules();
|
||||
if (modules.length > 0) {
|
||||
final Module module = modules[0];
|
||||
if (BuildoutFacet.getInstance(module) == null) {
|
||||
baseDir.refresh(false, false);
|
||||
final VirtualFile runner = BuildoutFacet.getRunner(baseDir);
|
||||
if (runner != null) {
|
||||
// TODO parse buildout.cfg and find out the part to use for the default script
|
||||
final File script = BuildoutFacet.findScript(null, "buildout", project.getBaseDir());
|
||||
if (script != null) {
|
||||
final ProjectFacetManager facetManager = ProjectFacetManager.getInstance(module.getProject());
|
||||
final BuildoutFacetConfiguration config = facetManager.createDefaultConfiguration(BuildoutFacetType.getInstance());
|
||||
config.setScriptName(script.getPath());
|
||||
setupFacet(module, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void setupFacet(Module module, @NotNull BuildoutFacetConfiguration config) {
|
||||
//TODO: refactor, see other python facets
|
||||
FacetManager facetManager = FacetManager.getInstance(module);
|
||||
final ModifiableFacetModel model = facetManager.createModifiableModel();
|
||||
BuildoutFacetType facetType = BuildoutFacetType.getInstance();
|
||||
BuildoutFacet facet = facetManager.createFacet(facetType, facetType.getDefaultFacetName(), config, null);
|
||||
model.addFacet(facet);
|
||||
|
||||
new WriteAction() {
|
||||
protected void run(final Result result) throws Throwable {
|
||||
model.commit();
|
||||
}
|
||||
}.execute();
|
||||
facet.updatePaths();
|
||||
BuildoutFacet.attachLibrary(module);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.jetbrains.python.buildout;
|
||||
|
||||
import com.intellij.application.options.ModuleAwareProjectConfigurable;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import com.intellij.openapi.options.NonDefaultProjectConfigurable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class BuildoutModulesConfigurable extends ModuleAwareProjectConfigurable implements NonDefaultProjectConfigurable {
|
||||
public BuildoutModulesConfigurable(Project project) {
|
||||
super(project, "Buildout Support", "reference-python-buildout");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Configurable createModuleConfigurable(Module module) {
|
||||
return new BuildoutConfigurable(module);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?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">
|
||||
<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="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 &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"/>
|
||||
</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"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Make available to &all projects"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.jetbrains.python.configuration;
|
||||
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class AddVEnvOptionsDialog extends DialogWrapper {
|
||||
private JBCheckBox myUseForThisProjectJBCheckBox;
|
||||
private JBCheckBox myMakeAvailableToAllJBCheckBox;
|
||||
private JPanel myMainPanel;
|
||||
|
||||
public AddVEnvOptionsDialog(Component parent) {
|
||||
super(parent, false);
|
||||
init();
|
||||
setTitle("Add Virtualenv");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
return myMainPanel;
|
||||
}
|
||||
|
||||
public boolean useForThisProject() {
|
||||
return myUseForThisProjectJBCheckBox.isSelected();
|
||||
}
|
||||
|
||||
public boolean makeAvailableToAll() {
|
||||
return myMakeAvailableToAllJBCheckBox.isSelected();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.configuration.EditSdkDialog">
|
||||
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="5" 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="600" height="400"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="a965b" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="bc0d9"/>
|
||||
<text value="&Name:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<vspacer id="41b5e">
|
||||
<constraints>
|
||||
<grid row="4" 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="bc0d9" class="javax.swing.JTextField" binding="myNameTextField">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="300" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties>
|
||||
<columns value="100"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="f30dd" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="&Interpreter path:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="f2225" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myInterpreterPathTextField">
|
||||
<constraints>
|
||||
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="6478c" class="com.intellij.ui.components.JBCheckBox" binding="myAssociateCheckbox">
|
||||
<constraints>
|
||||
<grid row="2" 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="&Associate this virtual environment with current project"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="8ba78" class="com.intellij.ui.components.JBLabel" binding="myRemoveAssociationLabel">
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="<html><a href="#">Remove association</a>"/>
|
||||
<visible value="true"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.jetbrains.python.configuration;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.SdkModificator;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.ui.ClickListener;
|
||||
import com.intellij.ui.DocumentAdapter;
|
||||
import com.intellij.ui.components.JBCheckBox;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.NullableFunction;
|
||||
import com.jetbrains.python.sdk.PythonSdkAdditionalData;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class EditSdkDialog extends DialogWrapper {
|
||||
private JPanel myMainPanel;
|
||||
private JTextField myNameTextField;
|
||||
private TextFieldWithBrowseButton myInterpreterPathTextField;
|
||||
private JBCheckBox myAssociateCheckbox;
|
||||
private JBLabel myRemoveAssociationLabel;
|
||||
private final boolean myWasAssociated;
|
||||
private boolean myAssociationRemoved = false;
|
||||
|
||||
protected EditSdkDialog(Project project, SdkModificator sdk, final NullableFunction<String, String> nameValidator) {
|
||||
super(project, true);
|
||||
setTitle("Edit Python Interpreter");
|
||||
myNameTextField.setText(sdk.getName());
|
||||
myNameTextField.getDocument().addDocumentListener(new DocumentAdapter() {
|
||||
@Override
|
||||
protected void textChanged(DocumentEvent e) {
|
||||
String nameError = nameValidator.fun(getName());
|
||||
setErrorText(nameError);
|
||||
setOKActionEnabled(nameError == null);
|
||||
}
|
||||
});
|
||||
myInterpreterPathTextField.setText(sdk.getHomePath());
|
||||
myInterpreterPathTextField.addBrowseFolderListener("Specify Interpreter Path", null, project,
|
||||
PythonSdkType.getInstance().getHomeChooserDescriptor());
|
||||
myRemoveAssociationLabel.setVisible(false);
|
||||
if (PythonSdkType.getVirtualEnvRoot(sdk.getHomePath()) == null) {
|
||||
myAssociateCheckbox.setVisible(false);
|
||||
}
|
||||
else {
|
||||
PythonSdkAdditionalData data = (PythonSdkAdditionalData) sdk.getSdkAdditionalData();
|
||||
if (data != null) {
|
||||
final String path = data.getAssociatedProjectPath();
|
||||
if (path != null) {
|
||||
myAssociateCheckbox.setSelected(true);
|
||||
final String basePath = project.getBasePath();
|
||||
if (basePath != null && !path.equals(FileUtil.toSystemIndependentName(basePath))) {
|
||||
myAssociateCheckbox.setEnabled(false);
|
||||
myAssociateCheckbox.setText("Associate this virtual environment with " + FileUtil.toSystemDependentName(path));
|
||||
myRemoveAssociationLabel.setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
myWasAssociated = myAssociateCheckbox.isSelected();
|
||||
init();
|
||||
new ClickListener() {
|
||||
@Override
|
||||
public boolean onClick(MouseEvent e, int clickCount) {
|
||||
myAssociateCheckbox.setSelected(false);
|
||||
myAssociateCheckbox.setEnabled(true);
|
||||
myAssociateCheckbox.setText("Associate this environment with current project");
|
||||
myRemoveAssociationLabel.setVisible(false);
|
||||
myAssociationRemoved = true;
|
||||
return true;
|
||||
}
|
||||
}.installOn(myRemoveAssociationLabel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createCenterPanel() {
|
||||
return myMainPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getPreferredFocusedComponent() {
|
||||
return myNameTextField;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return myNameTextField.getText();
|
||||
}
|
||||
|
||||
public String getHomePath() {
|
||||
return myInterpreterPathTextField.getText();
|
||||
}
|
||||
|
||||
public boolean associateWithProject() {
|
||||
return myAssociateCheckbox.isSelected();
|
||||
}
|
||||
|
||||
public boolean isAssociateChanged() {
|
||||
return myWasAssociated != associateWithProject() || myAssociationRemoved;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,203 @@
|
||||
package com.jetbrains.python.configuration;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
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.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.vfs.VirtualFile;
|
||||
import com.intellij.ui.CollectionComboBoxModel;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.sdk.PySdkListCellRenderer;
|
||||
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.List;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyActiveSdkConfigurable implements UnnamedConfigurable {
|
||||
private JPanel myPanel;
|
||||
private final Project myProject;
|
||||
@Nullable private final Module myModule;
|
||||
private JComboBox mySdkCombo;
|
||||
private JPanel myConfigureInterpretersPanel;
|
||||
private PyConfigurableInterpreterList myInterpreterList;
|
||||
private ProjectSdksModel myProjectSdksModel;
|
||||
private MyListener myListener;
|
||||
|
||||
public PyActiveSdkConfigurable(Project project) {
|
||||
myModule = null;
|
||||
myProject = project;
|
||||
init();
|
||||
}
|
||||
|
||||
public PyActiveSdkConfigurable(@NotNull Module module) {
|
||||
myModule = module;
|
||||
myProject = module.getProject();
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent createComponent() {
|
||||
return myPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
final Sdk sdk = getSdk();
|
||||
return sdk != myProjectSdksModel.findSdk((Sdk)mySdkCombo.getSelectedItem());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Sdk getSdk() {
|
||||
if (myModule == null) {
|
||||
return ProjectRootManager.getInstance(myProject).getProjectSdk();
|
||||
}
|
||||
final ModuleRootManager rootManager = ModuleRootManager.getInstance(myModule);
|
||||
return rootManager.getSdk();
|
||||
}
|
||||
|
||||
@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() {
|
||||
@Override
|
||||
public void run() {
|
||||
rootManager.setProjectSdk(selectedSdk);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
ModuleRootModificationUtil.setModuleSdk(myModule, selectedSdk);
|
||||
}
|
||||
final Sdk prevSdk = ProjectRootManager.getInstance(myProject).getProjectSdk();
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ProjectRootManager.getInstance(myProject).setProjectSdk(selectedSdk);
|
||||
}
|
||||
});
|
||||
|
||||
myProjectSdksModel.setProjectSdk(selectedSdk);
|
||||
|
||||
// update string literals if different LanguageLevel was selected
|
||||
if (prevSdk != null && selectedSdk != null) {
|
||||
final PythonSdkFlavor flavor1 = PythonSdkFlavor.getFlavor(selectedSdk);
|
||||
final PythonSdkFlavor flavor2 = PythonSdkFlavor.getFlavor(prevSdk);
|
||||
if (flavor1 != null && flavor2 != null) {
|
||||
final LanguageLevel languageLevel1 = flavor1.getLanguageLevel(selectedSdk);
|
||||
final LanguageLevel languageLevel2 = flavor2.getLanguageLevel(prevSdk);
|
||||
if ((languageLevel1.isPy3K() && languageLevel2.isPy3K()) ||
|
||||
(!languageLevel1.isPy3K()) && !languageLevel2.isPy3K()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
rehighlightStrings(myProject);
|
||||
}
|
||||
|
||||
public static void rehighlightStrings(final @NotNull Project project) {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
|
||||
if (editor instanceof EditorEx && editor.getProject() == project) {
|
||||
final VirtualFile vFile = ((EditorEx)editor).getVirtualFile();
|
||||
if (vFile != null) {
|
||||
final EditorHighlighter highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(project, vFile);
|
||||
((EditorEx)editor).setHighlighter(highlighter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
updateSdkList(false);
|
||||
|
||||
final Sdk sdk = getSdk();
|
||||
mySdkCombo.setSelectedItem(myProjectSdksModel.getProjectSdks().get(sdk));
|
||||
}
|
||||
|
||||
private void updateSdkList(boolean preserveSelection) {
|
||||
final List<Sdk> sdkList = myInterpreterList.getAllPythonSdks();
|
||||
Sdk selection = preserveSelection ? (Sdk)mySdkCombo.getSelectedItem() : null;
|
||||
if (!sdkList.contains(selection)) {
|
||||
selection = null;
|
||||
}
|
||||
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);
|
||||
mySdkCombo.setRenderer(new PySdkListCellRenderer());
|
||||
mySdkCombo.setModel(new CollectionComboBoxModel(sdkList, selection));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeUIResources() {
|
||||
myProjectSdksModel.removeListener(myListener);
|
||||
myInterpreterList.disposeModel();
|
||||
}
|
||||
|
||||
private static class MyListener implements SdkModel.Listener {
|
||||
private final PyActiveSdkConfigurable myConfigurable;
|
||||
|
||||
public MyListener(PyActiveSdkConfigurable configurable) {
|
||||
myConfigurable = configurable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sdkAdded(Sdk sdk) {
|
||||
myConfigurable.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeSdkRemove(Sdk sdk) {
|
||||
myConfigurable.updateSdkList(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sdkChanged(Sdk sdk, String previousName) {
|
||||
myConfigurable.updateSdkList(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sdkHomeSelected(Sdk sdk, String newSdkHome) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
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;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
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 {
|
||||
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
|
||||
@Override
|
||||
protected UnnamedConfigurable createModuleConfigurable(Module module) {
|
||||
return new PyActiveSdkConfigurable(module);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UnnamedConfigurable createDefaultProjectConfigurable() {
|
||||
return new PyActiveSdkConfigurable(myProject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws ConfigurationException {
|
||||
super.apply();
|
||||
|
||||
// TODO[catherine] proper per-module caching of framework installed state
|
||||
for (Module module : ModuleManager.getInstance(myProject).getModules()) {
|
||||
final Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
|
||||
if (sdk != null) {
|
||||
VFSTestFrameworkListener.getInstance().updateAllTestFrameworks(sdk);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
PythonSdkUpdater.getInstance().updateActiveSdks(myProject, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configurable[] getConfigurables() {
|
||||
return new Configurable[] { mySdkConfigurable };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
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.roots.ui.configuration.projectRoot.ProjectSdksModel;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Manages the SDK model shared between PythonSdkConfigurable and PyActiveSdkConfigurable.
|
||||
*
|
||||
* @author yole
|
||||
*/
|
||||
public class PyConfigurableInterpreterList {
|
||||
private ProjectSdksModel myModel;
|
||||
private JComboBox mySdkCombo;
|
||||
|
||||
public void setSdkCombo(final JComboBox sdkCombo) {
|
||||
mySdkCombo = sdkCombo;
|
||||
}
|
||||
|
||||
public void setSelectedSdk(final Sdk selectedSdk) {
|
||||
if (mySdkCombo != null)
|
||||
mySdkCombo.setSelectedItem(selectedSdk);
|
||||
}
|
||||
|
||||
public static PyConfigurableInterpreterList getInstance(Project project) {
|
||||
return ServiceManager.getService(project, PyConfigurableInterpreterList.class);
|
||||
}
|
||||
|
||||
public ProjectSdksModel getModel() {
|
||||
if (myModel == null) {
|
||||
myModel = new ProjectSdksModel();
|
||||
myModel.reset(null);
|
||||
}
|
||||
return myModel;
|
||||
}
|
||||
|
||||
public void disposeModel() {
|
||||
if (myModel != null) {
|
||||
myModel.disposeUIResources();
|
||||
myModel = null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Sdk> getAllPythonSdks() {
|
||||
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) {
|
||||
return Comparing.compare(o1.getName(), o2.getName());
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package com.jetbrains.python.configuration;
|
||||
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.openapi.options.newEditor.OptionsEditor;
|
||||
import com.intellij.ui.ClickListener;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
/**
|
||||
* @author traff
|
||||
*/
|
||||
public class PyConfigureInterpretersLinkPanel extends JPanel {
|
||||
private final JBLabel myConfigureLabel;
|
||||
|
||||
public PyConfigureInterpretersLinkPanel(final JPanel parentPanel) {
|
||||
super(new BorderLayout());
|
||||
myConfigureLabel = new JBLabel("<html><a href=\"#\">Configure Interpreters");
|
||||
myConfigureLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
new ClickListener() {
|
||||
@Override
|
||||
public boolean onClick(MouseEvent e, int clickCount) {
|
||||
if (clickCount == 1) {
|
||||
final OptionsEditor optionsEditor = OptionsEditor.KEY.getData(DataManager.getInstance().getDataContext(parentPanel));
|
||||
if (optionsEditor != null) {
|
||||
PythonSdkConfigurable configurable = optionsEditor.findConfigurable(PythonSdkConfigurable.class);
|
||||
if (configurable != null) {
|
||||
optionsEditor.clearSearchAndSelect(configurable);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}.installOn(myConfigureLabel);
|
||||
|
||||
add(myConfigureLabel, BorderLayout.CENTER);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
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.OptionalConfigurable;
|
||||
import com.intellij.openapi.options.UnnamedConfigurable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyDependenciesConfigurable extends ModuleAwareProjectConfigurable implements OptionalConfigurable {
|
||||
private final Project myProject;
|
||||
|
||||
public PyDependenciesConfigurable(Project project) {
|
||||
super(project, "Project Dependencies", "reference.settingsdialog.project.dependencies");
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected UnnamedConfigurable createModuleConfigurable(Module module) {
|
||||
return new PyModuleDependenciesConfigurable(module);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needDisplay() {
|
||||
return ModuleManager.getInstance(myProject).getModules().length > 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.jetbrains.python.configuration;
|
||||
|
||||
import com.intellij.openapi.diff.DiffContent;
|
||||
import com.intellij.openapi.diff.SimpleContent;
|
||||
import com.intellij.openapi.diff.impl.settings.DiffPreviewProvider;
|
||||
import com.jetbrains.python.PythonFileType;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
/**
|
||||
* @author oleg
|
||||
*/
|
||||
public class PyDiffPreviewProvider extends DiffPreviewProvider {
|
||||
@Override
|
||||
public DiffContent[] createContents() {
|
||||
return new DiffContent[]{createContent(LEFT_TEXT), createContent(CENTER_TEXT), createContent(RIGHT_TEXT)};
|
||||
}
|
||||
|
||||
private static SimpleContent createContent(final String text) {
|
||||
return new SimpleContent(text, PythonFileType.INSTANCE);
|
||||
}
|
||||
|
||||
@NonNls private static final String LEFT_TEXT = "class MyClass\n" +
|
||||
" value = 123\n" +
|
||||
"\n" +
|
||||
" def left_only(self):\n" +
|
||||
" bar(123)\n" +
|
||||
"\n" +
|
||||
" def bar(self, a)\n" +
|
||||
" print a\n"+
|
||||
"\n"+
|
||||
" def foo(self):\n" +
|
||||
" pass # Left changes\n"+
|
||||
"\n"+
|
||||
"\n";
|
||||
@NonNls private static final String CENTER_TEXT = "class MyClass\n" +
|
||||
" value = 123\n" +
|
||||
"\n" +
|
||||
" def bar(self, a)\n" +
|
||||
" print a\n"+
|
||||
"\n"+
|
||||
" def foo(self):\n" +
|
||||
" pass\n" +
|
||||
"\n" +
|
||||
"\n" +
|
||||
" def removed_from_left(self):\n" +
|
||||
" bar('PyCharmRulezzz')";
|
||||
@NonNls private static final String RIGHT_TEXT = "class MyClass\n" +
|
||||
" value = -123\n" +
|
||||
"\n" +
|
||||
" def bar(self, a)\n" +
|
||||
" print a\n"+
|
||||
"\n"+
|
||||
" def foo(self):\n" +
|
||||
" pass # Right changes\n" +
|
||||
"\n" +
|
||||
"\n" +
|
||||
" def removed_from_left(self):\n" +
|
||||
" bar('PyCharmRulezzz')";
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.configuration.PyModuleDependenciesConfigurable">
|
||||
<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"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="d3225" 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="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Project depends on these projects:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<grid id="4caac" binding="myListHolderPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" 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>
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package com.jetbrains.python.configuration;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.options.UnnamedConfigurable;
|
||||
import com.intellij.openapi.roots.ModifiableRootModel;
|
||||
import com.intellij.openapi.roots.ModuleOrderEntry;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.OrderEntry;
|
||||
import com.intellij.ui.CheckBoxList;
|
||||
import com.intellij.ui.ToolbarDecorator;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.ui.EditableListModelDecorator;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyModuleDependenciesConfigurable implements UnnamedConfigurable {
|
||||
private final Module myModule;
|
||||
private List<Module> myInitialDependencies;
|
||||
private JPanel myMainPanel;
|
||||
private JPanel myListHolderPanel;
|
||||
private CheckBoxList<Module> myDependenciesList;
|
||||
|
||||
public PyModuleDependenciesConfigurable(Module module) {
|
||||
myModule = module;
|
||||
myDependenciesList = new CheckBoxList<Module>();
|
||||
resetModel();
|
||||
ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myDependenciesList,
|
||||
new EditableListModelDecorator((DefaultListModel) myDependenciesList.getModel()));
|
||||
decorator.disableRemoveAction();
|
||||
myListHolderPanel.add(decorator.createPanel(), BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
private void resetModel() {
|
||||
List<Module> possibleDependencies = new ArrayList<Module>();
|
||||
myInitialDependencies = Arrays.asList(ModuleRootManager.getInstance(myModule).getDependencies());
|
||||
possibleDependencies.addAll(myInitialDependencies);
|
||||
for (Module otherModule : ModuleManager.getInstance(myModule.getProject()).getModules()) {
|
||||
if (!possibleDependencies.contains(otherModule) && otherModule != myModule) {
|
||||
possibleDependencies.add(otherModule);
|
||||
}
|
||||
}
|
||||
myDependenciesList.setItems(possibleDependencies, new Function<Module, String>() {
|
||||
@Override
|
||||
public String fun(Module module) {
|
||||
return module.getName();
|
||||
}
|
||||
});
|
||||
myDependenciesList.setBorder(null);
|
||||
for (Module dependency : myInitialDependencies) {
|
||||
myDependenciesList.setItemSelected(dependency, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createComponent() {
|
||||
return myMainPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified() {
|
||||
return collectDependencies().equals(myInitialDependencies);
|
||||
}
|
||||
|
||||
private List<Module> collectDependencies() {
|
||||
List<Module> result = new ArrayList<Module>();
|
||||
for (int i = 0; i < myDependenciesList.getItemsCount(); i++) {
|
||||
Module module = (Module)myDependenciesList.getItemAt(i);
|
||||
if (myDependenciesList.isItemSelected(module)) {
|
||||
result.add(module);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() throws ConfigurationException {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ModifiableRootModel model = ModuleRootManager.getInstance(myModule).getModifiableModel();
|
||||
List<ModuleOrderEntry> entries = new ArrayList<ModuleOrderEntry>();
|
||||
for (OrderEntry entry : model.getOrderEntries()) {
|
||||
if (entry instanceof ModuleOrderEntry) {
|
||||
entries.add((ModuleOrderEntry) entry);
|
||||
}
|
||||
}
|
||||
for (ModuleOrderEntry entry : entries) {
|
||||
model.removeOrderEntry(entry);
|
||||
}
|
||||
for (Module module : collectDependencies()) {
|
||||
model.addModuleOrderEntry(module);
|
||||
}
|
||||
model.commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
resetModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disposeUIResources() {
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
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.NonDefaultProjectConfigurable;
|
||||
import com.intellij.openapi.options.OptionalConfigurable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ui.configuration.PlatformContentEntriesConfigurable;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.model.java.JavaSourceRootType;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PythonContentEntriesConfigurable extends ModuleAwareProjectConfigurable implements NonDefaultProjectConfigurable, OptionalConfigurable {
|
||||
private final Project myProject;
|
||||
|
||||
public PythonContentEntriesConfigurable(final Project project) {
|
||||
super(project, "Project Structure", "reference.settingsdialog.project.structure");
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Module getSingleModule(Project project) {
|
||||
return ArrayUtil.getFirstElement(ModuleManager.getInstance(project).getModules());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needDisplay() {
|
||||
return getSingleModule(myProject) != null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Configurable createModuleConfigurable(Module module) {
|
||||
return new PlatformContentEntriesConfigurable(module, JavaSourceRootType.SOURCE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.configuration;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.fileTypes.FileTypes;
|
||||
import com.intellij.openapi.projectRoots.SdkAdditionalData;
|
||||
import com.intellij.openapi.projectRoots.SdkModificator;
|
||||
import com.intellij.openapi.projectRoots.ui.SdkPathEditor;
|
||||
import com.intellij.openapi.roots.OrderRootType;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.vfs.JarFileSystem;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.AnActionButton;
|
||||
import com.intellij.ui.ListCellRendererWrapper;
|
||||
import com.intellij.ui.ListUtil;
|
||||
import com.intellij.ui.ToolbarDecorator;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.jetbrains.python.sdk.PythonSdkAdditionalData;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author traff
|
||||
*/
|
||||
public class PythonPathEditor extends SdkPathEditor {
|
||||
private final PathListModel myPathListModel;
|
||||
|
||||
public PythonPathEditor(final String displayName,
|
||||
final OrderRootType orderRootType,
|
||||
final FileChooserDescriptor descriptor) {
|
||||
super(displayName, orderRootType, descriptor);
|
||||
myPathListModel = new PathListModel(orderRootType, getListModel());
|
||||
}
|
||||
|
||||
public void reset(@Nullable SdkModificator modificator) {
|
||||
if (modificator != null) {
|
||||
List<VirtualFile> list = Lists.newArrayList(modificator.getRoots(getOrderRootType()));
|
||||
resetPath(myPathListModel.reset(list, modificator));
|
||||
}
|
||||
else {
|
||||
setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void reload(@Nullable SdkModificator sdkModificator) {
|
||||
if (sdkModificator != null) {
|
||||
List<VirtualFile> list = Lists.newArrayList(sdkModificator.getRoots(getOrderRootType()));
|
||||
resetPath(myPathListModel.reload(list));
|
||||
setModified(true);
|
||||
}
|
||||
else {
|
||||
setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(SdkModificator sdkModificator) {
|
||||
sdkModificator.removeRoots(getOrderRootType());
|
||||
// add all items
|
||||
for (int i = 0; i < myPathListModel.getRowCount(); i++) {
|
||||
VirtualFile path = myPathListModel.getValueAt(i);
|
||||
if (!myPathListModel.isExcluded(path)) {
|
||||
sdkModificator.addRoot(path, getOrderRootType());
|
||||
}
|
||||
}
|
||||
setModified(false);
|
||||
myPathListModel.apply(sdkModificator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VirtualFile[] adjustAddedFileSet(Component component, VirtualFile[] files) {
|
||||
for (int i = 0, filesLength = files.length; i < filesLength; i++) {
|
||||
if (files[i].getFileType() == FileTypes.ARCHIVE) {
|
||||
files[i] = JarFileSystem.getInstance().getJarRootForLocalFile(files[i]);
|
||||
}
|
||||
}
|
||||
if (myPathListModel.add(Lists.newArrayList(files))) {
|
||||
setModified(true);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRemoveItems(int[] idxs, JList list) {
|
||||
List<Pair<VirtualFile, Integer>> removed = Lists.newArrayList();
|
||||
for (int i : idxs) {
|
||||
removed.add(Pair.create((VirtualFile)getListModel().get(i), i));
|
||||
}
|
||||
ListUtil.removeIndices(list, myPathListModel.remove(removed));
|
||||
list.updateUI();
|
||||
setModified(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ListCellRenderer createListCellRenderer(JBList list) {
|
||||
return new PythonPathListCellRenderer(super.createListCellRenderer(list), myPathListModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addToolbarButtons(ToolbarDecorator toolbarDecorator) {
|
||||
AnActionButton reloadButton = new AnActionButton("Reload List of Paths", AllIcons.Actions.Refresh) {
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
onReloadButtonClicked();
|
||||
}
|
||||
};
|
||||
toolbarDecorator.addExtraAction(reloadButton);
|
||||
}
|
||||
|
||||
protected void onReloadButtonClicked() {
|
||||
}
|
||||
|
||||
private static class PathListModel {
|
||||
private Set<VirtualFile> myAdded = Sets.newHashSet();
|
||||
private Set<VirtualFile> myExcluded = Sets.newHashSet();
|
||||
private Set<VirtualFile> myFoundFiles = Sets.newHashSet();
|
||||
private List<VirtualFile> myFilteredOut = Lists.newArrayList();
|
||||
private final DefaultListModel myListModel;
|
||||
private final OrderRootType myOrderRootType;
|
||||
private Set<VirtualFile> myUserAddedToRemove = Sets.newHashSet();
|
||||
|
||||
public PathListModel(OrderRootType orderRootType, DefaultListModel listModel) {
|
||||
myOrderRootType = orderRootType;
|
||||
myListModel = listModel;
|
||||
}
|
||||
|
||||
private int getRowCount() {
|
||||
return myListModel.getSize();
|
||||
}
|
||||
|
||||
private VirtualFile getValueAt(int row) {
|
||||
return (VirtualFile)myListModel.get(row);
|
||||
}
|
||||
|
||||
public boolean add(List<VirtualFile> files) {
|
||||
for (VirtualFile file : files) {
|
||||
if (!myFoundFiles.contains(file)) {
|
||||
if (!myExcluded.remove(file)) { //if it was excluded we only delete exclusion mark
|
||||
myAdded.add(file);
|
||||
myUserAddedToRemove.remove(file);
|
||||
}
|
||||
else {
|
||||
myFoundFiles.add(file);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
myExcluded.remove(file);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int[] remove(List<Pair<VirtualFile, Integer>> files) {
|
||||
List<Integer> toRemove = Lists.newArrayList();
|
||||
for (Pair<VirtualFile, Integer> e : files) {
|
||||
if (myAdded.contains(e.first)) {
|
||||
toRemove.add(e.second);
|
||||
myAdded.remove(e.first);
|
||||
myUserAddedToRemove.add(e.first);
|
||||
}
|
||||
else if (myExcluded.contains(e.first)) {
|
||||
myExcluded.remove(e.first);
|
||||
}
|
||||
else {
|
||||
myExcluded.add(e.first);
|
||||
}
|
||||
}
|
||||
return ArrayUtil.toIntArray(toRemove);
|
||||
}
|
||||
|
||||
public void apply(SdkModificator sdkModificator) {
|
||||
sdkModificator.setSdkAdditionalData(collectSdkAdditionalData(sdkModificator));
|
||||
addFilteredOutRoots(sdkModificator);
|
||||
}
|
||||
|
||||
private void addFilteredOutRoots(SdkModificator sdkModificator) {
|
||||
for (VirtualFile file : myFilteredOut) {
|
||||
sdkModificator.addRoot(file, myOrderRootType);
|
||||
}
|
||||
}
|
||||
|
||||
private SdkAdditionalData collectSdkAdditionalData(SdkModificator sdkModificator) {
|
||||
PythonSdkAdditionalData data = (PythonSdkAdditionalData)sdkModificator.getSdkAdditionalData();
|
||||
if (data == null) {
|
||||
data = new PythonSdkAdditionalData(null);
|
||||
}
|
||||
data.setAddedPathsFromVirtualFiles(myAdded);
|
||||
data.setExcludedPathsFromVirtualFiles(myExcluded);
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setAdded(Set<VirtualFile> added) {
|
||||
myAdded = Sets.newHashSet(added);
|
||||
}
|
||||
|
||||
public void setExcluded(Set<VirtualFile> excluded) {
|
||||
myExcluded = Sets.newHashSet(excluded);
|
||||
}
|
||||
|
||||
public String getPresentationSuffix(VirtualFile file) {
|
||||
if (myAdded.contains(file)) {
|
||||
return "(added by user)";
|
||||
}
|
||||
if (myExcluded.contains(file)) {
|
||||
return "(removed by user)";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public List<VirtualFile> reload(List<VirtualFile> list) {
|
||||
myFoundFiles.clear();
|
||||
myFoundFiles.addAll(list);
|
||||
List<VirtualFile> result = filterOutStubs(list, myFilteredOut);
|
||||
result.removeAll(myUserAddedToRemove);
|
||||
result.addAll(myAdded);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<VirtualFile> reset(List<VirtualFile> list, SdkModificator modificator) {
|
||||
myFilteredOut.clear();
|
||||
List<VirtualFile> result = filterOutStubs(list, myFilteredOut);
|
||||
|
||||
myFoundFiles.clear();
|
||||
myFoundFiles.addAll(list);
|
||||
myUserAddedToRemove.clear();
|
||||
|
||||
if (modificator.getSdkAdditionalData() instanceof PythonSdkAdditionalData) {
|
||||
PythonSdkAdditionalData data = (PythonSdkAdditionalData)modificator.getSdkAdditionalData();
|
||||
setAdded(data.getAddedPathFiles());
|
||||
setExcluded(data.getExcludedPathFiles());
|
||||
result.addAll(myExcluded);
|
||||
}
|
||||
else if (modificator.getSdkAdditionalData() == null) {
|
||||
myAdded.clear();
|
||||
myExcluded.clear();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<VirtualFile> filterOutStubs(List<VirtualFile> list, List<VirtualFile> filteredOut) {
|
||||
List<VirtualFile> result = Lists.newArrayList();
|
||||
filteredOut.clear();
|
||||
for (VirtualFile file : list) {
|
||||
if (!isStubPath(file)) {
|
||||
result.add(file);
|
||||
}
|
||||
else {
|
||||
filteredOut.add(file);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isStubPath(@NotNull VirtualFile file) {
|
||||
final String path = PythonSdkType.getSkeletonsRootPath(PathManager.getSystemPath());
|
||||
final VirtualFile skeletonRoot = LocalFileSystem.getInstance().findFileByPath(path);
|
||||
if (skeletonRoot != null) {
|
||||
return file.getPath().startsWith(skeletonRoot.getPath());
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isExcluded(VirtualFile path) {
|
||||
return myExcluded.contains(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class PythonPathListCellRenderer extends ListCellRendererWrapper<VirtualFile> {
|
||||
private final PathListModel model;
|
||||
|
||||
public PythonPathListCellRenderer(final ListCellRenderer listCellRenderer, PathListModel model) {
|
||||
super();
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(JList list, VirtualFile value, int index, boolean selected, boolean hasFocus) {
|
||||
String suffix = model.getPresentationSuffix(value);
|
||||
if (suffix.length() > 0) {
|
||||
suffix = " " + suffix;
|
||||
}
|
||||
setText(value != null ? value.getPresentableUrl() + suffix : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,571 @@
|
||||
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.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.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;
|
||||
import icons.PythonIcons;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
public class PythonSdkConfigurable implements Configurable, Configurable.NoScroll {
|
||||
private JPanel myPanel;
|
||||
private JList mySdkList;
|
||||
private JPanel mySplitterHolder;
|
||||
private PackagesNotificationPanel myNotificationsArea;
|
||||
private JPanel myNotificationsPlaceholder;
|
||||
private PythonPathEditor myPathEditor;
|
||||
private boolean mySdkListChanged = false;
|
||||
private final PyConfigurableInterpreterList myInterpreterList;
|
||||
private final ProjectSdksModel myProjectSdksModel;
|
||||
private final PyInstalledPackagesPanel myPackagesPanel;
|
||||
|
||||
private Map<Sdk, SdkModificator> myModificators = new FactoryMap<Sdk, SdkModificator>() {
|
||||
@Override
|
||||
protected SdkModificator create(Sdk sdk) {
|
||||
return sdk.getSdkModificator();
|
||||
}
|
||||
};
|
||||
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;
|
||||
|
||||
public void setNewProject(final boolean newProject) {
|
||||
myNewProject = newProject;
|
||||
}
|
||||
|
||||
public PythonSdkConfigurable(Project project) {
|
||||
myProject = project;
|
||||
myInterpreterList = PyConfigurableInterpreterList.getInstance(myProject);
|
||||
myProjectSdksModel = myInterpreterList.getModel();
|
||||
myFirstReset = true;
|
||||
|
||||
|
||||
mySdkList = new JBList();
|
||||
mySdkList.setCellRenderer(new PySdkListCellRenderer("", myModificators));
|
||||
mySdkList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
|
||||
ToolbarDecorator decorator = ToolbarDecorator.createDecorator(mySdkList).disableUpDownActions()
|
||||
.setAddAction(new AnActionButtonRunnable() {
|
||||
@Override
|
||||
public void run(AnActionButton button) {
|
||||
addSdk(button);
|
||||
}
|
||||
})
|
||||
.setEditAction(new AnActionButtonRunnable() {
|
||||
@Override
|
||||
public void run(AnActionButton button) {
|
||||
editSdk();
|
||||
}
|
||||
})
|
||||
.setRemoveAction(new AnActionButtonRunnable() {
|
||||
@Override
|
||||
public void run(AnActionButton button) {
|
||||
removeSdk();
|
||||
}
|
||||
})
|
||||
.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);
|
||||
|
||||
addListeners();
|
||||
}
|
||||
|
||||
private void addListeners() {
|
||||
myProjectSdksModel.addListener(new SdkModel.Listener() {
|
||||
@Override
|
||||
public void sdkAdded(Sdk sdk) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeSdkRemove(Sdk sdk) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sdkChanged(Sdk sdk, String previousName) {
|
||||
refreshSdkList();
|
||||
}
|
||||
|
||||
@Override
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
addCreatedSdk(sdk, true, setAsProjectInterpreter);
|
||||
}
|
||||
};
|
||||
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();
|
||||
}
|
||||
|
||||
public void apply() throws ConfigurationException {
|
||||
if (myPreviousSelection != null) {
|
||||
saveSdkPaths(myPreviousSelection);
|
||||
}
|
||||
for (SdkModificator modificator : myModifiedModificators) {
|
||||
modificator.commitChanges();
|
||||
}
|
||||
myModificators.clear();
|
||||
myModifiedModificators.clear();
|
||||
myProjectSdksModel.apply();
|
||||
mySdkListChanged = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Sdk getSelectedSdk() {
|
||||
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();
|
||||
if (!myShowOtherProjectVirtualenvs) {
|
||||
VirtualEnvProjectFilter.removeNotMatching(myProject, pythonSdks);
|
||||
}
|
||||
Collections.sort(pythonSdks, new PreferredSdkComparator());
|
||||
mySdkList.setModel(new CollectionListModel<Sdk>(pythonSdks));
|
||||
|
||||
mySdkListChanged = false;
|
||||
if (projectSdk == null) projectSdk = getSdk();
|
||||
if (projectSdk != null) {
|
||||
projectSdk = myProjectSdksModel.findSdk(projectSdk.getName());
|
||||
mySdkList.clearSelection();
|
||||
mySdkList.setSelectedValue(projectSdk, true);
|
||||
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();
|
||||
}
|
||||
return ProjectRootManager.getInstance(myProject).getProjectSdk();
|
||||
}
|
||||
|
||||
private void addSdk(AnActionButton button) {
|
||||
InterpreterPathChooser
|
||||
.show(myProject, myProjectSdksModel.getSdks(), button.getPreferredPopupPoint(), false, new NullableConsumer<Sdk>() {
|
||||
@Override
|
||||
public void consume(Sdk sdk) {
|
||||
addCreatedSdk(sdk, false, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addCreatedSdk(@Nullable final Sdk sdk, boolean newVirtualEnv, boolean makeActive) {
|
||||
if (sdk != null) {
|
||||
boolean isVirtualEnv = PythonSdkType.isVirtualEnv(sdk);
|
||||
boolean askSetAsProjectInterpreter = !myProject.isDefault() && !myNewProject;
|
||||
if (askSetAsProjectInterpreter && isVirtualEnv && !newVirtualEnv) {
|
||||
AddVEnvOptionsDialog dialog = new AddVEnvOptionsDialog(myPanel);
|
||||
dialog.show();
|
||||
if (dialog.getExitCode() != DialogWrapper.OK_EXIT_CODE) {
|
||||
return;
|
||||
}
|
||||
SdkModificator modificator = myModificators.get(sdk);
|
||||
setSdkAssociated(modificator, !dialog.makeAvailableToAll());
|
||||
myModifiedModificators.add(modificator);
|
||||
makeActive = dialog.useForThisProject();
|
||||
}
|
||||
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
|
||||
makeActive = 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;
|
||||
}
|
||||
if (makeActive) {
|
||||
SdkConfigurationUtil.setDirectoryProjectSdk(myProject, sdk);
|
||||
myProjectSdksModel.setProjectSdk(sdk);
|
||||
myInterpreterList.setSelectedSdk(sdk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void editSdk() {
|
||||
final Sdk currentSdk = getSelectedSdk();
|
||||
if (currentSdk != null) {
|
||||
if (currentSdk.getSdkAdditionalData() instanceof RemoteCredentials) {
|
||||
editRemoteSdk(currentSdk);
|
||||
}
|
||||
else {
|
||||
editSdk(currentSdk);
|
||||
}
|
||||
updateUI(currentSdk);
|
||||
}
|
||||
}
|
||||
|
||||
private void editRemoteSdk(Sdk currentSdk) {
|
||||
PythonRemoteInterpreterManager remoteInterpreterManager = PythonRemoteInterpreterManager.getInstance();
|
||||
if (remoteInterpreterManager != null) {
|
||||
final SdkModificator modificator = myModificators.get(currentSdk);
|
||||
Set<Sdk> existingSdks = Sets.newHashSet(myProjectSdksModel.getSdks());
|
||||
existingSdks.remove(currentSdk);
|
||||
if (remoteInterpreterManager.editSdk(myProject, modificator, existingSdks)) {
|
||||
myModifiedModificators.add(modificator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void editSdk(final Sdk currentSdk) {
|
||||
final SdkModificator modificator = myModificators.get(currentSdk);
|
||||
final EditSdkDialog dialog = new EditSdkDialog(myProject, modificator, new NullableFunction<String, String>() {
|
||||
@Override
|
||||
public String fun(String s) {
|
||||
if (isDuplicateSdkName(s, currentSdk)) {
|
||||
return "Please specify a unique name for the interpreter";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
if (dialog.isOK()) {
|
||||
final boolean pathChanged = !Comparing.equal(currentSdk.getHomePath(), dialog.getHomePath());
|
||||
if (!currentSdk.getName().equals(dialog.getName()) || pathChanged || dialog.isAssociateChanged()) {
|
||||
myModifiedModificators.add(modificator);
|
||||
modificator.setName(dialog.getName());
|
||||
modificator.setHomePath(dialog.getHomePath());
|
||||
|
||||
if (dialog.isAssociateChanged()) {
|
||||
setSdkAssociated(modificator, dialog.associateWithProject());
|
||||
}
|
||||
if (pathChanged) {
|
||||
reloadSdk(currentSdk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setSdkAssociated(SdkModificator modificator, boolean isAssociated) {
|
||||
PythonSdkAdditionalData additionalData = (PythonSdkAdditionalData)modificator.getSdkAdditionalData();
|
||||
if (additionalData == null) {
|
||||
additionalData = new PythonSdkAdditionalData(PythonSdkFlavor.getFlavor(modificator.getHomePath()));
|
||||
modificator.setSdkAdditionalData(additionalData);
|
||||
}
|
||||
if (isAssociated) {
|
||||
additionalData.associateWithProject(myProject);
|
||||
}
|
||||
else {
|
||||
additionalData.setAssociatedProjectPath(null);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDuplicateSdkName(String s, Sdk sdk) {
|
||||
for (Sdk existingSdk : myProjectSdksModel.getSdks()) {
|
||||
if (existingSdk == sdk) {
|
||||
continue;
|
||||
}
|
||||
String existingName;
|
||||
if (myModificators.containsKey(existingSdk)) {
|
||||
existingName = myModificators.get(existingSdk).getName();
|
||||
}
|
||||
else {
|
||||
existingName = existingSdk.getName();
|
||||
}
|
||||
if (existingName.equals(s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void removeSdk() {
|
||||
final Sdk current_sdk = getSelectedSdk();
|
||||
if (current_sdk != null) {
|
||||
myProjectSdksModel.removeSdk(current_sdk);
|
||||
if (myModificators.containsKey(current_sdk)) {
|
||||
SdkModificator modificator = myModificators.get(current_sdk);
|
||||
myModifiedModificators.remove(modificator);
|
||||
myModificators.remove(current_sdk);
|
||||
}
|
||||
refreshSdkList();
|
||||
mySdkListChanged = true;
|
||||
// TODO select initially selected SDK
|
||||
if (mySdkList.getSelectedIndex() < 0) {
|
||||
mySdkList.setSelectedIndex(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadSdk() {
|
||||
final Sdk currentSdk = getSelectedSdk();
|
||||
if (currentSdk != null) {
|
||||
myModifiedModificators.add(myModificators.get(currentSdk));
|
||||
reloadSdk(currentSdk);
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
public ToggleVirtualEnvFilterButton() {
|
||||
super("Show virtual environments associated with other projects", AllIcons.General.Filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSelected(AnActionEvent e) {
|
||||
return myShowOtherProjectVirtualenvs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelected(AnActionEvent e, boolean state) {
|
||||
myShowOtherProjectVirtualenvs = state;
|
||||
refreshSdkList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.jetbrains.python.configuration;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.jetbrains.python.sdk.PythonSdkAdditionalData;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class VirtualEnvProjectFilter implements Predicate<Sdk> {
|
||||
private final String myBasePath;
|
||||
|
||||
public VirtualEnvProjectFilter(@Nullable final String basePath) {
|
||||
myBasePath = basePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(@Nullable final Sdk input) {
|
||||
if (input != null && PythonSdkType.isVirtualEnv(input)) {
|
||||
PythonSdkAdditionalData data = (PythonSdkAdditionalData) input.getSdkAdditionalData();
|
||||
if (data != null) {
|
||||
final String path = data.getAssociatedProjectPath();
|
||||
if (path != null && (myBasePath == null || !path.equals(myBasePath))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeAllAssociated(List<Sdk> sdks) {
|
||||
Iterables.removeIf(sdks, new VirtualEnvProjectFilter(null));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.newProject;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.projectRoots.SdkAdditionalData;
|
||||
import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
|
||||
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.PythonSdkAdditionalData;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User : catherine
|
||||
*/
|
||||
public class PythonNewDirectoryProjectAction extends NewDirectoryProjectAction {
|
||||
private Sdk mySdk;
|
||||
private boolean myInstallFramework;
|
||||
|
||||
public void actionPerformed(final AnActionEvent e) {
|
||||
Project project = e.getData(PlatformDataKeys.PROJECT);
|
||||
if (project == null) {
|
||||
project = ProjectManager.getInstance().getDefaultProject();
|
||||
}
|
||||
PythonNewDirectoryProjectDialog dlg = new PythonNewDirectoryProjectDialog(project);
|
||||
dlg.show();
|
||||
if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) return;
|
||||
mySdk = dlg.getSdk();
|
||||
myInstallFramework = dlg.installFramework();
|
||||
Project newProject = generateProject(project, dlg);
|
||||
if (newProject != null) {
|
||||
SdkConfigurationUtil.setDirectoryProjectSdk(newProject, mySdk);
|
||||
final List<Sdk> sdks = PythonSdkType.getAllSdks();
|
||||
for (Sdk sdk : sdks) {
|
||||
final SdkAdditionalData additionalData = sdk.getSdkAdditionalData();
|
||||
if (additionalData instanceof PythonSdkAdditionalData) {
|
||||
((PythonSdkAdditionalData) additionalData).reassociateWithCreatedProject(newProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object showSettings(DirectoryProjectGenerator generator, VirtualFile baseDir)
|
||||
throws ProcessCanceledException {
|
||||
Object settings = super.showSettings(generator, baseDir);
|
||||
if (settings instanceof PyNewProjectSettings) {
|
||||
((PyNewProjectSettings)settings).setSdk(mySdk);
|
||||
((PyNewProjectSettings)settings).setInstallFramework(myInstallFramework);
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.newProject;
|
||||
|
||||
import com.intellij.facet.ui.ValidationResult;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.platform.DirectoryProjectGenerator;
|
||||
import com.intellij.platform.NewDirectoryProjectDialog;
|
||||
import com.intellij.remotesdk.RemoteSdkData;
|
||||
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.VirtualEnvProjectFilter;
|
||||
import com.jetbrains.python.packaging.PyExternalProcessException;
|
||||
import com.jetbrains.python.packaging.PyPackage;
|
||||
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;
|
||||
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
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.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User : catherine
|
||||
*/
|
||||
public class PythonNewDirectoryProjectDialog extends NewDirectoryProjectDialog {
|
||||
private ComboboxWithBrowseButton mySdkCombo;
|
||||
private final JCheckBox myFrameworkCheckbox;
|
||||
private boolean myInstallFrameworkChanged;
|
||||
private final Project myProject;
|
||||
|
||||
protected PythonNewDirectoryProjectDialog(Project project) {
|
||||
super(project);
|
||||
myProject = project;
|
||||
|
||||
final List<Sdk> sdks = PythonSdkType.getAllSdks();
|
||||
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
|
||||
public boolean value(Sdk sdk) {
|
||||
return sdk == preferred;
|
||||
}
|
||||
});
|
||||
final JLabel label = new JBLabel("Interpreter:", SwingConstants.LEFT) {
|
||||
@Override
|
||||
public Dimension getMinimumSize() {
|
||||
return new JLabel("Project name:").getPreferredSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return getMinimumSize();
|
||||
}
|
||||
};
|
||||
label.setLabelFor(mySdkCombo);
|
||||
final JPanel placeholder = getPlaceHolder();
|
||||
final GridBagConstraints constraints = new GridBagConstraints();
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = 0;
|
||||
constraints.anchor = GridBagConstraints.WEST;
|
||||
constraints.insets = new Insets(0, 0, 0, 10);
|
||||
placeholder.add(label, constraints);
|
||||
|
||||
constraints.gridx = 1;
|
||||
constraints.gridy = 0;
|
||||
constraints.fill = GridBagConstraints.BOTH;
|
||||
constraints.weightx = 1.0;
|
||||
constraints.insets = new Insets(0, 0, 0, 0);
|
||||
|
||||
placeholder.add(mySdkCombo, constraints);
|
||||
|
||||
final ActionListener listener = new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
checkValid();
|
||||
}
|
||||
};
|
||||
|
||||
myFrameworkCheckbox = new JBCheckBox("Install <framework>");
|
||||
constraints.gridx = 0;
|
||||
constraints.gridy = 1;
|
||||
constraints.gridwidth = 2;
|
||||
constraints.weightx = 0.0;
|
||||
placeholder.add(myFrameworkCheckbox, constraints);
|
||||
myFrameworkCheckbox.setVisible(false);
|
||||
|
||||
myFrameworkCheckbox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
myInstallFrameworkChanged = true;
|
||||
checkValid();
|
||||
}
|
||||
});
|
||||
|
||||
mySdkCombo.addActionListener(listener);
|
||||
mySdkCombo.getComboBox().addActionListener(listener);
|
||||
myProjectTypeComboBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
selectCompatiblePython();
|
||||
checkValid();
|
||||
}
|
||||
});
|
||||
|
||||
mySdkCombo.getComboBox().addPropertyChangeListener(new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent event) {
|
||||
checkValid();
|
||||
}
|
||||
});
|
||||
|
||||
final Dimension checkBoxSize = myFrameworkCheckbox.getPreferredSize();
|
||||
myRootPane.setPreferredSize(new Dimension(myRootPane.getPreferredSize().width,
|
||||
myRootPane.getPreferredSize().height + checkBoxSize.height));
|
||||
|
||||
checkValid();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getEmptyProjectGenerator() {
|
||||
return new DirectoryProjectGenerator() {
|
||||
|
||||
@NotNull
|
||||
@Nls
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Empty project";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object showGenerationSettings(VirtualFile baseDir) throws ProcessCanceledException {
|
||||
if (PythonSdkType.isRemote(getSdk())) {
|
||||
PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
|
||||
assert manager != null;
|
||||
return manager.showRemoteProjectSettingsDialog(baseDir, (RemoteSdkData)getSdk().getSdkAdditionalData());
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateProject(@NotNull Project project,
|
||||
@NotNull VirtualFile baseDir,
|
||||
Object settings,
|
||||
@NotNull Module module) {
|
||||
if (settings instanceof RemoteProjectSettings) {
|
||||
PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
|
||||
assert manager != null;
|
||||
manager.createDeployment(project, baseDir, (RemoteProjectSettings)settings,
|
||||
(RemoteSdkData)getSdk().getSdkAdditionalData());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ValidationResult validate(@NotNull String baseDirPath) {
|
||||
if (PythonSdkType.isRemote(getSdk())) {
|
||||
if (PythonRemoteInterpreterManager.getInstance() == null) {
|
||||
return new ValidationResult(PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED);
|
||||
}
|
||||
}
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkValid() {
|
||||
super.checkValid();
|
||||
Sdk sdk = getSdk();
|
||||
if (isOKActionEnabled()) {
|
||||
setOKActionEnabled(true);
|
||||
setErrorText(null);
|
||||
myFrameworkCheckbox.setVisible(false);
|
||||
|
||||
DirectoryProjectGenerator generator = getProjectGenerator();
|
||||
final boolean isPy3k = sdk != null && PythonSdkType.getLanguageLevelForSdk(sdk).isPy3K();
|
||||
if (sdk != null && PythonSdkType.isRemote(sdk) && !acceptsRemoteSdk(generator)) {
|
||||
setOKActionEnabled(false);
|
||||
setErrorText("Please choose a local interpreter");
|
||||
}
|
||||
else if (generator instanceof PyFrameworkProjectGenerator) {
|
||||
PyFrameworkProjectGenerator frameworkProjectGenerator = (PyFrameworkProjectGenerator)generator;
|
||||
String frameworkName = frameworkProjectGenerator.getFrameworkTitle();
|
||||
if (sdk != null && !isFrameworkInstalled(sdk)) {
|
||||
final PyPackageManagerImpl packageManager = (PyPackageManagerImpl)PyPackageManager.getInstance(sdk);
|
||||
final boolean onlyWithCache =
|
||||
PythonSdkFlavor.getFlavor(sdk) instanceof JythonSdkFlavor || PythonSdkFlavor.getFlavor(sdk) instanceof PyPySdkFlavor;
|
||||
try {
|
||||
if (onlyWithCache && packageManager.cacheIsNotNull() || !onlyWithCache) {
|
||||
final PyPackage pip = packageManager.findPackage("pip");
|
||||
myFrameworkCheckbox.setText("Install " + frameworkName);
|
||||
myFrameworkCheckbox.setMnemonic(frameworkName.charAt(0));
|
||||
myFrameworkCheckbox.setVisible(pip != null);
|
||||
if (!myInstallFrameworkChanged) {
|
||||
myFrameworkCheckbox.setSelected(pip != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (PyExternalProcessException e) {
|
||||
myFrameworkCheckbox.setVisible(false);
|
||||
}
|
||||
if (!myFrameworkCheckbox.isSelected()) {
|
||||
setErrorText("No " + frameworkName + " support installed in selected interpreter");
|
||||
setOKActionEnabled(false);
|
||||
}
|
||||
}
|
||||
if (isPy3k && !((PyFrameworkProjectGenerator)generator).supportsPython3()) {
|
||||
setErrorText(frameworkName + " is not supported for the selected interpreter");
|
||||
setOKActionEnabled(false);
|
||||
}
|
||||
}
|
||||
if (sdk == null) {
|
||||
setOKActionEnabled(false);
|
||||
setErrorText("No Python interpreter selected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void selectCompatiblePython() {
|
||||
DirectoryProjectGenerator generator = getProjectGenerator();
|
||||
if (generator instanceof PyFrameworkProjectGenerator && !((PyFrameworkProjectGenerator)generator).supportsPython3()) {
|
||||
Sdk sdk = getSdk();
|
||||
if (sdk != null && PythonSdkType.getLanguageLevelForSdk(sdk).isPy3K()) {
|
||||
Sdk python2Sdk = PythonSdkType.findPython2Sdk(null);
|
||||
if (python2Sdk != null) {
|
||||
mySdkCombo.getComboBox().setSelectedItem(python2Sdk);
|
||||
mySdkCombo.getComboBox().repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFrameworkInstalled(Sdk sdk) {
|
||||
PyFrameworkProjectGenerator projectGenerator = (PyFrameworkProjectGenerator)getProjectGenerator();
|
||||
|
||||
return projectGenerator != null && projectGenerator.isFrameworkInstalled(myProject, sdk);
|
||||
}
|
||||
|
||||
private static boolean acceptsRemoteSdk(DirectoryProjectGenerator generator) {
|
||||
if (generator instanceof PyFrameworkProjectGenerator) {
|
||||
return ((PyFrameworkProjectGenerator)generator).acceptsRemoteSdk();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean installFramework() {
|
||||
return myFrameworkCheckbox.isSelected() && myFrameworkCheckbox.isVisible();
|
||||
}
|
||||
|
||||
public Sdk getSdk() {
|
||||
return (Sdk)mySdkCombo.getComboBox().getSelectedItem();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user