All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jetbrains.python.sdk.CreateVirtualEnvDialog Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition python-community library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * Copyright 2000-2014 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.sdk;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.intellij.execution.ExecutionException;
import com.intellij.facet.ui.FacetEditorValidator;
import com.intellij.facet.ui.FacetValidatorsManager;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.FixedSizeButton;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.platform.LocationNameFieldsBinding;
import com.intellij.remote.RemoteSdkCredentialsHolder;
import com.intellij.ui.CollectionComboBoxModel;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.NullableConsumer;
import com.intellij.util.PathUtil;
import com.intellij.util.PlatformUtils;
import com.intellij.webcore.packaging.PackageManagementService;
import com.intellij.webcore.packaging.PackagesNotificationPanel;
import com.jetbrains.python.packaging.PyPackageManager;
import com.jetbrains.python.packaging.PyPackageService;
import com.jetbrains.python.packaging.ui.PyPackageManagementService;
import com.jetbrains.python.sdk.flavors.VirtualEnvSdkFlavor;
import com.jetbrains.python.ui.IdeaDialog;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CreateVirtualEnvDialog extends IdeaDialog {
  private JPanel myMainPanel;
  private JComboBox mySdkCombo;
  private TextFieldWithBrowseButton myDestination;
  private JTextField myName;
  private JBCheckBox mySitePackagesCheckBox;
  private JBCheckBox myMakeAvailableToAllProjectsCheckbox;
  @Nullable private Project myProject;
  private String myInitialPath;

  public interface VirtualEnvCallback {
    void virtualEnvCreated(Sdk sdk, boolean associateWithProject);
  }

  private static void setupVirtualEnvSdk(List allSdks,
                                         final String path,
                                         boolean associateWithProject,
                                         VirtualEnvCallback callback) {
    final VirtualFile sdkHome =
      ApplicationManager.getApplication().runWriteAction(new Computable() {
        @Nullable
        public VirtualFile compute() {
          return LocalFileSystem.getInstance().refreshAndFindFileByPath(path);
        }
      });
    if (sdkHome != null) {
      final String name =
        SdkConfigurationUtil.createUniqueSdkName(PythonSdkType.getInstance(), sdkHome.getPath(), allSdks);
      final ProjectJdkImpl sdk = new ProjectJdkImpl(name, PythonSdkType.getInstance());
      sdk.setHomePath(FileUtil.toSystemDependentName(sdkHome.getPath()));
      callback.virtualEnvCreated(sdk, associateWithProject);
    }
  }

  public CreateVirtualEnvDialog(Project project,
                                final List allSdks,
                                @Nullable Sdk suggestedBaseSdk) {
    super(project);
    setupDialog(project, allSdks, suggestedBaseSdk);
  }

  public CreateVirtualEnvDialog(Component owner,
                                final List allSdks,
                                @Nullable Sdk suggestedBaseSdk) {
    super(owner);
    setupDialog(null, allSdks, suggestedBaseSdk);
  }

  private void setupDialog(Project project, final List allSdks, @Nullable Sdk suggestedBaseSdk) {
    myProject = project;
    layoutPanel(allSdks);

    init();
    setTitle("Create Virtual Environment");
    Iterables.removeIf(allSdks, new Predicate() {
      @Override
      public boolean apply(Sdk s) {
        return PythonSdkType.isInvalid(s) || PythonSdkType.isVirtualEnv(s) || RemoteSdkCredentialsHolder.isRemoteSdk(s.getHomePath());
      }
    });
    if (suggestedBaseSdk == null && allSdks.size() > 0) {
      List sortedSdks = new ArrayList(allSdks);
      Collections.sort(sortedSdks, new PreferredSdkComparator());
      suggestedBaseSdk = sortedSdks.get(0);
    }
    updateSdkList(allSdks, suggestedBaseSdk);

    if (project == null || project.isDefault() || !PlatformUtils.isPyCharm()) {
      myMakeAvailableToAllProjectsCheckbox.setSelected(true);
      myMakeAvailableToAllProjectsCheckbox.setVisible(false);
    }

    setOKActionEnabled(false);

    myInitialPath = "";

    final VirtualFile file = VirtualEnvSdkFlavor.getDefaultLocation();

    if (file != null) {
      myInitialPath = file.getPath();
    }
    else {
      final String savedPath = PyPackageService.getInstance().getVirtualEnvBasePath();
      if (!StringUtil.isEmptyOrSpaces(savedPath)) {
        myInitialPath = savedPath;
      }
      else if (myProject != null) {
        final VirtualFile baseDir = myProject.getBaseDir();
        if (baseDir != null) {
          myInitialPath = baseDir.getPath();
        }
      }
    }

    addUpdater(myName);
    new LocationNameFieldsBinding(project, myDestination, myName, myInitialPath, "Select Location for Virtual Environment");

    registerValidators(new FacetValidatorsManager() {
      public void registerValidator(FacetEditorValidator validator, JComponent... componentsToWatch) {
      }

      public void validate() {
        checkValid();
      }
    });
    myMainPanel.setPreferredSize(new Dimension(300, 50));
    checkValid();
  }

  private void layoutPanel(final List allSdks) {
    final GridBagLayout layout = new GridBagLayout();
    myMainPanel = new JPanel(layout);

    final GridBagConstraints c = new GridBagConstraints();
    c.fill = GridBagConstraints.HORIZONTAL;
    c.insets = new Insets(2,2,2,2);

    c.gridx = 0;
    c.gridy = 0;
    c.weightx = 0.0;
    myMainPanel.add(new JBLabel("Name:"), c);

    c.gridx = 1;
    c.gridy = 0;
    c.gridwidth = 2;
    c.weightx = 1.0;
    myName = new JTextField();
    myMainPanel.add(myName, c);

    c.gridx = 0;
    c.gridy = 1;
    c.gridwidth = 1;
    c.weightx = 0.0;
    myMainPanel.add(new JBLabel("Location:"), c);

    c.gridx = 1;
    c.gridy = 1;
    c.gridwidth = 2;
    c.weightx = 1.0;
    myDestination = new TextFieldWithBrowseButton();
    myMainPanel.add(myDestination, c);

    c.gridx = 0;
    c.gridy = 2;
    c.gridwidth = 1;
    c.weightx = 0.0;
    myMainPanel.add(new JBLabel("Base interpreter:"), c);

    c.gridx = 1;
    c.gridy = 2;
    mySdkCombo = new ComboBox();
    c.insets = new Insets(2,2,2,2);
    c.weightx = 1.0;
    myMainPanel.add(mySdkCombo, c);

    c.gridx = 2;
    c.gridy = 2;
    c.insets = new Insets(0,0,2,2);
    c.weightx = 0.0;
    FixedSizeButton button = new FixedSizeButton();
    button.setPreferredSize(myDestination.getButton().getPreferredSize());
    myMainPanel.add(button, c);

    c.gridx = 0;
    c.gridy = 3;
    c.gridwidth = 3;
    c.insets = new Insets(2,2,2,2);
    mySitePackagesCheckBox = new JBCheckBox("Inherit global site-packages");
    myMainPanel.add(mySitePackagesCheckBox, c);

    c.gridx = 0;
    c.gridy = 4;
    myMakeAvailableToAllProjectsCheckbox = new JBCheckBox("Make available to all projects");
    myMainPanel.add(myMakeAvailableToAllProjectsCheckbox, c);
    button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        final PySdkService sdkService = PySdkService.getInstance();

        final PythonSdkType sdkType = PythonSdkType.getInstance();
        final FileChooserDescriptor descriptor = sdkType.getHomeChooserDescriptor();

        String suggestedPath = sdkType.suggestHomePath();
        VirtualFile suggestedDir = suggestedPath == null
                                   ? null
                                   : LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName(suggestedPath));
        final NullableConsumer consumer = new NullableConsumer() {
          @Override
          public void consume(@Nullable Sdk sdk) {
            if (sdk == null) return;
            if (!allSdks.contains(sdk)) {
              allSdks.add(sdk);
              sdkService.addSdk(sdk);
            }
            updateSdkList(allSdks, sdk);
          }
        };
        FileChooser.chooseFiles(descriptor, myProject, suggestedDir, new FileChooser.FileChooserConsumer() {
          @Override
          public void consume(List selectedFiles) {
            String path = selectedFiles.get(0).getPath();
            if (sdkType.isValidSdkHome(path)) {
              path = FileUtil.toSystemDependentName(path);
              Sdk newSdk = null;
              for (Sdk sdk : allSdks) {
                if (path.equals(sdk.getHomePath())) {
                  newSdk = sdk;
                }
              }
              if (newSdk == null) {
                newSdk = new PyDetectedSdk(path);
              }
              consumer.consume(newSdk);
            }
          }

          @Override
          public void cancelled() {
          }
        });

      }
    });
  }

  private void checkValid() {
    final String projectName = myName.getText();
    if (new File(getDestination()).exists()) {
      setOKActionEnabled(false);
      setErrorText("Directory already exists");
      return;
    }
    if (StringUtil.isEmptyOrSpaces(projectName)) {
      setOKActionEnabled(false);
      setErrorText("VirtualEnv name can't be empty");
      return;
    }
    if (!PathUtil.isValidFileName(projectName)) {
      setOKActionEnabled(false);
      setErrorText("Invalid directory name");
      return;
    }
    if (mySdkCombo.getSelectedItem() == null) {
      setOKActionEnabled(false);
      setErrorText("Select base interpreter");
      return;
    }
    if (StringUtil.isEmptyOrSpaces(myDestination.getText())) {
      setOKActionEnabled(false);
      setErrorText("Destination directory can't be empty");
      return;
    }

    setOKActionEnabled(true);
    setErrorText(null);
  }

  private void registerValidators(final FacetValidatorsManager validatorsManager) {
    myDestination.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
      @Override
      protected void textChanged(DocumentEvent e) {
        validatorsManager.validate();
      }
    });

    mySdkCombo.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent event) {
        validatorsManager.validate();
      }
    });

    myDestination.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent event) {
        validatorsManager.validate();
      }
    });
    myName.addCaretListener(new CaretListener() {
      @Override
      public void caretUpdate(CaretEvent event) {
        validatorsManager.validate();
      }
    });

    myDestination.getTextField().addCaretListener(new CaretListener() {
      @Override
      public void caretUpdate(CaretEvent event) {
        validatorsManager.validate();
      }
    });
  }

  private void updateSdkList(final List allSdks, @Nullable Sdk initialSelection) {
    mySdkCombo.setRenderer(new PySdkListCellRenderer(false));
    mySdkCombo.setModel(new CollectionComboBoxModel(allSdks, initialSelection));
    checkValid();
  }

  @Override
  protected JComponent createCenterPanel() {
    return myMainPanel;
  }

  public String getDestination() {
    return myDestination.getText();
  }

  public String getName() {
    return myName.getText();
  }

  @Override
  protected void doOKAction() {
    super.doOKAction();
    VirtualFile baseDir = myProject != null ? myProject.getBaseDir() : null;
    if (!myDestination.getText().startsWith(myInitialPath) &&
        (baseDir == null || !myDestination.getText().startsWith(baseDir.getPath()))) {
      String path = myDestination.getText();
      PyPackageService.getInstance().setVirtualEnvBasePath(!path.contains(File.separator) ?
                                                                    path : path.substring(0, path.lastIndexOf(File.separator)));
    }
  }

  public Sdk getSdk() {
    return (Sdk)mySdkCombo.getSelectedItem();
  }

  public boolean useGlobalSitePackages() {
    return mySitePackagesCheckBox.isSelected();
  }

  public boolean associateWithProject() {
    return !myMakeAvailableToAllProjectsCheckbox.isSelected();
  }

  @Override
  public JComponent getPreferredFocusedComponent() {
    return myName;
  }

  public void createVirtualEnv(final List allSdks, final VirtualEnvCallback callback) {
    final ProgressManager progman = ProgressManager.getInstance();
    final Sdk basicSdk = getSdk();
    final Task.Modal createTask = new Task.Modal(myProject, "Creating virtual environment for " + basicSdk.getName(), false) {
      String myPath;

      public void run(@NotNull final ProgressIndicator indicator) {
        final PyPackageManager packageManager = PyPackageManager.getInstance(basicSdk);
        try {
          indicator.setText("Creating virtual environment for " + basicSdk.getName());
          myPath = packageManager.createVirtualEnv(getDestination(), useGlobalSitePackages());
        }
        catch (final ExecutionException e) {
          ApplicationManager.getApplication().invokeLater(new Runnable() {
            @Override
            public void run() {
              final PackageManagementService.ErrorDescription description =
                PyPackageManagementService.toErrorDescription(Collections.singletonList(e), basicSdk);
              if (description != null) {
                PackagesNotificationPanel.showError("Failed to Create Virtual Environment", description);
              }
            }
          }, ModalityState.any());
        }
      }

      @Override
      public void onSuccess() {
        if (myPath != null) {
          final Application application = ApplicationManager.getApplication();
          application.invokeLater(new Runnable() {
            @Override
            public void run() {
              setupVirtualEnvSdk(allSdks, myPath, associateWithProject(), callback);
            }
          }, ModalityState.any());
        }
      }
    };
    progman.run(createTask);
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy