Tidy up terminal code

Consistently prefix private members with _ to be consistent with ext code and
prevent possible errors with not using correct event handler member.
This commit is contained in:
Daniel Imms
2016-09-15 12:54:10 -07:00
parent 5ba13ddbf3
commit e3a3c3ad1d
5 changed files with 261 additions and 249 deletions

View File

@@ -238,12 +238,12 @@ export class SwitchTerminalInstanceActionItem extends SelectActionItem {
@ITerminalService private terminalService: ITerminalService
) {
super(null, action, terminalService.getInstanceLabels(), terminalService.activeTerminalInstanceIndex);
this.toDispose.push(terminalService.onInstancesChanged(this.updateItems, this));
this.toDispose.push(terminalService.onActiveInstanceChanged(this.updateItems, this));
this.toDispose.push(terminalService.onInstanceTitleChanged(this.updateItems, this));
this.toDispose.push(terminalService.onInstancesChanged(this._updateItems, this));
this.toDispose.push(terminalService.onActiveInstanceChanged(this._updateItems, this));
this.toDispose.push(terminalService.onInstanceTitleChanged(this._updateItems, this));
}
private updateItems(): void {
private _updateItems(): void {
this.setOptions(this.terminalService.getInstanceLabels(), this.terminalService.activeTerminalInstanceIndex);
}
}

View File

@@ -89,29 +89,29 @@ export interface IShell {
export class TerminalConfigHelper {
public panelContainer: Builder;
private charMeasureElement: HTMLElement;
private _charMeasureElement: HTMLElement;
public constructor(
private platform: Platform,
@IConfigurationService private configurationService: IConfigurationService) {
private _platform: Platform,
@IConfigurationService private _configurationService: IConfigurationService) {
}
public getTheme(baseThemeId: string): string[] {
return DEFAULT_ANSI_COLORS[baseThemeId];
}
private measureFont(fontFamily: string, fontSize: number, lineHeight: number): ITerminalFont {
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 = this.panelContainer.div().getHTMLElement();
if (!this._charMeasureElement || !this._charMeasureElement.parentElement) {
this._charMeasureElement = this.panelContainer.div().getHTMLElement();
}
let style = this.charMeasureElement.style;
let style = this._charMeasureElement.style;
style.display = 'block';
style.fontFamily = fontFamily;
style.fontSize = fontSize + 'px';
style.height = Math.floor(lineHeight * fontSize) + 'px';
this.charMeasureElement.innerText = 'X';
let rect = this.charMeasureElement.getBoundingClientRect();
this._charMeasureElement.innerText = 'X';
let rect = this._charMeasureElement.getBoundingClientRect();
style.display = 'none';
let charWidth = Math.ceil(rect.width);
let charHeight = Math.ceil(rect.height);
@@ -129,8 +129,8 @@ export class TerminalConfigHelper {
* terminal.integrated.fontSize, terminal.integrated.lineHeight configuration properties
*/
public getFont(): ITerminalFont {
let terminalConfig = this.configurationService.getConfiguration<ITerminalConfiguration>().terminal.integrated;
let editorConfig = this.configurationService.getConfiguration<IConfiguration>();
let terminalConfig = this._configurationService.getConfiguration<ITerminalConfiguration>().terminal.integrated;
let editorConfig = this._configurationService.getConfiguration<IConfiguration>();
let fontFamily = terminalConfig.fontFamily || editorConfig.editor.fontFamily;
let fontSize = this.toInteger(terminalConfig.fontSize, 0) || editorConfig.editor.fontSize;
@@ -139,31 +139,31 @@ export class TerminalConfigHelper {
}
let lineHeight = this.toInteger(terminalConfig.lineHeight, DEFAULT_LINE_HEIGHT);
return this.measureFont(fontFamily, fontSize, lineHeight <= 0 ? DEFAULT_LINE_HEIGHT : lineHeight);
return this._measureFont(fontFamily, fontSize, lineHeight <= 0 ? DEFAULT_LINE_HEIGHT : lineHeight);
}
public getFontLigaturesEnabled(): boolean {
let terminalConfig = this.configurationService.getConfiguration<ITerminalConfiguration>().terminal.integrated;
let terminalConfig = this._configurationService.getConfiguration<ITerminalConfiguration>().terminal.integrated;
return terminalConfig.fontLigatures;
}
public getCursorBlink(): boolean {
let terminalConfig = this.configurationService.getConfiguration<ITerminalConfiguration>().terminal.integrated;
let terminalConfig = this._configurationService.getConfiguration<ITerminalConfiguration>().terminal.integrated;
return terminalConfig.cursorBlinking;
}
public getShell(): IShell {
let config = this.configurationService.getConfiguration<ITerminalConfiguration>();
let config = this._configurationService.getConfiguration<ITerminalConfiguration>();
let shell: IShell = {
executable: '',
args: []
};
if (this.platform === Platform.Windows) {
if (this._platform === Platform.Windows) {
shell.executable = config.terminal.integrated.shell.windows;
} else if (this.platform === Platform.Mac) {
} else if (this._platform === Platform.Mac) {
shell.executable = config.terminal.integrated.shell.osx;
shell.args = config.terminal.integrated.shellArgs.osx;
} else if (this.platform === Platform.Linux) {
} else if (this._platform === Platform.Linux) {
shell.executable = config.terminal.integrated.shell.linux;
shell.args = config.terminal.integrated.shellArgs.linux;
}
@@ -171,7 +171,7 @@ export class TerminalConfigHelper {
}
public isSetLocaleVariables(): boolean {
let config = this.configurationService.getConfiguration<ITerminalConfiguration>();
let config = this._configurationService.getConfiguration<ITerminalConfiguration>();
return config.terminal.integrated.setLocaleVariables;
}
@@ -187,7 +187,7 @@ export class TerminalConfigHelper {
}
public getCommandsToSkipShell(): string[] {
let config = this.configurationService.getConfiguration<ITerminalConfiguration>();
let config = this._configurationService.getConfiguration<ITerminalConfiguration>();
return config.terminal.integrated.commandsToSkipShell;
}
}

View File

@@ -26,81 +26,88 @@ import { TabFocus } from 'vs/editor/common/config/commonEditorConfig';
import { TerminalConfigHelper, IShell } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper';
export class TerminalInstance implements ITerminalInstance {
private static ID_COUNTER = 1;
private static EOL_REGEX = /\r?\n/g;
private static _idCounter = 1;
private _id: number;
private _title: string;
private _isExiting: boolean;
private _isVisible: boolean;
private _onDisposed: Emitter<TerminalInstance>;
private _onTitleChanged: Emitter<string>;
private _process: cp.ChildProcess;
private _skipTerminalKeybindings: Keybinding[];
private _title: string;
private _toDispose: lifecycle.IDisposable[];
private _wrapperElement: HTMLDivElement;
private _xterm: any;
private _xtermElement: HTMLDivElement;
public get id(): number { return this._id; }
public get title(): string { return this._title; }
public get onClosed(): Event<TerminalInstance> { return this._onDisposed.event; }
public get onTitleChanged(): Event<string> { return this._onTitleChanged.event; }
private isExiting: boolean = false;
private isVisible: boolean = false;
private toDispose: lifecycle.IDisposable[] = [];
private skipTerminalKeybindings: Keybinding[] = [];
private process: cp.ChildProcess;
private xterm: any;
private wrapperElement: HTMLDivElement;
private xtermElement: HTMLDivElement;
public get title(): string { return this._title; }
public constructor(
private terminalFocusContextKey: IContextKey<boolean>,
private configHelper: TerminalConfigHelper,
private container: HTMLElement,
private workspace: IWorkspace,
private _terminalFocusContextKey: IContextKey<boolean>,
private _configHelper: TerminalConfigHelper,
private _container: HTMLElement,
private _workspace: IWorkspace,
name: string,
shell: IShell,
@IKeybindingService private keybindingService: IKeybindingService,
@IMessageService private messageService: IMessageService
@IKeybindingService private _keybindingService: IKeybindingService,
@IMessageService private _messageService: IMessageService
) {
this._id = TerminalInstance.ID_COUNTER++;
this._toDispose = [];
this._skipTerminalKeybindings = [];
this._isExiting = false;
this._isVisible = false;
this._id = TerminalInstance._idCounter++;
this._onTitleChanged = new Emitter<string>();
this._onDisposed = new Emitter<TerminalInstance>();
this.createProcess(workspace, name, shell);
if (container) {
this.attachToElement(container);
this._createProcess(_workspace, name, shell);
if (_container) {
this.attachToElement(_container);
}
}
public addDisposable(disposable: lifecycle.IDisposable): void {
this.toDispose.push(disposable);
this._toDispose.push(disposable);
}
public attachToElement(container: HTMLElement): void {
if (this.wrapperElement) {
if (this._wrapperElement) {
throw new Error('The terminal instance has already been attached to a container');
}
this.container = container;
this.wrapperElement = document.createElement('div');
DOM.addClass(this.wrapperElement, 'terminal-wrapper');
this.xtermElement = document.createElement('div');
this._container = container;
this._wrapperElement = document.createElement('div');
DOM.addClass(this._wrapperElement, 'terminal-wrapper');
this._xtermElement = document.createElement('div');
this.xterm = xterm();
this.xterm.open(this.xtermElement);
this._xterm = xterm();
this._xterm.open(this._xtermElement);
this.process.on('message', (message) => {
this._process.on('message', (message) => {
if (message.type === 'data') {
this.xterm.write(message.content);
this._xterm.write(message.content);
}
});
this.xterm.on('data', (data) => {
this.process.send({
this._xterm.on('data', (data) => {
this._process.send({
event: 'input',
data: this.sanitizeInput(data)
});
return false;
});
this.xterm.attachCustomKeydownHandler((event: KeyboardEvent) => {
this._xterm.attachCustomKeydownHandler((event: KeyboardEvent) => {
// Allow the toggle tab mode keybinding to pass through the terminal so that focus can
// be escaped
let standardKeyboardEvent = new StandardKeyboardEvent(event);
if (this.skipTerminalKeybindings.some((k) => standardKeyboardEvent.equals(k.value))) {
if (this._skipTerminalKeybindings.some((k) => standardKeyboardEvent.equals(k.value))) {
event.preventDefault();
return false;
}
@@ -111,7 +118,7 @@ export class TerminalInstance implements ITerminalInstance {
}
});
let xtermHelper: HTMLElement = this.xterm.element.querySelector('.xterm-helpers');
let xtermHelper: HTMLElement = this._xterm.element.querySelector('.xterm-helpers');
let focusTrap: HTMLElement = document.createElement('div');
focusTrap.setAttribute('tabindex', '0');
DOM.addClass(focusTrap, 'focus-trap');
@@ -123,63 +130,63 @@ export class TerminalInstance implements ITerminalInstance {
let hidePanelElement = <HTMLElement>currentElement.querySelector('.hide-panel-action');
hidePanelElement.focus();
});
xtermHelper.insertBefore(focusTrap, this.xterm.textarea);
xtermHelper.insertBefore(focusTrap, this._xterm.textarea);
this.toDispose.push(DOM.addDisposableListener(this.xterm.textarea, 'focus', (event: KeyboardEvent) => {
this.terminalFocusContextKey.set(true);
this._toDispose.push(DOM.addDisposableListener(this._xterm.textarea, 'focus', (event: KeyboardEvent) => {
this._terminalFocusContextKey.set(true);
}));
this.toDispose.push(DOM.addDisposableListener(this.xterm.textarea, 'blur', (event: KeyboardEvent) => {
this.terminalFocusContextKey.reset();
this._toDispose.push(DOM.addDisposableListener(this._xterm.textarea, 'blur', (event: KeyboardEvent) => {
this._terminalFocusContextKey.reset();
}));
this.toDispose.push(DOM.addDisposableListener(this.xterm.element, 'focus', (event: KeyboardEvent) => {
this.terminalFocusContextKey.set(true);
this._toDispose.push(DOM.addDisposableListener(this._xterm.element, 'focus', (event: KeyboardEvent) => {
this._terminalFocusContextKey.set(true);
}));
this.toDispose.push(DOM.addDisposableListener(this.xterm.element, 'blur', (event: KeyboardEvent) => {
this.terminalFocusContextKey.reset();
this._toDispose.push(DOM.addDisposableListener(this._xterm.element, 'blur', (event: KeyboardEvent) => {
this._terminalFocusContextKey.reset();
}));
this.wrapperElement.appendChild(this.xtermElement);
this.container.appendChild(this.wrapperElement);
this._wrapperElement.appendChild(this._xtermElement);
this._container.appendChild(this._wrapperElement);
this.layout(new Dimension(this.container.offsetWidth, this.container.offsetHeight));
this.setVisible(this.isVisible);
this.layout(new Dimension(this._container.offsetWidth, this._container.offsetHeight));
this.setVisible(this._isVisible);
}
public copySelection(): void {
if (document.activeElement.classList.contains('xterm')) {
document.execCommand('copy');
} else {
this.messageService.show(Severity.Warning, nls.localize('terminal.integrated.copySelection.noSelection', 'Cannot copy terminal selection when terminal does not have focus'));
this._messageService.show(Severity.Warning, nls.localize('terminal.integrated.copySelection.noSelection', 'Cannot copy terminal selection when terminal does not have focus'));
}
}
public dispose(): void {
if (this.wrapperElement) {
this.container.removeChild(this.wrapperElement);
this.wrapperElement = null;
if (this._wrapperElement) {
this._container.removeChild(this._wrapperElement);
this._wrapperElement = null;
}
if (this.xterm) {
this.xterm.destroy();
this.xterm = null;
if (this._xterm) {
this._xterm.destroy();
this._xterm = null;
}
if (this.process) {
if (this.process.connected) {
this.process.disconnect();
this.process.kill();
if (this._process) {
if (this._process.connected) {
this._process.disconnect();
this._process.kill();
}
this.process = null;
this._process = null;
}
this._onDisposed.fire(this);
this.toDispose = lifecycle.dispose(this.toDispose);
this._toDispose = lifecycle.dispose(this._toDispose);
}
public focus(force?: boolean): void {
if (!this.xterm) {
if (!this._xterm) {
return;
}
let text = window.getSelection().toString();
if (!text || force) {
this.xterm.focus();
this._xterm.focus();
}
}
@@ -192,65 +199,65 @@ export class TerminalInstance implements ITerminalInstance {
if (addNewLine && text.substr(text.length - os.EOL.length) !== os.EOL) {
text += os.EOL;
}
this.process.send({
this._process.send({
event: 'input',
data: text
});
}
public setVisible(visible: boolean): void {
this.isVisible = visible;
if (this.wrapperElement) {
DOM.toggleClass(this.wrapperElement, 'active', visible);
this._isVisible = visible;
if (this._wrapperElement) {
DOM.toggleClass(this._wrapperElement, 'active', visible);
}
}
public scrollDown(): void {
this.xterm.scrollDisp(1);
this._xterm.scrollDisp(1);
}
public scrollUp(): void {
this.xterm.scrollDisp(-1);
this._xterm.scrollDisp(-1);
}
private sanitizeInput(data: any) {
return typeof data === 'string' ? data.replace(TerminalInstance.EOL_REGEX, os.EOL) : data;
}
private createProcess(workspace: IWorkspace, name: string, shell: IShell) {
let locale = this.configHelper.isSetLocaleVariables() ? platform.locale : undefined;
private _createProcess(workspace: IWorkspace, name: string, shell: IShell) {
let locale = this._configHelper.isSetLocaleVariables() ? platform.locale : undefined;
if (!shell.executable) {
shell = this.configHelper.getShell();
shell = this._configHelper.getShell();
}
let env = TerminalInstance.createTerminalEnv(process.env, shell, workspace, locale);
this._title = name ? name : '';
this.process = cp.fork('./terminalProcess', [], {
this._process = cp.fork('./terminalProcess', [], {
env: env,
cwd: URI.parse(path.dirname(require.toUrl('./terminalProcess'))).fsPath
});
if (!name) {
// Only listen for process title changes when a name is not provided
this.process.on('message', (message) => {
this._process.on('message', (message) => {
if (message.type === 'title') {
this._title = message.content ? message.content : '';
this._onTitleChanged.fire(this._title);
}
});
}
this.process.on('exit', (exitCode) => {
this._process.on('exit', (exitCode) => {
// Prevent dispose functions being triggered multiple times
if (!this.isExiting) {
this.isExiting = true;
if (!this._isExiting) {
this._isExiting = true;
this.dispose();
if (exitCode) {
this.messageService.show(Severity.Error, nls.localize('terminal.integrated.exitedWithCode', 'The terminal process terminated with exit code: {0}', exitCode));
this._messageService.show(Severity.Error, nls.localize('terminal.integrated.exitedWithCode', 'The terminal process terminated with exit code: {0}', exitCode));
}
}
});
}
public static createTerminalEnv(parentEnv: IStringDictionary<string>, shell: IShell, workspace: IWorkspace, locale?: string): IStringDictionary<string> {
let env = TerminalInstance.cloneEnv(parentEnv);
let env = TerminalInstance._cloneEnv(parentEnv);
env['PTYPID'] = process.pid.toString();
env['PTYSHELL'] = shell.executable;
if (shell.args) {
@@ -258,14 +265,14 @@ export class TerminalInstance implements ITerminalInstance {
env[`PTYSHELLARG${i}`] = arg;
});
}
env['PTYCWD'] = TerminalInstance.sanitizeCwd(workspace ? workspace.resource.fsPath : os.homedir());
env['PTYCWD'] = TerminalInstance._sanitizeCwd(workspace ? workspace.resource.fsPath : os.homedir());
if (locale) {
env['LANG'] = TerminalInstance.getLangEnvVariable(locale);
env['LANG'] = TerminalInstance._getLangEnvVariable(locale);
}
return env;
}
private static sanitizeCwd(cwd: string) {
private static _sanitizeCwd(cwd: string) {
// Make the drive letter uppercase on Windows (see #9448)
if (platform.platform === platform.Platform.Windows && cwd && cwd[1] === ':') {
return cwd[0].toUpperCase() + cwd.substr(1);
@@ -273,7 +280,7 @@ export class TerminalInstance implements ITerminalInstance {
return cwd;
}
private static cloneEnv(env: IStringDictionary<string>): IStringDictionary<string> {
private static _cloneEnv(env: IStringDictionary<string>): IStringDictionary<string> {
let newEnv: IStringDictionary<string> = Object.create(null);
Object.keys(env).forEach((key) => {
newEnv[key] = env[key];
@@ -281,7 +288,7 @@ export class TerminalInstance implements ITerminalInstance {
return newEnv;
}
private static getLangEnvVariable(locale: string) {
private static _getLangEnvVariable(locale: string) {
const parts = locale.split('-');
const n = parts.length;
if (n > 1) {
@@ -291,22 +298,22 @@ export class TerminalInstance implements ITerminalInstance {
}
public setCursorBlink(blink: boolean): void {
if (this.xterm && this.xterm.cursorBlink !== blink) {
this.xterm.cursorBlink = blink;
this.xterm.refresh(0, this.xterm.rows - 1);
if (this._xterm && this._xterm.cursorBlink !== blink) {
this._xterm.cursorBlink = blink;
this._xterm.refresh(0, this._xterm.rows - 1);
}
}
public setCommandsToSkipShell(commands: string[]): void {
this.skipTerminalKeybindings = commands.map((c) => {
return this.keybindingService.lookupKeybindings(c);
this._skipTerminalKeybindings = commands.map((c) => {
return this._keybindingService.lookupKeybindings(c);
}).reduce((prev, curr) => {
return prev.concat(curr);
});
}
public layout(dimension: Dimension): void {
let font = this.configHelper.getFont();
let font = this._configHelper.getFont();
if (!font || !font.charWidth || !font.charHeight) {
return;
}
@@ -317,12 +324,12 @@ export class TerminalInstance implements ITerminalInstance {
let innerWidth = dimension.width - leftPadding;
let cols = Math.floor(innerWidth / font.charWidth);
let rows = Math.floor(dimension.height / font.charHeight);
if (this.xterm) {
this.xterm.resize(cols, rows);
this.xterm.element.style.width = innerWidth + 'px';
if (this._xterm) {
this._xterm.resize(cols, rows);
this._xterm.element.style.width = innerWidth + 'px';
}
if (this.process.connected) {
this.process.send({
if (this._process.connected) {
this._process.send({
event: 'resize',
cols: cols,
rows: rows

View File

@@ -27,53 +27,54 @@ import { getBaseThemeId } from 'vs/platform/theme/common/themes';
export class TerminalPanel extends Panel {
private toDispose: lifecycle.IDisposable[] = [];
private actions: IAction[];
private contextMenuActions: IAction[];
private parentDomElement: HTMLElement;
private terminalContainer: HTMLElement;
private currentBaseThemeId: string;
private themeStyleElement: HTMLElement;
private fontStyleElement: HTMLElement;
private font: ITerminalFont;
private _actions: IAction[];
private _contextMenuActions: IAction[];
private _currentBaseThemeId: string;
private _font: ITerminalFont;
private _fontStyleElement: HTMLElement;
private _parentDomElement: HTMLElement;
private _terminalContainer: HTMLElement;
private _themeStyleElement: HTMLElement;
private _toDispose: lifecycle.IDisposable[];
constructor(
@IConfigurationService private configurationService: IConfigurationService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IInstantiationService private instantiationService: IInstantiationService,
@IKeybindingService private keybindingService: IKeybindingService,
@ITelemetryService telemetryService: ITelemetryService,
@ITerminalService private terminalService: ITerminalService,
@IThemeService private themeService: IThemeService
@IConfigurationService private _configurationService: IConfigurationService,
@IContextMenuService private _contextMenuService: IContextMenuService,
@IInstantiationService private _instantiationService: IInstantiationService,
@IKeybindingService private _keybindingService: IKeybindingService,
@ITerminalService private _terminalService: ITerminalService,
@IThemeService private _themeService: IThemeService,
@ITelemetryService telemetryService: ITelemetryService
) {
super(TERMINAL_PANEL_ID, telemetryService);
this._toDispose = [];
}
public create(parent: Builder): TPromise<any> {
super.create(parent);
this.parentDomElement = parent.getHTMLElement();
DOM.addClass(this.parentDomElement, 'integrated-terminal');
this.themeStyleElement = document.createElement('style');
this.fontStyleElement = document.createElement('style');
this._parentDomElement = parent.getHTMLElement();
DOM.addClass(this._parentDomElement, 'integrated-terminal');
this._themeStyleElement = document.createElement('style');
this._fontStyleElement = document.createElement('style');
this.terminalContainer = document.createElement('div');
DOM.addClass(this.terminalContainer, 'terminal-outer-container');
this.parentDomElement.appendChild(this.themeStyleElement);
this.parentDomElement.appendChild(this.fontStyleElement);
this.parentDomElement.appendChild(this.terminalContainer);
this._terminalContainer = document.createElement('div');
DOM.addClass(this._terminalContainer, 'terminal-outer-container');
this._parentDomElement.appendChild(this._themeStyleElement);
this._parentDomElement.appendChild(this._fontStyleElement);
this._parentDomElement.appendChild(this._terminalContainer);
this.attachEventListeners();
this._attachEventListeners();
this.terminalService.setContainers(this.getContainer(), this.terminalContainer);
this._terminalService.setContainers(this.getContainer(), this._terminalContainer);
this.toDispose.push(this.themeService.onDidColorThemeChange(this.updateTheme.bind(this)));
this.toDispose.push(this.configurationService.onDidUpdateConfiguration(this.updateConfig.bind(this)));
this.updateTheme();
this.updateConfig();
this._toDispose.push(this._themeService.onDidColorThemeChange(this._updateTheme.bind(this)));
this._toDispose.push(this._configurationService.onDidUpdateConfiguration(this._updateConfig.bind(this)));
this._updateTheme();
this._updateConfig();
// Force another layout (first is setContainers) since config has changed
this.layout(new Dimension(this.terminalContainer.offsetWidth, this.terminalContainer.offsetHeight));
this.layout(new Dimension(this._terminalContainer.offsetWidth, this._terminalContainer.offsetHeight));
return TPromise.as(void 0);
}
@@ -81,21 +82,21 @@ export class TerminalPanel extends Panel {
if (!dimension) {
return;
}
this.terminalService.terminalInstances.forEach((t) => {
this._terminalService.terminalInstances.forEach((t) => {
t.layout(dimension);
});
}
public setVisible(visible: boolean): TPromise<void> {
if (visible) {
if (this.terminalService.terminalInstances.length > 0) {
this.updateConfig();
this.updateTheme();
if (this._terminalService.terminalInstances.length > 0) {
this._updateConfig();
this._updateTheme();
} else {
return super.setVisible(visible).then(() => {
this.terminalService.createInstance();
this.updateConfig();
this.updateTheme();
this._terminalService.createInstance();
this._updateConfig();
this._updateTheme();
});
}
}
@@ -103,66 +104,66 @@ export class TerminalPanel extends Panel {
}
public getActions(): IAction[] {
if (!this.actions) {
this.actions = [
this.instantiationService.createInstance(SwitchTerminalInstanceAction, SwitchTerminalInstanceAction.ID, SwitchTerminalInstanceAction.LABEL),
this.instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.PANEL_LABEL),
this.instantiationService.createInstance(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.PANEL_LABEL)
if (!this._actions) {
this._actions = [
this._instantiationService.createInstance(SwitchTerminalInstanceAction, SwitchTerminalInstanceAction.ID, SwitchTerminalInstanceAction.LABEL),
this._instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.PANEL_LABEL),
this._instantiationService.createInstance(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.PANEL_LABEL)
];
this.actions.forEach(a => {
this.toDispose.push(a);
this._actions.forEach(a => {
this._toDispose.push(a);
});
}
return this.actions;
return this._actions;
}
private getContextMenuActions(): IAction[] {
if (!this.contextMenuActions) {
this.contextMenuActions = [
this.instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, nls.localize('createNewTerminal', "New Terminal")),
private _getContextMenuActions(): IAction[] {
if (!this._contextMenuActions) {
this._contextMenuActions = [
this._instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, nls.localize('createNewTerminal', "New Terminal")),
new Separator(),
this.instantiationService.createInstance(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, nls.localize('copy', "Copy")),
this.instantiationService.createInstance(TerminalPasteAction, TerminalPasteAction.ID, nls.localize('paste', "Paste"))
this._instantiationService.createInstance(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, nls.localize('copy', "Copy")),
this._instantiationService.createInstance(TerminalPasteAction, TerminalPasteAction.ID, nls.localize('paste', "Paste"))
];
this.contextMenuActions.forEach(a => {
this.toDispose.push(a);
this._contextMenuActions.forEach(a => {
this._toDispose.push(a);
});
}
return this.contextMenuActions;
return this._contextMenuActions;
}
public getActionItem(action: Action): IActionItem {
if (action.id === SwitchTerminalInstanceAction.ID) {
return this.instantiationService.createInstance(SwitchTerminalInstanceActionItem, action);
return this._instantiationService.createInstance(SwitchTerminalInstanceActionItem, action);
}
return super.getActionItem(action);
}
private attachEventListeners(): void {
this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'mousedown', (event: MouseEvent) => {
if (this.terminalService.terminalInstances.length === 0) {
private _attachEventListeners(): void {
this._toDispose.push(DOM.addDisposableListener(this._parentDomElement, 'mousedown', (event: MouseEvent) => {
if (this._terminalService.terminalInstances.length === 0) {
return;
}
if (event.which === 2 && platform.isLinux) {
// Drop selection and focus terminal on Linux to enable middle button paste when click
// occurs on the selection itself.
this.terminalService.getActiveInstance().focus();
this._terminalService.getActiveInstance().focus();
} else if (event.which === 3) {
// Trigger the context menu on right click
let anchor: HTMLElement | { x: number, y: number } = this.parentDomElement;
let anchor: HTMLElement | { x: number, y: number } = this._parentDomElement;
if (event instanceof MouseEvent) {
const standardEvent = new StandardMouseEvent(event);
anchor = { x: standardEvent.posx, y: standardEvent.posy };
}
this.contextMenuService.showContextMenu({
this._contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => TPromise.as(this.getContextMenuActions()),
getActionsContext: () => this.parentDomElement,
getActions: () => TPromise.as(this._getContextMenuActions()),
getActionsContext: () => this._parentDomElement,
getKeyBinding: (action) => {
const opts = this.keybindingService.lookupKeybindings(action.id);
const opts = this._keybindingService.lookupKeybindings(action.id);
if (opts.length > 0) {
return opts[0]; // only take the first one
}
@@ -171,16 +172,16 @@ export class TerminalPanel extends Panel {
});
}
}));
this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'mouseup', (event) => {
if (this.terminalService.terminalInstances.length === 0) {
this._toDispose.push(DOM.addDisposableListener(this._parentDomElement, 'mouseup', (event) => {
if (this._terminalService.terminalInstances.length === 0) {
return;
}
if (event.which !== 3) {
this.terminalService.getActiveInstance().focus();
this._terminalService.getActiveInstance().focus();
}
}));
this.toDispose.push(DOM.addDisposableListener(this.parentDomElement, 'keyup', (event: KeyboardEvent) => {
this._toDispose.push(DOM.addDisposableListener(this._parentDomElement, 'keyup', (event: KeyboardEvent) => {
if (event.keyCode === 27) {
// Keep terminal open on escape
event.stopPropagation();
@@ -188,65 +189,65 @@ export class TerminalPanel extends Panel {
}));
}
private updateTheme(themeId?: string): void {
private _updateTheme(themeId?: string): void {
if (!themeId) {
themeId = this.themeService.getColorTheme();
themeId = this._themeService.getColorTheme();
}
let baseThemeId = getBaseThemeId(themeId);
if (baseThemeId === this.currentBaseThemeId) {
if (baseThemeId === this._currentBaseThemeId) {
return;
}
this.currentBaseThemeId = baseThemeId;
this._currentBaseThemeId = baseThemeId;
let theme = this.terminalService.configHelper.getTheme(baseThemeId);
let theme = this._terminalService.configHelper.getTheme(baseThemeId);
let css = '';
theme.forEach((color: string, index: number) => {
let rgba = this.convertHexCssColorToRgba(color, 0.996);
let rgba = this._convertHexCssColorToRgba(color, 0.996);
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}; }`;
});
this.themeStyleElement.innerHTML = css;
this._themeStyleElement.innerHTML = css;
}
/**
* Converts a CSS hex color (#rrggbb) to a CSS rgba color (rgba(r, g, b, a)).
*/
private convertHexCssColorToRgba(hex: string, alpha: number): string {
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})`;
}
private updateConfig(): void {
this.updateFont();
this.updateCursorBlink();
this.updateCommandsToSkipShell();
private _updateConfig(): void {
this._updateFont();
this._updateCursorBlink();
this._updateCommandsToSkipShell();
}
private updateFont(): void {
if (this.terminalService.terminalInstances.length === 0) {
private _updateFont(): void {
if (this._terminalService.terminalInstances.length === 0) {
return;
}
let newFont = this.terminalService.configHelper.getFont();
DOM.toggleClass(this.parentDomElement, 'enable-ligatures', this.terminalService.configHelper.getFontLigaturesEnabled());
if (!this.font || this.fontsDiffer(this.font, newFont)) {
this.fontStyleElement.innerHTML = '.monaco-workbench .panel.integrated-terminal .xterm {' +
let newFont = this._terminalService.configHelper.getFont();
DOM.toggleClass(this._parentDomElement, 'enable-ligatures', this._terminalService.configHelper.getFontLigaturesEnabled());
if (!this._font || this._fontsDiffer(this._font, newFont)) {
this._fontStyleElement.innerHTML = '.monaco-workbench .panel.integrated-terminal .xterm {' +
`font-family: ${newFont.fontFamily};` +
`font-size: ${newFont.fontSize};` +
`line-height: ${newFont.lineHeight};` +
'}';
this.font = newFont;
this._font = newFont;
}
this.layout(new Dimension(this.parentDomElement.offsetWidth, this.parentDomElement.offsetHeight));
this.layout(new Dimension(this._parentDomElement.offsetWidth, this._parentDomElement.offsetHeight));
}
private fontsDiffer(a: ITerminalFont, b: ITerminalFont): boolean {
private _fontsDiffer(a: ITerminalFont, b: ITerminalFont): boolean {
return a.charHeight !== b.charHeight ||
a.charWidth !== b.charWidth ||
a.fontFamily !== b.fontFamily ||
@@ -254,15 +255,15 @@ export class TerminalPanel extends Panel {
a.lineHeight !== b.lineHeight;
}
private updateCursorBlink(): void {
this.terminalService.terminalInstances.forEach((instance) => {
instance.setCursorBlink(this.terminalService.configHelper.getCursorBlink());
private _updateCursorBlink(): void {
this._terminalService.terminalInstances.forEach((instance) => {
instance.setCursorBlink(this._terminalService.configHelper.getCursorBlink());
});
}
private updateCommandsToSkipShell(): void {
this.terminalService.terminalInstances.forEach((instance) => {
instance.setCommandsToSkipShell(this.terminalService.configHelper.getCommandsToSkipShell());
private _updateCommandsToSkipShell(): void {
this._terminalService.terminalInstances.forEach((instance) => {
instance.setCommandsToSkipShell(this._terminalService.configHelper.getCommandsToSkipShell());
});
}
}

View File

@@ -20,13 +20,16 @@ import { TerminalInstance } from 'vs/workbench/parts/terminal/electron-browser/t
export class TerminalService implements ITerminalService {
public _serviceBrand: any;
private _activeTerminalInstanceIndex: number = 0;
private _activeTerminalInstanceIndex: number;
private _configHelper: TerminalConfigHelper;
private _onActiveInstanceChanged: Emitter<string>;
private _onInstanceDisposed: Emitter<ITerminalInstance>;
private _onInstanceTitleChanged: Emitter<string>;
private _onInstancesChanged: Emitter<string>;
private _terminalInstances: ITerminalInstance[] = [];
private _terminalContainer: HTMLElement;
private _terminalFocusContextKey: IContextKey<boolean>;
private _terminalInstances: ITerminalInstance[];
public get activeTerminalInstanceIndex(): number { return this._activeTerminalInstanceIndex; }
public get configHelper(): TerminalConfigHelper { return this._configHelper; }
public get onActiveInstanceChanged(): Event<string> { return this._onActiveInstanceChanged.event; }
@@ -35,24 +38,25 @@ export class TerminalService implements ITerminalService {
public get onInstancesChanged(): Event<string> { return this._onInstancesChanged.event; }
public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; }
private terminalContainer: HTMLElement;
private terminalFocusContextKey: IContextKey<boolean>;
constructor(
@IConfigurationService private configurationService: IConfigurationService,
@IContextKeyService private contextKeyService: IContextKeyService,
@IInstantiationService private instantiationService: IInstantiationService,
@IPanelService private panelService: IPanelService,
@IPartService private partService: IPartService,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService
@IConfigurationService private _configurationService: IConfigurationService,
@IContextKeyService private _contextKeyService: IContextKeyService,
@IInstantiationService private _instantiationService: IInstantiationService,
@IPanelService private _panelService: IPanelService,
@IPartService private _partService: IPartService,
@IWorkspaceContextService private _workspaceContextService: IWorkspaceContextService
) {
this._terminalInstances = [];
this._activeTerminalInstanceIndex = 0;
this._onActiveInstanceChanged = new Emitter<string>();
this._onInstanceDisposed = new Emitter<ITerminalInstance>();
this._onInstancesChanged = new Emitter<string>();
this._onInstanceTitleChanged = new Emitter<string>();
this.terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this.contextKeyService);
this._configHelper = <TerminalConfigHelper>this.instantiationService.createInstance(TerminalConfigHelper, platform.platform);
this.onInstanceDisposed((terminalInstance) => { this.removeInstance(terminalInstance); });
this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService);
this._configHelper = <TerminalConfigHelper>this._instantiationService.createInstance(TerminalConfigHelper, platform.platform);
this.onInstanceDisposed((terminalInstance) => { this._removeInstance(terminalInstance); });
}
public createInstance(name?: string, shellPath?: string, shellArgs?: string[]): ITerminalInstance {
@@ -60,11 +64,11 @@ export class TerminalService implements ITerminalService {
executable: shellPath,
args: shellArgs
};
let terminalInstance = <TerminalInstance>this.instantiationService.createInstance(TerminalInstance,
this.terminalFocusContextKey,
let terminalInstance = <TerminalInstance>this._instantiationService.createInstance(TerminalInstance,
this._terminalFocusContextKey,
this._configHelper,
this.terminalContainer,
this.workspaceContextService.getWorkspace(),
this._terminalContainer,
this._workspaceContextService.getWorkspace(),
name,
shell);
terminalInstance.addDisposable(terminalInstance.onTitleChanged(this._onInstanceTitleChanged.fire, this._onInstanceTitleChanged));
@@ -82,7 +86,7 @@ export class TerminalService implements ITerminalService {
return this._terminalInstances.map((instance, index) => `${index + 1}: ${instance.title}`);
}
private removeInstance(terminalInstance: ITerminalInstance): void {
private _removeInstance(terminalInstance: ITerminalInstance): void {
let index = this.terminalInstances.indexOf(terminalInstance);
let wasActiveInstance = terminalInstance === this.getActiveInstance();
if (index !== -1) {
@@ -109,11 +113,11 @@ export class TerminalService implements ITerminalService {
}
public getInstanceFromId(terminalId: number): ITerminalInstance {
return this.terminalInstances[this.getIndexFromId(terminalId)];
return this.terminalInstances[this._getIndexFromId(terminalId)];
}
public setActiveInstance(terminalInstance: ITerminalInstance): void {
this.setActiveInstanceByIndex(this.getIndexFromId(terminalInstance.id));
this.setActiveInstanceByIndex(this._getIndexFromId(terminalInstance.id));
}
public setActiveInstanceByIndex(terminalIndex: number): void {
@@ -148,17 +152,17 @@ export class TerminalService implements ITerminalService {
public setContainers(panelContainer: Builder, terminalContainer: HTMLElement): void {
this._configHelper.panelContainer = panelContainer;
this.terminalContainer = terminalContainer;
this._terminalContainer = terminalContainer;
this._terminalInstances.forEach(terminalInstance => {
terminalInstance.attachToElement(this.terminalContainer);
terminalInstance.attachToElement(this._terminalContainer);
});
}
public showPanel(focus?: boolean): TPromise<void> {
return new TPromise<void>((complete) => {
let panel = this.panelService.getActivePanel();
let panel = this._panelService.getActivePanel();
if (!panel || panel.getId() !== TERMINAL_PANEL_ID) {
return this.panelService.openPanel(TERMINAL_PANEL_ID, focus).then(() => {
return this._panelService.openPanel(TERMINAL_PANEL_ID, focus).then(() => {
if (focus) {
this.getActiveInstance().focus(true);
}
@@ -174,22 +178,22 @@ export class TerminalService implements ITerminalService {
}
public hidePanel(): void {
const panel = this.panelService.getActivePanel();
const panel = this._panelService.getActivePanel();
if (panel && panel.getId() === TERMINAL_PANEL_ID) {
this.partService.setPanelHidden(true);
this._partService.setPanelHidden(true);
}
}
public togglePanel(): TPromise<any> {
const panel = this.panelService.getActivePanel();
const panel = this._panelService.getActivePanel();
if (panel && panel.getId() === TERMINAL_PANEL_ID) {
this.partService.setPanelHidden(true);
this._partService.setPanelHidden(true);
return TPromise.as(null);
}
this.showPanel(true);
}
private getIndexFromId(terminalId: number): number {
private _getIndexFromId(terminalId: number): number {
let terminalIndex = -1;
this.terminalInstances.forEach((terminalInstance, i) => {
if (terminalInstance.id === terminalId) {