diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index ea2528db..5f42d8ea 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -261,7 +261,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { this.coreMouseService.reset(); } - protected _updateOptions(key: string): void { + protected _updateOptions(key: keyof ITerminalOptions): void { // TODO: These listeners should be owned by individual components switch (key) { case 'scrollback': diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index e302d3e4..050b1cec 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -9,7 +9,7 @@ import { clone } from 'common/Clone'; import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; import { IBufferSet, IBuffer } from 'common/buffer/Types'; import { BufferSet } from 'common/buffer/BufferSet'; -import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType, ICharset, IModes, IAttributeData, IOscLinkData } from 'common/Types'; +import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType, ICharset, IModes, IAttributeData, IOscLinkData, IDisposable } from 'common/Types'; import { UnicodeV6 } from 'common/input/UnicodeV6'; import { IDecorationOptions, IDecoration } from 'xterm'; @@ -113,7 +113,7 @@ export class MockOptionsService implements IOptionsService { public serviceBrand: any; public readonly rawOptions: Required = clone(DEFAULT_OPTIONS); public options: Required = this.rawOptions; - public onOptionChange: IEvent = new EventEmitter().event; + public onOptionChange: IEvent = new EventEmitter().event; constructor(testOptions?: Partial) { if (testOptions) { for (const key of Object.keys(testOptions)) { @@ -121,6 +121,14 @@ export class MockOptionsService implements IOptionsService { } } } + // eslint-disable-next-line @typescript-eslint/naming-convention + public onSpecificOptionChange(key: T, listener: (arg1: ITerminalOptions[T]) => any): IDisposable { + return this.onOptionChange(eventKey => { + if (eventKey === key) { + listener(this.rawOptions[key]); + } + }); + } public setOptions(options: ITerminalOptions): void { for (const key of Object.keys(options)) { this.options[key] = options[key]; diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 336591e5..439d6a77 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -6,7 +6,7 @@ import { IOptionsService, ITerminalOptions, FontWeight } from 'common/services/Services'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { isMac } from 'common/Platform'; -import { CursorStyle } from 'common/Types'; +import { CursorStyle, IDisposable } from 'common/Types'; import { Disposable } from 'common/Lifecycle'; export const DEFAULT_OPTIONS: Readonly> = { @@ -58,7 +58,7 @@ export class OptionsService extends Disposable implements IOptionsService { public readonly rawOptions: Required; public options: Required; - private readonly _onOptionChange = this.register(new EventEmitter()); + private readonly _onOptionChange = this.register(new EventEmitter()); public readonly onOptionChange = this._onOptionChange.event; constructor(options: Partial) { @@ -82,6 +82,15 @@ export class OptionsService extends Disposable implements IOptionsService { this._setupOptions(); } + // eslint-disable-next-line @typescript-eslint/naming-convention + public onSpecificOptionChange(key: T, listener: (value: ITerminalOptions[T]) => any): IDisposable { + return this.onOptionChange(eventKey => { + if (eventKey === key) { + listener(this.rawOptions[key]); + } + }); + } + private _setupOptions(): void { const getter = (propName: string): any => { if (!(propName in DEFAULT_OPTIONS)) { diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index e2b517cd..9b591962 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -182,9 +182,25 @@ export interface IOptionsService { * internally. */ readonly rawOptions: Required; + + /** + * Options as exposed through the public API, this property uses getters and setters with + * validation which makes it safer but slower. {@link rawOptions} should be used for pretty much + * all internal usage for performance reasons. + */ readonly options: Required; - readonly onOptionChange: IEvent; + /** + * Adds an event listener for when any option changes. + */ + readonly onOptionChange: IEvent; + + /** + * Adds an event listener for when a specific option changes, this is a convenience method that is + * preferred over {@link onOptionChange} when only a single option is being listened to. + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + onSpecificOptionChange(key: T, listener: (arg1: ITerminalOptions[T]) => any): IDisposable; } export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' | number;