From 353a8c5388e3242af4957da5b89fc50a6e8ab663 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 25 Apr 2020 10:21:31 -0700 Subject: [PATCH 01/24] Create CoreTerminal, fix createInstance in strict mode --- .vscode/settings.json | 3 +- .../src/WebglRenderer.api.ts | 2 +- src/Terminal.ts | 29 ++----- src/TestUtils.test.ts | 4 +- src/Types.d.ts | 10 +-- src/browser/TestUtils.test.ts | 6 +- src/browser/services/CharSizeService.ts | 2 +- src/browser/services/CoreBrowserService.ts | 2 +- src/browser/services/MouseService.ts | 2 +- src/browser/services/RenderService.ts | 2 +- src/browser/services/SelectionService.ts | 2 +- src/browser/services/Services.ts | 12 +-- src/browser/services/SoundService.ts | 2 +- src/browser/tsconfig.json | 5 +- src/common/CoreTerminal.ts | 56 ++++++++++++++ src/common/Types.d.ts | 9 +++ src/common/services/Services.ts | 75 ++++++------------- src/common/tsconfig.json | 5 +- src/tsconfig-base.json | 3 +- 19 files changed, 127 insertions(+), 104 deletions(-) create mode 100644 src/common/CoreTerminal.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index f07ca545..38f4dd11 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "typescript.preferences.importModuleSpecifier": "non-relative", - "typescript.preferences.quoteStyle": "single" + "typescript.preferences.quoteStyle": "single", + "eslint.enable": true } diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts index daf7f6fc..7165edb3 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.api.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.api.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { ITerminalOptions } from '../../../src/Types'; +import { ITerminalOptions } from '../../../src/common/Types'; import { ITheme } from 'xterm'; import { assert } from 'chai'; import { openTerminal, pollFor, writeSync, getBrowserType } from '../../../out-test/api/TestUtils'; diff --git a/src/Terminal.ts b/src/Terminal.ts index e3a3801d..4a0bd82c 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -21,7 +21,7 @@ * http://linux.die.net/man/7/urxvt */ -import { IInputHandlingTerminal, ICompositionHelper, ITerminalOptions, ITerminal, IBrowser, CustomKeyEventHandler } from './Types'; +import { IInputHandlingTerminal, ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler } from './Types'; import { IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types'; import { CompositionHelper } from 'browser/input/CompositionHelper'; import { Viewport } from 'browser/Viewport'; @@ -39,39 +39,36 @@ 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, IBufferLine, IAttributeData, CoreMouseEventType, CoreMouseButton, CoreMouseAction } from 'common/Types'; +import { IKeyboardEvent, KeyboardResultType, IBufferLine, IAttributeData, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions } from 'common/Types'; import { evaluateKeyboardEvent } from 'common/input/Keyboard'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { updateWindowsModeWrappedState } from 'common/WindowsMode'; import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; -import { IOptionsService, IBufferService, ICoreMouseService, ICoreService, ILogService, IDirtyRowService, IInstantiationService, ICharsetService, IUnicodeService } from 'common/services/Services'; -import { OptionsService } from 'common/services/OptionsService'; +import { ICoreMouseService, ICoreService, IDirtyRowService, ICharsetService, IUnicodeService } from 'common/services/Services'; import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService } from 'browser/services/Services'; import { CharSizeService } from 'browser/services/CharSizeService'; -import { BufferService, MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService'; -import { Disposable } from 'common/Lifecycle'; +import { MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService'; import { IBufferSet, IBuffer } from 'common/buffer/Types'; import { MouseService } from 'browser/services/MouseService'; import { IParams, IFunctionIdentifier } from 'common/parser/Types'; import { CoreService } from 'common/services/CoreService'; -import { LogService } from 'common/services/LogService'; import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types'; import { DirtyRowService } from 'common/services/DirtyRowService'; -import { InstantiationService } from 'common/services/InstantiationService'; import { CoreMouseService } from 'common/services/CoreMouseService'; import { WriteBuffer } from 'common/input/WriteBuffer'; import { Linkifier2 } from 'browser/Linkifier2'; import { CoreBrowserService } from 'browser/services/CoreBrowserService'; import { UnicodeService } from 'common/services/UnicodeService'; import { CharsetService } from 'common/services/CharsetService'; +import { CoreTerminal } from 'common/CoreTerminal'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; -export class Terminal extends Disposable implements ITerminal, IDisposable, IInputHandlingTerminal { +export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingTerminal { public textarea: HTMLTextAreaElement; public element: HTMLElement; public screenElement: HTMLElement; @@ -92,14 +89,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _customKeyEventHandler: CustomKeyEventHandler; // common services - private _bufferService: IBufferService; private _coreService: ICoreService; private _charsetService: ICharsetService; private _coreMouseService: ICoreMouseService; private _dirtyRowService: IDirtyRowService; - private _instantiationService: IInstantiationService; - private _logService: ILogService; - public optionsService: IOptionsService; public unicodeService: IUnicodeService; // browser services @@ -192,16 +185,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp constructor( options: ITerminalOptions = {} ) { - super(); + super(options); + // TODO: Move these to CoreTerminal.ts // Setup and initialize common services - this._instantiationService = new InstantiationService(); - this.optionsService = new OptionsService(options); - this._instantiationService.setService(IOptionsService, this.optionsService); - this._bufferService = this._instantiationService.createInstance(BufferService); - this._instantiationService.setService(IBufferService, this._bufferService); - this._logService = this._instantiationService.createInstance(LogService); - this._instantiationService.setService(ILogService, this._logService); this._coreService = this._instantiationService.createInstance(CoreService, () => this.scrollToBottom()); this._instantiationService.setService(ICoreService, this._coreService); this._coreService.onData(e => this._onData.fire(e)); diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index cac0a43d..f0c56f15 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -4,9 +4,9 @@ */ import { IRenderer, IRenderDimensions, CharacterJoinerHandler, IRequestRedrawEvent } from 'browser/renderer/Types'; -import { IInputHandlingTerminal, ICompositionHelper, ITerminal, IBrowser, ITerminalOptions } from './Types'; +import { IInputHandlingTerminal, ICompositionHelper, ITerminal, IBrowser } from './Types'; import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types'; -import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset } from 'common/Types'; +import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, ITerminalOptions } from 'common/Types'; import { Buffer } from 'common/buffer/Buffer'; import * as Browser from 'common/Platform'; import { IDisposable, IMarker, IEvent, ISelectionPosition, ILinkProvider } from 'xterm'; diff --git a/src/Types.d.ts b/src/Types.d.ts index cb0ba844..14494e89 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -4,7 +4,7 @@ */ import { ITerminalOptions as IPublicTerminalOptions, IDisposable, IMarker, ISelectionPosition, ILinkProvider } from 'xterm'; -import { ICharset, IAttributeData, CharData, CoreMouseEventType } from 'common/Types'; +import { ICharset, IAttributeData, CharData, CoreMouseEventType, ITerminalOptions } from 'common/Types'; import { IEvent, IEventEmitter } from 'common/EventEmitter'; import { IColorSet, ILinkifier, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types'; import { IOptionsService, IUnicodeService } from 'common/services/Services'; @@ -216,14 +216,6 @@ export interface ILinkifierAccessor { linkifier2: ILinkifier2; } -// TODO: The options that are not in the public API should be reviewed -export interface ITerminalOptions extends IPublicTerminalOptions { - [key: string]: any; - cancelEvents?: boolean; - convertEol?: boolean; - termName?: string; -} - export interface IBrowser { isNode: boolean; userAgent: string; diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index 96d1eae0..1869ab87 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -9,7 +9,7 @@ import { IRenderDimensions, IRenderer, CharacterJoinerHandler } from 'browser/re import { IColorSet } from 'browser/Types'; export class MockCharSizeService implements ICharSizeService { - public serviceBrand: any; + public serviceBrand: undefined; public get hasValidSize(): boolean { return this.width > 0 && this.height > 0; } public onCharSizeChange: IEvent = new EventEmitter().event; constructor(public width: number, public height: number) {} @@ -17,7 +17,7 @@ export class MockCharSizeService implements ICharSizeService { } export class MockMouseService implements IMouseService { - public serviceBrand: any; + public serviceBrand: undefined; public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined { throw new Error('Not implemented'); } @@ -28,7 +28,7 @@ export class MockMouseService implements IMouseService { } export class MockRenderService implements IRenderService { - public serviceBrand: any; + public serviceBrand: undefined; public onDimensionsChange: IEvent = new EventEmitter().event; public onRenderedBufferChange: IEvent<{ start: number, end: number }, void> = new EventEmitter<{ start: number, end: number }>().event; public onRefreshRequest: IEvent<{ start: number, end: number}, void> = new EventEmitter<{ start: number, end: number }>().event; diff --git a/src/browser/services/CharSizeService.ts b/src/browser/services/CharSizeService.ts index 7623109c..79696a28 100644 --- a/src/browser/services/CharSizeService.ts +++ b/src/browser/services/CharSizeService.ts @@ -8,7 +8,7 @@ import { IEvent, EventEmitter } from 'common/EventEmitter'; import { ICharSizeService } from 'browser/services/Services'; export class CharSizeService implements ICharSizeService { - public serviceBrand: any; + public serviceBrand: undefined; public width: number = 0; public height: number = 0; diff --git a/src/browser/services/CoreBrowserService.ts b/src/browser/services/CoreBrowserService.ts index e3207d7b..985253d9 100644 --- a/src/browser/services/CoreBrowserService.ts +++ b/src/browser/services/CoreBrowserService.ts @@ -6,7 +6,7 @@ import { ICoreBrowserService } from './Services'; export class CoreBrowserService implements ICoreBrowserService { - public serviceBrand: any; + public serviceBrand: undefined; constructor( private _textarea: HTMLTextAreaElement diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index 39df6383..348ba64e 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -7,7 +7,7 @@ import { ICharSizeService, IRenderService, IMouseService } from './Services'; import { getCoords, getRawByteCoords } from 'browser/input/Mouse'; export class MouseService implements IMouseService { - public serviceBrand: any; + public serviceBrand: undefined; constructor( @IRenderService private readonly _renderService: IRenderService, diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index d7ff5814..61ba35ab 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -14,7 +14,7 @@ import { IOptionsService } from 'common/services/Services'; import { ICharSizeService, IRenderService } from 'browser/services/Services'; export class RenderService extends Disposable implements IRenderService { - public serviceBrand: any; + public serviceBrand: undefined; private _renderDebouncer: RenderDebouncer; private _screenDprMonitor: ScreenDprMonitor; diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 43875f1b..d70cfa11 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -67,7 +67,7 @@ export const enum SelectionMode { * when the selection is ready to be redrawn (on an animation frame). */ export class SelectionService implements ISelectionService { - public serviceBrand: any; + public serviceBrand: undefined; protected _model: SelectionModel; diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts index 3754e6fe..28b83985 100644 --- a/src/browser/services/Services.ts +++ b/src/browser/services/Services.ts @@ -12,7 +12,7 @@ import { IDisposable } from 'common/Types'; export const ICharSizeService = createDecorator('CharSizeService'); export interface ICharSizeService { - serviceBrand: any; + serviceBrand: undefined; readonly width: number; readonly height: number; @@ -25,14 +25,14 @@ export interface ICharSizeService { export const ICoreBrowserService = createDecorator('CoreBrowserService'); export interface ICoreBrowserService { - serviceBrand: any; + serviceBrand: undefined; readonly isFocused: boolean; } export const IMouseService = createDecorator('MouseService'); export interface IMouseService { - serviceBrand: any; + serviceBrand: undefined; getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined; getRawByteCoords(event: MouseEvent, element: HTMLElement, colCount: number, rowCount: number): { x: number, y: number } | undefined; @@ -40,7 +40,7 @@ export interface IMouseService { export const IRenderService = createDecorator('RenderService'); export interface IRenderService extends IDisposable { - serviceBrand: any; + serviceBrand: undefined; onDimensionsChange: IEvent; /** @@ -72,7 +72,7 @@ export interface IRenderService extends IDisposable { export const ISelectionService = createDecorator('SelectionService'); export interface ISelectionService { - serviceBrand: any; + serviceBrand: undefined; readonly selectionText: string; readonly hasSelection: boolean; @@ -100,7 +100,7 @@ export interface ISelectionService { export const ISoundService = createDecorator('SoundService'); export interface ISoundService { - serviceBrand: any; + serviceBrand: undefined; playBellSound(): void; } diff --git a/src/browser/services/SoundService.ts b/src/browser/services/SoundService.ts index 4b54c999..8d940c13 100644 --- a/src/browser/services/SoundService.ts +++ b/src/browser/services/SoundService.ts @@ -7,7 +7,7 @@ import { IOptionsService } from 'common/services/Services'; import { ISoundService } from 'browser/services/Services'; export class SoundService implements ISoundService { - public serviceBrand: any; + public serviceBrand: undefined; private static _audioContext: AudioContext; diff --git a/src/browser/tsconfig.json b/src/browser/tsconfig.json index 7465bbd3..74b4c057 100644 --- a/src/browser/tsconfig.json +++ b/src/browser/tsconfig.json @@ -14,7 +14,10 @@ "common/*": [ "./common/*" ] } }, - "include": [ "./**/*" ], + "include": [ + "./**/*", + "../../typings/xterm.d.ts" + ], "references": [ { "path": "../common" } ] diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts new file mode 100644 index 00000000..6930ce07 --- /dev/null +++ b/src/common/CoreTerminal.ts @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2014-2020 The xterm.js authors. All rights reserved. + * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) + * @license MIT + * + * Originally forked from (with the author's permission): + * Fabrice Bellard's javascript vt100 for jslinux: + * http://bellard.org/jslinux/ + * Copyright (c) 2011 Fabrice Bellard + * The original design remains. The terminal itself + * has been extended to include xterm CSI codes, among + * other features. + * + * Terminal Emulation References: + * http://vt100.net/ + * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt + * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + * http://invisible-island.net/vttest/ + * http://www.inwap.com/pdp10/ansicode.txt + * http://linux.die.net/man/4/console_codes + * http://linux.die.net/man/7/urxvt + */ + +import { Disposable } from 'common/Lifecycle'; +import { IInstantiationService, IOptionsService, IBufferService, ILogService } from 'common/services/Services'; +import { InstantiationService } from 'common/services/InstantiationService'; +import { LogService } from 'common/services/LogService'; +import { BufferService } from 'common/services/BufferService'; +import { OptionsService } from 'common/services/OptionsService'; +import { ITerminalOptions } from './Types'; + +export class CoreTerminal extends Disposable { + protected readonly _instantiationService: IInstantiationService; + protected readonly _bufferService: IBufferService; + protected readonly _logService: ILogService; + + public readonly optionsService: IOptionsService; + + constructor( + options: ITerminalOptions + ) { + super(); + + // Setup and initialize services + this._instantiationService = new InstantiationService(); + this.optionsService = new OptionsService(options); + this._instantiationService.setService(IOptionsService, this.optionsService); + this._bufferService = this._instantiationService.createInstance(BufferService); + this._instantiationService.setService(IBufferService, this._bufferService); + this._logService = this._instantiationService.createInstance(LogService); + this._instantiationService.setService(ILogService, this._logService); + } + + public get cols(): number { return this._bufferService.cols; } + public get rows(): number { return this._bufferService.rows; } +} diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index b250b45e..159c0876 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -3,6 +3,7 @@ * @license MIT */ +import { ITerminalOptions as IPublicTerminalOptions } from 'xterm'; import { IEvent, IEventEmitter } from 'common/EventEmitter'; import { IDeleteEvent, IInsertEvent } from 'common/CircularList'; @@ -10,6 +11,14 @@ export interface IDisposable { dispose(): void; } +// TODO: The options that are not in the public API should be reviewed +export interface ITerminalOptions extends IPublicTerminalOptions { + [key: string]: any; + cancelEvents?: boolean; + convertEol?: boolean; + termName?: string; +} + export type XtermListener = (...args: any[]) => void; /** diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 8a3e9b28..97a9f78f 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -10,7 +10,7 @@ import { createDecorator } from 'common/services/ServiceRegistry'; export const IBufferService = createDecorator('BufferService'); export interface IBufferService { - serviceBrand: any; + serviceBrand: undefined; readonly cols: number; readonly rows: number; @@ -51,12 +51,12 @@ export interface ICoreMouseService { /** * Human readable version of mouse events. */ - explainEvents(events: CoreMouseEventType): {[event: string]: boolean}; + explainEvents(events: CoreMouseEventType): { [event: string]: boolean }; } export const ICoreService = createDecorator('CoreService'); export interface ICoreService { - serviceBrand: any; + serviceBrand: undefined; /** * Initially the cursor will not be visible until the first time the terminal @@ -92,7 +92,7 @@ export interface ICoreService { export const ICharsetService = createDecorator('CharsetService'); export interface ICharsetService { - serviceBrand: any; + serviceBrand: undefined; charset: ICharset | undefined; readonly glevel: number; @@ -116,7 +116,7 @@ export interface ICharsetService { export const IDirtyRowService = createDecorator('DirtyRowService'); export interface IDirtyRowService { - serviceBrand: any; + serviceBrand: undefined; readonly start: number; readonly end: number; @@ -132,61 +132,32 @@ export interface IServiceIdentifier { type: T; } -export interface IConstructorSignature0 { - new(...services: { serviceBrand: any }[]): T; -} +export interface IBrandedService { + serviceBrand: undefined; +}; -export interface IConstructorSignature1 { - new(first: A1, ...services: { serviceBrand: any }[]): T; -} - -export interface IConstructorSignature2 { - new(first: A1, second: A2, ...services: { serviceBrand: any }[]): T; -} - -export interface IConstructorSignature3 { - new(first: A1, second: A2, third: A3, ...services: { serviceBrand: any }[]): T; -} - -export interface IConstructorSignature4 { - new(first: A1, second: A2, third: A3, fourth: A4, ...services: { serviceBrand: any }[]): T; -} - -export interface IConstructorSignature5 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, ...services: { serviceBrand: any }[]): T; -} - -export interface IConstructorSignature6 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, ...services: { serviceBrand: any }[]): T; -} - -export interface IConstructorSignature7 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, ...services: { serviceBrand: any }[]): T; -} - -export interface IConstructorSignature8 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { serviceBrand: any }[]): T; -} +type GetLeadingNonServiceArgs = + Args extends [...IBrandedService[]] ? [] + : Args extends [infer A1, ...IBrandedService[]] ? [A1] + : Args extends [infer A1, infer A2, ...IBrandedService[]] ? [A1, A2] + : Args extends [infer A1, infer A2, infer A3, ...IBrandedService[]] ? [A1, A2, A3] + : Args extends [infer A1, infer A2, infer A3, infer A4, ...IBrandedService[]] ? [A1, A2, A3, A4] + : Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, ...IBrandedService[]] ? [A1, A2, A3, A4, A5] + : Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, ...IBrandedService[]] ? [A1, A2, A3, A4, A5, A6] + : Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, infer A7, ...IBrandedService[]] ? [A1, A2, A3, A4, A5, A6, A7] + : Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, infer A7, infer A8, ...IBrandedService[]] ? [A1, A2, A3, A4, A5, A6, A7, A8] + : never; export const IInstantiationService = createDecorator('InstantiationService'); export interface IInstantiationService { setService(id: IServiceIdentifier, instance: T): void; getService(id: IServiceIdentifier): T | undefined; - - createInstance(ctor: IConstructorSignature0): T; - createInstance(ctor: IConstructorSignature1, first: A1): T; - createInstance(ctor: IConstructorSignature2, first: A1, second: A2): T; - createInstance(ctor: IConstructorSignature3, first: A1, second: A2, third: A3): T; - createInstance(ctor: IConstructorSignature4, first: A1, second: A2, third: A3, fourth: A4): T; - createInstance(ctor: IConstructorSignature5, first: A1, second: A2, third: A3, fourth: A4, fifth: A5): T; - createInstance(ctor: IConstructorSignature6, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6): T; - createInstance(ctor: IConstructorSignature7, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7): T; - createInstance(ctor: IConstructorSignature8, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8): T; + createInstance any, R extends InstanceType>(t: Ctor, ...args: GetLeadingNonServiceArgs>): R; } export const ILogService = createDecorator('LogService'); export interface ILogService { - serviceBrand: any; + serviceBrand: undefined; debug(message: any, ...optionalParams: any[]): void; info(message: any, ...optionalParams: any[]): void; @@ -196,7 +167,7 @@ export interface ILogService { export const IOptionsService = createDecorator('OptionsService'); export interface IOptionsService { - serviceBrand: any; + serviceBrand: undefined; readonly options: ITerminalOptions; @@ -311,7 +282,7 @@ export interface ITheme { export const IUnicodeService = createDecorator('UnicodeService'); export interface IUnicodeService { - serviceBrand: any; + serviceBrand: undefined; /** Register an Unicode version provider. */ register(provider: IUnicodeVersionProvider): void; /** Registered Unicode versions. */ diff --git a/src/common/tsconfig.json b/src/common/tsconfig.json index 59050a0b..7f35c80c 100644 --- a/src/common/tsconfig.json +++ b/src/common/tsconfig.json @@ -10,5 +10,8 @@ ], "baseUrl": ".." }, - "include": [ "./**/*" ] + "include": [ + "./**/*", + "../../typings/xterm.d.ts" + ] } diff --git a/src/tsconfig-base.json b/src/tsconfig-base.json index 84d0c924..0cd951a7 100644 --- a/src/tsconfig-base.json +++ b/src/tsconfig-base.json @@ -8,6 +8,7 @@ "removeComments": true, "pretty": true, - "incremental": true + "incremental": true, + "experimentalDecorators": true } } From 0d3bd2052b89a348898dbebc8efd27ec1b03718f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 25 Apr 2020 10:26:29 -0700 Subject: [PATCH 02/24] Remove remaining common service inits to common --- src/Terminal.ts | 35 ----------------------------------- src/common/CoreTerminal.ts | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 4a0bd82c..19fd2a88 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -46,22 +46,16 @@ import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { updateWindowsModeWrappedState } from 'common/WindowsMode'; import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; -import { ICoreMouseService, ICoreService, IDirtyRowService, ICharsetService, IUnicodeService } from 'common/services/Services'; import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService } from 'browser/services/Services'; import { CharSizeService } from 'browser/services/CharSizeService'; import { MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService'; import { IBufferSet, IBuffer } from 'common/buffer/Types'; import { MouseService } from 'browser/services/MouseService'; import { IParams, IFunctionIdentifier } from 'common/parser/Types'; -import { CoreService } from 'common/services/CoreService'; import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types'; -import { DirtyRowService } from 'common/services/DirtyRowService'; -import { CoreMouseService } from 'common/services/CoreMouseService'; import { WriteBuffer } from 'common/input/WriteBuffer'; import { Linkifier2 } from 'browser/Linkifier2'; import { CoreBrowserService } from 'browser/services/CoreBrowserService'; -import { UnicodeService } from 'common/services/UnicodeService'; -import { CharsetService } from 'common/services/CharsetService'; import { CoreTerminal } from 'common/CoreTerminal'; // Let it work inside Node.js for automated testing purposes. @@ -88,13 +82,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT private _customKeyEventHandler: CustomKeyEventHandler; - // common services - private _coreService: ICoreService; - private _charsetService: ICharsetService; - private _coreMouseService: ICoreMouseService; - private _dirtyRowService: IDirtyRowService; - public unicodeService: IUnicodeService; - // browser services private _charSizeService: ICharSizeService; private _mouseService: IMouseService; @@ -137,15 +124,8 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT // bufferline to clone/copy from for new blank lines private _blankLine: IBufferLine = null; - public get cols(): number { return this._bufferService.cols; } - public get rows(): number { return this._bufferService.rows; } - private _onCursorMove = new EventEmitter(); public get onCursorMove(): IEvent { return this._onCursorMove.event; } - private _onData = new EventEmitter(); - public get onData(): IEvent { return this._onData.event; } - private _onBinary = new EventEmitter(); - public get onBinary(): IEvent { return this._onBinary.event; } private _onKey = new EventEmitter<{ key: string, domEvent: KeyboardEvent }>(); public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._onKey.event; } private _onLineFeed = new EventEmitter(); @@ -187,21 +167,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT ) { super(options); - // TODO: Move these to CoreTerminal.ts - // Setup and initialize common services - this._coreService = this._instantiationService.createInstance(CoreService, () => this.scrollToBottom()); - this._instantiationService.setService(ICoreService, this._coreService); - this._coreService.onData(e => this._onData.fire(e)); - this._coreService.onBinary(e => this._onBinary.fire(e)); - this._coreMouseService = this._instantiationService.createInstance(CoreMouseService); - this._instantiationService.setService(ICoreMouseService, this._coreMouseService); - this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService); - this._instantiationService.setService(IDirtyRowService, this._dirtyRowService); - this.unicodeService = this._instantiationService.createInstance(UnicodeService); - this._instantiationService.setService(IUnicodeService, this.unicodeService); - this._charsetService = this._instantiationService.createInstance(CharsetService); - this._instantiationService.setService(ICharsetService, this._charsetService); - this._setupOptionsListeners(); this._setup(); diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 6930ce07..dfffa1d4 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -22,20 +22,36 @@ */ import { Disposable } from 'common/Lifecycle'; -import { IInstantiationService, IOptionsService, IBufferService, ILogService } from 'common/services/Services'; +import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, ICoreMouseService, IUnicodeService, IDirtyRowService } from 'common/services/Services'; import { InstantiationService } from 'common/services/InstantiationService'; import { LogService } from 'common/services/LogService'; import { BufferService } from 'common/services/BufferService'; import { OptionsService } from 'common/services/OptionsService'; import { ITerminalOptions } from './Types'; +import { CoreService } from 'common/services/CoreService'; +import { EventEmitter, IEvent } from 'common/EventEmitter'; +import { CoreMouseService } from 'common/services/CoreMouseService'; +import { DirtyRowService } from 'common/services/DirtyRowService'; +import { UnicodeService } from 'common/services/UnicodeService'; +import { CharsetService } from 'common/services/CharsetService'; -export class CoreTerminal extends Disposable { +export abstract class CoreTerminal extends Disposable { protected readonly _instantiationService: IInstantiationService; protected readonly _bufferService: IBufferService; protected readonly _logService: ILogService; + protected readonly _coreService: ICoreService; + protected readonly _charsetService: ICharsetService; + protected readonly _coreMouseService: ICoreMouseService; + protected readonly _dirtyRowService: IDirtyRowService; + public readonly unicodeService: IUnicodeService; public readonly optionsService: IOptionsService; + private _onData = new EventEmitter(); + public get onData(): IEvent { return this._onData.event; } + private _onBinary = new EventEmitter(); + public get onBinary(): IEvent { return this._onBinary.event; } + constructor( options: ITerminalOptions ) { @@ -49,8 +65,22 @@ export class CoreTerminal extends Disposable { this._instantiationService.setService(IBufferService, this._bufferService); this._logService = this._instantiationService.createInstance(LogService); this._instantiationService.setService(ILogService, this._logService); + this._coreService = this._instantiationService.createInstance(CoreService, () => this.scrollToBottom()); + this._instantiationService.setService(ICoreService, this._coreService); + this._coreService.onData(e => this._onData.fire(e)); + this._coreService.onBinary(e => this._onBinary.fire(e)); + this._coreMouseService = this._instantiationService.createInstance(CoreMouseService); + this._instantiationService.setService(ICoreMouseService, this._coreMouseService); + this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService); + this._instantiationService.setService(IDirtyRowService, this._dirtyRowService); + this.unicodeService = this._instantiationService.createInstance(UnicodeService); + this._instantiationService.setService(IUnicodeService, this.unicodeService); + this._charsetService = this._instantiationService.createInstance(CharsetService); + this._instantiationService.setService(ICharsetService, this._charsetService); } public get cols(): number { return this._bufferService.cols; } public get rows(): number { return this._bufferService.rows; } + + public abstract scrollToBottom(): void; } From b80c14639e8b7ffd473f058302f9262be4c3194d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 25 Apr 2020 10:34:37 -0700 Subject: [PATCH 03/24] Move option listeners to core --- src/Terminal.ts | 138 ++++++++++++++----------------------- src/common/CoreTerminal.ts | 59 ++++++++++++++-- 2 files changed, 104 insertions(+), 93 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 19fd2a88..6192beec 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -43,13 +43,12 @@ import { IKeyboardEvent, KeyboardResultType, IBufferLine, IAttributeData, CoreMo import { evaluateKeyboardEvent } from 'common/input/Keyboard'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; -import { updateWindowsModeWrappedState } from 'common/WindowsMode'; import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService } from 'browser/services/Services'; import { CharSizeService } from 'browser/services/CharSizeService'; import { MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService'; -import { IBufferSet, IBuffer } from 'common/buffer/Types'; +import { IBuffer } from 'common/buffer/Types'; import { MouseService } from 'browser/services/MouseService'; import { IParams, IFunctionIdentifier } from 'common/parser/Types'; import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types'; @@ -119,7 +118,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT private _accessibilityManager: AccessibilityManager; private _colorManager: ColorManager; private _theme: ITheme; - private _windowsMode: IDisposable | undefined; // bufferline to clone/copy from for new blank lines private _blankLine: IBufferLine = null; @@ -128,8 +126,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT public get onCursorMove(): IEvent { return this._onCursorMove.event; } private _onKey = new EventEmitter<{ key: string, domEvent: KeyboardEvent }>(); public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._onKey.event; } - private _onLineFeed = new EventEmitter(); - public get onLineFeed(): IEvent { return this._onLineFeed.event; } private _onRender = new EventEmitter<{ start: number, end: number }>(); public get onRender(): IEvent<{ start: number, end: number }> { return this._onRender.event; } private _onResize = new EventEmitter<{ cols: number, rows: number }>(); @@ -167,7 +163,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT ) { super(options); - this._setupOptionsListeners(); this._setup(); this._writeBuffer = new WriteBuffer(data => this._inputHandler.parse(data)); @@ -220,22 +215,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT } } - private _enableWindowsMode(): void { - if (!this._windowsMode) { - const disposables: IDisposable[] = []; - disposables.push(this.onLineFeed(updateWindowsModeWrappedState.bind(null, this._bufferService))); - disposables.push(this.addCsiHandler({ final: 'H' }, () => { - updateWindowsModeWrappedState(this._bufferService); - return false; - })); - this._windowsMode = { - dispose: () => { - disposables.forEach(d => d.dispose()); - } - }; - } - } - /** * Convenience property to active buffer. */ @@ -243,10 +222,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT return this.buffers.active; } - public get buffers(): IBufferSet { - return this._bufferService.buffers; - } - /** * Focus the terminal. Delegates focus handling to the terminal's DOM element. */ @@ -256,68 +231,59 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT } } - private _setupOptionsListeners(): void { + protected _updateOptions(key: string): void { + super._updateOptions(key); + // TODO: These listeners should be owned by individual components - this.optionsService.onOptionChange(key => { - switch (key) { - case 'fontFamily': - case 'fontSize': - // When the font changes the size of the cells may change which requires a renderer clear - this._renderService?.clear(); - this._charSizeService?.measure(); - break; - case 'cursorBlink': - case 'cursorStyle': - // The DOM renderer needs a row refresh to update the cursor styles - this.refresh(this.buffer.y, this.buffer.y); - break; - case 'drawBoldTextInBrightColors': - case 'letterSpacing': - case 'lineHeight': - case 'fontWeight': - case 'fontWeightBold': - case 'minimumContrastRatio': - // When the font changes the size of the cells may change which requires a renderer clear - if (this._renderService) { - this._renderService.clear(); - this._renderService.onResize(this.cols, this.rows); - this.refresh(0, this.rows - 1); + switch (key) { + case 'fontFamily': + case 'fontSize': + // When the font changes the size of the cells may change which requires a renderer clear + this._renderService?.clear(); + this._charSizeService?.measure(); + break; + case 'cursorBlink': + case 'cursorStyle': + // The DOM renderer needs a row refresh to update the cursor styles + this.refresh(this.buffer.y, this.buffer.y); + break; + case 'drawBoldTextInBrightColors': + case 'letterSpacing': + case 'lineHeight': + case 'fontWeight': + case 'fontWeightBold': + case 'minimumContrastRatio': + // When the font changes the size of the cells may change which requires a renderer clear + if (this._renderService) { + this._renderService.clear(); + this._renderService.onResize(this.cols, this.rows); + this.refresh(0, this.rows - 1); + } + break; + case 'rendererType': + if (this._renderService) { + this._renderService.setRenderer(this._createRenderer()); + this._renderService.onResize(this.cols, this.rows); + } + break; + case 'scrollback': + this.viewport?.syncScrollArea(); + break; + case 'screenReaderMode': + if (this.optionsService.options.screenReaderMode) { + if (!this._accessibilityManager && this._renderService) { + this._accessibilityManager = new AccessibilityManager(this, this._renderService); } - break; - case 'rendererType': - if (this._renderService) { - this._renderService.setRenderer(this._createRenderer()); - this._renderService.onResize(this.cols, this.rows); - } - break; - case 'scrollback': - this.buffers.resize(this.cols, this.rows); - this.viewport?.syncScrollArea(); - break; - case 'screenReaderMode': - if (this.optionsService.options.screenReaderMode) { - if (!this._accessibilityManager && this._renderService) { - this._accessibilityManager = new AccessibilityManager(this, this._renderService); - } - } else { - this._accessibilityManager?.dispose(); - this._accessibilityManager = null; - } - break; - case 'tabStopWidth': this.buffers.setupTabStops(); break; - case 'theme': - this._setTheme(this.optionsService.options.theme); - break; - case 'windowsMode': - if (this.optionsService.options.windowsMode) { - this._enableWindowsMode(); - } else { - this._windowsMode?.dispose(); - this._windowsMode = undefined; - } - break; - } - }); + } else { + this._accessibilityManager?.dispose(); + this._accessibilityManager = null; + } + break; + case 'tabStopWidth': this.buffers.setupTabStops(); break; + case 'theme': + this._setTheme(this.optionsService.options.theme); + break; + } } /** diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index dfffa1d4..7a8f5bff 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -27,13 +27,16 @@ import { InstantiationService } from 'common/services/InstantiationService'; import { LogService } from 'common/services/LogService'; import { BufferService } from 'common/services/BufferService'; import { OptionsService } from 'common/services/OptionsService'; -import { ITerminalOptions } from './Types'; +import { ITerminalOptions, IDisposable } from './Types'; import { CoreService } from 'common/services/CoreService'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { CoreMouseService } from 'common/services/CoreMouseService'; import { DirtyRowService } from 'common/services/DirtyRowService'; import { UnicodeService } from 'common/services/UnicodeService'; import { CharsetService } from 'common/services/CharsetService'; +import { updateWindowsModeWrappedState } from 'common/WindowsMode'; +import { IFunctionIdentifier, IParams } from 'common/parser/Types'; +import { IBufferSet } from 'common/buffer/Types'; export abstract class CoreTerminal extends Disposable { protected readonly _instantiationService: IInstantiationService; @@ -47,10 +50,18 @@ export abstract class CoreTerminal extends Disposable { public readonly unicodeService: IUnicodeService; public readonly optionsService: IOptionsService; - private _onData = new EventEmitter(); - public get onData(): IEvent { return this._onData.event; } + protected _windowsMode: IDisposable | undefined; + private _onBinary = new EventEmitter(); public get onBinary(): IEvent { return this._onBinary.event; } + private _onData = new EventEmitter(); + public get onData(): IEvent { return this._onData.event; } + protected _onLineFeed = new EventEmitter(); + public get onLineFeed(): IEvent { return this._onLineFeed.event; } + + public get cols(): number { return this._bufferService.cols; } + public get rows(): number { return this._bufferService.rows; } + public get buffers(): IBufferSet { return this._bufferService.buffers; } constructor( options: ITerminalOptions @@ -67,8 +78,6 @@ export abstract class CoreTerminal extends Disposable { this._instantiationService.setService(ILogService, this._logService); this._coreService = this._instantiationService.createInstance(CoreService, () => this.scrollToBottom()); this._instantiationService.setService(ICoreService, this._coreService); - this._coreService.onData(e => this._onData.fire(e)); - this._coreService.onBinary(e => this._onBinary.fire(e)); this._coreMouseService = this._instantiationService.createInstance(CoreMouseService); this._instantiationService.setService(ICoreMouseService, this._coreMouseService); this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService); @@ -77,10 +86,46 @@ export abstract class CoreTerminal extends Disposable { this._instantiationService.setService(IUnicodeService, this.unicodeService); this._charsetService = this._instantiationService.createInstance(CharsetService); this._instantiationService.setService(ICharsetService, this._charsetService); + + // Setup listeners + this._coreService.onData(e => this._onData.fire(e)); + this._coreService.onBinary(e => this._onBinary.fire(e)); + this.optionsService.onOptionChange(key => this._updateOptions(key)); } - public get cols(): number { return this._bufferService.cols; } - public get rows(): number { return this._bufferService.rows; } + protected _updateOptions(key: string): void { + // TODO: These listeners should be owned by individual components + switch (key) { + case 'scrollback': + this.buffers.resize(this.cols, this.rows); + break; + case 'windowsMode': + if (this.optionsService.options.windowsMode) { + this._enableWindowsMode(); + } else { + this._windowsMode?.dispose(); + this._windowsMode = undefined; + } + break; + } + } + + protected _enableWindowsMode(): void { + if (!this._windowsMode) { + const disposables: IDisposable[] = []; + disposables.push(this.onLineFeed(updateWindowsModeWrappedState.bind(null, this._bufferService))); + disposables.push(this.addCsiHandler({ final: 'H' }, () => { + updateWindowsModeWrappedState(this._bufferService); + return false; + })); + this._windowsMode = { + dispose: () => { + disposables.forEach(d => d.dispose()); + } + }; + } + } public abstract scrollToBottom(): void; + public abstract addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable; } From 4cec11be9c28fc593780d8e0c7ee2188526d41b9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 25 Apr 2020 11:11:05 -0700 Subject: [PATCH 04/24] Move bracketed paste mode to coreservice --- src/InputHandler.ts | 4 ++-- src/Terminal.ts | 16 +++++----------- src/Types.d.ts | 2 -- src/browser/Clipboard.ts | 8 ++++---- src/common/CoreTerminal.ts | 17 ++++++++++++++++- src/common/TestUtils.test.ts | 1 + src/common/Types.d.ts | 1 + src/common/services/CoreService.ts | 1 + 8 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 3717256e..371391da 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -1802,7 +1802,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.showCursor(); break; case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste) - this._terminal.bracketedPasteMode = true; + this._coreService.decPrivateModes.bracketedPasteMode = true; break; } } @@ -2020,7 +2020,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._terminal.showCursor(); break; case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste) - this._terminal.bracketedPasteMode = false; + this._coreService.decPrivateModes.bracketedPasteMode = false; break; } } diff --git a/src/Terminal.ts b/src/Terminal.ts index 6192beec..e97f3e58 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -90,7 +90,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT // modes public insertMode: boolean; - public bracketedPasteMode: boolean; // mouse properties public mouseEvents: CoreMouseEventType = CoreMouseEventType.NONE; @@ -173,20 +172,19 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT return; } super.dispose(); - this._windowsMode?.dispose(); - this._windowsMode = undefined; this._renderService?.dispose(); this._customKeyEventHandler = null; this.write = () => { }; this.element?.parentNode?.removeChild(this.element); } - private _setup(): void { + protected _setup(): void { + super._setup(); + this._customKeyEventHandler = null; // modes this.insertMode = false; - this.bracketedPasteMode = false; this._userScrolling = false; @@ -209,10 +207,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT if (!this.linkifier2) { this.linkifier2 = this._instantiationService.createInstance(Linkifier2); } - - if (this.options.windowsMode) { - this._enableWindowsMode(); - } } /** @@ -337,7 +331,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT } copyHandler(event, this._selectionService); })); - const pasteHandlerWrapper = (event: ClipboardEvent): void => handlePasteEvent(event, this.textarea, this.bracketedPasteMode, this._coreService); + const pasteHandlerWrapper = (event: ClipboardEvent): void => handlePasteEvent(event, this.textarea, this._coreService); this.register(addDisposableDomListener(this.textarea, 'paste', pasteHandlerWrapper)); this.register(addDisposableDomListener(this.element, 'paste', pasteHandlerWrapper)); @@ -982,7 +976,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT } public paste(data: string): void { - paste(data, this.textarea, this.bracketedPasteMode, this._coreService); + paste(data, this.textarea, this._coreService); } /** diff --git a/src/Types.d.ts b/src/Types.d.ts index 14494e89..4aaf9fa8 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -22,7 +22,6 @@ export type LineData = CharData[]; */ export interface IInputHandlingTerminal { insertMode: boolean; - bracketedPasteMode: boolean; sendFocus: boolean; buffers: IBufferSet; @@ -135,7 +134,6 @@ export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAcc buffer: IBuffer; buffers: IBufferSet; viewport: IViewport; - bracketedPasteMode: boolean; optionsService: IOptionsService; // TODO: We should remove options once components adopt optionsService options: ITerminalOptions; diff --git a/src/browser/Clipboard.ts b/src/browser/Clipboard.ts index a7c48bfb..5fe1e8fc 100644 --- a/src/browser/Clipboard.ts +++ b/src/browser/Clipboard.ts @@ -42,17 +42,17 @@ export function copyHandler(ev: ClipboardEvent, selectionService: ISelectionServ * @param ev The original paste event to be handled * @param term The terminal on which to apply the handled paste event */ -export function handlePasteEvent(ev: ClipboardEvent, textarea: HTMLTextAreaElement, bracketedPasteMode: boolean, coreService: ICoreService): void { +export function handlePasteEvent(ev: ClipboardEvent, textarea: HTMLTextAreaElement, coreService: ICoreService): void { ev.stopPropagation(); if (ev.clipboardData) { const text = ev.clipboardData.getData('text/plain'); - paste(text, textarea, bracketedPasteMode, coreService); + paste(text, textarea, coreService); } } -export function paste(text: string, textarea: HTMLTextAreaElement, bracketedPasteMode: boolean, coreService: ICoreService): void { +export function paste(text: string, textarea: HTMLTextAreaElement, coreService: ICoreService): void { text = prepareTextForTerminal(text); - text = bracketTextForPaste(text, bracketedPasteMode); + text = bracketTextForPaste(text, coreService.decPrivateModes.bracketedPasteMode); coreService.triggerDataEvent(text, true); textarea.value = ''; } diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 7a8f5bff..5428a3bc 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -50,7 +50,7 @@ export abstract class CoreTerminal extends Disposable { public readonly unicodeService: IUnicodeService; public readonly optionsService: IOptionsService; - protected _windowsMode: IDisposable | undefined; + private _windowsMode: IDisposable | undefined; private _onBinary = new EventEmitter(); public get onBinary(): IEvent { return this._onBinary.event; } @@ -93,6 +93,21 @@ export abstract class CoreTerminal extends Disposable { this.optionsService.onOptionChange(key => this._updateOptions(key)); } + public dispose(): void { + if (this._isDisposed) { + return; + } + super.dispose(); + this._windowsMode?.dispose(); + this._windowsMode = undefined; + } + + protected _setup(): void { + if (this.optionsService.options.windowsMode) { + this._enableWindowsMode(); + } + } + protected _updateOptions(key: string): void { // TODO: These listeners should be owned by individual components switch (key) { diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index cfa1abd7..6d47f05f 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -61,6 +61,7 @@ export class MockCoreService implements ICoreService { public decPrivateModes: IDecPrivateModes = { applicationCursorKeys: false, applicationKeypad: false, + bracketedPasteMode: false, origin: false, wraparound: true }; diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index 159c0876..c94093db 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -163,6 +163,7 @@ export interface IMarker extends IDisposable { export interface IDecPrivateModes { applicationCursorKeys: boolean; applicationKeypad: boolean; + bracketedPasteMode: boolean; origin: boolean; wraparound: boolean; // defaults: xterm - true, vt100 - false } diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index 2f0ff72f..56a0c04f 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -11,6 +11,7 @@ import { clone } from 'common/Clone'; const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({ applicationCursorKeys: false, applicationKeypad: false, + bracketedPasteMode: false, origin: false, wraparound: true // defaults: xterm - true, vt100 - false }); From d5729f9216af511d11b183fb2a7eaa58bf2f616b Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 25 Apr 2020 11:26:20 -0700 Subject: [PATCH 05/24] Move insertMode into core service --- src/InputHandler.test.ts | 14 +++++++------- src/InputHandler.ts | 10 +++------- src/Terminal.test.ts | 6 +++--- src/Terminal.ts | 17 +++++------------ src/TestUtils.test.ts | 2 -- src/Types.d.ts | 1 - src/common/TestUtils.test.ts | 5 ++++- src/common/Types.d.ts | 3 +++ src/common/services/CoreService.ts | 9 ++++++++- src/common/services/Services.ts | 3 ++- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 84d4dae5..6c5b9cbb 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -102,16 +102,16 @@ describe('InputHandler', () => { }); }); describe('setMode', () => { - it('should toggle Terminal.bracketedPasteMode', () => { + it('should toggle bracketedPasteMode', () => { const terminal = new MockInputHandlingTerminal(); - terminal.bracketedPasteMode = false; - const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const coreService = new MockCoreService(); + const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCharsetService(), coreService, new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); // Set bracketed paste mode inputHandler.setModePrivate(Params.fromArray([2004])); - assert.equal(terminal.bracketedPasteMode, true); + assert.equal(coreService.decPrivateModes.bracketedPasteMode, true); // Reset bracketed paste mode inputHandler.resetModePrivate(Params.fromArray([2004])); - assert.equal(terminal.bracketedPasteMode, false); + assert.equal(coreService.decPrivateModes.bracketedPasteMode, false); }); }); describe('regression tests', function(): void { @@ -1453,9 +1453,9 @@ describe('InputHandler', () => { }); it('should reset IRM', () => { term.writeSync('\x1b[4h'); - assert.equal(term.insertMode, true); + assert.equal((term as any)._coreService.modes.insertMode, true); term.writeSync('\x1b[!p'); - assert.equal(term.insertMode, false); + assert.equal((term as any)._coreService.modes.insertMode, false); }); it('should reset cursor visibility', () => { term.writeSync('\x1b[?25l'); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 371391da..c5c16501 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -489,7 +489,7 @@ export class InputHandler extends Disposable implements IInputHandler { const screenReaderMode = this._optionsService.options.screenReaderMode; const cols = this._bufferService.cols; const wraparoundMode = this._coreService.decPrivateModes.wraparound; - const insertMode = this._terminal.insertMode; + const insertMode = this._coreService.modes.insertMode; const curAttr = this._curAttrData; let bufferRow = buffer.lines.get(buffer.y + buffer.ybase); @@ -1587,15 +1587,12 @@ export class InputHandler extends Disposable implements IInputHandler { * | 4 | Insert Mode (IRM). | #Y | * | 12 | Send/receive (SRM). Always off. | #N | * | 20 | Automatic Newline (LNM). Always off. | #N | - * - * - * FIXME: why is LNM commented out? */ public setMode(params: IParams): void { for (let i = 0; i < params.length; i++) { switch (params.params[i]) { case 4: - this._terminal.insertMode = true; + this._coreService.modes.insertMode = true; break; case 20: // this._t.convertEol = true; @@ -1833,7 +1830,7 @@ export class InputHandler extends Disposable implements IInputHandler { for (let i = 0; i < params.length; i++) { switch (params.params[i]) { case 4: - this._terminal.insertMode = false; + this._coreService.modes.insertMode = false; break; case 20: // this._t.convertEol = false; @@ -2356,7 +2353,6 @@ export class InputHandler extends Disposable implements IInputHandler { */ public softReset(params: IParams): void { this._coreService.isCursorHidden = false; - this._terminal.insertMode = false; this._terminal.viewport?.syncScrollArea(); this._bufferService.buffer.scrollTop = 0; this._bufferService.buffer.scrollBottom = this._bufferService.rows - 1; diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 4d590699..d70ccb45 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -991,7 +991,7 @@ describe('Terminal', () => { term.writeSync(Array(9).join('0123456789').slice(-80)); term.buffer.x = 10; term.buffer.y = 0; - term.insertMode = true; + term.write('\x1b[4h'); term.writeSync('abcde'); expect(term.buffer.lines.get(0).length).eql(term.cols); expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('a'); @@ -1003,7 +1003,7 @@ describe('Terminal', () => { term.writeSync(Array(9).join('0123456789').slice(-80)); term.buffer.x = 10; term.buffer.y = 0; - term.insertMode = true; + term.write('\x1b[4h'); term.writeSync('¥¥¥'); expect(term.buffer.lines.get(0).length).eql(term.cols); expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('¥'); @@ -1016,7 +1016,7 @@ describe('Terminal', () => { term.writeSync(Array(41).join('¥')); term.buffer.x = 10; term.buffer.y = 0; - term.insertMode = true; + term.write('\x1b[4h'); term.writeSync('a'); expect(term.buffer.lines.get(0).length).eql(term.cols); expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('a'); diff --git a/src/Terminal.ts b/src/Terminal.ts index e97f3e58..5c2bbd7d 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -88,9 +88,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT private _selectionService: ISelectionService; private _soundService: ISoundService; - // modes - public insertMode: boolean; - // mouse properties public mouseEvents: CoreMouseEventType = CoreMouseEventType.NONE; public sendFocus: boolean; @@ -179,15 +176,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT } protected _setup(): void { - super._setup(); - - this._customKeyEventHandler = null; - - // modes - this.insertMode = false; - - this._userScrolling = false; - if (this._inputHandler) { this._inputHandler.reset(); } else { @@ -201,6 +189,11 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT this.register(this._inputHandler); } + super._setup(); + + this._customKeyEventHandler = null; + + this._userScrolling = false; if (!this.linkifier) { this.linkifier = this._instantiationService.createInstance(Linkifier); } diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index f0c56f15..ea0a5884 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -203,8 +203,6 @@ export class MockTerminal implements ITerminal { export class MockInputHandlingTerminal implements IInputHandlingTerminal { public onA11yCharEmitter: EventEmitter; public onA11yTabEmitter: EventEmitter; - public insertMode: boolean; - public bracketedPasteMode: boolean; public sendFocus: boolean; public buffers: IBufferSet; public buffer: IBuffer = new MockBuffer(); diff --git a/src/Types.d.ts b/src/Types.d.ts index 4aaf9fa8..a2000551 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -21,7 +21,6 @@ export type LineData = CharData[]; * InputHandler cleanly from the ITerminal interface. */ export interface IInputHandlingTerminal { - insertMode: boolean; sendFocus: boolean; buffers: IBufferSet; diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 6d47f05f..389c5382 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 } from 'common/Types'; +import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType, ICharset, IModes } from 'common/Types'; import { UnicodeV6 } from 'common/input/UnicodeV6'; export class MockBufferService implements IBufferService { @@ -58,6 +58,9 @@ export class MockCoreService implements ICoreService { public isCursorInitialized: boolean = false; public isCursorHidden: boolean = false; public isFocused: boolean = false; + public modes: IModes = { + insertMode: false + }; public decPrivateModes: IDecPrivateModes = { applicationCursorKeys: false, applicationKeypad: false, diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index c94093db..1d616f54 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -159,6 +159,9 @@ export interface IMarker extends IDisposable { readonly isDisposed: boolean; readonly line: number; } +export interface IModes { + insertMode: boolean; +} export interface IDecPrivateModes { applicationCursorKeys: boolean; diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index 56a0c04f..285532b5 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -5,9 +5,13 @@ import { ICoreService, ILogService, IOptionsService, IBufferService } from 'common/services/Services'; import { EventEmitter, IEvent } from 'common/EventEmitter'; -import { IDecPrivateModes } from 'common/Types'; +import { IDecPrivateModes, IModes } from 'common/Types'; import { clone } from 'common/Clone'; +const DEFAULT_MODES: IModes = Object.freeze({ + insertMode: false +}); + const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({ applicationCursorKeys: false, applicationKeypad: false, @@ -21,6 +25,7 @@ export class CoreService implements ICoreService { public isCursorInitialized: boolean = false; public isCursorHidden: boolean = false; + public modes: IModes; public decPrivateModes: IDecPrivateModes; private _onData = new EventEmitter(); @@ -37,10 +42,12 @@ export class CoreService implements ICoreService { @ILogService private readonly _logService: ILogService, @IOptionsService private readonly _optionsService: IOptionsService ) { + this.modes = clone(DEFAULT_MODES); this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES); } public reset(): void { + this.modes = clone(DEFAULT_MODES); this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES); } diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 97a9f78f..62ab45fd 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -5,7 +5,7 @@ import { IEvent } from 'common/EventEmitter'; import { IBuffer, IBufferSet } from 'common/buffer/Types'; -import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions } from 'common/Types'; +import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions, IModes } from 'common/Types'; import { createDecorator } from 'common/services/ServiceRegistry'; export const IBufferService = createDecorator('BufferService'); @@ -65,6 +65,7 @@ export interface ICoreService { isCursorInitialized: boolean; isCursorHidden: boolean; + readonly modes: IModes; readonly decPrivateModes: IDecPrivateModes; readonly onData: IEvent; From 37996bcd15be021500db0e4ad6f32233f0a8d6e8 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 25 Apr 2020 13:24:11 -0700 Subject: [PATCH 06/24] Move sendFocus to core service --- src/InputHandler.ts | 4 ++-- src/Terminal.ts | 5 ++--- src/TestUtils.test.ts | 1 - src/Types.d.ts | 2 -- src/common/TestUtils.test.ts | 1 + src/common/Types.d.ts | 1 + src/common/services/CoreService.ts | 1 + 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index c5c16501..7732d4cf 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -1771,7 +1771,7 @@ export class InputHandler extends Disposable implements IInputHandler { case 1004: // send focusin/focusout events // focusin: ^[[I // focusout: ^[[O - this._terminal.sendFocus = true; + this._coreService.decPrivateModes.sendFocus = true; break; case 1005: // utf8 ext mode mouse - removed in #2507 this._logService.debug('DECSET 1005 not supported (see #2507)'); @@ -1986,7 +1986,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._coreMouseService.activeProtocol = 'NONE'; break; case 1004: // send focusin/focusout events - this._terminal.sendFocus = false; + this._coreService.decPrivateModes.sendFocus = false; break; case 1005: // utf8 ext mode mouse - removed in #2507 this._logService.debug('DECRST 1005 not supported (see #2507)'); diff --git a/src/Terminal.ts b/src/Terminal.ts index 5c2bbd7d..bb02671f 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -90,7 +90,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT // mouse properties public mouseEvents: CoreMouseEventType = CoreMouseEventType.NONE; - public sendFocus: boolean; // write buffer private _writeBuffer: WriteBuffer; @@ -277,7 +276,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT * Binds the desired focus behavior on a given terminal object. */ private _onTextAreaFocus(ev: KeyboardEvent): void { - if (this.sendFocus) { + if (this._coreService.decPrivateModes.sendFocus) { this._coreService.triggerDataEvent(C0.ESC + '[I'); } this.updateCursorStyle(ev); @@ -302,7 +301,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT // screen readers reading it out. this.textarea.value = ''; this.refresh(this.buffer.y, this.buffer.y); - if (this.sendFocus) { + if (this._coreService.decPrivateModes.sendFocus) { this._coreService.triggerDataEvent(C0.ESC + '[O'); } this.element.classList.remove('focus'); diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index ea0a5884..c15ce560 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -203,7 +203,6 @@ export class MockTerminal implements ITerminal { export class MockInputHandlingTerminal implements IInputHandlingTerminal { public onA11yCharEmitter: EventEmitter; public onA11yTabEmitter: EventEmitter; - public sendFocus: boolean; public buffers: IBufferSet; public buffer: IBuffer = new MockBuffer(); public viewport: IViewport; diff --git a/src/Types.d.ts b/src/Types.d.ts index a2000551..15eda648 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -21,8 +21,6 @@ export type LineData = CharData[]; * InputHandler cleanly from the ITerminal interface. */ export interface IInputHandlingTerminal { - sendFocus: boolean; - buffers: IBufferSet; buffer: IBuffer; viewport: IViewport; diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 389c5382..55df896e 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -66,6 +66,7 @@ export class MockCoreService implements ICoreService { applicationKeypad: false, bracketedPasteMode: false, origin: false, + sendFocus: false, wraparound: true }; public onData: IEvent = new EventEmitter().event; diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index 1d616f54..cc7928c3 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -168,6 +168,7 @@ export interface IDecPrivateModes { applicationKeypad: boolean; bracketedPasteMode: boolean; origin: boolean; + sendFocus: boolean; wraparound: boolean; // defaults: xterm - true, vt100 - false } diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index 285532b5..55d425aa 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -17,6 +17,7 @@ const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({ applicationKeypad: false, bracketedPasteMode: false, origin: false, + sendFocus: false, wraparound: true // defaults: xterm - true, vt100 - false }); From c5ba1d62e7a6375938026ede64ff09d6e1ac2528 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 25 Apr 2020 13:31:45 -0700 Subject: [PATCH 07/24] Remove mouseEvents from Terminal Depend on CoreMouseService directly --- src/Terminal.ts | 12 ++++-------- src/common/TestUtils.test.ts | 1 + src/common/services/CoreMouseService.ts | 4 ++++ src/common/services/Services.ts | 1 + 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index bb02671f..c88a0715 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -88,9 +88,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT private _selectionService: ISelectionService; private _soundService: ISoundService; - // mouse properties - public mouseEvents: CoreMouseEventType = CoreMouseEventType.NONE; - // write buffer private _writeBuffer: WriteBuffer; @@ -500,7 +497,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService.onMouseDown(e))); // apply mouse event classes set by escape codes before terminal was attached - if (this.mouseEvents) { + if (this._coreMouseService.areMouseEventsActive) { this._selectionService.disable(); this.element.classList.add('enable-mouse-events'); } else { @@ -675,7 +672,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT }; this._coreMouseService.onProtocolChange(events => { // apply global changes on events - this.mouseEvents = events; if (events) { if (this.optionsService.options.logLevel === 'debug') { this._logService.debug('Binding to mouse events:', this._coreMouseService.explainEvents(events)); @@ -733,7 +729,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT // Don't send the mouse button to the pty if mouse events are disabled or // if the selection manager is having selection forced (ie. a modifier is // held). - if (!this.mouseEvents || this._selectionService.shouldForceSelection(ev)) { + if (!this._coreMouseService.areMouseEventsActive || this._selectionService.shouldForceSelection(ev)) { return; } @@ -787,13 +783,13 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT })); this.register(addDisposableDomListener(el, 'touchstart', (ev: TouchEvent) => { - if (this.mouseEvents) return; + if (this._coreMouseService.areMouseEventsActive) return; this.viewport.onTouchStart(ev); return this.cancel(ev); })); this.register(addDisposableDomListener(el, 'touchmove', (ev: TouchEvent) => { - if (this.mouseEvents) return; + if (this._coreMouseService.areMouseEventsActive) return; if (!this.viewport.onTouchMove(ev)) { return this.cancel(ev); } diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 55df896e..e5b3a425 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -31,6 +31,7 @@ export class MockBufferService implements IBufferService { } export class MockCoreMouseService implements ICoreMouseService { + public areMouseEventsActive: boolean = false; public activeEncoding: string = ''; public activeProtocol: string = ''; public addEncoding(name: string): void {} diff --git a/src/common/services/CoreMouseService.ts b/src/common/services/CoreMouseService.ts index c0ed3c6a..29a84761 100644 --- a/src/common/services/CoreMouseService.ts +++ b/src/common/services/CoreMouseService.ts @@ -192,6 +192,10 @@ export class CoreMouseService implements ICoreMouseService { return this._activeProtocol; } + public get areMouseEventsActive(): boolean { + return this._protocols[this._activeProtocol].events !== 0; + } + public set activeProtocol(name: string) { if (!this._protocols[name]) { throw new Error(`unknown protocol "${name}"`); diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 62ab45fd..05a6f3ae 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -27,6 +27,7 @@ export const ICoreMouseService = createDecorator('CoreMouseSe export interface ICoreMouseService { activeProtocol: string; activeEncoding: string; + areMouseEventsActive: boolean; addProtocol(name: string, protocol: ICoreMouseProtocol): void; addEncoding(name: string, encoding: CoreMouseEncoding): void; reset(): void; From 9346eba2f2916d4bad8e221dca7b85508915e160 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 25 Apr 2020 16:18:43 -0700 Subject: [PATCH 08/24] Remove Terminal.showCursor dep from input handler --- src/InputHandler.ts | 4 ++-- src/Terminal.test.ts | 2 -- src/Terminal.ts | 8 ++++---- src/TestUtils.test.ts | 6 ------ src/Types.d.ts | 4 ---- src/browser/services/RenderService.ts | 6 ++++-- 6 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 7732d4cf..c3f3446e 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -1794,9 +1794,9 @@ export class InputHandler extends Disposable implements IInputHandler { case 47: // alt screen buffer case 1047: // alt screen buffer this._bufferService.buffers.activateAltBuffer(this._eraseAttrData()); + this._coreService.isCursorInitialized = true; this._onRequestRefreshRows.fire(0, this._bufferService.rows - 1); this._terminal.viewport?.syncScrollArea(); - this._terminal.showCursor(); break; case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste) this._coreService.decPrivateModes.bracketedPasteMode = true; @@ -2012,9 +2012,9 @@ export class InputHandler extends Disposable implements IInputHandler { if (params.params[i] === 1049) { this.restoreCursor(); } + this._coreService.isCursorInitialized = true; this._onRequestRefreshRows.fire(0, this._bufferService.rows - 1); this._terminal.viewport?.syncScrollArea(); - this._terminal.showCursor(); break; case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste) this._coreService.decPrivateModes.bracketedPasteMode = false; diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index d70ccb45..40e1b141 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -140,7 +140,6 @@ describe('Terminal', () => { }; beforeEach(() => { - term.showCursor = () => { }; term.clearSelection = () => { }; }); @@ -531,7 +530,6 @@ describe('Terminal', () => { let evKeyPress: any; beforeEach(() => { - term.showCursor = () => { }; term.clearSelection = () => { }; // term.compositionHelper = { // isComposing: false, diff --git a/src/Terminal.ts b/src/Terminal.ts index c88a0715..dff303bb 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -278,7 +278,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT } this.updateCursorStyle(ev); this.element.classList.add('focus'); - this.showCursor(); + this._showCursor(); this._onFocus.fire(); } @@ -830,7 +830,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT /** * Display the cursor element */ - public showCursor(): void { + private _showCursor(): void { if (!this._coreService.isCursorInitialized) { this._coreService.isCursorInitialized = true; this.refresh(this.buffer.y, this.buffer.y); @@ -1165,7 +1165,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT } this._onKey.fire({ key: result.key, domEvent: event }); - this.showCursor(); + this._showCursor(); this._coreService.triggerDataEvent(result.key, true); // Cancel events when not in screen reader mode so events don't get bubbled up and handled by @@ -1242,7 +1242,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT key = String.fromCharCode(key); this._onKey.fire({ key, domEvent: ev }); - this.showCursor(); + this._showCursor(); this._coreService.triggerDataEvent(key, true); return true; diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index c15ce560..1cdc4c49 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -190,9 +190,6 @@ export class MockTerminal implements ITerminal { public reset(): void { throw new Error('Method not implemented.'); } - public showCursor(): void { - throw new Error('Method not implemented.'); - } public refresh(start: number, end: number): void { throw new Error('Method not implemented.'); } @@ -215,9 +212,6 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal { public resize(x: number, y: number): void { throw new Error('Method not implemented.'); } - public showCursor(): void { - throw new Error('Method not implemented.'); - } public handler(data: string): void { throw new Error('Method not implemented.'); } diff --git a/src/Types.d.ts b/src/Types.d.ts index 15eda648..2b26c070 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -21,8 +21,6 @@ export type LineData = CharData[]; * InputHandler cleanly from the ITerminal interface. */ export interface IInputHandlingTerminal { - buffers: IBufferSet; - buffer: IBuffer; viewport: IViewport; onA11yCharEmitter: IEventEmitter; @@ -31,7 +29,6 @@ export interface IInputHandlingTerminal { scroll(eraseAttr: IAttributeData, isWrapped?: boolean): void; is(term: string): boolean; resize(x: number, y: number): void; - showCursor(): void; handleTitle(title: string): void; } @@ -143,7 +140,6 @@ export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAcc scrollLines(disp: number, suppressScrollEvent?: boolean): void; cancel(ev: Event, force?: boolean): boolean | void; - showCursor(): void; } // Portions of the public API that are required by the internal Terminal diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 61ba35ab..d9aaf60a 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -10,7 +10,7 @@ import { Disposable } from 'common/Lifecycle'; import { ScreenDprMonitor } from 'browser/ScreenDprMonitor'; import { addDisposableDomListener } from 'browser/Lifecycle'; import { IColorSet } from 'browser/Types'; -import { IOptionsService } from 'common/services/Services'; +import { IOptionsService, IBufferService, ICoreService } from 'common/services/Services'; import { ICharSizeService, IRenderService } from 'browser/services/Services'; export class RenderService extends Disposable implements IRenderService { @@ -39,7 +39,9 @@ export class RenderService extends Disposable implements IRenderService { private _rowCount: number, screenElement: HTMLElement, @IOptionsService optionsService: IOptionsService, - @ICharSizeService charSizeService: ICharSizeService + @ICharSizeService charSizeService: ICharSizeService, + @IBufferService private readonly _bufferService: IBufferService, + @ICoreService private readonly _coreService: ICoreService ) { super(); this._renderDebouncer = new RenderDebouncer((start, end) => this._renderRows(start, end)); From e9db1ecd1f5dc33967c48935105f574860fa6324 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 25 Apr 2020 16:37:31 -0700 Subject: [PATCH 09/24] Remove Terminal.handleTitle dep --- src/InputHandler.ts | 4 +++- src/Terminal.test.ts | 2 +- src/Terminal.ts | 9 +-------- src/TestUtils.test.ts | 3 --- src/Types.d.ts | 3 ++- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index c3f3446e..f34ef145 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -236,6 +236,8 @@ export class InputHandler extends Disposable implements IInputHandler { public get onLineFeed(): IEvent { return this._onLineFeed.event; } private _onScroll = new EventEmitter(); public get onScroll(): IEvent { return this._onScroll.event; } + private _onTitleChange = new EventEmitter(); + public get onTitleChange(): IEvent { return this._onTitleChange.event; } constructor( private _terminal: IInputHandlingTerminal, @@ -2569,7 +2571,7 @@ export class InputHandler extends Disposable implements IInputHandler { */ public setTitle(data: string): void { this._windowTitle = data; - this._terminal.handleTitle(data); + this._onTitleChange.fire(data); } /** diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 40e1b141..61b40d6c 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -121,7 +121,7 @@ describe('Terminal', () => { assert.equal(e, 'title'); done(); }); - term.handleTitle('title'); + term.write('\x1b]2;title\x07'); }); }); diff --git a/src/Terminal.ts b/src/Terminal.ts index dff303bb..683437a9 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -182,6 +182,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT this._inputHandler.onRequestReset(() => this.reset()); this._inputHandler.onCursorMove(() => this._onCursorMove.fire()); this._inputHandler.onLineFeed(() => this._onLineFeed.fire()); + this._inputHandler.onTitleChange(title => this._onTitleChange.fire(title)); this.register(this._inputHandler); } @@ -1353,14 +1354,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT // this._onData.fire(data); // } - /** - * Emit the 'title' event and populate the given title. - * @param title The title to populate in the event. - */ - public handleTitle(title: string): void { - this._onTitleChange.fire(title); - } - /** * Reset terminal. * Note: Calling this directly from JS is synchronous but does not clear diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index 1cdc4c49..122640ff 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -215,9 +215,6 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal { public handler(data: string): void { throw new Error('Method not implemented.'); } - public handleTitle(title: string): void { - throw new Error('Method not implemented.'); - } } export class MockBuffer implements IBuffer { diff --git a/src/Types.d.ts b/src/Types.d.ts index 2b26c070..0aebc813 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -29,7 +29,6 @@ export interface IInputHandlingTerminal { scroll(eraseAttr: IAttributeData, isWrapped?: boolean): void; is(term: string): boolean; resize(x: number, y: number): void; - handleTitle(title: string): void; } export interface ICompositionHelper { @@ -44,6 +43,8 @@ export interface ICompositionHelper { * Calls the parser and handles actions generated by the parser. */ export interface IInputHandler { + onTitleChange: IEvent; + parse(data: string | Uint8Array): void; print(data: Uint32Array, start: number, end: number): void; From 3897950e22246107f3edc3658aee4497e8f4a9e5 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 25 Apr 2020 16:45:46 -0700 Subject: [PATCH 10/24] Remove a11y event dep, introduce forwardEvent helper --- src/InputHandler.ts | 8 ++++++-- src/Terminal.ts | 18 ++++++++++-------- src/Types.d.ts | 3 --- src/common/EventEmitter.ts | 4 ++++ 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index f34ef145..05d4a981 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -238,6 +238,10 @@ export class InputHandler extends Disposable implements IInputHandler { public get onScroll(): IEvent { return this._onScroll.event; } private _onTitleChange = new EventEmitter(); public get onTitleChange(): IEvent { return this._onTitleChange.event; } + private _onA11yChar = new EventEmitter(); + public get onA11yChar(): IEvent { return this._onA11yChar.event; } + private _onA11yTab = new EventEmitter(); + public get onA11yTab(): IEvent { return this._onA11yTab.event; } constructor( private _terminal: IInputHandlingTerminal, @@ -520,7 +524,7 @@ export class InputHandler extends Disposable implements IInputHandler { } if (screenReaderMode) { - this._terminal.onA11yCharEmitter.fire(stringFromCodePoint(code)); + this._onA11yChar.fire(stringFromCodePoint(code)); } // insert combining char at last cursor position @@ -738,7 +742,7 @@ export class InputHandler extends Disposable implements IInputHandler { const originalX = this._bufferService.buffer.x; this._bufferService.buffer.x = this._bufferService.buffer.nextStop(); if (this._optionsService.options.screenReaderMode) { - this._terminal.onA11yTabEmitter.fire(this._bufferService.buffer.x - originalX); + this._onA11yTab.fire(this._bufferService.buffer.x - originalX); } } diff --git a/src/Terminal.ts b/src/Terminal.ts index 683437a9..76ebf31f 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -41,7 +41,7 @@ import { ITheme, IMarker, IDisposable, ISelectionPosition, ILinkProvider } from import { DomRenderer } from 'browser/renderer/dom/DomRenderer'; import { IKeyboardEvent, KeyboardResultType, IBufferLine, IAttributeData, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions } from 'common/Types'; import { evaluateKeyboardEvent } from 'common/input/Keyboard'; -import { EventEmitter, IEvent } from 'common/EventEmitter'; +import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; @@ -133,10 +133,10 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT public get onFocus(): IEvent { return this._onFocus.event; } private _onBlur = new EventEmitter(); public get onBlur(): IEvent { return this._onBlur.event; } - public onA11yCharEmitter = new EventEmitter(); - public get onA11yChar(): IEvent { return this.onA11yCharEmitter.event; } - public onA11yTabEmitter = new EventEmitter(); - public get onA11yTab(): IEvent { return this.onA11yTabEmitter.event; } + private _onA11yCharEmitter = new EventEmitter(); + public get onA11yChar(): IEvent { return this._onA11yCharEmitter.event; } + private _onA11yTabEmitter = new EventEmitter(); + public get onA11yTab(): IEvent { return this._onA11yTabEmitter.event; } /** * Creates a new `Terminal` object. @@ -180,9 +180,11 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT this._inputHandler.onRequestBell(() => this.bell()); this._inputHandler.onRequestRefreshRows((start, end) => this.refresh(start, end)); this._inputHandler.onRequestReset(() => this.reset()); - this._inputHandler.onCursorMove(() => this._onCursorMove.fire()); - this._inputHandler.onLineFeed(() => this._onLineFeed.fire()); - this._inputHandler.onTitleChange(title => this._onTitleChange.fire(title)); + forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove); + forwardEvent(this._inputHandler.onLineFeed, this._onLineFeed); + forwardEvent(this._inputHandler.onTitleChange, this._onTitleChange); + forwardEvent(this._inputHandler.onA11yChar, this._onA11yCharEmitter); + forwardEvent(this._inputHandler.onA11yTab, this._onA11yTabEmitter); this.register(this._inputHandler); } diff --git a/src/Types.d.ts b/src/Types.d.ts index 0aebc813..4e4142b0 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -23,9 +23,6 @@ export type LineData = CharData[]; export interface IInputHandlingTerminal { viewport: IViewport; - onA11yCharEmitter: IEventEmitter; - onA11yTabEmitter: IEventEmitter; - scroll(eraseAttr: IAttributeData, isWrapped?: boolean): void; is(term: string): boolean; resize(x: number, y: number): void; diff --git a/src/common/EventEmitter.ts b/src/common/EventEmitter.ts index 301574e4..847e3249 100644 --- a/src/common/EventEmitter.ts +++ b/src/common/EventEmitter.ts @@ -63,3 +63,7 @@ export class EventEmitter implements IEventEmitter { this._disposed = true; } } + +export function forwardEvent(from: IEvent, to: IEventEmitter): void { + from(e => to.fire(e)); +} From 0709b0dc3b5bfe67cd39346ccb716d386e83fc48 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 25 Apr 2020 19:28:00 -0700 Subject: [PATCH 11/24] Break input handler dep on Terminal.scroll --- src/InputHandler.ts | 8 +++++--- src/Terminal.ts | 1 + src/TestUtils.test.ts | 11 ----------- src/Types.d.ts | 2 +- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 05d4a981..6f160961 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -236,6 +236,8 @@ export class InputHandler extends Disposable implements IInputHandler { public get onLineFeed(): IEvent { return this._onLineFeed.event; } private _onScroll = new EventEmitter(); public get onScroll(): IEvent { return this._onScroll.event; } + private _onScrollRequest = new EventEmitter(); + public get onScrollRequest(): IEvent { return this._onScrollRequest.event; } private _onTitleChange = new EventEmitter(); public get onTitleChange(): IEvent { return this._onTitleChange.event; } private _onA11yChar = new EventEmitter(); @@ -554,7 +556,7 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.y++; if (buffer.y === buffer.scrollBottom + 1) { buffer.y--; - this._terminal.scroll(this._eraseAttrData(), true); + this._onScrollRequest.fire(this._eraseAttrData(), true); } else { if (buffer.y >= this._bufferService.rows) { buffer.y = this._bufferService.rows - 1; @@ -693,7 +695,7 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.y++; if (buffer.y === buffer.scrollBottom + 1) { buffer.y--; - this._terminal.scroll(this._eraseAttrData()); + this._onScrollRequest.fire(this._eraseAttrData()); } else if (buffer.y >= this._bufferService.rows) { buffer.y = this._bufferService.rows - 1; } @@ -2676,7 +2678,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._bufferService.buffer.y++; if (buffer.y === buffer.scrollBottom + 1) { buffer.y--; - this._terminal.scroll(this._eraseAttrData()); + this._onScrollRequest.fire(this._eraseAttrData()); } else if (buffer.y >= this._bufferService.rows) { buffer.y = this._bufferService.rows - 1; } diff --git a/src/Terminal.ts b/src/Terminal.ts index 76ebf31f..c051f541 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -180,6 +180,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT this._inputHandler.onRequestBell(() => this.bell()); this._inputHandler.onRequestRefreshRows((start, end) => this.refresh(start, end)); this._inputHandler.onRequestReset(() => this.reset()); + this._inputHandler.onScrollRequest((eraseAttr, isWrapped) => this.scroll(eraseAttr, isWrapped || undefined)); forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove); forwardEvent(this._inputHandler.onLineFeed, this._onLineFeed); forwardEvent(this._inputHandler.onTitleChange, this._onTitleChange); diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index 122640ff..2027f994 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -14,7 +14,6 @@ import { Terminal } from './Terminal'; import { AttributeData } from 'common/buffer/AttributeData'; import { IColorManager, IColorSet, ILinkMatcherOptions, ILinkifier, IViewport, ILinkifier2 } from 'browser/Types'; import { IOptionsService, IUnicodeService } from 'common/services/Services'; -import { EventEmitter } from 'common/EventEmitter'; import { IParams, IFunctionIdentifier } from 'common/parser/Types'; import { ISelectionService } from 'browser/services/Services'; @@ -198,23 +197,13 @@ export class MockTerminal implements ITerminal { } export class MockInputHandlingTerminal implements IInputHandlingTerminal { - public onA11yCharEmitter: EventEmitter; - public onA11yTabEmitter: EventEmitter; - public buffers: IBufferSet; - public buffer: IBuffer = new MockBuffer(); public viewport: IViewport; - public scroll(eraseAttr: IAttributeData, isWrapped?: boolean): void { - throw new Error('Method not implemented.'); - } public is(term: string): boolean { throw new Error('Method not implemented.'); } public resize(x: number, y: number): void { throw new Error('Method not implemented.'); } - public handler(data: string): void { - throw new Error('Method not implemented.'); - } } export class MockBuffer implements IBuffer { diff --git a/src/Types.d.ts b/src/Types.d.ts index 4e4142b0..241d8228 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -23,7 +23,6 @@ export type LineData = CharData[]; export interface IInputHandlingTerminal { viewport: IViewport; - scroll(eraseAttr: IAttributeData, isWrapped?: boolean): void; is(term: string): boolean; resize(x: number, y: number): void; } @@ -41,6 +40,7 @@ export interface ICompositionHelper { */ export interface IInputHandler { onTitleChange: IEvent; + onScrollRequest: IEvent; parse(data: string | Uint8Array): void; print(data: Uint32Array, start: number, end: number): void; From a3ac0d542da9bbcfe14a1caed9cfafa0a053c7b7 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 25 Apr 2020 20:39:55 -0700 Subject: [PATCH 12/24] Remove unused imports --- src/InputHandler.ts | 1 - src/Types.d.ts | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 6f160961..dcdabe95 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -2479,7 +2479,6 @@ export class InputHandler extends Disposable implements IInputHandler { switch (params.params[0]) { case 14: // GetWinSizePixels, returns CSI 4 ; height ; width t if (rs && second !== 2) { - console.log(rs.dimensions); const w = rs.dimensions.scaledCanvasWidth.toFixed(0); const h = rs.dimensions.scaledCanvasHeight.toFixed(0); this._coreService.triggerDataEvent(`${C0.ESC}[4;${h};${w}t`); diff --git a/src/Types.d.ts b/src/Types.d.ts index 241d8228..047df8ec 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -3,10 +3,10 @@ * @license MIT */ -import { ITerminalOptions as IPublicTerminalOptions, IDisposable, IMarker, ISelectionPosition, ILinkProvider } from 'xterm'; -import { ICharset, IAttributeData, CharData, CoreMouseEventType, ITerminalOptions } from 'common/Types'; -import { IEvent, IEventEmitter } from 'common/EventEmitter'; -import { IColorSet, ILinkifier, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types'; +import { IDisposable, IMarker, ISelectionPosition, ILinkProvider } from 'xterm'; +import { IAttributeData, CharData, ITerminalOptions } from 'common/Types'; +import { IEvent } from 'common/EventEmitter'; +import { ILinkifier, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types'; import { IOptionsService, IUnicodeService } from 'common/services/Services'; import { IBuffer, IBufferSet } from 'common/buffer/Types'; import { IParams, IFunctionIdentifier } from 'common/parser/Types'; From eba372e2cc8d47629f33d4679ebd68de6df83d5d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 25 Apr 2020 20:51:33 -0700 Subject: [PATCH 13/24] More some of Terminal.resize into bufferservice --- src/Terminal.ts | 3 --- src/common/services/BufferService.ts | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index c051f541..102cc36e 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -1292,10 +1292,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT if (x < MINIMUM_COLS) x = MINIMUM_COLS; if (y < MINIMUM_ROWS) y = MINIMUM_ROWS; - this.buffers.resize(x, y); - this._bufferService.resize(x, y); - this.buffers.setupTabStops(this.cols); this._charSizeService?.measure(); diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index 1b6da792..a79fc305 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -30,6 +30,8 @@ export class BufferService implements IBufferService { public resize(cols: number, rows: number): void { this.cols = cols; this.rows = rows; + this.buffers.resize(cols, rows); + this.buffers.setupTabStops(this.cols); } public reset(): void { From a6949d522ea702f284f17f19fcc14137d28a3235 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 26 Apr 2020 06:16:13 -0700 Subject: [PATCH 14/24] Move resize event into bufferservice --- src/Terminal.ts | 11 ++++++----- src/common/CoreTerminal.ts | 9 ++++++--- src/common/services/BufferService.ts | 5 +++++ src/common/services/Services.ts | 2 +- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 102cc36e..41bbd177 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -120,8 +120,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._onKey.event; } private _onRender = new EventEmitter<{ start: number, end: number }>(); public get onRender(): IEvent<{ start: number, end: number }> { return this._onRender.event; } - private _onResize = new EventEmitter<{ cols: number, rows: number }>(); - public get onResize(): IEvent<{ cols: number, rows: number }> { return this._onResize.event; } private _onScroll = new EventEmitter(); public get onScroll(): IEvent { return this._onScroll.event; } private _onSelectionChange = new EventEmitter(); @@ -1289,19 +1287,22 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT return; } - if (x < MINIMUM_COLS) x = MINIMUM_COLS; - if (y < MINIMUM_ROWS) y = MINIMUM_ROWS; + x = Math.max(x, MINIMUM_COLS); + y = Math.max(y, MINIMUM_ROWS); this._bufferService.resize(x, y); + // TODO: Have charsize, viewport and renderservice depend on IBufferService.onResize? + this._charSizeService?.measure(); + // TODO: Call on resize event? // Sync the scroll area to make sure scroll events don't fire and scroll the viewport to an // invalid location this.viewport?.syncScrollArea(true); + // TODO: Move to renderservice this.refresh(0, this.rows - 1); - this._onResize.fire({ cols: x, rows: y }); } /** diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 5428a3bc..fd1d9309 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -29,7 +29,7 @@ import { BufferService } from 'common/services/BufferService'; import { OptionsService } from 'common/services/OptionsService'; import { ITerminalOptions, IDisposable } from './Types'; import { CoreService } from 'common/services/CoreService'; -import { EventEmitter, IEvent } from 'common/EventEmitter'; +import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter'; import { CoreMouseService } from 'common/services/CoreMouseService'; import { DirtyRowService } from 'common/services/DirtyRowService'; import { UnicodeService } from 'common/services/UnicodeService'; @@ -58,6 +58,8 @@ export abstract class CoreTerminal extends Disposable { public get onData(): IEvent { return this._onData.event; } protected _onLineFeed = new EventEmitter(); public get onLineFeed(): IEvent { return this._onLineFeed.event; } + private _onResize = new EventEmitter<{ cols: number, rows: number }>(); + public get onResize(): IEvent<{ cols: number, rows: number }> { return this._onResize.event; } public get cols(): number { return this._bufferService.cols; } public get rows(): number { return this._bufferService.rows; } @@ -88,8 +90,9 @@ export abstract class CoreTerminal extends Disposable { this._instantiationService.setService(ICharsetService, this._charsetService); // Setup listeners - this._coreService.onData(e => this._onData.fire(e)); - this._coreService.onBinary(e => this._onBinary.fire(e)); + forwardEvent(this._bufferService.onResize, this._onResize); + forwardEvent(this._coreService.onData, this._onData); + forwardEvent(this._coreService.onBinary, this._onBinary); this.optionsService.onOptionChange(key => this._updateOptions(key)); } diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index a79fc305..d62d2cb0 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -6,6 +6,7 @@ import { IBufferService, IOptionsService } from 'common/services/Services'; import { BufferSet } from 'common/buffer/BufferSet'; import { IBufferSet, IBuffer } from 'common/buffer/Types'; +import { EventEmitter, IEvent } from 'common/EventEmitter'; export const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars export const MINIMUM_ROWS = 1; @@ -17,6 +18,9 @@ export class BufferService implements IBufferService { public rows: number; public buffers: IBufferSet; + private _onResize = new EventEmitter<{ cols: number, rows: number }>(); + public get onResize(): IEvent<{ cols: number, rows: number }> { return this._onResize.event; } + public get buffer(): IBuffer { return this.buffers.active; } constructor( @@ -32,6 +36,7 @@ export class BufferService implements IBufferService { this.rows = rows; this.buffers.resize(cols, rows); this.buffers.setupTabStops(this.cols); + this._onResize.fire({ cols, rows }); } public reset(): void { diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 05a6f3ae..8fa3f57b 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -17,7 +17,7 @@ export interface IBufferService { readonly buffer: IBuffer; readonly buffers: IBufferSet; - // TODO: Move resize event here + onResize: IEvent<{ cols: number, rows: number }>; resize(cols: number, rows: number): void; reset(): void; From 451d9aab96df86f077ef39000490dc62ef654ce8 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 26 Apr 2020 06:21:16 -0700 Subject: [PATCH 15/24] Register forwardEvent calls --- src/Terminal.ts | 18 +++++++++--------- src/common/CoreTerminal.ts | 8 ++++---- src/common/EventEmitter.ts | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 41bbd177..9572345e 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -175,15 +175,15 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT } else { // Register input handler and refire/handle events this._inputHandler = new InputHandler(this, this._bufferService, this._charsetService, this._coreService, this._dirtyRowService, this._logService, this.optionsService, this._coreMouseService, this.unicodeService, this._instantiationService); - this._inputHandler.onRequestBell(() => this.bell()); - this._inputHandler.onRequestRefreshRows((start, end) => this.refresh(start, end)); - this._inputHandler.onRequestReset(() => this.reset()); - this._inputHandler.onScrollRequest((eraseAttr, isWrapped) => this.scroll(eraseAttr, isWrapped || undefined)); - forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove); - forwardEvent(this._inputHandler.onLineFeed, this._onLineFeed); - forwardEvent(this._inputHandler.onTitleChange, this._onTitleChange); - forwardEvent(this._inputHandler.onA11yChar, this._onA11yCharEmitter); - forwardEvent(this._inputHandler.onA11yTab, this._onA11yTabEmitter); + this.register(this._inputHandler.onRequestBell(() => this.bell())); + this.register(this._inputHandler.onRequestRefreshRows((start, end) => this.refresh(start, end))); + this.register(this._inputHandler.onRequestReset(() => this.reset())); + this.register(this._inputHandler.onScrollRequest((eraseAttr, isWrapped) => this.scroll(eraseAttr, isWrapped || undefined))); + this.register(forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove)); + this.register(forwardEvent(this._inputHandler.onLineFeed, this._onLineFeed)); + this.register(forwardEvent(this._inputHandler.onTitleChange, this._onTitleChange)); + this.register(forwardEvent(this._inputHandler.onA11yChar, this._onA11yCharEmitter)); + this.register(forwardEvent(this._inputHandler.onA11yTab, this._onA11yTabEmitter)); this.register(this._inputHandler); } diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index fd1d9309..90cc7ade 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -90,10 +90,10 @@ export abstract class CoreTerminal extends Disposable { this._instantiationService.setService(ICharsetService, this._charsetService); // Setup listeners - forwardEvent(this._bufferService.onResize, this._onResize); - forwardEvent(this._coreService.onData, this._onData); - forwardEvent(this._coreService.onBinary, this._onBinary); - this.optionsService.onOptionChange(key => this._updateOptions(key)); + this.register(forwardEvent(this._bufferService.onResize, this._onResize)); + this.register(forwardEvent(this._coreService.onData, this._onData)); + this.register(forwardEvent(this._coreService.onBinary, this._onBinary)); + this.register(this.optionsService.onOptionChange(key => this._updateOptions(key))); } public dispose(): void { diff --git a/src/common/EventEmitter.ts b/src/common/EventEmitter.ts index 847e3249..4684809f 100644 --- a/src/common/EventEmitter.ts +++ b/src/common/EventEmitter.ts @@ -64,6 +64,6 @@ export class EventEmitter implements IEventEmitter { } } -export function forwardEvent(from: IEvent, to: IEventEmitter): void { - from(e => to.fire(e)); +export function forwardEvent(from: IEvent, to: IEventEmitter): IDisposable { + return from(e => to.fire(e)); } From 3a1a0d659069e5abbaf060da3f1f2f6f930ded88 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 26 Apr 2020 06:34:36 -0700 Subject: [PATCH 16/24] Remove input handler Terminal.resize dep --- src/InputHandler.ts | 4 ++-- src/Terminal.ts | 21 ++++++--------------- src/Types.d.ts | 1 - src/browser/services/RenderService.ts | 4 ++-- src/common/CoreTerminal.ts | 13 ++++++++++++- src/common/TestUtils.test.ts | 1 + 6 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index dcdabe95..de643314 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -1741,7 +1741,7 @@ export class InputHandler extends Disposable implements IInputHandler { * through `options.windowsOptions`. */ if (this._optionsService.options.windowOptions.setWinLines) { - this._terminal.resize(132, this._bufferService.rows); + this._bufferService.resize(132, this._bufferService.rows); this._onRequestReset.fire(); } break; @@ -1968,7 +1968,7 @@ export class InputHandler extends Disposable implements IInputHandler { * through `options.windowsOptions`. */ if (this._optionsService.options.windowOptions.setWinLines) { - this._terminal.resize(80, this._bufferService.rows); + this._bufferService.resize(80, this._bufferService.rows); this._onRequestReset.fire(); } break; diff --git a/src/Terminal.ts b/src/Terminal.ts index 9572345e..6e192320 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -47,7 +47,6 @@ import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService } from 'browser/services/Services'; import { CharSizeService } from 'browser/services/CharSizeService'; -import { MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService'; import { IBuffer } from 'common/buffer/Types'; import { MouseService } from 'browser/services/MouseService'; import { IParams, IFunctionIdentifier } from 'common/parser/Types'; @@ -155,6 +154,9 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT this._setup(); + // Setup listeners + this._bufferService.onResize(e => this._afterResize(e.cols, e.rows)); + this._writeBuffer = new WriteBuffer(data => this._inputHandler.parse(data)); } @@ -1275,10 +1277,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT * @param y The number of rows to resize to. */ public resize(x: number, y: number): void { - if (isNaN(x) || isNaN(y)) { - return; - } - if (x === this.cols && y === this.rows) { // Check if we still need to measure the char size (fixes #785). if (this._charSizeService && !this._charSizeService.hasValidSize) { @@ -1287,22 +1285,15 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT return; } - x = Math.max(x, MINIMUM_COLS); - y = Math.max(y, MINIMUM_ROWS); - - this._bufferService.resize(x, y); - - // TODO: Have charsize, viewport and renderservice depend on IBufferService.onResize? + super.resize(x, y); + } + private _afterResize(x: number, y: number): void { this._charSizeService?.measure(); - // TODO: Call on resize event? // Sync the scroll area to make sure scroll events don't fire and scroll the viewport to an // invalid location this.viewport?.syncScrollArea(true); - - // TODO: Move to renderservice - this.refresh(0, this.rows - 1); } /** diff --git a/src/Types.d.ts b/src/Types.d.ts index 047df8ec..c3e1c53d 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -24,7 +24,6 @@ export interface IInputHandlingTerminal { viewport: IViewport; is(term: string): boolean; - resize(x: number, y: number): void; } export interface ICompositionHelper { diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index d9aaf60a..27b66458 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -40,8 +40,7 @@ export class RenderService extends Disposable implements IRenderService { screenElement: HTMLElement, @IOptionsService optionsService: IOptionsService, @ICharSizeService charSizeService: ICharSizeService, - @IBufferService private readonly _bufferService: IBufferService, - @ICoreService private readonly _coreService: ICoreService + @IBufferService private readonly _bufferService: IBufferService ) { super(); this._renderDebouncer = new RenderDebouncer((start, end) => this._renderRows(start, end)); @@ -51,6 +50,7 @@ export class RenderService extends Disposable implements IRenderService { this._screenDprMonitor.setListener(() => this.onDevicePixelRatioChange()); this.register(this._screenDprMonitor); + this.register(this._bufferService.onResize(e => this._fullRefresh())); this.register(optionsService.onOptionChange(() => this._renderer.onOptionsChanged())); this.register(charSizeService.onCharSizeChange(() => this.onCharSizeChanged())); diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 90cc7ade..49f3509d 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -25,7 +25,7 @@ import { Disposable } from 'common/Lifecycle'; import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, ICoreMouseService, IUnicodeService, IDirtyRowService } from 'common/services/Services'; import { InstantiationService } from 'common/services/InstantiationService'; import { LogService } from 'common/services/LogService'; -import { BufferService } from 'common/services/BufferService'; +import { BufferService, MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService'; import { OptionsService } from 'common/services/OptionsService'; import { ITerminalOptions, IDisposable } from './Types'; import { CoreService } from 'common/services/CoreService'; @@ -105,6 +105,17 @@ export abstract class CoreTerminal extends Disposable { this._windowsMode = undefined; } + public resize(x: number, y: number): void { + if (isNaN(x) || isNaN(y)) { + return; + } + + x = Math.max(x, MINIMUM_COLS); + y = Math.max(y, MINIMUM_ROWS); + + this._bufferService.resize(x, y); + } + protected _setup(): void { if (this.optionsService.options.windowsMode) { this._enableWindowsMode(); diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index e5b3a425..eacf92fe 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -16,6 +16,7 @@ export class MockBufferService implements IBufferService { public serviceBrand: any; public get buffer(): IBuffer { return this.buffers.active; } public buffers: IBufferSet = {} as any; + public onResize: IEvent<{ cols: number, rows: number }> = new EventEmitter<{ cols: number, rows: number }>().event; constructor( public cols: number, public rows: number, From dbced5c1a442b60113083ca8c7099a7f9990f2b4 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 26 Apr 2020 06:38:10 -0700 Subject: [PATCH 17/24] Move Terminal.is into InputHandler --- src/InputHandler.ts | 20 ++++++++++++++------ src/Terminal.ts | 8 -------- src/Types.d.ts | 2 -- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/InputHandler.ts b/src/InputHandler.ts index de643314..f4a084e2 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -1528,9 +1528,9 @@ export class InputHandler extends Disposable implements IInputHandler { if (params.params[0] > 0) { return; } - if (this._terminal.is('xterm') || this._terminal.is('rxvt-unicode') || this._terminal.is('screen')) { + if (this._is('xterm') || this._is('rxvt-unicode') || this._is('screen')) { this._coreService.triggerDataEvent(C0.ESC + '[?1;2c'); - } else if (this._terminal.is('linux')) { + } else if (this._is('linux')) { this._coreService.triggerDataEvent(C0.ESC + '[?6c'); } } @@ -1566,19 +1566,27 @@ export class InputHandler extends Disposable implements IInputHandler { // xterm and urxvt // seem to spit this // out around ~370 times (?). - if (this._terminal.is('xterm')) { + if (this._is('xterm')) { this._coreService.triggerDataEvent(C0.ESC + '[>0;276;0c'); - } else if (this._terminal.is('rxvt-unicode')) { + } else if (this._is('rxvt-unicode')) { this._coreService.triggerDataEvent(C0.ESC + '[>85;95;0c'); - } else if (this._terminal.is('linux')) { + } else if (this._is('linux')) { // not supported by linux console. // linux console echoes parameters. this._coreService.triggerDataEvent(params.params[0] + 'c'); - } else if (this._terminal.is('screen')) { + } else if (this._is('screen')) { this._coreService.triggerDataEvent(C0.ESC + '[>83;40003;0c'); } } + /** + * Evaluate if the current terminal is the given argument. + * @param term The terminal name to evaluate + */ + private _is(term: string): boolean { + return (this._optionsService.options.termName + '').indexOf(term) === 0; + } + /** * CSI Pm h Set Mode (SM). * Ps = 2 -> Keyboard Action Mode (AM). diff --git a/src/Terminal.ts b/src/Terminal.ts index 6e192320..9dab9651 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -1316,14 +1316,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT this._onScroll.fire(this.buffer.ydisp); } - /** - * Evaluate if the current terminal is the given argument. - * @param term The terminal name to evaluate - */ - public is(term: string): boolean { - return (this.options.termName + '').indexOf(term) === 0; - } - /** * Emit the data event and populate the given data. * @param data The data to populate in the event. diff --git a/src/Types.d.ts b/src/Types.d.ts index c3e1c53d..7861fc7f 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -22,8 +22,6 @@ export type LineData = CharData[]; */ export interface IInputHandlingTerminal { viewport: IViewport; - - is(term: string): boolean; } export interface ICompositionHelper { From ba40fe4ff4a16e794190e8876fb3707cc086e13c Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 26 Apr 2020 06:46:37 -0700 Subject: [PATCH 18/24] Remove Terminal dependency from InputHandler :tada: --- src/InputHandler.test.ts | 29 ++++++++++------------------- src/InputHandler.ts | 23 ++++++++++++----------- src/Terminal.ts | 29 ++++------------------------- src/TestUtils.test.ts | 12 +----------- src/Types.d.ts | 9 --------- 5 files changed, 27 insertions(+), 75 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 6c5b9cbb..fd07fcb2 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -5,7 +5,7 @@ import { assert, expect } from 'chai'; import { InputHandler } from './InputHandler'; -import { MockInputHandlingTerminal, TestTerminal } from './TestUtils.test'; +import { TestTerminal } from './TestUtils.test'; import { Terminal } from './Terminal'; import { IBufferLine, IAttributeData } from 'common/Types'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; @@ -39,12 +39,11 @@ class TestInputHandler extends InputHandler { describe('InputHandler', () => { describe('save and restore cursor', () => { - const terminal = new MockInputHandlingTerminal(); const bufferService = new MockBufferService(80, 30); bufferService.buffer.x = 1; bufferService.buffer.y = 2; bufferService.buffer.ybase = 0; - const inputHandler = new TestInputHandler(terminal, bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new TestInputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); inputHandler.curAttrData.fg = 3; // Save cursor position inputHandler.saveCursor(); @@ -64,7 +63,7 @@ describe('InputHandler', () => { describe('setCursorStyle', () => { it('should call Terminal.setOption with correct params', () => { const optionsService = new MockOptionsService(); - const inputHandler = new InputHandler(new MockInputHandlingTerminal(), new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), optionsService, new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), optionsService, new MockCoreMouseService(), new MockUnicodeService(), {} as any); inputHandler.setCursorStyle(Params.fromArray([0])); assert.equal(optionsService.options['cursorStyle'], 'block'); @@ -103,9 +102,8 @@ describe('InputHandler', () => { }); describe('setMode', () => { it('should toggle bracketedPasteMode', () => { - const terminal = new MockInputHandlingTerminal(); const coreService = new MockCoreService(); - const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCharsetService(), coreService, new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), coreService, new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); // Set bracketed paste mode inputHandler.setModePrivate(Params.fromArray([2004])); assert.equal(coreService.decPrivateModes.bracketedPasteMode, true); @@ -122,9 +120,8 @@ describe('InputHandler', () => { } it('insertChars', function(): void { - const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -160,9 +157,8 @@ describe('InputHandler', () => { expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a')); }); it('deleteChars', function(): void { - const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -201,9 +197,8 @@ describe('InputHandler', () => { expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a')); }); it('eraseInLine', function(): void { - const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); // fill 6 lines to test 3 different states inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -230,9 +225,8 @@ describe('InputHandler', () => { }); it('eraseInDisplay', function(): void { - const term = new Terminal({cols: 80, rows: 7}); const bufferService = new MockBufferService(80, 7); - const inputHandler = new InputHandler(term, bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); // fill display with a's for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -366,8 +360,7 @@ describe('InputHandler', () => { }); describe('print', () => { it('should not cause an infinite loop (regression test)', () => { - const term = new Terminal(); - const inputHandler = new InputHandler(term, new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); const container = new Uint32Array(10); container[0] = 0x200B; inputHandler.print(container, 0, 1); @@ -375,14 +368,12 @@ describe('InputHandler', () => { }); describe('alt screen', () => { - let term: Terminal; let bufferService: IBufferService; let handler: InputHandler; beforeEach(() => { - term = new Terminal(); bufferService = new MockBufferService(80, 30); - handler = new InputHandler(term, bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + handler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); }); it('should handle DECSET/DECRST 47 (alt screen buffer)', () => { handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST'); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index f4a084e2..bf05d511 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -4,7 +4,7 @@ * @license MIT */ -import { IInputHandler, IInputHandlingTerminal } from './Types'; +import { IInputHandler } from './Types'; import { C0, C1 } from 'common/data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets'; import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser'; @@ -238,6 +238,8 @@ export class InputHandler extends Disposable implements IInputHandler { public get onScroll(): IEvent { return this._onScroll.event; } private _onScrollRequest = new EventEmitter(); public get onScrollRequest(): IEvent { return this._onScrollRequest.event; } + private _onSyncScrollBarRequest = new EventEmitter(); + public get onSyncScrollBarRequest(): IEvent { return this._onSyncScrollBarRequest.event; } private _onTitleChange = new EventEmitter(); public get onTitleChange(): IEvent { return this._onTitleChange.event; } private _onA11yChar = new EventEmitter(); @@ -246,7 +248,6 @@ export class InputHandler extends Disposable implements IInputHandler { public get onA11yTab(): IEvent { return this._onA11yTab.event; } constructor( - private _terminal: IInputHandlingTerminal, private readonly _bufferService: IBufferService, private readonly _charsetService: ICharsetService, private readonly _coreService: ICoreService, @@ -256,8 +257,8 @@ export class InputHandler extends Disposable implements IInputHandler { private readonly _coreMouseService: ICoreMouseService, private readonly _unicodeService: IUnicodeService, private readonly _instantiationService: IInstantiationService, - private readonly _parser: IEscapeSequenceParser = new EscapeSequenceParser()) - { + private readonly _parser: IEscapeSequenceParser = new EscapeSequenceParser() + ) { super(); this.register(this._parser); @@ -1766,7 +1767,7 @@ export class InputHandler extends Disposable implements IInputHandler { case 66: this._logService.debug('Serial port requested application keypad.'); this._coreService.decPrivateModes.applicationKeypad = true; - this._terminal.viewport?.syncScrollArea(); + this._onSyncScrollBarRequest.fire(); break; case 9: // X10 Mouse // no release, no motion, no wheel, no modifiers. @@ -1812,7 +1813,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._bufferService.buffers.activateAltBuffer(this._eraseAttrData()); this._coreService.isCursorInitialized = true; this._onRequestRefreshRows.fire(0, this._bufferService.rows - 1); - this._terminal.viewport?.syncScrollArea(); + this._onSyncScrollBarRequest.fire(); break; case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste) this._coreService.decPrivateModes.bracketedPasteMode = true; @@ -1993,7 +1994,7 @@ export class InputHandler extends Disposable implements IInputHandler { case 66: this._logService.debug('Switching back to normal keypad.'); this._coreService.decPrivateModes.applicationKeypad = false; - this._terminal.viewport?.syncScrollArea(); + this._onSyncScrollBarRequest.fire(); break; case 9: // X10 Mouse case 1000: // vt200 mouse @@ -2030,7 +2031,7 @@ export class InputHandler extends Disposable implements IInputHandler { } this._coreService.isCursorInitialized = true; this._onRequestRefreshRows.fire(0, this._bufferService.rows - 1); - this._terminal.viewport?.syncScrollArea(); + this._onSyncScrollBarRequest.fire(); break; case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste) this._coreService.decPrivateModes.bracketedPasteMode = false; @@ -2369,7 +2370,7 @@ export class InputHandler extends Disposable implements IInputHandler { */ public softReset(params: IParams): void { this._coreService.isCursorHidden = false; - this._terminal.viewport?.syncScrollArea(); + this._onSyncScrollBarRequest.fire(); this._bufferService.buffer.scrollTop = 0; this._bufferService.buffer.scrollBottom = this._bufferService.rows - 1; this._curAttrData = DEFAULT_ATTR_DATA.clone(); @@ -2617,7 +2618,7 @@ export class InputHandler extends Disposable implements IInputHandler { public keypadApplicationMode(): void { this._logService.debug('Serial port requested application keypad.'); this._coreService.decPrivateModes.applicationKeypad = true; - this._terminal.viewport?.syncScrollArea(); + this._onSyncScrollBarRequest.fire(); } /** @@ -2628,7 +2629,7 @@ export class InputHandler extends Disposable implements IInputHandler { public keypadNumericMode(): void { this._logService.debug('Switching back to normal keypad.'); this._coreService.decPrivateModes.applicationKeypad = false; - this._terminal.viewport?.syncScrollArea(); + this._onSyncScrollBarRequest.fire(); } /** diff --git a/src/Terminal.ts b/src/Terminal.ts index 9dab9651..4606f1aa 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -21,7 +21,7 @@ * http://linux.die.net/man/7/urxvt */ -import { IInputHandlingTerminal, ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler } from './Types'; +import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler } from './Types'; import { IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types'; import { CompositionHelper } from 'browser/input/CompositionHelper'; import { Viewport } from 'browser/Viewport'; @@ -60,7 +60,7 @@ import { CoreTerminal } from 'common/CoreTerminal'; const document = (typeof window !== 'undefined') ? window.document : null; -export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingTerminal { +export class Terminal extends CoreTerminal implements ITerminal { public textarea: HTMLTextAreaElement; public element: HTMLElement; public screenElement: HTMLElement; @@ -176,7 +176,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT this._inputHandler.reset(); } else { // Register input handler and refire/handle events - this._inputHandler = new InputHandler(this, this._bufferService, this._charsetService, this._coreService, this._dirtyRowService, this._logService, this.optionsService, this._coreMouseService, this.unicodeService, this._instantiationService); + this._inputHandler = new InputHandler(this._bufferService, this._charsetService, this._coreService, this._dirtyRowService, this._logService, this.optionsService, this._coreMouseService, this.unicodeService, this._instantiationService); this.register(this._inputHandler.onRequestBell(() => this.bell())); this.register(this._inputHandler.onRequestRefreshRows((start, end) => this.refresh(start, end))); this.register(this._inputHandler.onRequestReset(() => this.reset())); @@ -462,6 +462,7 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT this._viewportScrollArea ); this.viewport.onThemeChange(this._colorManager.colors); + this.register(this._inputHandler.onSyncScrollBarRequest(() => this.viewport.syncScrollArea())); this.register(this.viewport); this.register(this.onCursorMove(() => this._renderService.onCursorMove())); @@ -1316,28 +1317,6 @@ export class Terminal extends CoreTerminal implements ITerminal, IInputHandlingT this._onScroll.fire(this.buffer.ydisp); } - /** - * Emit the data event and populate the given data. - * @param data The data to populate in the event. - */ - // public handler(data: string): void { - // // Prevents all events to pty process if stdin is disabled - // if (this.options.disableStdin) { - // return; - // } - - // // Clear the selection if the selection manager is available and has an active selection - // if (this.selectionService && this.selectionService.hasSelection) { - // this.selectionService.clearSelection(); - // } - - // // Input is being sent to the terminal, the terminal should focus the prompt. - // if (this.buffer.ybase !== this.buffer.ydisp) { - // this.scrollToBottom(); - // } - // this._onData.fire(data); - // } - /** * Reset terminal. * Note: Calling this directly from JS is synchronous but does not clear diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index 2027f994..32b2633f 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -4,7 +4,7 @@ */ import { IRenderer, IRenderDimensions, CharacterJoinerHandler, IRequestRedrawEvent } from 'browser/renderer/Types'; -import { IInputHandlingTerminal, ICompositionHelper, ITerminal, IBrowser } from './Types'; +import { ICompositionHelper, ITerminal, IBrowser } from './Types'; import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types'; import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, ITerminalOptions } from 'common/Types'; import { Buffer } from 'common/buffer/Buffer'; @@ -196,16 +196,6 @@ export class MockTerminal implements ITerminal { public deregisterCharacterJoiner(joinerId: number): void { } } -export class MockInputHandlingTerminal implements IInputHandlingTerminal { - public viewport: IViewport; - public is(term: string): boolean { - throw new Error('Method not implemented.'); - } - public resize(x: number, y: number): void { - throw new Error('Method not implemented.'); - } -} - export class MockBuffer implements IBuffer { public markers: IMarker[]; public addMarker(y: number): IMarker { diff --git a/src/Types.d.ts b/src/Types.d.ts index 7861fc7f..e9a2199d 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -15,15 +15,6 @@ export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean; export type LineData = CharData[]; -/** - * This interface encapsulates everything needed from the Terminal by the - * InputHandler. This cleanly separates the large amount of methods needed by - * InputHandler cleanly from the ITerminal interface. - */ -export interface IInputHandlingTerminal { - viewport: IViewport; -} - export interface ICompositionHelper { compositionstart(): void; compositionupdate(ev: CompositionEvent): void; From 81d289b266cbba584be752c010b4d1584d32ca4a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 26 Apr 2020 07:03:11 -0700 Subject: [PATCH 19/24] Remove RenderService dep in InputHandler --- src/InputHandler.test.ts | 18 +++++------ src/InputHandler.ts | 65 +++++++++++++++++++--------------------- src/Terminal.ts | 28 ++++++++++++++--- src/Types.d.ts | 2 +- 4 files changed, 65 insertions(+), 48 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index fd07fcb2..37a54e39 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -43,7 +43,7 @@ describe('InputHandler', () => { bufferService.buffer.x = 1; bufferService.buffer.y = 2; bufferService.buffer.ybase = 0; - const inputHandler = new TestInputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new TestInputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); inputHandler.curAttrData.fg = 3; // Save cursor position inputHandler.saveCursor(); @@ -63,7 +63,7 @@ describe('InputHandler', () => { describe('setCursorStyle', () => { it('should call Terminal.setOption with correct params', () => { const optionsService = new MockOptionsService(); - const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), optionsService, new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), optionsService, new MockCoreMouseService(), new MockUnicodeService()); inputHandler.setCursorStyle(Params.fromArray([0])); assert.equal(optionsService.options['cursorStyle'], 'block'); @@ -103,7 +103,7 @@ describe('InputHandler', () => { describe('setMode', () => { it('should toggle bracketedPasteMode', () => { const coreService = new MockCoreService(); - const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), coreService, new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), coreService, new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); // Set bracketed paste mode inputHandler.setModePrivate(Params.fromArray([2004])); assert.equal(coreService.decPrivateModes.bracketedPasteMode, true); @@ -121,7 +121,7 @@ describe('InputHandler', () => { it('insertChars', function(): void { const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -158,7 +158,7 @@ describe('InputHandler', () => { }); it('deleteChars', function(): void { const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -198,7 +198,7 @@ describe('InputHandler', () => { }); it('eraseInLine', function(): void { const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); // fill 6 lines to test 3 different states inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -226,7 +226,7 @@ describe('InputHandler', () => { }); it('eraseInDisplay', function(): void { const bufferService = new MockBufferService(80, 7); - const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); // fill display with a's for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -360,7 +360,7 @@ describe('InputHandler', () => { }); describe('print', () => { it('should not cause an infinite loop (regression test)', () => { - const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); const container = new Uint32Array(10); container[0] = 0x200B; inputHandler.print(container, 0, 1); @@ -373,7 +373,7 @@ describe('InputHandler', () => { beforeEach(() => { bufferService = new MockBufferService(80, 30); - handler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService(), {} as any); + handler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); }); it('should handle DECSET/DECRST 47 (alt screen buffer)', () => { handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST'); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index bf05d511..61ef5546 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -18,10 +18,9 @@ import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content import { CellData } from 'common/buffer/CellData'; import { AttributeData } from 'common/buffer/AttributeData'; import { IAttributeData, IDisposable, IWindowOptions } from 'common/Types'; -import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService, ICoreMouseService, ICharsetService, IUnicodeService, IInstantiationService } from 'common/services/Services'; +import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService, ICoreMouseService, ICharsetService, IUnicodeService } from 'common/services/Services'; import { OscHandler } from 'common/parser/OscParser'; import { DcsHandler } from 'common/parser/DcsParser'; -import { IRenderService } from 'browser/services/Services'; /** * Map collect to glevel. Used in `selectCharset`. @@ -94,7 +93,10 @@ function paramToWindowOption(n: number, opts: IWindowOptions): boolean { return false; } - +export enum WindowsOptionsReportType { + GET_WIN_SIZE_PIXELS = 0, + GET_CELL_SIZE_PIXELS = 1 +} /** * DCS subparser implementations @@ -224,28 +226,31 @@ export class InputHandler extends Disposable implements IInputHandler { private _curAttrData: IAttributeData = DEFAULT_ATTR_DATA.clone(); private _eraseAttrDataInternal: IAttributeData = DEFAULT_ATTR_DATA.clone(); + private _onRequestBell = new EventEmitter(); + public get onRequestBell(): IEvent { return this._onRequestBell.event; } private _onRequestRefreshRows = new EventEmitter(); public get onRequestRefreshRows(): IEvent { return this._onRequestRefreshRows.event; } private _onRequestReset = new EventEmitter(); public get onRequestReset(): IEvent { return this._onRequestReset.event; } - private _onRequestBell = new EventEmitter(); - public get onRequestBell(): IEvent { return this._onRequestBell.event; } + private _onRequestScroll = new EventEmitter(); + public get onRequestScroll(): IEvent { return this._onRequestScroll.event; } + private _onRequestSyncScrollBar = new EventEmitter(); + public get onRequestSyncScrollBar(): IEvent { return this._onRequestSyncScrollBar.event; } + private _onRequestWindowsOptionsReport = new EventEmitter(); + public get onRequestWindowsOptionsReport(): IEvent { return this._onRequestWindowsOptionsReport.event; } + + private _onA11yChar = new EventEmitter(); + public get onA11yChar(): IEvent { return this._onA11yChar.event; } + private _onA11yTab = new EventEmitter(); + public get onA11yTab(): IEvent { return this._onA11yTab.event; } private _onCursorMove = new EventEmitter(); public get onCursorMove(): IEvent { return this._onCursorMove.event; } private _onLineFeed = new EventEmitter(); public get onLineFeed(): IEvent { return this._onLineFeed.event; } private _onScroll = new EventEmitter(); public get onScroll(): IEvent { return this._onScroll.event; } - private _onScrollRequest = new EventEmitter(); - public get onScrollRequest(): IEvent { return this._onScrollRequest.event; } - private _onSyncScrollBarRequest = new EventEmitter(); - public get onSyncScrollBarRequest(): IEvent { return this._onSyncScrollBarRequest.event; } private _onTitleChange = new EventEmitter(); public get onTitleChange(): IEvent { return this._onTitleChange.event; } - private _onA11yChar = new EventEmitter(); - public get onA11yChar(): IEvent { return this._onA11yChar.event; } - private _onA11yTab = new EventEmitter(); - public get onA11yTab(): IEvent { return this._onA11yTab.event; } constructor( private readonly _bufferService: IBufferService, @@ -256,7 +261,6 @@ export class InputHandler extends Disposable implements IInputHandler { private readonly _optionsService: IOptionsService, private readonly _coreMouseService: ICoreMouseService, private readonly _unicodeService: IUnicodeService, - private readonly _instantiationService: IInstantiationService, private readonly _parser: IEscapeSequenceParser = new EscapeSequenceParser() ) { super(); @@ -557,7 +561,7 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.y++; if (buffer.y === buffer.scrollBottom + 1) { buffer.y--; - this._onScrollRequest.fire(this._eraseAttrData(), true); + this._onRequestScroll.fire(this._eraseAttrData(), true); } else { if (buffer.y >= this._bufferService.rows) { buffer.y = this._bufferService.rows - 1; @@ -696,7 +700,7 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.y++; if (buffer.y === buffer.scrollBottom + 1) { buffer.y--; - this._onScrollRequest.fire(this._eraseAttrData()); + this._onRequestScroll.fire(this._eraseAttrData()); } else if (buffer.y >= this._bufferService.rows) { buffer.y = this._bufferService.rows - 1; } @@ -1767,7 +1771,7 @@ export class InputHandler extends Disposable implements IInputHandler { case 66: this._logService.debug('Serial port requested application keypad.'); this._coreService.decPrivateModes.applicationKeypad = true; - this._onSyncScrollBarRequest.fire(); + this._onRequestSyncScrollBar.fire(); break; case 9: // X10 Mouse // no release, no motion, no wheel, no modifiers. @@ -1813,7 +1817,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._bufferService.buffers.activateAltBuffer(this._eraseAttrData()); this._coreService.isCursorInitialized = true; this._onRequestRefreshRows.fire(0, this._bufferService.rows - 1); - this._onSyncScrollBarRequest.fire(); + this._onRequestSyncScrollBar.fire(); break; case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste) this._coreService.decPrivateModes.bracketedPasteMode = true; @@ -1994,7 +1998,7 @@ export class InputHandler extends Disposable implements IInputHandler { case 66: this._logService.debug('Switching back to normal keypad.'); this._coreService.decPrivateModes.applicationKeypad = false; - this._onSyncScrollBarRequest.fire(); + this._onRequestSyncScrollBar.fire(); break; case 9: // X10 Mouse case 1000: // vt200 mouse @@ -2031,7 +2035,7 @@ export class InputHandler extends Disposable implements IInputHandler { } this._coreService.isCursorInitialized = true; this._onRequestRefreshRows.fire(0, this._bufferService.rows - 1); - this._onSyncScrollBarRequest.fire(); + this._onRequestSyncScrollBar.fire(); break; case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste) this._coreService.decPrivateModes.bracketedPasteMode = false; @@ -2370,7 +2374,7 @@ export class InputHandler extends Disposable implements IInputHandler { */ public softReset(params: IParams): void { this._coreService.isCursorHidden = false; - this._onSyncScrollBarRequest.fire(); + this._onRequestSyncScrollBar.fire(); this._bufferService.buffer.scrollTop = 0; this._bufferService.buffer.scrollBottom = this._bufferService.rows - 1; this._curAttrData = DEFAULT_ATTR_DATA.clone(); @@ -2484,21 +2488,14 @@ export class InputHandler extends Disposable implements IInputHandler { return; } const second = (params.length > 1) ? params.params[1] : 0; - const rs = this._instantiationService.getService(IRenderService); switch (params.params[0]) { case 14: // GetWinSizePixels, returns CSI 4 ; height ; width t - if (rs && second !== 2) { - const w = rs.dimensions.scaledCanvasWidth.toFixed(0); - const h = rs.dimensions.scaledCanvasHeight.toFixed(0); - this._coreService.triggerDataEvent(`${C0.ESC}[4;${h};${w}t`); + if (second !== 2) { + this._onRequestWindowsOptionsReport.fire(WindowsOptionsReportType.GET_WIN_SIZE_PIXELS); } break; case 16: // GetCellSizePixels, returns CSI 6 ; height ; width t - if (rs) { - const w = rs.dimensions.scaledCellWidth.toFixed(0); - const h = rs.dimensions.scaledCellHeight.toFixed(0); - this._coreService.triggerDataEvent(`${C0.ESC}[6;${h};${w}t`); - } + this._onRequestWindowsOptionsReport.fire(WindowsOptionsReportType.GET_CELL_SIZE_PIXELS); break; case 18: // GetWinSizeChars, returns CSI 8 ; height ; width t if (this._bufferService) { @@ -2618,7 +2615,7 @@ export class InputHandler extends Disposable implements IInputHandler { public keypadApplicationMode(): void { this._logService.debug('Serial port requested application keypad.'); this._coreService.decPrivateModes.applicationKeypad = true; - this._onSyncScrollBarRequest.fire(); + this._onRequestSyncScrollBar.fire(); } /** @@ -2629,7 +2626,7 @@ export class InputHandler extends Disposable implements IInputHandler { public keypadNumericMode(): void { this._logService.debug('Switching back to normal keypad.'); this._coreService.decPrivateModes.applicationKeypad = false; - this._onSyncScrollBarRequest.fire(); + this._onRequestSyncScrollBar.fire(); } /** @@ -2686,7 +2683,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._bufferService.buffer.y++; if (buffer.y === buffer.scrollBottom + 1) { buffer.y--; - this._onScrollRequest.fire(this._eraseAttrData()); + this._onRequestScroll.fire(this._eraseAttrData()); } else if (buffer.y >= this._bufferService.rows) { buffer.y = this._bufferService.rows - 1; } diff --git a/src/Terminal.ts b/src/Terminal.ts index 4606f1aa..e3af555b 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -27,7 +27,7 @@ import { CompositionHelper } from 'browser/input/CompositionHelper'; import { Viewport } from 'browser/Viewport'; import { rightClickHandler, moveTextAreaUnderMouseCursor, handlePasteEvent, copyHandler, paste } from 'browser/Clipboard'; import { C0 } from 'common/data/EscapeSequences'; -import { InputHandler } from './InputHandler'; +import { InputHandler, WindowsOptionsReportType } from './InputHandler'; import { Renderer } from 'browser/renderer/Renderer'; import { Linkifier } from 'browser/Linkifier'; import { SelectionService } from 'browser/services/SelectionService'; @@ -176,11 +176,12 @@ export class Terminal extends CoreTerminal implements ITerminal { this._inputHandler.reset(); } else { // Register input handler and refire/handle events - this._inputHandler = new InputHandler(this._bufferService, this._charsetService, this._coreService, this._dirtyRowService, this._logService, this.optionsService, this._coreMouseService, this.unicodeService, this._instantiationService); + this._inputHandler = new InputHandler(this._bufferService, this._charsetService, this._coreService, this._dirtyRowService, this._logService, this.optionsService, this._coreMouseService, this.unicodeService); this.register(this._inputHandler.onRequestBell(() => this.bell())); this.register(this._inputHandler.onRequestRefreshRows((start, end) => this.refresh(start, end))); this.register(this._inputHandler.onRequestReset(() => this.reset())); - this.register(this._inputHandler.onScrollRequest((eraseAttr, isWrapped) => this.scroll(eraseAttr, isWrapped || undefined))); + this.register(this._inputHandler.onRequestScroll((eraseAttr, isWrapped) => this.scroll(eraseAttr, isWrapped || undefined))); + this.register(this._inputHandler.onRequestWindowsOptionsReport(type => this._reportWindowsOptions(type))); this.register(forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove)); this.register(forwardEvent(this._inputHandler.onLineFeed, this._onLineFeed)); this.register(forwardEvent(this._inputHandler.onTitleChange, this._onTitleChange)); @@ -462,7 +463,7 @@ export class Terminal extends CoreTerminal implements ITerminal { this._viewportScrollArea ); this.viewport.onThemeChange(this._colorManager.colors); - this.register(this._inputHandler.onSyncScrollBarRequest(() => this.viewport.syncScrollArea())); + this.register(this._inputHandler.onRequestSyncScrollBar(() => this.viewport.syncScrollArea())); this.register(this.viewport); this.register(this.onCursorMove(() => this._renderService.onCursorMove())); @@ -1351,6 +1352,25 @@ export class Terminal extends CoreTerminal implements ITerminal { this.viewport?.syncScrollArea(); } + private _reportWindowsOptions(type: WindowsOptionsReportType): void { + if (!this._renderService) { + return; + } + + switch (type) { + case WindowsOptionsReportType.GET_WIN_SIZE_PIXELS: + const canvasWidth = this._renderService.dimensions.scaledCanvasWidth.toFixed(0); + const canvasHeight = this._renderService.dimensions.scaledCanvasHeight.toFixed(0); + this._coreService.triggerDataEvent(`${C0.ESC}[4;${canvasHeight};${canvasWidth}t`); + break; + case WindowsOptionsReportType.GET_CELL_SIZE_PIXELS: + const cellWidth = this._renderService.dimensions.scaledCellWidth.toFixed(0); + const cellHeight = this._renderService.dimensions.scaledCellHeight.toFixed(0); + this._coreService.triggerDataEvent(`${C0.ESC}[6;${cellHeight};${cellWidth}t`); + break; + } + } + // TODO: Remove cancel function and cancelEvents option public cancel(ev: Event, force?: boolean): boolean { if (!this.options.cancelEvents && !force) { diff --git a/src/Types.d.ts b/src/Types.d.ts index e9a2199d..2f57e99c 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -28,7 +28,7 @@ export interface ICompositionHelper { */ export interface IInputHandler { onTitleChange: IEvent; - onScrollRequest: IEvent; + onRequestScroll: IEvent; parse(data: string | Uint8Array): void; print(data: Uint32Array, start: number, end: number): void; From d756086b8edc0fc31582499012d7a50dd85f6c3f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 26 Apr 2020 07:24:30 -0700 Subject: [PATCH 20/24] Move InputHandler into common Part of #2749 Part of #1507 --- src/InputHandler.test.ts | 2 +- src/Terminal.ts | 2 +- src/Types.d.ts | 85 -------------------------- src/{ => common}/InputHandler.ts | 36 +++++------ src/common/TestUtils.test.ts | 1 - src/common/Types.d.ts | 86 +++++++++++++++++++++++++++ src/common/buffer/Buffer.ts | 2 +- src/common/buffer/Types.d.ts | 2 +- src/common/data/Charsets.ts | 6 +- src/common/services/CharsetService.ts | 11 ++-- src/common/services/Services.ts | 3 +- 11 files changed, 119 insertions(+), 117 deletions(-) rename src/{ => common}/InputHandler.ts (99%) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 37a54e39..5a3dc2b5 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -4,7 +4,7 @@ */ import { assert, expect } from 'chai'; -import { InputHandler } from './InputHandler'; +import { InputHandler } from './common/InputHandler'; import { TestTerminal } from './TestUtils.test'; import { Terminal } from './Terminal'; import { IBufferLine, IAttributeData } from 'common/Types'; diff --git a/src/Terminal.ts b/src/Terminal.ts index e3af555b..61c997bd 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -27,7 +27,7 @@ import { CompositionHelper } from 'browser/input/CompositionHelper'; import { Viewport } from 'browser/Viewport'; import { rightClickHandler, moveTextAreaUnderMouseCursor, handlePasteEvent, copyHandler, paste } from 'browser/Clipboard'; import { C0 } from 'common/data/EscapeSequences'; -import { InputHandler, WindowsOptionsReportType } from './InputHandler'; +import { InputHandler, WindowsOptionsReportType } from './common/InputHandler'; import { Renderer } from 'browser/renderer/Renderer'; import { Linkifier } from 'browser/Linkifier'; import { SelectionService } from 'browser/services/SelectionService'; diff --git a/src/Types.d.ts b/src/Types.d.ts index 2f57e99c..05c89e15 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -23,91 +23,6 @@ export interface ICompositionHelper { keydown(ev: KeyboardEvent): boolean; } -/** - * Calls the parser and handles actions generated by the parser. - */ -export interface IInputHandler { - onTitleChange: IEvent; - onRequestScroll: IEvent; - - parse(data: string | Uint8Array): void; - print(data: Uint32Array, start: number, end: number): void; - - /** C0 BEL */ bell(): void; - /** C0 LF */ lineFeed(): void; - /** C0 CR */ carriageReturn(): void; - /** C0 BS */ backspace(): void; - /** C0 HT */ tab(): void; - /** C0 SO */ shiftOut(): void; - /** C0 SI */ shiftIn(): void; - - /** CSI @ */ insertChars(params: IParams): void; - /** CSI SP @ */ scrollLeft(params: IParams): void; - /** CSI A */ cursorUp(params: IParams): void; - /** CSI SP A */ scrollRight(params: IParams): void; - /** CSI B */ cursorDown(params: IParams): void; - /** CSI C */ cursorForward(params: IParams): void; - /** CSI D */ cursorBackward(params: IParams): void; - /** CSI E */ cursorNextLine(params: IParams): void; - /** CSI F */ cursorPrecedingLine(params: IParams): void; - /** CSI G */ cursorCharAbsolute(params: IParams): void; - /** CSI H */ cursorPosition(params: IParams): void; - /** CSI I */ cursorForwardTab(params: IParams): void; - /** CSI J */ eraseInDisplay(params: IParams): void; - /** CSI K */ eraseInLine(params: IParams): void; - /** CSI L */ insertLines(params: IParams): void; - /** CSI M */ deleteLines(params: IParams): void; - /** CSI P */ deleteChars(params: IParams): void; - /** CSI S */ scrollUp(params: IParams): void; - /** CSI T */ scrollDown(params: IParams, collect?: string): void; - /** CSI X */ eraseChars(params: IParams): void; - /** CSI Z */ cursorBackwardTab(params: IParams): void; - /** CSI ` */ charPosAbsolute(params: IParams): void; - /** CSI a */ hPositionRelative(params: IParams): void; - /** CSI b */ repeatPrecedingCharacter(params: IParams): void; - /** CSI c */ sendDeviceAttributesPrimary(params: IParams): void; - /** CSI > c */ sendDeviceAttributesSecondary(params: IParams): void; - /** CSI d */ linePosAbsolute(params: IParams): void; - /** CSI e */ vPositionRelative(params: IParams): void; - /** CSI f */ hVPosition(params: IParams): void; - /** CSI g */ tabClear(params: IParams): void; - /** CSI h */ setMode(params: IParams, collect?: string): void; - /** CSI l */ resetMode(params: IParams, collect?: string): void; - /** CSI m */ charAttributes(params: IParams): void; - /** CSI n */ deviceStatus(params: IParams, collect?: string): void; - /** CSI p */ softReset(params: IParams, collect?: string): void; - /** CSI q */ setCursorStyle(params: IParams, collect?: string): void; - /** CSI r */ setScrollRegion(params: IParams, collect?: string): void; - /** CSI s */ saveCursor(params: IParams): void; - /** CSI u */ restoreCursor(params: IParams): void; - /** CSI ' } */ insertColumns(params: IParams): void; - /** CSI ' ~ */ deleteColumns(params: IParams): void; - /** OSC 0 - OSC 2 */ setTitle(data: string): void; - /** ESC E */ nextLine(): void; - /** ESC = */ keypadApplicationMode(): void; - /** ESC > */ keypadNumericMode(): void; - /** ESC % G - ESC % @ */ selectDefaultCharset(): void; - /** ESC ( C - ESC ) C - ESC * C - ESC + C - ESC - C - ESC . C - ESC / C */ selectCharset(collectAndFlag: string): void; - /** ESC D */ index(): void; - /** ESC H */ tabSet(): void; - /** ESC M */ reverseIndex(): void; - /** ESC c */ fullReset(): void; - /** ESC n - ESC o - ESC | - ESC } - ESC ~ */ setgLevel(level: number): void; - /** ESC # 8 */ screenAlignmentPattern(): void; -} - export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAccessor, ILinkifierAccessor { screenElement: HTMLElement; browser: IBrowser; diff --git a/src/InputHandler.ts b/src/common/InputHandler.ts similarity index 99% rename from src/InputHandler.ts rename to src/common/InputHandler.ts index 61ef5546..70ec8859 100644 --- a/src/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -4,7 +4,7 @@ * @license MIT */ -import { IInputHandler } from './Types'; +import { IInputHandler, IAttributeData, IDisposable, IWindowOptions } from 'common/Types'; import { C0, C1 } from 'common/data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets'; import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser'; @@ -17,7 +17,6 @@ import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IFunctionId import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content } from 'common/buffer/Constants'; import { CellData } from 'common/buffer/CellData'; import { AttributeData } from 'common/buffer/AttributeData'; -import { IAttributeData, IDisposable, IWindowOptions } from 'common/Types'; import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService, ICoreMouseService, ICharsetService, IUnicodeService } from 'common/services/Services'; import { OscHandler } from 'common/parser/OscParser'; import { DcsHandler } from 'common/parser/DcsParser'; @@ -504,7 +503,7 @@ export class InputHandler extends Disposable implements IInputHandler { const wraparoundMode = this._coreService.decPrivateModes.wraparound; const insertMode = this._coreService.modes.insertMode; const curAttr = this._curAttrData; - let bufferRow = buffer.lines.get(buffer.y + buffer.ybase); + let bufferRow = buffer.lines.get(buffer.y + buffer.ybase)!; this._dirtyRowService.markDirty(buffer.y); @@ -568,10 +567,10 @@ export class InputHandler extends Disposable implements IInputHandler { } // The line already exists (eg. the initial viewport), mark it as a // wrapped line - buffer.lines.get(buffer.y).isWrapped = true; + buffer.lines.get(buffer.y)!.isWrapped = true; } // row changed, get it again - bufferRow = buffer.lines.get(buffer.y + buffer.ybase); + bufferRow = buffer.lines.get(buffer.y + buffer.ybase)!; } else { buffer.x = cols - 1; if (chWidth === 2) { @@ -1037,7 +1036,7 @@ export class InputHandler extends Disposable implements IInputHandler { * @param end end - 1 is last erased cell */ private _eraseInBufferLine(y: number, start: number, end: number, clearWrap: boolean = false): void { - const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y); + const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y)!; line.replaceCells( start, end, @@ -1055,7 +1054,7 @@ export class InputHandler extends Disposable implements IInputHandler { * @param y row index */ private _resetBufferLine(y: number): void { - const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y); + const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y)!; line.fill(this._bufferService.buffer.getNullCell(this._eraseAttrData())); line.isWrapped = false; } @@ -1104,7 +1103,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._eraseInBufferLine(j, 0, this._bufferService.buffer.x + 1, true); if (this._bufferService.buffer.x + 1 >= this._bufferService.cols) { // Deleted entire previous line. This next line can no longer be wrapped. - this._bufferService.buffer.lines.get(j + 1).isWrapped = false; + this._bufferService.buffer.lines.get(j + 1)!.isWrapped = false; } while (j--) { this._resetBufferLine(j); @@ -1356,7 +1355,7 @@ export class InputHandler extends Disposable implements IInputHandler { } const param = params.params[0] || 1; for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) { - const line = buffer.lines.get(buffer.ybase + y); + const line = buffer.lines.get(buffer.ybase + y)!; line.deleteCells(0, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData()); line.isWrapped = false; } @@ -1389,7 +1388,7 @@ export class InputHandler extends Disposable implements IInputHandler { } const param = params.params[0] || 1; for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) { - const line = buffer.lines.get(buffer.ybase + y); + const line = buffer.lines.get(buffer.ybase + y)!; line.insertCells(0, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData()); line.isWrapped = false; } @@ -1412,7 +1411,7 @@ export class InputHandler extends Disposable implements IInputHandler { } const param = params.params[0] || 1; for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) { - const line = this._bufferService.buffer.lines.get(buffer.ybase + y); + const line = this._bufferService.buffer.lines.get(buffer.ybase + y)!; line.insertCells(buffer.x, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData()); line.isWrapped = false; } @@ -1435,7 +1434,7 @@ export class InputHandler extends Disposable implements IInputHandler { } const param = params.params[0] || 1; for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) { - const line = buffer.lines.get(buffer.ybase + y); + const line = buffer.lines.get(buffer.ybase + y)!; line.deleteCells(buffer.x, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData()); line.isWrapped = false; } @@ -2064,7 +2063,7 @@ export class InputHandler extends Disposable implements IInputHandler { do { accu[advance + cSpace] = params.params[pos + advance]; if (params.hasSubParams(pos + advance)) { - const subparams = params.getSubParams(pos + advance); + const subparams = params.getSubParams(pos + advance)!; let i = 0; do { if (accu[1] === 5) { @@ -2519,12 +2518,12 @@ export class InputHandler extends Disposable implements IInputHandler { case 23: // PopTitle if (second === 0 || second === 2) { if (this._windowTitleStack.length) { - this.setTitle(this._windowTitleStack.pop()); + this.setTitle(this._windowTitleStack.pop()!); } } if (second === 0 || second === 1) { if (this._iconNameStack.length) { - this.setIconName(this._iconNameStack.pop()); + this.setIconName(this._iconNameStack.pop()!); } } break; @@ -2788,8 +2787,11 @@ export class InputHandler extends Disposable implements IInputHandler { this._setCursor(0, 0); for (let yOffset = 0; yOffset < this._bufferService.rows; ++yOffset) { const row = buffer.y + buffer.ybase + yOffset; - buffer.lines.get(row).fill(cell); - buffer.lines.get(row).isWrapped = false; + const line = buffer.lines.get(row); + if (line) { + line.fill(cell); + line.isWrapped = false; + } } this._dirtyRowService.markAllDirty(); this._setCursor(0, 0); diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index eacf92fe..55e34084 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -49,7 +49,6 @@ export class MockCharsetService implements ICharsetService { public serviceBrand: any; public charset: ICharset | undefined; public glevel: number = 0; - public charsets: ReadonlyArray = []; public reset(): void {} public setgLevel(g: number): void {} public setgCharset(g: number, charset: ICharset): void {} diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index cc7928c3..49a723b2 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -6,6 +6,7 @@ import { ITerminalOptions as IPublicTerminalOptions } from 'xterm'; import { IEvent, IEventEmitter } from 'common/EventEmitter'; import { IDeleteEvent, IInsertEvent } from 'common/CircularList'; +import { IParams } from 'common/parser/Types'; export interface IDisposable { dispose(): void; @@ -300,3 +301,88 @@ export interface IWindowOptions { popTitle?: boolean; setWinLines?: boolean; } + +/** + * Calls the parser and handles actions generated by the parser. + */ +export interface IInputHandler { + onTitleChange: IEvent; + onRequestScroll: IEvent; + + parse(data: string | Uint8Array): void; + print(data: Uint32Array, start: number, end: number): void; + + /** C0 BEL */ bell(): void; + /** C0 LF */ lineFeed(): void; + /** C0 CR */ carriageReturn(): void; + /** C0 BS */ backspace(): void; + /** C0 HT */ tab(): void; + /** C0 SO */ shiftOut(): void; + /** C0 SI */ shiftIn(): void; + + /** CSI @ */ insertChars(params: IParams): void; + /** CSI SP @ */ scrollLeft(params: IParams): void; + /** CSI A */ cursorUp(params: IParams): void; + /** CSI SP A */ scrollRight(params: IParams): void; + /** CSI B */ cursorDown(params: IParams): void; + /** CSI C */ cursorForward(params: IParams): void; + /** CSI D */ cursorBackward(params: IParams): void; + /** CSI E */ cursorNextLine(params: IParams): void; + /** CSI F */ cursorPrecedingLine(params: IParams): void; + /** CSI G */ cursorCharAbsolute(params: IParams): void; + /** CSI H */ cursorPosition(params: IParams): void; + /** CSI I */ cursorForwardTab(params: IParams): void; + /** CSI J */ eraseInDisplay(params: IParams): void; + /** CSI K */ eraseInLine(params: IParams): void; + /** CSI L */ insertLines(params: IParams): void; + /** CSI M */ deleteLines(params: IParams): void; + /** CSI P */ deleteChars(params: IParams): void; + /** CSI S */ scrollUp(params: IParams): void; + /** CSI T */ scrollDown(params: IParams, collect?: string): void; + /** CSI X */ eraseChars(params: IParams): void; + /** CSI Z */ cursorBackwardTab(params: IParams): void; + /** CSI ` */ charPosAbsolute(params: IParams): void; + /** CSI a */ hPositionRelative(params: IParams): void; + /** CSI b */ repeatPrecedingCharacter(params: IParams): void; + /** CSI c */ sendDeviceAttributesPrimary(params: IParams): void; + /** CSI > c */ sendDeviceAttributesSecondary(params: IParams): void; + /** CSI d */ linePosAbsolute(params: IParams): void; + /** CSI e */ vPositionRelative(params: IParams): void; + /** CSI f */ hVPosition(params: IParams): void; + /** CSI g */ tabClear(params: IParams): void; + /** CSI h */ setMode(params: IParams, collect?: string): void; + /** CSI l */ resetMode(params: IParams, collect?: string): void; + /** CSI m */ charAttributes(params: IParams): void; + /** CSI n */ deviceStatus(params: IParams, collect?: string): void; + /** CSI p */ softReset(params: IParams, collect?: string): void; + /** CSI q */ setCursorStyle(params: IParams, collect?: string): void; + /** CSI r */ setScrollRegion(params: IParams, collect?: string): void; + /** CSI s */ saveCursor(params: IParams): void; + /** CSI u */ restoreCursor(params: IParams): void; + /** CSI ' } */ insertColumns(params: IParams): void; + /** CSI ' ~ */ deleteColumns(params: IParams): void; + /** OSC 0 + OSC 2 */ setTitle(data: string): void; + /** ESC E */ nextLine(): void; + /** ESC = */ keypadApplicationMode(): void; + /** ESC > */ keypadNumericMode(): void; + /** ESC % G + ESC % @ */ selectDefaultCharset(): void; + /** ESC ( C + ESC ) C + ESC * C + ESC + C + ESC - C + ESC . C + ESC / C */ selectCharset(collectAndFlag: string): void; + /** ESC D */ index(): void; + /** ESC H */ tabSet(): void; + /** ESC M */ reverseIndex(): void; + /** ESC c */ fullReset(): void; + /** ESC n + ESC o + ESC | + ESC } + ESC ~ */ setgLevel(level: number): void; + /** ESC # 8 */ screenAlignmentPattern(): void; +} diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index 152bab7a..1de39dbd 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -36,7 +36,7 @@ export class Buffer implements IBuffer { public savedY: number = 0; public savedX: number = 0; public savedCurAttrData = DEFAULT_ATTR_DATA.clone(); - public savedCharset: ICharset | null = DEFAULT_CHARSET; + public savedCharset: ICharset | undefined = DEFAULT_CHARSET; public markers: Marker[] = []; private _nullCell: ICellData = CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]); private _whitespaceCell: ICellData = CellData.fromCharData([0, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_WIDTH, WHITESPACE_CELL_CODE]); diff --git a/src/common/buffer/Types.d.ts b/src/common/buffer/Types.d.ts index e229d69c..c271f33c 100644 --- a/src/common/buffer/Types.d.ts +++ b/src/common/buffer/Types.d.ts @@ -31,7 +31,7 @@ export interface IBuffer { hasScrollback: boolean; savedY: number; savedX: number; - savedCharset: ICharset | null; + savedCharset: ICharset | undefined; savedCurAttrData: IAttributeData; isCursorInViewport: boolean; markers: IMarker[]; diff --git a/src/common/data/Charsets.ts b/src/common/data/Charsets.ts index 01087d8b..c72d5a23 100644 --- a/src/common/data/Charsets.ts +++ b/src/common/data/Charsets.ts @@ -10,12 +10,12 @@ import { ICharset } from 'common/Types'; * to be represented within the terminal with only 8-bit encoding. See ISO 2022 * for a discussion on character sets. Only VT100 character sets are supported. */ -export const CHARSETS: { [key: string]: ICharset | null } = {}; +export const CHARSETS: { [key: string]: ICharset | undefined } = {}; /** * The default character set, US. */ -export const DEFAULT_CHARSET: ICharset | null = CHARSETS['B']; +export const DEFAULT_CHARSET: ICharset | undefined = CHARSETS['B']; /** * DEC Special Character and Line Drawing Set. @@ -74,7 +74,7 @@ CHARSETS['A'] = { * United States character set * ESC (B */ -CHARSETS['B'] = null; +CHARSETS['B'] = undefined; /** * Dutch character set diff --git a/src/common/services/CharsetService.ts b/src/common/services/CharsetService.ts index 47a015f6..c5381065 100644 --- a/src/common/services/CharsetService.ts +++ b/src/common/services/CharsetService.ts @@ -10,22 +10,23 @@ export class CharsetService implements ICharsetService { public serviceBrand: any; public charset: ICharset | undefined; - public charsets: ICharset[] = []; public glevel: number = 0; + private _charsets: (ICharset | undefined)[] = []; + public reset(): void { this.charset = undefined; - this.charsets = []; + this._charsets = []; this.glevel = 0; } public setgLevel(g: number): void { this.glevel = g; - this.charset = this.charsets[g]; + this.charset = this._charsets[g]; } - public setgCharset(g: number, charset: ICharset): void { - this.charsets[g] = charset; + public setgCharset(g: number, charset: ICharset | undefined): void { + this._charsets[g] = charset; if (this.glevel === g) { this.charset = charset; } diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 8fa3f57b..6829d7b9 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -98,7 +98,6 @@ export interface ICharsetService { charset: ICharset | undefined; readonly glevel: number; - readonly charsets: ReadonlyArray; reset(): void; @@ -113,7 +112,7 @@ export interface ICharsetService { * @param g * @param charset */ - setgCharset(g: number, charset: ICharset): void; + setgCharset(g: number, charset: ICharset | undefined): void; } export const IDirtyRowService = createDecorator('DirtyRowService'); From c9313412594421303dd26d263478bac05e28ebb5 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 26 Apr 2020 08:08:49 -0700 Subject: [PATCH 21/24] Remove Terminal dep in InputHandler.test --- src/InputHandler.test.ts | 1561 +++++++++++++++++------------------- src/Terminal.test.ts | 84 ++ src/common/InputHandler.ts | 4 +- 3 files changed, 836 insertions(+), 813 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 5a3dc2b5..455fc754 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -5,8 +5,6 @@ import { assert, expect } from 'chai'; import { InputHandler } from './common/InputHandler'; -import { TestTerminal } from './TestUtils.test'; -import { Terminal } from './Terminal'; import { IBufferLine, IAttributeData } from 'common/Types'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; @@ -14,36 +12,52 @@ import { Attributes } from 'common/buffer/Constants'; import { AttributeData } from 'common/buffer/AttributeData'; import { Params } from 'common/parser/Params'; import { MockCoreService, MockBufferService, MockDirtyRowService, MockOptionsService, MockLogService, MockCoreMouseService, MockCharsetService, MockUnicodeService } from 'common/TestUtils.test'; -import { IBufferService } from 'common/services/Services'; +import { IBufferService, ICoreService } from 'common/services/Services'; import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; import { clone } from 'common/Clone'; +import { BufferService } from 'common/services/BufferService'; +import { CoreService } from 'common/services/CoreService'; -function getCursor(term: TestTerminal): number[] { +function getCursor(bufferService: IBufferService): number[] { return [ - term.buffer.x, - term.buffer.y + bufferService.buffer.x, + bufferService.buffer.y ]; } -function getLines(term: TestTerminal, limit: number = term.rows): string[] { +function getLines(bufferService: IBufferService, limit: number = bufferService.rows): string[] { const res: string[] = []; for (let i = 0; i < limit; ++i) { - res.push(term.buffer.lines.get(i).translateToString(true)); + res.push(bufferService.buffer.lines.get(i).translateToString(true)); } return res; } class TestInputHandler extends InputHandler { public get curAttrData(): IAttributeData { return (this as any)._curAttrData; } + public get windowTitleStack(): string[] { return this._windowTitleStack; } + public get iconNameStack(): string[] { return this._iconNameStack; } } describe('InputHandler', () => { - describe('save and restore cursor', () => { - const bufferService = new MockBufferService(80, 30); + let bufferService: IBufferService; + let coreService: ICoreService; + let optionsService: MockOptionsService; + let inputHandler: TestInputHandler; + + beforeEach(() => { + optionsService = new MockOptionsService(); + bufferService = new BufferService(optionsService); + bufferService.resize(80, 30); + coreService = new CoreService(() => {}, bufferService, new MockLogService(), optionsService); + + inputHandler = new TestInputHandler(bufferService, new MockCharsetService(), coreService, new MockDirtyRowService(), new MockLogService(), optionsService, new MockCoreMouseService(), new MockUnicodeService()); + }); + + it('save and restore cursor', () => { bufferService.buffer.x = 1; bufferService.buffer.y = 2; bufferService.buffer.ybase = 0; - const inputHandler = new TestInputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); inputHandler.curAttrData.fg = 3; // Save cursor position inputHandler.saveCursor(); @@ -62,9 +76,6 @@ describe('InputHandler', () => { }); describe('setCursorStyle', () => { it('should call Terminal.setOption with correct params', () => { - const optionsService = new MockOptionsService(); - const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), optionsService, new MockCoreMouseService(), new MockUnicodeService()); - inputHandler.setCursorStyle(Params.fromArray([0])); assert.equal(optionsService.options['cursorStyle'], 'block'); assert.equal(optionsService.options['cursorBlink'], true); @@ -341,23 +352,6 @@ describe('InputHandler', () => { expect(bufferService.buffer.lines.get(2).isWrapped).false; }); }); - it('convertEol setting', function(): void { - // not converting - const termNotConverting = new Terminal({cols: 15, rows: 10}); - (termNotConverting as any)._inputHandler.parse('Hello\nWorld'); - expect(termNotConverting.buffer.lines.get(0).translateToString(false)).equals('Hello '); - expect(termNotConverting.buffer.lines.get(1).translateToString(false)).equals(' World '); - expect(termNotConverting.buffer.lines.get(0).translateToString(true)).equals('Hello'); - expect(termNotConverting.buffer.lines.get(1).translateToString(true)).equals(' World'); - - // converting - const termConverting = new Terminal({cols: 15, rows: 10, convertEol: true}); - (termConverting as any)._inputHandler.parse('Hello\nWorld'); - expect(termConverting.buffer.lines.get(0).translateToString(false)).equals('Hello '); - expect(termConverting.buffer.lines.get(1).translateToString(false)).equals('World '); - expect(termConverting.buffer.lines.get(0).translateToString(true)).equals('Hello'); - expect(termConverting.buffer.lines.get(1).translateToString(true)).equals('World'); - }); describe('print', () => { it('should not cause an infinite loop (regression test)', () => { const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); @@ -423,837 +417,772 @@ describe('InputHandler', () => { }); describe('text attributes', () => { - let term: TestTerminal; - beforeEach(() => { - term = new TestTerminal(); - }); it('bold', () => { - term.writeSync('\x1b[1m'); - assert.equal(!!term.curAttrData.isBold(), true); - term.writeSync('\x1b[22m'); - assert.equal(!!term.curAttrData.isBold(), false); + inputHandler.parse('\x1b[1m'); + assert.equal(!!inputHandler.curAttrData.isBold(), true); + inputHandler.parse('\x1b[22m'); + assert.equal(!!inputHandler.curAttrData.isBold(), false); }); it('dim', () => { - term.writeSync('\x1b[2m'); - assert.equal(!!term.curAttrData.isDim(), true); - term.writeSync('\x1b[22m'); - assert.equal(!!term.curAttrData.isDim(), false); + inputHandler.parse('\x1b[2m'); + assert.equal(!!inputHandler.curAttrData.isDim(), true); + inputHandler.parse('\x1b[22m'); + assert.equal(!!inputHandler.curAttrData.isDim(), false); }); it('italic', () => { - term.writeSync('\x1b[3m'); - assert.equal(!!term.curAttrData.isItalic(), true); - term.writeSync('\x1b[23m'); - assert.equal(!!term.curAttrData.isItalic(), false); + inputHandler.parse('\x1b[3m'); + assert.equal(!!inputHandler.curAttrData.isItalic(), true); + inputHandler.parse('\x1b[23m'); + assert.equal(!!inputHandler.curAttrData.isItalic(), false); }); it('underline', () => { - term.writeSync('\x1b[4m'); - assert.equal(!!term.curAttrData.isUnderline(), true); - term.writeSync('\x1b[24m'); - assert.equal(!!term.curAttrData.isUnderline(), false); + inputHandler.parse('\x1b[4m'); + assert.equal(!!inputHandler.curAttrData.isUnderline(), true); + inputHandler.parse('\x1b[24m'); + assert.equal(!!inputHandler.curAttrData.isUnderline(), false); }); it('blink', () => { - term.writeSync('\x1b[5m'); - assert.equal(!!term.curAttrData.isBlink(), true); - term.writeSync('\x1b[25m'); - assert.equal(!!term.curAttrData.isBlink(), false); + inputHandler.parse('\x1b[5m'); + assert.equal(!!inputHandler.curAttrData.isBlink(), true); + inputHandler.parse('\x1b[25m'); + assert.equal(!!inputHandler.curAttrData.isBlink(), false); }); it('inverse', () => { - term.writeSync('\x1b[7m'); - assert.equal(!!term.curAttrData.isInverse(), true); - term.writeSync('\x1b[27m'); - assert.equal(!!term.curAttrData.isInverse(), false); + inputHandler.parse('\x1b[7m'); + assert.equal(!!inputHandler.curAttrData.isInverse(), true); + inputHandler.parse('\x1b[27m'); + assert.equal(!!inputHandler.curAttrData.isInverse(), false); }); it('invisible', () => { - term.writeSync('\x1b[8m'); - assert.equal(!!term.curAttrData.isInvisible(), true); - term.writeSync('\x1b[28m'); - assert.equal(!!term.curAttrData.isInvisible(), false); + inputHandler.parse('\x1b[8m'); + assert.equal(!!inputHandler.curAttrData.isInvisible(), true); + inputHandler.parse('\x1b[28m'); + assert.equal(!!inputHandler.curAttrData.isInvisible(), false); }); it('colormode palette 16', () => { - assert.equal(term.curAttrData.getFgColorMode(), 0); // DEFAULT - assert.equal(term.curAttrData.getBgColorMode(), 0); // DEFAULT + assert.equal(inputHandler.curAttrData.getFgColorMode(), 0); // DEFAULT + assert.equal(inputHandler.curAttrData.getBgColorMode(), 0); // DEFAULT // lower 8 colors for (let i = 0; i < 8; ++i) { - term.writeSync(`\x1b[${i + 30};${i + 40}m`); - assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_P16); - assert.equal(term.curAttrData.getFgColor(), i); - assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_P16); - assert.equal(term.curAttrData.getBgColor(), i); + inputHandler.parse(`\x1b[${i + 30};${i + 40}m`); + assert.equal(inputHandler.curAttrData.getFgColorMode(), Attributes.CM_P16); + assert.equal(inputHandler.curAttrData.getFgColor(), i); + assert.equal(inputHandler.curAttrData.getBgColorMode(), Attributes.CM_P16); + assert.equal(inputHandler.curAttrData.getBgColor(), i); } // reset to DEFAULT - term.writeSync(`\x1b[39;49m`); - assert.equal(term.curAttrData.getFgColorMode(), 0); - assert.equal(term.curAttrData.getBgColorMode(), 0); + inputHandler.parse(`\x1b[39;49m`); + assert.equal(inputHandler.curAttrData.getFgColorMode(), 0); + assert.equal(inputHandler.curAttrData.getBgColorMode(), 0); }); it('colormode palette 256', () => { - assert.equal(term.curAttrData.getFgColorMode(), 0); // DEFAULT - assert.equal(term.curAttrData.getBgColorMode(), 0); // DEFAULT + assert.equal(inputHandler.curAttrData.getFgColorMode(), 0); // DEFAULT + assert.equal(inputHandler.curAttrData.getBgColorMode(), 0); // DEFAULT // lower 8 colors for (let i = 0; i < 256; ++i) { - term.writeSync(`\x1b[38;5;${i};48;5;${i}m`); - assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_P256); - assert.equal(term.curAttrData.getFgColor(), i); - assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_P256); - assert.equal(term.curAttrData.getBgColor(), i); + inputHandler.parse(`\x1b[38;5;${i};48;5;${i}m`); + assert.equal(inputHandler.curAttrData.getFgColorMode(), Attributes.CM_P256); + assert.equal(inputHandler.curAttrData.getFgColor(), i); + assert.equal(inputHandler.curAttrData.getBgColorMode(), Attributes.CM_P256); + assert.equal(inputHandler.curAttrData.getBgColor(), i); } // reset to DEFAULT - term.writeSync(`\x1b[39;49m`); - assert.equal(term.curAttrData.getFgColorMode(), 0); - assert.equal(term.curAttrData.getFgColor(), -1); - assert.equal(term.curAttrData.getBgColorMode(), 0); - assert.equal(term.curAttrData.getBgColor(), -1); + inputHandler.parse(`\x1b[39;49m`); + assert.equal(inputHandler.curAttrData.getFgColorMode(), 0); + assert.equal(inputHandler.curAttrData.getFgColor(), -1); + assert.equal(inputHandler.curAttrData.getBgColorMode(), 0); + assert.equal(inputHandler.curAttrData.getBgColor(), -1); }); it('colormode RGB', () => { - assert.equal(term.curAttrData.getFgColorMode(), 0); // DEFAULT - assert.equal(term.curAttrData.getBgColorMode(), 0); // DEFAULT - term.writeSync(`\x1b[38;2;1;2;3;48;2;4;5;6m`); - assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_RGB); - assert.equal(term.curAttrData.getFgColor(), 1 << 16 | 2 << 8 | 3); - assert.deepEqual(AttributeData.toColorRGB(term.curAttrData.getFgColor()), [1, 2, 3]); - assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_RGB); - assert.deepEqual(AttributeData.toColorRGB(term.curAttrData.getBgColor()), [4, 5, 6]); + assert.equal(inputHandler.curAttrData.getFgColorMode(), 0); // DEFAULT + assert.equal(inputHandler.curAttrData.getBgColorMode(), 0); // DEFAULT + inputHandler.parse(`\x1b[38;2;1;2;3;48;2;4;5;6m`); + assert.equal(inputHandler.curAttrData.getFgColorMode(), Attributes.CM_RGB); + assert.equal(inputHandler.curAttrData.getFgColor(), 1 << 16 | 2 << 8 | 3); + assert.deepEqual(AttributeData.toColorRGB(inputHandler.curAttrData.getFgColor()), [1, 2, 3]); + assert.equal(inputHandler.curAttrData.getBgColorMode(), Attributes.CM_RGB); + assert.deepEqual(AttributeData.toColorRGB(inputHandler.curAttrData.getBgColor()), [4, 5, 6]); // reset to DEFAULT - term.writeSync(`\x1b[39;49m`); - assert.equal(term.curAttrData.getFgColorMode(), 0); - assert.equal(term.curAttrData.getFgColor(), -1); - assert.equal(term.curAttrData.getBgColorMode(), 0); - assert.equal(term.curAttrData.getBgColor(), -1); + inputHandler.parse(`\x1b[39;49m`); + assert.equal(inputHandler.curAttrData.getFgColorMode(), 0); + assert.equal(inputHandler.curAttrData.getFgColor(), -1); + assert.equal(inputHandler.curAttrData.getBgColorMode(), 0); + assert.equal(inputHandler.curAttrData.getBgColor(), -1); }); it('colormode transition RGB to 256', () => { // enter RGB for FG and BG - term.writeSync(`\x1b[38;2;1;2;3;48;2;4;5;6m`); + inputHandler.parse(`\x1b[38;2;1;2;3;48;2;4;5;6m`); // enter 256 for FG and BG - term.writeSync(`\x1b[38;5;255;48;5;255m`); - assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_P256); - assert.equal(term.curAttrData.getFgColor(), 255); - assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_P256); - assert.equal(term.curAttrData.getBgColor(), 255); + inputHandler.parse(`\x1b[38;5;255;48;5;255m`); + assert.equal(inputHandler.curAttrData.getFgColorMode(), Attributes.CM_P256); + assert.equal(inputHandler.curAttrData.getFgColor(), 255); + assert.equal(inputHandler.curAttrData.getBgColorMode(), Attributes.CM_P256); + assert.equal(inputHandler.curAttrData.getBgColor(), 255); }); it('colormode transition RGB to 16', () => { // enter RGB for FG and BG - term.writeSync(`\x1b[38;2;1;2;3;48;2;4;5;6m`); + inputHandler.parse(`\x1b[38;2;1;2;3;48;2;4;5;6m`); // enter 16 for FG and BG - term.writeSync(`\x1b[37;47m`); - assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_P16); - assert.equal(term.curAttrData.getFgColor(), 7); - assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_P16); - assert.equal(term.curAttrData.getBgColor(), 7); + inputHandler.parse(`\x1b[37;47m`); + assert.equal(inputHandler.curAttrData.getFgColorMode(), Attributes.CM_P16); + assert.equal(inputHandler.curAttrData.getFgColor(), 7); + assert.equal(inputHandler.curAttrData.getBgColorMode(), Attributes.CM_P16); + assert.equal(inputHandler.curAttrData.getBgColor(), 7); }); it('colormode transition 16 to 256', () => { // enter 16 for FG and BG - term.writeSync(`\x1b[37;47m`); + inputHandler.parse(`\x1b[37;47m`); // enter 256 for FG and BG - term.writeSync(`\x1b[38;5;255;48;5;255m`); - assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_P256); - assert.equal(term.curAttrData.getFgColor(), 255); - assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_P256); - assert.equal(term.curAttrData.getBgColor(), 255); + inputHandler.parse(`\x1b[38;5;255;48;5;255m`); + assert.equal(inputHandler.curAttrData.getFgColorMode(), Attributes.CM_P256); + assert.equal(inputHandler.curAttrData.getFgColor(), 255); + assert.equal(inputHandler.curAttrData.getBgColorMode(), Attributes.CM_P256); + assert.equal(inputHandler.curAttrData.getBgColor(), 255); }); it('colormode transition 256 to 16', () => { // enter 256 for FG and BG - term.writeSync(`\x1b[38;5;255;48;5;255m`); + inputHandler.parse(`\x1b[38;5;255;48;5;255m`); // enter 16 for FG and BG - term.writeSync(`\x1b[37;47m`); - assert.equal(term.curAttrData.getFgColorMode(), Attributes.CM_P16); - assert.equal(term.curAttrData.getFgColor(), 7); - assert.equal(term.curAttrData.getBgColorMode(), Attributes.CM_P16); - assert.equal(term.curAttrData.getBgColor(), 7); + inputHandler.parse(`\x1b[37;47m`); + assert.equal(inputHandler.curAttrData.getFgColorMode(), Attributes.CM_P16); + assert.equal(inputHandler.curAttrData.getFgColor(), 7); + assert.equal(inputHandler.curAttrData.getBgColorMode(), Attributes.CM_P16); + assert.equal(inputHandler.curAttrData.getBgColor(), 7); }); it('should zero missing RGB values', () => { - term.writeSync(`\x1b[38;2;1;2;3m`); - term.writeSync(`\x1b[38;2;5m`); - assert.deepEqual(AttributeData.toColorRGB(term.curAttrData.getFgColor()), [5, 0, 0]); + inputHandler.parse(`\x1b[38;2;1;2;3m`); + inputHandler.parse(`\x1b[38;2;5m`); + assert.deepEqual(AttributeData.toColorRGB(inputHandler.curAttrData.getFgColor()), [5, 0, 0]); }); }); describe('colon notation', () => { - let termColon: TestTerminal; - let termSemicolon: TestTerminal; + let inputHandler2: TestInputHandler; beforeEach(() => { - termColon = new TestTerminal(); - termSemicolon = new TestTerminal(); + inputHandler2 = new TestInputHandler(bufferService, new MockCharsetService(), coreService, new MockDirtyRowService(), new MockLogService(), optionsService, new MockCoreMouseService(), new MockUnicodeService()); }); describe('should equal to semicolon', () => { it('CSI 38:2::50:100:150 m', () => { - termColon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.writeSync('\x1b[38;2;50;100;150m'); - termColon.writeSync('\x1b[38:2::50:100:150m'); - assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.parse('\x1b[38;2;50;100;150m'); + inputHandler.parse('\x1b[38:2::50:100:150m'); + assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); it('CSI 38:2::50:100: m', () => { - termColon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.writeSync('\x1b[38;2;50;100;m'); - termColon.writeSync('\x1b[38:2::50:100:m'); - assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 0); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.parse('\x1b[38;2;50;100;m'); + inputHandler.parse('\x1b[38:2::50:100:m'); + assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 0); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); it('CSI 38:2::50:: m', () => { - termColon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.writeSync('\x1b[38;2;50;;m'); - termColon.writeSync('\x1b[38:2::50::m'); - assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 0 << 8 | 0); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.parse('\x1b[38;2;50;;m'); + inputHandler.parse('\x1b[38:2::50::m'); + assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 0 << 8 | 0); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); it('CSI 38:2:::: m', () => { - termColon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.writeSync('\x1b[38;2;;;m'); - termColon.writeSync('\x1b[38:2::::m'); - assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 0 << 16 | 0 << 8 | 0); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.parse('\x1b[38;2;;;m'); + inputHandler.parse('\x1b[38:2::::m'); + assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 0 << 16 | 0 << 8 | 0); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); it('CSI 38;2::50:100:150 m', () => { - termColon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.writeSync('\x1b[38;2;50;100;150m'); - termColon.writeSync('\x1b[38;2::50:100:150m'); - assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.parse('\x1b[38;2;50;100;150m'); + inputHandler.parse('\x1b[38;2::50:100:150m'); + assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); it('CSI 38;2;50:100:150 m', () => { - termColon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.writeSync('\x1b[38;2;50;100;150m'); - termColon.writeSync('\x1b[38;2;50:100:150m'); - assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.parse('\x1b[38;2;50;100;150m'); + inputHandler.parse('\x1b[38;2;50:100:150m'); + assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); it('CSI 38;2;50;100:150 m', () => { - termColon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.writeSync('\x1b[38;2;50;100;150m'); - termColon.writeSync('\x1b[38;2;50;100:150m'); - assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.parse('\x1b[38;2;50;100;150m'); + inputHandler.parse('\x1b[38;2;50;100:150m'); + assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); it('CSI 38:5:50 m', () => { - termColon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.writeSync('\x1b[38;5;50m'); - termColon.writeSync('\x1b[38:5:50m'); - assert.equal(termSemicolon.curAttrData.fg & 0xFF, 50); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.parse('\x1b[38;5;50m'); + inputHandler.parse('\x1b[38:5:50m'); + assert.equal(inputHandler2.curAttrData.fg & 0xFF, 50); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); it('CSI 38:5: m', () => { - termColon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.writeSync('\x1b[38;5;m'); - termColon.writeSync('\x1b[38:5:m'); - assert.equal(termSemicolon.curAttrData.fg & 0xFF, 0); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.parse('\x1b[38;5;m'); + inputHandler.parse('\x1b[38:5:m'); + assert.equal(inputHandler2.curAttrData.fg & 0xFF, 0); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); it('CSI 38;5:50 m', () => { - termColon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.writeSync('\x1b[38;5;50m'); - termColon.writeSync('\x1b[38;5:50m'); - assert.equal(termSemicolon.curAttrData.fg & 0xFF, 50); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.parse('\x1b[38;5;50m'); + inputHandler.parse('\x1b[38;5:50m'); + assert.equal(inputHandler2.curAttrData.fg & 0xFF, 50); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); }); describe('should fill early sequence end with default of 0', () => { it('CSI 38:2 m', () => { - termColon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.writeSync('\x1b[38;2m'); - termColon.writeSync('\x1b[38:2m'); - assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 0 << 16 | 0 << 8 | 0); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.parse('\x1b[38;2m'); + inputHandler.parse('\x1b[38:2m'); + assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 0 << 16 | 0 << 8 | 0); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); it('CSI 38:5 m', () => { - termColon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.curAttrData.fg = 0xFFFFFFFF; - termSemicolon.writeSync('\x1b[38;5m'); - termColon.writeSync('\x1b[38:5m'); - assert.equal(termSemicolon.curAttrData.fg & 0xFF, 0); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.curAttrData.fg = 0xFFFFFFFF; + inputHandler2.parse('\x1b[38;5m'); + inputHandler.parse('\x1b[38:5m'); + assert.equal(inputHandler2.curAttrData.fg & 0xFF, 0); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); }); describe('should not interfere with leading/following SGR attrs', () => { it('CSI 1 ; 38:2::50:100:150 ; 4 m', () => { - termSemicolon.writeSync('\x1b[1;38;2;50;100;150;4m'); - termColon.writeSync('\x1b[1;38:2::50:100:150;4m'); - assert.equal(!!termSemicolon.curAttrData.isBold(), true); - assert.equal(!!termSemicolon.curAttrData.isUnderline(), true); - assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler2.parse('\x1b[1;38;2;50;100;150;4m'); + inputHandler.parse('\x1b[1;38:2::50:100:150;4m'); + assert.equal(!!inputHandler2.curAttrData.isBold(), true); + assert.equal(!!inputHandler2.curAttrData.isUnderline(), true); + assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 150); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); it('CSI 1 ; 38:2::50:100: ; 4 m', () => { - termSemicolon.writeSync('\x1b[1;38;2;50;100;;4m'); - termColon.writeSync('\x1b[1;38:2::50:100:;4m'); - assert.equal(!!termSemicolon.curAttrData.isBold(), true); - assert.equal(!!termSemicolon.curAttrData.isUnderline(), true); - assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 0); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler2.parse('\x1b[1;38;2;50;100;;4m'); + inputHandler.parse('\x1b[1;38:2::50:100:;4m'); + assert.equal(!!inputHandler2.curAttrData.isBold(), true); + assert.equal(!!inputHandler2.curAttrData.isUnderline(), true); + assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 0); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); it('CSI 1 ; 38:2::50:100 ; 4 m', () => { - termSemicolon.writeSync('\x1b[1;38;2;50;100;;4m'); - termColon.writeSync('\x1b[1;38:2::50:100;4m'); - assert.equal(!!termSemicolon.curAttrData.isBold(), true); - assert.equal(!!termSemicolon.curAttrData.isUnderline(), true); - assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 0); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler2.parse('\x1b[1;38;2;50;100;;4m'); + inputHandler.parse('\x1b[1;38:2::50:100;4m'); + assert.equal(!!inputHandler2.curAttrData.isBold(), true); + assert.equal(!!inputHandler2.curAttrData.isUnderline(), true); + assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 50 << 16 | 100 << 8 | 0); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); it('CSI 1 ; 38:2:: ; 4 m', () => { - termSemicolon.writeSync('\x1b[1;38;2;;;;4m'); - termColon.writeSync('\x1b[1;38:2::;4m'); - assert.equal(!!termSemicolon.curAttrData.isBold(), true); - assert.equal(!!termSemicolon.curAttrData.isUnderline(), true); - assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 0); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler2.parse('\x1b[1;38;2;;;;4m'); + inputHandler.parse('\x1b[1;38:2::;4m'); + assert.equal(!!inputHandler2.curAttrData.isBold(), true); + assert.equal(!!inputHandler2.curAttrData.isUnderline(), true); + assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 0); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); it('CSI 1 ; 38;2:: ; 4 m', () => { - termSemicolon.writeSync('\x1b[1;38;2;;;;4m'); - termColon.writeSync('\x1b[1;38;2::;4m'); - assert.equal(!!termSemicolon.curAttrData.isBold(), true); - assert.equal(!!termSemicolon.curAttrData.isUnderline(), true); - assert.equal(termSemicolon.curAttrData.fg & 0xFFFFFF, 0); - assert.equal(termColon.curAttrData.fg, termSemicolon.curAttrData.fg); + inputHandler2.parse('\x1b[1;38;2;;;;4m'); + inputHandler.parse('\x1b[1;38;2::;4m'); + assert.equal(!!inputHandler2.curAttrData.isBold(), true); + assert.equal(!!inputHandler2.curAttrData.isUnderline(), true); + assert.equal(inputHandler2.curAttrData.fg & 0xFFFFFF, 0); + assert.equal(inputHandler.curAttrData.fg, inputHandler2.curAttrData.fg); }); }); }); describe('cursor positioning', () => { - let term: TestTerminal; beforeEach(() => { - term = new TestTerminal({cols: 10, rows: 10}); + bufferService.resize(10, 10); }); it('cursor forward (CUF)', () => { - term.writeSync('\x1b[C'); - assert.deepEqual(getCursor(term), [1, 0]); - term.writeSync('\x1b[1C'); - assert.deepEqual(getCursor(term), [2, 0]); - term.writeSync('\x1b[4C'); - assert.deepEqual(getCursor(term), [6, 0]); - term.writeSync('\x1b[100C'); - assert.deepEqual(getCursor(term), [9, 0]); + inputHandler.parse('\x1b[C'); + assert.deepEqual(getCursor(bufferService), [1, 0]); + inputHandler.parse('\x1b[1C'); + assert.deepEqual(getCursor(bufferService), [2, 0]); + inputHandler.parse('\x1b[4C'); + assert.deepEqual(getCursor(bufferService), [6, 0]); + inputHandler.parse('\x1b[100C'); + assert.deepEqual(getCursor(bufferService), [9, 0]); // should not change y - term.buffer.x = 8; - term.buffer.y = 4; - term.writeSync('\x1b[C'); - assert.deepEqual(getCursor(term), [9, 4]); + bufferService.buffer.x = 8; + bufferService.buffer.y = 4; + inputHandler.parse('\x1b[C'); + assert.deepEqual(getCursor(bufferService), [9, 4]); }); it('cursor backward (CUB)', () => { - term.writeSync('\x1b[D'); - assert.deepEqual(getCursor(term), [0, 0]); - term.writeSync('\x1b[1D'); - assert.deepEqual(getCursor(term), [0, 0]); + inputHandler.parse('\x1b[D'); + assert.deepEqual(getCursor(bufferService), [0, 0]); + inputHandler.parse('\x1b[1D'); + assert.deepEqual(getCursor(bufferService), [0, 0]); // place cursor at end of first line - term.writeSync('\x1b[100C'); - term.writeSync('\x1b[D'); - assert.deepEqual(getCursor(term), [8, 0]); - term.writeSync('\x1b[1D'); - assert.deepEqual(getCursor(term), [7, 0]); - term.writeSync('\x1b[4D'); - assert.deepEqual(getCursor(term), [3, 0]); - term.writeSync('\x1b[100D'); - assert.deepEqual(getCursor(term), [0, 0]); + inputHandler.parse('\x1b[100C'); + inputHandler.parse('\x1b[D'); + assert.deepEqual(getCursor(bufferService), [8, 0]); + inputHandler.parse('\x1b[1D'); + assert.deepEqual(getCursor(bufferService), [7, 0]); + inputHandler.parse('\x1b[4D'); + assert.deepEqual(getCursor(bufferService), [3, 0]); + inputHandler.parse('\x1b[100D'); + assert.deepEqual(getCursor(bufferService), [0, 0]); // should not change y - term.buffer.x = 4; - term.buffer.y = 4; - term.writeSync('\x1b[D'); - assert.deepEqual(getCursor(term), [3, 4]); + bufferService.buffer.x = 4; + bufferService.buffer.y = 4; + inputHandler.parse('\x1b[D'); + assert.deepEqual(getCursor(bufferService), [3, 4]); }); it('cursor down (CUD)', () => { - term.writeSync('\x1b[B'); - assert.deepEqual(getCursor(term), [0, 1]); - term.writeSync('\x1b[1B'); - assert.deepEqual(getCursor(term), [0, 2]); - term.writeSync('\x1b[4B'); - assert.deepEqual(getCursor(term), [0, 6]); - term.writeSync('\x1b[100B'); - assert.deepEqual(getCursor(term), [0, 9]); + inputHandler.parse('\x1b[B'); + assert.deepEqual(getCursor(bufferService), [0, 1]); + inputHandler.parse('\x1b[1B'); + assert.deepEqual(getCursor(bufferService), [0, 2]); + inputHandler.parse('\x1b[4B'); + assert.deepEqual(getCursor(bufferService), [0, 6]); + inputHandler.parse('\x1b[100B'); + assert.deepEqual(getCursor(bufferService), [0, 9]); // should not change x - term.buffer.x = 8; - term.buffer.y = 0; - term.writeSync('\x1b[B'); - assert.deepEqual(getCursor(term), [8, 1]); + bufferService.buffer.x = 8; + bufferService.buffer.y = 0; + inputHandler.parse('\x1b[B'); + assert.deepEqual(getCursor(bufferService), [8, 1]); }); it('cursor up (CUU)', () => { - term.writeSync('\x1b[A'); - assert.deepEqual(getCursor(term), [0, 0]); - term.writeSync('\x1b[1A'); - assert.deepEqual(getCursor(term), [0, 0]); + inputHandler.parse('\x1b[A'); + assert.deepEqual(getCursor(bufferService), [0, 0]); + inputHandler.parse('\x1b[1A'); + assert.deepEqual(getCursor(bufferService), [0, 0]); // place cursor at beginning of last row - term.writeSync('\x1b[100B'); - term.writeSync('\x1b[A'); - assert.deepEqual(getCursor(term), [0, 8]); - term.writeSync('\x1b[1A'); - assert.deepEqual(getCursor(term), [0, 7]); - term.writeSync('\x1b[4A'); - assert.deepEqual(getCursor(term), [0, 3]); - term.writeSync('\x1b[100A'); - assert.deepEqual(getCursor(term), [0, 0]); + inputHandler.parse('\x1b[100B'); + inputHandler.parse('\x1b[A'); + assert.deepEqual(getCursor(bufferService), [0, 8]); + inputHandler.parse('\x1b[1A'); + assert.deepEqual(getCursor(bufferService), [0, 7]); + inputHandler.parse('\x1b[4A'); + assert.deepEqual(getCursor(bufferService), [0, 3]); + inputHandler.parse('\x1b[100A'); + assert.deepEqual(getCursor(bufferService), [0, 0]); // should not change x - term.buffer.x = 8; - term.buffer.y = 9; - term.writeSync('\x1b[A'); - assert.deepEqual(getCursor(term), [8, 8]); + bufferService.buffer.x = 8; + bufferService.buffer.y = 9; + inputHandler.parse('\x1b[A'); + assert.deepEqual(getCursor(bufferService), [8, 8]); }); it('cursor next line (CNL)', () => { - term.writeSync('\x1b[E'); - assert.deepEqual(getCursor(term), [0, 1]); - term.writeSync('\x1b[1E'); - assert.deepEqual(getCursor(term), [0, 2]); - term.writeSync('\x1b[4E'); - assert.deepEqual(getCursor(term), [0, 6]); - term.writeSync('\x1b[100E'); - assert.deepEqual(getCursor(term), [0, 9]); + inputHandler.parse('\x1b[E'); + assert.deepEqual(getCursor(bufferService), [0, 1]); + inputHandler.parse('\x1b[1E'); + assert.deepEqual(getCursor(bufferService), [0, 2]); + inputHandler.parse('\x1b[4E'); + assert.deepEqual(getCursor(bufferService), [0, 6]); + inputHandler.parse('\x1b[100E'); + assert.deepEqual(getCursor(bufferService), [0, 9]); // should reset x to zero - term.buffer.x = 8; - term.buffer.y = 0; - term.writeSync('\x1b[E'); - assert.deepEqual(getCursor(term), [0, 1]); + bufferService.buffer.x = 8; + bufferService.buffer.y = 0; + inputHandler.parse('\x1b[E'); + assert.deepEqual(getCursor(bufferService), [0, 1]); }); it('cursor previous line (CPL)', () => { - term.writeSync('\x1b[F'); - assert.deepEqual(getCursor(term), [0, 0]); - term.writeSync('\x1b[1F'); - assert.deepEqual(getCursor(term), [0, 0]); + inputHandler.parse('\x1b[F'); + assert.deepEqual(getCursor(bufferService), [0, 0]); + inputHandler.parse('\x1b[1F'); + assert.deepEqual(getCursor(bufferService), [0, 0]); // place cursor at beginning of last row - term.writeSync('\x1b[100E'); - term.writeSync('\x1b[F'); - assert.deepEqual(getCursor(term), [0, 8]); - term.writeSync('\x1b[1F'); - assert.deepEqual(getCursor(term), [0, 7]); - term.writeSync('\x1b[4F'); - assert.deepEqual(getCursor(term), [0, 3]); - term.writeSync('\x1b[100F'); - assert.deepEqual(getCursor(term), [0, 0]); + inputHandler.parse('\x1b[100E'); + inputHandler.parse('\x1b[F'); + assert.deepEqual(getCursor(bufferService), [0, 8]); + inputHandler.parse('\x1b[1F'); + assert.deepEqual(getCursor(bufferService), [0, 7]); + inputHandler.parse('\x1b[4F'); + assert.deepEqual(getCursor(bufferService), [0, 3]); + inputHandler.parse('\x1b[100F'); + assert.deepEqual(getCursor(bufferService), [0, 0]); // should reset x to zero - term.buffer.x = 8; - term.buffer.y = 9; - term.writeSync('\x1b[F'); - assert.deepEqual(getCursor(term), [0, 8]); + bufferService.buffer.x = 8; + bufferService.buffer.y = 9; + inputHandler.parse('\x1b[F'); + assert.deepEqual(getCursor(bufferService), [0, 8]); }); it('cursor character absolute (CHA)', () => { - term.writeSync('\x1b[G'); - assert.deepEqual(getCursor(term), [0, 0]); - term.writeSync('\x1b[1G'); - assert.deepEqual(getCursor(term), [0, 0]); - term.writeSync('\x1b[2G'); - assert.deepEqual(getCursor(term), [1, 0]); - term.writeSync('\x1b[5G'); - assert.deepEqual(getCursor(term), [4, 0]); - term.writeSync('\x1b[100G'); - assert.deepEqual(getCursor(term), [9, 0]); + inputHandler.parse('\x1b[G'); + assert.deepEqual(getCursor(bufferService), [0, 0]); + inputHandler.parse('\x1b[1G'); + assert.deepEqual(getCursor(bufferService), [0, 0]); + inputHandler.parse('\x1b[2G'); + assert.deepEqual(getCursor(bufferService), [1, 0]); + inputHandler.parse('\x1b[5G'); + assert.deepEqual(getCursor(bufferService), [4, 0]); + inputHandler.parse('\x1b[100G'); + assert.deepEqual(getCursor(bufferService), [9, 0]); }); it('cursor position (CUP)', () => { - term.buffer.x = 5; - term.buffer.y = 5; - term.writeSync('\x1b[H'); - assert.deepEqual(getCursor(term), [0, 0]); - term.buffer.x = 5; - term.buffer.y = 5; - term.writeSync('\x1b[1H'); - assert.deepEqual(getCursor(term), [0, 0]); - term.buffer.x = 5; - term.buffer.y = 5; - term.writeSync('\x1b[1;1H'); - assert.deepEqual(getCursor(term), [0, 0]); - term.buffer.x = 5; - term.buffer.y = 5; - term.writeSync('\x1b[8H'); - assert.deepEqual(getCursor(term), [0, 7]); - term.buffer.x = 5; - term.buffer.y = 5; - term.writeSync('\x1b[;8H'); - assert.deepEqual(getCursor(term), [7, 0]); - term.buffer.x = 5; - term.buffer.y = 5; - term.writeSync('\x1b[100;100H'); - assert.deepEqual(getCursor(term), [9, 9]); + bufferService.buffer.x = 5; + bufferService.buffer.y = 5; + inputHandler.parse('\x1b[H'); + assert.deepEqual(getCursor(bufferService), [0, 0]); + bufferService.buffer.x = 5; + bufferService.buffer.y = 5; + inputHandler.parse('\x1b[1H'); + assert.deepEqual(getCursor(bufferService), [0, 0]); + bufferService.buffer.x = 5; + bufferService.buffer.y = 5; + inputHandler.parse('\x1b[1;1H'); + assert.deepEqual(getCursor(bufferService), [0, 0]); + bufferService.buffer.x = 5; + bufferService.buffer.y = 5; + inputHandler.parse('\x1b[8H'); + assert.deepEqual(getCursor(bufferService), [0, 7]); + bufferService.buffer.x = 5; + bufferService.buffer.y = 5; + inputHandler.parse('\x1b[;8H'); + assert.deepEqual(getCursor(bufferService), [7, 0]); + bufferService.buffer.x = 5; + bufferService.buffer.y = 5; + inputHandler.parse('\x1b[100;100H'); + assert.deepEqual(getCursor(bufferService), [9, 9]); }); it('horizontal position absolute (HPA)', () => { - term.writeSync('\x1b[`'); - assert.deepEqual(getCursor(term), [0, 0]); - term.writeSync('\x1b[1`'); - assert.deepEqual(getCursor(term), [0, 0]); - term.writeSync('\x1b[2`'); - assert.deepEqual(getCursor(term), [1, 0]); - term.writeSync('\x1b[5`'); - assert.deepEqual(getCursor(term), [4, 0]); - term.writeSync('\x1b[100`'); - assert.deepEqual(getCursor(term), [9, 0]); + inputHandler.parse('\x1b[`'); + assert.deepEqual(getCursor(bufferService), [0, 0]); + inputHandler.parse('\x1b[1`'); + assert.deepEqual(getCursor(bufferService), [0, 0]); + inputHandler.parse('\x1b[2`'); + assert.deepEqual(getCursor(bufferService), [1, 0]); + inputHandler.parse('\x1b[5`'); + assert.deepEqual(getCursor(bufferService), [4, 0]); + inputHandler.parse('\x1b[100`'); + assert.deepEqual(getCursor(bufferService), [9, 0]); }); it('horizontal position relative (HPR)', () => { - term.writeSync('\x1b[a'); - assert.deepEqual(getCursor(term), [1, 0]); - term.writeSync('\x1b[1a'); - assert.deepEqual(getCursor(term), [2, 0]); - term.writeSync('\x1b[4a'); - assert.deepEqual(getCursor(term), [6, 0]); - term.writeSync('\x1b[100a'); - assert.deepEqual(getCursor(term), [9, 0]); + inputHandler.parse('\x1b[a'); + assert.deepEqual(getCursor(bufferService), [1, 0]); + inputHandler.parse('\x1b[1a'); + assert.deepEqual(getCursor(bufferService), [2, 0]); + inputHandler.parse('\x1b[4a'); + assert.deepEqual(getCursor(bufferService), [6, 0]); + inputHandler.parse('\x1b[100a'); + assert.deepEqual(getCursor(bufferService), [9, 0]); // should not change y - term.buffer.x = 8; - term.buffer.y = 4; - term.writeSync('\x1b[a'); - assert.deepEqual(getCursor(term), [9, 4]); + bufferService.buffer.x = 8; + bufferService.buffer.y = 4; + inputHandler.parse('\x1b[a'); + assert.deepEqual(getCursor(bufferService), [9, 4]); }); it('vertical position absolute (VPA)', () => { - term.writeSync('\x1b[d'); - assert.deepEqual(getCursor(term), [0, 0]); - term.writeSync('\x1b[1d'); - assert.deepEqual(getCursor(term), [0, 0]); - term.writeSync('\x1b[2d'); - assert.deepEqual(getCursor(term), [0, 1]); - term.writeSync('\x1b[5d'); - assert.deepEqual(getCursor(term), [0, 4]); - term.writeSync('\x1b[100d'); - assert.deepEqual(getCursor(term), [0, 9]); + inputHandler.parse('\x1b[d'); + assert.deepEqual(getCursor(bufferService), [0, 0]); + inputHandler.parse('\x1b[1d'); + assert.deepEqual(getCursor(bufferService), [0, 0]); + inputHandler.parse('\x1b[2d'); + assert.deepEqual(getCursor(bufferService), [0, 1]); + inputHandler.parse('\x1b[5d'); + assert.deepEqual(getCursor(bufferService), [0, 4]); + inputHandler.parse('\x1b[100d'); + assert.deepEqual(getCursor(bufferService), [0, 9]); // should not change x - term.buffer.x = 8; - term.buffer.y = 4; - term.writeSync('\x1b[d'); - assert.deepEqual(getCursor(term), [8, 0]); + bufferService.buffer.x = 8; + bufferService.buffer.y = 4; + inputHandler.parse('\x1b[d'); + assert.deepEqual(getCursor(bufferService), [8, 0]); }); it('vertical position relative (VPR)', () => { - term.writeSync('\x1b[e'); - assert.deepEqual(getCursor(term), [0, 1]); - term.writeSync('\x1b[1e'); - assert.deepEqual(getCursor(term), [0, 2]); - term.writeSync('\x1b[4e'); - assert.deepEqual(getCursor(term), [0, 6]); - term.writeSync('\x1b[100e'); - assert.deepEqual(getCursor(term), [0, 9]); + inputHandler.parse('\x1b[e'); + assert.deepEqual(getCursor(bufferService), [0, 1]); + inputHandler.parse('\x1b[1e'); + assert.deepEqual(getCursor(bufferService), [0, 2]); + inputHandler.parse('\x1b[4e'); + assert.deepEqual(getCursor(bufferService), [0, 6]); + inputHandler.parse('\x1b[100e'); + assert.deepEqual(getCursor(bufferService), [0, 9]); // should not change x - term.buffer.x = 8; - term.buffer.y = 4; - term.writeSync('\x1b[e'); - assert.deepEqual(getCursor(term), [8, 5]); + bufferService.buffer.x = 8; + bufferService.buffer.y = 4; + inputHandler.parse('\x1b[e'); + assert.deepEqual(getCursor(bufferService), [8, 5]); }); describe('should clamp cursor into addressible range', () => { it('CUF', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[C'); - assert.deepEqual(getCursor(term), [9, 9]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[C'); - assert.deepEqual(getCursor(term), [1, 0]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[C'); + assert.deepEqual(getCursor(bufferService), [9, 9]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[C'); + assert.deepEqual(getCursor(bufferService), [1, 0]); }); it('CUB', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[D'); - assert.deepEqual(getCursor(term), [8, 9]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[D'); - assert.deepEqual(getCursor(term), [0, 0]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[D'); + assert.deepEqual(getCursor(bufferService), [8, 9]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[D'); + assert.deepEqual(getCursor(bufferService), [0, 0]); }); it('CUD', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[B'); - assert.deepEqual(getCursor(term), [9, 9]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[B'); - assert.deepEqual(getCursor(term), [0, 1]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[B'); + assert.deepEqual(getCursor(bufferService), [9, 9]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[B'); + assert.deepEqual(getCursor(bufferService), [0, 1]); }); it('CUU', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[A'); - assert.deepEqual(getCursor(term), [9, 8]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[A'); - assert.deepEqual(getCursor(term), [0, 0]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[A'); + assert.deepEqual(getCursor(bufferService), [9, 8]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[A'); + assert.deepEqual(getCursor(bufferService), [0, 0]); }); it('CNL', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[E'); - assert.deepEqual(getCursor(term), [0, 9]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[E'); - assert.deepEqual(getCursor(term), [0, 1]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[E'); + assert.deepEqual(getCursor(bufferService), [0, 9]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[E'); + assert.deepEqual(getCursor(bufferService), [0, 1]); }); it('CPL', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[F'); - assert.deepEqual(getCursor(term), [0, 8]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[F'); - assert.deepEqual(getCursor(term), [0, 0]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[F'); + assert.deepEqual(getCursor(bufferService), [0, 8]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[F'); + assert.deepEqual(getCursor(bufferService), [0, 0]); }); it('CHA', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[5G'); - assert.deepEqual(getCursor(term), [4, 9]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[5G'); - assert.deepEqual(getCursor(term), [4, 0]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[5G'); + assert.deepEqual(getCursor(bufferService), [4, 9]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[5G'); + assert.deepEqual(getCursor(bufferService), [4, 0]); }); it('CUP', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[5;5H'); - assert.deepEqual(getCursor(term), [4, 4]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[5;5H'); - assert.deepEqual(getCursor(term), [4, 4]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[5;5H'); + assert.deepEqual(getCursor(bufferService), [4, 4]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[5;5H'); + assert.deepEqual(getCursor(bufferService), [4, 4]); }); it('HPA', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[5`'); - assert.deepEqual(getCursor(term), [4, 9]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[5`'); - assert.deepEqual(getCursor(term), [4, 0]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[5`'); + assert.deepEqual(getCursor(bufferService), [4, 9]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[5`'); + assert.deepEqual(getCursor(bufferService), [4, 0]); }); it('HPR', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[a'); - assert.deepEqual(getCursor(term), [9, 9]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[a'); - assert.deepEqual(getCursor(term), [1, 0]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[a'); + assert.deepEqual(getCursor(bufferService), [9, 9]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[a'); + assert.deepEqual(getCursor(bufferService), [1, 0]); }); it('VPA', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[5d'); - assert.deepEqual(getCursor(term), [9, 4]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[5d'); - assert.deepEqual(getCursor(term), [0, 4]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[5d'); + assert.deepEqual(getCursor(bufferService), [9, 4]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[5d'); + assert.deepEqual(getCursor(bufferService), [0, 4]); }); it('VPR', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[e'); - assert.deepEqual(getCursor(term), [9, 9]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[e'); - assert.deepEqual(getCursor(term), [0, 1]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[e'); + assert.deepEqual(getCursor(bufferService), [9, 9]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[e'); + assert.deepEqual(getCursor(bufferService), [0, 1]); }); it('DCH', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[P'); - assert.deepEqual(getCursor(term), [9, 9]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[P'); - assert.deepEqual(getCursor(term), [0, 0]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[P'); + assert.deepEqual(getCursor(bufferService), [9, 9]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[P'); + assert.deepEqual(getCursor(bufferService), [0, 0]); }); it('DCH - should delete last cell', () => { - term.writeSync('0123456789\x1b[P'); - assert.equal(term.buffer.lines.get(0).translateToString(false), '012345678 '); + inputHandler.parse('0123456789\x1b[P'); + assert.equal(bufferService.buffer.lines.get(0).translateToString(false), '012345678 '); }); it('ECH', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[X'); - assert.deepEqual(getCursor(term), [9, 9]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[X'); - assert.deepEqual(getCursor(term), [0, 0]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[X'); + assert.deepEqual(getCursor(bufferService), [9, 9]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[X'); + assert.deepEqual(getCursor(bufferService), [0, 0]); }); it('ECH - should delete last cell', () => { - term.writeSync('0123456789\x1b[X'); - assert.equal(term.buffer.lines.get(0).translateToString(false), '012345678 '); + inputHandler.parse('0123456789\x1b[X'); + assert.equal(bufferService.buffer.lines.get(0).translateToString(false), '012345678 '); }); it('ICH', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[@'); - assert.deepEqual(getCursor(term), [9, 9]); - term.buffer.x = -10000; - term.buffer.y = -10000; - term.writeSync('\x1b[@'); - assert.deepEqual(getCursor(term), [0, 0]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[@'); + assert.deepEqual(getCursor(bufferService), [9, 9]); + bufferService.buffer.x = -10000; + bufferService.buffer.y = -10000; + inputHandler.parse('\x1b[@'); + assert.deepEqual(getCursor(bufferService), [0, 0]); }); it('ICH - should delete last cell', () => { - term.writeSync('0123456789\x1b[@'); - assert.equal(term.buffer.lines.get(0).translateToString(false), '012345678 '); + inputHandler.parse('0123456789\x1b[@'); + assert.equal(bufferService.buffer.lines.get(0).translateToString(false), '012345678 '); }); }); }); describe('DECSTBM - scroll margins', () => { - let term: TestTerminal; beforeEach(() => { - term = new TestTerminal({cols: 10, rows: 10}); + bufferService.resize(10, 10); }); it('should default to whole viewport', () => { - term.writeSync('\x1b[r'); - assert.equal(term.buffer.scrollTop, 0); - assert.equal(term.buffer.scrollBottom, 9); - term.writeSync('\x1b[3;7r'); - assert.equal(term.buffer.scrollTop, 2); - assert.equal(term.buffer.scrollBottom, 6); - term.writeSync('\x1b[0;0r'); - assert.equal(term.buffer.scrollTop, 0); - assert.equal(term.buffer.scrollBottom, 9); + inputHandler.parse('\x1b[r'); + assert.equal(bufferService.buffer.scrollTop, 0); + assert.equal(bufferService.buffer.scrollBottom, 9); + inputHandler.parse('\x1b[3;7r'); + assert.equal(bufferService.buffer.scrollTop, 2); + assert.equal(bufferService.buffer.scrollBottom, 6); + inputHandler.parse('\x1b[0;0r'); + assert.equal(bufferService.buffer.scrollTop, 0); + assert.equal(bufferService.buffer.scrollBottom, 9); }); it('should clamp bottom', () => { - term.writeSync('\x1b[3;1000r'); - assert.equal(term.buffer.scrollTop, 2); - assert.equal(term.buffer.scrollBottom, 9); + inputHandler.parse('\x1b[3;1000r'); + assert.equal(bufferService.buffer.scrollTop, 2); + assert.equal(bufferService.buffer.scrollBottom, 9); }); it('should only apply for top < bottom', () => { - term.writeSync('\x1b[7;2r'); - assert.equal(term.buffer.scrollTop, 0); - assert.equal(term.buffer.scrollBottom, 9); + inputHandler.parse('\x1b[7;2r'); + assert.equal(bufferService.buffer.scrollTop, 0); + assert.equal(bufferService.buffer.scrollBottom, 9); }); it('should home cursor', () => { - term.buffer.x = 10000; - term.buffer.y = 10000; - term.writeSync('\x1b[2;7r'); - assert.deepEqual(getCursor(term), [0, 0]); + bufferService.buffer.x = 10000; + bufferService.buffer.y = 10000; + inputHandler.parse('\x1b[2;7r'); + assert.deepEqual(getCursor(bufferService), [0, 0]); }); }); describe('scroll margins', () => { - let term: TestTerminal; beforeEach(() => { - term = new TestTerminal({cols: 10, rows: 10}); + bufferService.resize(10, 10); }); it('scrollUp', () => { - term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[2;4r\x1b[2Sm'); - assert.deepEqual(getLines(term), ['m', '3', '', '', '4', '5', '6', '7', '8', '9']); + inputHandler.parse('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[2;4r\x1b[2Sm'); + assert.deepEqual(getLines(bufferService), ['m', '3', '', '', '4', '5', '6', '7', '8', '9']); }); it('scrollDown', () => { - term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[2;4r\x1b[2Tm'); - assert.deepEqual(getLines(term), ['m', '', '', '1', '4', '5', '6', '7', '8', '9']); + inputHandler.parse('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[2;4r\x1b[2Tm'); + assert.deepEqual(getLines(bufferService), ['m', '', '', '1', '4', '5', '6', '7', '8', '9']); }); it('insertLines - out of margins', () => { - term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r'); - assert.equal(term.buffer.scrollTop, 2); - assert.equal(term.buffer.scrollBottom, 5); - term.writeSync('\x1b[2Lm'); - assert.deepEqual(getLines(term), ['m', '1', '2', '3', '4', '5', '6', '7', '8', '9']); - term.writeSync('\x1b[2H\x1b[2Ln'); - assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', '6', '7', '8', '9']); + inputHandler.parse('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r'); + assert.equal(bufferService.buffer.scrollTop, 2); + assert.equal(bufferService.buffer.scrollBottom, 5); + inputHandler.parse('\x1b[2Lm'); + assert.deepEqual(getLines(bufferService), ['m', '1', '2', '3', '4', '5', '6', '7', '8', '9']); + inputHandler.parse('\x1b[2H\x1b[2Ln'); + assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', '6', '7', '8', '9']); // skip below scrollbottom - term.writeSync('\x1b[7H\x1b[2Lo'); - assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', '7', '8', '9']); - term.writeSync('\x1b[8H\x1b[2Lp'); - assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', '9']); - term.writeSync('\x1b[100H\x1b[2Lq'); - assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', 'q']); + inputHandler.parse('\x1b[7H\x1b[2Lo'); + assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', 'o', '7', '8', '9']); + inputHandler.parse('\x1b[8H\x1b[2Lp'); + assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', '9']); + inputHandler.parse('\x1b[100H\x1b[2Lq'); + assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', 'q']); }); it('insertLines - within margins', () => { - term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r'); - assert.equal(term.buffer.scrollTop, 2); - assert.equal(term.buffer.scrollBottom, 5); - term.writeSync('\x1b[3H\x1b[2Lm'); - assert.deepEqual(getLines(term), ['0', '1', 'm', '', '2', '3', '6', '7', '8', '9']); - term.writeSync('\x1b[6H\x1b[2Ln'); - assert.deepEqual(getLines(term), ['0', '1', 'm', '', '2', 'n', '6', '7', '8', '9']); + inputHandler.parse('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r'); + assert.equal(bufferService.buffer.scrollTop, 2); + assert.equal(bufferService.buffer.scrollBottom, 5); + inputHandler.parse('\x1b[3H\x1b[2Lm'); + assert.deepEqual(getLines(bufferService), ['0', '1', 'm', '', '2', '3', '6', '7', '8', '9']); + inputHandler.parse('\x1b[6H\x1b[2Ln'); + assert.deepEqual(getLines(bufferService), ['0', '1', 'm', '', '2', 'n', '6', '7', '8', '9']); }); it('deleteLines - out of margins', () => { - term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r'); - assert.equal(term.buffer.scrollTop, 2); - assert.equal(term.buffer.scrollBottom, 5); - term.writeSync('\x1b[2Mm'); - assert.deepEqual(getLines(term), ['m', '1', '2', '3', '4', '5', '6', '7', '8', '9']); - term.writeSync('\x1b[2H\x1b[2Mn'); - assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', '6', '7', '8', '9']); + inputHandler.parse('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r'); + assert.equal(bufferService.buffer.scrollTop, 2); + assert.equal(bufferService.buffer.scrollBottom, 5); + inputHandler.parse('\x1b[2Mm'); + assert.deepEqual(getLines(bufferService), ['m', '1', '2', '3', '4', '5', '6', '7', '8', '9']); + inputHandler.parse('\x1b[2H\x1b[2Mn'); + assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', '6', '7', '8', '9']); // skip below scrollbottom - term.writeSync('\x1b[7H\x1b[2Mo'); - assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', '7', '8', '9']); - term.writeSync('\x1b[8H\x1b[2Mp'); - assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', '9']); - term.writeSync('\x1b[100H\x1b[2Mq'); - assert.deepEqual(getLines(term), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', 'q']); + inputHandler.parse('\x1b[7H\x1b[2Mo'); + assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', 'o', '7', '8', '9']); + inputHandler.parse('\x1b[8H\x1b[2Mp'); + assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', '9']); + inputHandler.parse('\x1b[100H\x1b[2Mq'); + assert.deepEqual(getLines(bufferService), ['m', 'n', '2', '3', '4', '5', 'o', 'p', '8', 'q']); }); it('deleteLines - within margins', () => { - term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r'); - assert.equal(term.buffer.scrollTop, 2); - assert.equal(term.buffer.scrollBottom, 5); - term.writeSync('\x1b[6H\x1b[2Mm'); - assert.deepEqual(getLines(term), ['0', '1', '2', '3', '4', 'm', '6', '7', '8', '9']); - term.writeSync('\x1b[3H\x1b[2Mn'); - assert.deepEqual(getLines(term), ['0', '1', 'n', 'm', '', '', '6', '7', '8', '9']); - }); - }); - describe('SL/SR/DECIC/DECDC', () => { - let term: TestTerminal; - beforeEach(() => { - term = new TestTerminal({cols: 5, rows: 5, scrollback: 1}); - }); - it('SL (scrollLeft)', () => { - term.writeSync('12345'.repeat(6)); - term.writeSync('\x1b[ @'); - assert.deepEqual(getLines(term, term.rows + 1), ['12345', '2345', '2345', '2345', '2345', '2345']); - term.writeSync('\x1b[0 @'); - assert.deepEqual(getLines(term, term.rows + 1), ['12345', '345', '345', '345', '345', '345']); - term.writeSync('\x1b[2 @'); - assert.deepEqual(getLines(term, term.rows + 1), ['12345', '5', '5', '5', '5', '5']); - }); - it('SR (scrollRight)', () => { - term.writeSync('12345'.repeat(6)); - term.writeSync('\x1b[ A'); - assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1234', ' 1234', ' 1234', ' 1234', ' 1234']); - term.writeSync('\x1b[0 A'); - assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 123', ' 123', ' 123', ' 123', ' 123']); - term.writeSync('\x1b[2 A'); - assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1', ' 1', ' 1', ' 1', ' 1']); - }); - it('insertColumns (DECIC)', () => { - term.writeSync('12345'.repeat(6)); - term.writeSync('\x1b[3;3H'); - term.writeSync('\x1b[\'}'); - assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']); - term.reset(); - term.writeSync('12345'.repeat(6)); - term.writeSync('\x1b[3;3H'); - term.writeSync('\x1b[1\'}'); - assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']); - term.reset(); - term.writeSync('12345'.repeat(6)); - term.writeSync('\x1b[3;3H'); - term.writeSync('\x1b[2\'}'); - assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 3', '12 3', '12 3', '12 3', '12 3']); - }); - it('deleteColumns (DECDC)', () => { - term.writeSync('12345'.repeat(6)); - term.writeSync('\x1b[3;3H'); - term.writeSync('\x1b[\'~'); - assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']); - term.reset(); - term.writeSync('12345'.repeat(6)); - term.writeSync('\x1b[3;3H'); - term.writeSync('\x1b[1\'~'); - assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']); - term.reset(); - term.writeSync('12345'.repeat(6)); - term.writeSync('\x1b[3;3H'); - term.writeSync('\x1b[2\'~'); - assert.deepEqual(getLines(term, term.rows + 1), ['12345', '125', '125', '125', '125', '125']); + inputHandler.parse('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[3;6r'); + assert.equal(bufferService.buffer.scrollTop, 2); + assert.equal(bufferService.buffer.scrollBottom, 5); + inputHandler.parse('\x1b[6H\x1b[2Mm'); + assert.deepEqual(getLines(bufferService), ['0', '1', '2', '3', '4', 'm', '6', '7', '8', '9']); + inputHandler.parse('\x1b[3H\x1b[2Mn'); + assert.deepEqual(getLines(bufferService), ['0', '1', 'n', 'm', '', '', '6', '7', '8', '9']); }); }); it('should parse big chunks in smaller subchunks', () => { // max single chunk size is hardcoded as 131072 const calls: any[] = []; - const term = new TestTerminal({cols: 10, rows: 10}); - (term as any)._inputHandler._parser.parse = (data: Uint32Array, length: number) => { + bufferService.resize(10, 10); + (inputHandler as any)._parser.parse = (data: Uint32Array, length: number) => { calls.push([data.length, length]); }; - term.writeSync('12345'); - term.writeSync('a'.repeat(10000)); - term.writeSync('a'.repeat(200000)); - term.writeSync('a'.repeat(300000)); + inputHandler.parse('12345'); + inputHandler.parse('a'.repeat(10000)); + inputHandler.parse('a'.repeat(200000)); + inputHandler.parse('a'.repeat(300000)); assert.deepEqual(calls, [ [4096, 5], [10000, 10000], @@ -1263,226 +1192,236 @@ describe('InputHandler', () => { }); describe('windowOptions', () => { it('all should be disabled by default and not report', () => { - const term = new TestTerminal({cols: 10, rows: 10}); + bufferService.resize(10, 10); const stack: string[] = []; - term.onData(data => stack.push(data)); - term.writeSync('\x1b[14t'); - term.writeSync('\x1b[16t'); - term.writeSync('\x1b[18t'); - term.writeSync('\x1b[20t'); - term.writeSync('\x1b[21t'); + coreService.onData(data => stack.push(data)); + inputHandler.parse('\x1b[14t'); + inputHandler.parse('\x1b[16t'); + inputHandler.parse('\x1b[18t'); + inputHandler.parse('\x1b[20t'); + inputHandler.parse('\x1b[21t'); assert.deepEqual(stack, []); }); it('14 - GetWinSizePixels', () => { - const term = new TestTerminal({cols: 10, rows: 10, windowOptions: {getWinSizePixels: true}}); + bufferService.resize(10, 10); + optionsService.options.windowOptions.getWinSizePixels = true; const stack: string[] = []; - term.onData(data => stack.push(data)); - term.writeSync('\x1b[14t'); + coreService.onData(data => stack.push(data)); + inputHandler.parse('\x1b[14t'); // does not report in test terminal due to missing renderer assert.deepEqual(stack, []); }); it('16 - GetCellSizePixels', () => { - const term = new TestTerminal({cols: 10, rows: 10, windowOptions: {getCellSizePixels: true}}); + bufferService.resize(10, 10); + optionsService.options.windowOptions.getCellSizePixels = true; const stack: string[] = []; - term.onData(data => stack.push(data)); - term.writeSync('\x1b[16t'); + coreService.onData(data => stack.push(data)); + inputHandler.parse('\x1b[16t'); // does not report in test terminal due to missing renderer assert.deepEqual(stack, []); }); it('18 - GetWinSizeChars', () => { - const term = new TestTerminal({cols: 10, rows: 10, windowOptions: {getWinSizeChars: true}}); + bufferService.resize(10, 10); + optionsService.options.windowOptions.getWinSizeChars = true; const stack: string[] = []; - term.onData(data => stack.push(data)); - term.writeSync('\x1b[18t'); + coreService.onData(data => stack.push(data)); + inputHandler.parse('\x1b[18t'); assert.deepEqual(stack, ['\x1b[8;10;10t']); - term.resize(50, 20); - term.writeSync('\x1b[18t'); + bufferService.resize(50, 20); + inputHandler.parse('\x1b[18t'); assert.deepEqual(stack, ['\x1b[8;10;10t', '\x1b[8;20;50t']); }); it('22/23 - PushTitle/PopTitle', () => { - const term = new TestTerminal({cols: 10, rows: 10, windowOptions: {pushTitle: true, popTitle: true}}); + bufferService.resize(10, 10); + optionsService.options.windowOptions.pushTitle = true; + optionsService.options.windowOptions.popTitle = true; const stack: string[] = []; - term.onTitleChange(data => stack.push(data)); - term.writeSync('\x1b]0;1\x07'); - term.writeSync('\x1b[22t'); - term.writeSync('\x1b]0;2\x07'); - term.writeSync('\x1b[22t'); - term.writeSync('\x1b]0;3\x07'); - term.writeSync('\x1b[22t'); - assert.deepEqual((term as any)._inputHandler._windowTitleStack, ['1', '2', '3']); - assert.deepEqual((term as any)._inputHandler._iconNameStack, ['1', '2', '3']); + inputHandler.onTitleChange(data => stack.push(data)); + inputHandler.parse('\x1b]0;1\x07'); + inputHandler.parse('\x1b[22t'); + inputHandler.parse('\x1b]0;2\x07'); + inputHandler.parse('\x1b[22t'); + inputHandler.parse('\x1b]0;3\x07'); + inputHandler.parse('\x1b[22t'); + assert.deepEqual(inputHandler.windowTitleStack, ['1', '2', '3']); + assert.deepEqual(inputHandler.iconNameStack, ['1', '2', '3']); assert.deepEqual(stack, ['1', '2', '3']); - term.writeSync('\x1b[23t'); - term.writeSync('\x1b[23t'); - term.writeSync('\x1b[23t'); - term.writeSync('\x1b[23t'); // one more to test "overflow" - assert.deepEqual((term as any)._inputHandler._windowTitleStack, []); - assert.deepEqual((term as any)._inputHandler._iconNameStack, []); + inputHandler.parse('\x1b[23t'); + inputHandler.parse('\x1b[23t'); + inputHandler.parse('\x1b[23t'); + inputHandler.parse('\x1b[23t'); // one more to test "overflow" + assert.deepEqual(inputHandler.windowTitleStack, []); + assert.deepEqual(inputHandler.iconNameStack, []); assert.deepEqual(stack, ['1', '2', '3', '3', '2', '1']); }); it('22/23 - PushTitle/PopTitle with ;1', () => { - const term = new TestTerminal({cols: 10, rows: 10, windowOptions: {pushTitle: true, popTitle: true}}); + bufferService.resize(10, 10); + optionsService.options.windowOptions.pushTitle = true; + optionsService.options.windowOptions.popTitle = true; const stack: string[] = []; - term.onTitleChange(data => stack.push(data)); - term.writeSync('\x1b]0;1\x07'); - term.writeSync('\x1b[22;1t'); - term.writeSync('\x1b]0;2\x07'); - term.writeSync('\x1b[22;1t'); - term.writeSync('\x1b]0;3\x07'); - term.writeSync('\x1b[22;1t'); - assert.deepEqual((term as any)._inputHandler._windowTitleStack, []); - assert.deepEqual((term as any)._inputHandler._iconNameStack, ['1', '2', '3']); + inputHandler.onTitleChange(data => stack.push(data)); + inputHandler.parse('\x1b]0;1\x07'); + inputHandler.parse('\x1b[22;1t'); + inputHandler.parse('\x1b]0;2\x07'); + inputHandler.parse('\x1b[22;1t'); + inputHandler.parse('\x1b]0;3\x07'); + inputHandler.parse('\x1b[22;1t'); + assert.deepEqual(inputHandler.windowTitleStack, []); + assert.deepEqual(inputHandler.iconNameStack, ['1', '2', '3']); assert.deepEqual(stack, ['1', '2', '3']); - term.writeSync('\x1b[23;1t'); - term.writeSync('\x1b[23;1t'); - term.writeSync('\x1b[23;1t'); - term.writeSync('\x1b[23;1t'); // one more to test "overflow" - assert.deepEqual((term as any)._inputHandler._windowTitleStack, []); - assert.deepEqual((term as any)._inputHandler._iconNameStack, []); + inputHandler.parse('\x1b[23;1t'); + inputHandler.parse('\x1b[23;1t'); + inputHandler.parse('\x1b[23;1t'); + inputHandler.parse('\x1b[23;1t'); // one more to test "overflow" + assert.deepEqual(inputHandler.windowTitleStack, []); + assert.deepEqual(inputHandler.iconNameStack, []); assert.deepEqual(stack, ['1', '2', '3']); }); it('22/23 - PushTitle/PopTitle with ;2', () => { - const term = new TestTerminal({cols: 10, rows: 10, windowOptions: {pushTitle: true, popTitle: true}}); + bufferService.resize(10, 10); + optionsService.options.windowOptions.pushTitle = true; + optionsService.options.windowOptions.popTitle = true; const stack: string[] = []; - term.onTitleChange(data => stack.push(data)); - term.writeSync('\x1b]0;1\x07'); - term.writeSync('\x1b[22;2t'); - term.writeSync('\x1b]0;2\x07'); - term.writeSync('\x1b[22;2t'); - term.writeSync('\x1b]0;3\x07'); - term.writeSync('\x1b[22;2t'); - assert.deepEqual((term as any)._inputHandler._windowTitleStack, ['1', '2', '3']); - assert.deepEqual((term as any)._inputHandler._iconNameStack, []); + inputHandler.onTitleChange(data => stack.push(data)); + inputHandler.parse('\x1b]0;1\x07'); + inputHandler.parse('\x1b[22;2t'); + inputHandler.parse('\x1b]0;2\x07'); + inputHandler.parse('\x1b[22;2t'); + inputHandler.parse('\x1b]0;3\x07'); + inputHandler.parse('\x1b[22;2t'); + assert.deepEqual(inputHandler.windowTitleStack, ['1', '2', '3']); + assert.deepEqual(inputHandler.iconNameStack, []); assert.deepEqual(stack, ['1', '2', '3']); - term.writeSync('\x1b[23;2t'); - term.writeSync('\x1b[23;2t'); - term.writeSync('\x1b[23;2t'); - term.writeSync('\x1b[23;2t'); // one more to test "overflow" - assert.deepEqual((term as any)._inputHandler._windowTitleStack, []); - assert.deepEqual((term as any)._inputHandler._iconNameStack, []); + inputHandler.parse('\x1b[23;2t'); + inputHandler.parse('\x1b[23;2t'); + inputHandler.parse('\x1b[23;2t'); + inputHandler.parse('\x1b[23;2t'); // one more to test "overflow" + assert.deepEqual(inputHandler.windowTitleStack, []); + assert.deepEqual(inputHandler.iconNameStack, []); assert.deepEqual(stack, ['1', '2', '3', '3', '2', '1']); }); it('DECCOLM - should only work with "SetWinLines" (24) enabled', () => { // disabled - const term = new TestTerminal({cols: 10, rows: 10}); - term.writeSync('\x1b[?3l'); - assert.equal((term as any)._bufferService.cols, 10); - term.writeSync('\x1b[?3h'); - assert.equal((term as any)._bufferService.cols, 10); + bufferService.resize(10, 10); + inputHandler.parse('\x1b[?3l'); + assert.equal(bufferService.cols, 10); + inputHandler.parse('\x1b[?3h'); + assert.equal(bufferService.cols, 10); // enabled - const term2 = new TestTerminal({cols: 10, rows: 10, windowOptions: {setWinLines: true}}); - term2.writeSync('\x1b[?3l'); - assert.equal((term2 as any)._bufferService.cols, 80); - term2.writeSync('\x1b[?3h'); - assert.equal((term2 as any)._bufferService.cols, 132); + inputHandler.reset(); + optionsService.options.windowOptions.setWinLines = true; + inputHandler.parse('\x1b[?3l'); + assert.equal(bufferService.cols, 80); + inputHandler.parse('\x1b[?3h'); + assert.equal(bufferService.cols, 132); }); }); describe('should correctly reset cells taken by wide chars', () => { - let term: TestTerminal; beforeEach(() => { - term = new TestTerminal({cols: 10, rows: 5, scrollback: 1}); - term.writeSync('¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥'); + bufferService.resize(10, 5); + optionsService.options.scrollback = 1; + inputHandler.parse('¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥'); }); it('print', () => { - term.writeSync('\x1b[H#'); - assert.deepEqual(getLines(term), ['# ¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); - term.writeSync('\x1b[1;6H######'); - assert.deepEqual(getLines(term), ['# ¥ #####', '# ¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); - term.writeSync('#'); - assert.deepEqual(getLines(term), ['# ¥ #####', '##¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); - term.writeSync('#'); - assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); - term.writeSync('\x1b[3;9H#'); - assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥#', '¥¥¥¥¥', '']); - term.writeSync('#'); - assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '¥¥¥¥¥', '']); - term.writeSync('#'); - assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '# ¥¥¥¥', '']); - term.writeSync('\x1b[4;10H#'); - assert.deepEqual(getLines(term), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '# ¥¥¥ #', '']); + inputHandler.parse('\x1b[H#'); + assert.deepEqual(getLines(bufferService), ['# ¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); + inputHandler.parse('\x1b[1;6H######'); + assert.deepEqual(getLines(bufferService), ['# ¥ #####', '# ¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); + inputHandler.parse('#'); + assert.deepEqual(getLines(bufferService), ['# ¥ #####', '##¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); + inputHandler.parse('#'); + assert.deepEqual(getLines(bufferService), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); + inputHandler.parse('\x1b[3;9H#'); + assert.deepEqual(getLines(bufferService), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥#', '¥¥¥¥¥', '']); + inputHandler.parse('#'); + assert.deepEqual(getLines(bufferService), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '¥¥¥¥¥', '']); + inputHandler.parse('#'); + assert.deepEqual(getLines(bufferService), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '# ¥¥¥¥', '']); + inputHandler.parse('\x1b[4;10H#'); + assert.deepEqual(getLines(bufferService), ['# ¥ #####', '### ¥¥¥', '¥¥¥¥##', '# ¥¥¥ #', '']); }); it('EL', () => { - term.writeSync('\x1b[1;6H\x1b[K#'); - assert.deepEqual(getLines(term), ['¥¥ #', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); - term.writeSync('\x1b[2;5H\x1b[1K'); - assert.deepEqual(getLines(term), ['¥¥ #', ' ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); - term.writeSync('\x1b[3;6H\x1b[1K'); - assert.deepEqual(getLines(term), ['¥¥ #', ' ¥¥', ' ¥¥', '¥¥¥¥¥', '']); + inputHandler.parse('\x1b[1;6H\x1b[K#'); + assert.deepEqual(getLines(bufferService), ['¥¥ #', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); + inputHandler.parse('\x1b[2;5H\x1b[1K'); + assert.deepEqual(getLines(bufferService), ['¥¥ #', ' ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); + inputHandler.parse('\x1b[3;6H\x1b[1K'); + assert.deepEqual(getLines(bufferService), ['¥¥ #', ' ¥¥', ' ¥¥', '¥¥¥¥¥', '']); }); it('ICH', () => { - term.writeSync('\x1b[1;6H\x1b[@'); - assert.deepEqual(getLines(term), ['¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); - term.writeSync('\x1b[2;4H\x1b[2@'); - assert.deepEqual(getLines(term), ['¥¥ ¥', '¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); - term.writeSync('\x1b[3;4H\x1b[3@'); - assert.deepEqual(getLines(term), ['¥¥ ¥', '¥ ¥¥', '¥ ¥', '¥¥¥¥¥', '']); - term.writeSync('\x1b[4;4H\x1b[4@'); - assert.deepEqual(getLines(term), ['¥¥ ¥', '¥ ¥¥', '¥ ¥', '¥ ¥', '']); + inputHandler.parse('\x1b[1;6H\x1b[@'); + assert.deepEqual(getLines(bufferService), ['¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); + inputHandler.parse('\x1b[2;4H\x1b[2@'); + assert.deepEqual(getLines(bufferService), ['¥¥ ¥', '¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); + inputHandler.parse('\x1b[3;4H\x1b[3@'); + assert.deepEqual(getLines(bufferService), ['¥¥ ¥', '¥ ¥¥', '¥ ¥', '¥¥¥¥¥', '']); + inputHandler.parse('\x1b[4;4H\x1b[4@'); + assert.deepEqual(getLines(bufferService), ['¥¥ ¥', '¥ ¥¥', '¥ ¥', '¥ ¥', '']); }); it('DCH', () => { - term.writeSync('\x1b[1;6H\x1b[P'); - assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); - term.writeSync('\x1b[2;6H\x1b[2P'); - assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); - term.writeSync('\x1b[3;6H\x1b[3P'); - assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥ ¥', '¥¥ ¥', '¥¥¥¥¥', '']); + inputHandler.parse('\x1b[1;6H\x1b[P'); + assert.deepEqual(getLines(bufferService), ['¥¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); + inputHandler.parse('\x1b[2;6H\x1b[2P'); + assert.deepEqual(getLines(bufferService), ['¥¥ ¥¥', '¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); + inputHandler.parse('\x1b[3;6H\x1b[3P'); + assert.deepEqual(getLines(bufferService), ['¥¥ ¥¥', '¥¥ ¥', '¥¥ ¥', '¥¥¥¥¥', '']); }); it('ECH', () => { - term.writeSync('\x1b[1;6H\x1b[X'); - assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); - term.writeSync('\x1b[2;6H\x1b[2X'); - assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); - term.writeSync('\x1b[3;6H\x1b[3X'); - assert.deepEqual(getLines(term), ['¥¥ ¥¥', '¥¥ ¥', '¥¥ ¥', '¥¥¥¥¥', '']); + inputHandler.parse('\x1b[1;6H\x1b[X'); + assert.deepEqual(getLines(bufferService), ['¥¥ ¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); + inputHandler.parse('\x1b[2;6H\x1b[2X'); + assert.deepEqual(getLines(bufferService), ['¥¥ ¥¥', '¥¥ ¥', '¥¥¥¥¥', '¥¥¥¥¥', '']); + inputHandler.parse('\x1b[3;6H\x1b[3X'); + assert.deepEqual(getLines(bufferService), ['¥¥ ¥¥', '¥¥ ¥', '¥¥ ¥', '¥¥¥¥¥', '']); }); }); describe('DECSTR', () => { - let term: TestTerminal; beforeEach(() => { - term = new TestTerminal({cols: 10, rows: 5, scrollback: 1}); - term.writeSync('01234567890123'); + bufferService.resize(10, 5); + optionsService.options.scrollback = 1; + inputHandler.parse('01234567890123'); }); it('should reset IRM', () => { - term.writeSync('\x1b[4h'); - assert.equal((term as any)._coreService.modes.insertMode, true); - term.writeSync('\x1b[!p'); - assert.equal((term as any)._coreService.modes.insertMode, false); + inputHandler.parse('\x1b[4h'); + assert.equal(coreService.modes.insertMode, true); + inputHandler.parse('\x1b[!p'); + assert.equal(coreService.modes.insertMode, false); }); it('should reset cursor visibility', () => { - term.writeSync('\x1b[?25l'); - assert.equal((term as any)._coreService.isCursorHidden, true); - term.writeSync('\x1b[!p'); - assert.equal((term as any)._coreService.isCursorHidden, false); + inputHandler.parse('\x1b[?25l'); + assert.equal(coreService.isCursorHidden, true); + inputHandler.parse('\x1b[!p'); + assert.equal(coreService.isCursorHidden, false); }); it('should reset scroll margins', () => { - term.writeSync('\x1b[2;4r'); - assert.equal((term as any)._bufferService.buffer.scrollTop, 1); - assert.equal((term as any)._bufferService.buffer.scrollBottom, 3); - term.writeSync('\x1b[!p'); - assert.equal((term as any)._bufferService.buffer.scrollTop, 0); - assert.equal((term as any)._bufferService.buffer.scrollBottom, term.rows - 1); + inputHandler.parse('\x1b[2;4r'); + assert.equal(bufferService.buffer.scrollTop, 1); + assert.equal(bufferService.buffer.scrollBottom, 3); + inputHandler.parse('\x1b[!p'); + assert.equal(bufferService.buffer.scrollTop, 0); + assert.equal(bufferService.buffer.scrollBottom, bufferService.rows - 1); }); it('should reset text attributes', () => { - term.writeSync('\x1b[1;2;32;43m'); - assert.equal(!!term.curAttrData.isBold(), true); - term.writeSync('\x1b[!p'); - assert.equal(!!term.curAttrData.isBold(), false); - assert.equal(term.curAttrData.fg, 0); - assert.equal(term.curAttrData.bg, 0); + inputHandler.parse('\x1b[1;2;32;43m'); + assert.equal(!!inputHandler.curAttrData.isBold(), true); + inputHandler.parse('\x1b[!p'); + assert.equal(!!inputHandler.curAttrData.isBold(), false); + assert.equal(inputHandler.curAttrData.fg, 0); + assert.equal(inputHandler.curAttrData.bg, 0); }); it('should reset DECSC data', () => { - term.writeSync('\x1b7'); - assert.equal((term as any)._bufferService.buffer.savedX, 4); - assert.equal((term as any)._bufferService.buffer.savedY, 1); - term.writeSync('\x1b[!p'); - assert.equal((term as any)._bufferService.buffer.savedX, 0); - assert.equal((term as any)._bufferService.buffer.savedY, 0); + inputHandler.parse('\x1b7'); + assert.equal(bufferService.buffer.savedX, 4); + assert.equal(bufferService.buffer.savedY, 1); + inputHandler.parse('\x1b[!p'); + assert.equal(bufferService.buffer.savedX, 0); + assert.equal(bufferService.buffer.savedY, 0); }); it('should reset DECOM', () => { - term.writeSync('\x1b[?6h'); - assert.equal((term as any)._coreService.decPrivateModes.origin, true); - term.writeSync('\x1b[!p'); - assert.equal((term as any)._coreService.decPrivateModes.origin, false); + inputHandler.parse('\x1b[?6h'); + assert.equal(coreService.decPrivateModes.origin, true); + inputHandler.parse('\x1b[!p'); + assert.equal(coreService.decPrivateModes.origin, false); }); }); }); diff --git a/src/Terminal.test.ts b/src/Terminal.test.ts index 61b40d6c..6488aa43 100644 --- a/src/Terminal.test.ts +++ b/src/Terminal.test.ts @@ -1401,6 +1401,90 @@ describe('Terminal', () => { assert.equal(windowsModeTerminal.buffer.lines.get(2).isWrapped, false); }); }); + it('convertEol setting', function(): void { + // not converting + const termNotConverting = new TestTerminal({cols: 15, rows: 10}); + termNotConverting.writeSync('Hello\nWorld'); + expect(termNotConverting.buffer.lines.get(0).translateToString(false)).equals('Hello '); + expect(termNotConverting.buffer.lines.get(1).translateToString(false)).equals(' World '); + expect(termNotConverting.buffer.lines.get(0).translateToString(true)).equals('Hello'); + expect(termNotConverting.buffer.lines.get(1).translateToString(true)).equals(' World'); + + // converting + const termConverting = new TestTerminal({cols: 15, rows: 10, convertEol: true}); + termConverting.writeSync('Hello\nWorld'); + expect(termConverting.buffer.lines.get(0).translateToString(false)).equals('Hello '); + expect(termConverting.buffer.lines.get(1).translateToString(false)).equals('World '); + expect(termConverting.buffer.lines.get(0).translateToString(true)).equals('Hello'); + expect(termConverting.buffer.lines.get(1).translateToString(true)).equals('World'); + }); + describe('Terminal InputHandler integration', () => { + function getLines(term: TestTerminal, limit: number = term.rows): string[] { + const res: string[] = []; + for (let i = 0; i < limit; ++i) { + res.push(term.buffer.lines.get(i).translateToString(true)); + } + return res; + } + + // This suite cannot live in InputHandler unless Terminal.scroll moved into IBufferService + describe('SL/SR/DECIC/DECDC', () => { + let term: TestTerminal; + beforeEach(() => { + term = new TestTerminal({cols: 5, rows: 5, scrollback: 1}); + }); + it('SL (scrollLeft)', () => { + term.writeSync('12345'.repeat(6)); + term.writeSync('\x1b[ @'); + assert.deepEqual(getLines(term, term.rows + 1), ['12345', '2345', '2345', '2345', '2345', '2345']); + term.writeSync('\x1b[0 @'); + assert.deepEqual(getLines(term, term.rows + 1), ['12345', '345', '345', '345', '345', '345']); + term.writeSync('\x1b[2 @'); + assert.deepEqual(getLines(term, term.rows + 1), ['12345', '5', '5', '5', '5', '5']); + }); + it('SR (scrollRight)', () => { + term.writeSync('12345'.repeat(6)); + term.writeSync('\x1b[ A'); + assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1234', ' 1234', ' 1234', ' 1234', ' 1234']); + term.writeSync('\x1b[0 A'); + assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 123', ' 123', ' 123', ' 123', ' 123']); + term.writeSync('\x1b[2 A'); + assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1', ' 1', ' 1', ' 1', ' 1']); + }); + it('insertColumns (DECIC)', () => { + term.writeSync('12345'.repeat(6)); + term.writeSync('\x1b[3;3H'); + term.writeSync('\x1b[\'}'); + assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']); + term.reset(); + term.writeSync('12345'.repeat(6)); + term.writeSync('\x1b[3;3H'); + term.writeSync('\x1b[1\'}'); + assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']); + term.reset(); + term.writeSync('12345'.repeat(6)); + term.writeSync('\x1b[3;3H'); + term.writeSync('\x1b[2\'}'); + assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 3', '12 3', '12 3', '12 3', '12 3']); + }); + it('deleteColumns (DECDC)', () => { + term.writeSync('12345'.repeat(6)); + term.writeSync('\x1b[3;3H'); + term.writeSync('\x1b[\'~'); + assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']); + term.reset(); + term.writeSync('12345'.repeat(6)); + term.writeSync('\x1b[3;3H'); + term.writeSync('\x1b[1\'~'); + assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']); + term.reset(); + term.writeSync('12345'.repeat(6)); + term.writeSync('\x1b[3;3H'); + term.writeSync('\x1b[2\'~'); + assert.deepEqual(getLines(term, term.rows + 1), ['12345', '125', '125', '125', '125', '125']); + }); + }); + }); }); class TestLinkifier extends Linkifier { diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 70ec8859..6a7460a4 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -219,8 +219,8 @@ export class InputHandler extends Disposable implements IInputHandler { private _workCell: CellData = new CellData(); private _windowTitle = ''; private _iconName = ''; - private _windowTitleStack: string[] = []; - private _iconNameStack: string[] = []; + protected _windowTitleStack: string[] = []; + protected _iconNameStack: string[] = []; private _curAttrData: IAttributeData = DEFAULT_ATTR_DATA.clone(); private _eraseAttrDataInternal: IAttributeData = DEFAULT_ATTR_DATA.clone(); From 212061be502858d21a5d2920f66713c7f418600a Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 26 Apr 2020 08:11:53 -0700 Subject: [PATCH 22/24] Move InputHandler.test into common, fix strict errors --- src/{ => common}/InputHandler.test.ts | 46 +++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) rename src/{ => common}/InputHandler.test.ts (97%) diff --git a/src/InputHandler.test.ts b/src/common/InputHandler.test.ts similarity index 97% rename from src/InputHandler.test.ts rename to src/common/InputHandler.test.ts index 455fc754..8f3ff9af 100644 --- a/src/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -4,7 +4,7 @@ */ import { assert, expect } from 'chai'; -import { InputHandler } from './common/InputHandler'; +import { InputHandler } from 'common/InputHandler'; import { IBufferLine, IAttributeData } from 'common/Types'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; @@ -28,7 +28,7 @@ function getCursor(bufferService: IBufferService): number[] { function getLines(bufferService: IBufferService, limit: number = bufferService.rows): string[] { const res: string[] = []; for (let i = 0; i < limit; ++i) { - res.push(bufferService.buffer.lines.get(i).translateToString(true)); + res.push(bufferService.buffer.lines.get(i)!.translateToString(true)); } return res; } @@ -126,7 +126,7 @@ describe('InputHandler', () => { describe('regression tests', function(): void { function termContent(bufferService: IBufferService, trim: boolean): string[] { const result = []; - for (let i = 0; i < bufferService.rows; ++i) result.push(bufferService.buffer.lines.get(i).translateToString(trim)); + for (let i = 0; i < bufferService.rows; ++i) result.push(bufferService.buffer.lines.get(i)!.translateToString(trim)); return result; } @@ -139,7 +139,7 @@ describe('InputHandler', () => { inputHandler.parse('1234567890'); inputHandler.parse(Array(bufferService.cols - 9).join('a')); inputHandler.parse('1234567890'); - const line1: IBufferLine = bufferService.buffer.lines.get(0); + const line1: IBufferLine = bufferService.buffer.lines.get(0)!; expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890'); // insert one char from params = [0] @@ -176,7 +176,7 @@ describe('InputHandler', () => { inputHandler.parse('1234567890'); inputHandler.parse(Array(bufferService.cols - 9).join('a')); inputHandler.parse('1234567890'); - const line1: IBufferLine = bufferService.buffer.lines.get(0); + const line1: IBufferLine = bufferService.buffer.lines.get(0)!; expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890'); // delete one char from params = [0] @@ -220,19 +220,19 @@ describe('InputHandler', () => { bufferService.buffer.y = 0; bufferService.buffer.x = 70; inputHandler.eraseInLine(Params.fromArray([0])); - expect(bufferService.buffer.lines.get(0).translateToString(false)).equals(Array(71).join('a') + ' '); + expect(bufferService.buffer.lines.get(0)!.translateToString(false)).equals(Array(71).join('a') + ' '); // params[1] - left erase bufferService.buffer.y = 1; bufferService.buffer.x = 70; inputHandler.eraseInLine(Params.fromArray([1])); - expect(bufferService.buffer.lines.get(1).translateToString(false)).equals(Array(71).join(' ') + ' aaaaaaaaa'); + expect(bufferService.buffer.lines.get(1)!.translateToString(false)).equals(Array(71).join(' ') + ' aaaaaaaaa'); // params[1] - left erase bufferService.buffer.y = 2; bufferService.buffer.x = 70; inputHandler.eraseInLine(Params.fromArray([2])); - expect(bufferService.buffer.lines.get(2).translateToString(false)).equals(Array(bufferService.cols + 1).join(' ')); + expect(bufferService.buffer.lines.get(2)!.translateToString(false)).equals(Array(bufferService.cols + 1).join(' ')); }); it('eraseInDisplay', function(): void { @@ -330,11 +330,11 @@ describe('InputHandler', () => { // params[1] left and above with wrap // confirm precondition that line 2 is wrapped - expect(bufferService.buffer.lines.get(2).isWrapped).true; + expect(bufferService.buffer.lines.get(2)!.isWrapped).true; bufferService.buffer.y = 2; bufferService.buffer.x = 40; inputHandler.eraseInDisplay(Params.fromArray([1])); - expect(bufferService.buffer.lines.get(2).isWrapped).false; + expect(bufferService.buffer.lines.get(2)!.isWrapped).false; // reset and add a wrapped line bufferService.buffer.y = 0; @@ -345,11 +345,11 @@ describe('InputHandler', () => { // params[1] left and above with wrap // confirm precondition that line 2 is wrapped - expect(bufferService.buffer.lines.get(2).isWrapped).true; + expect(bufferService.buffer.lines.get(2)!.isWrapped).true; bufferService.buffer.y = 1; bufferService.buffer.x = 90; // Cursor is beyond last column inputHandler.eraseInDisplay(Params.fromArray([1])); - expect(bufferService.buffer.lines.get(2).isWrapped).false; + expect(bufferService.buffer.lines.get(2)!.isWrapped).false; }); }); describe('print', () => { @@ -374,45 +374,45 @@ describe('InputHandler', () => { expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal(''); expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(' TEST'); // Text color of 'TEST' should be red - expect((bufferService.buffer.lines.get(1).loadCell(4, new CellData()).getFgColor())).to.equal(1); + expect((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor())).to.equal(1); }); it('should handle DECSET/DECRST 1047 (alt screen buffer)', () => { handler.parse('\x1b[?1047h\r\n\x1b[31mJUNK\x1b[?1047lTEST'); expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal(''); expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(' TEST'); // Text color of 'TEST' should be red - expect((bufferService.buffer.lines.get(1).loadCell(4, new CellData()).getFgColor())).to.equal(1); + expect((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor())).to.equal(1); }); it('should handle DECSET/DECRST 1048 (alt screen cursor)', () => { handler.parse('\x1b[?1048h\r\n\x1b[31mJUNK\x1b[?1048lTEST'); expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST'); expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('JUNK'); // Text color of 'TEST' should be default - expect(bufferService.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg); + expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg); // Text color of 'JUNK' should be red - expect((bufferService.buffer.lines.get(1).loadCell(0, new CellData()).getFgColor())).to.equal(1); + expect((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor())).to.equal(1); }); it('should handle DECSET/DECRST 1049 (alt screen buffer+cursor)', () => { handler.parse('\x1b[?1049h\r\n\x1b[31mJUNK\x1b[?1049lTEST'); expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST'); expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(''); // Text color of 'TEST' should be default - expect(bufferService.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg); + expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg); }); it('should handle DECSET/DECRST 1049 - maintains saved cursor for alt buffer', () => { handler.parse('\x1b[?1049h\r\n\x1b[31m\x1b[s\x1b[?1049lTEST'); expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST'); // Text color of 'TEST' should be default - expect(bufferService.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg); + expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg); handler.parse('\x1b[?1049h\x1b[uTEST'); expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('TEST'); // Text color of 'TEST' should be red - expect((bufferService.buffer.lines.get(1).loadCell(0, new CellData()).getFgColor())).to.equal(1); + expect((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor())).to.equal(1); }); it('should handle DECSET/DECRST 1049 - clears alt buffer with erase attributes', () => { handler.parse('\x1b[42m\x1b[?1049h'); // Buffer should be filled with green background - expect(bufferService.buffer.lines.get(20).loadCell(10, new CellData()).getBgColor()).to.equal(2); + expect(bufferService.buffer.lines.get(20)!.loadCell(10, new CellData()).getBgColor()).to.equal(2); }); }); @@ -1045,7 +1045,7 @@ describe('InputHandler', () => { }); it('DCH - should delete last cell', () => { inputHandler.parse('0123456789\x1b[P'); - assert.equal(bufferService.buffer.lines.get(0).translateToString(false), '012345678 '); + assert.equal(bufferService.buffer.lines.get(0)!.translateToString(false), '012345678 '); }); it('ECH', () => { bufferService.buffer.x = 10000; @@ -1059,7 +1059,7 @@ describe('InputHandler', () => { }); it('ECH - should delete last cell', () => { inputHandler.parse('0123456789\x1b[X'); - assert.equal(bufferService.buffer.lines.get(0).translateToString(false), '012345678 '); + assert.equal(bufferService.buffer.lines.get(0)!.translateToString(false), '012345678 '); }); it('ICH', () => { bufferService.buffer.x = 10000; @@ -1073,7 +1073,7 @@ describe('InputHandler', () => { }); it('ICH - should delete last cell', () => { inputHandler.parse('0123456789\x1b[@'); - assert.equal(bufferService.buffer.lines.get(0).translateToString(false), '012345678 '); + assert.equal(bufferService.buffer.lines.get(0)!.translateToString(false), '012345678 '); }); }); }); From b79a0a9832a8d9a4c1c89379e4431b4002eac71f Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 26 Apr 2020 08:12:53 -0700 Subject: [PATCH 23/24] Remove unwanted vscode setting --- .vscode/settings.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 38f4dd11..f07ca545 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,4 @@ { "typescript.preferences.importModuleSpecifier": "non-relative", - "typescript.preferences.quoteStyle": "single", - "eslint.enable": true + "typescript.preferences.quoteStyle": "single" } From d085768e8b1f319e873e0c3478272708fbe59b20 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 26 Apr 2020 08:23:38 -0700 Subject: [PATCH 24/24] Fix some lint issues --- test/api/Terminal.api.ts | 2 +- test/benchmark/EscapeSequenceParser.benchmark.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts index 6829fd0a..75e00152 100644 --- a/test/api/Terminal.api.ts +++ b/test/api/Terminal.api.ts @@ -539,7 +539,7 @@ describe('API Integration Tests', function(): void { assert.equal(await page.evaluate(`window.term.buffer.active.getLine(0).translateToString()`), 'norm '); assert.equal(await page.evaluate(`window.term.buffer.normal.getLine(0).translateToString()`), 'norm '); assert.equal(await page.evaluate(`window.term.buffer.alternate.getLine(0)`), undefined); - }) + }); }); it('dispose', async () => { diff --git a/test/benchmark/EscapeSequenceParser.benchmark.ts b/test/benchmark/EscapeSequenceParser.benchmark.ts index 68247715..e4591dde 100644 --- a/test/benchmark/EscapeSequenceParser.benchmark.ts +++ b/test/benchmark/EscapeSequenceParser.benchmark.ts @@ -19,9 +19,9 @@ function toUtf32(s: string): Uint32Array { } class DcsHandler implements IDcsHandler { - hook(params: IParams): void {} - put(data: Uint32Array, start: number, end: number): void {} - unhook(): void {} + public hook(params: IParams): void {} + public put(data: Uint32Array, start: number, end: number): void {} + public unhook(): void {} }