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

6
import DOM = require('vs/base/browser/dom');
7
import lifecycle = require('vs/base/common/lifecycle');
8
import platform = require('vs/base/common/platform');
9
import {Action, IAction} from 'vs/base/common/actions';
D
Daniel Imms 已提交
10
import {Builder, Dimension} from 'vs/base/browser/builder';
D
Daniel Imms 已提交
11
import {CloseTerminalAction, CreateNewTerminalAction, SwitchTerminalInstanceAction, SwitchTerminalInstanceActionItem} from 'vs/workbench/parts/terminal/electron-browser/terminalActions';
12
import {IActionItem} from 'vs/base/browser/ui/actionbar/actionbar';
13
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
D
Daniel Imms 已提交
14
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
D
Daniel Imms 已提交
15
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
16
import {ITerminalService, TERMINAL_PANEL_ID} from 'vs/workbench/parts/terminal/electron-browser/terminal';
17
import {IThemeService} from 'vs/workbench/services/themes/common/themeService';
18
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
D
Daniel Imms 已提交
19
import {Panel} from 'vs/workbench/browser/panel';
20
import {TPromise} from 'vs/base/common/winjs.base';
21 22
import {TerminalConfigHelper} from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper';
import {TerminalInstance} from 'vs/workbench/parts/terminal/electron-browser/terminalInstance';
D
Daniel Imms 已提交
23 24 25

export class TerminalPanel extends Panel {

26 27 28
	private toDispose: lifecycle.IDisposable[] = [];
	private terminalInstances: TerminalInstance[] = [];

D
Daniel Imms 已提交
29
	private actions: IAction[];
D
Daniel Imms 已提交
30
	private parentDomElement: HTMLElement;
D
Daniel Imms 已提交
31
	private terminalContainer: HTMLElement;
32
	private themeStyleElement: HTMLElement;
33
	private configurationHelper: TerminalConfigHelper;
34
	private activeTerminalIndex: number;
D
Daniel Imms 已提交
35 36

	constructor(
37
		@ITelemetryService telemetryService: ITelemetryService,
D
Daniel Imms 已提交
38 39
		@IConfigurationService private configurationService: IConfigurationService,
		@IInstantiationService private instantiationService: IInstantiationService,
40
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
41 42
		@ITerminalService private terminalService: ITerminalService,
		@IThemeService private themeService: IThemeService
D
Daniel Imms 已提交
43 44 45 46
	) {
		super(TERMINAL_PANEL_ID, telemetryService);
	}

47
	public layout(dimension?: Dimension): void {
D
Daniel Imms 已提交
48 49 50
		if (!dimension) {
			return;
		}
51
		if (this.terminalInstances.length > 0) {
52
			this.terminalInstances[this.activeTerminalIndex].layout(dimension);
53
		}
D
Daniel Imms 已提交
54 55
	}

D
Daniel Imms 已提交
56 57 58
	public getActions(): IAction[] {
		if (!this.actions) {
			this.actions = [
59
				this.instantiationService.createInstance(SwitchTerminalInstanceAction, SwitchTerminalInstanceAction.ID, SwitchTerminalInstanceAction.LABEL),
D
Daniel Imms 已提交
60 61
				this.instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.LABEL),
				this.instantiationService.createInstance(CloseTerminalAction, CloseTerminalAction.ID, CloseTerminalAction.LABEL)
D
Daniel Imms 已提交
62 63 64 65 66 67 68 69 70 71
			];

			this.actions.forEach(a => {
				this.toDispose.push(a);
			});
		}

		return this.actions;
	}

72 73 74 75 76 77 78 79
	public getActionItem(action: Action): IActionItem {
		if (action.id === SwitchTerminalInstanceAction.ID) {
			return this.instantiationService.createInstance(SwitchTerminalInstanceActionItem, action);
		}

		return super.getActionItem(action);
	}

D
Daniel Imms 已提交
80 81
	public create(parent: Builder): TPromise<void> {
		super.create(parent);
82
		this.parentDomElement = parent.getHTMLElement();
83
		this.terminalService.initConfigHelper(this.parentDomElement);
D
Daniel Imms 已提交
84
		DOM.addClass(this.parentDomElement, 'integrated-terminal');
85
		this.themeStyleElement = document.createElement('style');
D
Daniel Imms 已提交
86

D
Daniel Imms 已提交
87
		this.terminalContainer = document.createElement('div');
D
Daniel Imms 已提交
88
		DOM.addClass(this.terminalContainer, 'terminal-outer-container');
89
		this.parentDomElement.appendChild(this.themeStyleElement);
D
Daniel Imms 已提交
90 91
		this.parentDomElement.appendChild(this.terminalContainer);

92
		this.configurationHelper = new TerminalConfigHelper(platform.platform, this.configurationService, this.parentDomElement);
D
Daniel Imms 已提交
93
		this.toDispose.push(DOM.addDisposableListener(this.terminalContainer, 'wheel', (event: WheelEvent) => {
94
			this.terminalInstances[this.activeTerminalIndex].dispatchEvent(new WheelEvent(event.type, event));
95
		}));
96

D
Daniel Imms 已提交
97 98 99
		return this.createTerminal().then(() => {
			return Promise.resolve(void 0);
		});
100 101
	}

D
Daniel Imms 已提交
102
	public createNewTerminalInstance(): TPromise<void> {
103 104
		return this.createTerminal().then(() => {
			this.updateFont();
D
Daniel Imms 已提交
105
			this.focus();
106 107 108
		});
	}

D
Daniel Imms 已提交
109
	public closeActiveTerminal(): TPromise<void> {
110 111 112 113
		return this.closeTerminal(this.activeTerminalIndex);
	}

	public closeTerminal(index: number): TPromise<void> {
D
Daniel Imms 已提交
114
		return new TPromise<void>(resolve => {
115
			this.onTerminalInstanceExit(this.terminalInstances[index]);
D
Daniel Imms 已提交
116 117 118
		});
	}

119
	public setVisible(visible: boolean): TPromise<void> {
120
		if (visible) {
121
			if (this.terminalInstances.length > 0) {
122 123 124 125
				this.updateFont();
				this.updateTheme();
			} else {
				return super.setVisible(visible).then(() => {
D
Daniel Imms 已提交
126
					this.createNewTerminalInstance();
127 128
				});
			}
129 130 131 132
		}
		return super.setVisible(visible);
	}

D
Daniel Imms 已提交
133 134
	private createTerminal(): TPromise<TerminalInstance> {
		return new TPromise<TerminalInstance>(resolve => {
D
Daniel Imms 已提交
135
			var terminalInstance = new TerminalInstance(this.configurationHelper.getShell(), this.terminalContainer, this.contextService, this.terminalService, this.onTerminalInstanceExit.bind(this));
D
Daniel Imms 已提交
136
			this.terminalInstances.push(terminalInstance);
137
			this.setActiveTerminal(this.terminalInstances.length - 1);
138 139
			this.toDispose.push(this.themeService.onDidThemeChange(this.updateTheme.bind(this)));
			this.toDispose.push(this.configurationService.onDidUpdateConfiguration(this.updateFont.bind(this)));
D
Daniel Imms 已提交
140
			resolve(terminalInstance);
D
Daniel Imms 已提交
141 142
		});
	}
143

144
	/*private getTerminalInstanceIndex(terminalInstance: TerminalInstance): number {
D
Daniel Imms 已提交
145 146 147 148 149 150
		for (let i = 0; i < this.terminalInstances.length; i++) {
			if (terminalInstance === this.terminalInstances[i]) {
				return i;
			}
		};
		return -1;
151
	}*/
D
Daniel Imms 已提交
152

153 154 155
	private setActiveTerminal(index: number) {
		this.activeTerminalIndex = index;
		this.terminalInstances.forEach((terminalInstance, i) => {
156
			terminalInstance.toggleVisibility(i === this.activeTerminalIndex);
157 158 159
		});
	}

160
	private onTerminalInstanceExit(terminalInstance: TerminalInstance): void {
161 162
		for (var i = 0; i < this.terminalInstances.length; i++) {
			if (this.terminalInstances[i] === terminalInstance) {
D
Daniel Imms 已提交
163
				if (this.activeTerminalIndex > i) {
164 165
					this.activeTerminalIndex--;
				}
D
Daniel Imms 已提交
166 167
				let killedTerminal = this.terminalInstances.splice(i, 1)[0];
				killedTerminal.dispose();
168
			}
169
		}
D
Daniel Imms 已提交
170 171 172 173 174 175
		if (this.terminalInstances.length === 0) {
			this.activeTerminalIndex = -1;
			this.terminalService.toggle();
		} else {
			this.setActiveTerminal(Math.min(this.activeTerminalIndex, this.terminalInstances.length - 1));
		}
176 177 178 179 180 181
	}

	private updateTheme(themeId?: string): void {
		if (!themeId) {
			themeId = this.themeService.getTheme();
		}
D
Daniel Imms 已提交
182 183 184 185
		let theme = this.configurationHelper.getTheme(themeId);

		let css = '';
		theme.forEach((color: string, index: number) => {
186
			// TODO: The classes could probably be reduced, it's so long to beat the specificity of the general rule.
D
Daniel Imms 已提交
187
			let rgba = this.convertHexCssColorToRgba(color, 0.996);
D
Daniel Imms 已提交
188 189 190 191
			css += `.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-${index} { color: ${color}; }` +
				`.monaco-workbench .panel.integrated-terminal .xterm .xterm-color-${index}::selection { background-color: ${rgba}; }` +
				`.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-${index} { background-color: ${color}; }` +
				`.monaco-workbench .panel.integrated-terminal .xterm .xterm-bg-color-${index}::selection { color: ${color}; }`;
D
Daniel Imms 已提交
192 193
		});

194
		this.themeStyleElement.innerHTML = css;
D
Daniel Imms 已提交
195 196
	}

D
Daniel Imms 已提交
197 198 199
	/**
	 * Converts a CSS hex color (#rrggbb) to a CSS rgba color (rgba(r, g, b, a)).
	 */
D
Daniel Imms 已提交
200 201 202 203 204
	private convertHexCssColorToRgba(hex: string, alpha: number): string {
		let r = parseInt(hex.substr(1, 2), 16);
		let g = parseInt(hex.substr(3, 2), 16);
		let b = parseInt(hex.substr(5, 2), 16);
		return `rgba(${r}, ${g}, ${b}, ${alpha})`;
205 206
	}

207
	private updateFont(): void {
208
		if (this.terminalInstances.length === 0) {
209 210
			return;
		}
211
		this.terminalInstances[this.activeTerminalIndex].setFont(this.configurationHelper.getFont());
212
		this.layout(new Dimension(this.parentDomElement.offsetWidth, this.parentDomElement.offsetHeight));
213 214
	}

215
	public focus(): void {
216 217
		if (this.terminalInstances.length > 0) {
			this.terminalInstances[this.activeTerminalIndex].focus(true);
218 219 220
		}
	}

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
	public focusNext(): void {
		if (this.terminalInstances.length > 1) {
			this.activeTerminalIndex++;
			if (this.activeTerminalIndex >= this.terminalInstances.length) {
				this.activeTerminalIndex = 0;
			}
			this.setActiveTerminal(this.activeTerminalIndex);
			this.focus();
		}
	}

	public focusPrevious(): void {
		if (this.terminalInstances.length > 1) {
			this.activeTerminalIndex--;
			if (this.activeTerminalIndex < 0) {
				this.activeTerminalIndex = this.terminalInstances.length - 1;
			}
			this.setActiveTerminal(this.activeTerminalIndex);
			this.focus();
		}
	}

243 244
	public dispose(): void {
		this.toDispose = lifecycle.dispose(this.toDispose);
245 246
		while (this.terminalInstances.length > 0) {
			this.terminalInstances.pop().dispose();
247
		}
248 249
		super.dispose();
	}
D
Daniel Imms 已提交
250
}