diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts index 8141269d..4561b094 100644 --- a/src/browser/public/Terminal.ts +++ b/src/browser/public/Terminal.ts @@ -30,16 +30,22 @@ export class Terminal implements ITerminalApi { this._core = new TerminalCore(options); this._addonManager = new AddonManager(); - this._publicOptions = new Proxy(this._core.options, { - get: (target, propName: string): any => { - return target[propName]; - }, - set: (target, propName: string, value): any => { - this._checkReadonlyOptions(propName); - target[propName] = value; - return true; - } - }); + this._publicOptions = {}; + 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) { + const desc = { + get: getter.bind(this, propName), + set: setter.bind(this, propName) + }; + Object.defineProperty(this._publicOptions, propName, desc); + } } private _checkReadonlyOptions(propName: string): void { diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 67488be0..effe1ca5 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -120,11 +120,13 @@ export class MockLogService implements ILogService { export class MockOptionsService implements IOptionsService { public serviceBrand: any; + public readonly rawOptions: ITerminalOptions = clone(DEFAULT_OPTIONS); public options: ITerminalOptions = clone(DEFAULT_OPTIONS); public onOptionChange: IEvent = new EventEmitter().event; constructor(testOptions?: Partial) { if (testOptions) { for (const key of Object.keys(testOptions)) { + this.rawOptions = testOptions[key]; this.options[key] = testOptions[key]; } } diff --git a/src/common/services/OptionsService.test.ts b/src/common/services/OptionsService.test.ts index e140b5b4..ae9e77ec 100644 --- a/src/common/services/OptionsService.test.ts +++ b/src/common/services/OptionsService.test.ts @@ -28,6 +28,10 @@ describe('OptionsService', () => { it('uses default value if invalid constructor option value passed', () => { assert.equal(new OptionsService({ tabStopWidth: 0 }).getOption('tabStopWidth'), DEFAULT_OPTIONS.tabStopWidth); }); + it('object.keys return the correct number of options', () => { + const optionsService = new OptionsService({ cols: 80, rows: 25 }); + assert.notEqual(Object.keys(optionsService.options).length, 0); + }); }); describe('setOption', () => { let service: OptionsService; diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 6d866014..43fe9981 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -60,6 +60,7 @@ const FONT_WEIGHT_OPTIONS: Extract[] = ['normal', 'bold', '1 export class OptionsService implements IOptionsService { public serviceBrand: any; + public readonly rawOptions: ITerminalOptions; public options: ITerminalOptions; private _onOptionChange = new EventEmitter(); @@ -80,31 +81,39 @@ export class OptionsService implements IOptionsService { } // set up getters and setters for each option - this.options = this._setupOptions(defaultOptions); + this.rawOptions = defaultOptions; + this.options = { ... defaultOptions }; + this._setupOptions(); } - private _setupOptions(options: ITerminalOptions): ITerminalOptions { - return new Proxy(options, { - get: (target, propName: string): any => { - if (!(propName in DEFAULT_OPTIONS)) { - throw new Error(`No option with key "${propName}"`); - } - return target[propName]; - }, - set: (target, propName: string, value): any => { - if (!(propName in DEFAULT_OPTIONS)) { - throw new Error(`No option with key "${propName}"`); - } - - value = this._sanitizeAndValidateOption(propName, value); - // Don't fire an option change event if they didn't change - if (target[propName] !== value) { - target[propName] = value; - this._onOptionChange.fire(propName); - } - return true; + private _setupOptions(): void { + const getter = (propName: string): any => { + if (!(propName in DEFAULT_OPTIONS)) { + throw new Error(`No option with key "${propName}"`); } - }); + return this.rawOptions[propName]; + }; + + const setter = (propName: string, value: any): void => { + if (!(propName in DEFAULT_OPTIONS)) { + throw new Error(`No option with key "${propName}"`); + } + + value = this._sanitizeAndValidateOption(propName, value); + // Don't fire an option change event if they didn't change + if (this.rawOptions[propName] !== value) { + this.rawOptions[propName] = value; + this._onOptionChange.fire(propName); + } + }; + + for (const propName in this.rawOptions) { + const desc = { + get: getter.bind(this, propName), + set: setter.bind(this, propName) + }; + Object.defineProperty(this.options, propName, desc); + } } public setOption(key: string, value: any): void { diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 537ac6db..9b374e71 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -188,6 +188,7 @@ export const IOptionsService = createDecorator('OptionsService' export interface IOptionsService { serviceBrand: undefined; + readonly rawOptions: Readonly; readonly options: ITerminalOptions; readonly onOptionChange: IEvent; diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts index 7243e617..2b0c00d4 100644 --- a/test/api/Terminal.api.ts +++ b/test/api/Terminal.api.ts @@ -189,6 +189,10 @@ describe('API Integration Tests', function(): void { assert.equal(await page.evaluate(`window.term.options.fontSize`), 30); assert.equal(await page.evaluate(`window.term.options.fontFamily`), 'Arial'); }); + it('object.keys return the correct number of options', async () => { + await openTerminal(page); + assert.notEqual(await page.evaluate(`Object.keys(window.term.options).length`), 0); + }); }); describe('renderer', () => {