applied patch for student-python dependencies sincerely provided by Greg Shrago

new project panel steps and generator moved to the python-community-configure so it can be used in other modules without python-community-ide dependency
This commit is contained in:
Ekaterina Tuzova
2016-12-02 11:56:44 +03:00
parent d280fe0453
commit 33b887c1e1
14 changed files with 20 additions and 21 deletions
@@ -0,0 +1,45 @@
/*
* Copyright 2000-2016 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.steps;
import com.intellij.icons.AllIcons;
import com.intellij.ide.util.projectWizard.ProjectSettingsStepBase;
import com.intellij.ide.util.projectWizard.actions.ProjectSpecificAction;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.project.DumbAware;
import com.intellij.platform.CustomStepProjectGenerator;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.util.NullableConsumer;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class PluginSpecificProjectsStep extends DefaultActionGroup implements DumbAware {
public PluginSpecificProjectsStep(@NotNull final NullableConsumer<ProjectSettingsStepBase> callback,
@NotNull final List<DirectoryProjectGenerator> projectGenerators) {
super("Plugin-specific", true);
getTemplatePresentation().setIcon(AllIcons.Nodes.PluginLogo);
for (DirectoryProjectGenerator generator : projectGenerators) {
ProjectSettingsStepBase step = generator instanceof CustomStepProjectGenerator ?
((ProjectSettingsStepBase)((CustomStepProjectGenerator)generator).createStep(generator, callback)) :
new ProjectSpecificSettingsStep(generator, callback);
ProjectSpecificAction action = new ProjectSpecificAction(generator, step);
addAll(action.getChildren(null));
}
}
}
@@ -0,0 +1,461 @@
/*
* Copyright 2000-2016 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.steps;
import com.intellij.execution.ExecutionException;
import com.intellij.facet.ui.ValidationResult;
import com.intellij.ide.util.projectWizard.ProjectSettingsStepBase;
import com.intellij.ide.util.projectWizard.WebProjectTemplate;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.ui.*;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.HideableDecorator;
import com.intellij.ui.TextAccessor;
import com.intellij.util.NullableConsumer;
import com.intellij.util.PathUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.update.UiNotifyConnector;
import com.jetbrains.python.configuration.PyConfigurableInterpreterList;
import com.jetbrains.python.configuration.VirtualEnvProjectFilter;
import com.jetbrains.python.newProject.PyFrameworkProjectGenerator;
import com.jetbrains.python.newProject.PythonProjectGenerator;
import com.jetbrains.python.packaging.PyPackage;
import com.jetbrains.python.packaging.PyPackageManager;
import com.jetbrains.python.packaging.PyPackageUtil;
import com.jetbrains.python.remote.PyProjectSynchronizer;
import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
import com.jetbrains.python.sdk.PySdkUtil;
import com.jetbrains.python.sdk.PythonSdkType;
import icons.PythonIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase implements DumbAware {
private static final Logger LOGGER = Logger.getInstance(ProjectSpecificSettingsStep.class);
private PythonSdkChooserCombo mySdkCombo;
private boolean myInstallFramework;
private Sdk mySdk;
/**
* For remote projects path for project on remote side
*/
private PyRemotePathField myRemotePathField;
/**
* If remote path required for project creation or not
*/
private boolean myRemotePathRequired;
public ProjectSpecificSettingsStep(@NotNull final DirectoryProjectGenerator projectGenerator,
@NotNull final NullableConsumer<ProjectSettingsStepBase> callback) {
super(projectGenerator, callback);
}
private void acceptsSdk(@NotNull final DirectoryProjectGenerator<?> generator,
@NotNull final Sdk sdk,
@NotNull final File projectDirectory) throws PythonProjectGenerator.PyNoProjectAllowedOnSdkException {
if (generator instanceof PythonProjectGenerator) {
((PythonProjectGenerator<?>)generator).checkProjectCanBeCreatedOnSdk(sdk, projectDirectory);
}
}
@Override
protected JPanel createAndFillContentPanel() {
if (myProjectGenerator instanceof PythonProjectGenerator) {
// Allow generator to display custom error
((PythonProjectGenerator<?>)myProjectGenerator).setErrorCallback(this::setErrorText);
}
return createContentPanelWithAdvancedSettingsPanel();
}
@Override
@Nullable
protected JPanel createAdvancedSettings() {
JComponent advancedSettings = null;
if (myProjectGenerator instanceof PythonProjectGenerator) {
advancedSettings = ((PythonProjectGenerator)myProjectGenerator).getSettingsPanel(myProjectDirectory);
}
else if (myProjectGenerator instanceof WebProjectTemplate) {
advancedSettings = ((WebProjectTemplate)myProjectGenerator).getPeer().getComponent();
}
if (advancedSettings != null) {
final JPanel jPanel = new JPanel(new VerticalFlowLayout());
final HideableDecorator deco = new HideableDecorator(jPanel, "Mor&e Settings", false);
boolean isValid = checkValid();
deco.setOn(!isValid);
if (myProjectGenerator instanceof PythonProjectGenerator && !deco.isExpanded()) {
final ValidationResult result = ((PythonProjectGenerator)myProjectGenerator).warningValidation(getSdk());
deco.setOn(!result.isOk());
}
deco.setContentComponent(advancedSettings);
return jPanel;
}
return null;
}
@Nullable
public Sdk getSdk() {
if (!(myProjectGenerator instanceof PythonProjectGenerator)) return null;
if (mySdk != null) return mySdk;
if (((PythonProjectGenerator)myProjectGenerator).hideInterpreter()) return null;
return (Sdk)mySdkCombo.getComboBox().getSelectedItem();
}
public void setSdk(final Sdk sdk) {
mySdk = sdk;
}
public boolean installFramework() {
return myInstallFramework;
}
@Override
protected void registerValidators() {
super.registerValidators();
if (myProjectGenerator instanceof PythonProjectGenerator && !((PythonProjectGenerator)myProjectGenerator).hideInterpreter()) {
myLocationField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
final String path = myLocationField.getText().trim();
((PythonProjectGenerator)myProjectGenerator).locationChanged(PathUtil.getFileName(path));
}
});
mySdkCombo.getComboBox().addItemListener(e -> {
if (e.getStateChange() == ItemEvent.SELECTED) {
final Runnable checkValidOnSwing = () -> ApplicationManager.getApplication().invokeLater(this::checkValid);
ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> {
try {
// Refresh before validation to make sure no stale data
final Sdk sdk = getSdk();
if (sdk == null) {
return;
}
final boolean noPackages = PyPackageManager.getInstance(sdk).refreshAndGetPackages(true).isEmpty();
if (noPackages) {
LOGGER.warn(String.format("No packages on %s", sdk.getHomePath()));
}
checkValidOnSwing.run();
}
catch (final ExecutionException exception) {
LOGGER.warn(exception);
checkValidOnSwing.run();
}
}, "Refreshing List of Packages, Please Wait", false, null);
}
});
UiNotifyConnector.doWhenFirstShown(mySdkCombo, this::checkValid);
}
}
/**
* @return path for project on remote side provided by user
*/
@Nullable
final String getRemotePath() {
final PyRemotePathField field = myRemotePathField;
return (field != null ? field.getTextField().getText() : null);
}
@Override
protected void initGeneratorListeners() {
super.initGeneratorListeners();
if (myProjectGenerator instanceof PythonProjectGenerator) {
((PythonProjectGenerator)myProjectGenerator).addSettingsStateListener(this::checkValid);
myErrorLabel.addMouseListener(((PythonProjectGenerator)myProjectGenerator).getErrorLabelMouseListener());
}
}
@Override
public boolean checkValid() {
myInstallFramework = false;
if (!super.checkValid()) {
return false;
}
if (myProjectGenerator instanceof PythonProjectGenerator) {
final Sdk sdk = getSdk();
if (sdk == null) {
if (!((PythonProjectGenerator)myProjectGenerator).hideInterpreter()) {
setErrorText("No Python interpreter selected");
return false;
}
return true;
}
if (PythonSdkType.isInvalid(sdk)) {
setErrorText("Choose valid python interpreter");
return false;
}
final List<String> warningList = new ArrayList<>();
final boolean isPy3k = PythonSdkType.getLanguageLevelForSdk(sdk).isPy3K();
try {
acceptsSdk(myProjectGenerator, sdk, new File(myLocationField.getText()));
}
catch (final PythonProjectGenerator.PyNoProjectAllowedOnSdkException e) {
setErrorText(e.getMessage());
return false;
}
if (myRemotePathRequired && StringUtil.isEmpty(myRemotePathField.getTextField().getText())) {
setErrorText("Remote path not provided");
return false;
}
if (myProjectGenerator instanceof PyFrameworkProjectGenerator) {
PyFrameworkProjectGenerator frameworkProjectGenerator = (PyFrameworkProjectGenerator)myProjectGenerator;
String frameworkName = frameworkProjectGenerator.getFrameworkTitle();
if (!isFrameworkInstalled(sdk)) {
if (PyPackageUtil.packageManagementEnabled(sdk)) {
myInstallFramework = true;
final List<PyPackage> packages = PyPackageUtil.refreshAndGetPackagesModally(sdk);
if (packages == null) {
warningList.add(frameworkName + " will be installed on the selected interpreter");
return false;
}
if (!PyPackageUtil.hasManagement(packages)) {
warningList.add("Python packaging tools and " + frameworkName + " will be installed on the selected interpreter");
}
else {
warningList.add(frameworkName + " will be installed on the selected interpreter");
}
}
else {
warningList.add(frameworkName + " is not installed on the selected interpreter");
}
}
final ValidationResult warningResult = ((PythonProjectGenerator)myProjectGenerator).warningValidation(sdk);
if (!warningResult.isOk()) {
warningList.add(warningResult.getErrorMessage());
}
if (!warningList.isEmpty()) {
final String warning = StringUtil.join(warningList, "<br/>");
setWarningText(warning);
}
if (isPy3k && !((PyFrameworkProjectGenerator)myProjectGenerator).supportsPython3()) {
setErrorText(frameworkName + " is not supported for the selected interpreter");
return false;
}
}
}
return true;
}
private boolean isFrameworkInstalled(Sdk sdk) {
PyFrameworkProjectGenerator projectGenerator = (PyFrameworkProjectGenerator)getProjectGenerator();
return projectGenerator != null && projectGenerator.isFrameworkInstalled(sdk);
}
@Override
protected JPanel createBasePanel() {
if (myProjectGenerator instanceof PythonProjectGenerator) {
final BorderLayout layout = new BorderLayout();
final JPanel locationPanel = new JPanel(layout);
final JPanel panel = new JPanel(new VerticalFlowLayout(0, 2));
final LabeledComponent<TextFieldWithBrowseButton> location = createLocationComponent();
locationPanel.add(location, BorderLayout.CENTER);
panel.add(locationPanel);
if (((PythonProjectGenerator)myProjectGenerator).hideInterpreter()) {
addInterpreterButton(locationPanel, location);
}
else {
final LabeledComponent<PythonSdkChooserCombo> labeled = createInterpreterCombo();
UIUtil.mergeComponentsWithAnchor(labeled, location);
panel.add(labeled);
}
final PythonRemoteInterpreterManager remoteInterpreterManager = PythonRemoteInterpreterManager.getInstance();
final Sdk sdk = getSdk();
if (remoteInterpreterManager != null && sdk != null) {
createRemotePathField(panel, remoteInterpreterManager);
}
final JPanel basePanelExtension = ((PythonProjectGenerator)myProjectGenerator).extendBasePanel();
if (basePanelExtension != null) {
panel.add(basePanelExtension);
}
return panel;
}
return super.createBasePanel();
}
private void createRemotePathField(@NotNull final JPanel panelToAddField,
@NotNull final PythonRemoteInterpreterManager remoteInterpreterManager) {
myRemotePathField = new PyRemotePathField();
myRemotePathField.addActionListener(e -> {
final Sdk currentSdk = getSdk();
if (!PySdkUtil.isRemote(currentSdk)) {
return;
}
// If chosen SDK is remote then display
final Pair<Supplier<String>, JPanel> browserForm;
try {
browserForm = remoteInterpreterManager.createServerBrowserForm(currentSdk);
}
catch (final ExecutionException | InterruptedException ex) {
Logger.getInstance(ProjectSpecificSettingsStep.class).warn("Failed to create server browse button", ex);
JBPopupFactory.getInstance().createMessage("Failed to browse remote server. Make sure you have permissions. ").show(panelToAddField);
return;
}
if (browserForm != null) {
browserForm.second.setVisible(true);
final DialogWrapper wrapper = new MyRemoteServerBrowserDialog(browserForm.second);
if (wrapper.showAndGet()) {
myRemotePathField.getTextField().setText(browserForm.first.get());
}
}
});
mySdkCombo.addChangedListener(e -> configureMappingField(remoteInterpreterManager));
panelToAddField.add(myRemotePathField.getMainPanel());
myRemotePathField.addTextChangeListener(() -> checkValid());
configureMappingField(remoteInterpreterManager);
}
/**
* Enables or disables "remote path" based on interpreter.
*/
private void configureMappingField(@NotNull final PythonRemoteInterpreterManager remoteInterpreterManager) {
if (myRemotePathField == null) {
return;
}
final JPanel mainPanel = myRemotePathField.getMainPanel();
final PyProjectSynchronizer synchronizer = getSynchronizer(remoteInterpreterManager);
if (synchronizer != null) {
final String defaultRemotePath = synchronizer.getDefaultRemotePath();
final boolean mappingRequired = defaultRemotePath != null;
mainPanel.setVisible(mappingRequired);
final TextAccessor textField = myRemotePathField.getTextField();
if (mappingRequired && StringUtil.isEmpty(textField.getText())) {
textField.setText(defaultRemotePath);
}
myRemotePathRequired = mappingRequired;
}
else {
mainPanel.setVisible(false);
myRemotePathRequired = false;
}
}
@Nullable
private PyProjectSynchronizer getSynchronizer(@NotNull final PythonRemoteInterpreterManager manager) {
final Sdk sdk = getSdk();
if (sdk == null) {
return null;
}
return manager.getSynchronizer(sdk);
}
private void addInterpreterButton(final JPanel locationPanel, final LabeledComponent<TextFieldWithBrowseButton> location) {
final JButton interpreterButton = new FixedSizeButton(location);
if (SystemInfo.isMac && !UIUtil.isUnderDarcula()) {
interpreterButton.putClientProperty("JButton.buttonType", null);
}
interpreterButton.setIcon(PythonIcons.Python.Python);
interpreterButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final DialogBuilder builder = new DialogBuilder();
final JPanel panel = new JPanel();
final LabeledComponent<PythonSdkChooserCombo> interpreterCombo = createInterpreterCombo();
if (mySdk != null) {
mySdkCombo.getComboBox().setSelectedItem(mySdk);
}
panel.add(interpreterCombo);
builder.setCenterPanel(panel);
builder.setTitle("Select Python Interpreter");
if (builder.showAndGet()) {
mySdk = (Sdk)mySdkCombo.getComboBox().getSelectedItem();
}
}
});
locationPanel.add(interpreterButton, BorderLayout.EAST);
}
@NotNull
private LabeledComponent<PythonSdkChooserCombo> createInterpreterCombo() {
final Project project = ProjectManager.getInstance().getDefaultProject();
final List<Sdk> sdks = PyConfigurableInterpreterList.getInstance(project).getAllPythonSdks();
VirtualEnvProjectFilter.removeAllAssociated(sdks);
Sdk compatibleSdk = sdks.isEmpty() ? null : sdks.iterator().next();
DirectoryProjectGenerator generator = getProjectGenerator();
if (generator instanceof PyFrameworkProjectGenerator && !((PyFrameworkProjectGenerator)generator).supportsPython3()) {
if (compatibleSdk != null && PythonSdkType.getLanguageLevelForSdk(compatibleSdk).isPy3K()) {
Sdk python2Sdk = PythonSdkType.findPython2Sdk(sdks);
if (python2Sdk != null) {
compatibleSdk = python2Sdk;
}
}
}
final Sdk preferred = compatibleSdk;
mySdkCombo = new PythonSdkChooserCombo(project, sdks, sdk -> sdk == preferred);
if (SystemInfo.isMac && !UIUtil.isUnderDarcula()) {
mySdkCombo.putClientProperty("JButton.buttonType", null);
}
mySdkCombo.setButtonIcon(PythonIcons.Python.InterpreterGear);
return LabeledComponent.create(mySdkCombo, "Interpreter", BorderLayout.WEST);
}
/**
* Dialog to display remote server browser
*/
private static class MyRemoteServerBrowserDialog extends DialogWrapper {
private final JPanel myBrowserForm;
MyRemoteServerBrowserDialog(@NotNull final JPanel browserForm) {
super(true);
myBrowserForm = browserForm;
init();
}
@Nullable
@Override
protected JComponent createCenterPanel() {
return myBrowserForm;
}
}
}
@@ -0,0 +1,108 @@
/*
* Copyright 2000-2016 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.steps;
import com.google.common.collect.Lists;
import com.intellij.ide.util.projectWizard.AbstractNewProjectStep;
import com.intellij.ide.util.projectWizard.ProjectSettingsStepBase;
import com.intellij.ide.util.projectWizard.actions.ProjectSpecificAction;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.util.NullableConsumer;
import com.jetbrains.python.newProject.PyFrameworkProjectGenerator;
import com.jetbrains.python.newProject.PythonProjectGenerator;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
public class PyCharmNewProjectStep extends AbstractNewProjectStep {
public PyCharmNewProjectStep() {
super(new Customization());
}
public PyCharmNewProjectStep(@NotNull AbstractNewProjectStep.Customization customization) {
super(customization);
}
protected static class Customization extends AbstractNewProjectStep.Customization {
private final List<DirectoryProjectGenerator> pluginSpecificGenerators = Lists.newArrayList();
@NotNull
@Override
protected NullableConsumer<ProjectSettingsStepBase> createCallback() {
return new PythonGenerateProjectCallback();
}
@NotNull
@Override
protected DirectoryProjectGenerator createEmptyProjectGenerator() {
return new PythonBaseProjectGenerator();
}
@NotNull
@Override
protected ProjectSettingsStepBase createProjectSpecificSettingsStep(@NotNull DirectoryProjectGenerator projectGenerator,
@NotNull NullableConsumer<ProjectSettingsStepBase> callback) {
return new ProjectSpecificSettingsStep(projectGenerator, callback);
}
@NotNull
@Override
protected DirectoryProjectGenerator[] getProjectGenerators() {
DirectoryProjectGenerator[] generators = super.getProjectGenerators();
Arrays.sort(generators, (o1, o2) -> {
if (o1 instanceof PyFrameworkProjectGenerator && !(o2 instanceof PyFrameworkProjectGenerator)) return -1;
if (!(o1 instanceof PyFrameworkProjectGenerator) && o2 instanceof PyFrameworkProjectGenerator) return 1;
return o1.getName().compareTo(o2.getName());
});
return generators;
}
@Override
public void setUpBasicAction(@NotNull ProjectSpecificAction projectSpecificAction, @NotNull DirectoryProjectGenerator[] generators) {
if (generators.length == 0) {
projectSpecificAction.setPopup(false);
}
}
@NotNull
@Override
public AnAction[] getActions(@NotNull DirectoryProjectGenerator generator, @NotNull NullableConsumer<ProjectSettingsStepBase> callback) {
if (generator instanceof PythonProjectGenerator) {
ProjectSpecificAction group =
new ProjectSpecificAction(generator, new ProjectSpecificSettingsStep(generator, new PythonGenerateProjectCallback()));
return group.getChildren(null);
}
else {
pluginSpecificGenerators.add(generator);
return AnAction.EMPTY_ARRAY;
}
}
@NotNull
@Override
public AnAction[] getExtraActions(@NotNull NullableConsumer<ProjectSettingsStepBase> callback) {
if (!pluginSpecificGenerators.isEmpty()) {
PluginSpecificProjectsStep step = new PluginSpecificProjectsStep(callback, pluginSpecificGenerators);
return step.getChildren(null);
}
return AnAction.EMPTY_ARRAY;
}
}
}
@@ -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.newProject.steps.PyRemotePathField">
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="2" 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>
<vspacer id="8ed31">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="33807" 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>
<text value="Remote project location:"/>
</properties>
</component>
<component id="b1f9a" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myLocationField">
<constraints>
<grid row="0" 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>
</children>
</grid>
</form>
@@ -0,0 +1,76 @@
/*
* Copyright 2000-2016 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.steps;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.ui.TextAccessor;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.event.ActionListener;
/**
* Field for remote path with "browse" button
*
* @author Ilya.Kazakevich
*/
public final class PyRemotePathField {
private JPanel myMainPanel;
private TextFieldWithBrowseButton myLocationField;
@NotNull
JPanel getMainPanel() {
return myMainPanel;
}
/**
* Add listener for "browse" button
*/
void addActionListener(@NotNull final ActionListener listener) {
myLocationField.addActionListener(listener);
}
/**
* @param runnable to be called when text in textfield changed
*/
void addTextChangeListener(@NotNull final Runnable runnable) {
myLocationField.getTextField().getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
runnable.run();
}
@Override
public void removeUpdate(DocumentEvent e) {
runnable.run();
}
@Override
public void changedUpdate(DocumentEvent e) {
runnable.run();
}
});
}
/**
* @return test field with remote path
*/
@NotNull
TextAccessor getTextField() {
return myLocationField;
}
}
@@ -0,0 +1,84 @@
/*
* Copyright 2000-2016 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.steps;
import com.intellij.facet.ui.ValidationResult;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.python.newProject.PyNewProjectSettings;
import com.jetbrains.python.newProject.PythonProjectGenerator;
import com.jetbrains.python.remote.PyProjectSynchronizer;
import icons.PythonIcons;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.File;
public class PythonBaseProjectGenerator extends PythonProjectGenerator<PyNewProjectSettings> {
public PythonBaseProjectGenerator() {
super(true);
}
@NotNull
@Nls
@Override
public String getName() {
return "Pure Python";
}
@Override
@Nullable
public JComponent getSettingsPanel(File baseDir) throws ProcessCanceledException {
return null;
}
@Override
public Object getProjectSettings() {
return new PyNewProjectSettings();
}
@Nullable
@Override
public Icon getLogo() {
return PythonIcons.Python.Python_logo;
}
@Override
public void configureProject(@NotNull final Project project, @NotNull VirtualFile baseDir, @NotNull final PyNewProjectSettings settings,
@NotNull final Module module, @Nullable final PyProjectSynchronizer synchronizer) {
// Super should be called according to its contract unless we sync project explicitly (we do not, so we call super)
super.configureProject(project, baseDir, settings, module, synchronizer);
ApplicationManager.getApplication().runWriteAction(() -> ModuleRootModificationUtil.setModuleSdk(module, settings.getSdk()));
}
@NotNull
@Override
public ValidationResult validate(@NotNull String baseDirPath) {
/*if (PythonSdkType.isRemote(myProjectAction.getSdk())) {
if (PythonRemoteInterpreterManager.getInstance() == null) {
return new ValidationResult(PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED);
}
}*/
return ValidationResult.OK;
}
}
@@ -0,0 +1,139 @@
/*
* Copyright 2000-2016 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.steps;
import com.intellij.ide.util.projectWizard.AbstractNewProjectStep;
import com.intellij.ide.util.projectWizard.ProjectSettingsStepBase;
import com.intellij.ide.util.projectWizard.WebProjectTemplate;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.options.ConfigurationException;
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.roots.ui.configuration.projectRoot.ProjectSdksModel;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.util.BooleanFunction;
import com.intellij.util.NullableConsumer;
import com.jetbrains.python.configuration.PyConfigurableInterpreterList;
import com.jetbrains.python.newProject.PyNewProjectSettings;
import com.jetbrains.python.newProject.PythonProjectGenerator;
import com.jetbrains.python.sdk.PyDetectedSdk;
import com.jetbrains.python.sdk.PythonSdkAdditionalData;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.sdk.PythonSdkUpdater;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class PythonGenerateProjectCallback implements NullableConsumer<ProjectSettingsStepBase> {
private static final Logger LOG = Logger.getInstance(PythonGenerateProjectCallback.class);
@Override
public void consume(@Nullable ProjectSettingsStepBase step) {
if (!(step instanceof ProjectSpecificSettingsStep)) return;
final ProjectSpecificSettingsStep settingsStep = (ProjectSpecificSettingsStep)step;
final DirectoryProjectGenerator generator = settingsStep.getProjectGenerator();
Sdk sdk = settingsStep.getSdk();
if (sdk instanceof PyDetectedSdk) {
sdk = addDetectedSdk(settingsStep, sdk);
}
if (generator instanceof PythonProjectGenerator) {
final BooleanFunction<PythonProjectGenerator> beforeProjectGenerated = ((PythonProjectGenerator)generator).beforeProjectGenerated(sdk);
if (beforeProjectGenerated != null) {
final boolean result = beforeProjectGenerated.fun((PythonProjectGenerator)generator);
if (!result) {
Messages.showWarningDialog("Project can not be generated", "Error in Project Generation");
}
}
}
final Project newProject = generateProject(settingsStep);
if (generator instanceof PythonProjectGenerator && sdk == null) {
final PyNewProjectSettings settings = (PyNewProjectSettings)((PythonProjectGenerator)generator).getProjectSettings();
((PythonProjectGenerator)generator).createAndAddVirtualEnv(newProject, settings);
sdk = settings.getSdk();
}
if (newProject != null && generator instanceof PythonProjectGenerator) {
SdkConfigurationUtil.setDirectoryProjectSdk(newProject, sdk);
final List<Sdk> sdks = PythonSdkType.getAllSdks();
for (Sdk s : sdks) {
final SdkAdditionalData additionalData = s.getSdkAdditionalData();
if (additionalData instanceof PythonSdkAdditionalData) {
((PythonSdkAdditionalData)additionalData).reassociateWithCreatedProject(newProject);
}
}
}
}
private static Sdk addDetectedSdk(ProjectSpecificSettingsStep settingsStep, Sdk sdk) {
final Project project = ProjectManager.getInstance().getDefaultProject();
final ProjectSdksModel model = PyConfigurableInterpreterList.getInstance(project).getModel();
final String name = sdk.getName();
VirtualFile sdkHome = ApplicationManager.getApplication().runWriteAction((Computable<VirtualFile>)() -> LocalFileSystem.getInstance().refreshAndFindFileByPath(name));
sdk = SdkConfigurationUtil.createAndAddSDK(sdkHome.getPath(), PythonSdkType.getInstance());
if (sdk != null) {
PythonSdkUpdater.updateOrShowError(sdk, null, project, null);
}
model.addSdk(sdk);
settingsStep.setSdk(sdk);
try {
model.apply();
}
catch (ConfigurationException exception) {
LOG.error("Error adding detected python interpreter " + exception.getMessage());
}
return sdk;
}
@Nullable
private static Project generateProject(@NotNull final ProjectSettingsStepBase settings) {
final DirectoryProjectGenerator generator = settings.getProjectGenerator();
final String location = FileUtil.expandUserHome(settings.getProjectLocation());
return AbstractNewProjectStep.doGenerateProject(ProjectManager.getInstance().getDefaultProject(), location, generator,
file -> computeProjectSettings(generator, (ProjectSpecificSettingsStep)settings));
}
public static Object computeProjectSettings(DirectoryProjectGenerator<?> generator, final ProjectSpecificSettingsStep settings) {
Object projectSettings = null;
if (generator instanceof PythonProjectGenerator) {
final PythonProjectGenerator<?> projectGenerator = (PythonProjectGenerator<?>)generator;
projectSettings = projectGenerator.getProjectSettings();
}
else if (generator instanceof WebProjectTemplate) {
projectSettings = ((WebProjectTemplate<?>)generator).getPeer().getSettings();
}
if (projectSettings instanceof PyNewProjectSettings) {
final PyNewProjectSettings newProjectSettings = (PyNewProjectSettings)projectSettings;
newProjectSettings.setSdk(settings.getSdk());
newProjectSettings.setInstallFramework(settings.installFramework());
newProjectSettings.setRemotePath(settings.getRemotePath());
}
return projectSettings;
}
}
@@ -0,0 +1,106 @@
/*
* Copyright 2000-2016 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.steps;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.options.ConfigurationException;
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.ui.ComboBox;
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.PyConfigurableInterpreterList;
import com.jetbrains.python.sdk.PySdkListCellRenderer;
import com.jetbrains.python.sdk.PythonSdkDetailsStep;
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();
private static final Logger LOG = Logger.getInstance(PythonSdkChooserCombo.class);
@SuppressWarnings("unchecked")
public PythonSdkChooserCombo(final Project project, List<Sdk> sdks, final Condition<Sdk> acceptableSdkCondition) {
super(new ComboBox<>());
final Sdk initialSelection = ContainerUtil.find(sdks, acceptableSdkCondition);
final JComboBox comboBox = getComboBox();
comboBox.setModel(new CollectionComboBoxModel(sdks, initialSelection));
comboBox.setRenderer(new PySdkListCellRenderer(true));
addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showOptions(project);
notifyChanged(e);
}
});
comboBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
notifyChanged(e);
updateTooltip();
}
});
updateTooltip();
}
private void updateTooltip() {
final Object item = getComboBox().getSelectedItem();
getComboBox().setToolTipText(item instanceof Sdk ? ((Sdk)item).getHomePath() : null);
}
private void showOptions(final Project project) {
final PyConfigurableInterpreterList interpreterList = PyConfigurableInterpreterList.getInstance(project);
final Sdk[] sdks = interpreterList.getModel().getSdks();
//noinspection unchecked
final JComboBox<Sdk> comboBox = getComboBox();
final Sdk oldSelectedSdk = (Sdk)comboBox.getSelectedItem();
PythonSdkDetailsStep.show(project, sdks, null, this, getButton().getLocationOnScreen(), sdk -> {
if (sdk == null) return;
final ProjectSdksModel projectSdksModel = interpreterList.getModel();
if (projectSdksModel.findSdk(sdk) == null) {
projectSdksModel.addSdk(sdk);
try {
projectSdksModel.apply();
}
catch (ConfigurationException e) {
LOG.error("Error adding new python interpreter " + e.getMessage());
}
}
final List<Sdk> committedSdks = interpreterList.getAllPythonSdks();
final Sdk copiedSdk = interpreterList.getModel().findSdk(sdk.getName());
comboBox.setModel(new CollectionComboBoxModel<>(committedSdks, oldSelectedSdk));
comboBox.setSelectedItem(copiedSdk);
}, true);
}
private void notifyChanged(ActionEvent e) {
for (ActionListener changedListener : myChangedListeners) {
changedListener.actionPerformed(e);
}
}
@SuppressWarnings("UnusedDeclaration")
public void addChangedListener(ActionListener listener) {
myChangedListeners.add(listener);
}
}