diff --git a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts index 4ddd5ea82a3..d817564f633 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts @@ -71,7 +71,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb return Promise.resolve(this._proxy.$substituteVariables(folder ? folder.uri : undefined, config)); } - runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { return Promise.resolve(this._proxy.$runInTerminal(args, config)); } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 58363b7e6e5..0461e14bb4e 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -978,7 +978,7 @@ export type IDebugSessionDto = IDebugSessionFullDto | DebugSessionUUID; export interface ExtHostDebugServiceShape { $substituteVariables(folder: UriComponents | undefined, config: IConfig): Thenable; - $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Thenable; + $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Thenable; $startDASession(handle: number, session: IDebugSessionDto): Thenable; $stopDASession(handle: number): Thenable; $sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void; diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index a0dadc757d4..fb4d48f6bfc 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -280,7 +280,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { // RPC methods (ExtHostDebugServiceShape) - public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Thenable { + public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Thenable { if (args.kind === 'integrated') { @@ -311,12 +311,12 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { this._integratedTerminalInstance.show(); - return new Promise((resolve) => { - setTimeout(_ => { - const command = prepareCommand(args, config); - this._integratedTerminalInstance.sendText(command, true); - resolve(void 0); - }, 500); + return this._integratedTerminalInstance.processId.then(shellProcessId => { + + const command = prepareCommand(args, config); + this._integratedTerminalInstance.sendText(command, true); + + return shellProcessId; }); }); diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index f934d884a42..026fb8c21d3 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -111,7 +111,7 @@ export interface IExpression extends IReplElement, IExpressionContainer { export interface IDebugger { createDebugAdapter(session: IDebugSession, outputService: IOutputService): Promise; - runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise; + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise; getCustomTelemetryService(): Thenable; } @@ -543,7 +543,7 @@ export interface IDebugAdapterProvider { } export interface ITerminalLauncher { - runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise; + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise; } export interface ITerminalSettings { @@ -602,7 +602,7 @@ export interface IConfigurationManager { createDebugAdapter(session: IDebugSession): IDebugAdapter; substituteVariables(debugType: string, folder: IWorkspaceFolder, config: IConfig): Promise; - runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise; + runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise; } export interface ILaunch { diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index 6affbc149b1..6f9a582d4db 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -108,7 +108,7 @@ export class ConfigurationManager implements IConfigurationManager { return Promise.resolve(config); } - public runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { + public runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { let tl: ITerminalLauncher = this.debugAdapterFactories.get(debugType); if (!tl) { if (!this.terminalLauncher) { diff --git a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts index 888ce94a575..2d86e1b54dc 100644 --- a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts @@ -475,9 +475,14 @@ export class RawDebugSession { switch (request.command) { case 'runInTerminal': - dbgr.runInTerminal(request.arguments).then(_ => { - response.body = {}; - safeSendResponse(response); + dbgr.runInTerminal(request.arguments as DebugProtocol.RunInTerminalRequestArguments).then(shellProcessId => { + const resp = response as DebugProtocol.RunInTerminalResponse; + if (typeof shellProcessId === 'number') { + resp.body = { + shellProcessId: shellProcessId + }; + } + safeSendResponse(resp); }, err => { response.success = false; response.message = err.message; diff --git a/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts b/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts index 94ad0563156..7e5aaa16489 100644 --- a/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts +++ b/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts @@ -21,7 +21,7 @@ export class TerminalLauncher implements ITerminalLauncher { ) { } - runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { if (args.kind === 'external') { return this.nativeTerminalService.runInTerminal(args.title, args.cwd, args.args, args.env || {}); @@ -44,12 +44,30 @@ export class TerminalLauncher implements ITerminalLauncher { this.terminalService.setActiveInstance(t); this.terminalService.showPanel(true); - return new Promise((resolve, error) => { + return new Promise((resolve, error) => { + + if (typeof t.processId === 'number') { + // no need to wait + resolve(t.processId); + } + + // shell not ready: wait for ready event + const toDispose = t.onProcessIdReady(t => { + toDispose.dispose(); + resolve(t.processId); + }); + + // do not wait longer than 1 second setTimeout(_ => { - const command = prepareCommand(args, config); - t.sendText(command, true); - resolve(void 0); - }, 500); + error(new Error('terminal shell timeout')); + }, 1000); + + }).then(shellProcessId => { + + const command = prepareCommand(args, config); + t.sendText(command, true); + + return shellProcessId; }); } } diff --git a/src/vs/workbench/parts/debug/node/debugger.ts b/src/vs/workbench/parts/debug/node/debugger.ts index 5942a817343..392a7188163 100644 --- a/src/vs/workbench/parts/debug/node/debugger.ts +++ b/src/vs/workbench/parts/debug/node/debugger.ts @@ -116,7 +116,7 @@ export class Debugger implements IDebugger { } } - runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise { + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise { const config = this.configurationService.getValue('terminal'); return this.configurationManager.runInTerminal(this.inExtHost() ? this.type : '*', args, config); } diff --git a/src/vs/workbench/parts/debug/node/terminals.ts b/src/vs/workbench/parts/debug/node/terminals.ts index 74d04c9ed4f..5d514e483a6 100644 --- a/src/vs/workbench/parts/debug/node/terminals.ts +++ b/src/vs/workbench/parts/debug/node/terminals.ts @@ -67,10 +67,10 @@ export function getDefaultTerminalWindows(): string { } abstract class TerminalLauncher implements ITerminalLauncher { - public runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { + public runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { return this.runInTerminal0(args.title, args.cwd, args.args, args.env || {}, config); } - runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, config): Promise { + runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, config): Promise { return void 0; } } @@ -79,11 +79,11 @@ class WinTerminalService extends TerminalLauncher { private static readonly CMD = 'cmd.exe'; - public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { + public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { const exec = configuration.external.windowsExec || getDefaultTerminalWindows(); - return new Promise((c, e) => { + return new Promise((c, e) => { const title = `"${dir} - ${TERMINAL_TITLE}"`; const command = `""${args.join('" "')}" & pause"`; // use '|' to only pause on non-zero exit code @@ -107,7 +107,7 @@ class WinTerminalService extends TerminalLauncher { const cmd = cp.spawn(WinTerminalService.CMD, cmdArgs, options); cmd.on('error', e); - c(null); + c(undefined); }); } } @@ -117,11 +117,11 @@ class MacTerminalService extends TerminalLauncher { private static readonly DEFAULT_TERMINAL_OSX = 'Terminal.app'; private static readonly OSASCRIPT = '/usr/bin/osascript'; // osascript is the AppleScript interpreter on OS X - public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { + public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { const terminalApp = configuration.external.osxExec || MacTerminalService.DEFAULT_TERMINAL_OSX; - return new Promise((c, e) => { + return new Promise((c, e) => { if (terminalApp === MacTerminalService.DEFAULT_TERMINAL_OSX || terminalApp === 'iTerm.app') { @@ -163,7 +163,7 @@ class MacTerminalService extends TerminalLauncher { }); osa.on('exit', (code: number) => { if (code === 0) { // OK - c(null); + c(undefined); } else { if (stderr) { const lines = stderr.split('\n', 1); @@ -184,12 +184,12 @@ class LinuxTerminalService extends TerminalLauncher { private static readonly WAIT_MESSAGE = nls.localize('press.any.key', "Press any key to continue..."); - public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { + public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { const terminalConfig = configuration.external; const execThenable: Thenable = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady(); - return new Promise((c, e) => { + return new Promise((c, e) => { let termArgs: string[] = []; //termArgs.push('--title'); @@ -225,7 +225,7 @@ class LinuxTerminalService extends TerminalLauncher { }); cmd.on('exit', (code: number) => { if (code === 0) { // OK - c(null); + c(undefined); } else { if (stderr) { const lines = stderr.split('\n', 1); diff --git a/src/vs/workbench/parts/execution/common/execution.ts b/src/vs/workbench/parts/execution/common/execution.ts index 99a38917a3c..4be8a75555e 100644 --- a/src/vs/workbench/parts/execution/common/execution.ts +++ b/src/vs/workbench/parts/execution/common/execution.ts @@ -11,5 +11,5 @@ export const ITerminalService = createDecorator('nativeTermina export interface ITerminalService { _serviceBrand: any; openTerminal(path: string): void; - runInTerminal(title: string, cwd: string, args: string[], env: IProcessEnvironment): Promise; + runInTerminal(title: string, cwd: string, args: string[], env: IProcessEnvironment): Promise; } \ No newline at end of file diff --git a/src/vs/workbench/parts/execution/electron-browser/terminalService.ts b/src/vs/workbench/parts/execution/electron-browser/terminalService.ts index f5efd5494a2..7fde5a1f56d 100644 --- a/src/vs/workbench/parts/execution/electron-browser/terminalService.ts +++ b/src/vs/workbench/parts/execution/electron-browser/terminalService.ts @@ -37,13 +37,13 @@ export class WinTerminalService implements ITerminalService { this.spawnTerminal(cp, configuration, processes.getWindowsShell(), cwd); } - public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { + public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { const configuration = this._configurationService.getValue(); const terminalConfig = configuration.terminal.external; const exec = terminalConfig.windowsExec || getDefaultTerminalWindows(); - return new Promise((c, e) => { + return new Promise((c, e) => { const title = `"${dir} - ${TERMINAL_TITLE}"`; const command = `""${args.join('" "')}" & pause"`; // use '|' to only pause on non-zero exit code @@ -67,7 +67,7 @@ export class WinTerminalService implements ITerminalService { const cmd = cp.spawn(WinTerminalService.CMD, cmdArgs, options); cmd.on('error', e); - c(); + c(undefined); }); } @@ -128,13 +128,13 @@ export class MacTerminalService implements ITerminalService { this.spawnTerminal(cp, configuration, cwd); } - public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { + public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { const configuration = this._configurationService.getValue(); const terminalConfig = configuration.terminal.external; const terminalApp = terminalConfig.osxExec || DEFAULT_TERMINAL_OSX; - return new Promise((c, e) => { + return new Promise((c, e) => { if (terminalApp === DEFAULT_TERMINAL_OSX || terminalApp === 'iTerm.app') { @@ -176,7 +176,7 @@ export class MacTerminalService implements ITerminalService { }); osa.on('exit', (code: number) => { if (code === 0) { // OK - c(); + c(undefined); } else { if (stderr) { const lines = stderr.split('\n', 1); @@ -220,13 +220,13 @@ export class LinuxTerminalService implements ITerminalService { this.spawnTerminal(cp, configuration, cwd); } - public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { + public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { const configuration = this._configurationService.getValue(); const terminalConfig = configuration.terminal.external; const execPromise = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady(); - return new Promise((c, e) => { + return new Promise((c, e) => { let termArgs: string[] = []; //termArgs.push('--title'); @@ -262,7 +262,7 @@ export class LinuxTerminalService implements ITerminalService { }); cmd.on('exit', (code: number) => { if (code === 0) { // OK - c(); + c(undefined); } else { if (stderr) { const lines = stderr.split('\n', 1);