--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="intellij.platform.lang" />
+ <orderEntry type="module" module-name="intellij.platform.ide.impl" />
+ <orderEntry type="library" name="Guava" level="project" />
+ <orderEntry type="library" name="pty4j" level="project" />
+ <orderEntry type="library" name="jna" level="project" />
+ <orderEntry type="module" module-name="intellij.platform.remoteServers" />
+ <orderEntry type="module" module-name="intellij.platform.remoteServers.impl" />
+ <orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
+ <orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
+ <orderEntry type="library" name="jediterm-pty" level="project" />
++ <orderEntry type="module" module-name="intellij.platform.lang.impl" />
+ </component>
+</module>
*/
package org.jetbrains.plugins.terminal;
+import com.google.common.collect.Lists;
import com.intellij.execution.TaskExecutor;
++
+import com.intellij.execution.process.ProcessAdapter;
+import com.intellij.execution.process.ProcessEvent;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.execution.process.ProcessWaitFor;
+import com.intellij.internal.statistic.service.fus.collectors.FUSUsageContext;
+ import com.intellij.execution.configurations.EncodingEnvironmentUtil;
+ import com.intellij.execution.process.*;
+ import com.intellij.openapi.application.Application;
+ import com.intellij.openapi.application.ApplicationManager;
+ import com.intellij.openapi.components.PathMacroManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.util.Consumer;
-import com.intellij.util.containers.HashMap;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.EnvironmentUtil;
+import com.intellij.util.concurrency.AppExecutorUtil;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.text.CaseInsensitiveStringHashingStrategy;
import com.jediterm.pty.PtyProcessTtyConnector;
import com.jediterm.terminal.TtyConnector;
import com.pty4j.PtyProcess;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
+import java.net.URL;
import java.nio.charset.Charset;
++import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
return new LocalTerminalDirectRunner(project);
}
- @Override
- protected PtyProcess createProcess(@Nullable String directory) throws ExecutionException {
- Map<String, String> envs = getTerminalEnvironment();
- try {
- return PtyProcess.exec(getCommand(), envs, directory != null ? directory : currentProjectFolder());
- }
- catch (IOException e) {
- throw new ExecutionException(e);
- }
- }
+
+ private Map<String, String> getTerminalEnvironment() {
++
++
+ Map<String, String> envs = new HashMap<String, String>();
+
- if (TerminalOptionsProvider.getInstance().passParentEnvs()) {
++ if (TerminalOptionsProvider.Companion.getInstance().passParentEnvs()) {
+ envs.putAll(System.getenv());
+ }
+
+ envs.put("TERM", "xterm-256color");
+ EncodingEnvironmentUtil.setLocaleEnvironmentIfMac(envs, myDefaultCharset);
+
+ PathMacroManager macroManager = PathMacroManager.getInstance(myProject);
- for (Map.Entry<String, String> env : TerminalOptionsProvider.getInstance().getUserSpecifiedEnvs().entrySet()) {
++ for (Map.Entry<String, String> env : TerminalOptionsProvider.Companion.getInstance().getUserSpecifiedEnvs().entrySet()) {
+ envs.put(env.getKey(), macroManager.expandPath(env.getValue()));
+ }
+ return envs;
+ }
+
- private String currentProjectFolder() {
- final ProjectRootManager projectRootManager = ProjectRootManager.getInstance(myProject);
+ @Override
+ protected PtyProcess createProcess(@Nullable String directory) throws ExecutionException {
- Map<String, String> envs = new THashMap<>(SystemInfo.isWindows ? CaseInsensitiveStringHashingStrategy.INSTANCE
- : ContainerUtil.canonicalStrategy());
- envs.putAll(System.getenv());
- if (!SystemInfo.isWindows) {
- envs.put("TERM", "xterm-256color");
- }
++ Map<String, String> envs = getTerminalEnvironment();
- final VirtualFile[] roots = projectRootManager.getContentRoots();
- if (roots.length == 1) {
- roots[0].getCanonicalPath();
+ if (SystemInfo.isMac) {
+ EnvironmentUtil.setLocaleEnv(envs, myDefaultCharset);
+ }
+
+ String[] command = getCommand(envs);
+
+ for (LocalTerminalCustomizer customizer : LocalTerminalCustomizer.EP_NAME.getExtensions()) {
+ try {
+ command = customizer.customizeCommandAndEnvironment(myProject, command, envs);
+
+ if (directory == null) {
+ directory = customizer.getDefaultFolder(myProject);
+ }
+ }
+ catch (Exception e) {
+ LOG.error("Exception during customization of the terminal session", e);
+ }
+ }
+
+ try {
+ TerminalUsageTriggerCollector.Companion.trigger(myProject, "local.exec", FUSUsageContext.create(FUSUsageContext.getOSNameContextData(), SystemInfo.getOsNameAndVersion(), getShellName(command[0])));
+ return PtyProcess.exec(command, envs, directory != null
+ ? directory
+ : TerminalProjectOptionsProvider.Companion.getInstance(myProject).getStartingDirectory());
+ }
+ catch (IOException e) {
+ throw new ExecutionException(e);
}
- final VirtualFile baseDir = myProject.getBaseDir();
- return baseDir == null ? null : baseDir.getCanonicalPath();
}
@Override
--- /dev/null
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.plugins.terminal
+
+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.util.SystemInfo
+import java.io.File
++import java.util.HashMap
+
+/**
+ * @author traff
+ */
+
+@State(name = "TerminalOptionsProvider", storages = [(Storage("terminal.xml"))])
+class TerminalOptionsProvider : PersistentStateComponent<TerminalOptionsProvider.State> {
+ private val myState = State()
+
+ var shellPath: String? by ValueWithDefault(State::myShellPath, myState) { defaultShellPath }
+
+ override fun getState(): State? {
+ return myState
+ }
+
+ override fun loadState(state: State) {
+ myState.myCloseSessionOnLogout = state.myCloseSessionOnLogout
+ myState.myReportMouse = state.myReportMouse
+ myState.mySoundBell = state.mySoundBell
+ myState.myTabName = state.myTabName
+ myState.myCopyOnSelection = state.myCopyOnSelection
+ myState.myPasteOnMiddleMouseButton = state.myPasteOnMiddleMouseButton
+ myState.myOverrideIdeShortcuts = state.myOverrideIdeShortcuts
+ myState.myShellIntegration = state.myShellIntegration
+ myState.myShellPath = state.myShellPath
+ myState.myHighlightHyperlinks = state.myHighlightHyperlinks
+ }
+
+ fun closeSessionOnLogout(): Boolean {
+ return myState.myCloseSessionOnLogout
+ }
+
+ fun enableMouseReporting(): Boolean {
+ return myState.myReportMouse
+ }
+
+ fun audibleBell(): Boolean {
+ return myState.mySoundBell
+ }
+
+ var tabName: String
+ get() = myState.myTabName
+ set(tabName) {
+ myState.myTabName = tabName
+ }
+
+ fun overrideIdeShortcuts(): Boolean {
+ return myState.myOverrideIdeShortcuts
+ }
+
+ fun setOverrideIdeShortcuts(overrideIdeShortcuts: Boolean) {
+ myState.myOverrideIdeShortcuts = overrideIdeShortcuts
+ }
+
+ fun shellIntegration(): Boolean {
+ return myState.myShellIntegration
+ }
+
+ fun setShellIntegration(shellIntegration: Boolean) {
+ myState.myShellIntegration = shellIntegration
+ }
+
+ class State {
+ var myShellPath: String? = null
+ var myTabName: String = "Local"
+ var myCloseSessionOnLogout: Boolean = true
+ var myReportMouse: Boolean = true
+ var mySoundBell: Boolean = true
+ var myCopyOnSelection: Boolean = true
+ var myPasteOnMiddleMouseButton: Boolean = true
+ var myOverrideIdeShortcuts: Boolean = true
+ var myShellIntegration: Boolean = true
+ var myHighlightHyperlinks: Boolean = true
++ var myUserSpecifiedEnvs: Map<String, String> = hashMapOf("PROJECT_DIR" to "\$PROJECT_DIR$")
++ var myPassParentEnvs = true
+ }
+
+ fun setCloseSessionOnLogout(closeSessionOnLogout: Boolean) {
+ myState.myCloseSessionOnLogout = closeSessionOnLogout
+ }
+
+ fun setReportMouse(reportMouse: Boolean) {
+ myState.myReportMouse = reportMouse
+ }
+
+ fun setSoundBell(soundBell: Boolean) {
+ myState.mySoundBell = soundBell
+ }
+
+ fun copyOnSelection(): Boolean {
+ return myState.myCopyOnSelection
+ }
+
+ fun setCopyOnSelection(copyOnSelection: Boolean) {
+ myState.myCopyOnSelection = copyOnSelection
+ }
+
+ fun pasteOnMiddleMouseButton(): Boolean {
+ return myState.myPasteOnMiddleMouseButton
+ }
+
+ fun setPasteOnMiddleMouseButton(pasteOnMiddleMouseButton: Boolean) {
+ myState.myPasteOnMiddleMouseButton = pasteOnMiddleMouseButton
+ }
+
+ fun highlightHyperlinks(): Boolean {
+ return myState.myHighlightHyperlinks
+ }
+
+ fun setHighlightHyperlinks(highlight: Boolean) {
+ myState.myHighlightHyperlinks = highlight
+ }
+
++ fun getUserSpecifiedEnvs(): Map<String, String> {
++ return myState.myUserSpecifiedEnvs
++ }
++
++ fun setUserSpecifiedEnvs(userSpecifiedEnvs: Map<String, String>) {
++ myState.myUserSpecifiedEnvs = userSpecifiedEnvs
++ }
++
++ fun setPassParentEnvs(passParentEnvs: Boolean) {
++ myState.myPassParentEnvs = passParentEnvs
++ }
++
++ fun passParentEnvs(): Boolean {
++ return myState.myPassParentEnvs
++ }
++
+ val defaultShellPath: String
+ get() {
+ val shell = System.getenv("SHELL")
+
+ if (shell != null && File(shell).canExecute()) {
+ return shell
+ }
+
+ if (SystemInfo.isUnix) {
+ if (File("/bin/bash").exists()) {
+ return "/bin/bash"
+ }
+ else {
+ return "/bin/sh"
+ }
+ }
+ else {
+ return "cmd.exe"
+ }
+ }
+
+ companion object {
+ val instance: TerminalOptionsProvider
+ get() = ServiceManager.getService(TerminalOptionsProvider::class.java)
+ }
+}
+
+
+
+
+
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
- <clientProperties>
- <BorderFactoryClass class="java.lang.String" value=""/>
- </clientProperties>
<border type="none"/>
<children>
- <grid id="57ec0" layout-manager="GridLayoutManager" row-count="3" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <vspacer id="d777c">
+ <constraints>
+ <grid row="2" column="0" row-span="7" col-span="4" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+ </constraints>
+ </vspacer>
- <grid id="871ca" binding="myProjectSettingsPanel" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
++ <grid id="871ca" binding="myProjectSettingsPanel" 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>
- <grid row="0" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ <grid row="0" column="0" row-span="1" col-span="4" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
</constraints>
<properties/>
</component>
- <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+ <component id="a7470" class="javax.swing.JLabel">
+ <constraints>
- <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
++ <grid row="1" 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="Environment Variables"/>
+ </properties>
+ </component>
+ <component id="9454c" class="com.intellij.execution.configuration.EnvironmentVariablesTextFieldWithBrowseButton" binding="myEnvVarField">
+ <constraints>
++ <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+ </constraints>
+ <properties>
+ <text value=""/>
+ </properties>
+ </component>
</children>
</grid>
- <vspacer id="d777c">
- <constraints>
- <grid row="6" column="0" row-span="1" col-span="3" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
- </constraints>
- </vspacer>
- <grid id="caa1d" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+ <grid id="5c27f" binding="myGlobalSettingsPanel" layout-manager="GridLayoutManager" row-count="10" 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="3" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+ <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"/>
</constraints>
<properties/>
<border type="none"/>
-/*
- * Copyright 2000-2013 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.
- */
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.terminal;
+import com.google.common.collect.Lists;
+ import com.intellij.execution.configuration.EnvironmentVariablesTextFieldWithBrowseButton;
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;
private JBCheckBox myPasteOnMiddleButtonCheckBox;
private JBCheckBox myCopyOnSelectionCheckBox;
private JBCheckBox myOverrideIdeShortcuts;
++
+ private JBCheckBox myShellIntegration;
+ private TextFieldWithBrowseButton myStartDirectoryField;
+ private JPanel myProjectSettingsPanel;
+ private JPanel myGlobalSettingsPanel;
+ private JPanel myConfigurablesPanel;
+ private JBCheckBox myHighlightHyperlinks;
++
+ private EnvironmentVariablesTextFieldWithBrowseButton myEnvVarField;
++
private TerminalOptionsProvider myOptionsProvider;
+ private TerminalProjectOptionsProvider myProjectOptionsProvider;
+
+ private final java.util.List<UnnamedConfigurable> myConfigurables = Lists.newArrayList();
- public JComponent createPanel(@NotNull TerminalOptionsProvider provider) {
+ public JComponent createPanel(@NotNull TerminalOptionsProvider provider, @NotNull TerminalProjectOptionsProvider projectOptionsProvider) {
myOptionsProvider = provider;
+ myProjectOptionsProvider = projectOptionsProvider;
+
+ myProjectSettingsPanel.setBorder(IdeBorderFactory.createTitledBorder("Project settings"));
+ myGlobalSettingsPanel.setBorder(IdeBorderFactory.createTitledBorder("Application settings"));
FileChooserDescriptor fileChooserDescriptor = new FileChooserDescriptor(true, false, false, false, false, false);
|| (myCopyOnSelectionCheckBox.isSelected() != myOptionsProvider.copyOnSelection())
|| (myPasteOnMiddleButtonCheckBox.isSelected() != myOptionsProvider.pasteOnMiddleMouseButton())
|| (myOverrideIdeShortcuts.isSelected() != myOptionsProvider.overrideIdeShortcuts())
- myConfigurables.stream().anyMatch(c -> c.isModified());
+ || (myShellIntegration.isSelected() != myOptionsProvider.shellIntegration())
+ || (myHighlightHyperlinks.isSelected() != myOptionsProvider.highlightHyperlinks()) ||
- || (myEnvVarField.isPassParentEnvs() != myOptionsProvider.passParentEnvs())
- ;
++ myConfigurables.stream().anyMatch(c -> c.isModified())
+ || !Comparing.equal(myEnvVarField.getEnvs(), myOptionsProvider.getUserSpecifiedEnvs())
++ || (myEnvVarField.isPassParentEnvs() != myOptionsProvider.passParentEnvs());
}
public void apply() {
myOptionsProvider.setCopyOnSelection(myCopyOnSelectionCheckBox.isSelected());
myOptionsProvider.setPasteOnMiddleMouseButton(myPasteOnMiddleButtonCheckBox.isSelected());
myOptionsProvider.setOverrideIdeShortcuts(myOverrideIdeShortcuts.isSelected());
+ myOptionsProvider.setShellIntegration(myShellIntegration.isSelected());
+ myOptionsProvider.setHighlightHyperlinks(myHighlightHyperlinks.isSelected());
+ myConfigurables.forEach(c -> {
+ try {
+ c.apply();
+ }
+ catch (ConfigurationException e) {
+ //pass
+ }
+ });
+ myOptionsProvider.setUserSpecifiedEnvs(myEnvVarField.getEnvs());
+ myOptionsProvider.setPassParentEnvs(myEnvVarField.isPassParentEnvs());
}
public void reset() {
myCopyOnSelectionCheckBox.setSelected(myOptionsProvider.copyOnSelection());
myPasteOnMiddleButtonCheckBox.setSelected(myOptionsProvider.pasteOnMiddleMouseButton());
myOverrideIdeShortcuts.setSelected(myOptionsProvider.overrideIdeShortcuts());
+ myShellIntegration.setSelected(myOptionsProvider.shellIntegration());
+ myHighlightHyperlinks.setSelected(myOptionsProvider.highlightHyperlinks());
+ myConfigurables.forEach(c -> c.reset());
+ myEnvVarField.setEnvs(myOptionsProvider.getUserSpecifiedEnvs());
+ myEnvVarField.setPassParentEnvs(myOptionsProvider.passParentEnvs());
}
+
+ public Color getDefaultValueColor() {
+ return findColorByKey("TextField.inactiveForeground", "nimbusDisabledText");
+ }
+
+ @NotNull
+ private static Color findColorByKey(String... colorKeys) {
+ Color c = null;
+ for (String key : colorKeys) {
+ c = UIManager.getColor(key);
+ if (c != null) {
+ break;
+ }
+ }
+
+ assert c != null : "Can't find color for keys " + Arrays.toString(colorKeys);
+ return c;
+ }
+
+ public Color getChangedValueColor() {
+ return findColorByKey("TextField.foreground");
+ }
}