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.Consumer;
31 import com.intellij.util.concurrency.AppExecutorUtil;
32 import com.intellij.util.containers.HashMap;
33 import com.jediterm.pty.PtyProcessTtyConnector;
34 import com.jediterm.terminal.TtyConnector;
35 import com.pty4j.PtyProcess;
36 import com.pty4j.util.PtyUtil;
37 import org.jetbrains.annotations.NotNull;
38 import org.jetbrains.annotations.Nullable;
41 import java.io.IOException;
42 import java.io.OutputStream;
43 import java.nio.charset.Charset;
45 import java.util.concurrent.ExecutionException;
46 import java.util.concurrent.Future;
51 public class LocalTerminalDirectRunner extends AbstractTerminalRunner<PtyProcess> {
52 private static final Logger LOG = Logger.getInstance(LocalTerminalDirectRunner.class);
54 private final Charset myDefaultCharset;
56 public LocalTerminalDirectRunner(Project project) {
58 myDefaultCharset = CharsetToolkit.UTF8_CHARSET;
61 private static boolean hasLoginArgument(String name) {
62 return name.equals("bash") || name.equals("sh") || name.equals("zsh");
65 private static String getShellName(String path) {
66 return new File(path).getName();
69 private static File findRCFile() {
71 final String folder = PtyUtil.getPtyLibFolderPath();
73 File rcFile = new File(folder, "jediterm.in");
74 if (rcFile.exists()) {
80 LOG.warn("Unable to get JAR folder", e);
86 public static LocalTerminalDirectRunner createTerminalRunner(Project project) {
87 return new LocalTerminalDirectRunner(project);
91 protected PtyProcess createProcess(@Nullable String directory) throws ExecutionException {
92 Map<String, String> envs = new HashMap<String, String>(System.getenv());
93 if (!SystemInfo.isWindows) {
94 envs.put("TERM", "xterm-256color");
96 EncodingEnvironmentUtil.setLocaleEnvironmentIfMac(envs, myDefaultCharset);
98 return PtyProcess.exec(getCommand(), envs, directory != null ? directory : currentProjectFolder());
100 catch (IOException e) {
101 throw new ExecutionException(e);
105 private String currentProjectFolder() {
106 final ProjectRootManager projectRootManager = ProjectRootManager.getInstance(myProject);
108 final VirtualFile[] roots = projectRootManager.getContentRoots();
109 if (roots.length == 1) {
110 roots[0].getCanonicalPath();
112 final VirtualFile baseDir = myProject.getBaseDir();
113 return baseDir == null ? null : baseDir.getCanonicalPath();
117 protected ProcessHandler createProcessHandler(final PtyProcess process) {
118 return new PtyProcessHandler(process, getCommand()[0]);
122 protected TtyConnector createTtyConnector(PtyProcess process) {
123 return new PtyProcessTtyConnector(process, myDefaultCharset);
127 public String runningTargetName() {
128 return "Local Terminal";
132 protected String getTerminalConnectionName(PtyProcess process) {
133 return "Local Terminal";
136 public String[] getCommand() {
138 String shellPath = TerminalOptionsProvider.getInstance().getShellPath();
140 if (SystemInfo.isUnix) {
141 File rcFile = findRCFile();
143 String shellName = getShellName(shellPath);
145 if (rcFile != null && (shellName.equals("bash") || shellName.equals("sh"))) {
146 command = new String[]{shellPath, "--rcfile", rcFile.getAbsolutePath(), "-i"};
148 else if (hasLoginArgument(shellName)) {
149 command = new String[]{shellPath, "--login"};
152 command = shellPath.split(" ");
156 command = new String[]{shellPath};
162 private static class PtyProcessHandler extends ProcessHandler implements TaskExecutor {
164 private final PtyProcess myProcess;
165 private final ProcessWaitFor myWaitFor;
167 public PtyProcessHandler(PtyProcess process, @NotNull String presentableName) {
169 myWaitFor = new ProcessWaitFor(process, this, presentableName);
173 public void startNotify() {
174 addProcessListener(new ProcessAdapter() {
176 public void startNotified(ProcessEvent event) {
178 myWaitFor.setTerminationCallback(integer -> notifyProcessTerminated(integer));
181 removeProcessListener(this);
190 protected void destroyProcessImpl() {
195 protected void detachProcessImpl() {
196 destroyProcessImpl();
200 public boolean detachIsDefault() {
205 public boolean isSilentlyDestroyOnClose() {
211 public OutputStream getProcessInput() {
212 return myProcess.getOutputStream();
217 public Future<?> executeTask(@NotNull Runnable task) {
218 return AppExecutorUtil.getAppExecutorService().submit(task);