terminalPanel.ts 5.6 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.
 *--------------------------------------------------------------------------------------------*/

import termJs = require('term.js');
7
import lifecycle = require('vs/base/common/lifecycle');
D
Daniel Imms 已提交
8
import fs = require('fs');
9
import DOM = require('vs/base/browser/dom');
D
Daniel Imms 已提交
10
import {fork, Terminal} from 'pty.js';
11
import platform = require('vs/base/common/platform');
D
Daniel Imms 已提交
12 13
import {TPromise} from 'vs/base/common/winjs.base';
import {Builder, Dimension} from 'vs/base/browser/builder';
14
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
D
Daniel Imms 已提交
15
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
16
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
17
import {ITerminalConfiguration, TERMINAL_PANEL_ID} from 'vs/workbench/parts/terminal/common/terminal';
D
Daniel Imms 已提交
18
import {Panel} from 'vs/workbench/browser/panel';
19 20
import {DomScrollableElement} from 'vs/base/browser/ui/scrollbar/scrollableElement';
import {ScrollbarVisibility} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
D
Daniel Imms 已提交
21 22 23 24 25 26

const TERMINAL_CHAR_WIDTH = 8;
const TERMINAL_CHAR_HEIGHT = 18;

export class TerminalPanel extends Panel {

27
	private toDispose: lifecycle.IDisposable[];
D
Daniel Imms 已提交
28 29 30 31 32 33
	private ptyProcess: Terminal;
	private parentDomElement: HTMLElement;
	private terminal;
	private terminalDomElement: HTMLDivElement;

	constructor(
34
		@IConfigurationService private configurationService: IConfigurationService,
35 36
		@ITelemetryService telemetryService: ITelemetryService,
		@IWorkspaceContextService private contextService: IWorkspaceContextService
D
Daniel Imms 已提交
37 38
	) {
		super(TERMINAL_PANEL_ID, telemetryService);
39
		this.toDispose = [];
D
Daniel Imms 已提交
40 41 42 43 44 45 46 47 48 49 50 51
	}

	public layout(dimension: Dimension): void {
		let cols = Math.floor(this.parentDomElement.offsetWidth / TERMINAL_CHAR_WIDTH);
		let rows = Math.floor(this.parentDomElement.offsetHeight / TERMINAL_CHAR_HEIGHT);
		this.terminal.resize(cols, rows);
		this.ptyProcess.resize(cols, rows);
	}

	public create(parent: Builder): TPromise<void> {
		super.create(parent);

52 53
		this.parentDomElement = parent.getHTMLElement();

54
		return this.createTerminal();
55 56
	}

57 58 59 60 61 62 63 64 65
	private createTerminal(): TPromise<void> {
		return new TPromise<void>(resolve => {
			this.parentDomElement.innerHTML = '';
			this.ptyProcess = fork(this.getShell(), [], {
				name: fs.existsSync('/usr/share/terminfo/x/xterm-256color') ? 'xterm-256color' : 'xterm',
				cwd: this.contextService.getWorkspace() ? this.contextService.getWorkspace().resource.fsPath : process.env.HOME
			});
			this.terminalDomElement = document.createElement('div');
			this.parentDomElement.classList.add('integrated-terminal');
66
			let terminalScrollbar = new DomScrollableElement(this.terminalDomElement, {
67 68 69 70
				canUseTranslate3d: false,
				horizontal: ScrollbarVisibility.Hidden,
				vertical: ScrollbarVisibility.Auto
			});
71
			this.toDispose.push(terminalScrollbar);
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
			this.terminal = termJs({
				cursorBlink: false // term.js' blinking cursor breaks selection
			});

			this.ptyProcess.on('data', (data) => {
				this.terminal.write(data);
			});
			this.terminal.on('data', (data) => {
				this.ptyProcess.write(data);
				return false;
			});
			this.ptyProcess.on('exit', (data) => {
				this.terminal.destroy();
				// TODO: When multiple terminals are supported this should do something smarter. There is
				// also a weird bug here at leasy on Ubuntu 15.10 where the new terminal text does not
				// repaint correctly.
				this.createTerminal();
			});
90
			this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'mousedown', (event) => {
91 92 93 94 95
				// Drop selection and focus terminal on Linux to enable middle button paste when click
				// occurs on the selection itself.
				if (event.which === 2 && platform.isLinux) {
					this.focusTerminal(true);
				}
96 97
			}));
			this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'mouseup', (event) => {
98 99 100
				if (event.which !== 3) {
					this.focusTerminal();
				}
101
			}));
102 103

			this.terminal.open(this.terminalDomElement);
104
			this.parentDomElement.appendChild(terminalScrollbar.getDomNode());
105 106 107 108 109

			let config = this.configurationService.getConfiguration<ITerminalConfiguration>();
			this.terminalDomElement.style.fontFamily = config.integratedTerminal.fontFamily;
			this.terminal.colors = this.getTerminalColors();
			resolve(void 0);
D
Daniel Imms 已提交
110 111
		});
	}
112

113 114 115 116
	public focus(): void {
		this.focusTerminal(true);
	}

117
	private focusTerminal(force?: boolean): void {
118
		let text = window.getSelection().toString();
119
		if (!text || force) {
120 121 122 123 124 125 126
			this.terminal.focus();
			if (this.terminal._textarea) {
				this.terminal._textarea.focus();
			}
		}
	}

127 128 129
	private getShell(): string {
		let config = this.configurationService.getConfiguration<ITerminalConfiguration>();
		if (platform.isWindows) {
130
			return config.integratedTerminal.shell.windows;
131
		}
132 133 134 135 136 137
		return config.integratedTerminal.shell.unixLike;
	}

	private getTerminalColors(): string[] {
		let config = this.configurationService.getConfiguration<ITerminalConfiguration>().integratedTerminal.ansiColors;
		let colors = [
D
Daniel Imms 已提交
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
			config.black,
			config.red,
			config.green,
			config.yellow,
			config.blue,
			config.magenta,
			config.cyan,
			config.white,
			config.brightBlack,
			config.brightRed,
			config.brightGreen,
			config.brightYellow,
			config.brightBlue,
			config.brightMagenta,
			config.brightCyan,
			config.brightWhite
154 155
		];
		return colors;
156
	}
157 158 159 160 161

	public dispose(): void {
		this.toDispose = lifecycle.dispose(this.toDispose);
		super.dispose();
	}
D
Daniel Imms 已提交
162
}