mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge pull request #3284 from Tyriar/tyriar/charJoinerService
Convert CharacterJoinerRegistry to a service
This commit is contained in:
+17
-6
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.');
|
||||
}
|
||||
|
||||
Vendored
+7
@@ -302,3 +302,10 @@ interface IBufferCellPosition {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export type CharacterJoinerHandler = (text: string) => [number, number][];
|
||||
|
||||
export interface ICharacterJoiner {
|
||||
id: number;
|
||||
handler: CharacterJoinerHandler;
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Vendored
-25
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+71
-71
@@ -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]]
|
||||
);
|
||||
});
|
||||
+10
-6
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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][];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user