diff --git a/packages/debug/src/browser/debug-session.tsx b/packages/debug/src/browser/debug-session.tsx index 223f4f5b00ca1..e83fe5f55579b 100644 --- a/packages/debug/src/browser/debug-session.tsx +++ b/packages/debug/src/browser/debug-session.tsx @@ -374,7 +374,13 @@ export class DebugSession implements CompositeTreeElement { } protected async doCreateTerminal(options: TerminalWidgetOptions): Promise { - let terminal = this.terminalServer.all.find(t => t.title.label === options.title || t.title.caption === options.title); + let terminal = undefined; + for (const t of this.terminalServer.all) { + if ((t.title.label === options.title || t.title.caption === options.title) && (await t.hasChildProcesses()) === false) { + terminal = t; + } + } + if (!terminal) { terminal = await this.terminalServer.newTerminal(options); await terminal.start(); diff --git a/packages/terminal/src/browser/base/terminal-widget.ts b/packages/terminal/src/browser/base/terminal-widget.ts index e63c91b851f02..8f04abf5aa0b0 100644 --- a/packages/terminal/src/browser/base/terminal-widget.ts +++ b/packages/terminal/src/browser/base/terminal-widget.ts @@ -47,6 +47,11 @@ export abstract class TerminalWidget extends BaseWidget { * Cleat terminal output. */ abstract clearOutput(): void; + + /** + * Whether the terminal process has child processes. + */ + abstract hasChildProcesses(): Promise; } /** diff --git a/packages/terminal/src/browser/terminal-widget-impl.ts b/packages/terminal/src/browser/terminal-widget-impl.ts index f04d0ac74ba4b..1fa778db17014 100644 --- a/packages/terminal/src/browser/terminal-widget-impl.ts +++ b/packages/terminal/src/browser/terminal-widget-impl.ts @@ -263,6 +263,10 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget this.term.clear(); } + async hasChildProcesses(): Promise { + return this.shellTerminalServer.hasChildProcesses(await this.processId); + } + storeState(): object { this.closeOnDispose = false; return { terminalId: this.terminalId, titleLabel: this.title.label }; diff --git a/packages/terminal/src/common/shell-terminal-protocol.ts b/packages/terminal/src/common/shell-terminal-protocol.ts index 8cd3b83f9547a..88b6ed128d8c3 100644 --- a/packages/terminal/src/common/shell-terminal-protocol.ts +++ b/packages/terminal/src/common/shell-terminal-protocol.ts @@ -20,6 +20,7 @@ import { IBaseTerminalServer, IBaseTerminalServerOptions } from './base-terminal export const IShellTerminalServer = Symbol('IShellTerminalServer'); export interface IShellTerminalServer extends IBaseTerminalServer { + hasChildProcesses(processId: number | undefined): Promise; } export const shellTerminalPath = '/services/shell-terminal'; diff --git a/packages/terminal/src/node/shell-terminal-server.ts b/packages/terminal/src/node/shell-terminal-server.ts index 0c6bf8ce6af09..3b5322bc73e95 100644 --- a/packages/terminal/src/node/shell-terminal-server.ts +++ b/packages/terminal/src/node/shell-terminal-server.ts @@ -20,6 +20,8 @@ import { IShellTerminalServerOptions } from '../common/shell-terminal-protocol'; import { BaseTerminalServer } from '../node/base-terminal-server'; import { ShellProcessFactory } from '../node/shell-process'; import { ProcessManager } from '@theia/process/lib/node'; +import { isWindows } from '@theia/core/lib/common/os'; +import * as cp from 'child_process'; @injectable() export class ShellTerminalServer extends BaseTerminalServer { @@ -41,4 +43,35 @@ export class ShellTerminalServer extends BaseTerminalServer { return Promise.resolve(-1); } } + + // copied and modified from https://github.com/microsoft/vscode/blob/4636be2b71c87bfb0bfe3c94278b447a5efcc1f1/src/vs/workbench/contrib/debug/node/terminals.ts#L50-L75 + async hasChildProcesses(processId: number | undefined): Promise { + if (processId) { + try { + // if shell has at least one child process, assume that shell is busy + if (isWindows) { + const result = cp.spawnSync('wmic', ['process', 'get', 'ParentProcessId']); + if (result.stdout) { + const pids = result.stdout.toString().split('\r\n'); + return pids.some(p => parseInt(p) === processId); + } + } else { + const result = cp.spawnSync('/usr/bin/pgrep', ['-lP', String(processId)]); + if (result.stdout) { + const r = result.stdout.toString().trim(); + if (r.length === 0 || r.indexOf(' tmux') >= 0) { + return false; + } else { + return true; + } + } + } + } catch (e) { + // silently ignore + } + } + // fall back to safe side + return true; + } + }