mainThreadTerminalService.ts 8.3 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';
10
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto } from '../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

D
Daniel Imms 已提交
20
	constructor(
21
		extHostContext: IExtHostContext,
22
		@ITerminalService private terminalService: ITerminalService
D
Daniel Imms 已提交
23
	) {
24
		this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
D
Daniel Imms 已提交
25
		this._toDispose.push(terminalService.onInstanceCreated((instance) => {
D
Daniel Imms 已提交
26
			// Delay this message so the TerminalInstance constructor has a chance to finish and
27 28
			// 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.
29
			setTimeout(() => this._onTerminalOpened(instance), EXT_HOST_CREATION_DELAY);
30
		}));
D
Daniel Imms 已提交
31 32 33
		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)));
34
		this._toDispose.push(terminalService.onInstanceRequestExtHostProcess(request => this._onTerminalRequestExtHostProcess(request)));
35
		this._toDispose.push(terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.id : undefined)));
D
Daniel Imms 已提交
36 37 38 39 40 41

		// Set initial ext host state
		this.terminalService.terminalInstances.forEach(t => {
			this._onTerminalOpened(t);
			t.processReady.then(() => this._onTerminalProcessIdReady(t));
		});
42 43 44 45
	}

	public dispose(): void {
		this._toDispose = dispose(this._toDispose);
46 47 48

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

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

64
	public $createTerminalRenderer(name: string): TPromise<number> {
D
Daniel Imms 已提交
65 66
		const instance = this.terminalService.createTerminalRenderer(name);
		return TPromise.as(instance.id);
67 68
	}

D
Daniel Imms 已提交
69
	public $show(terminalId: number, preserveFocus: boolean): void {
D
Daniel Imms 已提交
70
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
71 72 73 74
		if (terminalInstance) {
			this.terminalService.setActiveInstance(terminalInstance);
			this.terminalService.showPanel(!preserveFocus);
		}
D
Daniel Imms 已提交
75
	}
D
Daniel Imms 已提交
76

D
Daniel Imms 已提交
77
	public $hide(terminalId: number): void {
78
		if (this.terminalService.getActiveInstance().id === terminalId) {
D
Daniel Imms 已提交
79
			this.terminalService.hidePanel();
80
		}
D
Daniel Imms 已提交
81 82 83
	}

	public $dispose(terminalId: number): void {
D
Daniel Imms 已提交
84
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
85 86 87
		if (terminalInstance) {
			terminalInstance.dispose();
		}
D
Daniel Imms 已提交
88 89
	}

90
	public $terminalRendererWrite(terminalId: number, text: string): void {
D
Daniel Imms 已提交
91
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
92 93 94 95 96
		if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
			terminalInstance.write(text);
		}
	}

97 98 99 100 101 102 103
	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 已提交
104 105 106 107 108 109 110
	public $terminalRendererSetDimensions(terminalId: number, dimensions: ITerminalDimensions): void {
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
		if (terminalInstance && terminalInstance.shellLaunchConfig.isRendererOnly) {
			terminalInstance.setDimensions(dimensions);
		}
	}

D
Daniel Imms 已提交
111
	public $terminalRendererRegisterOnInputListener(terminalId: number): void {
112 113
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
		if (terminalInstance) {
D
Daniel Imms 已提交
114
			terminalInstance.addDisposable(terminalInstance.onRendererInput(data => this._onTerminalRendererInput(terminalId, data)));
115 116 117
		}
	}

D
Daniel Imms 已提交
118
	public $sendText(terminalId: number, text: string, addNewLine: boolean): void {
D
Daniel Imms 已提交
119
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
120 121 122
		if (terminalInstance) {
			terminalInstance.sendText(text, addNewLine);
		}
D
Daniel Imms 已提交
123
	}
124

D
Daniel Imms 已提交
125
	public $registerOnDataListener(terminalId: number): void {
D
Daniel Imms 已提交
126
		const terminalInstance = this.terminalService.getInstanceFromId(terminalId);
D
Daniel Imms 已提交
127
		if (terminalInstance) {
D
Daniel Imms 已提交
128 129 130
			terminalInstance.addDisposable(terminalInstance.onData(data => {
				this._onTerminalData(terminalId, data);
			}));
D
Daniel Imms 已提交
131 132 133
		}
	}

134 135 136 137
	private _onActiveTerminalChanged(terminalId: number | undefined): void {
		this._proxy.$acceptActiveTerminalChanged(terminalId);
	}

D
Daniel Imms 已提交
138 139 140 141
	private _onTerminalData(terminalId: number, data: string): void {
		this._proxy.$acceptTerminalProcessData(terminalId, data);
	}

D
Daniel Imms 已提交
142 143
	private _onTerminalRendererInput(terminalId: number, data: string): void {
		this._proxy.$acceptTerminalRendererInput(terminalId, data);
144 145
	}

146
	private _onTerminalDisposed(terminalInstance: ITerminalInstance): void {
147 148
		this._proxy.$acceptTerminalClosed(terminalInstance.id);
	}
149

150 151 152 153
	private _onTerminalOpened(terminalInstance: ITerminalInstance): void {
		this._proxy.$acceptTerminalOpened(terminalInstance.id, terminalInstance.title);
	}

154 155 156
	private _onTerminalProcessIdReady(terminalInstance: ITerminalInstance): void {
		this._proxy.$acceptTerminalProcessId(terminalInstance.id, terminalInstance.processId);
	}
157

D
Daniel Imms 已提交
158 159 160 161 162 163 164 165
	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);
		}
	}

166 167 168 169 170 171 172 173 174 175
	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);
176 177 178
		request.proxy.onInput(data => this._proxy.$acceptProcessInput(request.proxy.terminalId, data));
		request.proxy.onResize((cols, rows) => this._proxy.$acceptProcessResize(request.proxy.terminalId, cols, rows));
		request.proxy.onShutdown(() => this._proxy.$acceptProcessShutdown(request.proxy.terminalId));
179 180 181 182 183 184 185 186 187 188 189 190 191
	}

	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 已提交
192 193 194

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