mainThreadTerminalService.ts 9.2 KB
Newer Older
D
Daniel Imms 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

J
Johannes Rieken 已提交
7
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
8
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY } from 'vs/workbench/parts/terminal/common/terminal';
J
Johannes Rieken 已提交
9
import { TPromise } from 'vs/base/common/winjs.base';
A
Alex Dima 已提交
10
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto } from 'vs/workbench/api/node/extHost.protocol';
11
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
D
Daniel Imms 已提交
12

13
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
14
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
D
Daniel Imms 已提交
15

16
	private _proxy: ExtHostTerminalServiceShape;
17 18
	private _toDispose: IDisposable[] = [];
	private _terminalProcesses: { [id: number]: ITerminalProcessExtHostProxy } = {};
19 20
	private _terminalOnDidWriteDataListeners: { [id: number]: IDisposable } = {};
	private _terminalOnDidAcceptInputListeners: { [id: number]: IDisposable } = {};
21

D
Daniel Imms 已提交
22
	constructor(
23
		extHostContext: IExtHostContext,
24
		@ITerminalService private terminalService: ITerminalService
D
Daniel Imms 已提交
25
	) {
26
		this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
D
Daniel Imms 已提交
27
		this._toDispose.push(terminalService.onInstanceCreated((instance) => {
D
Daniel Imms 已提交
28
			// Delay this message so the TerminalInstance constructor has a chance to finish and
29 30
			// return the ID normally to the extension host. The ID that is passed here will be used
			// to register non-extension API terminals in the extension host.
31
			setTimeout(() => this._onTerminalOpened(instance), EXT_HOST_CREATION_DELAY);
32
		}));
D
Daniel Imms 已提交
33 34 35
		this._toDispose.push(terminalService.onInstanceDisposed(instance => this._onTerminalDisposed(instance)));
		this._toDispose.push(terminalService.onInstanceProcessIdReady(instance => this._onTerminalProcessIdReady(instance)));
		this._toDispose.push(terminalService.onInstanceDimensionsChanged(instance => this._onInstanceDimensionsChanged(instance)));
36
		this._toDispose.push(terminalService.onInstanceRequestExtHostProcess(request => this._onTerminalRequestExtHostProcess(request)));
37
		this._toDispose.push(terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.id : undefined)));
D
Daniel Imms 已提交
38 39 40 41 42 43

		// Set initial ext host state
		this.terminalService.terminalInstances.forEach(t => {
			this._onTerminalOpened(t);
			t.processReady.then(() => this._onTerminalProcessIdReady(t));
		});
44 45 46 47
		const activeInstance = this.terminalService.getActiveInstance();
		if (activeInstance) {
			this._proxy.$acceptActiveTerminalChanged(activeInstance.id);
		}
48 49 50 51
	}

	public dispose(): void {
		this._toDispose = dispose(this._toDispose);
52 53 54

		// TODO@Daniel: Should all the previously created terminals be disposed
		// when the extension host process goes down ?
D
Daniel Imms 已提交
55
	}
D
Daniel Imms 已提交
56

57
	public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string, env?: { [key: string]: string }, waitOnExit?: boolean): TPromise<number> {
58 59 60 61
		const shellLaunchConfig: IShellLaunchConfig = {
			name,
			executable: shellPath,
			args: shellArgs,
D
Daniel Imms 已提交
62
			cwd,
63
			waitOnExit,
64 65
			ignoreConfigurationCwd: true,
			env
66
		};
67
		return TPromise.as(this.terminalService.createTerminal(shellLaunchConfig).id);
D
Daniel Imms 已提交
68 69
	}

70
	public $createTerminalRenderer(name: string): TPromise<number> {
D
Daniel Imms 已提交
71 72
		const instance = this.terminalService.createTerminalRenderer(name);
		return TPromise.as(instance.id);
73 74
	}

D
Daniel Imms 已提交
75
	public $show(terminalId: number, preserveFocus: boolean): void {
D
Daniel Imms 已提交
76
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
77 78 79 80
		if (terminalInstance) {
			this.terminalService.setActiveInstance(terminalInstance);
			this.terminalService.showPanel(!preserveFocus);
		}
D
Daniel Imms 已提交
81
	}
D
Daniel Imms 已提交
82

D
Daniel Imms 已提交
83
	public $hide(terminalId: number): void {
84
		if (this.terminalService.getActiveInstance().id === terminalId) {
D
Daniel Imms 已提交
85
			this.terminalService.hidePanel();
86
		}
D
Daniel Imms 已提交
87 88 89
	}

	public $dispose(terminalId: number): void {
D
Daniel Imms 已提交
90
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
91 92 93
		if (terminalInstance) {
			terminalInstance.dispose();
		}
D
Daniel Imms 已提交
94 95
	}

96
	public $terminalRendererWrite(terminalId: number, text: string): void {
D
Daniel Imms 已提交
97
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
98 99 100 101 102
		if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
			terminalInstance.write(text);
		}
	}

103 104 105 106 107 108 109
	public $terminalRendererSetName(terminalId: number, name: string): void {
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
		if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
			terminalInstance.setTitle(name, false);
		}
	}

D
Daniel Imms 已提交
110 111 112 113 114 115 116
	public $terminalRendererSetDimensions(terminalId: number, dimensions: ITerminalDimensions): void {
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
		if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
			terminalInstance.setDimensions(dimensions);
		}
	}

D
Daniel Imms 已提交
117
	public $terminalRendererRegisterOnInputListener(terminalId: number): void {
118
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
119 120 121 122 123 124 125
		if (!terminalInstance) {
			return;
		}

		// Listener already registered
		if (this._terminalOnDidAcceptInputListeners.hasOwnProperty(terminalId)) {
			return;
126
		}
127 128 129 130

		// Register
		this._terminalOnDidAcceptInputListeners[terminalId] = terminalInstance.onRendererInput(data => this._onTerminalRendererInput(terminalId, data));
		terminalInstance.addDisposable(this._terminalOnDidAcceptInputListeners[terminalId]);
131 132
	}

D
Daniel Imms 已提交
133
	public $sendText(terminalId: number, text: string, addNewLine: boolean): void {
D
Daniel Imms 已提交
134
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
135 136 137
		if (terminalInstance) {
			terminalInstance.sendText(text, addNewLine);
		}
D
Daniel Imms 已提交
138
	}
139

D
Daniel Imms 已提交
140
	public $registerOnDataListener(terminalId: number): void {
D
Daniel Imms 已提交
141
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
142 143
		if (!terminalInstance) {
			return;
D
Daniel Imms 已提交
144
		}
145 146 147 148 149 150 151 152 153 154 155

		// Listener already registered
		if (this._terminalOnDidWriteDataListeners[terminalId]) {
			return;
		}

		// Register
		this._terminalOnDidWriteDataListeners[terminalId] = terminalInstance.onData(data => {
			this._onTerminalData(terminalId, data);
		});
		terminalInstance.addDisposable(this._terminalOnDidWriteDataListeners[terminalId]);
D
Daniel Imms 已提交
156 157
	}

158 159 160 161
	private _onActiveTerminalChanged(terminalId: number | undefined): void {
		this._proxy.$acceptActiveTerminalChanged(terminalId);
	}

D
Daniel Imms 已提交
162 163 164 165
	private _onTerminalData(terminalId: number, data: string): void {
		this._proxy.$acceptTerminalProcessData(terminalId, data);
	}

D
Daniel Imms 已提交
166 167
	private _onTerminalRendererInput(terminalId: number, data: string): void {
		this._proxy.$acceptTerminalRendererInput(terminalId, data);
168 169
	}

170
	private _onTerminalDisposed(terminalInstance: ITerminalInstance): void {
171 172
		this._proxy.$acceptTerminalClosed(terminalInstance.id);
	}
173

174 175 176 177
	private _onTerminalOpened(terminalInstance: ITerminalInstance): void {
		this._proxy.$acceptTerminalOpened(terminalInstance.id, terminalInstance.title);
	}

178 179 180
	private _onTerminalProcessIdReady(terminalInstance: ITerminalInstance): void {
		this._proxy.$acceptTerminalProcessId(terminalInstance.id, terminalInstance.processId);
	}
181

D
Daniel Imms 已提交
182 183 184 185 186 187 188 189
	private _onInstanceDimensionsChanged(instance: ITerminalInstance): void {
		// Only send the dimensions if the terminal is a renderer only as there is no API to access
		// dimensions on a plain Terminal.
		if (instance.shellLaunchConfig.isRendererOnly) {
			this._proxy.$acceptTerminalRendererDimensions(instance.id, instance.cols, instance.rows);
		}
	}

190 191 192 193 194 195 196 197 198 199
	private _onTerminalRequestExtHostProcess(request: ITerminalProcessExtHostRequest): void {
		this._terminalProcesses[request.proxy.terminalId] = request.proxy;
		const shellLaunchConfigDto: ShellLaunchConfigDto = {
			name: request.shellLaunchConfig.name,
			executable: request.shellLaunchConfig.executable,
			args: request.shellLaunchConfig.args,
			cwd: request.shellLaunchConfig.cwd,
			env: request.shellLaunchConfig.env
		};
		this._proxy.$createProcess(request.proxy.terminalId, shellLaunchConfigDto, request.cols, request.rows);
200
		request.proxy.onInput(data => this._proxy.$acceptProcessInput(request.proxy.terminalId, data));
D
Daniel Imms 已提交
201
		request.proxy.onResize(dimensions => this._proxy.$acceptProcessResize(request.proxy.terminalId, dimensions.cols, dimensions.rows));
202
		request.proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(request.proxy.terminalId, immediate));
203 204 205 206 207 208 209 210 211 212 213 214 215
	}

	public $sendProcessTitle(terminalId: number, title: string): void {
		this._terminalProcesses[terminalId].emitTitle(title);
	}

	public $sendProcessData(terminalId: number, data: string): void {
		this._terminalProcesses[terminalId].emitData(data);
	}

	public $sendProcessPid(terminalId: number, pid: number): void {
		this._terminalProcesses[terminalId].emitPid(pid);
	}
D
Daniel Imms 已提交
216 217 218

	public $sendProcessExit(terminalId: number, exitCode: number): void {
		this._terminalProcesses[terminalId].emitExit(exitCode);
D
Daniel Imms 已提交
219
		delete this._terminalProcesses[terminalId];
D
Daniel Imms 已提交
220
	}
D
Daniel Imms 已提交
221
}