Provide settings for the virtualenv activation in terminal (PY-21583)

This commit is contained in:
Dmitry Trofimov
2016-11-24 02:13:52 +01:00
parent cd064fc526
commit ae1c32c9b7
6 changed files with 110 additions and 11 deletions
@@ -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;
@@ -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
}
@@ -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"/>
@@ -260,6 +260,14 @@
</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>
@@ -15,7 +15,10 @@
*/
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() {
@@ -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>
@@ -15,7 +15,12 @@
*/
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() {
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)
}
}
}