diff --git a/plugins/terminal/intellij.terminal.iml b/plugins/terminal/intellij.terminal.iml
index ed89719334cd..01b0d3b35bb5 100644
--- a/plugins/terminal/intellij.terminal.iml
+++ b/plugins/terminal/intellij.terminal.iml
@@ -22,5 +22,6 @@
+
\ No newline at end of file
diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalTabCloseListener.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalTabCloseListener.kt
index b7794ac3837d..146b08d38948 100644
--- a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalTabCloseListener.kt
+++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalTabCloseListener.kt
@@ -5,8 +5,13 @@ import com.intellij.execution.TerminateRemoteProcessDialog
import com.intellij.execution.process.NopProcessHandler
import com.intellij.execution.ui.BaseContentCloseListener
import com.intellij.execution.ui.RunContentManagerImpl
+import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
import com.intellij.ui.content.Content
+import com.intellij.util.ObjectUtils
+import com.jediterm.terminal.ProcessTtyConnector
+import org.jetbrains.plugins.terminal.arrangement.ProcessInfoUtil
class TerminalTabCloseListener(val content: Content,
val project: Project) : BaseContentCloseListener(content, project) {
@@ -21,6 +26,15 @@ class TerminalTabCloseListener(val content: Content,
if (widget == null || !widget.isSessionRunning) {
return true
}
+ val connector = widget.ttyConnector as? ProcessTtyConnector
+ try {
+ if (connector != null && !TerminalUtil.hasRunningCommands(connector)) {
+ return true
+ }
+ }
+ catch (e: Exception) {
+ LOG.error(e)
+ }
val proxy = NopProcessHandler().apply { startNotify() }
// don't show 'disconnect' button
proxy.putUserData(RunContentManagerImpl.ALWAYS_USE_DEFAULT_STOPPING_BEHAVIOUR_KEY, true)
@@ -32,3 +46,5 @@ class TerminalTabCloseListener(val content: Content,
return project === this.project && closeQuery(this.content, true)
}
}
+
+private val LOG = logger()
diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalUtil.java b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalUtil.java
index a43a44966812..5a4d85a16b48 100644
--- a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalUtil.java
+++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalUtil.java
@@ -1,15 +1,36 @@
// Copyright 2000-2019 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.execution.process.OSProcessUtil;
+import com.intellij.execution.process.UnixProcessManager;
import com.intellij.openapi.Disposable;
+import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Disposer;
+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.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.terminal.JBTerminalWidget;
+import com.intellij.util.Processor;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
+import com.intellij.util.execution.ParametersListUtil;
+import com.jediterm.terminal.ProcessTtyConnector;
+import com.pty4j.windows.WinPtyProcess;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.jvnet.winp.WinProcess;
+import org.jvnet.winp.WinpException;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.StringTokenizer;
public class TerminalUtil {
+
+ private static final Logger LOG = Logger.getInstance(TerminalUtil.class);
+
private TerminalUtil() {}
@NotNull
@@ -33,4 +54,52 @@ public class TerminalUtil {
return result;
}
+ public static boolean hasRunningCommands(@NotNull ProcessTtyConnector connector) throws IllegalStateException {
+ Process process = connector.getProcess();
+ if (!process.isAlive()) return false;
+ if (SystemInfo.isUnix) {
+ int shellPid = OSProcessUtil.getProcessID(process);
+ MultiMap pidToChildPidsMap = MultiMap.create();
+ UnixProcessManager.processPSOutput(UnixProcessManager.getPSCmd(false, false), new Processor() {
+ @Override
+ public boolean process(String s) {
+ StringTokenizer st = new StringTokenizer(s, " ");
+ int parentPid = Integer.parseInt(st.nextToken());
+ int pid = Integer.parseInt(st.nextToken());
+ pidToChildPidsMap.putValue(parentPid, pid);
+ return false;
+ }
+ });
+ return !pidToChildPidsMap.get(shellPid).isEmpty();
+ }
+ else if (SystemInfo.isWindows) {
+ WinPtyProcess winPty = (WinPtyProcess)process;
+ try {
+ String executable = FileUtil.toSystemIndependentName(StringUtil.notNullize(getExecutable(winPty.getChildProcessId())));
+ int consoleProcessCount = winPty.getConsoleProcessCount();
+ if (executable.endsWith("/Git/bin/bash.exe")) {
+ return consoleProcessCount > 3;
+ }
+ return consoleProcessCount > 2;
+ }
+ catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ throw new IllegalStateException("Unknown OS: " + SystemInfo.OS_NAME);
+ }
+
+ @Nullable
+ private static String getExecutable(int pid) {
+ WinProcess winProcess = new WinProcess(pid);
+ String commandLine;
+ try {
+ commandLine = winProcess.getCommandLine();
+ }
+ catch (WinpException e) {
+ LOG.error(e);
+ return null;
+ }
+ return ContainerUtil.getFirstItem(ParametersListUtil.parse(commandLine));
+ }
}