Skip to content

Commit

Permalink
Pull individual terminal logic out of terminalPanel
Browse files Browse the repository at this point in the history
The character measure logic has been shuffled around a little and now is done
primarily in setVisible after the terminal is confirmed to be within the DOM.

Part of #6458
  • Loading branch information
Tyriar authored and aeschli committed Jun 6, 2016
1 parent 632641d commit d87ec53
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 182 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ configurationRegistry.registerConfiguration({
'default': TERMINAL_DEFAULT_SHELL_WINDOWS
},
'terminal.integrated.fontFamily': {
'description': nls.localize('terminal.integrated.fontFamily', "The font family used by the terminal (CSS font-family format), this defaults to editor.fontFamily's value."),
'description': nls.localize('terminal.integrated.fontFamily', "Controls the font family of the terminal, this defaults to editor.fontFamily's value."),
'type': 'string'
},
'terminal.integrated.fontSize': {
'description': nls.localize('terminal.integrated.fontSize', "The font size used by the terminal (in pixels), this defaults to editor.fontSize's value."),
'description': nls.localize('terminal.integrated.fontSize', "Controls the font size of the terminal, this defaults to editor.fontSize's value."),
'type': 'number'
},
'terminal.integrated.lineHeight': {
'description': nls.localize('terminal.integrated.lineHeight', "The line height used by the terminal (in pixels), this defaults to editor.lineHeight's value."),
'description': nls.localize('terminal.integrated.lineHeight', "Controls the line height of the terminal, this defaults to editor.lineHeight's value."),
'type': 'number'
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {Platform} from 'vs/base/common/platform';
import {IConfiguration} from 'vs/editor/common/config/defaultConfig';
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
import {ITerminalConfiguration} from 'vs/workbench/parts/terminal/electron-browser/terminal';
import {IThemeService} from 'vs/workbench/services/themes/common/themeService';
import {GOLDEN_LINE_HEIGHT_RATIO} from 'vs/editor/common/config/defaultConfig';
import {Builder} from 'vs/base/browser/builder';

Expand Down Expand Up @@ -83,21 +82,22 @@ export interface ITerminalFont {
*/
export class TerminalConfigHelper {
private charMeasureElement: HTMLElement;
private font: ITerminalFont;

public constructor(
private platform: Platform,
private configurationService: IConfigurationService,
private themeService: IThemeService,
private parentDomElement: HTMLElement) {
}

public getTheme(): string[] {
let baseThemeId = getBaseThemeId(this.themeService.getTheme());
public getTheme(themeId: string): string[] {
let baseThemeId = getBaseThemeId(themeId);
return DEFAULT_ANSI_COLORS[baseThemeId];
}

private neasureFont(fontFamily: string, fontSize: number, lineHeight: number): ITerminalFont {
if (!this.charMeasureElement) {
private measureFont(fontFamily: string, fontSize: number, lineHeight: number): ITerminalFont {
// Create charMeasureElement if it hasn't been created or if it was orphaned by its parent
if (!this.charMeasureElement || !this.charMeasureElement.parentElement) {
this.charMeasureElement = new Builder(this.parentDomElement, true).div().build().getHTMLElement();
}
let style = this.charMeasureElement.style;
Expand All @@ -110,13 +110,14 @@ export class TerminalConfigHelper {
style.display = 'none';
let charWidth = Math.ceil(rect.width);
let charHeight = Math.ceil(rect.height);
return {
this.font = {
fontFamily,
fontSize,
lineHeight,
charWidth,
charHeight
};
return this.font;
}

/**
Expand All @@ -135,7 +136,7 @@ export class TerminalConfigHelper {
lineHeight = Math.round(GOLDEN_LINE_HEIGHT_RATIO * fontSize);
}

return this.neasureFont(fontFamily, fontSize, lineHeight);
return this.measureFont(fontFamily, fontSize, lineHeight);
}

public getShell(): string {
Expand Down
165 changes: 165 additions & 0 deletions src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import cp = require('child_process');
import termJs = require('term.js');
import lifecycle = require('vs/base/common/lifecycle');
import os = require('os');
import path = require('path');
import URI from 'vs/base/common/uri';
import DOM = require('vs/base/browser/dom');
import platform = require('vs/base/common/platform');
import {Dimension} from 'vs/base/browser/builder';
import {IStringDictionary} from 'vs/base/common/collections';
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
import {ITerminalService} from 'vs/workbench/parts/terminal/electron-browser/terminal';
import {DomScrollableElement} from 'vs/base/browser/ui/scrollbar/scrollableElement';
import {ScrollbarVisibility} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
import {ITerminalFont} from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper';

export class TerminalInstance {

private toDispose: lifecycle.IDisposable[];
private ptyProcess: cp.ChildProcess;
private terminal;
private terminalDomElement: HTMLDivElement;
private font: ITerminalFont;

public constructor(
private shell: string,
private parentDomElement: HTMLElement,
private contextService: IWorkspaceContextService,
private terminalService: ITerminalService,
private onExitCallback: (TerminalInstance) => void
) {
this.toDispose = [];
this.parentDomElement.innerHTML = '';
this.ptyProcess = this.createTerminalProcess();
this.terminalDomElement = document.createElement('div');
this.parentDomElement.classList.add('integrated-terminal');
let terminalScrollbar = new DomScrollableElement(this.terminalDomElement, {
canUseTranslate3d: false,
horizontal: ScrollbarVisibility.Hidden,
vertical: ScrollbarVisibility.Auto
});
this.toDispose.push(terminalScrollbar);
this.terminal = termJs({
cursorBlink: false // term.js' blinking cursor breaks selection
});

this.ptyProcess.on('message', (data) => {
this.terminal.write(data);
});
this.terminal.on('data', (data) => {
this.ptyProcess.send({
event: 'input',
data: data
});
return false;
});
this.ptyProcess.on('exit', (exitCode) => {
this.dispose();
// TODO: When multiple terminals are supported this should do something smarter. There is
// also a weird bug here at least on Ubuntu 15.10 where the new terminal text does not
// repaint correctly.
if (exitCode !== 0) {
// TODO: Allow the terminal to be relaunched after an error
console.error('Integrated terminal exited with code ' + exitCode);
}
this.onExitCallback(this);
});
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();
}
}));

this.terminal.open(this.terminalDomElement);
this.parentDomElement.appendChild(terminalScrollbar.getDomNode());
}

public layout(dimension: Dimension): void {
if (!this.font || !this.font.charWidth || !this.font.charHeight) {
return;
}
let cols = Math.floor(this.parentDomElement.offsetWidth / this.font.charWidth);
let rows = Math.floor(this.parentDomElement.offsetHeight / this.font.charHeight);
if (this.terminal) {
this.terminal.resize(cols, rows);
}
if (this.ptyProcess.connected) {
this.ptyProcess.send({
event: 'resize',
cols: cols,
rows: rows
});
}
}

private cloneEnv(): IStringDictionary<string> {
let newEnv: IStringDictionary<string> = Object.create(null);
Object.keys(process.env).forEach((key) => {
newEnv[key] = process.env[key];
});
return newEnv;
}

private createTerminalProcess(): cp.ChildProcess {
let env = this.cloneEnv();
env['PTYSHELL'] = this.shell;
env['PTYCWD'] = this.contextService.getWorkspace() ? this.contextService.getWorkspace().resource.fsPath : os.homedir();
return cp.fork('./terminalProcess', [], {
env: env,
cwd: URI.parse(path.dirname(require.toUrl('./terminalProcess'))).fsPath
});
}

public setTheme(colors: string[]): void {
if (!this.terminal) {
return;
}
this.terminal.colors = colors;
this.terminal.refresh(0, this.terminal.rows);
}

public setFont(font: ITerminalFont): void {
this.font = font;
this.terminalDomElement.style.fontFamily = this.font.fontFamily;
this.terminalDomElement.style.lineHeight = this.font.lineHeight + 'px';
this.terminalDomElement.style.fontSize = this.font.fontSize + 'px';
}

public focus(force?: boolean): void {
if (!this.terminal) {
return;
}
let text = window.getSelection().toString();
if (!text || force) {
this.terminal.focus();
if (this.terminal._textarea) {
this.terminal._textarea.focus();
}
}
}

public dispose(): void {
this.toDispose = lifecycle.dispose(this.toDispose);
this.terminal.destroy();
this.ptyProcess.kill();
}
}
Loading

0 comments on commit d87ec53

Please sign in to comment.