terminalPanel.ts 8.7 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 {KillTerminalAction, 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';
C
Christof Marti 已提交
15
import {IMessageService} from 'vs/platform/message/common/message';
D
Daniel Imms 已提交
16
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
17
import {ITerminalProcess, ITerminalService, TERMINAL_PANEL_ID} from 'vs/workbench/parts/terminal/electron-browser/terminal';
18
import {IThemeService} from 'vs/workbench/services/themes/common/themeService';
19
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
D
Daniel Imms 已提交
20
import {Panel} from 'vs/workbench/browser/panel';
21
import {TPromise} from 'vs/base/common/winjs.base';
22 23
import {TerminalConfigHelper} from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper';
import {TerminalInstance} from 'vs/workbench/parts/terminal/electron-browser/terminalInstance';
D
Daniel Imms 已提交
24 25 26

export class TerminalPanel extends Panel {

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

D
Daniel Imms 已提交
30
	private actions: IAction[];
D
Daniel Imms 已提交
31
	private parentDomElement: HTMLElement;
D
Daniel Imms 已提交
32
	private terminalContainer: HTMLElement;
33
	private themeStyleElement: HTMLElement;
34
	private configurationHelper: TerminalConfigHelper;
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
		@ITerminalService private terminalService: ITerminalService,
C
Christof Marti 已提交
42 43
		@IThemeService private themeService: IThemeService,
		@IMessageService private messageService: IMessageService
D
Daniel Imms 已提交
44 45 46 47
	) {
		super(TERMINAL_PANEL_ID, telemetryService);
	}

48
	public layout(dimension?: Dimension): void {
D
Daniel Imms 已提交
49 50 51
		if (!dimension) {
			return;
		}
D
Daniel Imms 已提交
52 53
		let activeIndex = this.terminalService.getActiveTerminalIndex();
		if (activeIndex !== -1 && this.terminalInstances.length > 0) {
54
			this.terminalInstances[this.terminalService.getActiveTerminalIndex()].layout(dimension);
55
		}
D
Daniel Imms 已提交
56 57
	}

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

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

		return this.actions;
	}

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

		return super.getActionItem(action);
	}

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

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

D
Daniel Imms 已提交
94
		this.configurationHelper = new TerminalConfigHelper(platform.platform, this.configurationService, parent);
95

96
		return this.terminalService.createNew();
97 98
	}

99 100
	public createNewTerminalInstance(terminalProcess: ITerminalProcess): TPromise<void> {
		return this.createTerminal(terminalProcess).then(() => {
101
			this.updateConfig();
D
Daniel Imms 已提交
102
			this.focus();
103 104 105
		});
	}

D
Daniel Imms 已提交
106
	public closeActiveTerminal(): TPromise<void> {
107
		return this.closeTerminal(this.terminalService.getActiveTerminalIndex());
108 109 110
	}

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

117
	public setVisible(visible: boolean): TPromise<void> {
118
		if (visible) {
119
			if (this.terminalInstances.length > 0) {
120
				this.updateConfig();
121 122 123
				this.updateTheme();
			} else {
				return super.setVisible(visible).then(() => {
124
					this.terminalService.createNew();
125 126
				});
			}
127 128 129 130
		}
		return super.setVisible(visible);
	}

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

144
	public setActiveTerminal(newActiveIndex: number) {
145
		this.terminalInstances.forEach((terminalInstance, i) => {
146
			terminalInstance.toggleVisibility(i === newActiveIndex);
147 148 149
		});
	}

150
	private onTerminalInstanceExit(terminalInstance: TerminalInstance): void {
151 152
		let index = this.terminalInstances.indexOf(terminalInstance);
		if (index !== -1) {
D
Daniel Imms 已提交
153
			this.terminalInstances[index].dispose();
154
			this.terminalInstances.splice(index, 1);
155
		}
D
Daniel Imms 已提交
156 157 158
		if (this.terminalInstances.length > 0) {
			this.setActiveTerminal(this.terminalService.getActiveTerminalIndex());
		}
D
Daniel Imms 已提交
159
		if (this.terminalInstances.length === 0) {
D
Daniel Imms 已提交
160
			this.terminalService.hide();
D
Daniel Imms 已提交
161
		} else {
D
Daniel Imms 已提交
162
			this.terminalService.focus();
D
Daniel Imms 已提交
163
		}
164 165 166 167 168 169
	}

	private updateTheme(themeId?: string): void {
		if (!themeId) {
			themeId = this.themeService.getTheme();
		}
D
Daniel Imms 已提交
170 171 172 173 174
		let theme = this.configurationHelper.getTheme(themeId);

		let css = '';
		theme.forEach((color: string, index: number) => {
			let rgba = this.convertHexCssColorToRgba(color, 0.996);
D
Daniel Imms 已提交
175 176 177 178
			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 已提交
179 180
		});

181
		this.themeStyleElement.innerHTML = css;
D
Daniel Imms 已提交
182 183
	}

D
Daniel Imms 已提交
184 185 186
	/**
	 * Converts a CSS hex color (#rrggbb) to a CSS rgba color (rgba(r, g, b, a)).
	 */
D
Daniel Imms 已提交
187 188 189 190 191
	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})`;
192 193
	}

194 195 196 197 198
	private updateConfig(): void {
		this.updateFont();
		this.updateCursorBlink();
	}

199
	private updateFont(): void {
200
		if (this.terminalInstances.length === 0) {
201 202
			return;
		}
203
		this.terminalInstances[this.terminalService.getActiveTerminalIndex()].setFont(this.configurationHelper.getFont());
204
		this.layout(new Dimension(this.parentDomElement.offsetWidth, this.parentDomElement.offsetHeight));
205 206
	}

D
Daniel Imms 已提交
207 208 209 210 211 212
	private updateCursorBlink(): void {
		this.terminalInstances.forEach((instance) => {
			instance.setCursorBlink(this.configurationHelper.getCursorBlink());
		});
	}

213
	public focus(): void {
D
Daniel Imms 已提交
214 215 216
		let activeIndex = this.terminalService.getActiveTerminalIndex();
		if (activeIndex !== -1 && this.terminalInstances.length > 0) {
			this.terminalInstances[activeIndex].focus(true);
217 218 219
		}
	}

220 221
	public dispose(): void {
		this.toDispose = lifecycle.dispose(this.toDispose);
222 223
		while (this.terminalInstances.length > 0) {
			this.terminalInstances.pop().dispose();
224
		}
225 226
		super.dispose();
	}
D
Daniel Imms 已提交
227
}