From f19d7f0b705d81da2334381b307ed2f09a6ab4fb Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 2 Apr 2021 07:08:06 -0700 Subject: [PATCH 1/5] Require spacing of objects outside of test files --- .eslintrc.json | 14 +- .../src/atlas/WebglCharAtlas.ts | 4 +- .../src/renderLayer/BaseRenderLayer.ts | 2 +- src/browser/renderer/BaseRenderLayer.ts | 2 +- .../renderer/atlas/DynamicCharAtlas.ts | 4 +- src/common/CircularList.ts | 4 +- src/common/InputHandler.ts | 148 +++++++++--------- src/common/buffer/Buffer.ts | 2 +- 8 files changed, 96 insertions(+), 84 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 8c416f159..114a0f1d0 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -151,6 +151,10 @@ "warn", "never" ], + "object-curly-spacing": [ + "warn", + "always" + ], "prefer-const": "warn", "spaced-comment": [ "warn", @@ -160,5 +164,13 @@ "exceptions": ["-"] } ] - } + }, + "overrides": [ + { + "files": ["**/*.test.ts"], + "rules": { + "object-curly-spacing": "off" + } + } + ] } diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts index 223ec16ae..d252f065c 100644 --- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts +++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts @@ -80,12 +80,12 @@ export class WebglCharAtlas implements IDisposable { // The canvas needs alpha because we use clearColor to convert the background color to alpha. // It might also contain some characters with transparent backgrounds if allowTransparency is // set. - this._cacheCtx = throwIfFalsy(this.cacheCanvas.getContext('2d', {alpha: true})); + this._cacheCtx = throwIfFalsy(this.cacheCanvas.getContext('2d', { alpha: true })); this._tmpCanvas = document.createElement('canvas'); this._tmpCanvas.width = this._config.scaledCharWidth * 2 + TMP_CANVAS_GLYPH_PADDING * 2; this._tmpCanvas.height = this._config.scaledCharHeight + TMP_CANVAS_GLYPH_PADDING * 2; - this._tmpCtx = throwIfFalsy(this._tmpCanvas.getContext('2d', {alpha: this._config.allowTransparency})); + this._tmpCtx = throwIfFalsy(this._tmpCanvas.getContext('2d', { alpha: this._config.allowTransparency })); } public dispose(): void { diff --git a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts index da3b4d418..afd2b384f 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts @@ -46,7 +46,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { } private _initCanvas(): void { - this._ctx = throwIfFalsy(this._canvas.getContext('2d', {alpha: this._alpha})); + this._ctx = throwIfFalsy(this._canvas.getContext('2d', { alpha: this._alpha })); // Draw the background if this is an opaque layer if (!this._alpha) { this._clearAll(); diff --git a/src/browser/renderer/BaseRenderLayer.ts b/src/browser/renderer/BaseRenderLayer.ts index 8afec352c..86e12f489 100644 --- a/src/browser/renderer/BaseRenderLayer.ts +++ b/src/browser/renderer/BaseRenderLayer.ts @@ -66,7 +66,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { } private _initCanvas(): void { - this._ctx = throwIfFalsy(this._canvas.getContext('2d', {alpha: this._alpha})); + this._ctx = throwIfFalsy(this._canvas.getContext('2d', { alpha: this._alpha })); // Draw the background if this is an opaque layer if (!this._alpha) { this._clearAll(); diff --git a/src/browser/renderer/atlas/DynamicCharAtlas.ts b/src/browser/renderer/atlas/DynamicCharAtlas.ts index 696fb63c4..d370fc8f3 100644 --- a/src/browser/renderer/atlas/DynamicCharAtlas.ts +++ b/src/browser/renderer/atlas/DynamicCharAtlas.ts @@ -91,12 +91,12 @@ export class DynamicCharAtlas extends BaseCharAtlas { // The canvas needs alpha because we use clearColor to convert the background color to alpha. // It might also contain some characters with transparent backgrounds if allowTransparency is // set. - this._cacheCtx = throwIfFalsy(this._cacheCanvas.getContext('2d', {alpha: true})); + this._cacheCtx = throwIfFalsy(this._cacheCanvas.getContext('2d', { alpha: true })); const tmpCanvas = document.createElement('canvas'); tmpCanvas.width = this._config.scaledCharWidth; tmpCanvas.height = this._config.scaledCharHeight; - this._tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d', {alpha: this._config.allowTransparency})); + this._tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d', { alpha: this._config.allowTransparency })); this._width = Math.floor(TEXTURE_WIDTH / this._config.scaledCharWidth); this._height = Math.floor(TEXTURE_HEIGHT / this._config.scaledCharHeight); diff --git a/src/common/CircularList.ts b/src/common/CircularList.ts index ab00e6812..4d2c04eca 100644 --- a/src/common/CircularList.ts +++ b/src/common/CircularList.ts @@ -158,7 +158,7 @@ export class CircularList implements ICircularList { this._array[this._getCyclicIndex(i)] = this._array[this._getCyclicIndex(i + deleteCount)]; } this._length -= deleteCount; - this.onDeleteEmitter.fire({index: start, amount: deleteCount}); + this.onDeleteEmitter.fire({ index: start, amount: deleteCount }); } // Add items @@ -169,7 +169,7 @@ export class CircularList implements ICircularList { this._array[this._getCyclicIndex(start + i)] = items[i]; } if (items.length) { - this.onInsertEmitter.fire({index: start, amount: items.length}); + this.onInsertEmitter.fire({ index: start, amount: items.length }); } // Adjust length as needed diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 13b0a0e7c..da315005e 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -24,7 +24,7 @@ import { DcsHandler } from 'common/parser/DcsParser'; /** * Map collect to glevel. Used in `selectCharset`. */ -const GLEVEL: {[key: string]: number} = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1, '.': 2}; +const GLEVEL: {[key: string]: number} = { '(': 0, ')': 1, '*': 2, '+': 3, '-': 1, '.': 2 }; /** * VT commands done by the parser - FIXME: move this to the parser? @@ -174,7 +174,7 @@ class DECRQSS implements IDcsHandler { this._coreService.triggerDataEvent(`${C0.ESC}P1$r0m${C0.ESC}\\`); break; case ' q': // DECSCUSR - const STYLES: {[key: string]: number} = {'block': 2, 'underline': 4, 'bar': 6}; + const STYLES: {[key: string]: number} = { 'block': 2, 'underline': 4, 'bar': 6 }; let style = STYLES[this._optionsService.options.cursorStyle]; style -= this._optionsService.options.cursorBlink ? 1 : 0; this._coreService.triggerDataEvent(`${C0.ESC}P1$r${style} q${C0.ESC}\\`); @@ -314,53 +314,53 @@ export class InputHandler extends Disposable implements IInputHandler { /** * CSI handler */ - this._parser.registerCsiHandler({final: '@'}, params => this.insertChars(params)); - this._parser.registerCsiHandler({intermediates: ' ', final: '@'}, params => this.scrollLeft(params)); - this._parser.registerCsiHandler({final: 'A'}, params => this.cursorUp(params)); - this._parser.registerCsiHandler({intermediates: ' ', final: 'A'}, params => this.scrollRight(params)); - this._parser.registerCsiHandler({final: 'B'}, params => this.cursorDown(params)); - this._parser.registerCsiHandler({final: 'C'}, params => this.cursorForward(params)); - this._parser.registerCsiHandler({final: 'D'}, params => this.cursorBackward(params)); - this._parser.registerCsiHandler({final: 'E'}, params => this.cursorNextLine(params)); - this._parser.registerCsiHandler({final: 'F'}, params => this.cursorPrecedingLine(params)); - this._parser.registerCsiHandler({final: 'G'}, params => this.cursorCharAbsolute(params)); - this._parser.registerCsiHandler({final: 'H'}, params => this.cursorPosition(params)); - this._parser.registerCsiHandler({final: 'I'}, params => this.cursorForwardTab(params)); - this._parser.registerCsiHandler({final: 'J'}, params => this.eraseInDisplay(params)); - this._parser.registerCsiHandler({prefix: '?', final: 'J'}, params => this.eraseInDisplay(params)); - this._parser.registerCsiHandler({final: 'K'}, params => this.eraseInLine(params)); - this._parser.registerCsiHandler({prefix: '?', final: 'K'}, params => this.eraseInLine(params)); - this._parser.registerCsiHandler({final: 'L'}, params => this.insertLines(params)); - this._parser.registerCsiHandler({final: 'M'}, params => this.deleteLines(params)); - this._parser.registerCsiHandler({final: 'P'}, params => this.deleteChars(params)); - this._parser.registerCsiHandler({final: 'S'}, params => this.scrollUp(params)); - this._parser.registerCsiHandler({final: 'T'}, params => this.scrollDown(params)); - this._parser.registerCsiHandler({final: 'X'}, params => this.eraseChars(params)); - this._parser.registerCsiHandler({final: 'Z'}, params => this.cursorBackwardTab(params)); - this._parser.registerCsiHandler({final: '`'}, params => this.charPosAbsolute(params)); - this._parser.registerCsiHandler({final: 'a'}, params => this.hPositionRelative(params)); - this._parser.registerCsiHandler({final: 'b'}, params => this.repeatPrecedingCharacter(params)); - this._parser.registerCsiHandler({final: 'c'}, params => this.sendDeviceAttributesPrimary(params)); - this._parser.registerCsiHandler({prefix: '>', final: 'c'}, params => this.sendDeviceAttributesSecondary(params)); - this._parser.registerCsiHandler({final: 'd'}, params => this.linePosAbsolute(params)); - this._parser.registerCsiHandler({final: 'e'}, params => this.vPositionRelative(params)); - this._parser.registerCsiHandler({final: 'f'}, params => this.hVPosition(params)); - this._parser.registerCsiHandler({final: 'g'}, params => this.tabClear(params)); - this._parser.registerCsiHandler({final: 'h'}, params => this.setMode(params)); - this._parser.registerCsiHandler({prefix: '?', final: 'h'}, params => this.setModePrivate(params)); - this._parser.registerCsiHandler({final: 'l'}, params => this.resetMode(params)); - this._parser.registerCsiHandler({prefix: '?', final: 'l'}, params => this.resetModePrivate(params)); - this._parser.registerCsiHandler({final: 'm'}, params => this.charAttributes(params)); - this._parser.registerCsiHandler({final: 'n'}, params => this.deviceStatus(params)); - this._parser.registerCsiHandler({prefix: '?', final: 'n'}, params => this.deviceStatusPrivate(params)); - this._parser.registerCsiHandler({intermediates: '!', final: 'p'}, params => this.softReset(params)); - this._parser.registerCsiHandler({intermediates: ' ', final: 'q'}, params => this.setCursorStyle(params)); - this._parser.registerCsiHandler({final: 'r'}, params => this.setScrollRegion(params)); - this._parser.registerCsiHandler({final: 's'}, params => this.saveCursor(params)); - this._parser.registerCsiHandler({final: 't'}, params => this.windowOptions(params)); - this._parser.registerCsiHandler({final: 'u'}, params => this.restoreCursor(params)); - this._parser.registerCsiHandler({intermediates: '\'', final: '}'}, params => this.insertColumns(params)); - this._parser.registerCsiHandler({intermediates: '\'', final: '~'}, params => this.deleteColumns(params)); + this._parser.registerCsiHandler({ final: '@' }, params => this.insertChars(params)); + this._parser.registerCsiHandler({ intermediates: ' ', final: '@' }, params => this.scrollLeft(params)); + this._parser.registerCsiHandler({ final: 'A' }, params => this.cursorUp(params)); + this._parser.registerCsiHandler({ intermediates: ' ', final: 'A' }, params => this.scrollRight(params)); + this._parser.registerCsiHandler({ final: 'B' }, params => this.cursorDown(params)); + this._parser.registerCsiHandler({ final: 'C' }, params => this.cursorForward(params)); + this._parser.registerCsiHandler({ final: 'D' }, params => this.cursorBackward(params)); + this._parser.registerCsiHandler({ final: 'E' }, params => this.cursorNextLine(params)); + this._parser.registerCsiHandler({ final: 'F' }, params => this.cursorPrecedingLine(params)); + this._parser.registerCsiHandler({ final: 'G' }, params => this.cursorCharAbsolute(params)); + this._parser.registerCsiHandler({ final: 'H' }, params => this.cursorPosition(params)); + this._parser.registerCsiHandler({ final: 'I' }, params => this.cursorForwardTab(params)); + this._parser.registerCsiHandler({ final: 'J' }, params => this.eraseInDisplay(params)); + this._parser.registerCsiHandler({ prefix: '?', final: 'J' }, params => this.eraseInDisplay(params)); + this._parser.registerCsiHandler({ final: 'K' }, params => this.eraseInLine(params)); + this._parser.registerCsiHandler({ prefix: '?', final: 'K' }, params => this.eraseInLine(params)); + this._parser.registerCsiHandler({ final: 'L' }, params => this.insertLines(params)); + this._parser.registerCsiHandler({ final: 'M' }, params => this.deleteLines(params)); + this._parser.registerCsiHandler({ final: 'P' }, params => this.deleteChars(params)); + this._parser.registerCsiHandler({ final: 'S' }, params => this.scrollUp(params)); + this._parser.registerCsiHandler({ final: 'T' }, params => this.scrollDown(params)); + this._parser.registerCsiHandler({ final: 'X' }, params => this.eraseChars(params)); + this._parser.registerCsiHandler({ final: 'Z' }, params => this.cursorBackwardTab(params)); + this._parser.registerCsiHandler({ final: '`' }, params => this.charPosAbsolute(params)); + this._parser.registerCsiHandler({ final: 'a' }, params => this.hPositionRelative(params)); + this._parser.registerCsiHandler({ final: 'b' }, params => this.repeatPrecedingCharacter(params)); + this._parser.registerCsiHandler({ final: 'c' }, params => this.sendDeviceAttributesPrimary(params)); + this._parser.registerCsiHandler({ prefix: '>', final: 'c' }, params => this.sendDeviceAttributesSecondary(params)); + this._parser.registerCsiHandler({ final: 'd' }, params => this.linePosAbsolute(params)); + this._parser.registerCsiHandler({ final: 'e' }, params => this.vPositionRelative(params)); + this._parser.registerCsiHandler({ final: 'f' }, params => this.hVPosition(params)); + this._parser.registerCsiHandler({ final: 'g' }, params => this.tabClear(params)); + this._parser.registerCsiHandler({ final: 'h' }, params => this.setMode(params)); + this._parser.registerCsiHandler({ prefix: '?', final: 'h' }, params => this.setModePrivate(params)); + this._parser.registerCsiHandler({ final: 'l' }, params => this.resetMode(params)); + this._parser.registerCsiHandler({ prefix: '?', final: 'l' }, params => this.resetModePrivate(params)); + this._parser.registerCsiHandler({ final: 'm' }, params => this.charAttributes(params)); + this._parser.registerCsiHandler({ final: 'n' }, params => this.deviceStatus(params)); + this._parser.registerCsiHandler({ prefix: '?', final: 'n' }, params => this.deviceStatusPrivate(params)); + this._parser.registerCsiHandler({ intermediates: '!', final: 'p' }, params => this.softReset(params)); + this._parser.registerCsiHandler({ intermediates: ' ', final: 'q' }, params => this.setCursorStyle(params)); + this._parser.registerCsiHandler({ final: 'r' }, params => this.setScrollRegion(params)); + this._parser.registerCsiHandler({ final: 's' }, params => this.saveCursor(params)); + this._parser.registerCsiHandler({ final: 't' }, params => this.windowOptions(params)); + this._parser.registerCsiHandler({ final: 'u' }, params => this.restoreCursor(params)); + this._parser.registerCsiHandler({ intermediates: '\'', final: '}' }, params => this.insertColumns(params)); + this._parser.registerCsiHandler({ intermediates: '\'', final: '~' }, params => this.deleteColumns(params)); /** * execute handler @@ -426,32 +426,32 @@ export class InputHandler extends Disposable implements IInputHandler { /** * ESC handlers */ - this._parser.registerEscHandler({final: '7'}, () => this.saveCursor()); - this._parser.registerEscHandler({final: '8'}, () => this.restoreCursor()); - this._parser.registerEscHandler({final: 'D'}, () => this.index()); - this._parser.registerEscHandler({final: 'E'}, () => this.nextLine()); - this._parser.registerEscHandler({final: 'H'}, () => this.tabSet()); - this._parser.registerEscHandler({final: 'M'}, () => this.reverseIndex()); - this._parser.registerEscHandler({final: '='}, () => this.keypadApplicationMode()); - this._parser.registerEscHandler({final: '>'}, () => this.keypadNumericMode()); - this._parser.registerEscHandler({final: 'c'}, () => this.fullReset()); - this._parser.registerEscHandler({final: 'n'}, () => this.setgLevel(2)); - this._parser.registerEscHandler({final: 'o'}, () => this.setgLevel(3)); - this._parser.registerEscHandler({final: '|'}, () => this.setgLevel(3)); - this._parser.registerEscHandler({final: '}'}, () => this.setgLevel(2)); - this._parser.registerEscHandler({final: '~'}, () => this.setgLevel(1)); - this._parser.registerEscHandler({intermediates: '%', final: '@'}, () => this.selectDefaultCharset()); - this._parser.registerEscHandler({intermediates: '%', final: 'G'}, () => this.selectDefaultCharset()); + this._parser.registerEscHandler({ final: '7' }, () => this.saveCursor()); + this._parser.registerEscHandler({ final: '8' }, () => this.restoreCursor()); + this._parser.registerEscHandler({ final: 'D' }, () => this.index()); + this._parser.registerEscHandler({ final: 'E' }, () => this.nextLine()); + this._parser.registerEscHandler({ final: 'H' }, () => this.tabSet()); + this._parser.registerEscHandler({ final: 'M' }, () => this.reverseIndex()); + this._parser.registerEscHandler({ final: '=' }, () => this.keypadApplicationMode()); + this._parser.registerEscHandler({ final: '>' }, () => this.keypadNumericMode()); + this._parser.registerEscHandler({ final: 'c' }, () => this.fullReset()); + this._parser.registerEscHandler({ final: 'n' }, () => this.setgLevel(2)); + this._parser.registerEscHandler({ final: 'o' }, () => this.setgLevel(3)); + this._parser.registerEscHandler({ final: '|' }, () => this.setgLevel(3)); + this._parser.registerEscHandler({ final: '}' }, () => this.setgLevel(2)); + this._parser.registerEscHandler({ final: '~' }, () => this.setgLevel(1)); + this._parser.registerEscHandler({ intermediates: '%', final: '@' }, () => this.selectDefaultCharset()); + this._parser.registerEscHandler({ intermediates: '%', final: 'G' }, () => this.selectDefaultCharset()); for (const flag in CHARSETS) { - this._parser.registerEscHandler({intermediates: '(', final: flag}, () => this.selectCharset('(' + flag)); - this._parser.registerEscHandler({intermediates: ')', final: flag}, () => this.selectCharset(')' + flag)); - this._parser.registerEscHandler({intermediates: '*', final: flag}, () => this.selectCharset('*' + flag)); - this._parser.registerEscHandler({intermediates: '+', final: flag}, () => this.selectCharset('+' + flag)); - this._parser.registerEscHandler({intermediates: '-', final: flag}, () => this.selectCharset('-' + flag)); - this._parser.registerEscHandler({intermediates: '.', final: flag}, () => this.selectCharset('.' + flag)); - this._parser.registerEscHandler({intermediates: '/', final: flag}, () => this.selectCharset('/' + flag)); // TODO: supported? + this._parser.registerEscHandler({ intermediates: '(', final: flag }, () => this.selectCharset('(' + flag)); + this._parser.registerEscHandler({ intermediates: ')', final: flag }, () => this.selectCharset(')' + flag)); + this._parser.registerEscHandler({ intermediates: '*', final: flag }, () => this.selectCharset('*' + flag)); + this._parser.registerEscHandler({ intermediates: '+', final: flag }, () => this.selectCharset('+' + flag)); + this._parser.registerEscHandler({ intermediates: '-', final: flag }, () => this.selectCharset('-' + flag)); + this._parser.registerEscHandler({ intermediates: '.', final: flag }, () => this.selectCharset('.' + flag)); + this._parser.registerEscHandler({ intermediates: '/', final: flag }, () => this.selectCharset('/' + flag)); // TODO: supported? } - this._parser.registerEscHandler({intermediates: '#', final: '8'}, () => this.screenAlignmentPattern()); + this._parser.registerEscHandler({ intermediates: '#', final: '8' }, () => this.screenAlignmentPattern()); /** * error handler @@ -464,7 +464,7 @@ export class InputHandler extends Disposable implements IInputHandler { /** * DCS handler */ - this._parser.registerDcsHandler({intermediates: '$', final: 'q'}, new DECRQSS(this._bufferService, this._coreService, this._logService, this._optionsService)); + this._parser.registerDcsHandler({ intermediates: '$', final: 'q' }, new DECRQSS(this._bufferService, this._coreService, this._logService, this._optionsService)); } public dispose(): void { diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts index e06ffb018..c788bf361 100644 --- a/src/common/buffer/Buffer.ts +++ b/src/common/buffer/Buffer.ts @@ -671,6 +671,6 @@ export class BufferStringIterator implements IBufferStringIterator { content += this._buffer.translateBufferLineToString(i, this._trimRight); } this._current = range.last + 1; - return {range, content}; + return { range, content }; } } From 999116c5367cfd643fa4aca5cdd395c5f8628133 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 2 Apr 2021 07:43:39 -0700 Subject: [PATCH 2/5] Convert CharacterJoinerRegistry to a service Part of #3094, #3283 --- src/browser/Terminal.ts | 17 ++- src/browser/TestUtils.test.ts | 12 +- src/browser/Types.d.ts | 7 + src/browser/renderer/CursorRenderLayer.ts | 8 +- src/browser/renderer/LinkRenderLayer.ts | 4 +- src/browser/renderer/Renderer.ts | 26 +--- src/browser/renderer/SelectionRenderLayer.ts | 4 +- src/browser/renderer/TextRenderLayer.ts | 20 ++- src/browser/renderer/Types.d.ts | 25 --- src/browser/renderer/dom/DomRenderer.ts | 5 +- .../CharacterJoinerService.test.ts} | 142 +++++++++--------- .../CharacterJoinerService.ts} | 16 +- src/browser/services/RenderService.ts | 10 +- src/browser/services/Services.ts | 14 +- 14 files changed, 138 insertions(+), 172 deletions(-) rename src/browser/{renderer/CharacterJoinerRegistry.test.ts => services/CharacterJoinerService.test.ts} (60%) rename src/browser/{renderer/CharacterJoinerRegistry.ts => services/CharacterJoinerService.ts} (95%) diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index 1ab207c7f..d04f9e388 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; @@ -450,6 +452,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); @@ -551,7 +556,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}"`); } @@ -915,13 +920,13 @@ export class Terminal extends CoreTerminal implements ITerminal { } public registerCharacterJoiner(handler: CharacterJoinerHandler): number { - const joinerId = this._renderService!.registerCharacterJoiner(handler); + 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!.deregister(joinerId)) { this.refresh(0, this.rows - 1); } } diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index a2140aa5d..7ab7d9b1c 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 f743934e1..15eef8047 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 d358d580b..a78b20484 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 c41955d93..2492f9215 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 b9d02ff86..bcfae2667 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 ) { 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 80022f011..9054e3ca0 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 1f35fae16..48bf848e9 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 cab14b88e..fc137bc8f 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 f0a922594..8dd1ac0e1 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 bca12d6bd..94abc4d5d 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 5385b76b4..ea65c29b6 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 51971091c..fc2eb4354 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 f06e320bf..8c8a7bd9b 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][]; +} From b511f1b52aa5e82b817beccc9ac80b300518616c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 2 Apr 2021 07:46:30 -0700 Subject: [PATCH 3/5] Fix other case of no spacing --- src/common/CoreTerminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/CoreTerminal.ts b/src/common/CoreTerminal.ts index 699a1b1c4..815326d68 100644 --- a/src/common/CoreTerminal.ts +++ b/src/common/CoreTerminal.ts @@ -122,7 +122,7 @@ export abstract class CoreTerminal extends Disposable implements ICoreTerminal { this.register(forwardEvent(this._coreService.onBinary, this._onBinary)); this.register(this.optionsService.onOptionChange(key => this._updateOptions(key))); this.register(this._bufferService.onScroll(event => { - this._onScroll.fire({position: this._bufferService.buffer.ydisp, source: ScrollSource.TERMINAL}); + this._onScroll.fire({ position: this._bufferService.buffer.ydisp, source: ScrollSource.TERMINAL }); this._dirtyRowService.markRangeDirty(this._bufferService.buffer.scrollTop, this._bufferService.buffer.scrollBottom); })); From 5f96f33cf03b739ccb9c7ee339f7eeacabacd3f8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 2 Apr 2021 07:50:22 -0700 Subject: [PATCH 4/5] Fix dangling comma --- src/browser/renderer/Renderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/renderer/Renderer.ts b/src/browser/renderer/Renderer.ts index bcfae2667..c88ce2562 100644 --- a/src/browser/renderer/Renderer.ts +++ b/src/browser/renderer/Renderer.ts @@ -36,7 +36,7 @@ export class Renderer extends Disposable implements IRenderer { instantiationService: IInstantiationService, @IBufferService private readonly _bufferService: IBufferService, @ICharSizeService private readonly _charSizeService: ICharSizeService, - @IOptionsService private readonly _optionsService: IOptionsService, + @IOptionsService private readonly _optionsService: IOptionsService ) { super(); const allowTransparency = this._optionsService.options.allowTransparency; From 1124f8b5139518f06c89d4aa0951fe3922c00f20 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 2 Apr 2021 07:57:24 -0700 Subject: [PATCH 5/5] Improve error when adding char joiner before open --- src/browser/Terminal.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index d0c698a67..2525fd573 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -919,13 +919,19 @@ export class Terminal extends CoreTerminal implements ITerminal { } public registerCharacterJoiner(handler: CharacterJoinerHandler): number { - const joinerId = this._characterJoinerService!.register(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._characterJoinerService!.deregister(joinerId)) { + if (!this._characterJoinerService) { + throw new Error('Terminal must be opened first'); + } + if (this._characterJoinerService.deregister(joinerId)) { this.refresh(0, this.rows - 1); } }