2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.jetbrains.plugins.terminal;
18 import com.intellij.execution.TaskExecutor;
19 import com.intellij.execution.configurations.EncodingEnvironmentUtil;
20 import com.intellij.execution.process.ProcessAdapter;
21 import com.intellij.execution.process.ProcessEvent;
22 import com.intellij.execution.process.ProcessHandler;
23 import com.intellij.execution.process.ProcessWaitFor;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.roots.ProjectRootManager;
27 import com.intellij.openapi.util.SystemInfo;
28 import com.intellij.openapi.vfs.CharsetToolkit;
29 import com.intellij.openapi.vfs.VirtualFile;
30 import com.intellij.util.concurrency.AppExecutorUtil;
31 import com.intellij.util.containers.HashMap;
32 import com.jediterm.pty.PtyProcessTtyConnector;
33 import com.jediterm.terminal.TtyConnector;
34 import com.pty4j.PtyProcess;
35 import com.pty4j.util.PtyUtil;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
40 import java.io.IOException;
41 import java.io.OutputStream;
42 import java.nio.charset.Charset;
44 import java.util.concurrent.ExecutionException;
45 import java.util.concurrent.Future;
50 public class LocalTerminalDirectRunner extends AbstractTerminalRunner<PtyProcess> {
51 private static final Logger LOG = Logger.getInstance(LocalTerminalDirectRunner.class);
53 private final Charset myDefaultCharset;
55 public LocalTerminalDirectRunner(Project project) {
57 myDefaultCharset = CharsetToolkit.UTF8_CHARSET;
60 private static boolean hasLoginArgument(String name) {
61 return name.equals("bash") || name.equals("sh") || name.equals("zsh");
64 private static String getShellName(String path) {
65 return new File(path).getName();
68 private static File findRCFile() {
70 final String folder = PtyUtil.getPtyLibFolderPath();
72 File rcFile = new File(folder, "jediterm.in");
73 if (rcFile.exists()) {
79 LOG.warn("Unable to get JAR folder", e);
85 public static LocalTerminalDirectRunner createTerminalRunner(Project project) {
86 return new LocalTerminalDirectRunner(project);
90 protected PtyProcess createProcess(@Nullable String directory) throws ExecutionException {
91 Map<String, String> envs = new HashMap<String, String>(System.getenv());
92 if (!SystemInfo.isWindows) {
93 envs.put("TERM", "xterm-256color");
95 EncodingEnvironmentUtil.setLocaleEnvironmentIfMac(envs, myDefaultCharset);
97 for (LocalTerminalCustomizer customizer: LocalTerminalCustomizer.EP_NAME.getExtensions()) {
98 customizer.setupEnvironment(envs);
102 return PtyProcess.exec(getCommand(), envs, directory != null ? directory : currentProjectFolder());
104 catch (IOException e) {
105 throw new ExecutionException(e);
109 private String currentProjectFolder() {
110 final ProjectRootManager projectRootManager = ProjectRootManager.getInstance(myProject);
112 final VirtualFile[] roots = projectRootManager.getContentRoots();
113 if (roots.length == 1) {
114 roots[0].getCanonicalPath();
116 final VirtualFile baseDir = myProject.getBaseDir();
117 return baseDir == null ? null : baseDir.getCanonicalPath();
121 protected ProcessHandler createProcessHandler(final PtyProcess process) {
122 return new PtyProcessHandler(process, getCommand()[0]);
126 protected TtyConnector createTtyConnector(PtyProcess process) {
127 return new PtyProcessTtyConnector(process, myDefaultCharset);
131 public String runningTargetName() {
132 return "Local Terminal";
136 protected String getTerminalConnectionName(PtyProcess process) {
137 return "Local Terminal";
140 public String[] getCommand() {
142 String shellPath = TerminalOptionsProvider.getInstance().getShellPath();
144 if (SystemInfo.isUnix) {
145 File rcFile = findRCFile();
147 String shellName = getShellName(shellPath);
149 if (rcFile != null && (shellName.equals("bash") || shellName.equals("sh"))) {
150 command = new String[]{shellPath, "--rcfile", rcFile.getAbsolutePath(), "-i"};
152 else if (hasLoginArgument(shellName)) {
153 command = new String[]{shellPath, "--login"};
156 command = shellPath.split(" ");
160 command = new String[]{shellPath};
166 private static class PtyProcessHandler extends ProcessHandler implements TaskExecutor {
168 private final PtyProcess myProcess;
169 private final ProcessWaitFor myWaitFor;
171 public PtyProcessHandler(PtyProcess process, @NotNull String presentableName) {
173 myWaitFor = new ProcessWaitFor(process, this, presentableName);
177 public void startNotify() {
178 addProcessListener(new ProcessAdapter() {
180 public void startNotified(ProcessEvent event) {
182 myWaitFor.setTerminationCallback(integer -> notifyProcessTerminated(integer));
185 removeProcessListener(this);
194 protected void destroyProcessImpl() {
199 protected void detachProcessImpl() {
200 destroyProcessImpl();
204 public boolean detachIsDefault() {
209 public boolean isSilentlyDestroyOnClose() {
215 public OutputStream getProcessInput() {
216 return myProcess.getOutputStream();
221 public Future<?> executeTask(@NotNull Runnable task) {
222 return AppExecutorUtil.getAppExecutorService().submit(task);