terminalInstance.ts 4.9 KB
Newer Older
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 DOM = require('vs/base/browser/dom');
7
import lifecycle = require('vs/base/common/lifecycle');
C
Christof Marti 已提交
8
import nls = require('vs/nls');
9
import platform = require('vs/base/common/platform');
10
import xterm = require('xterm');
11
import {Dimension} from 'vs/base/browser/builder';
C
Christof Marti 已提交
12
import {IMessageService, Severity} from 'vs/platform/message/common/message';
13
import {ITerminalFont} from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper';
14 15
import {ITerminalProcess, ITerminalService} from 'vs/workbench/parts/terminal/electron-browser/terminal';
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
16 17

export class TerminalInstance {
18
	private isExiting: boolean = false;
19 20

	private toDispose: lifecycle.IDisposable[];
21
	private xterm;
22
	private terminalDomElement: HTMLDivElement;
23
	private wrapperElement: HTMLDivElement;
24 25 26
	private font: ITerminalFont;

	public constructor(
27
		private terminalProcess: ITerminalProcess,
28 29 30
		private parentDomElement: HTMLElement,
		private contextService: IWorkspaceContextService,
		private terminalService: ITerminalService,
C
Christof Marti 已提交
31
		private messageService: IMessageService,
32 33 34
		private onExitCallback: (TerminalInstance) => void
	) {
		this.toDispose = [];
35
		this.wrapperElement = document.createElement('div');
D
Tidy up  
Daniel Imms 已提交
36
		DOM.addClass(this.wrapperElement, 'terminal-wrapper');
37
		this.terminalDomElement = document.createElement('div');
38
		this.xterm = xterm();
39

40
		this.terminalProcess.process.on('message', (message) => {
D
Daniel Imms 已提交
41
			if (message.type === 'data') {
42
				this.xterm.write(message.content);
D
Daniel Imms 已提交
43
			}
44
		});
45
		this.xterm.on('data', (data) => {
46
			this.terminalProcess.process.send({
47 48 49 50 51
				event: 'input',
				data: data
			});
			return false;
		});
52
		this.terminalProcess.process.on('exit', (exitCode) => {
53 54 55 56 57
			// Prevent dispose functions being triggered multiple times
			if (!this.isExiting) {
				this.isExiting = true;
				this.dispose();
				if (exitCode) {
C
Christof Marti 已提交
58
					this.messageService.show(Severity.Error, nls.localize('terminal.integrated.exitedWithCode', 'The terminal process terminated with exit code: {0}', exitCode));
59 60
				}
				this.onExitCallback(this);
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
			}
		});
		this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'mousedown', (event) => {
			// 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.focus(true);
			}
		}));
		this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'mouseup', (event) => {
			if (event.which !== 3) {
				this.focus();
			}
		}));
		this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'keyup', (event: KeyboardEvent) => {
			// Keep terminal open on escape
			if (event.keyCode === 27) {
				event.stopPropagation();
			}
		}));

82
		this.xterm.open(this.terminalDomElement);
83
		this.wrapperElement.appendChild(this.terminalDomElement);
84
		this.parentDomElement.appendChild(this.wrapperElement);
85 86 87 88 89 90
	}

	public layout(dimension: Dimension): void {
		if (!this.font || !this.font.charWidth || !this.font.charHeight) {
			return;
		}
91 92 93
		if (!dimension.height) { // Minimized
			return;
		}
D
Daniel Imms 已提交
94 95
		let cols = Math.floor(dimension.width / this.font.charWidth);
		let rows = Math.floor(dimension.height / this.font.charHeight);
96 97
		if (this.xterm) {
			this.xterm.resize(cols, rows);
98
		}
99 100
		if (this.terminalProcess.process.connected) {
			this.terminalProcess.process.send({
101 102 103 104 105 106 107
				event: 'resize',
				cols: cols,
				rows: rows
			});
		}
	}

108
	public toggleVisibility(visible: boolean) {
D
Daniel Imms 已提交
109
		DOM.toggleClass(this.wrapperElement, 'active', visible);
110 111
	}

112 113 114
	public setFont(font: ITerminalFont): void {
		this.font = font;
		this.terminalDomElement.style.fontFamily = this.font.fontFamily;
115 116
		this.terminalDomElement.style.lineHeight = this.font.lineHeight;
		this.terminalDomElement.style.fontSize = this.font.fontSize;
117 118
	}

D
Daniel Imms 已提交
119 120 121 122 123 124 125
	public setCursorBlink(blink: boolean): void {
		if (this.xterm && this.xterm.cursorBlink !== blink) {
			this.xterm.cursorBlink = blink;
			this.xterm.refresh(0, this.xterm.rows - 1);
		}
	}

126
	public focus(force?: boolean): void {
127
		if (!this.xterm) {
128 129 130 131
			return;
		}
		let text = window.getSelection().toString();
		if (!text || force) {
132
			this.xterm.focus();
133 134 135 136
		}
	}

	public dispose(): void {
D
Daniel Imms 已提交
137 138 139 140
		if (this.wrapperElement) {
			this.parentDomElement.removeChild(this.wrapperElement);
			this.wrapperElement = null;
		}
141 142 143 144 145 146 147 148
		if (this.xterm) {
			this.xterm.destroy();
			this.xterm = null;
		}
		if (this.terminalProcess) {
			this.terminalService.killTerminalProcess(this.terminalProcess);
			this.terminalProcess = null;
		}
149 150 151
		this.toDispose = lifecycle.dispose(this.toDispose);
	}
}