mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge pull request #3546 from silamon/options2
Allow setting multiple options through term.options
This commit is contained in:
@@ -11,7 +11,7 @@ import { IBufferService, IUnicodeService } from 'common/services/Services';
|
||||
import { Linkifier } from 'browser/Linkifier';
|
||||
import { MockLogService, MockUnicodeService } from 'common/TestUtils.test';
|
||||
import { IRegisteredLinkMatcher, IMouseZoneManager, IMouseZone } from 'browser/Types';
|
||||
import { IMarker } from 'common/Types';
|
||||
import { IMarker, ITerminalOptions } from 'common/Types';
|
||||
|
||||
const INIT_COLS = 80;
|
||||
const INIT_ROWS = 24;
|
||||
@@ -1501,6 +1501,22 @@ describe('Terminal', () => {
|
||||
assert.deepEqual(markers.map(el => el.line), [-1, -1, 0, 1, 2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('options', () => {
|
||||
beforeEach(async () => {
|
||||
term = new TestTerminal({});
|
||||
});
|
||||
it('get options', () => {
|
||||
assert.equal(term.options.cols, 80);
|
||||
assert.equal(term.options.rows, 24);
|
||||
});
|
||||
it('set options', async () => {
|
||||
term.options.cols = 40;
|
||||
assert.equal(term.options.cols, 40);
|
||||
term.options.rows = 20;
|
||||
assert.equal(term.options.rows, 20);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class TestLinkifier extends Linkifier {
|
||||
|
||||
@@ -39,7 +39,7 @@ import { MouseZoneManager } from 'browser/MouseZoneManager';
|
||||
import { AccessibilityManager } from './AccessibilityManager';
|
||||
import { ITheme, IMarker, IDisposable, ISelectionPosition, ILinkProvider } from 'xterm';
|
||||
import { DomRenderer } from 'browser/renderer/dom/DomRenderer';
|
||||
import { IKeyboardEvent, KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions, ScrollSource, IAnsiColorChangeEvent } from 'common/Types';
|
||||
import { IKeyboardEvent, KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ScrollSource, IAnsiColorChangeEvent } from 'common/Types';
|
||||
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
|
||||
import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
|
||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
@@ -52,9 +52,9 @@ import { MouseService } from 'browser/services/MouseService';
|
||||
import { Linkifier2 } from 'browser/Linkifier2';
|
||||
import { CoreBrowserService } from 'browser/services/CoreBrowserService';
|
||||
import { CoreTerminal } from 'common/CoreTerminal';
|
||||
import { ITerminalOptions as IInitializedTerminalOptions } from 'common/services/Services';
|
||||
import { rgba } from 'browser/Color';
|
||||
import { CharacterJoinerService } from 'browser/services/CharacterJoinerService';
|
||||
import { ITerminalOptions } from 'common/services/Services';
|
||||
|
||||
// Let it work inside Node.js for automated testing purposes.
|
||||
const document: Document = (typeof window !== 'undefined') ? window.document : null as any;
|
||||
@@ -74,9 +74,6 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
|
||||
public browser: IBrowser = Browser as any;
|
||||
|
||||
// TODO: We should remove options once components adopt optionsService
|
||||
public get options(): IInitializedTerminalOptions { return this.optionsService.options; }
|
||||
|
||||
private _customKeyEventHandler: CustomKeyEventHandler | undefined;
|
||||
|
||||
// browser services
|
||||
|
||||
Vendored
-1
@@ -16,7 +16,6 @@ export interface ITerminal extends IPublicTerminal, ICoreTerminal {
|
||||
browser: IBrowser;
|
||||
buffer: IBuffer;
|
||||
viewport: IViewport | undefined;
|
||||
// TODO: We should remove options once components adopt optionsService
|
||||
options: ITerminalOptions;
|
||||
linkifier: ILinkifier;
|
||||
linkifier2: ILinkifier2;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBufferNamespace as IBufferNamespaceApi, IParser, ILinkProvider, IUnicodeHandling, FontWeight, IModes } from 'xterm';
|
||||
import { Terminal as ITerminalApi, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBufferNamespace as IBufferNamespaceApi, IParser, ILinkProvider, IUnicodeHandling, FontWeight, IModes } from 'xterm';
|
||||
import { ITerminal } from 'browser/Types';
|
||||
import { Terminal as TerminalCore } from 'browser/Terminal';
|
||||
import * as Strings from 'browser/LocalizableStrings';
|
||||
@@ -12,16 +12,45 @@ import { ParserApi } from 'common/public/ParserApi';
|
||||
import { UnicodeApi } from 'common/public/UnicodeApi';
|
||||
import { AddonManager } from 'common/public/AddonManager';
|
||||
import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi';
|
||||
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: ITerminal;
|
||||
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 = {};
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -90,7 +119,12 @@ export class Terminal implements ITerminalApi {
|
||||
};
|
||||
}
|
||||
public get options(): ITerminalOptions {
|
||||
return this._core.options;
|
||||
return this._publicOptions;
|
||||
}
|
||||
public set options(options: ITerminalOptions) {
|
||||
for (const propName in options) {
|
||||
this._publicOptions[propName] = options[propName];
|
||||
}
|
||||
}
|
||||
public blur(): void {
|
||||
this._core.blur();
|
||||
@@ -216,6 +250,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._checkReadonlyOptions(key);
|
||||
this._core.optionsService.setOption(key, value);
|
||||
}
|
||||
public refresh(start: number, end: number): void {
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
*/
|
||||
|
||||
import { Disposable } from 'common/Lifecycle';
|
||||
import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, ICoreMouseService, IUnicodeService, IDirtyRowService, LogLevelEnum } from 'common/services/Services';
|
||||
import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, ICoreMouseService, IUnicodeService, IDirtyRowService, LogLevelEnum, ITerminalOptions } from 'common/services/Services';
|
||||
import { InstantiationService } from 'common/services/InstantiationService';
|
||||
import { LogService } from 'common/services/LogService';
|
||||
import { BufferService, MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService';
|
||||
import { OptionsService } from 'common/services/OptionsService';
|
||||
import { ITerminalOptions, IDisposable, IBufferLine, IAttributeData, ICoreTerminal, IKeyboardEvent, IScrollEvent, ScrollSource } from 'common/Types';
|
||||
import { IDisposable, IBufferLine, IAttributeData, ICoreTerminal, IKeyboardEvent, IScrollEvent, ScrollSource, ITerminalOptions as IPublicTerminalOptions } from 'common/Types';
|
||||
import { CoreService } from 'common/services/CoreService';
|
||||
import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
|
||||
import { CoreMouseService } from 'common/services/CoreMouseService';
|
||||
@@ -86,7 +86,12 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
|
||||
public get cols(): number { return this._bufferService.cols; }
|
||||
public get rows(): number { return this._bufferService.rows; }
|
||||
public get buffers(): IBufferSet { return this._bufferService.buffers; }
|
||||
public get options(): ITerminalOptions { return this.optionsService.publicOptions; }
|
||||
public get options(): ITerminalOptions { return this.optionsService.options; }
|
||||
public set options(options: ITerminalOptions) {
|
||||
for (const key in options) {
|
||||
this.optionsService.options[key] = options[key];
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
options: Partial<ITerminalOptions>
|
||||
|
||||
@@ -121,16 +121,19 @@ export class MockLogService implements ILogService {
|
||||
export class MockOptionsService implements IOptionsService {
|
||||
public serviceBrand: any;
|
||||
public options: ITerminalOptions = clone(DEFAULT_OPTIONS);
|
||||
public publicOptions: ITerminalOptions = clone(DEFAULT_OPTIONS);
|
||||
public onOptionChange: IEvent<string> = new EventEmitter<string>().event;
|
||||
constructor(testOptions?: Partial<ITerminalOptions>) {
|
||||
if (testOptions) {
|
||||
for (const key of Object.keys(testOptions)) {
|
||||
this.options[key] = testOptions[key];
|
||||
this.publicOptions[key] = testOptions[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
public setOptions(options: ITerminalOptions): void {
|
||||
for (const key of Object.keys(options)) {
|
||||
this.options[key] = options[key];
|
||||
}
|
||||
}
|
||||
public setOption<T>(key: string, value: T): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
@@ -57,17 +57,11 @@ export const DEFAULT_OPTIONS: Readonly<ITerminalOptions> = {
|
||||
|
||||
const FONT_WEIGHT_OPTIONS: Extract<FontWeight, string>[] = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'];
|
||||
|
||||
/**
|
||||
* 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 serviceBrand: any;
|
||||
|
||||
private _options: ITerminalOptions;
|
||||
public options: ITerminalOptions;
|
||||
public publicOptions: ITerminalOptions;
|
||||
|
||||
private _onOptionChange = new EventEmitter<string>();
|
||||
public get onOptionChange(): IEvent<string> { return this._onOptionChange.event; }
|
||||
@@ -87,11 +81,10 @@ export class OptionsService implements IOptionsService {
|
||||
}
|
||||
|
||||
// set up getters and setters for each option
|
||||
this.options = this._setupOptions(this._options, false);
|
||||
this.publicOptions = this._setupOptions(this._options, true);
|
||||
this.options = this._setupOptions(this._options);
|
||||
}
|
||||
|
||||
private _setupOptions(options: ITerminalOptions, isPublic: boolean): ITerminalOptions {
|
||||
private _setupOptions(options: ITerminalOptions): ITerminalOptions {
|
||||
const copiedOptions = { ... options };
|
||||
for (const propName in copiedOptions) {
|
||||
Object.defineProperty(copiedOptions, propName, {
|
||||
@@ -106,13 +99,6 @@ export class OptionsService implements IOptionsService {
|
||||
throw new Error(`No option with key "${propName}"`);
|
||||
}
|
||||
|
||||
// Throw an error if any constructor only option is modified
|
||||
// from terminal.options
|
||||
// Modifications from anywhere else are allowed
|
||||
if (isPublic && CONSTRUCTOR_ONLY_OPTIONS.includes(propName)) {
|
||||
throw new Error(`Option "${propName}" can only be set in the constructor`);
|
||||
}
|
||||
|
||||
value = this._sanitizeAndValidateOption(propName, value);
|
||||
// Don't fire an option change event if they didn't change
|
||||
if (this._options[propName] !== value) {
|
||||
@@ -126,7 +112,7 @@ export class OptionsService implements IOptionsService {
|
||||
}
|
||||
|
||||
public setOption(key: string, value: any): void {
|
||||
this.publicOptions[key] = value;
|
||||
this.options[key] = value;
|
||||
}
|
||||
|
||||
private _sanitizeAndValidateOption(key: string, value: any): any {
|
||||
@@ -181,6 +167,6 @@ export class OptionsService implements IOptionsService {
|
||||
}
|
||||
|
||||
public getOption(key: string): any {
|
||||
return this.publicOptions[key];
|
||||
return this.options[key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +164,14 @@ export interface IInstantiationService {
|
||||
createInstance<Ctor extends new (...args: any[]) => any, R extends InstanceType<Ctor>>(t: Ctor, ...args: GetLeadingNonServiceArgs<ConstructorParameters<Ctor>>): R;
|
||||
}
|
||||
|
||||
export enum LogLevelEnum {
|
||||
DEBUG = 0,
|
||||
INFO = 1,
|
||||
WARN = 2,
|
||||
ERROR = 3,
|
||||
OFF = 4
|
||||
}
|
||||
|
||||
export const ILogService = createDecorator<ILogService>('LogService');
|
||||
export interface ILogService {
|
||||
serviceBrand: undefined;
|
||||
@@ -181,7 +189,6 @@ export interface IOptionsService {
|
||||
serviceBrand: undefined;
|
||||
|
||||
readonly options: ITerminalOptions;
|
||||
readonly publicOptions: ITerminalOptions;
|
||||
|
||||
readonly onOptionChange: IEvent<string>;
|
||||
|
||||
@@ -191,13 +198,7 @@ export interface IOptionsService {
|
||||
|
||||
export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' | number;
|
||||
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'off';
|
||||
export enum LogLevelEnum {
|
||||
DEBUG = 0,
|
||||
INFO = 1,
|
||||
WARN = 2,
|
||||
ERROR = 3,
|
||||
OFF = 4
|
||||
}
|
||||
|
||||
export type RendererType = 'dom' | 'canvas';
|
||||
|
||||
export interface ITerminalOptions {
|
||||
@@ -207,6 +208,7 @@ export interface ITerminalOptions {
|
||||
bellSound: string;
|
||||
bellStyle: 'none' | 'sound' /* | 'visual' | 'both' */;
|
||||
cols: number;
|
||||
convertEol: boolean;
|
||||
cursorBlink: boolean;
|
||||
cursorStyle: 'block' | 'underline' | 'bar';
|
||||
cursorWidth: number;
|
||||
@@ -240,7 +242,6 @@ export interface ITerminalOptions {
|
||||
|
||||
[key: string]: any;
|
||||
cancelEvents: boolean;
|
||||
convertEol: boolean;
|
||||
termName: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { assert } from 'chai';
|
||||
import { pollFor, timeout, writeSync, openTerminal, launchBrowser } from './TestUtils';
|
||||
import { Browser, Page } from 'playwright';
|
||||
import { fail } from 'assert';
|
||||
|
||||
const APP = 'http://127.0.0.1:3001/test';
|
||||
|
||||
@@ -160,6 +161,36 @@ describe('API Integration Tests', function(): void {
|
||||
assert.equal(await page.evaluate(`window.term.getOption('rendererType')`), 'dom');
|
||||
});
|
||||
|
||||
describe('options', () => {
|
||||
it('getter', async () => {
|
||||
await openTerminal(page);
|
||||
assert.equal(await page.evaluate(`window.term.options.rendererType`), 'canvas');
|
||||
assert.equal(await page.evaluate(`window.term.options.cols`), 80);
|
||||
assert.equal(await page.evaluate(`window.term.options.rows`), 24);
|
||||
});
|
||||
it('setter', async () => {
|
||||
await openTerminal(page);
|
||||
try {
|
||||
await page.evaluate('window.term.options.cols = 40');
|
||||
fail();
|
||||
} catch {}
|
||||
try {
|
||||
await page.evaluate('window.term.options.rows = 20');
|
||||
fail();
|
||||
} catch {}
|
||||
await page.evaluate('window.term.options.scrollback = 1');
|
||||
assert.equal(await page.evaluate(`window.term.options.scrollback`), 1);
|
||||
await page.evaluate(`
|
||||
window.term.options = {
|
||||
fontSize: 30,
|
||||
fontFamily: 'Arial'
|
||||
};
|
||||
`);
|
||||
assert.equal(await page.evaluate(`window.term.options.fontSize`), 30);
|
||||
assert.equal(await page.evaluate(`window.term.options.fontFamily`), 'Arial');
|
||||
});
|
||||
});
|
||||
|
||||
describe('renderer', () => {
|
||||
it('foreground', async () => {
|
||||
await openTerminal(page, { rendererType: 'dom' });
|
||||
|
||||
Vendored
+20
-2
@@ -636,9 +636,27 @@ declare module 'xterm' {
|
||||
readonly modes: IModes;
|
||||
|
||||
/**
|
||||
* Get the terminal options
|
||||
* 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',
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
readonly options: ITerminalOptions;
|
||||
options: ITerminalOptions;
|
||||
|
||||
/**
|
||||
* Natural language strings that can be localized.
|
||||
|
||||
Reference in New Issue
Block a user