Merge branch 'master' into addons_in_repo

This commit is contained in:
Daniel Imms
2019-06-01 14:36:55 -07:00
committed by GitHub
13 changed files with 338 additions and 271 deletions
+1 -1
View File
@@ -1366,7 +1366,7 @@ describe('Buffer', () => {
const input = '\thttps://google.de';
terminal.writeSync(input);
const s = terminal.buffer.iterator(true).next().content;
assert.equal(s, Array(terminal.getOption('tabStopWidth') + 1).join(' ') + 'https://google.de');
assert.equal(s, Array(terminal.options.tabStopWidth + 1).join(' ') + 'https://google.de');
});
});
describe('BufferStringIterator', function(): void {
+4 -4
View File
@@ -1842,19 +1842,19 @@ export class InputHandler extends Disposable implements IInputHandler {
switch (param) {
case 1:
case 2:
this._terminal.setOption('cursorStyle', 'block');
this._terminal.options.cursorStyle = 'block';
break;
case 3:
case 4:
this._terminal.setOption('cursorStyle', 'underline');
this._terminal.options.cursorStyle = 'underline';
break;
case 5:
case 6:
this._terminal.setOption('cursorStyle', 'bar');
this._terminal.options.cursorStyle = 'bar';
break;
}
const isBlinking = param % 2 === 1;
this._terminal.setOption('cursorBlink', isBlinking);
this._terminal.options.cursorBlink = isBlinking;
}
}
-6
View File
@@ -5,12 +5,6 @@
import { ITerminal, ISoundManager } from './Types';
// Source: https://freesound.org/people/altemark/sounds/45759/
// This sound is released under the Creative Commons Attribution 3.0 Unported
// (CC BY 3.0) license. It was created by 'altemark'. No modifications have been
// made, apart from the conversion to base64.
export const DEFAULT_BELL_SOUND = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQBAADpAFgCwAMlBZoG/wdmCcoKRAypDQ8PbRDBEQQTOxRtFYcWlBePGIUZXhoiG88bcBz7HHIdzh0WHlMeZx51HmkeUx4WHs8dah0AHXwc3hs9G4saxRnyGBIYGBcQFv8U4RPAEoYRQBACD70NWwwHC6gJOwjWBloF7gOBAhABkf8b/qv8R/ve+Xf4Ife79W/0JfPZ8Z/wde9N7ijtE+wU6xvqM+lb6H7nw+YX5mrlxuQz5Mzje+Ma49fioeKD4nXiYeJy4pHitOL04j/jn+MN5IPkFOWs5U3mDefM55/ogOl36m7rdOyE7abuyu8D8Unyj/Pg9D/2qfcb+Yn6/vuK/Qj/lAAlAg==';
export class SoundManager implements ISoundManager {
private static _audioContext: AudioContext;
+3 -31
View File
@@ -43,7 +43,7 @@ describe('Terminal', () => {
});
it('should not mutate the options parameter', () => {
term.setOption('cols', 1000);
term.options.cols = 1000;
assert.deepEqual(termOptions, {
cols: INIT_COLS,
@@ -51,22 +51,6 @@ describe('Terminal', () => {
});
});
describe('getOption', () => {
it('should retrieve the option correctly', () => {
// In the `options` namespace.
term.options.cursorBlink = true;
assert.equal(term.getOption('cursorBlink'), true);
// On the Terminal instance
delete term.options.cursorBlink;
term.options.cursorBlink = false;
assert.equal(term.getOption('cursorBlink'), false);
});
it('should throw when retrieving a non-existant option', () => {
assert.throws(term.getOption.bind(term, 'fake', true));
});
});
describe('events', () => {
it('should fire the onData evnet', (done) => {
term.onData(() => done());
@@ -335,18 +319,6 @@ describe('Terminal', () => {
});
});
describe('setOption', () => {
it('should set option correctly', () => {
term.setOption('cursorBlink', true);
assert.equal(term.options.cursorBlink, true);
term.setOption('cursorBlink', false);
assert.equal(term.options.cursorBlink, false);
});
it('should throw when setting a non-existant option', () => {
assert.throws(term.setOption.bind(term, 'fake', true));
});
});
describe('reset', () => {
it('should not affect cursorState', () => {
term.cursorState = 1;
@@ -625,7 +597,7 @@ describe('Terminal', () => {
describe('when scrollback === 0', () => {
beforeEach(() => {
term.setOption('scrollback', 0);
term.optionsService.setOption('scrollback', 0);
assert.equal(term.buffer.lines.maxLength, INIT_ROWS);
});
@@ -730,7 +702,7 @@ describe('Terminal', () => {
describe('with macOptionIsMeta', () => {
beforeEach(() => {
term.browser.isMac = true;
term.setOption('macOptionIsMeta', true);
term.options.macOptionIsMeta = true;
});
it('should interfere with the alt key on keyDown', () => {
+80 -199
View File
@@ -24,7 +24,7 @@
import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, ILinkifier, ILinkMatcherOptions, CustomKeyEventHandler, LinkMatcherHandler, CharacterJoinerHandler, IMouseZoneManager } from './Types';
import { IRenderer } from './renderer/Types';
import { BufferSet } from './BufferSet';
import { Buffer, MAX_BUFFER_SIZE } from './Buffer';
import { Buffer } from './Buffer';
import { CompositionHelper } from './CompositionHelper';
import { EventEmitter } from 'common/EventEmitter';
import { Viewport } from './Viewport';
@@ -39,7 +39,7 @@ import * as Browser from 'common/Platform';
import { addDisposableDomListener } from 'ui/Lifecycle';
import * as Strings from './Strings';
import { MouseHelper } from './MouseHelper';
import { DEFAULT_BELL_SOUND, SoundManager } from './SoundManager';
import { SoundManager } from './SoundManager';
import { MouseZoneManager } from './MouseZoneManager';
import { AccessibilityManager } from './AccessibilityManager';
import { ITheme, IMarker, IDisposable, ISelectionPosition } from 'xterm';
@@ -48,12 +48,13 @@ import { DomRenderer } from './renderer/dom/DomRenderer';
import { IKeyboardEvent } from 'common/Types';
import { evaluateKeyboardEvent } from 'core/input/Keyboard';
import { KeyboardResultType, ICharset, IBufferLine, IAttributeData } from 'core/Types';
import { clone } from 'common/Clone';
import { EventEmitter2, IEvent } from 'common/EventEmitter2';
import { Attributes, DEFAULT_ATTR_DATA } from 'core/buffer/BufferLine';
import { applyWindowsMode } from './WindowsMode';
import { ColorManager } from 'ui/ColorManager';
import { RenderCoordinator } from './renderer/RenderCoordinator';
import { IOptionsService } from 'common/options/Types';
import { OptionsService } from 'common/options/OptionsService';
// Let it work inside Node.js for automated testing purposes.
const document = (typeof window !== 'undefined') ? window.document : null;
@@ -77,44 +78,6 @@ const WRITE_BUFFER_LENGTH_THRESHOLD = 50;
const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars
const MINIMUM_ROWS = 1;
/**
* The set of options that only have an effect when set in the Terminal constructor.
*/
const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows'];
const DEFAULT_OPTIONS: ITerminalOptions = {
cols: 80,
rows: 24,
convertEol: false,
termName: 'xterm',
cursorBlink: false,
cursorStyle: 'block',
bellSound: DEFAULT_BELL_SOUND,
bellStyle: 'none',
drawBoldTextInBrightColors: true,
fontFamily: 'courier-new, courier, monospace',
fontSize: 15,
fontWeight: 'normal',
fontWeightBold: 'bold',
lineHeight: 1.0,
letterSpacing: 0,
scrollback: 1000,
screenKeys: false,
screenReaderMode: false,
debug: false,
macOptionIsMeta: false,
macOptionClickForcesSelection: false,
cancelEvents: false,
disableStdin: false,
useFlowControl: false,
allowTransparency: false,
tabStopWidth: 8,
theme: undefined,
rightClickSelectsWord: Browser.isMac,
rendererType: 'canvas',
windowsMode: false
};
export class Terminal extends EventEmitter implements ITerminal, IDisposable, IInputHandlingTerminal {
public textarea: HTMLTextAreaElement;
public element: HTMLElement;
@@ -135,7 +98,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
public browser: IBrowser = <any>Browser;
public options: ITerminalOptions;
// TODO: We should remove options once components adopt optionsService
public get options(): ITerminalOptions { return this.optionsService.options; }
// TODO: This can be changed to an enum or boolean, 0 and 1 seem to be the only options
public cursorState: number;
@@ -143,6 +107,9 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
private _customKeyEventHandler: CustomKeyEventHandler;
// services
public optionsService: IOptionsService;
// modes
public applicationKeypad: boolean;
public applicationCursor: boolean;
@@ -257,7 +224,10 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
options: ITerminalOptions = {}
) {
super();
this.options = clone(options);
this.optionsService = new OptionsService(options);
this._setupOptionsListeners();
// this.options = clone(options);
this._setup();
// TODO: Remove these in v4
@@ -289,24 +259,11 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
}
private _setup(): void {
Object.keys(DEFAULT_OPTIONS).forEach((key) => {
if (this.options[key] === null || this.options[key] === undefined) {
this.options[key] = DEFAULT_OPTIONS[key];
}
});
// this.context = options.context || window;
// this.document = options.document || document;
// TODO: WHy not document.body?
this._parent = document ? document.body : null;
this.cols = Math.max(this.options.cols, MINIMUM_COLS);
this.rows = Math.max(this.options.rows, MINIMUM_ROWS);
if (this.options.handler) {
this.onData(this.options.handler);
}
this.cursorState = 0;
this.cursorHidden = false;
this._customKeyEventHandler = null;
@@ -394,82 +351,59 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
return document.activeElement === this.textarea && document.hasFocus();
}
/**
* Retrieves an option's value from the terminal.
* @param key The option key.
*/
public getOption(key: string): any {
if (!(key in DEFAULT_OPTIONS)) {
throw new Error('No option with key "' + key + '"');
}
return this.options[key];
}
/**
* Sets an option on the terminal.
* @param key The option key.
* @param value The option value.
*/
public setOption(key: string, value: any): void {
if (!(key in DEFAULT_OPTIONS)) {
throw new Error('No option with key "' + key + '"');
}
if (CONSTRUCTOR_ONLY_OPTIONS.indexOf(key) !== -1) {
console.error(`Option "${key}" can only be set in the constructor`);
}
if (this.options[key] === value) {
return;
}
switch (key) {
case 'bellStyle':
if (!value) {
value = 'none';
}
break;
case 'cursorStyle':
if (!value) {
value = 'block';
}
break;
case 'fontWeight':
if (!value) {
value = 'normal';
}
break;
case 'fontWeightBold':
if (!value) {
value = 'bold';
}
break;
case 'lineHeight':
if (value < 1) {
console.warn(`${key} cannot be less than 1, value: ${value}`);
return;
}
case 'rendererType':
if (!value) {
value = 'canvas';
}
break;
case 'tabStopWidth':
if (value < 1) {
console.warn(`${key} cannot be less than 1, value: ${value}`);
return;
}
break;
case 'theme':
this._setTheme(<ITheme>value);
break;
case 'scrollback':
value = Math.min(value, MAX_BUFFER_SIZE);
if (value < 0) {
console.warn(`${key} cannot be less than 0, value: ${value}`);
return;
}
if (this.options[key] !== value) {
const newBufferLength = this.rows + value;
private _setupOptionsListeners(): void {
// TODO: These listeners should be owned by individual components
this.optionsService.onOptionChange(key => {
switch (key) {
case 'fontFamily':
case 'fontSize':
// When the font changes the size of the cells may change which requires a renderer clear
if (this._renderCoordinator) {
this._renderCoordinator.clear();
this.charMeasure.measure(this.options);
}
break;
case 'drawBoldTextInBrightColors':
case 'letterSpacing':
case 'lineHeight':
case 'fontWeight':
case 'fontWeightBold':
// When the font changes the size of the cells may change which requires a renderer clear
if (this._renderCoordinator) {
this._renderCoordinator.clear();
this._renderCoordinator.onResize(this.cols, this.rows);
this.refresh(0, this.rows - 1);
}
break;
case 'rendererType':
if (this._renderCoordinator) {
this._renderCoordinator.setRenderer(this._createRenderer());
}
break;
case 'scrollback':
this.buffers.resize(this.cols, this.rows);
if (this.viewport) {
this.viewport.syncScrollArea();
}
break;
case 'screenReaderMode':
if (this.optionsService.options.screenReaderMode) {
if (!this._accessibilityManager && this._renderCoordinator) {
this._accessibilityManager = new AccessibilityManager(this, this._renderCoordinator.dimensions);
}
} else {
if (this._accessibilityManager) {
this._accessibilityManager.dispose();
this._accessibilityManager = null;
}
}
break;
case 'tabStopWidth': this.buffers.setupTabStops(); break;
case 'theme':
this._setTheme(this.optionsService.options.theme);
break;
case 'scrollback':
const newBufferLength = this.rows + this.optionsService.options.scrollback;
if (this.buffer.lines.length > newBufferLength) {
const amountToTrim = this.buffer.lines.length - newBufferLength;
const needsRefresh = (this.buffer.ydisp - amountToTrim < 0);
@@ -480,72 +414,20 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
this.refresh(0, this.rows - 1);
}
}
}
break;
}
this.options[key] = value;
switch (key) {
case 'fontFamily':
case 'fontSize':
// When the font changes the size of the cells may change which requires a renderer clear
if (this._renderCoordinator) {
this._renderCoordinator.clear();
this.charMeasure.measure(this.options);
}
break;
case 'drawBoldTextInBrightColors':
case 'letterSpacing':
case 'lineHeight':
case 'fontWeight':
case 'fontWeightBold':
// When the font changes the size of the cells may change which requires a renderer clear
if (this._renderCoordinator) {
this._renderCoordinator.clear();
this._renderCoordinator.onResize(this.cols, this.rows);
this.refresh(0, this.rows - 1);
}
break;
case 'rendererType':
if (this._renderCoordinator) {
this._renderCoordinator.setRenderer(this._createRenderer());
}
break;
case 'scrollback':
this.buffers.resize(this.cols, this.rows);
if (this.viewport) {
this.viewport.syncScrollArea();
}
break;
case 'screenReaderMode':
if (value) {
if (!this._accessibilityManager && this._renderCoordinator) {
this._accessibilityManager = new AccessibilityManager(this, this._renderCoordinator.dimensions);
case 'windowsMode':
if (this.optionsService.options.windowsMode) {
if (!this._windowsMode) {
this._windowsMode = applyWindowsMode(this);
}
} else {
if (this._windowsMode) {
this._windowsMode.dispose();
this._windowsMode = undefined;
}
}
} else {
if (this._accessibilityManager) {
this._accessibilityManager.dispose();
this._accessibilityManager = null;
}
}
break;
case 'tabStopWidth': this.buffers.setupTabStops(); break;
case 'windowsMode':
if (value) {
if (!this._windowsMode) {
this._windowsMode = applyWindowsMode(this);
}
} else {
if (this._windowsMode) {
this._windowsMode.dispose();
this._windowsMode = undefined;
}
}
break;
}
// Inform renderer of changes
if (this._renderCoordinator) {
this._renderCoordinator.onOptionsChanged();
}
break;
}
});
}
/**
@@ -717,7 +599,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
this.textarea = document.createElement('textarea');
this.textarea.classList.add('xterm-helper-textarea');
// TODO: New API to set title? This could say "Terminal bash input", etc.
this.textarea.setAttribute('aria-label', Strings.promptLabel);
this.textarea.setAttribute('aria-multiline', 'false');
this.textarea.setAttribute('autocorrect', 'off');
@@ -744,7 +625,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
this._colorManager.setTheme(this._theme);
const renderer = this._createRenderer();
this._renderCoordinator = new RenderCoordinator(renderer, this.rows, this.screenElement);
this._renderCoordinator = new RenderCoordinator(renderer, this.rows, this.screenElement, this.optionsService);
this._renderCoordinator.onRender(e => this._onRender.fire(e));
this.onResize(e => this._renderCoordinator.resize(e.cols, e.rows));
+4 -4
View File
@@ -13,6 +13,7 @@ import { IDisposable, IMarker, IEvent, ISelectionPosition } from 'xterm';
import { Terminal } from './Terminal';
import { AttributeData } from 'core/buffer/BufferLine';
import { IColorManager, IColorSet } from 'ui/Types';
import { IOptionsService } from 'common/options/Types';
export class TestTerminal extends Terminal {
writeSync(data: string): void {
@@ -29,9 +30,11 @@ export class MockTerminal implements ITerminal {
onTitleChange: IEvent<string>;
onScroll: IEvent<number>;
onKey: IEvent<{ key: string; domEvent: KeyboardEvent; }>;
onRender: IEvent<{ start: number; end: number; }>;
onRender: IEvent<{ start: number
; end: number; }>;
onResize: IEvent<{ cols: number; rows: number; }>;
markers: IMarker[];
optionsService: IOptionsService;
addMarker(cursorYOffset: number): IMarker {
throw new Error('Method not implemented.');
}
@@ -42,9 +45,6 @@ export class MockTerminal implements ITerminal {
throw new Error('Method not implemented.');
}
static string: any;
getOption(key: any): any {
throw new Error('Method not implemented.');
}
setOption(key: any, value: any): void {
throw new Error('Method not implemented.');
}
+4 -4
View File
@@ -8,6 +8,7 @@ import { ICharset, IAttributeData, ICellData, IBufferLine, CharData } from 'core
import { ICircularList } from 'common/Types';
import { IEvent } from 'common/EventEmitter2';
import { IColorSet } from 'ui/Types';
import { IOptionsService } from 'common/options/Types';
export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
@@ -72,7 +73,6 @@ export interface IInputHandlingTerminal extends IEventEmitter {
showCursor(): void;
refresh(start: number, end: number): void;
error(text: string, data?: any): void;
setOption(key: string, value: any): void;
tabSet(): void;
handler(data: string): void;
handleTitle(title: string): void;
@@ -204,7 +204,6 @@ export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAcc
writeBuffer: string[];
cursorHidden: boolean;
cursorState: number;
options: ITerminalOptions;
buffer: IBuffer;
buffers: IBufferSet;
isFocused: boolean;
@@ -212,6 +211,9 @@ export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAcc
viewport: IViewport;
bracketedPasteMode: boolean;
applicationCursor: boolean;
optionsService: IOptionsService;
// TODO: We should remove options once components adopt optionsService
options: ITerminalOptions;
handler(data: string): void;
scrollLines(disp: number, suppressScrollEvent?: boolean): void;
@@ -265,8 +267,6 @@ export interface IPublicTerminal extends IDisposable, IEventEmitter {
clear(): void;
write(data: string): void;
writeUtf8(data: Uint8Array): void;
getOption(key: string): any;
setOption(key: string, value: any): void;
refresh(start: number, end: number): void;
reset(): void;
}
-8
View File
@@ -38,14 +38,6 @@ describe('clone', () => {
});
});
it('should clone null values', () => {
const test: any = {
a: null
};
assert.deepEqual(clone(test), { a: null });
});
it('should clone array values', () => {
const test = {
a: [1, 2, 3],
+2 -7
View File
@@ -6,22 +6,17 @@
/*
* A simple utility for cloning values
*/
export function clone<T>(val: T, depth: number = 5): T | null {
export function clone<T>(val: T, depth: number = 5): T {
if (typeof val !== 'object') {
return val;
}
// cloning null always returns null
if (val === null) {
return null;
}
// If we're cloning an array, use an array as the base, otherwise use an object
const clonedObject: any = Array.isArray(val) ? [] : {};
for (const key in val) {
// Recursively clone eack item unless we're at the maximum depth
clonedObject[key] = depth <= 1 ? val[key] : clone(val[key], depth - 1);
clonedObject[key] = depth <= 1 ? val[key] : (val[key] ? clone(val[key], depth - 1) : val[key]);
}
return clonedObject as T;
+128
View File
@@ -0,0 +1,128 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IOptionsService, ITerminalOptions, IPartialTerminalOptions } from 'common/options/Types';
import { EventEmitter2, IEvent } from 'common/EventEmitter2';
import { isMac } from 'common/Platform';
import { clone } from 'common/Clone';
// Source: https://freesound.org/people/altemark/sounds/45759/
// This sound is released under the Creative Commons Attribution 3.0 Unported
// (CC BY 3.0) license. It was created by 'altemark'. No modifications have been
// made, apart from the conversion to base64.
export const DEFAULT_BELL_SOUND = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQBAADpAFgCwAMlBZoG/wdmCcoKRAypDQ8PbRDBEQQTOxRtFYcWlBePGIUZXhoiG88bcBz7HHIdzh0WHlMeZx51HmkeUx4WHs8dah0AHXwc3hs9G4saxRnyGBIYGBcQFv8U4RPAEoYRQBACD70NWwwHC6gJOwjWBloF7gOBAhABkf8b/qv8R/ve+Xf4Ife79W/0JfPZ8Z/wde9N7ijtE+wU6xvqM+lb6H7nw+YX5mrlxuQz5Mzje+Ma49fioeKD4nXiYeJy4pHitOL04j/jn+MN5IPkFOWs5U3mDefM55/ogOl36m7rdOyE7abuyu8D8Unyj/Pg9D/2qfcb+Yn6/vuK/Qj/lAAlAg==';
// TODO: Freeze?
const DEFAULT_OPTIONS: ITerminalOptions = {
cols: 80,
rows: 24,
cursorBlink: false,
cursorStyle: 'block',
bellSound: DEFAULT_BELL_SOUND,
bellStyle: 'none',
drawBoldTextInBrightColors: true,
fontFamily: 'courier-new, courier, monospace',
fontSize: 15,
fontWeight: 'normal',
fontWeightBold: 'bold',
lineHeight: 1.0,
letterSpacing: 0,
scrollback: 1000,
screenReaderMode: false,
macOptionIsMeta: false,
macOptionClickForcesSelection: false,
disableStdin: false,
allowTransparency: false,
tabStopWidth: 8,
theme: {},
rightClickSelectsWord: isMac,
rendererType: 'canvas',
windowsMode: false,
convertEol: false,
termName: 'xterm',
screenKeys: false,
debug: false,
cancelEvents: false,
useFlowControl: false
};
/**
* The set of options that only have an effect when set in the Terminal constructor.
*/
const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows'];
export class OptionsService implements IOptionsService {
public options: ITerminalOptions;
private _onOptionChange = new EventEmitter2<string>();
public get onOptionChange(): IEvent<string> { return this._onOptionChange.event; }
constructor(options: IPartialTerminalOptions) {
this.options = clone(DEFAULT_OPTIONS);
Object.keys(options).forEach(k => {
if (k in this.options) {
const newValue = options[k as keyof IPartialTerminalOptions] as any;
this.options[k] = newValue;
}
});
}
public setOption(key: string, value: any): void {
if (!(key in DEFAULT_OPTIONS)) {
throw new Error('No option with key "' + key + '"');
}
if (CONSTRUCTOR_ONLY_OPTIONS.indexOf(key) !== -1) {
throw new Error(`Option "${key}" can only be set in the constructor`);
}
if (this.options[key] === value) {
return;
}
value = this._sanitizeAndValidateOption(key, value);
// Don't fire an option change event if they didn't change
if (this.options[key] === value) {
return;
}
this.options[key] = value;
this._onOptionChange.fire(key);
}
private _sanitizeAndValidateOption(key: string, value: any): any {
switch (key) {
case 'bellStyle':
case 'cursorStyle':
case 'fontWeight':
case 'fontWeightBold':
case 'rendererType':
if (!value) {
value = DEFAULT_OPTIONS[key];
}
break;
case 'lineHeight':
case 'tabStopWidth':
if (value < 1) {
throw new Error(`${key} cannot be less than 1, value: ${value}`);
}
break;
case 'scrollback':
value = Math.min(value, 4294967295);
if (value < 0) {
throw new Error(`${key} cannot be less than 0, value: ${value}`);
}
break;
}
return value;
}
public getOption(key: string): any {
if (!(key in DEFAULT_OPTIONS)) {
throw new Error(`No option with key "${key}"`);
}
return this.options[key];
}
}
+105
View File
@@ -0,0 +1,105 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IEvent } from 'common/EventEmitter2';
export interface IOptionsService {
readonly onOptionChange: IEvent<string>;
// TODO: as const?
readonly options: ITerminalOptions;
setOption<T>(key: string, value: T): void;
getOption<T>(key: string): T | undefined;
}
export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
export type RendererType = 'dom' | 'canvas';
export interface IPartialTerminalOptions {
allowTransparency?: boolean;
bellSound?: string;
bellStyle?: 'none' /*| 'visual'*/ | 'sound' /*| 'both'*/;
cols?: number;
cursorBlink?: boolean;
cursorStyle?: 'block' | 'underline' | 'bar';
disableStdin?: boolean;
drawBoldTextInBrightColors?: boolean;
fontSize?: number;
fontFamily?: string;
fontWeight?: FontWeight;
fontWeightBold?: FontWeight;
letterSpacing?: number;
lineHeight?: number;
macOptionIsMeta?: boolean;
macOptionClickForcesSelection?: boolean;
rendererType?: RendererType;
rightClickSelectsWord?: boolean;
rows?: number;
screenReaderMode?: boolean;
scrollback?: number;
tabStopWidth?: number;
theme?: ITheme;
windowsMode?: boolean;
}
export interface ITerminalOptions {
allowTransparency: boolean;
bellSound: string;
bellStyle: 'none' /*| 'visual'*/ | 'sound' /*| 'both'*/;
cols: number;
cursorBlink: boolean;
cursorStyle: 'block' | 'underline' | 'bar';
disableStdin: boolean;
drawBoldTextInBrightColors: boolean;
fontSize: number;
fontFamily: string;
fontWeight: FontWeight;
fontWeightBold: FontWeight;
letterSpacing: number;
lineHeight: number;
macOptionIsMeta: boolean;
macOptionClickForcesSelection: boolean;
rendererType: RendererType;
rightClickSelectsWord: boolean;
rows: number;
screenReaderMode: boolean;
scrollback: number;
tabStopWidth: number;
theme: ITheme;
windowsMode: boolean;
[key: string]: any;
cancelEvents: boolean;
convertEol: boolean;
debug: boolean;
screenKeys: boolean;
termName: string;
useFlowControl: boolean;
}
export interface ITheme {
foreground?: string;
background?: string;
cursor?: string;
cursorAccent?: string;
selection?: string;
black?: string;
red?: string;
green?: string;
yellow?: string;
blue?: string;
magenta?: string;
cyan?: string;
white?: string;
brightBlack?: string;
brightRed?: string;
brightGreen?: string;
brightYellow?: string;
brightBlue?: string;
brightMagenta?: string;
brightCyan?: string;
brightWhite?: string;
}
+2 -2
View File
@@ -152,7 +152,7 @@ export class Terminal implements ITerminalApi {
public getOption(key: 'handler'): (data: string) => void;
public getOption(key: string): any;
public getOption(key: any): any {
return this._core.getOption(key);
return this._core.optionsService.getOption(key);
}
public setOption(key: 'bellSound' | 'fontFamily' | 'termName', value: string): void;
public setOption(key: 'fontWeight' | 'fontWeightBold', value: 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'): void;
@@ -166,7 +166,7 @@ export class Terminal implements ITerminalApi {
public setOption(key: 'cols' | 'rows', value: number): void;
public setOption(key: string, value: any): void;
public setOption(key: any, value: any): void {
this._core.setOption(key, value);
this._core.optionsService.setOption(key, value);
}
public refresh(start: number, end: number): void {
this._core.refresh(start, end);
+5 -5
View File
@@ -11,6 +11,7 @@ import { ScreenDprMonitor } from 'ui/ScreenDprMonitor';
import { addDisposableDomListener } from 'ui/Lifecycle';
import { IColorSet } from 'ui/Types';
import { CharacterJoinerHandler } from '../Types';
import { IOptionsService } from 'common/options/Types';
export class RenderCoordinator extends Disposable {
private _renderDebouncer: RenderDebouncer;
@@ -33,7 +34,8 @@ export class RenderCoordinator extends Disposable {
constructor(
private _renderer: IRenderer,
private _rowCount: number,
screenElement: HTMLElement
screenElement: HTMLElement,
optionsService: IOptionsService
) {
super();
this._renderDebouncer = new RenderDebouncer((start, end) => this._renderRows(start, end));
@@ -43,6 +45,8 @@ export class RenderCoordinator extends Disposable {
this._screenDprMonitor.setListener(() => this._renderer.onDevicePixelRatioChange());
this.register(this._screenDprMonitor);
this.register(optionsService.onOptionChange(() => this._renderer.onOptionsChanged()));
// dprchange should handle this case, we need this as well for browsers that don't support the
// matchMedia query.
this.register(addDisposableDomListener(window, 'resize', () => this._renderer.onDevicePixelRatioChange()));
@@ -144,10 +148,6 @@ export class RenderCoordinator extends Disposable {
this._renderer.onCursorMove();
}
public onOptionsChanged(): void {
this._renderer.onOptionsChanged();
}
public clear(): void {
this._renderer.clear();
}