From 7236f593e7e8363ac940660a105c15e4f51ea198 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 19 Sep 2016 14:32:35 -0700 Subject: [PATCH] Implement vscode.Terminal.processId Fixes #11919 --- src/vs/vscode.d.ts | 7 +++++ src/vs/workbench/api/node/extHost.protocol.ts | 1 + .../api/node/extHostTerminalService.ts | 26 +++++++++++++++++++ .../api/node/mainThreadTerminalService.ts | 5 ++++ .../terminal/electron-browser/terminal.ts | 6 +++++ .../electron-browser/terminalInstance.ts | 10 ++++++- .../electron-browser/terminalProcess.js | 8 ++++++ .../electron-browser/terminalService.ts | 6 ++++- 8 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index d5017f6361538..4cca5c91cf0bb 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -3012,6 +3012,13 @@ declare namespace vscode { */ name: string; + /** + * The process ID of the shell process. + * + * @readonly + */ + processId: Thenable; + /** * Send text to the terminal. The text is written to the stdin of the underlying pty process * (shell) of the terminal. diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index be5f7ff7d67a8..7dabd44f5758d 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -312,6 +312,7 @@ export abstract class ExtHostQuickOpenShape { export abstract class ExtHostTerminalServiceShape { $acceptTerminalClosed(id: number): void { throw ni(); } + $acceptTerminalProcessId(id: number, processId: number): void { throw ni(); } } // --- proxy identifiers diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index dbc17f3a3df75..ad1365212064c 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -13,6 +13,7 @@ export class ExtHostTerminal implements vscode.Terminal { private _name: string; private _id: number; + private _processId: number; private _proxy: MainThreadTerminalServiceShape; private _disposed: boolean; private _queuedRequests: ApiRequest[]; @@ -34,6 +35,16 @@ export class ExtHostTerminal implements vscode.Terminal { return this._name; } + public get processId(): Thenable { + this._checkDisposed(); + if (this._processId) { + return Promise.resolve(this._processId); + } + setTimeout(() => { + return this.processId; + }, 200); + } + public sendText(text: string, addNewLine: boolean = true): void { this._checkDisposed(); this._queueApiRequest(this._proxy.$sendText, [text, addNewLine]); @@ -56,6 +67,10 @@ export class ExtHostTerminal implements vscode.Terminal { } } + public setProcessId(processId: number): void { + this._processId = processId; + } + private _queueApiRequest(callback: (...args: any[]) => void, args: any[]) { let request: ApiRequest = new ApiRequest(callback, args); if (!this._id) { @@ -104,6 +119,16 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { this._onDidCloseTerminal.fire(terminal); } + public $acceptTerminalProcessId(id: number, processId: number): void { + let terminal = this._getTerminalById(id); + terminal.setProcessId(processId); + } + + private _getTerminalById(id: number): ExtHostTerminal { + let index = this._getTerminalIndexById(id); + return index !== null ? this._terminals[index] : null; + } + private _getTerminalIndexById(id: number): number { let index: number = null; this._terminals.some((terminal, i) => { @@ -118,6 +143,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { } class ApiRequest { + private _callback: (...args: any[]) => void; private _args: any[]; diff --git a/src/vs/workbench/api/node/mainThreadTerminalService.ts b/src/vs/workbench/api/node/mainThreadTerminalService.ts index b41793c9dafc7..4e39271706ccc 100644 --- a/src/vs/workbench/api/node/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/node/mainThreadTerminalService.ts @@ -27,6 +27,7 @@ export class MainThreadTerminalService extends MainThreadTerminalServiceShape { this._proxy = threadService.get(ExtHostContext.ExtHostTerminalService); this._toDispose = []; this._toDispose.push(terminalService.onInstanceDisposed((terminalInstance) => this._onTerminalDisposed(terminalInstance))); + this._toDispose.push(terminalService.onInstanceProcessIdReady((terminalInstance) => this._onTerminalProcessIdReady(terminalInstance))); } public dispose(): void { @@ -68,4 +69,8 @@ export class MainThreadTerminalService extends MainThreadTerminalServiceShape { private _onTerminalDisposed(terminalInstance: ITerminalInstance): void { this._proxy.$acceptTerminalClosed(terminalInstance.id); } + + private _onTerminalProcessIdReady(terminalInstance: ITerminalInstance): void { + this._proxy.$acceptTerminalProcessId(terminalInstance.id, terminalInstance.processId); + } } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.ts index f716d663801aa..755caf5115a8a 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.ts @@ -59,6 +59,7 @@ export interface ITerminalService { configHelper: TerminalConfigHelper; onActiveInstanceChanged: Event; onInstanceDisposed: Event; + onInstanceProcessIdReady: Event; onInstancesChanged: Event; onInstanceTitleChanged: Event; terminalInstances: ITerminalInstance[]; @@ -85,6 +86,11 @@ export interface ITerminalInstance { */ id: number; + /** + * The process ID of the shell process. + */ + processId: number; + /** * An event that fires when the terminal instance's title changes. */ diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 5b7e5093df61a..6a82136f23676 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -34,8 +34,10 @@ export class TerminalInstance implements ITerminalInstance { private _isExiting: boolean; private _isVisible: boolean; private _onDisposed: Emitter; + private _onProcessIdReady: Emitter; private _onTitleChanged: Emitter; private _process: cp.ChildProcess; + private _processId: number; private _skipTerminalKeybindings: Keybinding[]; private _title: string; private _toDispose: lifecycle.IDisposable[]; @@ -44,7 +46,9 @@ export class TerminalInstance implements ITerminalInstance { private _xtermElement: HTMLDivElement; public get id(): number { return this._id; } + public get processId(): number { return this._processId; } public get onClosed(): Event { return this._onDisposed.event; } + public get onProcessIdReady(): Event { return this._onProcessIdReady.event; } public get onTitleChanged(): Event { return this._onTitleChanged.event; } public get title(): string { return this._title; } @@ -64,8 +68,9 @@ export class TerminalInstance implements ITerminalInstance { this._isVisible = false; this._id = TerminalInstance._idCounter++; - this._onTitleChanged = new Emitter(); this._onDisposed = new Emitter(); + this._onProcessIdReady = new Emitter(); + this._onTitleChanged = new Emitter(); this._createProcess(_workspace, name, shell); @@ -94,6 +99,9 @@ export class TerminalInstance implements ITerminalInstance { this._process.on('message', (message) => { if (message.type === 'data') { this._xterm.write(message.content); + } else if (message.type === 'pid') { + this._processId = message.content; + this._onProcessIdReady.fire(this); } }); this._xterm.on('data', (data) => { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcess.js b/src/vs/workbench/parts/terminal/electron-browser/terminalProcess.js index 9b38c52dd7f7a..c6188f790b98b 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcess.js +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcess.js @@ -51,6 +51,7 @@ process.on('message', function (message) { } }); +sendProcessId(); setupTitlePolling(); function getArgs() { @@ -91,6 +92,13 @@ function setupPlanB(parentPid) { }, 5000); } +function sendProcessId() { + process.send({ + type: 'pid', + content: ptyProcess.pid + }); +} + function setupTitlePolling() { sendProcessTitle(); setInterval(function () { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts index e507754516711..f66cd80aa91d2 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts @@ -24,6 +24,7 @@ export class TerminalService implements ITerminalService { private _configHelper: TerminalConfigHelper; private _onActiveInstanceChanged: Emitter; private _onInstanceDisposed: Emitter; + private _onInstanceProcessIdReady: Emitter; private _onInstanceTitleChanged: Emitter; private _onInstancesChanged: Emitter; private _terminalContainer: HTMLElement; @@ -34,6 +35,7 @@ export class TerminalService implements ITerminalService { public get configHelper(): TerminalConfigHelper { return this._configHelper; } public get onActiveInstanceChanged(): Event { return this._onActiveInstanceChanged.event; } public get onInstanceDisposed(): Event { return this._onInstanceDisposed.event; } + public get onInstanceProcessIdReady(): Event { return this._onInstanceProcessIdReady.event; } public get onInstanceTitleChanged(): Event { return this._onInstanceTitleChanged.event; } public get onInstancesChanged(): Event { return this._onInstancesChanged.event; } public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; } @@ -51,8 +53,9 @@ export class TerminalService implements ITerminalService { this._onActiveInstanceChanged = new Emitter(); this._onInstanceDisposed = new Emitter(); - this._onInstancesChanged = new Emitter(); + this._onInstanceProcessIdReady = new Emitter(); this._onInstanceTitleChanged = new Emitter(); + this._onInstancesChanged = new Emitter(); this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService); this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper, platform.platform); @@ -73,6 +76,7 @@ export class TerminalService implements ITerminalService { shell); terminalInstance.addDisposable(terminalInstance.onTitleChanged(this._onInstanceTitleChanged.fire, this._onInstanceTitleChanged)); terminalInstance.addDisposable(terminalInstance.onClosed(this._onInstanceDisposed.fire, this._onInstanceDisposed)); + terminalInstance.addDisposable(terminalInstance.onProcessIdReady(this._onInstanceProcessIdReady.fire, this._onInstanceProcessIdReady)); this.terminalInstances.push(terminalInstance); if (this.terminalInstances.length === 1) { // It's the first instance so it should be made active automatically