terminal: show confirmation dialog on attempt to close it if there are running processes (IDEA-205690)

This commit is contained in:
Sergey Simonchik
2019-03-23 14:44:29 +03:00
parent e5a6ee299b
commit daa09d1183
3 changed files with 86 additions and 0 deletions
+1
View File
@@ -22,5 +22,6 @@
<orderEntry type="library" name="jediterm-pty" level="project" />
<orderEntry type="module" module-name="intellij.platform.lang.impl" />
<orderEntry type="module" module-name="intellij.platform.configurationStore.impl" scope="TEST" />
<orderEntry type="library" name="winp" level="project" />
</component>
</module>
@@ -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<TerminalTabCloseListener>()
@@ -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<Integer, Integer> pidToChildPidsMap = MultiMap.create();
UnixProcessManager.processPSOutput(UnixProcessManager.getPSCmd(false, false), new Processor<String>() {
@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));
}
}