From 8bdeb14bc6e081761fea8f54ee0369838255c0c5 Mon Sep 17 00:00:00 2001 From: Simon Lamon Date: Sat, 8 Jan 2022 10:14:33 +0000 Subject: [PATCH 1/2] add options to headless typings --- src/headless/public/Terminal.test.ts | 39 ++++++++++++++++++--- src/headless/public/Terminal.ts | 52 +++++++++++++++++++++++++++- typings/xterm-headless.d.ts | 23 ++++++++++++ 3 files changed, 109 insertions(+), 5 deletions(-) diff --git a/src/headless/public/Terminal.test.ts b/src/headless/public/Terminal.test.ts index 4247aae9..a65d8775 100644 --- a/src/headless/public/Terminal.test.ts +++ b/src/headless/public/Terminal.test.ts @@ -5,10 +5,11 @@ import { deepStrictEqual, strictEqual, throws } from 'assert'; import { Terminal } from 'headless/public/Terminal'; +import { ITerminalOptions } from 'xterm-headless'; let term: Terminal; -describe('Headless API Tests', function(): void { +describe('Headless API Tests', function (): void { beforeEach(() => { // Create default terminal to be used by most tests term = new Terminal(); @@ -119,13 +120,43 @@ describe('Headless API Tests', function(): void { strictEqual(term.getOption('scrollback'), 50); }); + describe('options', () => { + const termOptions = { + cols: 80, + rows: 24 + }; + + beforeEach(async () => { + term = new Terminal(termOptions); + }); + it('get options', () => { + const options: ITerminalOptions = term.options; + strictEqual(options.cols, 80); + strictEqual(options.rows, 24); + }); + it('set options', async () => { + const options: ITerminalOptions = term.options; + throws(() => options.cols = 40); + throws(() => options.rows = 20); + term.options.scrollback = 1; + strictEqual(term.options.scrollback, 1); + term.options= { + fontSize: 12, + fontFamily: 'Arial' + }; + strictEqual(term.options.fontSize, 12); + strictEqual(term.options.fontFamily, 'Arial'); + }); + }); + + describe('loadAddon', () => { it('constructor', async () => { term = new Terminal({ cols: 5 }); let cols = 0; term.loadAddon({ activate: (t) => cols = t.cols, - dispose: () => {} + dispose: () => { } }); strictEqual(cols, 5); }); @@ -133,7 +164,7 @@ describe('Headless API Tests', function(): void { it('dispose (addon)', async () => { let disposeCalled = false; const addon = { - activate: () => {}, + activate: () => { }, dispose: () => disposeCalled = true }; term.loadAddon(addon); @@ -145,7 +176,7 @@ describe('Headless API Tests', function(): void { it('dispose (terminal)', async () => { let disposeCalled = false; term.loadAddon({ - activate: () => {}, + activate: () => { }, dispose: () => disposeCalled = true }); strictEqual(disposeCalled, false); diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index 29e83581..0de5c54d 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -7,19 +7,61 @@ import { IEvent } from 'common/EventEmitter'; import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi'; import { ParserApi } from 'common/public/ParserApi'; import { UnicodeApi } from 'common/public/UnicodeApi'; -import { IBufferNamespace as IBufferNamespaceApi, IMarker, IModes, IParser, ITerminalAddon, ITerminalOptions, IUnicodeHandling, Terminal as ITerminalApi } from 'xterm-headless'; +import { IBufferNamespace as IBufferNamespaceApi, IMarker, IModes, IParser, ITerminalAddon, IUnicodeHandling, Terminal as ITerminalApi } from 'xterm-headless'; import { Terminal as TerminalCore } from 'headless/Terminal'; import { AddonManager } from 'common/public/AddonManager'; +import { ITerminalOptions } from 'common/Types'; + +/** + * The set of options that only have an effect when set in the Terminal constructor. + */ + const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows']; export class Terminal implements ITerminalApi { private _core: TerminalCore; private _addonManager: AddonManager; private _parser: IParser | undefined; private _buffer: BufferNamespaceApi | undefined; + private _publicOptions: ITerminalOptions; constructor(options?: ITerminalOptions) { this._core = new TerminalCore(options); this._addonManager = new AddonManager(); + + this._publicOptions = { ... this._core.options }; + const getter = (propName: string): any => { + return this._core.options[propName]; + }; + const setter = (propName: string, value: any): void => { + this._checkReadonlyOptions(propName); + this._core.options[propName] = value; + }; + + for (const propName in this._core.options) { + Object.defineProperty(this._publicOptions, propName, { + get: () => { + return this._core.options[propName]; + }, + set: (value: any) => { + this._checkReadonlyOptions(propName); + this._core.options[propName] = value; + } + }); + const desc = { + get: getter.bind(this, propName), + set: setter.bind(this, propName) + }; + Object.defineProperty(this._publicOptions, propName, desc); + } + } + + private _checkReadonlyOptions(propName: string): void { + // Throw an error if any constructor only option is modified + // from terminal.options + // Modifications from anywhere else are allowed + if (CONSTRUCTOR_ONLY_OPTIONS.includes(propName)) { + throw new Error(`Option "${propName}" can only be set in the constructor`); + } } private _checkProposedApi(): void { @@ -82,6 +124,14 @@ export class Terminal implements ITerminalApi { wraparoundMode: m.wraparound }; } + public get options(): ITerminalOptions { + return this._publicOptions; + } + public set options(options: ITerminalOptions) { + for (const propName in options) { + this._publicOptions[propName] = options[propName]; + } + } public resize(columns: number, rows: number): void { this._verifyIntegers(columns, rows); this._core.resize(columns, rows); diff --git a/typings/xterm-headless.d.ts b/typings/xterm-headless.d.ts index 84b715e8..6d2b3e27 100644 --- a/typings/xterm-headless.d.ts +++ b/typings/xterm-headless.d.ts @@ -534,6 +534,29 @@ declare module 'xterm-headless' { */ readonly modes: IModes; + /** + * Gets or sets the terminal options. This supports setting multiple options. + * + * @example Get a single option + * ```typescript + * console.log(terminal.options.fontSize); + * ``` + * + * @example Set a single option + * ```typescript + * terminal.options.fontSize = 12; + * ``` + * + * @example Set multiple options + * ```typescript + * terminal.options = { + * fontSize: 12, + * fontFamily: 'Arial', + * }; + * ``` + */ + options: ITerminalOptions; + /** * Natural language strings that can be localized. */ From f03d0858cc143ca60d37ee0bf6b05ff561d413b3 Mon Sep 17 00:00:00 2001 From: Simon Lamon Date: Tue, 11 Jan 2022 11:58:53 +0000 Subject: [PATCH 2/2] fix linting error --- src/headless/public/Terminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index 0de5c54d..9aa171a4 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -15,7 +15,7 @@ import { ITerminalOptions } from 'common/Types'; /** * The set of options that only have an effect when set in the Terminal constructor. */ - const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows']; +const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows']; export class Terminal implements ITerminalApi { private _core: TerminalCore;