diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index f14bffe0..2525fd57 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -21,8 +21,8 @@ * http://linux.die.net/man/7/urxvt */ -import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types'; -import { IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types'; +import { ICompositionHelper, ITerminal, IBrowser, CustomKeyEventHandler, ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport, ILinkifier2, CharacterJoinerHandler } from 'browser/Types'; +import { IRenderer } from 'browser/renderer/Types'; import { CompositionHelper } from 'browser/input/CompositionHelper'; import { Viewport } from 'browser/Viewport'; import { rightClickHandler, moveTextAreaUnderMouseCursor, handlePasteEvent, copyHandler, paste } from 'browser/Clipboard'; @@ -45,7 +45,7 @@ import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; -import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService } from 'browser/services/Services'; +import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService, ICharacterJoinerService } from 'browser/services/Services'; import { CharSizeService } from 'browser/services/CharSizeService'; import { IBuffer } from 'common/buffer/Types'; import { MouseService } from 'browser/services/MouseService'; @@ -54,6 +54,7 @@ import { CoreBrowserService } from 'browser/services/CoreBrowserService'; import { CoreTerminal } from 'common/CoreTerminal'; import { ITerminalOptions as IInitializedTerminalOptions } from 'common/services/Services'; import { rgba } from 'browser/Color'; +import { CharacterJoinerService } from 'browser/services/CharacterJoinerService'; // Let it work inside Node.js for automated testing purposes. const document: Document = (typeof window !== 'undefined') ? window.document : null as any; @@ -82,6 +83,7 @@ export class Terminal extends CoreTerminal implements ITerminal { private _charSizeService: ICharSizeService | undefined; private _mouseService: IMouseService | undefined; private _renderService: IRenderService | undefined; + private _characterJoinerService: ICharacterJoinerService | undefined; private _selectionService: ISelectionService | undefined; private _soundService: ISoundService | undefined; @@ -449,6 +451,9 @@ export class Terminal extends CoreTerminal implements ITerminal { this.register(this.optionsService.onOptionChange(e => this._colorManager!.onOptionsChange(e))); this._colorManager.setTheme(this._theme); + this._characterJoinerService = this._instantiationService.createInstance(CharacterJoinerService); + this._instantiationService.setService(ICharacterJoinerService, this._characterJoinerService); + const renderer = this._createRenderer(); this._renderService = this.register(this._instantiationService.createInstance(RenderService, renderer, this.rows, this.screenElement)); this._instantiationService.setService(IRenderService, this._renderService); @@ -550,7 +555,7 @@ export class Terminal extends CoreTerminal implements ITerminal { private _createRenderer(): IRenderer { switch (this.options.rendererType) { - case 'canvas': return this._instantiationService.createInstance(Renderer, this._colorManager!.colors, this.screenElement!, this.linkifier, this.linkifier2); + case 'canvas': return this._instantiationService.createInstance(Renderer, this._colorManager!.colors, this.screenElement!, this.linkifier, this.linkifier2, this._instantiationService); case 'dom': return this._instantiationService.createInstance(DomRenderer, this._colorManager!.colors, this.element!, this.screenElement!, this._viewportElement!, this.linkifier, this.linkifier2); default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`); } @@ -914,13 +919,19 @@ export class Terminal extends CoreTerminal implements ITerminal { } public registerCharacterJoiner(handler: CharacterJoinerHandler): number { - const joinerId = this._renderService!.registerCharacterJoiner(handler); + if (!this._characterJoinerService) { + throw new Error('Terminal must be opened first'); + } + const joinerId = this._characterJoinerService.register(handler); this.refresh(0, this.rows - 1); return joinerId; } public deregisterCharacterJoiner(joinerId: number): void { - if (this._renderService!.deregisterCharacterJoiner(joinerId)) { + if (!this._characterJoinerService) { + throw new Error('Terminal must be opened first'); + } + if (this._characterJoinerService.deregister(joinerId)) { this.refresh(0, this.rows - 1); } } diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index a2140aa5..7ab7d9b1 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -6,8 +6,8 @@ import { IDisposable, IMarker, ISelectionPosition, ILinkProvider } from 'xterm'; import { IEvent, EventEmitter } from 'common/EventEmitter'; import { ICharSizeService, IMouseService, IRenderService, ISelectionService } from 'browser/services/Services'; -import { IRenderDimensions, IRenderer, CharacterJoinerHandler, IRequestRedrawEvent } from 'browser/renderer/Types'; -import { IColorSet, ILinkMatcherOptions, ITerminal, ILinkifier, ILinkifier2, IBrowser, IViewport, IColorManager, ICompositionHelper } from 'browser/Types'; +import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/Types'; +import { IColorSet, ILinkMatcherOptions, ITerminal, ILinkifier, ILinkifier2, IBrowser, IViewport, IColorManager, ICompositionHelper, CharacterJoinerHandler } from 'browser/Types'; import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types'; import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, ITerminalOptions } from 'common/Types'; import { Buffer } from 'common/buffer/Buffer'; @@ -284,8 +284,6 @@ export class MockRenderer implements IRenderer { public onDevicePixelRatioChange(): void { } public clear(): void { } public renderRows(start: number, end: number): void { } - public registerCharacterJoiner(handler: CharacterJoinerHandler): number { return 0; } - public deregisterCharacterJoiner(): boolean { return true; } } export class MockViewport implements IViewport { @@ -409,12 +407,6 @@ export class MockRenderService implements IRenderService { public clear(): void { throw new Error('Method not implemented.'); } - public registerCharacterJoiner(handler: CharacterJoinerHandler): number { - throw new Error('Method not implemented.'); - } - public deregisterCharacterJoiner(joinerId: number): boolean { - throw new Error('Method not implemented.'); - } public dispose(): void { throw new Error('Method not implemented.'); } diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts index f743934e..15eef804 100644 --- a/src/browser/Types.d.ts +++ b/src/browser/Types.d.ts @@ -302,3 +302,10 @@ interface IBufferCellPosition { x: number; y: number; } + +export type CharacterJoinerHandler = (text: string) => [number, number][]; + +export interface ICharacterJoiner { + id: number; + handler: CharacterJoinerHandler; +} diff --git a/src/browser/renderer/CursorRenderLayer.ts b/src/browser/renderer/CursorRenderLayer.ts index d358d580..a78b2048 100644 --- a/src/browser/renderer/CursorRenderLayer.ts +++ b/src/browser/renderer/CursorRenderLayer.ts @@ -37,10 +37,10 @@ export class CursorRenderLayer extends BaseRenderLayer { colors: IColorSet, rendererId: number, private _onRequestRedraw: IEventEmitter, - bufferService: IBufferService, - optionsService: IOptionsService, - private readonly _coreService: ICoreService, - private readonly _coreBrowserService: ICoreBrowserService + @IBufferService bufferService: IBufferService, + @IOptionsService optionsService: IOptionsService, + @ICoreService private readonly _coreService: ICoreService, + @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService ) { super(container, 'cursor', zIndex, true, colors, rendererId, bufferService, optionsService); this._state = { diff --git a/src/browser/renderer/LinkRenderLayer.ts b/src/browser/renderer/LinkRenderLayer.ts index c41955d9..2492f921 100644 --- a/src/browser/renderer/LinkRenderLayer.ts +++ b/src/browser/renderer/LinkRenderLayer.ts @@ -20,8 +20,8 @@ export class LinkRenderLayer extends BaseRenderLayer { rendererId: number, linkifier: ILinkifier, linkifier2: ILinkifier2, - bufferService: IBufferService, - optionsService: IOptionsService + @IBufferService bufferService: IBufferService, + @IOptionsService optionsService: IOptionsService ) { super(container, 'link', zIndex, true, colors, rendererId, bufferService, optionsService); linkifier.onShowLinkUnderline(e => this._onShowLinkUnderline(e)); diff --git a/src/browser/renderer/Renderer.ts b/src/browser/renderer/Renderer.ts index b9d02ff8..c88ce256 100644 --- a/src/browser/renderer/Renderer.ts +++ b/src/browser/renderer/Renderer.ts @@ -6,13 +6,12 @@ import { TextRenderLayer } from 'browser/renderer/TextRenderLayer'; import { SelectionRenderLayer } from 'browser/renderer/SelectionRenderLayer'; import { CursorRenderLayer } from 'browser/renderer/CursorRenderLayer'; -import { IRenderLayer, IRenderer, IRenderDimensions, CharacterJoinerHandler, ICharacterJoinerRegistry, IRequestRedrawEvent } from 'browser/renderer/Types'; +import { IRenderLayer, IRenderer, IRenderDimensions, IRequestRedrawEvent } from 'browser/renderer/Types'; import { LinkRenderLayer } from 'browser/renderer/LinkRenderLayer'; -import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistry'; import { Disposable } from 'common/Lifecycle'; import { IColorSet, ILinkifier, ILinkifier2 } from 'browser/Types'; import { ICharSizeService, ICoreBrowserService } from 'browser/services/Services'; -import { IBufferService, IOptionsService, ICoreService } from 'common/services/Services'; +import { IBufferService, IOptionsService, ICoreService, IInstantiationService } from 'common/services/Services'; import { removeTerminalFromCache } from 'browser/renderer/atlas/CharAtlasCache'; import { EventEmitter, IEvent } from 'common/EventEmitter'; @@ -23,7 +22,6 @@ export class Renderer extends Disposable implements IRenderer { private _renderLayers: IRenderLayer[]; private _devicePixelRatio: number; - private _characterJoinerRegistry: ICharacterJoinerRegistry; public dimensions: IRenderDimensions; @@ -35,20 +33,18 @@ export class Renderer extends Disposable implements IRenderer { private readonly _screenElement: HTMLElement, linkifier: ILinkifier, linkifier2: ILinkifier2, + instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService, @ICharSizeService private readonly _charSizeService: ICharSizeService, - @IOptionsService private readonly _optionsService: IOptionsService, - @ICoreService coreService: ICoreService, - @ICoreBrowserService coreBrowserService: ICoreBrowserService + @IOptionsService private readonly _optionsService: IOptionsService ) { super(); const allowTransparency = this._optionsService.options.allowTransparency; - this._characterJoinerRegistry = new CharacterJoinerRegistry(this._bufferService); this._renderLayers = [ - new TextRenderLayer(this._screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency, this._id, this._bufferService, _optionsService), - new SelectionRenderLayer(this._screenElement, 1, this._colors, this._id, this._bufferService, _optionsService), - new LinkRenderLayer(this._screenElement, 2, this._colors, this._id, linkifier, linkifier2, this._bufferService, _optionsService), - new CursorRenderLayer(this._screenElement, 3, this._colors, this._id, this._onRequestRedraw, this._bufferService, _optionsService, coreService, coreBrowserService) + instantiationService.createInstance(TextRenderLayer, this._screenElement, 0, this._colors, allowTransparency, this._id), + instantiationService.createInstance(SelectionRenderLayer, this._screenElement, 1, this._colors, this._id), + instantiationService.createInstance(LinkRenderLayer, this._screenElement, 2, this._colors, this._id, linkifier, linkifier2), + instantiationService.createInstance(CursorRenderLayer, this._screenElement, 3, this._colors, this._id, this._onRequestRedraw) ]; this.dimensions = { scaledCharWidth: 0, @@ -210,12 +206,4 @@ export class Renderer extends Disposable implements IRenderer { this.dimensions.actualCellHeight = this.dimensions.canvasHeight / this._bufferService.rows; this.dimensions.actualCellWidth = this.dimensions.canvasWidth / this._bufferService.cols; } - - public registerCharacterJoiner(handler: CharacterJoinerHandler): number { - return this._characterJoinerRegistry.registerCharacterJoiner(handler); - } - - public deregisterCharacterJoiner(joinerId: number): boolean { - return this._characterJoinerRegistry.deregisterCharacterJoiner(joinerId); - } } diff --git a/src/browser/renderer/SelectionRenderLayer.ts b/src/browser/renderer/SelectionRenderLayer.ts index 80022f01..9054e3ca 100644 --- a/src/browser/renderer/SelectionRenderLayer.ts +++ b/src/browser/renderer/SelectionRenderLayer.ts @@ -23,8 +23,8 @@ export class SelectionRenderLayer extends BaseRenderLayer { zIndex: number, colors: IColorSet, rendererId: number, - bufferService: IBufferService, - optionsService: IOptionsService + @IBufferService bufferService: IBufferService, + @IOptionsService optionsService: IOptionsService ) { super(container, 'selection', zIndex, true, colors, rendererId, bufferService, optionsService); this._clearState(); diff --git a/src/browser/renderer/TextRenderLayer.ts b/src/browser/renderer/TextRenderLayer.ts index 1f35fae1..48bf848e 100644 --- a/src/browser/renderer/TextRenderLayer.ts +++ b/src/browser/renderer/TextRenderLayer.ts @@ -3,16 +3,17 @@ * @license MIT */ -import { ICharacterJoinerRegistry, IRenderDimensions } from 'browser/renderer/Types'; +import { IRenderDimensions } from 'browser/renderer/Types'; import { CharData, ICellData } from 'common/Types'; import { GridCache } from 'browser/renderer/GridCache'; import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer'; import { AttributeData } from 'common/buffer/AttributeData'; import { NULL_CELL_CODE, Content } from 'common/buffer/Constants'; -import { JoinedCellData } from 'browser/renderer/CharacterJoinerRegistry'; import { IColorSet } from 'browser/Types'; import { CellData } from 'common/buffer/CellData'; import { IOptionsService, IBufferService } from 'common/services/Services'; +import { ICharacterJoinerService } from 'browser/services/Services'; +import { JoinedCellData } from 'browser/services/CharacterJoinerService'; /** * This CharData looks like a null character, which will forc a clear and render @@ -26,22 +27,20 @@ export class TextRenderLayer extends BaseRenderLayer { private _characterWidth: number = 0; private _characterFont: string = ''; private _characterOverlapCache: { [key: string]: boolean } = {}; - private _characterJoinerRegistry: ICharacterJoinerRegistry; private _workCell = new CellData(); constructor( container: HTMLElement, zIndex: number, colors: IColorSet, - characterJoinerRegistry: ICharacterJoinerRegistry, alpha: boolean, rendererId: number, - bufferService: IBufferService, - optionsService: IOptionsService + @IBufferService bufferService: IBufferService, + @IOptionsService optionsService: IOptionsService, + @ICharacterJoinerService private readonly _characterJoinerService: ICharacterJoinerService ) { super(container, 'text', zIndex, alpha, colors, rendererId, bufferService, optionsService); this._state = new GridCache(); - this._characterJoinerRegistry = characterJoinerRegistry; } public resize(dim: IRenderDimensions): void { @@ -67,7 +66,6 @@ export class TextRenderLayer extends BaseRenderLayer { private _forEachCell( firstRow: number, lastRow: number, - joinerRegistry: ICharacterJoinerRegistry | null, callback: ( cell: ICellData, x: number, @@ -77,7 +75,7 @@ export class TextRenderLayer extends BaseRenderLayer { for (let y = firstRow; y <= lastRow; y++) { const row = y + this._bufferService.buffer.ydisp; const line = this._bufferService.buffer.lines.get(row); - const joinedRanges = joinerRegistry ? joinerRegistry.getJoinedCharacters(row) : []; + const joinedRanges = this._characterJoinerService.getJoinedCharacters(row); for (let x = 0; x < this._bufferService.cols; x++) { line!.loadCell(x, this._workCell); let cell = this._workCell; @@ -160,7 +158,7 @@ export class TextRenderLayer extends BaseRenderLayer { ctx.save(); - this._forEachCell(firstRow, lastRow, null, (cell, x, y) => { + this._forEachCell(firstRow, lastRow, (cell, x, y) => { // libvte and xterm both draw the background (but not foreground) of invisible characters, // so we should too. let nextFillStyle = null; // null represents default background color @@ -213,7 +211,7 @@ export class TextRenderLayer extends BaseRenderLayer { } private _drawForeground(firstRow: number, lastRow: number): void { - this._forEachCell(firstRow, lastRow, this._characterJoinerRegistry, (cell, x, y) => { + this._forEachCell(firstRow, lastRow, (cell, x, y) => { if (cell.isInvisible()) { return; } diff --git a/src/browser/renderer/Types.d.ts b/src/browser/renderer/Types.d.ts index cab14b88..fc137bc8 100644 --- a/src/browser/renderer/Types.d.ts +++ b/src/browser/renderer/Types.d.ts @@ -7,8 +7,6 @@ import { IDisposable } from 'common/Types'; import { IColorSet } from 'browser/Types'; import { IEvent } from 'common/EventEmitter'; -export type CharacterJoinerHandler = (text: string) => [number, number][]; - export interface IRenderDimensions { scaledCharWidth: number; scaledCharHeight: number; @@ -54,19 +52,6 @@ export interface IRenderer extends IDisposable { onOptionsChanged(): void; clear(): void; renderRows(start: number, end: number): void; - registerCharacterJoiner(handler: CharacterJoinerHandler): number; - deregisterCharacterJoiner(joinerId: number): boolean; -} - -export interface ICharacterJoiner { - id: number; - handler: CharacterJoinerHandler; -} - -export interface ICharacterJoinerRegistry { - registerCharacterJoiner(handler: (text: string) => [number, number][]): number; - deregisterCharacterJoiner(joinerId: number): boolean; - getJoinedCharacters(row: number): [number, number][]; } export interface IRenderLayer extends IDisposable { @@ -106,16 +91,6 @@ export interface IRenderLayer extends IDisposable { */ onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void; - /** - * Registers a handler to join characters to render as a group - */ - registerCharacterJoiner?(joiner: ICharacterJoiner): void; - - /** - * Deregisters the specified character joiner handler - */ - deregisterCharacterJoiner?(joinerId: number): void; - /** * Resize the render layer. */ diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index f0a92259..8dd1ac0e 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IRenderer, IRenderDimensions, CharacterJoinerHandler, IRequestRedrawEvent } from 'browser/renderer/Types'; +import { IRenderer, IRenderDimensions, IRequestRedrawEvent } from 'browser/renderer/Types'; import { BOLD_CLASS, ITALIC_CLASS, CURSOR_CLASS, CURSOR_STYLE_BLOCK_CLASS, CURSOR_BLINK_CLASS, CURSOR_STYLE_BAR_CLASS, CURSOR_STYLE_UNDERLINE_CLASS, DomRendererRowFactory } from 'browser/renderer/dom/DomRendererRowFactory'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { Disposable } from 'common/Lifecycle'; @@ -372,9 +372,6 @@ export class DomRenderer extends Disposable implements IRenderer { return `.${TERMINAL_CLASS_PREFIX}${this._terminalClass}`; } - public registerCharacterJoiner(handler: CharacterJoinerHandler): number { return -1; } - public deregisterCharacterJoiner(joinerId: number): boolean { return false; } - private _onLinkHover(e: ILinkifierEvent): void { this._setCellUnderline(e.x1, e.x2, e.y1, e.y2, e.cols, true); } diff --git a/src/browser/renderer/CharacterJoinerRegistry.test.ts b/src/browser/services/CharacterJoinerService.test.ts similarity index 60% rename from src/browser/renderer/CharacterJoinerRegistry.test.ts rename to src/browser/services/CharacterJoinerService.test.ts index bca12d6b..94abc4d5 100644 --- a/src/browser/renderer/CharacterJoinerRegistry.test.ts +++ b/src/browser/services/CharacterJoinerService.test.ts @@ -4,15 +4,15 @@ */ import { assert } from 'chai'; -import { ICharacterJoinerRegistry } from 'browser/renderer/Types'; -import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistry'; +import { ICharacterJoinerService } from 'browser/services/Services'; +import { CharacterJoinerService } from 'browser/services/CharacterJoinerService'; import { BufferLine } from 'common/buffer/BufferLine'; import { IBufferLine } from 'common/Types'; import { CellData } from 'common/buffer/CellData'; import { MockBufferService } from 'common/TestUtils.test'; -describe('CharacterJoinerRegistry', () => { - let registry: ICharacterJoinerRegistry; +describe('CharacterJoinerService', () => { + let service: ICharacterJoinerService; beforeEach(() => { const bufferService = new MockBufferService(16, 10); @@ -39,225 +39,225 @@ describe('CharacterJoinerRegistry', () => { for (let i = 0; i < sub.length; ++i) line6.setCell(i + oldSize, sub.loadCell(i, new CellData())); lines.set(6, line6); - registry = new CharacterJoinerRegistry(bufferService); + service = new CharacterJoinerService(bufferService); }); it('has no joiners upon creation', () => { - assert.deepEqual(registry.getJoinedCharacters(0), []); + assert.deepEqual(service.getJoinedCharacters(0), []); }); it('returns ranges matched by the registered joiners', () => { - registry.registerCharacterJoiner(substringJoiner('->')); + service.register(substringJoiner('->')); assert.deepEqual( - registry.getJoinedCharacters(0), + service.getJoinedCharacters(0), [[2, 4], [7, 9], [12, 14]] ); }); it('processes the input using all provided joiners', () => { - registry.registerCharacterJoiner(substringJoiner('->')); + service.register(substringJoiner('->')); assert.deepEqual( - registry.getJoinedCharacters(1), + service.getJoinedCharacters(1), [[2, 4], [12, 14]] ); - registry.registerCharacterJoiner(substringJoiner('=>')); + service.register(substringJoiner('=>')); assert.deepEqual( - registry.getJoinedCharacters(1), + service.getJoinedCharacters(1), [[2, 4], [7, 9], [12, 14]] ); }); it('removes deregistered joiners from future calls', () => { - const joiner1 = registry.registerCharacterJoiner(substringJoiner('->')); - const joiner2 = registry.registerCharacterJoiner(substringJoiner('=>')); + const joiner1 = service.register(substringJoiner('->')); + const joiner2 = service.register(substringJoiner('=>')); assert.deepEqual( - registry.getJoinedCharacters(1), + service.getJoinedCharacters(1), [[2, 4], [7, 9], [12, 14]] ); - registry.deregisterCharacterJoiner(joiner1); + service.deregister(joiner1); assert.deepEqual( - registry.getJoinedCharacters(1), + service.getJoinedCharacters(1), [[7, 9]] ); - registry.deregisterCharacterJoiner(joiner2); + service.deregister(joiner2); assert.deepEqual( - registry.getJoinedCharacters(1), + service.getJoinedCharacters(1), [] ); }); it('doesn\'t process joins on differently-styled characters', () => { - registry.registerCharacterJoiner(substringJoiner('->')); + service.register(substringJoiner('->')); assert.deepEqual( - registry.getJoinedCharacters(2), + service.getJoinedCharacters(2), [[2, 4], [12, 14]] ); }); it('returns an empty list of ranges if there is nothing to be joined', () => { - registry.registerCharacterJoiner(substringJoiner('->')); + service.register(substringJoiner('->')); assert.deepEqual( - registry.getJoinedCharacters(3), + service.getJoinedCharacters(3), [] ); }); it('returns an empty list of ranges if the line is empty', () => { - registry.registerCharacterJoiner(substringJoiner('->')); + service.register(substringJoiner('->')); assert.deepEqual( - registry.getJoinedCharacters(4), + service.getJoinedCharacters(4), [] ); }); it('returns false when trying to deregister a joiner that does not exist', () => { - registry.registerCharacterJoiner(substringJoiner('->')); - assert.deepEqual(registry.deregisterCharacterJoiner(123), false); + service.register(substringJoiner('->')); + assert.deepEqual(service.deregister(123), false); assert.deepEqual( - registry.getJoinedCharacters(0), + service.getJoinedCharacters(0), [[2, 4], [7, 9], [12, 14]] ); }); it('doesn\'t process same-styled ranges that only have one character', () => { - registry.registerCharacterJoiner(substringJoiner('a')); - registry.registerCharacterJoiner(substringJoiner('b')); - registry.registerCharacterJoiner(substringJoiner('d')); + service.register(substringJoiner('a')); + service.register(substringJoiner('b')); + service.register(substringJoiner('d')); assert.deepEqual( - registry.getJoinedCharacters(5), + service.getJoinedCharacters(5), [[5, 6]] ); }); it('handles ranges that extend all the way to the end of the line', () => { - registry.registerCharacterJoiner(substringJoiner('-> d')); + service.register(substringJoiner('-> d')); assert.deepEqual( - registry.getJoinedCharacters(2), + service.getJoinedCharacters(2), [[12, 16]] ); }); it('handles adjacent ranges', () => { - registry.registerCharacterJoiner(substringJoiner('->')); - registry.registerCharacterJoiner(substringJoiner('> c ')); + service.register(substringJoiner('->')); + service.register(substringJoiner('> c ')); assert.deepEqual( - registry.getJoinedCharacters(2), + service.getJoinedCharacters(2), [[2, 4], [8, 12], [12, 14]] ); }); it('handles fullwidth characters in the middle of ranges', () => { - registry.registerCharacterJoiner(substringJoiner('wi¥de')); + service.register(substringJoiner('wi¥de')); assert.deepEqual( - registry.getJoinedCharacters(6), + service.getJoinedCharacters(6), [[0, 6]] ); }); it('handles fullwidth characters at the end of ranges', () => { - registry.registerCharacterJoiner(substringJoiner('wi¥')); + service.register(substringJoiner('wi¥')); assert.deepEqual( - registry.getJoinedCharacters(6), + service.getJoinedCharacters(6), [[0, 4]] ); }); it('handles emojis in the middle of ranges', () => { - registry.registerCharacterJoiner(substringJoiner('emo\xf0\x9f\x98\x81 ji')); + service.register(substringJoiner('emo\xf0\x9f\x98\x81 ji')); assert.deepEqual( - registry.getJoinedCharacters(6), + service.getJoinedCharacters(6), [[6, 13]] ); }); it('handles emojis at the end of ranges', () => { - registry.registerCharacterJoiner(substringJoiner('emo\xf0\x9f\x98\x81 ')); + service.register(substringJoiner('emo\xf0\x9f\x98\x81 ')); assert.deepEqual( - registry.getJoinedCharacters(6), + service.getJoinedCharacters(6), [[6, 11]] ); }); it('handles ranges after wide and emoji characters', () => { - registry.registerCharacterJoiner(substringJoiner('abc')); + service.register(substringJoiner('abc')); assert.deepEqual( - registry.getJoinedCharacters(6), + service.getJoinedCharacters(6), [[13, 16]] ); }); describe('range merging', () => { it('inserts a new range before the existing ones', () => { - registry.registerCharacterJoiner(() => [[1, 2], [2, 3]]); - registry.registerCharacterJoiner(() => [[0, 1]]); + service.register(() => [[1, 2], [2, 3]]); + service.register(() => [[0, 1]]); assert.deepEqual( - registry.getJoinedCharacters(0), + service.getJoinedCharacters(0), [[0, 1], [1, 2], [2, 3]] ); }); it('inserts in between two ranges', () => { - registry.registerCharacterJoiner(() => [[0, 2], [4, 6]]); - registry.registerCharacterJoiner(() => [[2, 4]]); + service.register(() => [[0, 2], [4, 6]]); + service.register(() => [[2, 4]]); assert.deepEqual( - registry.getJoinedCharacters(0), + service.getJoinedCharacters(0), [[0, 2], [2, 4], [4, 6]] ); }); it('inserts after the last range', () => { - registry.registerCharacterJoiner(() => [[0, 2], [4, 6]]); - registry.registerCharacterJoiner(() => [[6, 8]]); + service.register(() => [[0, 2], [4, 6]]); + service.register(() => [[6, 8]]); assert.deepEqual( - registry.getJoinedCharacters(0), + service.getJoinedCharacters(0), [[0, 2], [4, 6], [6, 8]] ); }); it('extends the beginning of a range', () => { - registry.registerCharacterJoiner(() => [[0, 2], [4, 6]]); - registry.registerCharacterJoiner(() => [[3, 5]]); + service.register(() => [[0, 2], [4, 6]]); + service.register(() => [[3, 5]]); assert.deepEqual( - registry.getJoinedCharacters(0), + service.getJoinedCharacters(0), [[0, 2], [3, 6]] ); }); it('extends the end of a range', () => { - registry.registerCharacterJoiner(() => [[0, 2], [4, 6]]); - registry.registerCharacterJoiner(() => [[1, 4]]); + service.register(() => [[0, 2], [4, 6]]); + service.register(() => [[1, 4]]); assert.deepEqual( - registry.getJoinedCharacters(0), + service.getJoinedCharacters(0), [[0, 4], [4, 6]] ); }); it('extends the last range', () => { - registry.registerCharacterJoiner(() => [[0, 2], [4, 6]]); - registry.registerCharacterJoiner(() => [[5, 7]]); + service.register(() => [[0, 2], [4, 6]]); + service.register(() => [[5, 7]]); assert.deepEqual( - registry.getJoinedCharacters(0), + service.getJoinedCharacters(0), [[0, 2], [4, 7]] ); }); it('connects two ranges', () => { - registry.registerCharacterJoiner(() => [[0, 2], [4, 6]]); - registry.registerCharacterJoiner(() => [[1, 5]]); + service.register(() => [[0, 2], [4, 6]]); + service.register(() => [[1, 5]]); assert.deepEqual( - registry.getJoinedCharacters(0), + service.getJoinedCharacters(0), [[0, 6]] ); }); it('connects more than two ranges', () => { - registry.registerCharacterJoiner(() => [[0, 2], [4, 6], [8, 10], [12, 14]]); - registry.registerCharacterJoiner(() => [[1, 10]]); + service.register(() => [[0, 2], [4, 6], [8, 10], [12, 14]]); + service.register(() => [[1, 10]]); assert.deepEqual( - registry.getJoinedCharacters(0), + service.getJoinedCharacters(0), [[0, 10], [12, 14]] ); }); diff --git a/src/browser/renderer/CharacterJoinerRegistry.ts b/src/browser/services/CharacterJoinerService.ts similarity index 95% rename from src/browser/renderer/CharacterJoinerRegistry.ts rename to src/browser/services/CharacterJoinerService.ts index 5385b76b..ea65c29b 100644 --- a/src/browser/renderer/CharacterJoinerRegistry.ts +++ b/src/browser/services/CharacterJoinerService.ts @@ -4,11 +4,12 @@ */ import { IBufferLine, ICellData, CharData } from 'common/Types'; -import { ICharacterJoinerRegistry, ICharacterJoiner } from 'browser/renderer/Types'; +import { ICharacterJoiner } from 'browser/Types'; import { AttributeData } from 'common/buffer/AttributeData'; import { WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants'; import { CellData } from 'common/buffer/CellData'; import { IBufferService } from 'common/services/Services'; +import { ICharacterJoinerService } from 'browser/services/Services'; export class JoinedCellData extends AttributeData implements ICellData { private _width: number; @@ -55,15 +56,18 @@ export class JoinedCellData extends AttributeData implements ICellData { } } -export class CharacterJoinerRegistry implements ICharacterJoinerRegistry { +export class CharacterJoinerService implements ICharacterJoinerService { + public serviceBrand: undefined; private _characterJoiners: ICharacterJoiner[] = []; private _nextCharacterJoinerId: number = 0; private _workCell: CellData = new CellData(); - constructor(private _bufferService: IBufferService) { } + constructor( + @IBufferService private _bufferService: IBufferService + ) { } - public registerCharacterJoiner(handler: (text: string) => [number, number][]): number { + public register(handler: (text: string) => [number, number][]): number { const joiner: ICharacterJoiner = { id: this._nextCharacterJoinerId++, handler @@ -73,7 +77,7 @@ export class CharacterJoinerRegistry implements ICharacterJoinerRegistry { return joiner.id; } - public deregisterCharacterJoiner(joinerId: number): boolean { + public deregister(joinerId: number): boolean { for (let i = 0; i < this._characterJoiners.length; i++) { if (this._characterJoiners[i].id === joinerId) { this._characterJoiners.splice(i, 1); @@ -177,7 +181,7 @@ export class CharacterJoinerRegistry implements ICharacterJoinerRegistry { // We merge any overlapping ranges across the different joiners const joinerRanges = this._characterJoiners[i].handler(text); for (let j = 0; j < joinerRanges.length; j++) { - CharacterJoinerRegistry._mergeRanges(joinedRanges, joinerRanges[j]); + CharacterJoinerService._mergeRanges(joinedRanges, joinerRanges[j]); } } this._stringRangesToCellRanges(joinedRanges, lineData, startCol); diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 51971091..fc2eb435 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IRenderer, IRenderDimensions, CharacterJoinerHandler } from 'browser/renderer/Types'; +import { IRenderer, IRenderDimensions } from 'browser/renderer/Types'; import { RenderDebouncer } from 'browser/RenderDebouncer'; import { EventEmitter, IEvent } from 'common/EventEmitter'; import { Disposable } from 'common/Lifecycle'; @@ -214,12 +214,4 @@ export class RenderService extends Disposable implements IRenderService { public clear(): void { this._renderer.clear(); } - - public registerCharacterJoiner(handler: CharacterJoinerHandler): number { - return this._renderer.registerCharacterJoiner(handler); - } - - public deregisterCharacterJoiner(joinerId: number): boolean { - return this._renderer.deregisterCharacterJoiner(joinerId); - } } diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts index f06e320b..8c8a7bd9 100644 --- a/src/browser/services/Services.ts +++ b/src/browser/services/Services.ts @@ -4,7 +4,7 @@ */ import { IEvent } from 'common/EventEmitter'; -import { IRenderDimensions, IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types'; +import { IRenderDimensions, IRenderer } from 'browser/renderer/Types'; import { IColorSet } from 'browser/Types'; import { ISelectionRedrawRequestEvent as ISelectionRequestRedrawEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types'; import { createDecorator } from 'common/services/ServiceRegistry'; @@ -66,8 +66,6 @@ export interface IRenderService extends IDisposable { onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void; onCursorMove(): void; clear(): void; - registerCharacterJoiner(handler: CharacterJoinerHandler): number; - deregisterCharacterJoiner(joinerId: number): boolean; } export const ISelectionService = createDecorator('SelectionService'); @@ -104,3 +102,13 @@ export interface ISoundService { playBellSound(): void; } + + +export const ICharacterJoinerService = createDecorator('CharacterJoinerService'); +export interface ICharacterJoinerService { + serviceBrand: undefined; + + register(handler: (text: string) => [number, number][]): number; + deregister(joinerId: number): boolean; + getJoinedCharacters(row: number): [number, number][]; +}