diff --git a/addons/addon-canvas/src/BaseRenderLayer.ts b/addons/addon-canvas/src/BaseRenderLayer.ts index cd3cfa1f..05a877ab 100644 --- a/addons/addon-canvas/src/BaseRenderLayer.ts +++ b/addons/addon-canvas/src/BaseRenderLayer.ts @@ -12,7 +12,6 @@ import { allowRescaling, throwIfFalsy } from 'browser/renderer/shared/RendererUt import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel'; import { IRasterizedGlyph, IRenderDimensions, ISelectionRenderModel, ITextureAtlas } from 'browser/renderer/shared/Types'; import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; -import { EventEmitter, forwardEvent } from 'common/EventEmitter'; import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { isSafari } from 'common/Platform'; import { ICellData } from 'common/Types'; @@ -21,6 +20,7 @@ import { WHITESPACE_CELL_CODE } from 'common/buffer/Constants'; import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services'; import { Terminal } from '@xterm/xterm'; import { IRenderLayer } from './Types'; +import { Emitter, Event } from 'vs/base/common/event'; export abstract class BaseRenderLayer extends Disposable implements IRenderLayer { private _canvas: HTMLCanvasElement; @@ -42,7 +42,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer public get canvas(): HTMLCanvasElement { return this._canvas; } public get cacheCanvas(): HTMLCanvasElement { return this._charAtlas?.pages[0].canvas!; } - private readonly _onAddTextureAtlasCanvas = this.register(new EventEmitter()); + private readonly _onAddTextureAtlasCanvas = this.register(new Emitter()); public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event; constructor( @@ -122,7 +122,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer return; } this._charAtlas = acquireTextureAtlas(this._terminal, this._optionsService.rawOptions, colorSet, this._deviceCellWidth, this._deviceCellHeight, this._deviceCharWidth, this._deviceCharHeight, this._coreBrowserService.dpr); - this._charAtlasDisposable.value = forwardEvent(this._charAtlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas); + this._charAtlasDisposable.value = Event.forward(this._charAtlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas); this._charAtlas.warmUp(); for (let i = 0; i < this._charAtlas.pages.length; i++) { this._bitmapGenerator[i] = new BitmapGenerator(this._charAtlas.pages[i].canvas); diff --git a/addons/addon-canvas/src/CanvasAddon.ts b/addons/addon-canvas/src/CanvasAddon.ts index 4d32b021..91d11f3f 100644 --- a/addons/addon-canvas/src/CanvasAddon.ts +++ b/addons/addon-canvas/src/CanvasAddon.ts @@ -7,19 +7,19 @@ import type { ITerminalAddon, Terminal } from '@xterm/xterm'; import type { CanvasAddon as ICanvasApi } from '@xterm/addon-canvas'; import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; import { ITerminal } from 'browser/Types'; -import { EventEmitter, forwardEvent } from 'common/EventEmitter'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { setTraceLogger } from 'common/services/LogService'; import { IBufferService, IDecorationService, ILogService } from 'common/services/Services'; import { CanvasRenderer } from './CanvasRenderer'; +import { Emitter, Event } from 'vs/base/common/event'; export class CanvasAddon extends Disposable implements ITerminalAddon , ICanvasApi { private _terminal?: Terminal; private _renderer?: CanvasRenderer; - private readonly _onChangeTextureAtlas = this.register(new EventEmitter()); + private readonly _onChangeTextureAtlas = this.register(new Emitter()); public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event; - private readonly _onAddTextureAtlasCanvas = this.register(new EventEmitter()); + private readonly _onAddTextureAtlasCanvas = this.register(new Emitter()); public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event; public get textureAtlas(): HTMLCanvasElement | undefined { @@ -54,8 +54,8 @@ export class CanvasAddon extends Disposable implements ITerminalAddon , ICanvasA setTraceLogger(logService); this._renderer = new CanvasRenderer(terminal, screenElement, linkifier, bufferService, charSizeService, optionsService, characterJoinerService, coreService, coreBrowserService, decorationService, themeService); - this.register(forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas)); - this.register(forwardEvent(this._renderer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas)); + this.register(Event.forward(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas)); + this.register(Event.forward(this._renderer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas)); renderService.setRenderer(this._renderer); renderService.handleResize(bufferService.cols, bufferService.rows); diff --git a/addons/addon-canvas/src/CanvasRenderer.ts b/addons/addon-canvas/src/CanvasRenderer.ts index 148ae736..7d08c7b1 100644 --- a/addons/addon-canvas/src/CanvasRenderer.ts +++ b/addons/addon-canvas/src/CanvasRenderer.ts @@ -9,7 +9,6 @@ import { observeDevicePixelDimensions } from 'browser/renderer/shared/DevicePixe import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils'; import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types'; import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; -import { EventEmitter, forwardEvent } from 'common/EventEmitter'; import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { IBufferService, ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; import { Terminal } from '@xterm/xterm'; @@ -18,6 +17,7 @@ import { LinkRenderLayer } from './LinkRenderLayer'; import { SelectionRenderLayer } from './SelectionRenderLayer'; import { TextRenderLayer } from './TextRenderLayer'; import { IRenderLayer } from './Types'; +import { Emitter, Event } from 'vs/base/common/event'; export class CanvasRenderer extends Disposable implements IRenderer { private _renderLayers: IRenderLayer[]; @@ -26,11 +26,11 @@ export class CanvasRenderer extends Disposable implements IRenderer { public dimensions: IRenderDimensions; - private readonly _onRequestRedraw = this.register(new EventEmitter()); + private readonly _onRequestRedraw = this.register(new Emitter()); public readonly onRequestRedraw = this._onRequestRedraw.event; - private readonly _onChangeTextureAtlas = this.register(new EventEmitter()); + private readonly _onChangeTextureAtlas = this.register(new Emitter()); public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event; - private readonly _onAddTextureAtlasCanvas = this.register(new EventEmitter()); + private readonly _onAddTextureAtlasCanvas = this.register(new Emitter()); public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event; constructor( @@ -55,7 +55,7 @@ export class CanvasRenderer extends Disposable implements IRenderer { new CursorRenderLayer(this._terminal, this._screenElement, 3, this._onRequestRedraw, this._bufferService, this._optionsService, coreService, this._coreBrowserService, decorationService, _themeService) ]; for (const layer of this._renderLayers) { - forwardEvent(layer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas); + Event.forward(layer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas); } this.dimensions = createRenderDimensions(); this._devicePixelRatio = this._coreBrowserService.dpr; diff --git a/addons/addon-canvas/src/CursorRenderLayer.ts b/addons/addon-canvas/src/CursorRenderLayer.ts index adc1db18..d9dfe8ce 100644 --- a/addons/addon-canvas/src/CursorRenderLayer.ts +++ b/addons/addon-canvas/src/CursorRenderLayer.ts @@ -6,7 +6,6 @@ import { CursorBlinkStateManager } from 'browser/renderer/shared/CursorBlinkStateManager'; import { IRenderDimensions, IRequestRedrawEvent } from 'browser/renderer/shared/Types'; import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; -import { IEventEmitter } from 'common/EventEmitter'; import { MutableDisposable } from 'common/Lifecycle'; import { isFirefox } from 'common/Platform'; import { ICellData } from 'common/Types'; @@ -14,6 +13,7 @@ import { CellData } from 'common/buffer/CellData'; import { IBufferService, ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; import { Terminal } from '@xterm/xterm'; import { BaseRenderLayer } from './BaseRenderLayer'; +import type { Emitter } from 'vs/base/common/event'; interface ICursorState { x: number; @@ -33,7 +33,7 @@ export class CursorRenderLayer extends BaseRenderLayer { terminal: Terminal, container: HTMLElement, zIndex: number, - private readonly _onRequestRedraw: IEventEmitter, + private readonly _onRequestRedraw: Emitter, bufferService: IBufferService, optionsService: IOptionsService, private readonly _coreService: ICoreService, diff --git a/addons/addon-canvas/src/Types.ts b/addons/addon-canvas/src/Types.ts index 73e6c836..9b06129e 100644 --- a/addons/addon-canvas/src/Types.ts +++ b/addons/addon-canvas/src/Types.ts @@ -4,8 +4,8 @@ */ import { IDisposable } from 'common/Types'; -import { IEvent } from 'common/EventEmitter'; import { IRenderDimensions } from 'browser/renderer/shared/Types'; +import type { Event } from 'vs/base/common/event'; export interface IRequestRedrawEvent { start: number; @@ -23,7 +23,7 @@ export interface IRenderer extends IDisposable { * Fires when the renderer is requesting to be redrawn on the next animation * frame but is _not_ a result of content changing (eg. selection changes). */ - readonly onRequestRedraw: IEvent; + readonly onRequestRedraw: Event; handleDevicePixelRatioChange(): void; handleResize(cols: number, rows: number): void; @@ -42,7 +42,7 @@ export interface IRenderLayer extends IDisposable { readonly canvas: HTMLCanvasElement; readonly cacheCanvas: HTMLCanvasElement; - readonly onAddTextureAtlasCanvas: IEvent; + readonly onAddTextureAtlasCanvas: Event; /** * Called when the terminal loses focus. */ diff --git a/addons/addon-canvas/src/tsconfig.json b/addons/addon-canvas/src/tsconfig.json index 1ed8c2ac..093acefb 100644 --- a/addons/addon-canvas/src/tsconfig.json +++ b/addons/addon-canvas/src/tsconfig.json @@ -18,6 +18,9 @@ "browser/*": [ "../../../src/browser/*" ], + "vs/*": [ + "../../../src/vs/*" + ], "@xterm/addon-canvas": [ "../typings/addon-canvas.d.ts" ] @@ -38,6 +41,9 @@ }, { "path": "../../../src/browser" + }, + { + "path": "../../../src/vs" } ] } diff --git a/addons/addon-canvas/webpack.config.js b/addons/addon-canvas/webpack.config.js index e0c7fde2..c38d9286 100644 --- a/addons/addon-canvas/webpack.config.js +++ b/addons/addon-canvas/webpack.config.js @@ -26,7 +26,8 @@ module.exports = { extensions: [ '.js' ], alias: { common: path.resolve('../../out/common'), - browser: path.resolve('../../out/browser') + browser: path.resolve('../../out/browser'), + vs: path.resolve('../../out/vs') } }, output: { diff --git a/addons/addon-search/src/SearchAddon.ts b/addons/addon-search/src/SearchAddon.ts index d42f4d27..8726b5b3 100644 --- a/addons/addon-search/src/SearchAddon.ts +++ b/addons/addon-search/src/SearchAddon.ts @@ -5,8 +5,8 @@ import type { Terminal, IDisposable, ITerminalAddon, IDecoration } from '@xterm/xterm'; import type { SearchAddon as ISearchApi } from '@xterm/addon-search'; -import { EventEmitter } from 'common/EventEmitter'; import { Disposable, toDisposable, disposeArray, MutableDisposable, getDisposeArrayDisposable } from 'common/Lifecycle'; +import { Emitter } from 'vs/base/common/event'; export interface ISearchOptions { regex?: boolean; @@ -80,7 +80,7 @@ export class SearchAddon extends Disposable implements ITerminalAddon , ISearchA private _linesCacheTimeoutId = 0; private _linesCacheDisposables = new MutableDisposable(); - private readonly _onDidChangeResults = this.register(new EventEmitter<{ resultIndex: number, resultCount: number }>()); + private readonly _onDidChangeResults = this.register(new Emitter<{ resultIndex: number, resultCount: number }>()); public readonly onDidChangeResults = this._onDidChangeResults.event; constructor(options?: Partial) { diff --git a/addons/addon-search/src/tsconfig.json b/addons/addon-search/src/tsconfig.json index bd3e48ad..a926a66f 100644 --- a/addons/addon-search/src/tsconfig.json +++ b/addons/addon-search/src/tsconfig.json @@ -18,6 +18,9 @@ "common/*": [ "../../../src/common/*" ], + "vs/*": [ + "../../../src/vs/*" + ], "@xterm/addon-search" : [ "../typings/addon-search.d.ts" ] @@ -30,6 +33,9 @@ "references": [ { "path": "../../../src/common" + }, + { + "path": "../../../src/vs" } ] } diff --git a/addons/addon-search/webpack.config.js b/addons/addon-search/webpack.config.js index 78580548..adc3d4bd 100644 --- a/addons/addon-search/webpack.config.js +++ b/addons/addon-search/webpack.config.js @@ -25,7 +25,8 @@ module.exports = { modules: ['./node_modules'], extensions: [ '.js' ], alias: { - common: path.resolve('../../out/common') + common: path.resolve('../../out/common'), + vs: path.resolve('../../out/vs') } }, output: { diff --git a/addons/addon-unicode11/webpack.config.js b/addons/addon-unicode11/webpack.config.js index 746d2581..9b66049f 100644 --- a/addons/addon-unicode11/webpack.config.js +++ b/addons/addon-unicode11/webpack.config.js @@ -25,7 +25,8 @@ module.exports = { modules: ['./node_modules'], extensions: [ '.js' ], alias: { - common: path.resolve('../../out/common') + common: path.resolve('../../out/common'), + vs: path.resolve('../../out/vs') } }, output: { diff --git a/addons/addon-webgl/src/WebglAddon.ts b/addons/addon-webgl/src/WebglAddon.ts index 2b3a7f96..dd1d0cba 100644 --- a/addons/addon-webgl/src/WebglAddon.ts +++ b/addons/addon-webgl/src/WebglAddon.ts @@ -7,25 +7,25 @@ import type { ITerminalAddon, Terminal } from '@xterm/xterm'; import type { WebglAddon as IWebglApi } from '@xterm/addon-webgl'; import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; import { ITerminal } from 'browser/Types'; -import { EventEmitter, forwardEvent } from 'common/EventEmitter'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { getSafariVersion, isSafari } from 'common/Platform'; import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services'; import { IWebGL2RenderingContext } from './Types'; import { WebglRenderer } from './WebglRenderer'; import { setTraceLogger } from 'common/services/LogService'; +import { Emitter, Event } from 'vs/base/common/event'; export class WebglAddon extends Disposable implements ITerminalAddon , IWebglApi { private _terminal?: Terminal; private _renderer?: WebglRenderer; - private readonly _onChangeTextureAtlas = this.register(new EventEmitter()); + private readonly _onChangeTextureAtlas = this.register(new Emitter()); public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event; - private readonly _onAddTextureAtlasCanvas = this.register(new EventEmitter()); + private readonly _onAddTextureAtlasCanvas = this.register(new Emitter()); public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event; - private readonly _onRemoveTextureAtlasCanvas = this.register(new EventEmitter()); + private readonly _onRemoveTextureAtlasCanvas = this.register(new Emitter()); public readonly onRemoveTextureAtlasCanvas = this._onRemoveTextureAtlasCanvas.event; - private readonly _onContextLoss = this.register(new EventEmitter()); + private readonly _onContextLoss = this.register(new Emitter()); public readonly onContextLoss = this._onContextLoss.event; constructor( @@ -81,10 +81,10 @@ export class WebglAddon extends Disposable implements ITerminalAddon , IWebglApi themeService, this._preserveDrawingBuffer )); - this.register(forwardEvent(this._renderer.onContextLoss, this._onContextLoss)); - this.register(forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas)); - this.register(forwardEvent(this._renderer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas)); - this.register(forwardEvent(this._renderer.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas)); + this.register(Event.forward(this._renderer.onContextLoss, this._onContextLoss)); + this.register(Event.forward(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas)); + this.register(Event.forward(this._renderer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas)); + this.register(Event.forward(this._renderer.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas)); renderService.setRenderer(this._renderer); this.register(toDisposable(() => { diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index fa178652..4e7bb803 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -12,7 +12,6 @@ import { observeDevicePixelDimensions } from 'browser/renderer/shared/DevicePixe import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils'; import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ITextureAtlas } from 'browser/renderer/shared/Types'; import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; -import { EventEmitter, forwardEvent } from 'common/EventEmitter'; import { Disposable, MutableDisposable, getDisposeArrayDisposable, toDisposable } from 'common/Lifecycle'; import { CharData, IBufferLine, ICellData } from 'common/Types'; import { AttributeData } from 'common/buffer/AttributeData'; @@ -26,6 +25,7 @@ import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_EXT_OFFSET import { IWebGL2RenderingContext } from './Types'; import { LinkRenderLayer } from './renderLayer/LinkRenderLayer'; import { IRenderLayer } from './renderLayer/Types'; +import { Emitter, Event } from 'vs/base/common/event'; export class WebglRenderer extends Disposable implements IRenderer { private _renderLayers: IRenderLayer[]; @@ -51,15 +51,15 @@ export class WebglRenderer extends Disposable implements IRenderer { private _isAttached: boolean; private _contextRestorationTimeout: number | undefined; - private readonly _onChangeTextureAtlas = this.register(new EventEmitter()); + private readonly _onChangeTextureAtlas = this.register(new Emitter()); public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event; - private readonly _onAddTextureAtlasCanvas = this.register(new EventEmitter()); + private readonly _onAddTextureAtlasCanvas = this.register(new Emitter()); public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event; - private readonly _onRemoveTextureAtlasCanvas = this.register(new EventEmitter()); + private readonly _onRemoveTextureAtlasCanvas = this.register(new Emitter()); public readonly onRemoveTextureAtlasCanvas = this._onRemoveTextureAtlasCanvas.event; - private readonly _onRequestRedraw = this.register(new EventEmitter()); + private readonly _onRequestRedraw = this.register(new Emitter()); public readonly onRequestRedraw = this._onRequestRedraw.event; - private readonly _onContextLoss = this.register(new EventEmitter()); + private readonly _onContextLoss = this.register(new Emitter()); public readonly onContextLoss = this._onContextLoss.event; constructor( @@ -277,8 +277,8 @@ export class WebglRenderer extends Disposable implements IRenderer { if (this._charAtlas !== atlas) { this._onChangeTextureAtlas.fire(atlas.pages[0].canvas); this._charAtlasDisposable.value = getDisposeArrayDisposable([ - forwardEvent(atlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas), - forwardEvent(atlas.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas) + Event.forward(atlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas), + Event.forward(atlas.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas) ]); } this._charAtlas = atlas; diff --git a/addons/addon-webgl/src/tsconfig.json b/addons/addon-webgl/src/tsconfig.json index 924d3762..55d754d7 100644 --- a/addons/addon-webgl/src/tsconfig.json +++ b/addons/addon-webgl/src/tsconfig.json @@ -18,6 +18,9 @@ "browser/*": [ "../../../src/browser/*" ], + "vs/*": [ + "../../../src/vs/*" + ], "@xterm/addon-webgl": [ "../typings/addon-webgl.d.ts" ] @@ -39,6 +42,9 @@ }, { "path": "../../../src/browser" + }, + { + "path": "../../../src/vs" } ] } diff --git a/addons/addon-webgl/webpack.config.js b/addons/addon-webgl/webpack.config.js index 7365acff..2eab992e 100644 --- a/addons/addon-webgl/webpack.config.js +++ b/addons/addon-webgl/webpack.config.js @@ -26,7 +26,8 @@ module.exports = { extensions: [ '.js' ], alias: { common: path.resolve('../../out/common'), - browser: path.resolve('../../out/browser') + browser: path.resolve('../../out/browser'), + vs: path.resolve('../../out/vs') } }, output: { diff --git a/demo/client.ts b/demo/client.ts index 57208184..c2b5ee3c 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -235,6 +235,7 @@ if (document.location.pathname === '/test') { document.getElementById('htmlserialize').addEventListener('click', htmlSerializeButtonHandler); document.getElementById('custom-glyph').addEventListener('click', writeCustomGlyphHandler); document.getElementById('load-test').addEventListener('click', loadTest); + document.getElementById('load-test-long-lines').addEventListener('click', loadTestLongLines); document.getElementById('print-cjk').addEventListener('click', addCjk); document.getElementById('print-cjk-sgr').addEventListener('click', addCjkRandomSgr); document.getElementById('powerline-symbol-test').addEventListener('click', powerlineSymbolTest); @@ -840,6 +841,39 @@ function loadTest(): void { }); } +function loadTestLongLines(): void { + const rendererName = addons.webgl.instance ? 'webgl' : !!addons.canvas.instance ? 'canvas' : 'dom'; + const testData = []; + let byteCount = 0; + for (let i = 0; i < 50; i++) { + const count = 1 + Math.floor(Math.random() * 500); + byteCount += count + 2; + const data = new Uint8Array(count + 2); + data[0] = 0x0A; // \n + for (let i = 1; i < count + 1; i++) { + data[i] = 0x61 + Math.floor(Math.random() * (0x7A - 0x61)); + } + // End each line with \r so the cursor remains constant, this is what ls/tree do and improves + // performance significantly due to the cursor DOM element not needing to change + data[data.length - 1] = 0x0D; // \r + testData.push(data); + } + const start = performance.now(); + for (let i = 0; i < 1024 * 50; i++) { + for (const d of testData) { + term.write(d); + } + } + // Wait for all data to be parsed before evaluating time + term.write('', () => { + const time = Math.round(performance.now() - start); + const mbs = ((byteCount / 1024) * (1 / (time / 1000))).toFixed(2); + term.write(`\n\r\nWrote ${byteCount}kB in ${time}ms (${mbs}MB/s) using the (${rendererName} renderer)`); + // Send ^C to get a new prompt + term._core._onData.fire('\x03'); + }); +} + function powerlineSymbolTest(): void { function s(char: string): string { return `${char} \x1b[7m${char}\x1b[0m `; diff --git a/demo/index.html b/demo/index.html index 238c9886..06451847 100644 --- a/demo/index.html +++ b/demo/index.html @@ -86,6 +86,7 @@
Performance
+
diff --git a/package.json b/package.json index 894ec18a..479d9dc6 100644 --- a/package.json +++ b/package.json @@ -52,8 +52,8 @@ "test-integration-webkit": "node ./bin/test_integration.js --workers=75% \"--project=WebKit\"", "test-integration-debug": "node ./bin/test_integration.js --workers=1 --headed --timeout=30000", "benchmark": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json", - "benchmark-baseline": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-tsc/test-benchmark/test/benchmark/*benchmark.js", - "benchmark-eval": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-tsc/test-benchmark/test/benchmark/*benchmark.js", + "benchmark-baseline": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-test/benchmark/*benchmark.js", + "benchmark-eval": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-test/benchmark/*benchmark.js", "clean": "rm -rf lib out addons/*/lib addons/*/out", "vtfeatures": "node bin/extract_vtfeatures.js src/**/*.ts src/*.ts", "prepackage": "npm run build", diff --git a/src/browser/CoreBrowserTerminal.ts b/src/browser/CoreBrowserTerminal.ts index b9329bcf..235cb79f 100644 --- a/src/browser/CoreBrowserTerminal.ts +++ b/src/browser/CoreBrowserTerminal.ts @@ -44,7 +44,6 @@ import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, ILinkPr import { ThemeService } from 'browser/services/ThemeService'; import { channels, color } from 'common/Color'; import { CoreTerminal } from 'common/CoreTerminal'; -import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter'; import { MutableDisposable, toDisposable } from 'common/Lifecycle'; import * as Browser from 'common/Platform'; import { ColorRequestType, CoreMouseAction, CoreMouseButton, CoreMouseEventType, IColorEvent, ITerminalOptions, KeyboardResultType, SpecialColorIndex } from 'common/Types'; @@ -58,6 +57,7 @@ import { IDecorationService } from 'common/services/Services'; import { WindowsOptionsReportType } from '../common/InputHandler'; import { AccessibilityManager } from './AccessibilityManager'; import { Linkifier } from './Linkifier'; +import { Emitter, Event } from 'vs/base/common/event'; export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { public textarea: HTMLTextAreaElement | undefined; @@ -121,29 +121,29 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { private _compositionHelper: ICompositionHelper | undefined; private _accessibilityManager: MutableDisposable = this.register(new MutableDisposable()); - private readonly _onCursorMove = this.register(new EventEmitter()); + private readonly _onCursorMove = this.register(new Emitter()); public readonly onCursorMove = this._onCursorMove.event; - private readonly _onKey = this.register(new EventEmitter<{ key: string, domEvent: KeyboardEvent }>()); + private readonly _onKey = this.register(new Emitter<{ key: string, domEvent: KeyboardEvent }>()); public readonly onKey = this._onKey.event; - private readonly _onRender = this.register(new EventEmitter<{ start: number, end: number }>()); + private readonly _onRender = this.register(new Emitter<{ start: number, end: number }>()); public readonly onRender = this._onRender.event; - private readonly _onSelectionChange = this.register(new EventEmitter()); + private readonly _onSelectionChange = this.register(new Emitter()); public readonly onSelectionChange = this._onSelectionChange.event; - private readonly _onTitleChange = this.register(new EventEmitter()); + private readonly _onTitleChange = this.register(new Emitter()); public readonly onTitleChange = this._onTitleChange.event; - private readonly _onBell = this.register(new EventEmitter()); + private readonly _onBell = this.register(new Emitter()); public readonly onBell = this._onBell.event; - private _onFocus = this.register(new EventEmitter()); - public get onFocus(): IEvent { return this._onFocus.event; } - private _onBlur = this.register(new EventEmitter()); - public get onBlur(): IEvent { return this._onBlur.event; } - private _onA11yCharEmitter = this.register(new EventEmitter()); - public get onA11yChar(): IEvent { return this._onA11yCharEmitter.event; } - private _onA11yTabEmitter = this.register(new EventEmitter()); - public get onA11yTab(): IEvent { return this._onA11yTabEmitter.event; } - private _onWillOpen = this.register(new EventEmitter()); - public get onWillOpen(): IEvent { return this._onWillOpen.event; } + private _onFocus = this.register(new Emitter()); + public get onFocus(): Event { return this._onFocus.event; } + private _onBlur = this.register(new Emitter()); + public get onBlur(): Event { return this._onBlur.event; } + private _onA11yCharEmitter = this.register(new Emitter()); + public get onA11yChar(): Event { return this._onA11yCharEmitter.event; } + private _onA11yTabEmitter = this.register(new Emitter()); + public get onA11yTab(): Event { return this._onA11yTabEmitter.event; } + private _onWillOpen = this.register(new Emitter()); + public get onWillOpen(): Event { return this._onWillOpen.event; } constructor( options: Partial = {} @@ -160,15 +160,15 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { // Setup InputHandler listeners this.register(this._inputHandler.onRequestBell(() => this._onBell.fire())); - this.register(this._inputHandler.onRequestRefreshRows((start, end) => this.refresh(start, end))); + this.register(this._inputHandler.onRequestRefreshRows((e) => this.refresh(e?.start ?? 0, e?.end ?? (this.rows - 1)))); this.register(this._inputHandler.onRequestSendFocus(() => this._reportFocus())); this.register(this._inputHandler.onRequestReset(() => this.reset())); this.register(this._inputHandler.onRequestWindowsOptionsReport(type => this._reportWindowsOptions(type))); this.register(this._inputHandler.onColor((event) => this._handleColorEvent(event))); - this.register(forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove)); - 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(Event.forward(this._inputHandler.onCursorMove, this._onCursorMove)); + this.register(Event.forward(this._inputHandler.onTitleChange, this._onTitleChange)); + this.register(Event.forward(this._inputHandler.onA11yChar, this._onA11yCharEmitter)); + this.register(Event.forward(this._inputHandler.onA11yTab, this._onA11yTabEmitter)); // Setup listeners this.register(this._bufferService.onResize(e => this._afterResize(e.cols, e.rows))); @@ -672,7 +672,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { * Note: 'mousedown' currently is "always on" and not managed * by onProtocolChange. */ - const requestedEvents: { [key: string]: ((ev: Event) => void) | null } = { + const requestedEvents: { [key: string]: ((ev: MouseEvent | WheelEvent) => void) | null } = { mouseup: null, wheel: null, mousedrag: null, @@ -1292,7 +1292,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { } // TODO: Remove cancel function and cancelEvents option - public cancel(ev: Event, force?: boolean): boolean | undefined { + public cancel(ev: MouseEvent | WheelEvent | KeyboardEvent | InputEvent, force?: boolean): boolean | undefined { if (!this.options.cancelEvents && !force) { return; } diff --git a/src/browser/Linkifier.ts b/src/browser/Linkifier.ts index ac37e42f..d01f0ebe 100644 --- a/src/browser/Linkifier.ts +++ b/src/browser/Linkifier.ts @@ -5,11 +5,11 @@ import { addDisposableDomListener } from 'browser/Lifecycle'; import { IBufferCellPosition, ILink, ILinkDecorations, ILinkWithState, ILinkifier2, ILinkifierEvent } from 'browser/Types'; -import { EventEmitter } from 'common/EventEmitter'; import { Disposable, disposeArray, getDisposeArrayDisposable, toDisposable } from 'common/Lifecycle'; import { IDisposable } from 'common/Types'; import { IBufferService } from 'common/services/Services'; import { ILinkProviderService, IMouseService, IRenderService } from './services/Services'; +import { Emitter } from 'vs/base/common/event'; export class Linkifier extends Disposable implements ILinkifier2 { public get currentLink(): ILinkWithState | undefined { return this._currentLink; } @@ -23,9 +23,9 @@ export class Linkifier extends Disposable implements ILinkifier2 { private _activeProviderReplies: Map | undefined; private _activeLine: number = -1; - private readonly _onShowLinkUnderline = this.register(new EventEmitter()); + private readonly _onShowLinkUnderline = this.register(new Emitter()); public readonly onShowLinkUnderline = this._onShowLinkUnderline.event; - private readonly _onHideLinkUnderline = this.register(new EventEmitter()); + private readonly _onHideLinkUnderline = this.register(new Emitter()); public readonly onHideLinkUnderline = this._onHideLinkUnderline.event; constructor( diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index 45af130f..cf878cbc 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -4,7 +4,6 @@ */ import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration } from '@xterm/xterm'; -import { IEvent, EventEmitter } from 'common/EventEmitter'; import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services'; import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types'; import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet, IBufferElementProvider } from 'browser/Types'; @@ -19,6 +18,7 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types'; import { css } from 'common/Color'; import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils'; +import { Emitter, type Event } from 'vs/base/common/event'; export class TestTerminal extends CoreBrowserTerminal { public get curAttrData(): IAttributeData { return (this as any)._inputHandler._curAttrData; } @@ -30,23 +30,23 @@ export class TestTerminal extends CoreBrowserTerminal { } export class MockTerminal implements ITerminal { - public onBlur!: IEvent; - public onFocus!: IEvent; - public onA11yChar!: IEvent; - public onWriteParsed!: IEvent; - public onA11yTab!: IEvent; - public onCursorMove!: IEvent; - public onLineFeed!: IEvent; - public onSelectionChange!: IEvent; - public onData!: IEvent; - public onBinary!: IEvent; - public onTitleChange!: IEvent; - public onBell!: IEvent; - public onScroll!: IEvent; - public onWillOpen!: IEvent; - public onKey!: IEvent<{ key: string, domEvent: KeyboardEvent }>; - public onRender!: IEvent<{ start: number, end: number }>; - public onResize!: IEvent<{ cols: number, rows: number }>; + public onBlur!: Event; + public onFocus!: Event; + public onA11yChar!: Event; + public onWriteParsed!: Event; + public onA11yTab!: Event; + public onCursorMove!: Event; + public onLineFeed!: Event; + public onSelectionChange!: Event; + public onData!: Event; + public onBinary!: Event; + public onTitleChange!: Event; + public onBell!: Event; + public onScroll!: Event; + public onWillOpen!: Event; + public onKey!: Event<{ key: string, domEvent: KeyboardEvent }>; + public onRender!: Event<{ start: number, end: number }>; + public onResize!: Event<{ cols: number, rows: number }>; public markers!: IMarker[]; public linkifier: ILinkifier2 | undefined; public coreMouseService!: ICoreMouseService; @@ -193,7 +193,7 @@ export class MockTerminal implements ITerminal { public scrollToRow(absoluteRow: number): number { throw new Error('Method not implemented.'); } - public cancel(ev: Event, force?: boolean): void { + public cancel(ev: MouseEvent | WheelEvent | KeyboardEvent | InputEvent, force?: boolean): void { throw new Error('Method not implemented.'); } public log(text: string): void { @@ -267,9 +267,9 @@ export class MockBuffer implements IBuffer { } export class MockRenderer implements IRenderer { - public onRequestRedraw!: IEvent; - public onCanvasResize!: IEvent<{ width: number, height: number }>; - public onRender!: IEvent<{ start: number, end: number }>; + public onRequestRedraw!: Event; + public onCanvasResize!: Event<{ width: number, height: number }>; + public onRender!: Event<{ start: number, end: number }>; public dispose(): void { throw new Error('Method not implemented.'); } @@ -302,7 +302,7 @@ export class MockRenderer implements IRenderer { } export class MockViewport implements IViewport { - private readonly _onRequestScrollLines = new EventEmitter<{ amount: number, suppressScrollEvent: boolean }>(); + private readonly _onRequestScrollLines = new Emitter<{ amount: number, suppressScrollEvent: boolean }>(); public readonly onRequestScrollLines = this._onRequestScrollLines.event; public dispose(): void { throw new Error('Method not implemented.'); @@ -356,8 +356,8 @@ export class MockCompositionHelper implements ICompositionHelper { } export class MockCoreBrowserService implements ICoreBrowserService { - public onDprChange = new EventEmitter().event; - public onWindowChange = new EventEmitter().event; + public onDprChange = new Emitter().event; + public onWindowChange = new Emitter().event; public serviceBrand: undefined; public isFocused: boolean = true; public get window(): Window & typeof globalThis { @@ -372,7 +372,7 @@ export class MockCoreBrowserService implements ICoreBrowserService { export class MockCharSizeService implements ICharSizeService { public serviceBrand: undefined; public get hasValidSize(): boolean { return this.width > 0 && this.height > 0; } - public onCharSizeChange: IEvent = new EventEmitter().event; + public onCharSizeChange: Event = new Emitter().event; constructor(public width: number, public height: number) {} public measure(): void {} } @@ -390,10 +390,10 @@ export class MockMouseService implements IMouseService { export class MockRenderService implements IRenderService { public serviceBrand: undefined; - public onDimensionsChange: IEvent = new EventEmitter().event; - public onRenderedViewportChange: IEvent<{ start: number, end: number }, void> = new EventEmitter<{ start: number, end: number }>().event; - public onRender: 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; + public onDimensionsChange: Event = new Emitter().event; + public onRenderedViewportChange: Event<{ start: number, end: number }> = new Emitter<{ start: number, end: number }>().event; + public onRender: Event<{ start: number, end: number }> = new Emitter<{ start: number, end: number }>().event; + public onRefreshRequest: Event<{ start: number, end: number}> = new Emitter<{ start: number, end: number }>().event; public dimensions: IRenderDimensions = createRenderDimensions(); public refreshRows(start: number, end: number): void { throw new Error('Method not implemented.'); @@ -464,10 +464,10 @@ export class MockSelectionService implements ISelectionService { public hasSelection: boolean = false; public selectionStart: [number, number] | undefined; public selectionEnd: [number, number] | undefined; - public onLinuxMouseSelection = new EventEmitter().event; - public onRequestRedraw = new EventEmitter().event; - public onRequestScrollLines = new EventEmitter().event; - public onSelectionChange = new EventEmitter().event; + public onLinuxMouseSelection = new Emitter().event; + public onRequestRedraw = new Emitter().event; + public onRequestScrollLines = new Emitter().event; + public onSelectionChange = new Emitter().event; public disable(): void { throw new Error('Method not implemented.'); } @@ -511,7 +511,7 @@ export class MockSelectionService implements ISelectionService { export class MockThemeService implements IThemeService{ public serviceBrand: undefined; - public onChangeColors = new EventEmitter().event; + public onChangeColors = new Emitter().event; public restoreColor(slot?: ColorIndex | undefined): void { throw new Error('Method not implemented.'); } diff --git a/src/browser/Types.ts b/src/browser/Types.ts index 43e1f9e2..10e60411 100644 --- a/src/browser/Types.ts +++ b/src/browser/Types.ts @@ -3,11 +3,11 @@ * @license MIT */ -import { IEvent } from 'common/EventEmitter'; import { CharData, IColor, ICoreTerminal, ITerminalOptions } from 'common/Types'; import { IBuffer } from 'common/buffer/Types'; import { IDisposable, Terminal as ITerminalApi } from '@xterm/xterm'; import { channels, css } from 'common/Color'; +import type { Event } from 'vs/base/common/event'; /** * A portion of the public API that are implemented identially internally and simply passed through. @@ -21,13 +21,13 @@ export interface ITerminal extends InternalPassthroughApis, ICoreTerminal { linkifier: ILinkifier2 | undefined; options: Required; - onBlur: IEvent; - onFocus: IEvent; - onA11yChar: IEvent; - onA11yTab: IEvent; - onWillOpen: IEvent; + onBlur: Event; + onFocus: Event; + onA11yChar: Event; + onA11yTab: Event; + onWillOpen: Event; - cancel(ev: Event, force?: boolean): boolean | void; + cancel(ev: MouseEvent | WheelEvent | KeyboardEvent | InputEvent, force?: boolean): boolean | void; } export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean; @@ -98,7 +98,7 @@ export interface IPartialColorSet { export interface IViewport extends IDisposable { scrollBarWidth: number; - readonly onRequestScrollLines: IEvent<{ amount: number, suppressScrollEvent: boolean }>; + readonly onRequestScrollLines: Event<{ amount: number, suppressScrollEvent: boolean }>; syncScrollArea(immediate?: boolean, force?: boolean): void; getLinesScrolled(ev: WheelEvent): number; getBufferElements(startLine: number, endLine?: number): { bufferElements: HTMLElement[], cursorElement?: HTMLElement }; @@ -128,8 +128,8 @@ export interface ILinkWithState { } export interface ILinkifier2 extends IDisposable { - onShowLinkUnderline: IEvent; - onHideLinkUnderline: IEvent; + onShowLinkUnderline: Event; + onHideLinkUnderline: Event; readonly currentLink: ILinkWithState | undefined; } diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index 64fcba2c..221420a2 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -5,18 +5,18 @@ import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; import { ViewportConstants } from 'browser/shared/Constants'; -import { EventEmitter, runAndSubscribe } from 'common/EventEmitter'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { IBufferService, ICoreMouseService, IOptionsService } from 'common/services/Services'; import { CoreMouseEventType } from 'common/Types'; import { scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import type { ScrollableElementChangeOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; +import { Emitter, Event } from 'vs/base/common/event'; import { Scrollable, ScrollbarVisibility, type ScrollEvent } from 'vs/base/common/scrollable'; export class Viewport extends Disposable { - protected _onRequestScrollLines = this.register(new EventEmitter()); + protected _onRequestScrollLines = this.register(new Emitter()); public readonly onRequestScrollLines = this._onRequestScrollLines.event; private _scrollableElement: SmoothScrollableElement; @@ -70,7 +70,7 @@ export class Viewport extends Disposable { })); this._scrollableElement.setScrollDimensions({ height: 0, scrollHeight: 0 }); - this.register(runAndSubscribe(themeService.onChangeColors, () => { + this.register(Event.runAndSubscribe(themeService.onChangeColors, () => { this._scrollableElement.getDomNode().style.backgroundColor = themeService.colors.background.css; })); element.appendChild(this._scrollableElement.getDomNode()); @@ -79,7 +79,7 @@ export class Viewport extends Disposable { this._styleElement = coreBrowserService.window.document.createElement('style'); screenElement.appendChild(this._styleElement); this.register(toDisposable(() => this._styleElement.remove())); - this.register(runAndSubscribe(themeService.onChangeColors, () => { + this.register(Event.runAndSubscribe(themeService.onChangeColors, () => { this._styleElement.textContent = [ `.xterm .xterm-scrollable-element > .scrollbar > .slider {`, ` background: ${themeService.colors.scrollbarSliderBackground.css};`, diff --git a/src/browser/public/Terminal.ts b/src/browser/public/Terminal.ts index 572bac04..d41b0883 100644 --- a/src/browser/public/Terminal.ts +++ b/src/browser/public/Terminal.ts @@ -6,7 +6,6 @@ import * as Strings from 'browser/LocalizableStrings'; import { CoreBrowserTerminal as TerminalCore } from 'browser/CoreBrowserTerminal'; import { IBufferRange, ITerminal } from 'browser/Types'; -import { IEvent } from 'common/EventEmitter'; import { Disposable } from 'common/Lifecycle'; import { ITerminalOptions } from 'common/Types'; import { AddonManager } from 'common/public/AddonManager'; @@ -14,6 +13,7 @@ import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi'; import { ParserApi } from 'common/public/ParserApi'; import { UnicodeApi } from 'common/public/UnicodeApi'; import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm'; +import type { Event } from 'vs/base/common/event'; /** * The set of options that only have an effect when set in the Terminal constructor. @@ -68,18 +68,18 @@ export class Terminal extends Disposable implements ITerminalApi { } } - public get onBell(): IEvent { return this._core.onBell; } - public get onBinary(): IEvent { return this._core.onBinary; } - public get onCursorMove(): IEvent { return this._core.onCursorMove; } - public get onData(): IEvent { return this._core.onData; } - public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._core.onKey; } - public get onLineFeed(): IEvent { return this._core.onLineFeed; } - public get onRender(): IEvent<{ start: number, end: number }> { return this._core.onRender; } - public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; } - public get onScroll(): IEvent { return this._core.onScroll; } - public get onSelectionChange(): IEvent { return this._core.onSelectionChange; } - public get onTitleChange(): IEvent { return this._core.onTitleChange; } - public get onWriteParsed(): IEvent { return this._core.onWriteParsed; } + public get onBell(): Event { return this._core.onBell; } + public get onBinary(): Event { return this._core.onBinary; } + public get onCursorMove(): Event { return this._core.onCursorMove; } + public get onData(): Event { return this._core.onData; } + public get onKey(): Event<{ key: string, domEvent: KeyboardEvent }> { return this._core.onKey; } + public get onLineFeed(): Event { return this._core.onLineFeed; } + public get onRender(): Event<{ start: number, end: number }> { return this._core.onRender; } + public get onResize(): Event<{ cols: number, rows: number }> { return this._core.onResize; } + public get onScroll(): Event { return this._core.onScroll; } + public get onSelectionChange(): Event { return this._core.onSelectionChange; } + public get onTitleChange(): Event { return this._core.onTitleChange; } + public get onWriteParsed(): Event { return this._core.onWriteParsed; } public get element(): HTMLElement | undefined { return this._core.element; } public get parser(): IParser { diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index 92d152f0..ce8721c0 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -12,9 +12,9 @@ import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ISelectionRenderMode import { ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from 'browser/Types'; import { color } from 'common/Color'; -import { EventEmitter } from 'common/EventEmitter'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { IBufferService, IInstantiationService, IOptionsService } from 'common/services/Services'; +import { Emitter } from 'vs/base/common/event'; const TERMINAL_CLASS_PREFIX = 'xterm-dom-renderer-owner-'; @@ -45,7 +45,7 @@ export class DomRenderer extends Disposable implements IRenderer { public dimensions: IRenderDimensions; - public readonly onRequestRedraw = this.register(new EventEmitter()).event; + public readonly onRequestRedraw = this.register(new Emitter()).event; constructor( private readonly _terminal: ITerminal, diff --git a/src/browser/renderer/shared/TextureAtlas.ts b/src/browser/renderer/shared/TextureAtlas.ts index af2cafb8..a86cd459 100644 --- a/src/browser/renderer/shared/TextureAtlas.ts +++ b/src/browser/renderer/shared/TextureAtlas.ts @@ -9,13 +9,13 @@ import { tryDrawCustomChar } from 'browser/renderer/shared/CustomGlyphs'; import { computeNextVariantOffset, treatGlyphAsBackgroundColor, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; import { IBoundingBox, ICharAtlasConfig, IRasterizedGlyph, ITextureAtlas } from 'browser/renderer/shared/Types'; import { NULL_COLOR, channels, color, rgba } from 'common/Color'; -import { EventEmitter } from 'common/EventEmitter'; import { FourKeyMap } from 'common/MultiKeyMap'; import { IdleTaskQueue } from 'common/TaskQueue'; import { IColor } from 'common/Types'; import { AttributeData } from 'common/buffer/AttributeData'; import { Attributes, DEFAULT_COLOR, DEFAULT_EXT, UnderlineStyle } from 'common/buffer/Constants'; import { IUnicodeService } from 'common/services/Services'; +import { Emitter } from 'vs/base/common/event'; /** * A shared object which is used to draw nothing for a particular cell. @@ -79,9 +79,9 @@ export class TextureAtlas implements ITextureAtlas { public static maxAtlasPages: number | undefined; public static maxTextureSize: number | undefined; - private readonly _onAddTextureAtlasCanvas = new EventEmitter(); + private readonly _onAddTextureAtlasCanvas = new Emitter(); public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event; - private readonly _onRemoveTextureAtlasCanvas = new EventEmitter(); + private readonly _onRemoveTextureAtlasCanvas = new Emitter(); public readonly onRemoveTextureAtlasCanvas = this._onRemoveTextureAtlasCanvas.event; constructor( diff --git a/src/browser/renderer/shared/Types.ts b/src/browser/renderer/shared/Types.ts index c0d5e9d6..3b3c7659 100644 --- a/src/browser/renderer/shared/Types.ts +++ b/src/browser/renderer/shared/Types.ts @@ -6,7 +6,7 @@ import { FontWeight, Terminal } from '@xterm/xterm'; import { IColorSet, ITerminal } from 'browser/Types'; import { IDisposable } from 'common/Types'; -import { IEvent } from 'common/EventEmitter'; +import type { Event } from 'vs/base/common/event'; export interface ICharAtlasConfig { customGlyphs: boolean; @@ -71,7 +71,7 @@ export interface IRenderer extends IDisposable { * Fires when the renderer is requesting to be redrawn on the next animation * frame but is _not_ a result of content changing (eg. selection changes). */ - readonly onRequestRedraw: IEvent; + readonly onRequestRedraw: Event; dispose(): void; handleDevicePixelRatioChange(): void; @@ -89,8 +89,8 @@ export interface IRenderer extends IDisposable { export interface ITextureAtlas extends IDisposable { readonly pages: { canvas: HTMLCanvasElement, version: number }[]; - onAddTextureAtlasCanvas: IEvent; - onRemoveTextureAtlasCanvas: IEvent; + onAddTextureAtlasCanvas: Event; + onRemoveTextureAtlasCanvas: Event; /** * Warm up the texture atlas, adding common glyphs to avoid slowing early frame. diff --git a/src/browser/services/CharSizeService.ts b/src/browser/services/CharSizeService.ts index da14b67d..faf81d97 100644 --- a/src/browser/services/CharSizeService.ts +++ b/src/browser/services/CharSizeService.ts @@ -4,9 +4,9 @@ */ import { IOptionsService } from 'common/services/Services'; -import { EventEmitter } from 'common/EventEmitter'; import { ICharSizeService } from 'browser/services/Services'; import { Disposable } from 'common/Lifecycle'; +import { Emitter } from 'vs/base/common/event'; export class CharSizeService extends Disposable implements ICharSizeService { public serviceBrand: undefined; @@ -17,7 +17,7 @@ export class CharSizeService extends Disposable implements ICharSizeService { public get hasValidSize(): boolean { return this.width > 0 && this.height > 0; } - private readonly _onCharSizeChange = this.register(new EventEmitter()); + private readonly _onCharSizeChange = this.register(new Emitter()); public readonly onCharSizeChange = this._onCharSizeChange.event; constructor( diff --git a/src/browser/services/CoreBrowserService.ts b/src/browser/services/CoreBrowserService.ts index a6c066b2..268afb77 100644 --- a/src/browser/services/CoreBrowserService.ts +++ b/src/browser/services/CoreBrowserService.ts @@ -5,8 +5,8 @@ import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { ICoreBrowserService } from './Services'; -import { EventEmitter, forwardEvent } from 'common/EventEmitter'; import { addDisposableDomListener } from 'browser/Lifecycle'; +import { Emitter, Event } from 'vs/base/common/event'; export class CoreBrowserService extends Disposable implements ICoreBrowserService { public serviceBrand: undefined; @@ -15,9 +15,9 @@ export class CoreBrowserService extends Disposable implements ICoreBrowserServic private _cachedIsFocused: boolean | undefined = undefined; private _screenDprMonitor = this.register(new ScreenDprMonitor(this._window)); - private readonly _onDprChange = this.register(new EventEmitter()); + private readonly _onDprChange = this.register(new Emitter()); public readonly onDprChange = this._onDprChange.event; - private readonly _onWindowChange = this.register(new EventEmitter()); + private readonly _onWindowChange = this.register(new Emitter()); public readonly onWindowChange = this._onWindowChange.event; constructor( @@ -29,7 +29,7 @@ export class CoreBrowserService extends Disposable implements ICoreBrowserServic // Monitor device pixel ratio this.register(this.onWindowChange(w => this._screenDprMonitor.setWindow(w))); - this.register(forwardEvent(this._screenDprMonitor.onDprChange, this._onDprChange)); + this.register(Event.forward(this._screenDprMonitor.onDprChange, this._onDprChange)); this.register( addDisposableDomListener(this._textarea, 'focus', () => (this._isFocused = true)) @@ -80,7 +80,7 @@ class ScreenDprMonitor extends Disposable { private _resolutionMediaMatchList: MediaQueryList | undefined; private _windowResizeListener = this.register(new MutableDisposable()); - private readonly _onDprChange = this.register(new EventEmitter()); + private readonly _onDprChange = this.register(new Emitter()); public readonly onDprChange = this._onDprChange.event; constructor(private _parentWindow: Window) { diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index d4f2be46..119813cf 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -7,10 +7,10 @@ import { RenderDebouncer } from 'browser/RenderDebouncer'; import { IRenderDebouncerWithCallback } from 'browser/Types'; import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types'; import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; -import { EventEmitter } from 'common/EventEmitter'; import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { DebouncedIdleTask } from 'common/TaskQueue'; import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services'; +import { Emitter } from 'vs/base/common/event'; interface ISelectionState { start: [number, number] | undefined; @@ -38,13 +38,13 @@ export class RenderService extends Disposable implements IRenderService { columnSelectMode: false }; - private readonly _onDimensionsChange = this.register(new EventEmitter()); + private readonly _onDimensionsChange = this.register(new Emitter()); public readonly onDimensionsChange = this._onDimensionsChange.event; - private readonly _onRenderedViewportChange = this.register(new EventEmitter<{ start: number, end: number }>()); + private readonly _onRenderedViewportChange = this.register(new Emitter<{ start: number, end: number }>()); public readonly onRenderedViewportChange = this._onRenderedViewportChange.event; - private readonly _onRender = this.register(new EventEmitter<{ start: number, end: number }>()); + private readonly _onRender = this.register(new Emitter<{ start: number, end: number }>()); public readonly onRender = this._onRender.event; - private readonly _onRefreshRequest = this.register(new EventEmitter<{ start: number, end: number }>()); + private readonly _onRefreshRequest = this.register(new Emitter<{ start: number, end: number }>()); public readonly onRefreshRequest = this._onRefreshRequest.event; public get dimensions(): IRenderDimensions { return this._renderer.value!.dimensions; } diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index e134802e..df309599 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -9,7 +9,6 @@ import { moveToCellSequence } from 'browser/input/MoveToCell'; import { SelectionModel } from 'browser/selection/SelectionModel'; import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types'; import { ICoreBrowserService, IMouseService, IRenderService, ISelectionService } from 'browser/services/Services'; -import { EventEmitter } from 'common/EventEmitter'; import { Disposable, toDisposable } from 'common/Lifecycle'; import * as Browser from 'common/Platform'; import { IBufferLine, IDisposable } from 'common/Types'; @@ -17,6 +16,7 @@ import { getRangeLength } from 'common/buffer/BufferRange'; import { CellData } from 'common/buffer/CellData'; import { IBuffer } from 'common/buffer/Types'; import { IBufferService, ICoreService, IOptionsService } from 'common/services/Services'; +import { Emitter } from 'vs/base/common/event'; /** * The number of pixels the mouse needs to be above or below the viewport in @@ -111,13 +111,13 @@ export class SelectionService extends Disposable implements ISelectionService { private _oldSelectionStart: [number, number] | undefined = undefined; private _oldSelectionEnd: [number, number] | undefined = undefined; - private readonly _onLinuxMouseSelection = this.register(new EventEmitter()); + private readonly _onLinuxMouseSelection = this.register(new Emitter()); public readonly onLinuxMouseSelection = this._onLinuxMouseSelection.event; - private readonly _onRedrawRequest = this.register(new EventEmitter()); + private readonly _onRedrawRequest = this.register(new Emitter()); public readonly onRequestRedraw = this._onRedrawRequest.event; - private readonly _onSelectionChange = this.register(new EventEmitter()); + private readonly _onSelectionChange = this.register(new Emitter()); public readonly onSelectionChange = this._onSelectionChange.event; - private readonly _onRequestScrollLines = this.register(new EventEmitter()); + private readonly _onRequestScrollLines = this.register(new Emitter()); public readonly onRequestScrollLines = this._onRequestScrollLines.event; constructor( diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts index a82eabd0..19a8fcbc 100644 --- a/src/browser/services/Services.ts +++ b/src/browser/services/Services.ts @@ -3,12 +3,12 @@ * @license MIT */ -import { IEvent } from 'common/EventEmitter'; import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types'; import { IColorSet, ILink, ReadonlyColorSet } from 'browser/Types'; import { ISelectionRedrawRequestEvent as ISelectionRequestRedrawEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types'; import { createDecorator } from 'common/services/ServiceRegistry'; import { AllColorIndex, IDisposable } from 'common/Types'; +import type { Event } from 'vs/base/common/event'; export const ICharSizeService = createDecorator('CharSizeService'); export interface ICharSizeService { @@ -18,7 +18,7 @@ export interface ICharSizeService { readonly height: number; readonly hasValidSize: boolean; - readonly onCharSizeChange: IEvent; + readonly onCharSizeChange: Event; measure(): void; } @@ -29,8 +29,8 @@ export interface ICoreBrowserService { readonly isFocused: boolean; - readonly onDprChange: IEvent; - readonly onWindowChange: IEvent; + readonly onDprChange: Event; + readonly onWindowChange: Event; /** * Gets or sets the parent window that the terminal is rendered into. DOM and rendering APIs (e.g. @@ -61,17 +61,17 @@ export const IRenderService = createDecorator('RenderService'); export interface IRenderService extends IDisposable { serviceBrand: undefined; - onDimensionsChange: IEvent; + onDimensionsChange: Event; /** * Fires when buffer changes are rendered. This does not fire when only cursor * or selections are rendered. */ - onRenderedViewportChange: IEvent<{ start: number, end: number }>; + onRenderedViewportChange: Event<{ start: number, end: number }>; /** * Fires on render */ - onRender: IEvent<{ start: number, end: number }>; - onRefreshRequest: IEvent<{ start: number, end: number }>; + onRender: Event<{ start: number, end: number }>; + onRefreshRequest: Event<{ start: number, end: number }>; dimensions: IRenderDimensions; @@ -101,10 +101,10 @@ export interface ISelectionService { readonly selectionStart: [number, number] | undefined; readonly selectionEnd: [number, number] | undefined; - readonly onLinuxMouseSelection: IEvent; - readonly onRequestRedraw: IEvent; - readonly onRequestScrollLines: IEvent; - readonly onSelectionChange: IEvent; + readonly onLinuxMouseSelection: Event; + readonly onRequestRedraw: Event; + readonly onRequestScrollLines: Event; + readonly onSelectionChange: Event; disable(): void; enable(): void; @@ -136,7 +136,7 @@ export interface IThemeService { readonly colors: ReadonlyColorSet; - readonly onChangeColors: IEvent; + readonly onChangeColors: Event; restoreColor(slot?: AllColorIndex): void; /** diff --git a/src/browser/services/ThemeService.ts b/src/browser/services/ThemeService.ts index 3e6dd481..ce6633c5 100644 --- a/src/browser/services/ThemeService.ts +++ b/src/browser/services/ThemeService.ts @@ -7,10 +7,10 @@ import { ColorContrastCache } from 'browser/ColorContrastCache'; import { IThemeService } from 'browser/services/Services'; import { DEFAULT_ANSI_COLORS, IColorContrastCache, IColorSet, ReadonlyColorSet } from 'browser/Types'; import { color, css, NULL_COLOR } from 'common/Color'; -import { EventEmitter } from 'common/EventEmitter'; import { Disposable } from 'common/Lifecycle'; import { IOptionsService, ITheme } from 'common/services/Services'; import { AllColorIndex, IColor, SpecialColorIndex } from 'common/Types'; +import { Emitter } from 'vs/base/common/event'; interface IRestoreColorSet { foreground: IColor; @@ -40,7 +40,7 @@ export class ThemeService extends Disposable implements IThemeService { public get colors(): ReadonlyColorSet { return this._colors; } - private readonly _onChangeColors = this.register(new EventEmitter()); + private readonly _onChangeColors = this.register(new Emitter()); public readonly onChangeColors = this._onChangeColors.event; constructor( diff --git a/src/common/CircularList.ts b/src/common/CircularList.ts index 622a1eff..94351034 100644 --- a/src/common/CircularList.ts +++ b/src/common/CircularList.ts @@ -4,8 +4,8 @@ */ import { ICircularList } from 'common/Types'; -import { EventEmitter } from 'common/EventEmitter'; import { Disposable } from 'common/Lifecycle'; +import { Emitter } from 'vs/base/common/event'; export interface IInsertEvent { index: number; @@ -26,11 +26,11 @@ export class CircularList extends Disposable implements ICircularList { private _startIndex: number; private _length: number; - public readonly onDeleteEmitter = this.register(new EventEmitter()); + public readonly onDeleteEmitter = this.register(new Emitter()); public readonly onDelete = this.onDeleteEmitter.event; - public readonly onInsertEmitter = this.register(new EventEmitter()); + public readonly onInsertEmitter = this.register(new Emitter()); public readonly onInsert = this.onInsertEmitter.event; - public readonly onTrimEmitter = this.register(new EventEmitter()); + public readonly onTrimEmitter = this.register(new Emitter()); public readonly onTrim = this.onTrimEmitter.event; constructor( diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 132be05b..a93eb934 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -29,7 +29,6 @@ import { BufferService, MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/Buffe import { OptionsService } from 'common/services/OptionsService'; import { IDisposable, IAttributeData, ICoreTerminal, IScrollEvent } from 'common/Types'; import { CoreService } from 'common/services/CoreService'; -import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter'; import { CoreMouseService } from 'common/services/CoreMouseService'; import { UnicodeService } from 'common/services/UnicodeService'; import { CharsetService } from 'common/services/CharsetService'; @@ -39,6 +38,7 @@ import { IBufferSet } from 'common/buffer/Types'; import { InputHandler } from 'common/InputHandler'; import { WriteBuffer } from 'common/input/WriteBuffer'; import { OscLinkService } from 'common/services/OscLinkService'; +import { Emitter, Event } from 'vs/base/common/event'; // Only trigger this warning a single time per session let hasWriteSyncWarnHappened = false; @@ -59,26 +59,26 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { private _writeBuffer: WriteBuffer; private _windowsWrappingHeuristics = this.register(new MutableDisposable()); - private readonly _onBinary = this.register(new EventEmitter()); + private readonly _onBinary = this.register(new Emitter()); public readonly onBinary = this._onBinary.event; - private readonly _onData = this.register(new EventEmitter()); + private readonly _onData = this.register(new Emitter()); public readonly onData = this._onData.event; - protected _onLineFeed = this.register(new EventEmitter()); + protected _onLineFeed = this.register(new Emitter()); public readonly onLineFeed = this._onLineFeed.event; - private readonly _onResize = this.register(new EventEmitter<{ cols: number, rows: number }>()); + private readonly _onResize = this.register(new Emitter<{ cols: number, rows: number }>()); public readonly onResize = this._onResize.event; - protected readonly _onWriteParsed = this.register(new EventEmitter()); + protected readonly _onWriteParsed = this.register(new Emitter()); public readonly onWriteParsed = this._onWriteParsed.event; /** * Internally we track the source of the scroll but this is meaningless outside the library so * it's filtered out. */ - protected _onScrollApi?: EventEmitter; - protected _onScroll = this.register(new EventEmitter()); - public get onScroll(): IEvent { + protected _onScrollApi?: Emitter; + protected _onScroll = this.register(new Emitter()); + public get onScroll(): Event { if (!this._onScrollApi) { - this._onScrollApi = this.register(new EventEmitter()); + this._onScrollApi = this.register(new Emitter()); this._onScroll.event(ev => { this._onScrollApi?.fire(ev.position); }); @@ -123,13 +123,13 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { // Register input handler and handle/forward events this._inputHandler = this.register(new InputHandler(this._bufferService, this._charsetService, this.coreService, this._logService, this.optionsService, this._oscLinkService, this.coreMouseService, this.unicodeService)); - this.register(forwardEvent(this._inputHandler.onLineFeed, this._onLineFeed)); + this.register(Event.forward(this._inputHandler.onLineFeed, this._onLineFeed)); this.register(this._inputHandler); // Setup listeners - 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(Event.forward(this._bufferService.onResize, this._onResize)); + this.register(Event.forward(this.coreService.onData, this._onData)); + this.register(Event.forward(this.coreService.onBinary, this._onBinary)); this.register(this.coreService.onRequestScrollToBottom(() => this.scrollToBottom(true))); this.register(this.coreService.onUserInput(() => this._writeBuffer.handleUserInput())); this.register(this.optionsService.onMultipleOptionChange(['windowsMode', 'windowsPty'], () => this._handleWindowsPtyOptionChange())); @@ -139,7 +139,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { })); // Setup WriteBuffer this._writeBuffer = this.register(new WriteBuffer((data, promiseResult) => this._inputHandler.parse(data, promiseResult))); - this.register(forwardEvent(this._writeBuffer.onWriteParsed, this._onWriteParsed)); + this.register(Event.forward(this._writeBuffer.onWriteParsed, this._onWriteParsed)); } public write(data: string | Uint8Array, callback?: () => void): void { diff --git a/src/common/EventEmitter.test.ts b/src/common/EventEmitter.test.ts deleted file mode 100644 index e9013a99..00000000 --- a/src/common/EventEmitter.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2019 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { assert } from 'chai'; -import { EventEmitter } from 'common/EventEmitter'; - -describe('EventEmitter', () => { - it('should fire listeners multiple times', () => { - const order: string[] = []; - const emitter = new EventEmitter(); - emitter.event(data => order.push(data + 'a')); - emitter.event(data => order.push(data + 'b')); - emitter.fire(1); - emitter.fire(2); - assert.deepEqual(order, [ '1a', '1b', '2a', '2b' ]); - }); - - it('should not fire listeners once disposed', () => { - const order: string[] = []; - const emitter = new EventEmitter(); - emitter.event(data => order.push(data + 'a')); - const disposeB = emitter.event(data => order.push(data + 'b')); - emitter.event(data => order.push(data + 'c')); - emitter.fire(1); - disposeB.dispose(); - emitter.fire(2); - assert.deepEqual(order, [ '1a', '1b', '1c', '2a', '2c' ]); - }); -}); diff --git a/src/common/EventEmitter.ts b/src/common/EventEmitter.ts deleted file mode 100644 index 06ddf7fb..00000000 --- a/src/common/EventEmitter.ts +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2019 The xterm.js authors. All rights reserved. - * @license MIT - */ - -import { IDisposable } from 'common/Types'; - -interface IListener { - (arg1: T, arg2: U): void; -} - -export interface IEvent { - (listener: (arg1: T, arg2: U) => any): IDisposable; -} - -export interface IEventEmitter { - event: IEvent; - fire(arg1: T, arg2: U): void; - dispose(): void; -} - -export class EventEmitter implements IEventEmitter { - private _listeners: Set> = new Set(); - private _event?: IEvent; - private _disposed: boolean = false; - - public get event(): IEvent { - if (!this._event) { - this._event = (listener: (arg1: T, arg2: U) => any) => { - this._listeners.add(listener); - const disposable = { - dispose: () => { - if (!this._disposed) { - this._listeners.delete(listener); - } - } - }; - return disposable; - }; - } - return this._event; - } - - public fire(arg1: T, arg2: U): void { - const queue: IListener[] = []; - for (const l of this._listeners.values()) { - queue.push(l); - } - for (let i = 0; i < queue.length; i++) { - queue[i].call(undefined, arg1, arg2); - } - } - - public dispose(): void { - this.clearListeners(); - this._disposed = true; - } - - public clearListeners(): void { - if (this._listeners) { - this._listeners.clear(); - } - } -} - -export function forwardEvent(from: IEvent, to: IEventEmitter): IDisposable { - return from(e => to.fire(e)); -} - -export function runAndSubscribe(event: IEvent, handler: (e: T | undefined) => any): IDisposable { - handler(undefined); - return event(e => handler(e)); -} diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index bef0eaa7..861b759d 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -11,7 +11,6 @@ import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser'; import { Disposable } from 'common/Lifecycle'; import { StringToUtf32, stringFromCodePoint, Utf8ToUtf32 } from 'common/input/TextDecoder'; import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; -import { EventEmitter } from 'common/EventEmitter'; import { IParsingState, IEscapeSequenceParser, IParams, IFunctionIdentifier } from 'common/parser/Types'; import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content, UnderlineStyle } from 'common/buffer/Constants'; import { CellData } from 'common/buffer/CellData'; @@ -22,6 +21,7 @@ import { OscHandler } from 'common/parser/OscParser'; import { DcsHandler } from 'common/parser/DcsParser'; import { IBuffer } from 'common/buffer/Types'; import { parseColor } from 'common/input/XParseColor'; +import { Emitter } from 'vs/base/common/event'; /** * Map collect to glevel. Used in `selectCharset`. @@ -132,32 +132,32 @@ export class InputHandler extends Disposable implements IInputHandler { private _activeBuffer: IBuffer; - private readonly _onRequestBell = this.register(new EventEmitter()); + private readonly _onRequestBell = this.register(new Emitter()); public readonly onRequestBell = this._onRequestBell.event; - private readonly _onRequestRefreshRows = this.register(new EventEmitter()); + private readonly _onRequestRefreshRows = this.register(new Emitter<{ start: number, end: number } | undefined>()); public readonly onRequestRefreshRows = this._onRequestRefreshRows.event; - private readonly _onRequestReset = this.register(new EventEmitter()); + private readonly _onRequestReset = this.register(new Emitter()); public readonly onRequestReset = this._onRequestReset.event; - private readonly _onRequestSendFocus = this.register(new EventEmitter()); + private readonly _onRequestSendFocus = this.register(new Emitter()); public readonly onRequestSendFocus = this._onRequestSendFocus.event; - private readonly _onRequestSyncScrollBar = this.register(new EventEmitter()); + private readonly _onRequestSyncScrollBar = this.register(new Emitter()); public readonly onRequestSyncScrollBar = this._onRequestSyncScrollBar.event; - private readonly _onRequestWindowsOptionsReport = this.register(new EventEmitter()); + private readonly _onRequestWindowsOptionsReport = this.register(new Emitter()); public readonly onRequestWindowsOptionsReport = this._onRequestWindowsOptionsReport.event; - private readonly _onA11yChar = this.register(new EventEmitter()); + private readonly _onA11yChar = this.register(new Emitter()); public readonly onA11yChar = this._onA11yChar.event; - private readonly _onA11yTab = this.register(new EventEmitter()); + private readonly _onA11yTab = this.register(new Emitter()); public readonly onA11yTab = this._onA11yTab.event; - private readonly _onCursorMove = this.register(new EventEmitter()); + private readonly _onCursorMove = this.register(new Emitter()); public readonly onCursorMove = this._onCursorMove.event; - private readonly _onLineFeed = this.register(new EventEmitter()); + private readonly _onLineFeed = this.register(new Emitter()); public readonly onLineFeed = this._onLineFeed.event; - private readonly _onScroll = this.register(new EventEmitter()); + private readonly _onScroll = this.register(new Emitter()); public readonly onScroll = this._onScroll.event; - private readonly _onTitleChange = this.register(new EventEmitter()); + private readonly _onTitleChange = this.register(new Emitter()); public readonly onTitleChange = this._onTitleChange.event; - private readonly _onColor = this.register(new EventEmitter()); + private readonly _onColor = this.register(new Emitter()); public readonly onColor = this._onColor.event; private _parseStack: IParseStack = { @@ -500,7 +500,10 @@ export class InputHandler extends Disposable implements IInputHandler { const viewportEnd = this._dirtyRowTracker.end + (this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp); const viewportStart = this._dirtyRowTracker.start + (this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp); if (viewportStart < this._bufferService.rows) { - this._onRequestRefreshRows.fire(Math.min(viewportStart, this._bufferService.rows - 1), Math.min(viewportEnd, this._bufferService.rows - 1)); + this._onRequestRefreshRows.fire({ + start: Math.min(viewportStart, this._bufferService.rows - 1), + end: Math.min(viewportEnd, this._bufferService.rows - 1) + }); } } @@ -1943,7 +1946,7 @@ export class InputHandler extends Disposable implements IInputHandler { case 1047: // alt screen buffer this._bufferService.buffers.activateAltBuffer(this._eraseAttrData()); this._coreService.isCursorInitialized = true; - this._onRequestRefreshRows.fire(0, this._bufferService.rows - 1); + this._onRequestRefreshRows.fire(undefined); this._onRequestSyncScrollBar.fire(); break; case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste) @@ -2171,7 +2174,7 @@ export class InputHandler extends Disposable implements IInputHandler { this.restoreCursor(); } this._coreService.isCursorInitialized = true; - this._onRequestRefreshRows.fire(0, this._bufferService.rows - 1); + this._onRequestRefreshRows.fire(undefined); this._onRequestSyncScrollBar.fire(); break; case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste) diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index fbf3ebcd..8c9634db 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -5,7 +5,6 @@ import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, ICoreMouseService, ICharsetService, UnicodeCharProperties, UnicodeCharWidth, IUnicodeService, IUnicodeVersionProvider, LogLevelEnum, IDecorationService, IInternalDecoration, IOscLinkService } from 'common/services/Services'; import { UnicodeService } from 'common/services/UnicodeService'; -import { IEvent, EventEmitter } from 'common/EventEmitter'; import { clone } from 'common/Clone'; import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; import { IBufferSet, IBuffer } from 'common/buffer/Types'; @@ -13,13 +12,14 @@ import { BufferSet } from 'common/buffer/BufferSet'; import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType, ICharset, IModes, IAttributeData, IOscLinkData, IDisposable } from 'common/Types'; import { UnicodeV6 } from 'common/input/UnicodeV6'; import { IDecorationOptions, IDecoration } from '@xterm/xterm'; +import { Emitter, type Event } from 'vs/base/common/event'; 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; - public onScroll: IEvent = new EventEmitter().event; + public onResize: Event<{ cols: number, rows: number }> = new Emitter<{ cols: number, rows: number }>().event; + public onScroll: Event = new Emitter().event; public isUserScrolling: boolean = false; constructor( public cols: number, @@ -62,7 +62,7 @@ export class MockCoreMouseService implements ICoreMouseService { public addProtocol(name: string): void { } public reset(): void { } public triggerMouseEvent(event: ICoreMouseEvent): boolean { return false; } - public onProtocolChange: IEvent = new EventEmitter().event; + public onProtocolChange: Event = new Emitter().event; public explainEvents(events: CoreMouseEventType): { [event: string]: boolean } { throw new Error('Method not implemented.'); } @@ -94,10 +94,10 @@ export class MockCoreService implements ICoreService { sendFocus: false, wraparound: true }; - public onData: IEvent = new EventEmitter().event; - public onUserInput: IEvent = new EventEmitter().event; - public onBinary: IEvent = new EventEmitter().event; - public onRequestScrollToBottom: IEvent = new EventEmitter().event; + public onData: Event = new Emitter().event; + public onUserInput: Event = new Emitter().event; + public onBinary: Event = new Emitter().event; + public onRequestScrollToBottom: Event = new Emitter().event; public reset(): void { } public triggerDataEvent(data: string, wasUserInput?: boolean): void { } public triggerBinaryEvent(data: string): void { } @@ -117,7 +117,7 @@ export class MockOptionsService implements IOptionsService { public serviceBrand: any; public readonly rawOptions: Required = clone(DEFAULT_OPTIONS); public options: Required = this.rawOptions; - public onOptionChange: IEvent = new EventEmitter().event; + public onOptionChange: Event = new Emitter().event; constructor(testOptions?: Partial) { if (testOptions) { for (const key of Object.keys(testOptions)) { @@ -169,7 +169,7 @@ export class MockUnicodeService implements IUnicodeService { } public versions: string[] = []; public activeVersion: string = ''; - public onChange: IEvent = new EventEmitter().event; + public onChange: Event = new Emitter().event; public wcwidth = (codepoint: number): UnicodeCharWidth => this._provider.wcwidth(codepoint); public charProperties(codepoint: number, preceding: UnicodeCharProperties): UnicodeCharProperties { let width = this.wcwidth(codepoint); @@ -192,8 +192,8 @@ export class MockUnicodeService implements IUnicodeService { export class MockDecorationService implements IDecorationService { public serviceBrand: any; public get decorations(): IterableIterator { return [].values(); } - public onDecorationRegistered = new EventEmitter().event; - public onDecorationRemoved = new EventEmitter().event; + public onDecorationRegistered = new Emitter().event; + public onDecorationRemoved = new Emitter().event; public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined { return undefined; } public reset(): void { } public forEachDecorationAtCell(x: number, line: number, layer: 'bottom' | 'top' | undefined, callback: (decoration: IInternalDecoration) => void): void { } diff --git a/src/common/Types.ts b/src/common/Types.ts index f98a7d3e..289aa1f6 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -4,12 +4,12 @@ */ import { IDeleteEvent, IInsertEvent } from 'common/CircularList'; -import { IEvent, IEventEmitter } from 'common/EventEmitter'; import { Attributes, UnderlineStyle } from 'common/buffer/Constants'; // eslint-disable-line no-unused-vars import { IBufferSet } from 'common/buffer/Types'; import { IParams } from 'common/parser/Types'; import { ICoreMouseService, ICoreService, IOptionsService, IUnicodeService } from 'common/services/Services'; import { IFunctionIdentifier, ITerminalOptions as IPublicTerminalOptions } from '@xterm/xterm'; +import type { Emitter, Event } from 'vs/base/common/event'; export interface ICoreTerminal { coreMouseService: ICoreMouseService; @@ -67,12 +67,12 @@ export interface ICircularList { maxLength: number; isFull: boolean; - onDeleteEmitter: IEventEmitter; - onDelete: IEvent; - onInsertEmitter: IEventEmitter; - onInsert: IEvent; - onTrimEmitter: IEventEmitter; - onTrim: IEvent; + onDeleteEmitter: Emitter; + onDelete: Event; + onInsertEmitter: Emitter; + onInsert: Event; + onTrimEmitter: Emitter; + onTrim: Event; get(index: number): T | undefined; set(index: number, value: T): void; @@ -258,7 +258,7 @@ export interface IMarker extends IDisposable { readonly id: number; readonly isDisposed: boolean; readonly line: number; - onDispose: IEvent; + onDispose: Event; } export interface IModes { insertMode: boolean; @@ -446,7 +446,7 @@ export type IColorEvent = (IColorReportRequest | IColorSetRequest | IColorRestor * Calls the parser and handles actions generated by the parser. */ export interface IInputHandler { - onTitleChange: IEvent; + onTitleChange: Event; parse(data: string | Uint8Array, promiseResult?: boolean): void | Promise; print(data: Uint32Array, start: number, end: number): void; diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index 1d2922e8..d5e05731 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -161,6 +161,10 @@ export class Buffer implements IBuffer { this.lines.maxLength = newMaxLength; } + // if (this._cols > newCols) { + // console.log('increase!'); + // } + // The following adjustments should only happen if the buffer has been // initialized/filled. if (this.lines.length > 0) { diff --git a/src/common/buffer/BufferSet.ts b/src/common/buffer/BufferSet.ts index 61165fa1..03767537 100644 --- a/src/common/buffer/BufferSet.ts +++ b/src/common/buffer/BufferSet.ts @@ -3,12 +3,12 @@ * @license MIT */ -import { EventEmitter } from 'common/EventEmitter'; import { Disposable } from 'common/Lifecycle'; import { IAttributeData } from 'common/Types'; import { Buffer } from 'common/buffer/Buffer'; import { IBuffer, IBufferSet } from 'common/buffer/Types'; import { IBufferService, IOptionsService } from 'common/services/Services'; +import { Emitter } from 'vs/base/common/event'; /** * The BufferSet represents the set of two buffers used by xterm terminals (normal and alt) and @@ -19,7 +19,7 @@ export class BufferSet extends Disposable implements IBufferSet { private _alt!: Buffer; private _activeBuffer!: Buffer; - private readonly _onBufferActivate = this.register(new EventEmitter<{activeBuffer: IBuffer, inactiveBuffer: IBuffer}>()); + private readonly _onBufferActivate = this.register(new Emitter<{ activeBuffer: IBuffer, inactiveBuffer: IBuffer }>()); public readonly onBufferActivate = this._onBufferActivate.event; /** diff --git a/src/common/buffer/Marker.ts b/src/common/buffer/Marker.ts index 96df6366..efe12847 100644 --- a/src/common/buffer/Marker.ts +++ b/src/common/buffer/Marker.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { EventEmitter } from 'common/EventEmitter'; import { disposeArray } from 'common/Lifecycle'; import { IDisposable, IMarker } from 'common/Types'; +import { Emitter } from 'vs/base/common/event'; export class Marker implements IMarker { private static _nextId = 1; @@ -16,7 +16,7 @@ export class Marker implements IMarker { private readonly _id: number = Marker._nextId++; public get id(): number { return this._id; } - private readonly _onDispose = this.register(new EventEmitter()); + private readonly _onDispose = this.register(new Emitter()); public readonly onDispose = this._onDispose.event; constructor( diff --git a/src/common/buffer/Types.ts b/src/common/buffer/Types.ts index 78f01d55..a59c0e17 100644 --- a/src/common/buffer/Types.ts +++ b/src/common/buffer/Types.ts @@ -4,7 +4,7 @@ */ import { IAttributeData, ICircularList, IBufferLine, ICellData, IMarker, ICharset, IDisposable } from 'common/Types'; -import { IEvent } from 'common/EventEmitter'; +import type { Event } from 'vs/base/common/event'; // BufferIndex denotes a position in the buffer: [rowIndex, colIndex] export type BufferIndex = [number, number]; @@ -42,7 +42,7 @@ export interface IBufferSet extends IDisposable { normal: IBuffer; active: IBuffer; - onBufferActivate: IEvent<{ activeBuffer: IBuffer, inactiveBuffer: IBuffer }>; + onBufferActivate: Event<{ activeBuffer: IBuffer, inactiveBuffer: IBuffer }>; activateNormalBuffer(): void; activateAltBuffer(fillAttr?: IAttributeData): void; diff --git a/src/common/input/WriteBuffer.ts b/src/common/input/WriteBuffer.ts index ac0730cb..0bbca86a 100644 --- a/src/common/input/WriteBuffer.ts +++ b/src/common/input/WriteBuffer.ts @@ -4,8 +4,8 @@ * @license MIT */ -import { EventEmitter } from 'common/EventEmitter'; import { Disposable } from 'common/Lifecycle'; +import { Emitter } from 'vs/base/common/event'; declare const setTimeout: (handler: () => void, timeout?: number) => void; @@ -43,7 +43,7 @@ export class WriteBuffer extends Disposable { private _syncCalls = 0; private _didUserInput = false; - private readonly _onWriteParsed = this.register(new EventEmitter()); + private readonly _onWriteParsed = this.register(new Emitter()); public readonly onWriteParsed = this._onWriteParsed.event; constructor(private _action: (data: string | Uint8Array, promiseResult?: boolean) => void | Promise) { diff --git a/src/common/public/BufferNamespaceApi.ts b/src/common/public/BufferNamespaceApi.ts index cef0ed16..b7098ea5 100644 --- a/src/common/public/BufferNamespaceApi.ts +++ b/src/common/public/BufferNamespaceApi.ts @@ -5,15 +5,15 @@ import { IBuffer as IBufferApi, IBufferNamespace as IBufferNamespaceApi } from '@xterm/xterm'; import { BufferApiView } from 'common/public/BufferApiView'; -import { EventEmitter } from 'common/EventEmitter'; import { ICoreTerminal } from 'common/Types'; import { Disposable } from 'common/Lifecycle'; +import { Emitter } from 'vs/base/common/event'; export class BufferNamespaceApi extends Disposable implements IBufferNamespaceApi { private _normal: BufferApiView; private _alternate: BufferApiView; - private readonly _onBufferChange = this.register(new EventEmitter()); + private readonly _onBufferChange = this.register(new Emitter()); public readonly onBufferChange = this._onBufferChange.event; constructor(private _core: ICoreTerminal) { diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index 77af447a..0d7ede3e 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -3,12 +3,12 @@ * @license MIT */ -import { EventEmitter } from 'common/EventEmitter'; import { Disposable } from 'common/Lifecycle'; import { IAttributeData, IBufferLine } from 'common/Types'; import { BufferSet } from 'common/buffer/BufferSet'; import { IBuffer, IBufferSet } from 'common/buffer/Types'; import { IBufferService, IOptionsService } from 'common/services/Services'; +import { Emitter } from 'vs/base/common/event'; export const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars export const MINIMUM_ROWS = 1; @@ -22,9 +22,9 @@ export class BufferService extends Disposable implements IBufferService { /** Whether the user is scrolling (locks the scroll position) */ public isUserScrolling: boolean = false; - private readonly _onResize = this.register(new EventEmitter<{ cols: number, rows: number }>()); + private readonly _onResize = this.register(new Emitter<{ cols: number, rows: number }>()); public readonly onResize = this._onResize.event; - private readonly _onScroll = this.register(new EventEmitter()); + private readonly _onScroll = this.register(new Emitter()); public readonly onScroll = this._onScroll.event; public get buffer(): IBuffer { return this.buffers.active; } diff --git a/src/common/services/CoreMouseService.ts b/src/common/services/CoreMouseService.ts index 052353bc..66e7ae7c 100644 --- a/src/common/services/CoreMouseService.ts +++ b/src/common/services/CoreMouseService.ts @@ -3,9 +3,9 @@ * @license MIT */ import { IBufferService, ICoreService, ICoreMouseService } from 'common/services/Services'; -import { EventEmitter } from 'common/EventEmitter'; import { ICoreMouseProtocol, ICoreMouseEvent, CoreMouseEncoding, CoreMouseEventType, CoreMouseButton, CoreMouseAction } from 'common/Types'; import { Disposable } from 'common/Lifecycle'; +import { Emitter } from 'vs/base/common/event'; /** * Supported default protocols. @@ -175,7 +175,7 @@ export class CoreMouseService extends Disposable implements ICoreMouseService { private _activeEncoding: string = ''; private _lastEvent: ICoreMouseEvent | null = null; - private readonly _onProtocolChange = this.register(new EventEmitter()); + private readonly _onProtocolChange = this.register(new Emitter()); public readonly onProtocolChange = this._onProtocolChange.event; constructor( diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index 71985a17..2566aa40 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -4,10 +4,10 @@ */ import { clone } from 'common/Clone'; -import { EventEmitter } from 'common/EventEmitter'; import { Disposable } from 'common/Lifecycle'; import { IDecPrivateModes, IModes } from 'common/Types'; import { IBufferService, ICoreService, ILogService, IOptionsService } from 'common/services/Services'; +import { Emitter } from 'vs/base/common/event'; const DEFAULT_MODES: IModes = Object.freeze({ insertMode: false @@ -31,13 +31,13 @@ export class CoreService extends Disposable implements ICoreService { public modes: IModes; public decPrivateModes: IDecPrivateModes; - private readonly _onData = this.register(new EventEmitter()); + private readonly _onData = this.register(new Emitter()); public readonly onData = this._onData.event; - private readonly _onUserInput = this.register(new EventEmitter()); + private readonly _onUserInput = this.register(new Emitter()); public readonly onUserInput = this._onUserInput.event; - private readonly _onBinary = this.register(new EventEmitter()); + private readonly _onBinary = this.register(new Emitter()); public readonly onBinary = this._onBinary.event; - private readonly _onRequestScrollToBottom = this.register(new EventEmitter()); + private readonly _onRequestScrollToBottom = this.register(new Emitter()); public readonly onRequestScrollToBottom = this._onRequestScrollToBottom.event; constructor( diff --git a/src/common/services/DecorationService.test.ts b/src/common/services/DecorationService.test.ts index a2cb56d2..7abcff5b 100644 --- a/src/common/services/DecorationService.test.ts +++ b/src/common/services/DecorationService.test.ts @@ -5,15 +5,15 @@ import { assert } from 'chai'; import { DecorationService } from './DecorationService'; -import { EventEmitter } from 'common/EventEmitter'; import { IMarker } from 'common/Types'; import { Disposable } from 'common/Lifecycle'; +import { Emitter } from 'vs/base/common/event'; const fakeMarker: IMarker = Object.freeze(new class extends Disposable { public readonly id = 1; public readonly line = 1; public readonly isDisposed = false; - public readonly onDispose = new EventEmitter().event; + public readonly onDispose = new Emitter().event; }()); describe('DecorationService', () => { diff --git a/src/common/services/DecorationService.ts b/src/common/services/DecorationService.ts index c9be78af..23b3511e 100644 --- a/src/common/services/DecorationService.ts +++ b/src/common/services/DecorationService.ts @@ -4,12 +4,12 @@ */ import { css } from 'common/Color'; -import { EventEmitter } from 'common/EventEmitter'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { IDecorationService, IInternalDecoration } from 'common/services/Services'; import { SortedList } from 'common/SortedList'; import { IColor } from 'common/Types'; import { IDecoration, IDecorationOptions, IMarker } from '@xterm/xterm'; +import { Emitter } from 'vs/base/common/event'; // Work variables to avoid garbage collection let $xmin = 0; @@ -25,9 +25,9 @@ export class DecorationService extends Disposable implements IDecorationService */ private readonly _decorations: SortedList = new SortedList(e => e?.marker.line); - private readonly _onDecorationRegistered = this.register(new EventEmitter()); + private readonly _onDecorationRegistered = this.register(new Emitter()); public readonly onDecorationRegistered = this._onDecorationRegistered.event; - private readonly _onDecorationRemoved = this.register(new EventEmitter()); + private readonly _onDecorationRemoved = this.register(new Emitter()); public readonly onDecorationRemoved = this._onDecorationRemoved.event; public get decorations(): IterableIterator { return this._decorations.values(); } @@ -95,9 +95,9 @@ class Decoration extends Disposable implements IInternalDecoration { public element: HTMLElement | undefined; public get isDisposed(): boolean { return this._isDisposed; } - public readonly onRenderEmitter = this.register(new EventEmitter()); + public readonly onRenderEmitter = this.register(new Emitter()); public readonly onRender = this.onRenderEmitter.event; - private readonly _onDispose = this.register(new EventEmitter()); + private readonly _onDispose = this.register(new Emitter()); public readonly onDispose = this._onDispose.event; private _cachedBg: IColor | undefined | null = null; diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 0375f6ad..5a3da107 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -3,11 +3,11 @@ * @license MIT */ -import { EventEmitter } from 'common/EventEmitter'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { isMac } from 'common/Platform'; import { CursorStyle, IDisposable } from 'common/Types'; import { FontWeight, IOptionsService, ITerminalOptions } from 'common/services/Services'; +import { Emitter } from 'vs/base/common/event'; export const DEFAULT_OPTIONS: Readonly> = { cols: 80, @@ -65,7 +65,7 @@ export class OptionsService extends Disposable implements IOptionsService { public readonly rawOptions: Required; public options: Required; - private readonly _onOptionChange = this.register(new EventEmitter()); + private readonly _onOptionChange = this.register(new Emitter()); public readonly onOptionChange = this._onOptionChange.event; constructor(options: Partial) { diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 4b3c4e3f..d677556c 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -4,10 +4,10 @@ */ import { IDecoration, IDecorationOptions, ILinkHandler, ILogger, IWindowsPty } from '@xterm/xterm'; -import { IEvent, IEventEmitter } from 'common/EventEmitter'; import { CoreMouseEncoding, CoreMouseEventType, CursorInactiveStyle, CursorStyle, IAttributeData, ICharset, IColor, ICoreMouseEvent, ICoreMouseProtocol, IDecPrivateModes, IDisposable, IModes, IOscLinkData, IWindowOptions } from 'common/Types'; import { IBuffer, IBufferSet } from 'common/buffer/Types'; import { createDecorator } from 'common/services/ServiceRegistry'; +import type { Emitter, Event } from 'vs/base/common/event'; export const IBufferService = createDecorator('BufferService'); export interface IBufferService { @@ -18,8 +18,8 @@ export interface IBufferService { readonly buffer: IBuffer; readonly buffers: IBufferSet; isUserScrolling: boolean; - onResize: IEvent<{ cols: number, rows: number }>; - onScroll: IEvent; + onResize: Event<{ cols: number, rows: number }>; + onScroll: Event; scroll(eraseAttr: IAttributeData, isWrapped?: boolean): void; scrollLines(disp: number, suppressScrollEvent?: boolean): void; resize(cols: number, rows: number): void; @@ -52,7 +52,7 @@ export interface ICoreMouseService { /** * Event to announce changes in mouse tracking. */ - onProtocolChange: IEvent; + onProtocolChange: Event; /** * Human readable version of mouse events. @@ -74,10 +74,10 @@ export interface ICoreService { readonly modes: IModes; readonly decPrivateModes: IDecPrivateModes; - readonly onData: IEvent; - readonly onUserInput: IEvent; - readonly onBinary: IEvent; - readonly onRequestScrollToBottom: IEvent; + readonly onData: Event; + readonly onUserInput: Event; + readonly onBinary: Event; + readonly onRequestScrollToBottom: Event; reset(): void; @@ -186,7 +186,7 @@ export interface IOptionsService { /** * Adds an event listener for when any option changes. */ - readonly onOptionChange: IEvent; + readonly onOptionChange: Event; /** * Adds an event listener for when a specific option changes, this is a convenience method that is @@ -338,7 +338,7 @@ export interface IUnicodeService { /** Currently active version. */ activeVersion: string; /** Event triggered, when activate version changed. */ - readonly onChange: IEvent; + readonly onChange: Event; /** * Unicode version dependent @@ -363,8 +363,8 @@ export const IDecorationService = createDecorator('Decoratio export interface IDecorationService extends IDisposable { serviceBrand: undefined; readonly decorations: IterableIterator; - readonly onDecorationRegistered: IEvent; - readonly onDecorationRemoved: IEvent; + readonly onDecorationRegistered: Event; + readonly onDecorationRemoved: Event; registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined; reset(): void; /** @@ -377,5 +377,5 @@ export interface IInternalDecoration extends IDecoration { readonly options: IDecorationOptions; readonly backgroundColorRGB: IColor | undefined; readonly foregroundColorRGB: IColor | undefined; - readonly onRenderEmitter: IEventEmitter; + readonly onRenderEmitter: Emitter; } diff --git a/src/common/services/UnicodeService.ts b/src/common/services/UnicodeService.ts index 767eecaf..12e4ec41 100644 --- a/src/common/services/UnicodeService.ts +++ b/src/common/services/UnicodeService.ts @@ -3,9 +3,9 @@ * @license MIT */ -import { EventEmitter } from 'common/EventEmitter'; import { UnicodeV6 } from 'common/input/UnicodeV6'; import { IUnicodeService, IUnicodeVersionProvider, UnicodeCharProperties, UnicodeCharWidth } from 'common/services/Services'; +import { Emitter } from 'vs/base/common/event'; export class UnicodeService implements IUnicodeService { public serviceBrand: any; @@ -14,7 +14,7 @@ export class UnicodeService implements IUnicodeService { private _active: string = ''; private _activeProvider: IUnicodeVersionProvider; - private readonly _onChange = new EventEmitter(); + private readonly _onChange = new Emitter(); public readonly onChange = this._onChange.event; public static extractShouldJoin(value: UnicodeCharProperties): boolean { diff --git a/src/common/tsconfig.json b/src/common/tsconfig.json index b8baa6c0..e9b3673a 100644 --- a/src/common/tsconfig.json +++ b/src/common/tsconfig.json @@ -9,10 +9,16 @@ "types": [ "../../node_modules/@types/mocha" ], - "baseUrl": ".." + "baseUrl": "..", + "paths": { + "vs/*": [ "./vs/*" ] + } }, "include": [ "./**/*", "../../typings/xterm.d.ts" + ], + "references": [ + { "path": "../vs" } ] } diff --git a/src/headless/Terminal.ts b/src/headless/Terminal.ts index 0b078ba8..ac996737 100644 --- a/src/headless/Terminal.ts +++ b/src/headless/Terminal.ts @@ -24,19 +24,19 @@ import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { IBuffer } from 'common/buffer/Types'; import { CoreTerminal } from 'common/CoreTerminal'; -import { EventEmitter, forwardEvent } from 'common/EventEmitter'; import { IMarker, ITerminalOptions } from 'common/Types'; +import { Emitter, Event } from 'vs/base/common/event'; export class Terminal extends CoreTerminal { - private readonly _onBell = this.register(new EventEmitter()); + private readonly _onBell = this.register(new Emitter()); public readonly onBell = this._onBell.event; - private readonly _onCursorMove = this.register(new EventEmitter()); + private readonly _onCursorMove = this.register(new Emitter()); public readonly onCursorMove = this._onCursorMove.event; - private readonly _onTitleChange = this.register(new EventEmitter()); + private readonly _onTitleChange = this.register(new Emitter()); public readonly onTitleChange = this._onTitleChange.event; - private readonly _onA11yCharEmitter = this.register(new EventEmitter()); + private readonly _onA11yCharEmitter = this.register(new Emitter()); public readonly onA11yChar = this._onA11yCharEmitter.event; - private readonly _onA11yTabEmitter = this.register(new EventEmitter()); + private readonly _onA11yTabEmitter = this.register(new Emitter()); public readonly onA11yTab = this._onA11yTabEmitter.event; constructor( @@ -49,10 +49,10 @@ export class Terminal extends CoreTerminal { // Setup InputHandler listeners this.register(this._inputHandler.onRequestBell(() => this.bell())); this.register(this._inputHandler.onRequestReset(() => this.reset())); - this.register(forwardEvent(this._inputHandler.onCursorMove, this._onCursorMove)); - 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(Event.forward(this._inputHandler.onCursorMove, this._onCursorMove)); + this.register(Event.forward(this._inputHandler.onTitleChange, this._onTitleChange)); + this.register(Event.forward(this._inputHandler.onA11yChar, this._onA11yCharEmitter)); + this.register(Event.forward(this._inputHandler.onA11yTab, this._onA11yTabEmitter)); } /** diff --git a/src/headless/public/Terminal.ts b/src/headless/public/Terminal.ts index 1b39c184..c2d3bcb6 100644 --- a/src/headless/public/Terminal.ts +++ b/src/headless/public/Terminal.ts @@ -3,7 +3,6 @@ * @license MIT */ -import { IEvent } from 'common/EventEmitter'; import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi'; import { ParserApi } from 'common/public/ParserApi'; import { UnicodeApi } from 'common/public/UnicodeApi'; @@ -12,6 +11,7 @@ import { Terminal as TerminalCore } from 'headless/Terminal'; import { AddonManager } from 'common/public/AddonManager'; import { ITerminalOptions } from 'common/Types'; import { Disposable } from 'common/Lifecycle'; +import type { Event } from 'vs/base/common/event'; /** * The set of options that only have an effect when set in the Terminal constructor. */ @@ -72,15 +72,15 @@ export class Terminal extends Disposable implements ITerminalApi { } } - public get onBell(): IEvent { return this._core.onBell; } - public get onBinary(): IEvent { return this._core.onBinary; } - public get onCursorMove(): IEvent { return this._core.onCursorMove; } - public get onData(): IEvent { return this._core.onData; } - public get onLineFeed(): IEvent { return this._core.onLineFeed; } - public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; } - public get onScroll(): IEvent { return this._core.onScroll; } - public get onTitleChange(): IEvent { return this._core.onTitleChange; } - public get onWriteParsed(): IEvent { return this._core.onWriteParsed; } + public get onBell(): Event { return this._core.onBell; } + public get onBinary(): Event { return this._core.onBinary; } + public get onCursorMove(): Event { return this._core.onCursorMove; } + public get onData(): Event { return this._core.onData; } + public get onLineFeed(): Event { return this._core.onLineFeed; } + public get onResize(): Event<{ cols: number, rows: number }> { return this._core.onResize; } + public get onScroll(): Event { return this._core.onScroll; } + public get onTitleChange(): Event { return this._core.onTitleChange; } + public get onWriteParsed(): Event { return this._core.onWriteParsed; } public get parser(): IParser { this._checkProposedApi(); diff --git a/src/headless/tsconfig.json b/src/headless/tsconfig.json index e48c4fd5..ae518a7e 100644 --- a/src/headless/tsconfig.json +++ b/src/headless/tsconfig.json @@ -12,7 +12,8 @@ ], "baseUrl": "../", "paths": { - "common/*": [ "./common/*" ] + "common/*": [ "./common/*" ], + "vs/*": [ "./vs/*" ] } }, "include": [ @@ -21,6 +22,7 @@ "../../typings/xterm-headless.d.ts" ], "references": [ - { "path": "../common" } + { "path": "../common" }, + { "path": "../vs" } ] } diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index d563a2c7..5900c7d9 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -615,6 +615,22 @@ export namespace Event { return result.event; } + /** + * A convenience function for forwarding an event to another emitter which + * improves readability.allows Event.forward(event, emitter) instead of `event(e => emitter.fire(e))`. + * @param from The event to forward. + * @param to The emitter to forward the event to. + * @example + * Event.forward(event, emitter); + * // equivalent to + * event(e => emitter.fire(e)); + * // equivalent to + * event(emitter.fire, emitter); + */ + export function forward(from: Event, to: Emitter): IDisposable { + return from(e => to.fire(e)); + } + /** * Adds a listener to an event and calls the listener immediately with undefined as the event object. * diff --git a/test/playwright/TestUtils.ts b/test/playwright/TestUtils.ts index 79408d41..dea3fcc7 100644 --- a/test/playwright/TestUtils.ts +++ b/test/playwright/TestUtils.ts @@ -7,11 +7,10 @@ import { Browser, JSHandle, Page } from '@playwright/test'; import { deepStrictEqual, strictEqual } from 'assert'; import type { IRenderDimensions } from 'browser/renderer/shared/Types'; import type { IRenderService } from 'browser/services/Services'; -import type { ICoreTerminal, IMarker } from 'common/Types'; +import type { ICoreTerminal, IDisposable, IMarker } from 'common/Types'; import * as playwright from '@playwright/test'; import { PageFunction } from 'playwright-core/types/structs'; import { IBuffer, IBufferCell, IBufferLine, IBufferNamespace, IBufferRange, IDecoration, IDecorationOptions, IModes, ITerminalInitOnlyOptions, ITerminalOptions, Terminal } from '@xterm/xterm'; -import { EventEmitter } from '../../out/common/EventEmitter'; export interface ITestContext { browser: Browser; @@ -35,6 +34,56 @@ export async function createTestContext(browser: Browser): Promise }; } +interface IListener { + (arg1: T, arg2: U): void; +} +export interface IEvent { + (listener: (arg1: T, arg2: U) => any): IDisposable; +} +class EventEmitter { + private _listeners: Set> = new Set(); + private _event?: IEvent; + private _disposed: boolean = false; + + public get event(): IEvent { + if (!this._event) { + this._event = (listener: (arg1: T, arg2: U) => any) => { + this._listeners.add(listener); + const disposable = { + dispose: () => { + if (!this._disposed) { + this._listeners.delete(listener); + } + } + }; + return disposable; + }; + } + return this._event; + } + + public fire(arg1: T, arg2: U): void { + const queue: IListener[] = []; + for (const l of this._listeners.values()) { + queue.push(l); + } + for (let i = 0; i < queue.length; i++) { + queue[i].call(undefined, arg1, arg2); + } + } + + public dispose(): void { + this.clearListeners(); + this._disposed = true; + } + + public clearListeners(): void { + if (this._listeners) { + this._listeners.clear(); + } + } +} + type EnsureAsync = T extends PromiseLike ? T : Promise; type EnsureAsyncProperties = { [Key in keyof T]: EnsureAsync @@ -107,22 +156,32 @@ export class TerminalProxy implements ITerminalProxyCustomMethods, PlaywrightApi * Initialize the proxy for a new terminal object. */ public async initTerm(): Promise { - for (const emitter of [ - this._onBell, - this._onBinary, - this._onCursorMove, - this._onData, - this._onKey, - this._onLineFeed, - this._onRender, - this._onResize, - this._onScroll, - this._onSelectionChange, - this._onTitleChange, - this._onWriteParsed - ]) { - emitter.clearListeners(); - } + this._onBell.dispose(); + this._onBinary.dispose(); + this._onCursorMove.dispose(); + this._onData.dispose(); + this._onKey.dispose(); + this._onLineFeed.dispose(); + this._onRender.dispose(); + this._onResize.dispose(); + this._onScroll.dispose(); + this._onSelectionChange.dispose(); + this._onTitleChange.dispose(); + this._onWriteParsed.dispose(); + + this._onBell = new EventEmitter(); + this._onBinary = new EventEmitter(); + this._onCursorMove = new EventEmitter(); + this._onData = new EventEmitter(); + this._onKey = new EventEmitter(); + this._onLineFeed = new EventEmitter(); + this._onRender = new EventEmitter(); + this._onResize = new EventEmitter(); + this._onScroll = new EventEmitter(); + this._onSelectionChange = new EventEmitter(); + this._onTitleChange = new EventEmitter(); + this._onWriteParsed = new EventEmitter(); + await this.evaluate(([term]) => term.onBell((window as any).onBell)); await this.evaluate(([term]) => term.onBinary((window as any).onBinary)); await this.evaluate(([term]) => term.onCursorMove((window as any).onCursorMove)); @@ -139,29 +198,29 @@ export class TerminalProxy implements ITerminalProxyCustomMethods, PlaywrightApi // #region Events private _onBell = new EventEmitter(); - public readonly onBell = this._onBell.event; + public get onBell(): IEvent { return this._onBell.event; } private _onBinary = new EventEmitter(); - public readonly onBinary = this._onBinary.event; + public get onBinary(): IEvent { return this._onBinary.event; } private _onCursorMove = new EventEmitter(); - public readonly onCursorMove = this._onCursorMove.event; + public get onCursorMove(): IEvent { return this._onCursorMove.event; } private _onData = new EventEmitter(); - public readonly onData = this._onData.event; + public get onData(): IEvent { return this._onData.event; } private _onKey = new EventEmitter<{ key: string, domEvent: KeyboardEvent }>(); - public readonly onKey = this._onKey.event; + public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._onKey.event; } private _onLineFeed = new EventEmitter(); - public readonly onLineFeed = this._onLineFeed.event; + public get onLineFeed(): IEvent { return this._onLineFeed.event; } private _onRender = new EventEmitter<{ start: number, end: number }>(); - public readonly onRender = this._onRender.event; + public get onRender(): IEvent<{ start: number, end: number }> { return this._onRender.event; } private _onResize = new EventEmitter<{ cols: number, rows: number }>(); - public readonly onResize = this._onResize.event; + public get onResize(): IEvent<{ cols: number, rows: number }> { return this._onResize.event; } private _onScroll = new EventEmitter(); - public readonly onScroll = this._onScroll.event; + public get onScroll(): IEvent { return this._onScroll.event; } private _onSelectionChange = new EventEmitter(); - public readonly onSelectionChange = this._onSelectionChange.event; + public get onSelectionChange(): IEvent { return this._onSelectionChange.event; } private _onTitleChange = new EventEmitter(); - public readonly onTitleChange = this._onTitleChange.event; + public get onTitleChange(): IEvent { return this._onTitleChange.event; } private _onWriteParsed = new EventEmitter(); - public readonly onWriteParsed = this._onWriteParsed.event; + public get onWriteParsed(): IEvent { return this._onWriteParsed.event; } // #endregion // #region Simple properties diff --git a/test/playwright/tsconfig.json b/test/playwright/tsconfig.json index 255e6eb0..3bef7ec7 100644 --- a/test/playwright/tsconfig.json +++ b/test/playwright/tsconfig.json @@ -22,6 +22,9 @@ ], "common/*": [ "../../src/common/*" + ], + "vs/*": [ + "../../src/vs/*" ] }, "target": "ESNext", @@ -38,6 +41,9 @@ }, { "path": "../../src/common" + }, + { + "path": "../../src/vs" } ] } diff --git a/webpack.config.headless.js b/webpack.config.headless.js index 12e7484d..5b3fffe7 100644 --- a/webpack.config.headless.js +++ b/webpack.config.headless.js @@ -31,7 +31,8 @@ const config = { extensions: [ '.js' ], alias: { common: path.resolve('./out/common'), - headless: path.resolve('./out/headless') + headless: path.resolve('./out/headless'), + vs: path.resolve('./out/vs') } }, output: {