Merge pull request #3284 from Tyriar/tyriar/charJoinerService

Convert CharacterJoinerRegistry to a service
This commit is contained in:
Daniel Imms
2021-04-02 07:57:38 -07:00
committed by GitHub
14 changed files with 145 additions and 173 deletions
+17 -6
View File
@@ -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);
}
}
+2 -10
View File
@@ -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.');
}
+7
View File
@@ -302,3 +302,10 @@ interface IBufferCellPosition {
x: number;
y: number;
}
export type CharacterJoinerHandler = (text: string) => [number, number][];
export interface ICharacterJoiner {
id: number;
handler: CharacterJoinerHandler;
}
+4 -4
View File
@@ -37,10 +37,10 @@ export class CursorRenderLayer extends BaseRenderLayer {
colors: IColorSet,
rendererId: number,
private _onRequestRedraw: IEventEmitter<IRequestRedrawEvent>,
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 = {
+2 -2
View File
@@ -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));
+8 -20
View File
@@ -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);
}
}
+2 -2
View File
@@ -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();
+9 -11
View File
@@ -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<CharData>();
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;
}
-25
View File
@@ -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.
*/
+1 -4
View File
@@ -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);
}
@@ -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]]
);
});
@@ -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);
+1 -9
View File
@@ -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);
}
}
+11 -3
View File
@@ -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<ISelectionService>('SelectionService');
@@ -104,3 +102,13 @@ export interface ISoundService {
playBellSound(): void;
}
export const ICharacterJoinerService = createDecorator<ICharacterJoinerService>('CharacterJoinerService');
export interface ICharacterJoinerService {
serviceBrand: undefined;
register(handler: (text: string) => [number, number][]): number;
deregister(joinerId: number): boolean;
getJoinedCharacters(row: number): [number, number][];
}