Provide settings for the virtualenv activation in terminal (PY-21583) appcode/171.1211 clion/171.1209 dbe/171.1207 idea/171.1208 phpstorm/171.1212 pycharm/171.1210 pycharm/171.1213 rubymine/171.1206
authorDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Thu, 24 Nov 2016 01:13:52 +0000 (02:13 +0100)
committerDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Thu, 24 Nov 2016 01:13:52 +0000 (02:13 +0100)
plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalCustomizer.java
plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalProjectOptionsProvider.kt
plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalSettingsPanel.form
plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalSettingsPanel.java
python/python-terminal/resources/META-INF/python-terminal-plugin.xml
python/python-terminal/src/com/jetbrains/python/sdk/PyVirtualEnvTerminalCustomizer.kt

index 61cc66bcb863fe9631ccfc243a2f7fa262c4e8ca..caa6d26867f14c70709d2080924c3b89e20e65e8 100644 (file)
@@ -16,6 +16,7 @@
 package org.jetbrains.plugins.terminal;
 
 import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.options.UnnamedConfigurable;
 import com.intellij.openapi.project.Project;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -29,6 +30,11 @@ public abstract class LocalTerminalCustomizer {
     return command;
   }
 
+  @Nullable
+  public UnnamedConfigurable getConfigurable(Project project) {
+    return null;
+  }
+
   @Nullable
   protected String getDefaultFolder() {
     return null;
index 2ca6168dcd5ecf362030daac79400fc0f60bf82b..4fabaa744dd45cecd77d9694a951d3ce38ac2713 100644 (file)
@@ -30,7 +30,7 @@ import kotlin.reflect.*
  * @author traff
  */
 @State(name = "TerminalProjectOptionsProvider", storages = arrayOf(Storage("terminal.xml")))
-class TerminalProjectOptionsProvider(private val myProject: Project) : PersistentStateComponent<TerminalProjectOptionsProvider.State> {
+class TerminalProjectOptionsProvider(val project: Project) : PersistentStateComponent<TerminalProjectOptionsProvider.State> {
 
   private val myState = State()
 
@@ -73,13 +73,13 @@ class TerminalProjectOptionsProvider(private val myProject: Project) : Persisten
 
 
   private fun currentProjectFolder(): String? {
-    val projectRootManager = ProjectRootManager.getInstance(myProject)
+    val projectRootManager = ProjectRootManager.getInstance(project)
 
     val roots = projectRootManager.contentRoots
     if (roots.size == 1) {
       roots[0].canonicalPath
     }
-    val baseDir = myProject.baseDir
+    val baseDir = project.baseDir
     return baseDir?.canonicalPath
   }
 
index f8963c843dd92e55f8fad2ca0a2e9857049a3c1e..1a7eae9b845a38fbd4ac56b72263be3408ae0fda 100644 (file)
@@ -64,7 +64,7 @@
               </component>
             </children>
           </grid>
-          <grid id="5c27f" binding="myGlobalSettingsPanel" layout-manager="GridLayoutManager" row-count="8" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+          <grid id="5c27f" binding="myGlobalSettingsPanel" layout-manager="GridLayoutManager" row-count="9" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
             <margin top="0" left="0" bottom="0" right="0"/>
             <constraints>
               <grid row="1" column="0" row-span="1" col-span="4" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
                   </hspacer>
                 </children>
               </grid>
+              <grid id="5fc56" binding="myConfigurablesPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
+                <constraints>
+                  <grid row="8" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties/>
+                <border type="none"/>
+                <children/>
+              </grid>
             </children>
           </grid>
         </children>
index 42c2ec0d256826a45b220d9ade2ec5110fe4ed91..e2d1fe4344100a088fa39067e9165f17e1d55e02 100644 (file)
  */
 package org.jetbrains.plugins.terminal;
 
+import com.google.common.collect.Lists;
 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.UnnamedConfigurable;
 import com.intellij.openapi.ui.TextComponentAccessor;
 import com.intellij.openapi.ui.TextFieldWithBrowseButton;
 import com.intellij.openapi.util.Comparing;
@@ -47,9 +50,12 @@ public class TerminalSettingsPanel {
   private TextFieldWithBrowseButton myStartDirectoryField;
   private JPanel myProjectSettingsPanel;
   private JPanel myGlobalSettingsPanel;
+  private JPanel myConfigurablesPanel;
   private TerminalOptionsProvider myOptionsProvider;
   private TerminalProjectOptionsProvider myProjectOptionsProvider;
 
+  private java.util.List<UnnamedConfigurable> myConfigurables = Lists.newArrayList();
+
   public JComponent createPanel(@NotNull TerminalOptionsProvider provider, @NotNull TerminalProjectOptionsProvider projectOptionsProvider) {
     myOptionsProvider = provider;
     myProjectOptionsProvider = projectOptionsProvider;
@@ -94,6 +100,17 @@ public class TerminalSettingsPanel {
       }
     });
 
+    for (LocalTerminalCustomizer c : LocalTerminalCustomizer.EP_NAME.getExtensions()) {
+      UnnamedConfigurable configurable = c.getConfigurable(projectOptionsProvider.getProject());
+      if (configurable != null) {
+        myConfigurables.add(configurable);
+        JComponent component = configurable.createComponent();
+        if (component != null) {
+          myConfigurablesPanel.add(component, BorderLayout.CENTER);
+        }
+      }
+    }
+
     return myWholePanel;
   }
 
@@ -107,8 +124,8 @@ public class TerminalSettingsPanel {
            || (myCopyOnSelectionCheckBox.isSelected() != myOptionsProvider.copyOnSelection())
            || (myPasteOnMiddleButtonCheckBox.isSelected() != myOptionsProvider.pasteOnMiddleMouseButton())
            || (myOverrideIdeShortcuts.isSelected() != myOptionsProvider.overrideIdeShortcuts())
-           || (myShellIntegration.isSelected() != myOptionsProvider.shellIntegration())
-      ;
+           || (myShellIntegration.isSelected() != myOptionsProvider.shellIntegration()) ||
+           myConfigurables.stream().anyMatch(c -> c.isModified());
   }
 
   public void apply() {
@@ -122,6 +139,14 @@ public class TerminalSettingsPanel {
     myOptionsProvider.setPasteOnMiddleMouseButton(myPasteOnMiddleButtonCheckBox.isSelected());
     myOptionsProvider.setOverrideIdeShortcuts(myOverrideIdeShortcuts.isSelected());
     myOptionsProvider.setShellIntegration(myShellIntegration.isSelected());
+    myConfigurables.forEach(c -> {
+      try {
+        c.apply();
+      }
+      catch (ConfigurationException e) {
+        //pass
+      }
+    });
   }
 
   public void reset() {
@@ -135,6 +160,7 @@ public class TerminalSettingsPanel {
     myPasteOnMiddleButtonCheckBox.setSelected(myOptionsProvider.pasteOnMiddleMouseButton());
     myOverrideIdeShortcuts.setSelected(myOptionsProvider.overrideIdeShortcuts());
     myShellIntegration.setSelected(myOptionsProvider.shellIntegration());
+    myConfigurables.forEach(c -> c.reset());
   }
 
   public Color getDefaultValueColor() {
index bd4bbdf7012876d89b70bce1b71a6d6c284e44d5..342eb2600037f41ed720d12446774faa7a65e6fa 100644 (file)
@@ -3,5 +3,9 @@
     <localTerminalCustomizer implementation="com.jetbrains.python.sdk.PyVirtualEnvTerminalCustomizer"/>
   </extensions>
 
+  <extensions defaultExtensionNs="com.intellij">
+    <projectService serviceImplementation="com.jetbrains.python.sdk.PyVirtualEnvTerminalSettings"/>
+  </extensions>
+
 
 </idea-plugin>
index 622580abbda94ad6ec531ed6a30330263081dd1b..102fd77f7c3a48bc308fb7f15b9dea4afa42e280 100644 (file)
  */
 package com.jetbrains.python.sdk
 
+import com.intellij.openapi.components.PersistentStateComponent
+import com.intellij.openapi.components.ServiceManager
+import com.intellij.openapi.components.State
+import com.intellij.openapi.components.Storage
 import com.intellij.openapi.module.ModuleManager
+import com.intellij.openapi.options.UnnamedConfigurable
 import com.intellij.openapi.project.Project
 import com.intellij.openapi.projectRoots.Sdk
 import com.intellij.openapi.util.SystemInfo
@@ -23,20 +28,20 @@ import com.jetbrains.python.run.PyVirtualEnvReader
 import com.jetbrains.python.run.findActivateScript
 import org.jetbrains.plugins.terminal.LocalTerminalCustomizer
 import java.io.File
+import javax.swing.JCheckBox
 
 /**
  * @author traff
  */
 
-class PyVirtualEnvTerminalCustomizer : LocalTerminalCustomizer() {
-
 
+class PyVirtualEnvTerminalCustomizer : LocalTerminalCustomizer() {
   override fun customizeCommandAndEnvironment(project: Project,
                                               command: Array<out String>,
                                               envs: MutableMap<String, String>): Array<out String> {
     val sdk: Sdk? = findSdk(project)
 
-    if (sdk != null && PythonSdkType.isVirtualEnv(sdk)) {
+    if (sdk != null && PythonSdkType.isVirtualEnv(sdk) && PyVirtualEnvTerminalSettings.getInstance(project).virtualEnvActivate) {
       // in case of virtualenv sdk on unix we activate virtualenv
       val path = sdk.homePath
 
@@ -53,14 +58,16 @@ class PyVirtualEnvTerminalCustomizer : LocalTerminalCustomizer() {
           //for other shells we read envs from activate script by the default shell and pass them to the process
           val reader = PyVirtualEnvReader(path)
           reader.activate?.let {
-            envs.putAll(reader.readShellEnv().mapKeys { k-> k.key.toUpperCase() }.filterKeys { k -> k in arrayOf("PATH", "PS1", "VIRTUAL_ENV", "PYTHONHOME", "PROMPT") })
+            envs.putAll(reader.readShellEnv().mapKeys { k -> k.key.toUpperCase() }.filterKeys { k ->
+              k in arrayOf("PATH", "PS1", "VIRTUAL_ENV", "PYTHONHOME", "PROMPT")
+            })
           }
         }
       }
     }
 
     // for some reason virtualenv isn't activated in the rcfile for the login shell, so we make it non-login
-    return command.filter { arg -> arg != "--login" && arg != "-l"}.toTypedArray()
+    return command.filter { arg -> arg != "--login" && arg != "-l" }.toTypedArray()
   }
 
 
@@ -79,5 +86,53 @@ class PyVirtualEnvTerminalCustomizer : LocalTerminalCustomizer() {
   override fun getDefaultFolder(): String? {
     return null
   }
+
+  override fun getConfigurable(project: Project) = object : UnnamedConfigurable {
+    val settings = PyVirtualEnvTerminalSettings.getInstance(project)
+
+    var myCheckbox: JCheckBox = JCheckBox("Activate virtualenv")
+
+    override fun createComponent() = myCheckbox
+
+    override fun isModified() = myCheckbox.isSelected != settings.virtualEnvActivate
+
+    override fun apply() {
+      settings.virtualEnvActivate = myCheckbox.isSelected
+    }
+
+    override fun reset() {
+      myCheckbox.isSelected = settings.virtualEnvActivate
+    }
+  }
+
+
+}
+
+class SettingsState {
+  var virtualEnvActivate = true
+}
+
+@State(name = "PyVirtualEnvTerminalCustomizer", storages = arrayOf(Storage("python-terminal.xml")))
+class PyVirtualEnvTerminalSettings : PersistentStateComponent<SettingsState> {
+  var myState = SettingsState()
+
+  var virtualEnvActivate: Boolean
+    get() = myState.virtualEnvActivate
+    set(value) {
+      myState.virtualEnvActivate = value
+    }
+
+  override fun getState() = myState
+
+  override fun loadState(state: SettingsState) {
+    myState.virtualEnvActivate = state.virtualEnvActivate
+  }
+
+  companion object {
+    fun getInstance(project: Project): PyVirtualEnvTerminalSettings {
+      return ServiceManager.getService(project, PyVirtualEnvTerminalSettings::class.java)
+    }
+  }
+
 }