mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
PY-14036: Support remote Django (and other) project creation
* See PyProjectSynchronizer for entry point * DownloadAction refactored to extract download * VagrantSupportImpl refactored to fetch mapped folders
This commit is contained in:
@@ -21,6 +21,7 @@ import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.PathMappingSettings;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -53,6 +54,13 @@ public abstract class VagrantSupport {
|
||||
|
||||
public abstract void runVagrant(@NotNull String vagrantFolder, @Nullable String machineName) throws ExecutionException;
|
||||
|
||||
/**
|
||||
* @param vagrantFolder folder with Vagrantfile
|
||||
* @return path mappings from vagrant file
|
||||
*/
|
||||
@Nullable
|
||||
public abstract PathMappingSettings getMappedFolders(@NotNull String vagrantFolder);
|
||||
|
||||
public abstract Collection<? extends RemoteConnector> getVagrantInstancesConnectors(@NotNull Project project);
|
||||
|
||||
public abstract boolean isVagrantInstance(VirtualFile dir);
|
||||
|
||||
@@ -13,7 +13,6 @@ import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.platform.DirectoryProjectGenerator;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.jetbrains.edu.coursecreator.actions.CCCreateLesson;
|
||||
@@ -27,6 +26,7 @@ import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
import com.jetbrains.python.newProject.PyNewProjectSettings;
|
||||
import com.jetbrains.python.newProject.PythonProjectGenerator;
|
||||
import com.jetbrains.python.remote.PyProjectSynchronizer;
|
||||
import icons.CourseCreatorPythonIcons;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -38,7 +38,7 @@ import java.io.File;
|
||||
import static com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator.OUR_COURSES_DIR;
|
||||
|
||||
|
||||
public class PyCCProjectGenerator extends PythonProjectGenerator<PyNewProjectSettings> {
|
||||
public class PyCCProjectGenerator extends PythonProjectGenerator<PyNewProjectSettings> {
|
||||
private static final Logger LOG = Logger.getInstance(PyCCProjectGenerator.class);
|
||||
private CCNewProjectPanel mySettingsPanel;
|
||||
|
||||
@@ -57,7 +57,9 @@ public class PyCCProjectGenerator extends PythonProjectGenerator<PyNewProjectSet
|
||||
|
||||
@Override
|
||||
public void configureProject(@NotNull final Project project, @NotNull final VirtualFile baseDir,
|
||||
@NotNull PyNewProjectSettings settings, @NotNull Module module) {
|
||||
@NotNull PyNewProjectSettings settings,
|
||||
@NotNull Module module,
|
||||
@Nullable final PyProjectSynchronizer synchronizer) {
|
||||
generateProject(project, baseDir, mySettingsPanel);
|
||||
}
|
||||
|
||||
@@ -84,7 +86,8 @@ public class PyCCProjectGenerator extends PythonProjectGenerator<PyNewProjectSet
|
||||
}
|
||||
|
||||
private static void createTestHelper(@NotNull Project project, PsiDirectory projectDir) {
|
||||
final FileTemplate template = FileTemplateManager.getInstance(project).getInternalTemplate(FileUtil.getNameWithoutExtension(EduNames.TEST_HELPER));
|
||||
final FileTemplate template =
|
||||
FileTemplateManager.getInstance(project).getInternalTemplate(FileUtil.getNameWithoutExtension(EduNames.TEST_HELPER));
|
||||
try {
|
||||
FileTemplateUtil.createFromTemplate(template, EduNames.TEST_HELPER, null, projectDir);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.intellij.openapi.projectRoots.SdkAdditionalData;
|
||||
import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
|
||||
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.platform.DirectoryProjectGenerator;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.util.BooleanFunction;
|
||||
@@ -34,6 +33,7 @@ import com.jetbrains.python.newProject.PyNewProjectSettings;
|
||||
import com.jetbrains.python.newProject.PythonProjectGenerator;
|
||||
import com.jetbrains.python.packaging.PyPackageManager;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.remote.PyProjectSynchronizer;
|
||||
import com.jetbrains.python.sdk.AbstractCreateVirtualEnvDialog;
|
||||
import com.jetbrains.python.sdk.PyDetectedSdk;
|
||||
import com.jetbrains.python.sdk.PythonSdkAdditionalData;
|
||||
@@ -118,7 +118,9 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator<PyN
|
||||
|
||||
@Override
|
||||
public void configureProject(@NotNull final Project project, @NotNull final VirtualFile baseDir,
|
||||
@NotNull PyNewProjectSettings settings, @NotNull Module module) {
|
||||
@NotNull PyNewProjectSettings settings,
|
||||
@NotNull Module module,
|
||||
@Nullable PyProjectSynchronizer synchronizer) {
|
||||
myGenerator.generateProject(project, baseDir);
|
||||
final String testHelper = "test_helper.py";
|
||||
if (baseDir.findChild(testHelper) != null) return;
|
||||
|
||||
15
python/helpers/pycharm/_jb_create_folder.py
Normal file
15
python/helpers/pycharm/_jb_create_folder.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""
|
||||
Accepts folder, creates (if does not exist) it and checks it is writable.
|
||||
Empty output if ok. Error in stderr otherwise
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
folder = sys.argv[1]
|
||||
|
||||
d = os.path.dirname(folder)
|
||||
if not os.path.exists(folder):
|
||||
os.makedirs(folder)
|
||||
|
||||
if not os.access(folder, os.W_OK):
|
||||
raise Exception("Dir {0} is not writable".format(folder))
|
||||
12
python/helpers/pycharm/_jb_django_project_creator.py
Normal file
12
python/helpers/pycharm/_jb_django_project_creator.py
Normal file
@@ -0,0 +1,12 @@
|
||||
"""
|
||||
Accepts 2 args: project name and dir where one should be created
|
||||
"""
|
||||
import sys, os
|
||||
from django.core import management
|
||||
|
||||
project_name = sys.argv[1]
|
||||
path = sys.argv[2]
|
||||
if not os.path.exists(path):
|
||||
os.mkdir(path)
|
||||
|
||||
management.execute_from_command_line(argv=["django-admin", "startproject", project_name, path])
|
||||
@@ -22,9 +22,7 @@ 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.intellij.remote.RemoteSdkCredentials;
|
||||
import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
|
||||
import com.jetbrains.python.remote.RemoteProjectSettings;
|
||||
import com.jetbrains.python.remote.PyProjectSynchronizer;
|
||||
import icons.PythonIcons;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -33,7 +31,12 @@ import org.jetbrains.annotations.Nullable;
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
|
||||
public class PythonBaseProjectGenerator extends PythonProjectGenerator<PyNewProjectSettings> {
|
||||
public class PythonBaseProjectGenerator extends PythonProjectGenerator<PyNewProjectSettings> {
|
||||
|
||||
public PythonBaseProjectGenerator() {
|
||||
super(true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Nls
|
||||
@Override
|
||||
@@ -59,19 +62,11 @@ public class PythonBaseProjectGenerator extends PythonProjectGenerator<PyNewProj
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureProject(@NotNull final Project project,
|
||||
@NotNull VirtualFile baseDir,
|
||||
@Nullable final PyNewProjectSettings settings,
|
||||
@NotNull final Module module) {
|
||||
if (settings instanceof RemoteProjectSettings) {
|
||||
PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
|
||||
assert manager != null;
|
||||
manager.createDeployment(project, baseDir, (RemoteProjectSettings)settings,
|
||||
(RemoteSdkCredentials)settings.getSdk().getSdkAdditionalData());
|
||||
}
|
||||
else if (settings != null) {
|
||||
ApplicationManager.getApplication().runWriteAction(() -> ModuleRootModificationUtil.setModuleSdk(module, settings.getSdk()));
|
||||
}
|
||||
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
|
||||
|
||||
@@ -15,19 +15,26 @@
|
||||
*/
|
||||
package com.jetbrains.python.newProject.actions;
|
||||
|
||||
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;
|
||||
@@ -38,7 +45,11 @@ 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;
|
||||
@@ -50,28 +61,44 @@ 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 static boolean acceptsRemoteSdk(DirectoryProjectGenerator generator) {
|
||||
if (generator instanceof PyFrameworkProjectGenerator) {
|
||||
return ((PyFrameworkProjectGenerator)generator).acceptsRemoteSdk();
|
||||
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);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JPanel createAndFillContentPanel() {
|
||||
if (myProjectGenerator instanceof PythonProjectGenerator) {
|
||||
// Allow generator to display custom error
|
||||
((PythonProjectGenerator<?>)myProjectGenerator).setErrorCallback(this::setErrorText);
|
||||
}
|
||||
return createContentPanelWithAdvancedSettingsPanel();
|
||||
}
|
||||
|
||||
@@ -79,8 +106,9 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme
|
||||
@Nullable
|
||||
protected JPanel createAdvancedSettings() {
|
||||
JComponent advancedSettings = null;
|
||||
if (myProjectGenerator instanceof PythonProjectGenerator)
|
||||
if (myProjectGenerator instanceof PythonProjectGenerator) {
|
||||
advancedSettings = ((PythonProjectGenerator)myProjectGenerator).getSettingsPanel(myProjectDirectory);
|
||||
}
|
||||
else if (myProjectGenerator instanceof WebProjectTemplate) {
|
||||
advancedSettings = ((WebProjectTemplate)myProjectGenerator).getPeer().getComponent();
|
||||
}
|
||||
@@ -99,6 +127,7 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Sdk getSdk() {
|
||||
if (!(myProjectGenerator instanceof PythonProjectGenerator)) return null;
|
||||
if (mySdk != null) return mySdk;
|
||||
@@ -125,16 +154,43 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme
|
||||
((PythonProjectGenerator)myProjectGenerator).locationChanged(PathUtil.getFileName(path));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
mySdkCombo.getComboBox().addItemListener(e -> {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
checkValid();
|
||||
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();
|
||||
@@ -160,17 +216,25 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (PythonSdkType.isInvalid(sdk)) {
|
||||
if (PythonSdkType.isInvalid(sdk)) {
|
||||
setErrorText("Choose valid python interpreter");
|
||||
return false;
|
||||
}
|
||||
final List<String> warningList = new ArrayList<>();
|
||||
final boolean isPy3k = PythonSdkType.getLanguageLevelForSdk(sdk).isPy3K();
|
||||
if (PythonSdkType.isRemote(sdk) && !acceptsRemoteSdk(myProjectGenerator)) {
|
||||
setErrorText("Please choose a local interpreter");
|
||||
try {
|
||||
acceptsSdk(myProjectGenerator, sdk, new File(myLocationField.getText()));
|
||||
}
|
||||
catch (final PythonProjectGenerator.PyNoProjectAllowedOnSdkException e) {
|
||||
setErrorText(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
else if (myProjectGenerator instanceof PyFrameworkProjectGenerator) {
|
||||
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)) {
|
||||
@@ -186,9 +250,9 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme
|
||||
}
|
||||
else {
|
||||
warningList.add(frameworkName + " will be installed on the selected interpreter");
|
||||
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
warningList.add(frameworkName + " is not installed on the selected interpreter");
|
||||
}
|
||||
}
|
||||
@@ -236,6 +300,14 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme
|
||||
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);
|
||||
@@ -246,10 +318,81 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme
|
||||
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())
|
||||
if (SystemInfo.isMac && !UIUtil.isUnderDarcula()) {
|
||||
interpreterButton.putClientProperty("JButton.buttonType", null);
|
||||
}
|
||||
interpreterButton.setIcon(PythonIcons.Python.Python);
|
||||
interpreterButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
@@ -289,10 +432,31 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme
|
||||
|
||||
final Sdk preferred = compatibleSdk;
|
||||
mySdkCombo = new PythonSdkChooserCombo(project, sdks, sdk -> sdk == preferred);
|
||||
if (SystemInfo.isMac && !UIUtil.isUnderDarcula())
|
||||
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,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.actions.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.actions;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -119,17 +119,20 @@ public class PythonGenerateProjectCallback implements NullableConsumer<ProjectSe
|
||||
file -> computeProjectSettings(generator, (ProjectSpecificSettingsStep)settings));
|
||||
}
|
||||
|
||||
public static Object computeProjectSettings(DirectoryProjectGenerator generator, ProjectSpecificSettingsStep settings) {
|
||||
public static Object computeProjectSettings(DirectoryProjectGenerator<?> generator, final ProjectSpecificSettingsStep settings) {
|
||||
Object projectSettings = null;
|
||||
if (generator instanceof PythonProjectGenerator) {
|
||||
projectSettings = ((PythonProjectGenerator)generator).getProjectSettings();
|
||||
final PythonProjectGenerator<?> projectGenerator = (PythonProjectGenerator<?>)generator;
|
||||
projectSettings = projectGenerator.getProjectSettings();
|
||||
}
|
||||
else if (generator instanceof WebProjectTemplate) {
|
||||
projectSettings = ((WebProjectTemplate)generator).getPeer().getSettings();
|
||||
projectSettings = ((WebProjectTemplate<?>)generator).getPeer().getSettings();
|
||||
}
|
||||
if (projectSettings instanceof PyNewProjectSettings) {
|
||||
((PyNewProjectSettings)projectSettings).setSdk(settings.getSdk());
|
||||
((PyNewProjectSettings)projectSettings).setInstallFramework(settings.installFramework());
|
||||
final PyNewProjectSettings newProjectSettings = (PyNewProjectSettings)projectSettings;
|
||||
newProjectSettings.setSdk(settings.getSdk());
|
||||
newProjectSettings.setInstallFramework(settings.installFramework());
|
||||
newProjectSettings.setRemotePath(settings.getRemotePath());
|
||||
}
|
||||
return projectSettings;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,5 @@ public interface PyFrameworkProjectGenerator {
|
||||
|
||||
boolean isFrameworkInstalled(Sdk sdk);
|
||||
|
||||
boolean acceptsRemoteSdk();
|
||||
|
||||
boolean supportsPython3();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.jetbrains.python.newProject;
|
||||
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Project generation settings selected on the first page of the new project dialog.
|
||||
@@ -25,6 +26,11 @@ import com.intellij.openapi.projectRoots.Sdk;
|
||||
public class PyNewProjectSettings {
|
||||
private Sdk mySdk;
|
||||
private boolean myInstallFramework;
|
||||
/**
|
||||
* Path on remote server for remote project
|
||||
*/
|
||||
@Nullable
|
||||
private String myRemotePath;
|
||||
|
||||
public Sdk getSdk() {
|
||||
return mySdk;
|
||||
@@ -41,4 +47,13 @@ public class PyNewProjectSettings {
|
||||
public boolean installFramework() {
|
||||
return myInstallFramework;
|
||||
}
|
||||
|
||||
public final void setRemotePath(@Nullable final String remotePath) {
|
||||
myRemotePath = remotePath;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public final String getRemotePath() {
|
||||
return myRemotePath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,18 @@
|
||||
package com.jetbrains.python.newProject;
|
||||
|
||||
import com.intellij.facet.ui.ValidationResult;
|
||||
import com.intellij.icons.AllIcons.General;
|
||||
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.ui.Messages;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.platform.DirectoryProjectGenerator;
|
||||
import com.intellij.util.BooleanFunction;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import com.jetbrains.python.remote.*;
|
||||
import com.jetbrains.python.sdk.PySdkUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -33,18 +35,40 @@ import javax.swing.*;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* This class encapsulates remote settings, so one should extend it for any python project that supports remote generation, at least
|
||||
* Instead of {@link #generateProject(Project, VirtualFile, PyNewProjectSettings, Module)} inheritor shall use
|
||||
* {@link #configureProject(Project, VirtualFile, PyNewProjectSettings, Module)}
|
||||
* {@link #configureProject(Project, VirtualFile, PyNewProjectSettings, Module, PyProjectSynchronizer)}*
|
||||
* <br/>
|
||||
* If your project does not support remote projects generation, be sure to set flag in ctor:{@link #PythonProjectGenerator(boolean)}
|
||||
* <br/>
|
||||
*
|
||||
* @param <T> project settings
|
||||
*/
|
||||
public abstract class PythonProjectGenerator<T extends PyNewProjectSettings> implements DirectoryProjectGenerator<T> {
|
||||
private final List<SettingsListener> myListeners = ContainerUtil.newArrayList();
|
||||
private final boolean myAllowRemoteProjectCreation;
|
||||
@Nullable private MouseListener myErrorLabelMouseListener;
|
||||
|
||||
protected Consumer<String> myErrorCallback;
|
||||
|
||||
protected PythonProjectGenerator() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param allowRemoteProjectCreation if project of this type could be created remotely
|
||||
*/
|
||||
protected PythonProjectGenerator(final boolean allowRemoteProjectCreation) {
|
||||
myAllowRemoteProjectCreation = allowRemoteProjectCreation;
|
||||
}
|
||||
|
||||
public final void setErrorCallback(@NotNull final Consumer<String> errorCallback) {
|
||||
myErrorCallback = errorCallback;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JComponent getSettingsPanel(File baseDir) throws ProcessCanceledException {
|
||||
return null;
|
||||
@@ -55,29 +79,113 @@ public abstract class PythonProjectGenerator<T extends PyNewProjectSettings> imp
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if project type and remote ask allows project creation.
|
||||
* Throws exception with reason if can't
|
||||
*
|
||||
* @param sdk sdk to check
|
||||
* @param projectDirectory base project directory
|
||||
* @throws PyNoProjectAllowedOnSdkException project can't be created (check message)
|
||||
*/
|
||||
public void checkProjectCanBeCreatedOnSdk(@NotNull final Sdk sdk,
|
||||
@NotNull final File projectDirectory) throws PyNoProjectAllowedOnSdkException {
|
||||
|
||||
// Check if project does not support remote creation at all
|
||||
if (!myAllowRemoteProjectCreation && PySdkUtil.isRemote(sdk)) {
|
||||
throw new PyNoProjectAllowedOnSdkException(
|
||||
"Can't create project of this type on remote interpreter. Choose local interpreter.");
|
||||
}
|
||||
|
||||
|
||||
// Check if project synchronizer could be used with this project dir
|
||||
// No project can be created remotely if project synchronizer can't work with it
|
||||
|
||||
final PythonRemoteInterpreterManager remoteManager = PythonRemoteInterpreterManager.getInstance();
|
||||
if (remoteManager == null) {
|
||||
return;
|
||||
}
|
||||
final PyProjectSynchronizer synchronizer = remoteManager.getSynchronizer(sdk);
|
||||
if (synchronizer == null) {
|
||||
return;
|
||||
}
|
||||
final String syncError = synchronizer.checkSynchronizationAvailable(new PySyncCheckOnly(projectDirectory));
|
||||
if (syncError != null) {
|
||||
throw new PyNoProjectAllowedOnSdkException(syncError);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void generateProject(@NotNull final Project project,
|
||||
@NotNull final VirtualFile baseDir,
|
||||
@Nullable final T settings,
|
||||
@NotNull final Module module) {
|
||||
assert settings != null : "No project settings provided";
|
||||
|
||||
/*Instead of this method overwrite ``configureProject``*/
|
||||
|
||||
// If we deal with remote project -- use remote manager to configure it
|
||||
final PythonRemoteInterpreterManager remoteManager = PythonRemoteInterpreterManager.getInstance();
|
||||
final Sdk sdk = (settings != null ? settings.getSdk() : null);
|
||||
if (remoteManager != null && PythonSdkType.isRemote(sdk)) {
|
||||
remoteManager.prepareRemoteSettingsIfNeeded(module, sdk);
|
||||
final Sdk sdk = settings.getSdk();
|
||||
|
||||
final PyProjectSynchronizer synchronizer = (remoteManager != null ? remoteManager.getSynchronizer(sdk) : null);
|
||||
|
||||
if (synchronizer != null) {
|
||||
// Before project creation we need to configure sync
|
||||
// We call "checkSynchronizationAvailable" until it returns success (means sync is available)
|
||||
// Or user confirms she does not need sync
|
||||
String userProvidedPath = settings.getRemotePath();
|
||||
while (true) {
|
||||
final String syncError = synchronizer.checkSynchronizationAvailable(new PySyncCheckCreateIfPossible(module, userProvidedPath));
|
||||
if (syncError == null) {
|
||||
break;
|
||||
}
|
||||
userProvidedPath = null; // According to checkSynchronizationAvailable should be cleared
|
||||
final String message = String.format("Local/Remote synchronization is not configured correctly.\n%s\n" +
|
||||
"You may need to sync local and remote project manually.\n\n Do you want to continue? \n\n" +
|
||||
"Say 'Yes' to stay with misconfigured mappings or 'No' to start manual configuration process.",
|
||||
syncError);
|
||||
if (Messages.showYesNoDialog(project,
|
||||
message,
|
||||
"Synchronization not Configured",
|
||||
General.WarningDialog) == Messages.YES) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
configureProject(project, baseDir, settings, module);
|
||||
|
||||
configureProject(project, baseDir, settings, module, synchronizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does real work to generate project
|
||||
* Does real work to generate project.
|
||||
* Parent class does its best to handle remote interpreters.
|
||||
* Inheritors should only create project.
|
||||
* To support remote project creation, be sure to use {@link PyProjectSynchronizer}.
|
||||
* <br/>
|
||||
* When overwriting this method, <strong>be sure</strong> to call super() or call
|
||||
* {@link PyProjectSynchronizer#syncProject(Module, PySyncDirection, Consumer)} at least once: automatic sync works only after it.
|
||||
*
|
||||
* @param synchronizer null if project is local and no sync required.
|
||||
* Otherwise, be sure to use it move code between local (java) and remote (python) side.
|
||||
* Remote interpreters can't be used with out of it. Contract is following:
|
||||
* <ol>
|
||||
* <li>Create some code on python (remote) side using helpers</li>
|
||||
* <li>call {@link PyProjectSynchronizer#syncProject(Module, PySyncDirection, Consumer)}</li>
|
||||
* <li>Change locally</li>
|
||||
* <li>call {@link PyProjectSynchronizer#syncProject(Module, PySyncDirection, Consumer)} again in opposite direction</li>
|
||||
* </ol>
|
||||
*/
|
||||
protected abstract void configureProject(@NotNull final Project project,
|
||||
@NotNull final VirtualFile baseDir,
|
||||
@Nullable final T settings,
|
||||
@NotNull final Module module);
|
||||
|
||||
protected void configureProject(@NotNull final Project project,
|
||||
@NotNull final VirtualFile baseDir,
|
||||
@NotNull final T settings,
|
||||
@NotNull final Module module,
|
||||
@Nullable final PyProjectSynchronizer synchronizer) {
|
||||
// Automatic deployment works only after first sync
|
||||
if (synchronizer != null) {
|
||||
synchronizer.syncProject(module, PySyncDirection.JAVA_TO_PYTHON, null);
|
||||
}
|
||||
}
|
||||
|
||||
public Object getProjectSettings() {
|
||||
return new PyNewProjectSettings();
|
||||
@@ -124,4 +232,17 @@ public abstract class PythonProjectGenerator<T extends PyNewProjectSettings> imp
|
||||
|
||||
public void createAndAddVirtualEnv(Project project, PyNewProjectSettings settings) {
|
||||
}
|
||||
|
||||
/**
|
||||
* To be thrown if project can't be created on this sdk
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public static class PyNoProjectAllowedOnSdkException extends Exception {
|
||||
/**
|
||||
* @param reason why project can't be created
|
||||
*/
|
||||
PyNoProjectAllowedOnSdkException(@NotNull final String reason) {
|
||||
super(reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
107
python/src/com/jetbrains/python/remote/PyProjectSynchronizer.kt
Normal file
107
python/src/com/jetbrains/python/remote/PyProjectSynchronizer.kt
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.remote
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import java.io.File
|
||||
import java.util.function.Consumer
|
||||
|
||||
/**
|
||||
* ProjectSynchronizer is an engine that synchronize code between local and remote system or between java (which is local)
|
||||
* and python (which may be remote).
|
||||
* This engine is sdk-specific and used by [com.jetbrains.python.newProject.PythonProjectGenerator] (and friends).
|
||||
*
|
||||
* When generator creates remote project, it may use python helpers (with aid of tasks) and it may need some way
|
||||
* to pull remote files, patch them and push 'em back. The way it does it is skd-specific and this interface encapsulates it.
|
||||
*
|
||||
* Using this engine makes your generator compatible with remote interpreters.
|
||||
*
|
||||
* Project synchronizer is also responsible for project configuration for sync: it cooperates with user to make sure remote project is
|
||||
* configured correctly.
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
interface PyProjectSynchronizer {
|
||||
|
||||
/**
|
||||
* Checks if sync is available.
|
||||
* It supports several strategies: see concrete instance documentation.
|
||||
*
|
||||
* @param syncCheckStrategy strategy to check if sync is available.
|
||||
* Several strategies are supported: see concrete instance documentation.
|
||||
* @return null if sync is available or error message if something prevents project from sync.
|
||||
*/
|
||||
|
||||
fun checkSynchronizationAvailable(syncCheckStrategy: PySyncCheckStrategy): String?
|
||||
|
||||
/**
|
||||
* @return if remote box allows user to configure remote path, this method returns default path
|
||||
* that should be shown to user.
|
||||
* If returns null, user can't configure remote path and GUI should not provide such ability
|
||||
*/
|
||||
fun getDefaultRemotePath(): String?
|
||||
|
||||
|
||||
/**
|
||||
* Synchronizes project.
|
||||
* @param module current module
|
||||
* @param syncDirection local-to-remote (aka java-to-python) or opposite. See enum value doc.
|
||||
* @param callback code to be called after sync completion. Argument tells if sync was success or not.
|
||||
*/
|
||||
fun syncProject(module: Module, syncDirection: PySyncDirection,
|
||||
callback: Consumer<Boolean>?)
|
||||
}
|
||||
|
||||
/**
|
||||
* Several strategies to be used for [PyProjectSynchronizer.checkSynchronizationAvailable].
|
||||
* See concrete impls.
|
||||
*/
|
||||
interface PySyncCheckStrategy
|
||||
|
||||
/**
|
||||
* Checks if specific folder could be synced with remote interpreter.
|
||||
* It does not cooperate with user but simply checks folder instead.
|
||||
*
|
||||
* Strategy should return "false" only if it is technically impossible to sync with this folder what ever user does.
|
||||
* If it is possible but requires some aid from user should return true.
|
||||
*
|
||||
* No remote project creation would be allowed if this strategy returns "false".
|
||||
*/
|
||||
class PySyncCheckOnly(val projectBaseDir: File) : PySyncCheckStrategy
|
||||
|
||||
/**
|
||||
* Checks if project with specific module could be synced with remote server.
|
||||
* It may contact user taking one through some wizard steps to configure project to support remote interpreter.
|
||||
* So, it does its best to make project synchronizable.*
|
||||
*
|
||||
* @param remotePath user provided remote path. Should only be provided if [PyProjectSynchronizer.getDefaultRemotePath] is not null.
|
||||
* This argument should only be provided first time. On next call always provide null to prevent infinite loop because
|
||||
* user will be asked for path only if this argument is null.
|
||||
*/
|
||||
class PySyncCheckCreateIfPossible(val module: Module, val remotePath: String? ) : PySyncCheckStrategy
|
||||
|
||||
/**
|
||||
* Local-remote sync direction
|
||||
*/
|
||||
enum class PySyncDirection {
|
||||
/**
|
||||
* aka local-to-remote
|
||||
*/
|
||||
JAVA_TO_PYTHON,
|
||||
/**
|
||||
* aka remote-to-local
|
||||
*/
|
||||
PYTHON_TO_JAVA,
|
||||
}
|
||||
@@ -22,12 +22,12 @@ import com.intellij.execution.configurations.ParamsGroup;
|
||||
import com.intellij.execution.process.ProcessHandler;
|
||||
import com.intellij.execution.process.ProcessOutput;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
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.SdkModificator;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.remote.*;
|
||||
@@ -44,10 +44,12 @@ import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author traff
|
||||
@@ -136,16 +138,33 @@ public abstract class PythonRemoteInterpreterManager {
|
||||
RemoteSdkCredentials data);
|
||||
|
||||
/**
|
||||
* Prepares project (i.e. sets appropriate mappings) if sdk is remote.
|
||||
* Do not call this method if sdk is not remote: id does nothing
|
||||
* @param sdk current sdk
|
||||
* @return project synchronizer for this sdk. See {@link PyProjectSynchronizer} for more info
|
||||
* @see PyProjectSynchronizer
|
||||
*/
|
||||
public abstract void prepareRemoteSettingsIfNeeded(@NotNull final Module module,
|
||||
@NotNull final Sdk sdk);
|
||||
@Nullable
|
||||
public abstract PyProjectSynchronizer getSynchronizer(@NotNull final Sdk sdk);
|
||||
|
||||
public abstract void copyFromRemote(Sdk sdk, @NotNull Project project,
|
||||
RemoteSdkCredentials data,
|
||||
List<PathMappingSettings.PathMapping> mappings);
|
||||
|
||||
/**
|
||||
* Creates form to browse remote box.
|
||||
* You need to show it to user using dialog.
|
||||
*
|
||||
* @return null if remote sdk can't be browsed.
|
||||
* First argument is consumer to get path, chosen by user.
|
||||
* Second is panel to display to user
|
||||
*
|
||||
* @throws ExecutionException credentials can't be obtained due to remote server error
|
||||
* @throws InterruptedException credentials can't be obtained due to remote server error
|
||||
*/
|
||||
@Nullable
|
||||
public abstract Pair<Supplier<String>, JPanel> createServerBrowserForm(@NotNull final Sdk remoteSdk)
|
||||
throws ExecutionException, InterruptedException;
|
||||
|
||||
|
||||
@Nullable
|
||||
public static PythonRemoteInterpreterManager getInstance() {
|
||||
if (EP_NAME.getExtensions().length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user