Remove common/Clone.ts and its tests. Use the native structuredClone API for cloning default modes, options, and test fixtures. Fixes #5911 Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
243 lines
9.5 KiB
TypeScript
243 lines
9.5 KiB
TypeScript
/**
|
|
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
|
* @license MIT
|
|
*/
|
|
|
|
import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IMouseStateService, ICharsetService, UnicodeCharProperties, UnicodeCharWidth, IUnicodeService, IUnicodeVersionProvider, LogLevelEnum, IDecorationService, IInternalDecoration, IOscLinkService, type IBufferResizeEvent } from 'common/services/Services';
|
|
import { UnicodeService } from 'common/services/UnicodeService';
|
|
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, IDisposable, IBufferLine, IExtendedAttrs } from 'common/Types';
|
|
import { UnicodeV6 } from 'common/input/UnicodeV6';
|
|
import { IDecorationOptions, IDecoration } from '@xterm/xterm';
|
|
import { Emitter, type IEvent } from 'common/Event';
|
|
import { CellData } from 'common/buffer/CellData';
|
|
import { DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH } from 'common/buffer/Constants';
|
|
|
|
export function createCellData(attr: number, char: string, width: number): CellData {
|
|
return CellData.fromCharData([attr, char, width, char.length === 0 ? 0 : char.charCodeAt(0)]);
|
|
}
|
|
|
|
export function extendedAttributes(line: IBufferLine, index: number): IExtendedAttrs | undefined {
|
|
const cell = new CellData();
|
|
line.loadCell(index, cell);
|
|
return cell.hasExtendedAttrs() !== 0 ? cell.extended : undefined;
|
|
}
|
|
|
|
export const NULL_CELL_DATA = Object.freeze(createCellData(DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH));
|
|
|
|
export class MockBufferService implements IBufferService {
|
|
public serviceBrand: any;
|
|
public get buffer(): IBuffer { return this.buffers.active; }
|
|
public buffers: IBufferSet = {} as any;
|
|
public onResize: IEvent<IBufferResizeEvent> = new Emitter<IBufferResizeEvent>().event;
|
|
public onScroll: IEvent<number> = new Emitter<number>().event;
|
|
private readonly _onScroll = new Emitter<number>();
|
|
public isUserScrolling: boolean = false;
|
|
constructor(
|
|
public cols: number,
|
|
public rows: number,
|
|
optionsService: IOptionsService = new MockOptionsService()
|
|
) {
|
|
this.buffers = new BufferSet(optionsService, this, new MockLogService());
|
|
// Listen to buffer activation events and automatically fire scroll events
|
|
this.buffers.onBufferActivate(e => {
|
|
this._onScroll.fire(e.activeBuffer.ydisp);
|
|
});
|
|
}
|
|
public scrollPages(pageCount: number): void {
|
|
throw new Error('Method not implemented.');
|
|
}
|
|
public scrollToTop(): void {
|
|
throw new Error('Method not implemented.');
|
|
}
|
|
public scrollToLine(line: number): void {
|
|
throw new Error('Method not implemented.');
|
|
}
|
|
public scroll(eraseAttr: IAttributeData, isWrapped: boolean): void {
|
|
throw new Error('Method not implemented.');
|
|
}
|
|
public scrollToBottom(): void {
|
|
throw new Error('Method not implemented.');
|
|
}
|
|
public scrollLines(disp: number, suppressScrollEvent?: boolean): void {
|
|
throw new Error('Method not implemented.');
|
|
}
|
|
public resize(cols: number, rows: number): void {
|
|
this.cols = cols;
|
|
this.rows = rows;
|
|
}
|
|
public reset(): void { }
|
|
}
|
|
|
|
export class MockMouseStateService implements IMouseStateService {
|
|
public serviceBrand: any;
|
|
public areMouseEventsActive: boolean = false;
|
|
public activeEncoding: string = '';
|
|
public activeProtocol: string = '';
|
|
public isDefaultEncoding: boolean = true;
|
|
public isPixelEncoding: boolean = false;
|
|
public addEncoding(name: string): void { }
|
|
public addProtocol(name: string): void { }
|
|
public reset(): void { }
|
|
public onProtocolChange: IEvent<CoreMouseEventType> = new Emitter<CoreMouseEventType>().event;
|
|
public restrictMouseEvent(event: ICoreMouseEvent): boolean { return true; }
|
|
public encodeMouseEvent(event: ICoreMouseEvent): string { return ''; }
|
|
public setCustomWheelEventHandler(customWheelEventHandler: ((event: WheelEvent) => boolean) | undefined): void { }
|
|
public allowCustomWheelEvent(ev: WheelEvent): boolean { return true; }
|
|
}
|
|
|
|
export class MockCharsetService implements ICharsetService {
|
|
public serviceBrand: any;
|
|
public charset: ICharset | undefined;
|
|
public glevel: number = 0;
|
|
public charsets: (ICharset | undefined)[] = [];
|
|
public reset(): void { }
|
|
public setgLevel(g: number): void {
|
|
this.glevel = g;
|
|
this.charset = this.charsets[g];
|
|
}
|
|
public setgCharset(g: number, charset: ICharset | undefined): void {
|
|
this.charsets[g] = charset;
|
|
if (this.glevel === g) {
|
|
this.charset = charset;
|
|
}
|
|
}
|
|
}
|
|
|
|
export class MockCoreService implements ICoreService {
|
|
public serviceBrand: any;
|
|
public isCursorInitialized: boolean = true;
|
|
public isCursorHidden: boolean = false;
|
|
public isFocused: boolean = false;
|
|
public modes: IModes = {
|
|
insertMode: false
|
|
};
|
|
public decPrivateModes: IDecPrivateModes = {
|
|
applicationCursorKeys: false,
|
|
applicationKeypad: false,
|
|
bracketedPasteMode: false,
|
|
colorSchemeUpdates: false,
|
|
cursorBlink: undefined,
|
|
cursorStyle: undefined,
|
|
origin: false,
|
|
reverseWraparound: false,
|
|
sendFocus: false,
|
|
synchronizedOutput: false,
|
|
win32InputMode: false,
|
|
wraparound: true
|
|
};
|
|
public kittyKeyboard = {
|
|
flags: 0,
|
|
mainFlags: 0,
|
|
altFlags: 0,
|
|
mainStack: [] as number[],
|
|
altStack: [] as number[]
|
|
};
|
|
public onData: IEvent<string> = new Emitter<string>().event;
|
|
public onUserInput: IEvent<void> = new Emitter<void>().event;
|
|
public onBinary: IEvent<string> = new Emitter<string>().event;
|
|
public onRequestScrollToBottom: IEvent<void> = new Emitter<void>().event;
|
|
public reset(): void { }
|
|
public triggerDataEvent(data: string, wasUserInput?: boolean): void { }
|
|
public triggerBinaryEvent(data: string): void { }
|
|
}
|
|
|
|
export class MockLogService implements ILogService {
|
|
public serviceBrand: any;
|
|
public logLevel = LogLevelEnum.DEBUG;
|
|
public trace(message: any, ...optionalParams: any[]): void { }
|
|
public debug(message: any, ...optionalParams: any[]): void { }
|
|
public info(message: any, ...optionalParams: any[]): void { }
|
|
public warn(message: any, ...optionalParams: any[]): void { }
|
|
public error(message: any, ...optionalParams: any[]): void { }
|
|
}
|
|
|
|
export class MockOptionsService implements IOptionsService {
|
|
public serviceBrand: any;
|
|
public readonly rawOptions: Required<ITerminalOptions> = structuredClone(DEFAULT_OPTIONS);
|
|
public options: Required<ITerminalOptions> = this.rawOptions;
|
|
public onOptionChange: IEvent<keyof ITerminalOptions> = new Emitter<keyof ITerminalOptions>().event;
|
|
constructor(testOptions?: Partial<ITerminalOptions>) {
|
|
if (testOptions) {
|
|
for (const key of Object.keys(testOptions)) {
|
|
this.rawOptions[key] = testOptions[key];
|
|
}
|
|
}
|
|
}
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
public onSpecificOptionChange<T extends keyof ITerminalOptions>(key: T, listener: (arg1: ITerminalOptions[T]) => any): IDisposable {
|
|
return this.onOptionChange(eventKey => {
|
|
if (eventKey === key) {
|
|
listener(this.rawOptions[key]);
|
|
}
|
|
});
|
|
}
|
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
public onMultipleOptionChange(keys: (keyof ITerminalOptions)[], listener: () => any): IDisposable {
|
|
return this.onOptionChange(eventKey => {
|
|
if (keys.indexOf(eventKey) !== -1) {
|
|
listener();
|
|
}
|
|
});
|
|
}
|
|
public setOptions(options: ITerminalOptions): void {
|
|
for (const key of Object.keys(options)) {
|
|
this.options[key] = options[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
export class MockOscLinkService implements IOscLinkService {
|
|
public serviceBrand: any;
|
|
public registerLink(linkData: IOscLinkData): number {
|
|
return 1;
|
|
}
|
|
public getLinkData(linkId: number): IOscLinkData | undefined {
|
|
return undefined;
|
|
}
|
|
public addLineToLink(linkId: number, y: number): void {
|
|
}
|
|
}
|
|
|
|
// defaults to V6 always to keep tests passing
|
|
export class MockUnicodeService implements IUnicodeService {
|
|
public serviceBrand: any;
|
|
private _provider = new UnicodeV6();
|
|
public register(provider: IUnicodeVersionProvider): void {
|
|
throw new Error('Method not implemented.');
|
|
}
|
|
public versions: string[] = [];
|
|
public activeVersion: string = '';
|
|
public onChange: IEvent<string> = new Emitter<string>().event;
|
|
public wcwidth = (codepoint: number): UnicodeCharWidth => this._provider.wcwidth(codepoint);
|
|
public charProperties(codepoint: number, preceding: UnicodeCharProperties): UnicodeCharProperties {
|
|
let width = this.wcwidth(codepoint);
|
|
let shouldJoin = width === 0 && preceding !== 0;
|
|
if (shouldJoin) {
|
|
const oldWidth = UnicodeService.extractWidth(preceding);
|
|
if (oldWidth === 0) {
|
|
shouldJoin = false;
|
|
} else if (oldWidth > width) {
|
|
width = oldWidth;
|
|
}
|
|
}
|
|
return UnicodeService.createPropertyValue(0, width, shouldJoin);
|
|
}
|
|
public getStringCellWidth(s: string): number {
|
|
throw new Error('Method not implemented.');
|
|
}
|
|
}
|
|
|
|
export class MockDecorationService implements IDecorationService {
|
|
public serviceBrand: any;
|
|
public get decorations(): IterableIterator<IInternalDecoration> { return [].values(); }
|
|
public onDecorationRegistered = new Emitter<IInternalDecoration>().event;
|
|
public onDecorationRemoved = new Emitter<IInternalDecoration>().event;
|
|
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined { return undefined; }
|
|
public reset(): void { }
|
|
public forEachDecorationAtCell(x: number, line: number, layer: 'bottom' | 'top' | undefined, callback: (decoration: IInternalDecoration) => void): void { }
|
|
public dispose(): void { }
|
|
}
|