From c078f3db487c33be1828a4d64dc351e946423d99 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 18:25:22 -0700 Subject: [PATCH 01/12] Move range updating to DirtyRowService --- src/InputHandler.test.ts | 20 +++--- src/InputHandler.ts | 86 ++++++++++---------------- src/Terminal.ts | 59 +++--------------- src/Types.d.ts | 1 - src/common/TestUtils.test.ts | 11 +++- src/common/Types.d.ts | 5 ++ src/common/services/DirtyRowService.ts | 51 +++++++++++++++ src/common/services/Services.d.ts | 10 +++ src/renderer/dom/DomRenderer.ts | 3 - 9 files changed, 127 insertions(+), 119 deletions(-) create mode 100644 src/common/services/DirtyRowService.ts diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 53d0bc49..558cfe7e 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -13,7 +13,7 @@ import { CellData } from 'common/buffer/CellData'; import { Attributes } from 'common/buffer/Constants'; import { AttributeData } from 'common/buffer/AttributeData'; import { Params } from 'common/parser/Params'; -import { MockCoreService, MockBufferService, MockOptionsService, MockLogService } from 'common/TestUtils.test'; +import { MockCoreService, MockBufferService, MockDirtyRowService, MockOptionsService, MockLogService } from 'common/TestUtils.test'; import { IBufferService } from 'common/services/Services'; function getCursor(term: TestTerminal): number[] { @@ -31,7 +31,7 @@ describe('InputHandler', () => { bufferService.buffer.x = 1; bufferService.buffer.y = 2; bufferService.buffer.ybase = 0; - const inputHandler = new InputHandler(terminal, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(terminal, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // Save cursor position inputHandler.saveCursor(); assert.equal(bufferService.buffer.x, 1); @@ -50,7 +50,7 @@ describe('InputHandler', () => { describe('setCursorStyle', () => { it('should call Terminal.setOption with correct params', () => { const terminal = new MockInputHandlingTerminal(); - const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); const collect = ' '; inputHandler.setCursorStyle(Params.fromArray([0]), collect); @@ -93,7 +93,7 @@ describe('InputHandler', () => { const terminal = new MockInputHandlingTerminal(); const collect = '?'; terminal.bracketedPasteMode = false; - const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // Set bracketed paste mode inputHandler.setMode(Params.fromArray([2004]), collect); assert.equal(terminal.bracketedPasteMode, true); @@ -112,7 +112,7 @@ describe('InputHandler', () => { it('insertChars', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -150,7 +150,7 @@ describe('InputHandler', () => { it('deleteChars', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -191,7 +191,7 @@ describe('InputHandler', () => { it('eraseInLine', function(): void { const term = new Terminal(); const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // fill 6 lines to test 3 different states inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -220,7 +220,7 @@ describe('InputHandler', () => { it('eraseInDisplay', function(): void { const term = new Terminal({cols: 80, rows: 7}); const bufferService = new MockBufferService(80, 7); - const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); // fill display with a's for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -355,7 +355,7 @@ describe('InputHandler', () => { describe('print', () => { it('should not cause an infinite loop (regression test)', () => { const term = new Terminal(); - const inputHandler = new InputHandler(term, new MockBufferService(80, 30), new MockCoreService(), new MockLogService(), new MockOptionsService()); + const inputHandler = new InputHandler(term, new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); const container = new Uint32Array(10); container[0] = 0x200B; inputHandler.print(container, 0, 1); @@ -370,7 +370,7 @@ describe('InputHandler', () => { beforeEach(() => { term = new Terminal(); bufferService = new MockBufferService(80, 30); - handler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService()); + handler = new InputHandler(term, bufferService, new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); }); it('should handle DECSET/DECRST 47 (alt screen buffer)', () => { handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST'); diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 0f4da041..cc767a16 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -19,7 +19,7 @@ import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content import { CellData } from 'common/buffer/CellData'; import { AttributeData } from 'common/buffer/AttributeData'; import { IAttributeData, IDisposable } from 'common/Types'; -import { ICoreService, IBufferService, IOptionsService, ILogService } from 'common/services/Services'; +import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService } from 'common/services/Services'; import { ISelectionService } from 'browser/services/Services'; /** @@ -128,12 +128,13 @@ export class InputHandler extends Disposable implements IInputHandler { public get onScroll(): IEvent { return this._onScroll.event; } constructor( - protected _terminal: IInputHandlingTerminal, - private _bufferService: IBufferService, - private _coreService: ICoreService, - private _logService: ILogService, - private _optionsService: IOptionsService, - private _parser: IEscapeSequenceParser = new EscapeSequenceParser()) + protected _terminal: IInputHandlingTerminal, + private readonly _bufferService: IBufferService, + private readonly _coreService: ICoreService, + private readonly _dirtyRowService: IDirtyRowService, + private readonly _logService: ILogService, + private readonly _optionsService: IOptionsService, + private readonly _parser: IEscapeSequenceParser = new EscapeSequenceParser()) { super(); @@ -306,7 +307,6 @@ export class InputHandler extends Disposable implements IInputHandler { public dispose(): void { super.dispose(); - this._terminal = null; } // TODO: When InputHandler moves into common, browser dependencies need to move out @@ -315,11 +315,6 @@ export class InputHandler extends Disposable implements IInputHandler { } public parse(data: string): void { - // Ensure the terminal is not disposed - if (!this._terminal) { - return; - } - let buffer = this._bufferService.buffer; const cursorStartX = buffer.x; const cursorStartY = buffer.y; @@ -338,11 +333,6 @@ export class InputHandler extends Disposable implements IInputHandler { } public parseUtf8(data: Uint8Array): void { - // Ensure the terminal is not disposed - if (!this._terminal) { - return; - } - let buffer = this._bufferService.buffer; const cursorStartX = buffer.x; const cursorStartY = buffer.y; @@ -365,14 +355,14 @@ export class InputHandler extends Disposable implements IInputHandler { let chWidth: number; const buffer = this._bufferService.buffer; const charset = this._terminal.charset; - const screenReaderMode = this._terminal.options.screenReaderMode; + const screenReaderMode = this._optionsService.options.screenReaderMode; const cols = this._bufferService.cols; const wraparoundMode = this._terminal.wraparoundMode; const insertMode = this._terminal.insertMode; const curAttr = this._terminal.curAttrData; let bufferRow = buffer.lines.get(buffer.y + buffer.ybase); - this._terminal.updateRange(buffer.y); + this._dirtyRowService.markDirty(buffer.y); for (let pos = start; pos < end; ++pos) { code = data[pos]; @@ -481,7 +471,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._parser.precedingCodepoint = this._workCell.content; } } - this._terminal.updateRange(buffer.y); + this._dirtyRowService.markDirty(buffer.y); } /** @@ -514,7 +504,7 @@ export class InputHandler extends Disposable implements IInputHandler { // make buffer local for faster access const buffer = this._bufferService.buffer; - if (this._terminal.options.convertEol) { + if (this._optionsService.options.convertEol) { buffer.x = 0; } buffer.y++; @@ -559,7 +549,7 @@ export class InputHandler extends Disposable implements IInputHandler { } const originalX = this._bufferService.buffer.x; this._bufferService.buffer.x = this._bufferService.buffer.nextStop(); - if (this._terminal.options.screenReaderMode) { + if (this._optionsService.options.screenReaderMode) { this._terminal.onA11yTabEmitter.fire(this._bufferService.buffer.x - originalX); } } @@ -830,16 +820,16 @@ export class InputHandler extends Disposable implements IInputHandler { switch (params.params[0]) { case 0: j = this._bufferService.buffer.y; - this._terminal.updateRange(j); + this._dirtyRowService.markDirty(j); this._eraseInBufferLine(j++, this._bufferService.buffer.x, this._bufferService.cols, this._bufferService.buffer.x === 0); for (; j < this._bufferService.rows; j++) { this._resetBufferLine(j); } - this._terminal.updateRange(j); + this._dirtyRowService.markDirty(j); break; case 1: j = this._bufferService.buffer.y; - this._terminal.updateRange(j); + this._dirtyRowService.markDirty(j); // Deleted front part of line and everything before. This line will no longer be wrapped. this._eraseInBufferLine(j, 0, this._bufferService.buffer.x + 1, true); if (this._bufferService.buffer.x + 1 >= this._bufferService.cols) { @@ -849,15 +839,15 @@ export class InputHandler extends Disposable implements IInputHandler { while (j--) { this._resetBufferLine(j); } - this._terminal.updateRange(0); + this._dirtyRowService.markDirty(0); break; case 2: j = this._bufferService.rows; - this._terminal.updateRange(j - 1); + this._dirtyRowService.markDirty(j - 1); while (j--) { this._resetBufferLine(j); } - this._terminal.updateRange(0); + this._dirtyRowService.markDirty(0); break; case 3: // Clear scrollback (everything not in viewport) @@ -897,7 +887,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._eraseInBufferLine(this._bufferService.buffer.y, 0, this._bufferService.cols); break; } - this._terminal.updateRange(this._bufferService.buffer.y); + this._dirtyRowService.markDirty(this._bufferService.buffer.y); } /** @@ -926,9 +916,7 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.lines.splice(row, 0, buffer.getBlankLine(this._terminal.eraseAttrData())); } - // this.maxRange(); - this._terminal.updateRange(buffer.y); - this._terminal.updateRange(buffer.scrollBottom); + this._dirtyRowService.markRangeDirty(buffer.y, buffer.scrollBottom); buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only? } @@ -959,9 +947,7 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.lines.splice(j, 0, buffer.getBlankLine(this._terminal.eraseAttrData())); } - // this.maxRange(); - this._terminal.updateRange(buffer.y); - this._terminal.updateRange(buffer.scrollBottom); + this._dirtyRowService.markRangeDirty(buffer.y, buffer.scrollBottom); buffer.x = 0; // see https://vt100.net/docs/vt220-rm/chapter4.html - vt220 only? } @@ -978,7 +964,7 @@ export class InputHandler extends Disposable implements IInputHandler { params.params[0] || 1, this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData()) ); - this._terminal.updateRange(this._bufferService.buffer.y); + this._dirtyRowService.markDirty(this._bufferService.buffer.y); } } @@ -995,7 +981,7 @@ export class InputHandler extends Disposable implements IInputHandler { params.params[0] || 1, this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData()) ); - this._terminal.updateRange(this._bufferService.buffer.y); + this._dirtyRowService.markDirty(this._bufferService.buffer.y); } } @@ -1012,9 +998,7 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1); buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA)); } - // this.maxRange(); - this._terminal.updateRange(buffer.scrollTop); - this._terminal.updateRange(buffer.scrollBottom); + this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom); } /** @@ -1031,9 +1015,7 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1); buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA)); } - // this.maxRange(); - this._terminal.updateRange(buffer.scrollTop); - this._terminal.updateRange(buffer.scrollBottom); + this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom); } } @@ -1050,7 +1032,7 @@ export class InputHandler extends Disposable implements IInputHandler { this._bufferService.buffer.x + (params.params[0] || 1), this._bufferService.buffer.getNullCell(this._terminal.eraseAttrData()) ); - this._terminal.updateRange(this._bufferService.buffer.y); + this._dirtyRowService.markDirty(this._bufferService.buffer.y); } } @@ -1893,19 +1875,19 @@ export class InputHandler extends Disposable implements IInputHandler { switch (param) { case 1: case 2: - this._terminal.options.cursorStyle = 'block'; + this._optionsService.options.cursorStyle = 'block'; break; case 3: case 4: - this._terminal.options.cursorStyle = 'underline'; + this._optionsService.options.cursorStyle = 'underline'; break; case 5: case 6: - this._terminal.options.cursorStyle = 'bar'; + this._optionsService.options.cursorStyle = 'bar'; break; } const isBlinking = param % 2 === 1; - this._terminal.options.cursorBlink = isBlinking; + this._optionsService.options.cursorBlink = isBlinking; } } @@ -2097,8 +2079,7 @@ export class InputHandler extends Disposable implements IInputHandler { const scrollRegionHeight = buffer.scrollBottom - buffer.scrollTop; buffer.lines.shiftElements(buffer.y + buffer.ybase, scrollRegionHeight, 1); buffer.lines.set(buffer.y + buffer.ybase, buffer.getBlankLine(this._terminal.eraseAttrData())); - this._terminal.updateRange(buffer.scrollTop); - this._terminal.updateRange(buffer.scrollBottom); + this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom); } else { buffer.y--; this._restrictCursor(); // quickfix to not run out of bounds @@ -2152,8 +2133,7 @@ export class InputHandler extends Disposable implements IInputHandler { buffer.lines.get(row).fill(cell); buffer.lines.get(row).isWrapped = false; } - this._terminal.updateRange(0); - this._terminal.updateRange(this._bufferService.rows); + this._dirtyRowService.markAllDirty(); this._setCursor(0, 0); } } diff --git a/src/Terminal.ts b/src/Terminal.ts index e1d0a4d7..e23ea778 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -47,7 +47,7 @@ import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { applyWindowsMode } from './WindowsMode'; import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; -import { IOptionsService, IBufferService, ICoreService, ILogService } from 'common/services/Services'; +import { IOptionsService, IBufferService, ICoreService, ILogService, IDirtyRowService } from 'common/services/Services'; import { OptionsService } from 'common/services/OptionsService'; import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService } from 'browser/services/Services'; import { CharSizeService } from 'browser/services/CharSizeService'; @@ -60,6 +60,7 @@ import { IParams } from 'common/parser/Types'; import { CoreService } from 'common/services/CoreService'; import { LogService } from 'common/services/LogService'; import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport } from 'browser/Types'; +import { DirtyRowService } from 'common/services/DirtyRowService'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -111,6 +112,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp // common services private _bufferService: IBufferService; private _coreService: ICoreService; + private _dirtyRowService: IDirtyRowService; private _logService: ILogService; public optionsService: IOptionsService; @@ -148,8 +150,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp public urxvtMouse: boolean; // misc - private _refreshStart: number; - private _refreshEnd: number; public savedCols: number; public curAttrData: IAttributeData; @@ -243,6 +243,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._bufferService = new BufferService(this.optionsService); this._coreService = new CoreService(() => this.scrollToBottom(), this._bufferService, this.optionsService); this._coreService.onData(e => this._onData.fire(e)); + this._dirtyRowService = new DirtyRowService(this._bufferService); this._logService = new LogService(this.optionsService); this._setupOptionsListeners(); @@ -300,7 +301,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._userScrolling = false; // Register input handler and refire/handle events - this._inputHandler = new InputHandler(this, this._bufferService, this._coreService, this._logService, this.optionsService); + this._inputHandler = new InputHandler(this, this._bufferService, this._coreService, this._dirtyRowService, this._logService, this.optionsService); this._inputHandler.onCursorMove(() => this._onCursorMove.fire()); this._inputHandler.onLineFeed(() => this._onLineFeed.fire()); this.register(this._inputHandler); @@ -1136,8 +1137,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp } // Flag rows that need updating - this.updateRange(this.buffer.scrollTop); - this.updateRange(this.buffer.scrollBottom); + this._dirtyRowService.markRangeDirty(this.buffer.scrollTop, this.buffer.scrollBottom); this._onScroll.fire(this.buffer.ydisp); } @@ -1258,19 +1258,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._xoffSentToCatchUp = false; } - this._refreshStart = this.buffer.y; - this._refreshEnd = this.buffer.y; - - // HACK: Set the parser state based on it's state at the time of return. - // This works around the bug #662 which saw the parser state reset in the - // middle of parsing escape sequence in two chunks. For some reason the - // state of the parser resets to 0 after exiting parser.parse. This change - // just sets the state back based on the correct return statement. - this._inputHandler.parseUtf8(data); - this.updateRange(this.buffer.y); - this.refresh(this._refreshStart, this._refreshEnd); + this.refresh(this._dirtyRowService.start, this._dirtyRowService.end); if (Date.now() - startTime >= WRITE_TIMEOUT_MS) { break; @@ -1345,19 +1335,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._xoffSentToCatchUp = false; } - this._refreshStart = this.buffer.y; - this._refreshEnd = this.buffer.y; - - // HACK: Set the parser state based on it's state at the time of return. - // This works around the bug #662 which saw the parser state reset in the - // middle of parsing escape sequence in two chunks. For some reason the - // state of the parser resets to 0 after exiting parser.parse. This change - // just sets the state back based on the correct return statement. - this._inputHandler.parse(data); - this.updateRange(this.buffer.y); - this.refresh(this._refreshStart, this._refreshEnd); + this.refresh(this._dirtyRowService.start, this._dirtyRowService.end); if (Date.now() - startTime >= WRITE_TIMEOUT_MS) { break; @@ -1717,29 +1697,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._onResize.fire({ cols: x, rows: y }); } - /** - * Updates the range of rows to refresh - * @param y The number of rows to refresh next. - */ - public updateRange(y: number): void { - if (y < this._refreshStart) this._refreshStart = y; - if (y > this._refreshEnd) this._refreshEnd = y; - // if (y > this.refreshEnd) { - // this.refreshEnd = y; - // if (y > this.rows - 1) { - // this.refreshEnd = this.rows - 1; - // } - // } - } - - /** - * Set the range of refreshing to the maximum value - */ - public maxRange(): void { - this._refreshStart = 0; - this._refreshEnd = this.rows - 1; - } - /** * Clear the entire buffer, making the prompt line the new first line. */ diff --git a/src/Types.d.ts b/src/Types.d.ts index 124aa428..f96ba07d 100644 --- a/src/Types.d.ts +++ b/src/Types.d.ts @@ -55,7 +55,6 @@ export interface IInputHandlingTerminal { bell(): void; focus(): void; - updateRange(y: number): void; scroll(isWrapped?: boolean): void; setgLevel(g: number): void; eraseAttrData(): IAttributeData; diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index c786a06d..67584a36 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IPartialTerminalOptions } from 'common/services/Services'; +import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IPartialTerminalOptions, IDirtyRowService } from 'common/services/Services'; import { IEvent, EventEmitter } from 'common/EventEmitter'; import { clone } from 'common/Clone'; import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; @@ -36,6 +36,15 @@ export class MockCoreService implements ICoreService { triggerDataEvent(data: string, wasUserInput?: boolean): void {} } +export class MockDirtyRowService implements IDirtyRowService { + start: number = 0; + end: number = 0; + clearRange(): void {} + markDirty(y: number): void {} + markRangeDirty(y1: number, y2: number): void {} + markAllDirty(): void {} +} + export class MockLogService implements ILogService { debug(message: any, ...optionalParams: any[]): void {} info(message: any, ...optionalParams: any[]): void {} diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index 320aaeb8..b25b50a5 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -153,3 +153,8 @@ export interface IMarker extends IDisposable { export interface IDecPrivateModes { applicationCursorKeys: boolean; } + +export interface IRowRange { + start: number; + end: number; +} diff --git a/src/common/services/DirtyRowService.ts b/src/common/services/DirtyRowService.ts new file mode 100644 index 00000000..58f40dca --- /dev/null +++ b/src/common/services/DirtyRowService.ts @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IBufferService, IDirtyRowService } from 'common/services/Services'; + +export class DirtyRowService implements IDirtyRowService { + private _start!: number; + private _end!: number; + + public get start(): number { return this._start; } + public get end(): number { return this._end; } + + constructor( + private readonly _bufferService: IBufferService + ) { + this.clearRange(); + } + + public clearRange(): void { + this._start = this._bufferService.buffer.y; + this._end = this._bufferService.buffer.y; + } + + public markDirty(y: number): void { + if (y < this._start) { + this._start = y; + } else if (y > this._end) { + this._end = y; + } + } + + public markRangeDirty(y1: number, y2: number): void { + if (y1 > y2) { + const temp = y1; + y1 = y2; + y2 = temp; + } + if (y1 < this._start) { + this._start = y1; + } + if (y2 > this._end) { + this._end = y2; + } + } + + public markAllDirty(): void { + this.markRangeDirty(0, this._bufferService.rows - 1); + } +} diff --git a/src/common/services/Services.d.ts b/src/common/services/Services.d.ts index 9a98ca92..427728f1 100644 --- a/src/common/services/Services.d.ts +++ b/src/common/services/Services.d.ts @@ -38,6 +38,16 @@ export interface ICoreService { triggerDataEvent(data: string, wasUserInput?: boolean): void; } +export interface IDirtyRowService { + readonly start: number; + readonly end: number; + + clearRange(): void; + markDirty(y: number): void; + markRangeDirty(y1: number, y2: number): void; + markAllDirty(): void; +} + export interface ILogService { debug(message: any, ...optionalParams: any[]): void; info(message: any, ...optionalParams: any[]): void; diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts index 60bf50da..22b50349 100644 --- a/src/renderer/dom/DomRenderer.ts +++ b/src/renderer/dom/DomRenderer.ts @@ -21,9 +21,6 @@ const SELECTION_CLASS = 'xterm-selection'; let nextTerminalId = 1; -// TODO: Pull into an addon when TS composite projects allow easier sharing of code (not just -// interfaces) between core and addons - /** * A fallback renderer for when canvas is slow. This is not meant to be * particularly fast or feature complete, more just stable and usable for when From 21fbe2585887eb3d5794235c02aa33f4485778da Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 19:50:16 -0700 Subject: [PATCH 02/12] Dependency Injection prototype --- src/Terminal.ts | 15 +++- src/common/services/BufferService.ts | 2 +- src/common/services/CoreService.ts | 4 +- src/common/services/DirtyRowService.ts | 2 +- src/common/services/InstantiationService.ts | 81 +++++++++++++++++++ src/common/services/ServiceRegistry.ts | 43 ++++++++++ .../services/{Services.d.ts => Services.ts} | 15 ++++ src/common/tsconfig.json | 3 + src/tsconfig-library-base.json | 3 +- tslint.json | 7 +- 10 files changed, 160 insertions(+), 15 deletions(-) create mode 100644 src/common/services/InstantiationService.ts create mode 100644 src/common/services/ServiceRegistry.ts rename src/common/services/{Services.d.ts => Services.ts} (86%) diff --git a/src/Terminal.ts b/src/Terminal.ts index e23ea778..11ef2d6e 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -47,7 +47,7 @@ import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { applyWindowsMode } from './WindowsMode'; import { ColorManager } from 'browser/ColorManager'; import { RenderService } from 'browser/services/RenderService'; -import { IOptionsService, IBufferService, ICoreService, ILogService, IDirtyRowService } from 'common/services/Services'; +import { IOptionsService, IBufferService, ICoreService, ILogService, IDirtyRowService, IInstantiationService } from 'common/services/Services'; import { OptionsService } from 'common/services/OptionsService'; import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService } from 'browser/services/Services'; import { CharSizeService } from 'browser/services/CharSizeService'; @@ -61,6 +61,7 @@ import { CoreService } from 'common/services/CoreService'; import { LogService } from 'common/services/LogService'; import { ILinkifier, IMouseZoneManager, LinkMatcherHandler, ILinkMatcherOptions, IViewport } from 'browser/Types'; import { DirtyRowService } from 'common/services/DirtyRowService'; +import { InstantiationService } from 'common/services/InstantiationService'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -113,6 +114,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _bufferService: IBufferService; private _coreService: ICoreService; private _dirtyRowService: IDirtyRowService; + private _instantiationService: IInstantiationService; private _logService: ILogService; public optionsService: IOptionsService; @@ -239,11 +241,16 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp super(); // Setup and initialize common services + this._instantiationService = new InstantiationService(); this.optionsService = new OptionsService(options); - this._bufferService = new BufferService(this.optionsService); - this._coreService = new CoreService(() => this.scrollToBottom(), this._bufferService, this.optionsService); + this._instantiationService.setService(IOptionsService, this.optionsService); + this._bufferService = this._instantiationService.createInstance(BufferService); + this._instantiationService.setService(IBufferService, this._bufferService); + this._coreService = this._instantiationService.createInstance(CoreService, () => this.scrollToBottom()); + this._instantiationService.setService(ICoreService, this._coreService); this._coreService.onData(e => this._onData.fire(e)); - this._dirtyRowService = new DirtyRowService(this._bufferService); + this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService); + // this._instantiationService.setService(IDirtyRowService, this._dirtyRowService); this._logService = new LogService(this.optionsService); this._setupOptionsListeners(); diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index 6ff08061..e7dd1b64 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -18,7 +18,7 @@ export class BufferService implements IBufferService { public get buffer(): IBuffer { return this.buffers.active; } constructor( - private _optionsService: IOptionsService + @IOptionsService private _optionsService: IOptionsService ) { this.cols = Math.max(_optionsService.options.cols, MINIMUM_COLS); this.rows = Math.max(_optionsService.options.rows, MINIMUM_ROWS); diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index 295b5a08..da60e787 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -23,8 +23,8 @@ export class CoreService implements ICoreService { constructor( // TODO: Move this into a service private readonly _scrollToBottom: () => void, - private readonly _bufferService: IBufferService, - private readonly _optionsService: IOptionsService + @IBufferService private readonly _bufferService: IBufferService, + @IOptionsService private readonly _optionsService: IOptionsService ) { this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES); } diff --git a/src/common/services/DirtyRowService.ts b/src/common/services/DirtyRowService.ts index 58f40dca..b89fb918 100644 --- a/src/common/services/DirtyRowService.ts +++ b/src/common/services/DirtyRowService.ts @@ -13,7 +13,7 @@ export class DirtyRowService implements IDirtyRowService { public get end(): number { return this._end; } constructor( - private readonly _bufferService: IBufferService + @IBufferService private readonly _bufferService: IBufferService ) { this.clearRange(); } diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts new file mode 100644 index 00000000..fb846e7a --- /dev/null +++ b/src/common/services/InstantiationService.ts @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IInstantiationService, IServiceIdentifier } from 'common/services/Services'; +import { getServiceDependencies } from 'common/services/ServiceRegistry'; + +declare const console: any; + +export class ServiceCollection { + + private _entries = new Map, any>(); + + constructor(...entries: [IServiceIdentifier, any][]) { + for (const [id, service] of entries) { + this.set(id, service); + } + } + + set(id: IServiceIdentifier, instance: T): T { + const result = this._entries.get(id); + this._entries.set(id, instance); + return result; + } + + forEach(callback: (id: IServiceIdentifier, instance: any) => any): void { + this._entries.forEach((value, key) => callback(key, value)); + } + + has(id: IServiceIdentifier): boolean { + return this._entries.has(id); + } + + get(id: IServiceIdentifier): T { + return this._entries.get(id); + } +} + +export class InstantiationService implements IInstantiationService { + private readonly _services: ServiceCollection = new ServiceCollection(); + + constructor() { + this._services.set(IInstantiationService, this); + } + + public setService(id: IServiceIdentifier, instance: T): void { + this._services.set(id, instance); + } + + public createInstance(ctor: any, ...args: any[]): any { + const serviceDependencies = getServiceDependencies(ctor).sort((a, b) => a.index - b.index); + + let serviceArgs: any[] = []; + for (const dependency of serviceDependencies) { + let service = this._services.get(dependency.id); + if (!service) { + throw new Error(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id}.`); + } + serviceArgs.push(service); + } + + let firstServiceArgPos = serviceDependencies.length > 0 ? serviceDependencies[0].index : args.length; + + // check for argument mismatches, adjust static args if needed + if (args.length !== firstServiceArgPos) { + console.warn(`[createInstance] First service dependency of ${ctor.name} at position ${ + firstServiceArgPos + 1} conflicts with ${args.length} static arguments`); + + let delta = firstServiceArgPos - args.length; + if (delta > 0) { + args = args.concat(new Array(delta)); + } else { + args = args.slice(0, firstServiceArgPos); + } + } + console.log('args', args, 'serviceArgs', serviceArgs); + // now create the instance + return new ctor(...[...args, ...serviceArgs]); + } +} diff --git a/src/common/services/ServiceRegistry.ts b/src/common/services/ServiceRegistry.ts new file mode 100644 index 00000000..2d7ed811 --- /dev/null +++ b/src/common/services/ServiceRegistry.ts @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IServiceIdentifier } from 'common/services/Services'; + +const DI_TARGET = 'di$target'; +const DI_DEPENDENCIES = 'di$dependencies'; + +export const serviceRegistry: Map> = new Map(); + +export function getServiceDependencies(ctor: any): { id: IServiceIdentifier, index: number, optional: boolean }[] { + return ctor[DI_DEPENDENCIES] || []; +} + +export function createDecorator(id: string): IServiceIdentifier { + if (serviceRegistry.has(id)) { + return serviceRegistry.get(id)!; + } + + const decorator = function (target: Function, key: string, index: number): any { + if (arguments.length !== 3) { + throw new Error('@IServiceName-decorator can only be used to decorate a parameter'); + } + + storeServiceDependency(decorator, target, index); + }; + + decorator.toString = () => id; + + serviceRegistry.set(id, decorator); + return decorator; +} + +function storeServiceDependency(id: Function, target: Function, index: number): void { + if ((target as any)[DI_TARGET] === target) { + (target as any)[DI_DEPENDENCIES].push({ id, index }); + } else { + (target as any)[DI_DEPENDENCIES] = [{ id, index }]; + (target as any)[DI_TARGET] = target; + } +} diff --git a/src/common/services/Services.d.ts b/src/common/services/Services.ts similarity index 86% rename from src/common/services/Services.d.ts rename to src/common/services/Services.ts index 427728f1..837b9b80 100644 --- a/src/common/services/Services.d.ts +++ b/src/common/services/Services.ts @@ -6,7 +6,9 @@ import { IEvent } from 'common/EventEmitter'; import { IBuffer, IBufferSet } from 'common/buffer/Types'; import { IDecPrivateModes } from 'common/Types'; +import { createDecorator } from 'common/services/ServiceRegistry'; +export const IBufferService = createDecorator('BufferService'); export interface IBufferService { readonly cols: number; readonly rows: number; @@ -19,6 +21,7 @@ export interface IBufferService { reset(): void; } +export const ICoreService = createDecorator('CoreService'); export interface ICoreService { readonly decPrivateModes: IDecPrivateModes; @@ -48,6 +51,17 @@ export interface IDirtyRowService { markAllDirty(): void; } +export interface IServiceIdentifier { + (...args: any[]): void; + type: T; +} + +export const IInstantiationService = createDecorator('InstantiationService'); +export interface IInstantiationService { + setService(id: IServiceIdentifier, instance: T): void; + createInstance(ctor: any, ...rest: any[]): any; +} + export interface ILogService { debug(message: any, ...optionalParams: any[]): void; info(message: any, ...optionalParams: any[]): void; @@ -55,6 +69,7 @@ export interface ILogService { error(message: any, ...optionalParams: any[]): void; } +export const IOptionsService = createDecorator('OptionsService'); export interface IOptionsService { readonly options: ITerminalOptions; diff --git a/src/common/tsconfig.json b/src/common/tsconfig.json index dca04f9a..59050a0b 100644 --- a/src/common/tsconfig.json +++ b/src/common/tsconfig.json @@ -1,6 +1,9 @@ { "extends": "../tsconfig-library-base", "compilerOptions": { + "lib": [ + "es2015" + ], "outDir": "../../out", "types": [ "../../node_modules/@types/mocha" diff --git a/src/tsconfig-library-base.json b/src/tsconfig-library-base.json index 7a9eedf3..e08695d7 100644 --- a/src/tsconfig-library-base.json +++ b/src/tsconfig-library-base.json @@ -3,6 +3,7 @@ "compilerOptions": { "composite": true, "strict": true, - "declarationMap": true + "declarationMap": true, + "experimentalDecorators": true } } diff --git a/tslint.json b/tslint.json index a18111bf..43662039 100644 --- a/tslint.json +++ b/tslint.json @@ -71,12 +71,6 @@ "variable-declaration": "nospace" } ], - "variable-name": [ - true, - "ban-keywords", - "check-format", - "allow-leading-underscore" - ], "whitespace": [ true, "check-branch", @@ -99,6 +93,7 @@ {"type": "member", "modifiers": ["protected"], "format": "camelCase", "leadingUnderscore": "require"}, {"type": "member", "modifiers": ["private"], "format": "camelCase", "leadingUnderscore": "require"}, {"type": "variable", "modifiers": ["const"], "format": ["camelCase", "UPPER_CASE"]}, + {"type": "variable", "modifiers": ["const", "export"], "filter": "^I.+Service$", "format": "PascalCase", "prefix": "I"}, {"type": "interface", "prefix": "I"} ], "no-else-after-return": { From 8359605951524d93580e073e8e2cf397d0505193 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 19:54:01 -0700 Subject: [PATCH 03/12] Add license and credit to VS Code --- README.md | 2 ++ src/common/services/InstantiationService.ts | 6 ++++++ src/common/services/ServiceRegistry.ts | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/README.md b/README.md index d01a0f33..0b4c64b9 100644 --- a/README.md +++ b/README.md @@ -185,3 +185,5 @@ If you contribute code to this project, you are implicitly allowing your code to Copyright (c) 2017-2019, [The xterm.js authors](https://github.com/xtermjs/xterm.js/graphs/contributors) (MIT License)
Copyright (c) 2014-2017, SourceLair, Private Company ([www.sourcelair.com](https://www.sourcelair.com/home)) (MIT License)
Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) + +Some files in this code base are heavily influenced on implementations in [Visual Studio Code](https://github.com/Microsoft/vscode) (MIT License). diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts index fb846e7a..98c23b6e 100644 --- a/src/common/services/InstantiationService.ts +++ b/src/common/services/InstantiationService.ts @@ -1,7 +1,13 @@ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT + * + * This was heavily inspired from microsoft/vscode's dependency injection system (MIT). */ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ import { IInstantiationService, IServiceIdentifier } from 'common/services/Services'; import { getServiceDependencies } from 'common/services/ServiceRegistry'; diff --git a/src/common/services/ServiceRegistry.ts b/src/common/services/ServiceRegistry.ts index 2d7ed811..450af492 100644 --- a/src/common/services/ServiceRegistry.ts +++ b/src/common/services/ServiceRegistry.ts @@ -1,7 +1,13 @@ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT + * + * This was heavily inspired from microsoft/vscode's dependency injection system (MIT). */ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ import { IServiceIdentifier } from 'common/services/Services'; From 5cc8ad802c42876928cd345bfba0832fde759fba Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 22:02:13 -0700 Subject: [PATCH 04/12] Get type safety in DI, fix character joiner not using buffer service --- src/Terminal.ts | 7 +-- src/common/TestUtils.test.ts | 5 ++ src/common/services/BufferService.ts | 2 + src/common/services/CoreService.ts | 2 + src/common/services/DirtyRowService.ts | 2 + src/common/services/InstantiationService.ts | 14 +---- src/common/services/LogService.ts | 4 +- src/common/services/OptionsService.ts | 2 + src/common/services/Services.ts | 59 ++++++++++++++++++++- src/renderer/Renderer.ts | 8 +-- 10 files changed, 85 insertions(+), 20 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 11ef2d6e..5cda1d24 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -250,8 +250,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._instantiationService.setService(ICoreService, this._coreService); this._coreService.onData(e => this._onData.fire(e)); this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService); - // this._instantiationService.setService(IDirtyRowService, this._dirtyRowService); + this._instantiationService.setService(IDirtyRowService, this._dirtyRowService); this._logService = new LogService(this.optionsService); + this._instantiationService.setService(ILogService, this._logService); this._setupOptionsListeners(); this._setup(); @@ -684,8 +685,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp private _createRenderer(): IRenderer { switch (this.options.rendererType) { - case 'canvas': return new Renderer(this, this._colorManager.colors, this._charSizeService); break; - case 'dom': return new DomRenderer(this, this._colorManager.colors, this._charSizeService, this.optionsService); break; + case 'canvas': return new Renderer(this._colorManager.colors, this, this._bufferService, this._charSizeService); + case 'dom': return new DomRenderer(this, this._colorManager.colors, this._charSizeService, this.optionsService); default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`); } } diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 67584a36..60ae3225 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -12,6 +12,7 @@ import { BufferSet } from 'common/buffer/BufferSet'; import { IDecPrivateModes } from 'common/Types'; export class MockBufferService implements IBufferService { + _serviceBrand: any; public get buffer(): IBuffer { return this.buffers.active; } public buffers: IBufferSet = {} as any; constructor( @@ -29,6 +30,7 @@ export class MockBufferService implements IBufferService { } export class MockCoreService implements ICoreService { + _serviceBrand: any; decPrivateModes: IDecPrivateModes = {} as any; onData: IEvent = new EventEmitter().event; onUserInput: IEvent = new EventEmitter().event; @@ -37,6 +39,7 @@ export class MockCoreService implements ICoreService { } export class MockDirtyRowService implements IDirtyRowService { + _serviceBrand: any; start: number = 0; end: number = 0; clearRange(): void {} @@ -46,6 +49,7 @@ export class MockDirtyRowService implements IDirtyRowService { } export class MockLogService implements ILogService { + _serviceBrand: any; debug(message: any, ...optionalParams: any[]): void {} info(message: any, ...optionalParams: any[]): void {} warn(message: any, ...optionalParams: any[]): void {} @@ -53,6 +57,7 @@ export class MockLogService implements ILogService { } export class MockOptionsService implements IOptionsService { + _serviceBrand: any; options: ITerminalOptions = clone(DEFAULT_OPTIONS); onOptionChange: IEvent = new EventEmitter().event; constructor(testOptions?: IPartialTerminalOptions) { diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index e7dd1b64..130fbee2 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -11,6 +11,8 @@ export const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars export const MINIMUM_ROWS = 1; export class BufferService implements IBufferService { + _serviceBrand: any; + public cols: number; public rows: number; public buffers: IBufferSet; diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index da60e787..3887d95a 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -13,6 +13,8 @@ const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({ }); export class CoreService implements ICoreService { + _serviceBrand: any; + public decPrivateModes: IDecPrivateModes; private _onData = new EventEmitter(); diff --git a/src/common/services/DirtyRowService.ts b/src/common/services/DirtyRowService.ts index b89fb918..835d4069 100644 --- a/src/common/services/DirtyRowService.ts +++ b/src/common/services/DirtyRowService.ts @@ -6,6 +6,8 @@ import { IBufferService, IDirtyRowService } from 'common/services/Services'; export class DirtyRowService implements IDirtyRowService { + _serviceBrand: any; + private _start!: number; private _end!: number; diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts index 98c23b6e..037fbcfd 100644 --- a/src/common/services/InstantiationService.ts +++ b/src/common/services/InstantiationService.ts @@ -12,8 +12,6 @@ import { IInstantiationService, IServiceIdentifier } from 'common/services/Services'; import { getServiceDependencies } from 'common/services/ServiceRegistry'; -declare const console: any; - export class ServiceCollection { private _entries = new Map, any>(); @@ -70,17 +68,9 @@ export class InstantiationService implements IInstantiationService { // check for argument mismatches, adjust static args if needed if (args.length !== firstServiceArgPos) { - console.warn(`[createInstance] First service dependency of ${ctor.name} at position ${ - firstServiceArgPos + 1} conflicts with ${args.length} static arguments`); + throw new Error(`[createInstance] First service dependency of ${ctor.name} at position ${firstServiceArgPos + 1} conflicts with ${args.length} static arguments`); + } - let delta = firstServiceArgPos - args.length; - if (delta > 0) { - args = args.concat(new Array(delta)); - } else { - args = args.slice(0, firstServiceArgPos); - } - } - console.log('args', args, 'serviceArgs', serviceArgs); // now create the instance return new ctor(...[...args, ...serviceArgs]); } diff --git a/src/common/services/LogService.ts b/src/common/services/LogService.ts index f7480078..1e14f06c 100644 --- a/src/common/services/LogService.ts +++ b/src/common/services/LogService.ts @@ -37,10 +37,12 @@ const optionsKeyToLogLevel: { [key: string]: LogLevel } = { const LOG_PREFIX = 'xterm.js: '; export class LogService implements ILogService { + _serviceBrand: any; + private _logLevel!: LogLevel; constructor( - private readonly _optionsService: IOptionsService + @IOptionsService private readonly _optionsService: IOptionsService ) { this._updateLogLevel(); this._optionsService.onOptionChange(key => { diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 7396491e..4f534dc4 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -56,6 +56,8 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows']; export class OptionsService implements IOptionsService { + _serviceBrand: any; + public options: ITerminalOptions; private _onOptionChange = new EventEmitter(); diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 837b9b80..3433af28 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -10,6 +10,8 @@ import { createDecorator } from 'common/services/ServiceRegistry'; export const IBufferService = createDecorator('BufferService'); export interface IBufferService { + _serviceBrand: any; + readonly cols: number; readonly rows: number; readonly buffer: IBuffer; @@ -23,6 +25,8 @@ export interface IBufferService { export const ICoreService = createDecorator('CoreService'); export interface ICoreService { + _serviceBrand: any; + readonly decPrivateModes: IDecPrivateModes; readonly onData: IEvent; @@ -41,7 +45,10 @@ export interface ICoreService { triggerDataEvent(data: string, wasUserInput?: boolean): void; } +export const IDirtyRowService = createDecorator('DirtyRowService'); export interface IDirtyRowService { + _serviceBrand: any; + readonly start: number; readonly end: number; @@ -56,13 +63,61 @@ export interface IServiceIdentifier { type: T; } +export interface IConstructorSignature0 { + new(...services: { _serviceBrand: any; }[]): T; +} + +export interface IConstructorSignature1 { + new(first: A1, ...services: { _serviceBrand: any; }[]): T; +} + +export interface IConstructorSignature2 { + new(first: A1, second: A2, ...services: { _serviceBrand: any; }[]): T; +} + +export interface IConstructorSignature3 { + new(first: A1, second: A2, third: A3, ...services: { _serviceBrand: any; }[]): T; +} + +export interface IConstructorSignature4 { + new(first: A1, second: A2, third: A3, fourth: A4, ...services: { _serviceBrand: any; }[]): T; +} + +export interface IConstructorSignature5 { + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, ...services: { _serviceBrand: any; }[]): T; +} + +export interface IConstructorSignature6 { + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, ...services: { _serviceBrand: any; }[]): T; +} + +export interface IConstructorSignature7 { + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, ...services: { _serviceBrand: any; }[]): T; +} + +export interface IConstructorSignature8 { + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { _serviceBrand: any; }[]): T; +} + export const IInstantiationService = createDecorator('InstantiationService'); export interface IInstantiationService { setService(id: IServiceIdentifier, instance: T): void; - createInstance(ctor: any, ...rest: any[]): any; + + createInstance(ctor: IConstructorSignature0): T; + createInstance(ctor: IConstructorSignature1, first: A1): T; + createInstance(ctor: IConstructorSignature2, first: A1, second: A2): T; + createInstance(ctor: IConstructorSignature3, first: A1, second: A2, third: A3): T; + createInstance(ctor: IConstructorSignature4, first: A1, second: A2, third: A3, fourth: A4): T; + createInstance(ctor: IConstructorSignature5, first: A1, second: A2, third: A3, fourth: A4, fifth: A5): T; + createInstance(ctor: IConstructorSignature6, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6): T; + createInstance(ctor: IConstructorSignature7, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7): T; + createInstance(ctor: IConstructorSignature8, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8): T; } +export const ILogService = createDecorator('LogService'); export interface ILogService { + _serviceBrand: any; + debug(message: any, ...optionalParams: any[]): void; info(message: any, ...optionalParams: any[]): void; warn(message: any, ...optionalParams: any[]): void; @@ -71,6 +126,8 @@ export interface ILogService { export const IOptionsService = createDecorator('OptionsService'); export interface IOptionsService { + _serviceBrand: any; + readonly options: ITerminalOptions; readonly onOptionChange: IEvent; diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index a6e8fca7..80d17cde 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -14,6 +14,7 @@ import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistr import { Disposable } from 'common/Lifecycle'; import { IColorSet } from 'browser/Types'; import { ICharSizeService } from 'browser/services/Services'; +import { IBufferService } from '../../out/common/services/Services'; export class Renderer extends Disposable implements IRenderer { private _renderLayers: IRenderLayer[]; @@ -23,13 +24,14 @@ export class Renderer extends Disposable implements IRenderer { public dimensions: IRenderDimensions; constructor( - private _terminal: ITerminal, private _colors: IColorSet, - private _charSizeService: ICharSizeService + private readonly _terminal: ITerminal, + readonly _bufferService: IBufferService, + private readonly _charSizeService: ICharSizeService ) { super(); const allowTransparency = this._terminal.options.allowTransparency; - this._characterJoinerRegistry = new CharacterJoinerRegistry(_terminal); + this._characterJoinerRegistry = new CharacterJoinerRegistry(this._bufferService); this._renderLayers = [ new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency), From 88c88f11418dccd77a07321182bea7fa694f5d06 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 22:26:13 -0700 Subject: [PATCH 05/12] Adopt DI in browser services --- src/Terminal.ts | 22 +++++++++++-------- src/browser/TestUtils.test.ts | 2 ++ src/browser/services/CharSizeService.ts | 8 ++++--- src/browser/services/MouseService.ts | 6 +++-- src/browser/services/RenderService.ts | 8 ++++--- src/browser/services/SelectionService.ts | 12 +++++----- .../services/{Services.d.ts => Services.ts} | 16 ++++++++++++++ src/browser/services/SoundService.ts | 4 +++- 8 files changed, 55 insertions(+), 23 deletions(-) rename src/browser/services/{Services.d.ts => Services.ts} (82%) diff --git a/src/Terminal.ts b/src/Terminal.ts index 5cda1d24..08783230 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -251,7 +251,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._coreService.onData(e => this._onData.fire(e)); this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService); this._instantiationService.setService(IDirtyRowService, this._dirtyRowService); - this._logService = new LogService(this.optionsService); + this._logService = this._instantiationService.createInstance(LogService); this._instantiationService.setService(ILogService, this._logService); this._setupOptionsListeners(); @@ -585,7 +585,8 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this.register(addDisposableDomListener(this.textarea, 'blur', () => this._onTextAreaBlur())); this._helperContainer.appendChild(this.textarea); - this._charSizeService = new CharSizeService(this._document, this._helperContainer, this.optionsService); + this._charSizeService = this._instantiationService.createInstance(CharSizeService, this._document, this._helperContainer); + this._instantiationService.setService(ICharSizeService, this._charSizeService); this._compositionView = document.createElement('div'); this._compositionView.classList.add('composition-view'); @@ -601,12 +602,15 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._colorManager.setTheme(this._theme); const renderer = this._createRenderer(); - this._renderService = new RenderService(renderer, this.rows, this.screenElement, this.optionsService, this._charSizeService); + this._renderService = this._instantiationService.createInstance(RenderService, renderer, this.rows, this.screenElement); + this._instantiationService.setService(IRenderService, this._renderService); this._renderService.onRender(e => this._onRender.fire(e)); this.onResize(e => this._renderService.resize(e.cols, e.rows)); - this._soundService = new SoundService(this.optionsService); - this._mouseService = new MouseService(this._renderService, this._charSizeService); + this._soundService = this._instantiationService.createInstance(SoundService); + this._instantiationService.setService(ISoundService, this._soundService); + this._mouseService = this._instantiationService.createInstance(MouseService); + this._instantiationService.setService(IMouseService, this._mouseService); this.viewport = new Viewport( (amount: number, suppressEvent: boolean) => this.scrollLines(amount, suppressEvent), @@ -625,11 +629,11 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this.register(this.onFocus(() => this._renderService.onFocus())); this.register(this._renderService.onDimensionsChange(() => this.viewport.syncScrollArea())); - this._selectionService = new SelectionService( + this._selectionService = this._instantiationService.createInstance(SelectionService, (amount: number, suppressEvent: boolean) => this.scrollLines(amount, suppressEvent), - this.element, this.screenElement, this._charSizeService, this._bufferService, this._coreService, - this._mouseService, this.optionsService - ); + this.element, + this.screenElement); + this._instantiationService.setService(ISelectionService, this._selectionService); this.register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire())); this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService.onMouseDown(e))); this.register(this._selectionService.onRedrawRequest(e => this._renderService.onSelectionChanged(e.start, e.end, e.columnSelectMode))); diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index d89dc391..70624e48 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -7,6 +7,7 @@ import { IEvent, EventEmitter } from 'common/EventEmitter'; import { ICharSizeService, IMouseService } from 'browser/services/Services'; export class MockCharSizeService implements ICharSizeService { + _serviceBrand: any; get hasValidSize(): boolean { return this.width > 0 && this.height > 0; } onCharSizeChange: IEvent = new EventEmitter().event; constructor(public width: number, public height: number) {} @@ -14,6 +15,7 @@ export class MockCharSizeService implements ICharSizeService { } export class MockMouseService implements IMouseService { + _serviceBrand: any; public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined { throw new Error('Not implemented'); } diff --git a/src/browser/services/CharSizeService.ts b/src/browser/services/CharSizeService.ts index 42920bfa..6f381c69 100644 --- a/src/browser/services/CharSizeService.ts +++ b/src/browser/services/CharSizeService.ts @@ -8,6 +8,8 @@ import { IEvent, EventEmitter } from 'common/EventEmitter'; import { ICharSizeService } from 'browser/services/Services'; export class CharSizeService implements ICharSizeService { + _serviceBrand: any; + public width: number = 0; public height: number = 0; private _measureStrategy: IMeasureStrategy; @@ -18,9 +20,9 @@ export class CharSizeService implements ICharSizeService { public get onCharSizeChange(): IEvent { return this._onCharSizeChange.event; } constructor( - document: Document, - parentElement: HTMLElement, - private _optionsService: IOptionsService + readonly document: Document, + readonly parentElement: HTMLElement, + @IOptionsService private readonly _optionsService: IOptionsService ) { this._measureStrategy = new DomMeasureStrategy(document, parentElement, this._optionsService); } diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index 76968698..306cde59 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -7,9 +7,11 @@ import { ICharSizeService, IRenderService, IMouseService } from './Services'; import { getCoords, getRawByteCoords } from 'browser/input/Mouse'; export class MouseService implements IMouseService { + _serviceBrand: any; + constructor( - private readonly _renderService: IRenderService, - private readonly _charSizeService: ICharSizeService + @IRenderService private readonly _renderService: IRenderService, + @ICharSizeService private readonly _charSizeService: ICharSizeService ) { } diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index b41f1ebc..72ce77a8 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -14,6 +14,8 @@ import { IOptionsService } from 'common/services/Services'; import { ICharSizeService, IRenderService } from 'browser/services/Services'; export class RenderService extends Disposable implements IRenderService { + _serviceBrand: any; + private _renderDebouncer: RenderDebouncer; private _screenDprMonitor: ScreenDprMonitor; @@ -34,9 +36,9 @@ export class RenderService extends Disposable implements IRenderService { constructor( private _renderer: IRenderer, private _rowCount: number, - screenElement: HTMLElement, - optionsService: IOptionsService, - charSizeService: ICharSizeService + readonly screenElement: HTMLElement, + @IOptionsService readonly optionsService: IOptionsService, + @ICharSizeService readonly charSizeService: ICharSizeService ) { super(); this._renderDebouncer = new RenderDebouncer((start, end) => this._renderRows(start, end)); diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 6a9498a1..dd7fe48e 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -67,6 +67,8 @@ export const enum SelectionMode { * when the selection is ready to be redrawn (on an animation frame). */ export class SelectionService implements ISelectionService { + _serviceBrand: any; + protected _model: SelectionModel; /** @@ -114,11 +116,11 @@ export class SelectionService implements ISelectionService { private readonly _scrollLines: (amount: number, suppressEvent: boolean) => void, private readonly _element: HTMLElement, private readonly _screenElement: HTMLElement, - private readonly _charSizeService: ICharSizeService, - private readonly _bufferService: IBufferService, - private readonly _coreService: ICoreService, - private readonly _mouseService: IMouseService, - private readonly _optionsService: IOptionsService + @ICharSizeService private readonly _charSizeService: ICharSizeService, + @IBufferService private readonly _bufferService: IBufferService, + @ICoreService private readonly _coreService: ICoreService, + @IMouseService private readonly _mouseService: IMouseService, + @IOptionsService private readonly _optionsService: IOptionsService ) { // Init listeners this._mouseMoveListener = event => this._onMouseMove(event); diff --git a/src/browser/services/Services.d.ts b/src/browser/services/Services.ts similarity index 82% rename from src/browser/services/Services.d.ts rename to src/browser/services/Services.ts index 603d4109..8c626e18 100644 --- a/src/browser/services/Services.d.ts +++ b/src/browser/services/Services.ts @@ -7,8 +7,12 @@ import { IEvent } from 'common/EventEmitter'; import { IRenderDimensions, IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types'; import { IColorSet } from 'browser/Types'; import { ISelectionRedrawRequestEvent } from 'browser/selection/Types'; +import { createDecorator } from 'common/services/ServiceRegistry'; +export const ICharSizeService = createDecorator('CharSizeService'); export interface ICharSizeService { + _serviceBrand: any; + readonly width: number; readonly height: number; readonly hasValidSize: boolean; @@ -18,12 +22,18 @@ export interface ICharSizeService { measure(): void; } +export const IMouseService = createDecorator('MouseService'); export interface IMouseService { + _serviceBrand: any; + getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined; getRawByteCoords(event: MouseEvent, element: HTMLElement, colCount: number, rowCount: number): { x: number, y: number } | undefined; } +export const IRenderService = createDecorator('RenderService'); export interface IRenderService { + _serviceBrand: any; + onDimensionsChange: IEvent; onRender: IEvent<{ start: number, end: number }>; onRefreshRequest: IEvent<{ start: number, end: number }>; @@ -48,7 +58,10 @@ export interface IRenderService { deregisterCharacterJoiner(joinerId: number): boolean; } +export const ISelectionService = createDecorator('SelectionService'); export interface ISelectionService { + _serviceBrand: any; + readonly selectionText: string; readonly hasSelection: boolean; readonly selectionStart: [number, number] | undefined; @@ -73,6 +86,9 @@ export interface ISelectionService { onMouseDown(event: MouseEvent): void; } +export const ISoundService = createDecorator('SoundService'); export interface ISoundService { + _serviceBrand: any; + playBellSound(): void; } diff --git a/src/browser/services/SoundService.ts b/src/browser/services/SoundService.ts index 31380031..89353f45 100644 --- a/src/browser/services/SoundService.ts +++ b/src/browser/services/SoundService.ts @@ -7,6 +7,8 @@ import { IOptionsService } from 'common/services/Services'; import { ISoundService } from 'browser/services/Services'; export class SoundService implements ISoundService { + _serviceBrand: any; + private static _audioContext: AudioContext; static get audioContext(): AudioContext | null { @@ -22,7 +24,7 @@ export class SoundService implements ISoundService { } constructor( - private _optionsService: IOptionsService + @IOptionsService private _optionsService: IOptionsService ) { } From 9315b5089710d557f690ae982d9662d5105f55c8 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 22:56:54 -0700 Subject: [PATCH 06/12] Use createInstance on service hungry objects --- src/Terminal.ts | 9 +++------ src/browser/MouseZoneManager.ts | 6 +++--- src/browser/Viewport.ts | 6 +++--- src/browser/input/CompositionHelper.ts | 8 ++++---- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/Terminal.ts b/src/Terminal.ts index 08783230..aee393b3 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -612,13 +612,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._mouseService = this._instantiationService.createInstance(MouseService); this._instantiationService.setService(IMouseService, this._mouseService); - this.viewport = new Viewport( + this.viewport = this._instantiationService.createInstance(Viewport, (amount: number, suppressEvent: boolean) => this.scrollLines(amount, suppressEvent), this._viewportElement, - this._viewportScrollArea, - this._bufferService, - this._charSizeService, - this._renderService + this._viewportScrollArea ); this.viewport.onThemeChange(this._colorManager.colors); this.register(this.viewport); @@ -651,7 +648,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp })); this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this._selectionService.refresh())); - this._mouseZoneManager = new MouseZoneManager(this.element, this.screenElement, this._bufferService, this._mouseService, this._selectionService); + this._mouseZoneManager = this._instantiationService.createInstance(MouseZoneManager, this.element, this.screenElement); this.register(this._mouseZoneManager); this.register(this.onScroll(() => this._mouseZoneManager.clearAll())); this.linkifier.attachToDom(this.element, this._mouseZoneManager); diff --git a/src/browser/MouseZoneManager.ts b/src/browser/MouseZoneManager.ts index 589428f8..7eb7c5f8 100644 --- a/src/browser/MouseZoneManager.ts +++ b/src/browser/MouseZoneManager.ts @@ -35,9 +35,9 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { constructor( private readonly _element: HTMLElement, private readonly _screenElement: HTMLElement, - private readonly _bufferService: IBufferService, - private readonly _mouseService: IMouseService, - private readonly _selectionService: ISelectionService + @IBufferService private readonly _bufferService: IBufferService, + @IMouseService private readonly _mouseService: IMouseService, + @ISelectionService private readonly _selectionService: ISelectionService ) { super(); diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index 270d4c67..9625588d 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -36,9 +36,9 @@ export class Viewport extends Disposable implements IViewport { private readonly _scrollLines: (amount: number, suppressEvent: boolean) => void, private readonly _viewportElement: HTMLElement, private readonly _scrollArea: HTMLElement, - private readonly _bufferService: IBufferService, - private readonly _charSizeService: ICharSizeService, - private readonly _renderService: IRenderService + @IBufferService private readonly _bufferService: IBufferService, + @ICharSizeService private readonly _charSizeService: ICharSizeService, + @IRenderService private readonly _renderService: IRenderService ) { super(); diff --git a/src/browser/input/CompositionHelper.ts b/src/browser/input/CompositionHelper.ts index a8c8bb14..e6994ea3 100644 --- a/src/browser/input/CompositionHelper.ts +++ b/src/browser/input/CompositionHelper.ts @@ -37,10 +37,10 @@ export class CompositionHelper { constructor( private readonly _textarea: HTMLTextAreaElement, private readonly _compositionView: HTMLElement, - private readonly _bufferService: IBufferService, - private readonly _optionsService: IOptionsService, - private readonly _charSizeService: ICharSizeService, - private readonly _coreService: ICoreService + @IBufferService private readonly _bufferService: IBufferService, + @IOptionsService private readonly _optionsService: IOptionsService, + @ICharSizeService private readonly _charSizeService: ICharSizeService, + @ICoreService private readonly _coreService: ICoreService ) { this._isComposing = false; this._isSendingComposition = false; From 358a707835a4639fef4dfa94d2801fa9912e06f9 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 23:44:08 -0700 Subject: [PATCH 07/12] Fix most lint and tests --- src/InputHandler.test.ts | 46 +++++++++++---------- src/Terminal.ts | 2 +- src/common/services/InstantiationService.ts | 32 +++++++------- src/common/services/Services.ts | 38 ++++++++--------- src/renderer/Renderer.ts | 4 +- tslint.json | 2 + 6 files changed, 64 insertions(+), 60 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index 558cfe7e..d4cebac1 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -15,6 +15,8 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { Params } from 'common/parser/Params'; import { MockCoreService, MockBufferService, MockDirtyRowService, MockOptionsService, MockLogService } from 'common/TestUtils.test'; import { IBufferService } from 'common/services/Services'; +import { DEFAULT_OPTIONS } from '../out/common/services/OptionsService'; +import { clone } from '../out/common/Clone'; function getCursor(term: TestTerminal): number[] { return [ @@ -49,43 +51,43 @@ describe('InputHandler', () => { }); describe('setCursorStyle', () => { it('should call Terminal.setOption with correct params', () => { - const terminal = new MockInputHandlingTerminal(); - const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService()); + const optionsService = new MockOptionsService(); + const inputHandler = new InputHandler(new MockInputHandlingTerminal(), new MockBufferService(80, 30), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), optionsService); const collect = ' '; inputHandler.setCursorStyle(Params.fromArray([0]), collect); - assert.equal(terminal.options['cursorStyle'], 'block'); - assert.equal(terminal.options['cursorBlink'], true); + assert.equal(optionsService.options['cursorStyle'], 'block'); + assert.equal(optionsService.options['cursorBlink'], true); - terminal.options = {}; + optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([1]), collect); - assert.equal(terminal.options['cursorStyle'], 'block'); - assert.equal(terminal.options['cursorBlink'], true); + assert.equal(optionsService.options['cursorStyle'], 'block'); + assert.equal(optionsService.options['cursorBlink'], true); - terminal.options = {}; + optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([2]), collect); - assert.equal(terminal.options['cursorStyle'], 'block'); - assert.equal(terminal.options['cursorBlink'], false); + assert.equal(optionsService.options['cursorStyle'], 'block'); + assert.equal(optionsService.options['cursorBlink'], false); - terminal.options = {}; + optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([3]), collect); - assert.equal(terminal.options['cursorStyle'], 'underline'); - assert.equal(terminal.options['cursorBlink'], true); + assert.equal(optionsService.options['cursorStyle'], 'underline'); + assert.equal(optionsService.options['cursorBlink'], true); - terminal.options = {}; + optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([4]), collect); - assert.equal(terminal.options['cursorStyle'], 'underline'); - assert.equal(terminal.options['cursorBlink'], false); + assert.equal(optionsService.options['cursorStyle'], 'underline'); + assert.equal(optionsService.options['cursorBlink'], false); - terminal.options = {}; + optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([5]), collect); - assert.equal(terminal.options['cursorStyle'], 'bar'); - assert.equal(terminal.options['cursorBlink'], true); + assert.equal(optionsService.options['cursorStyle'], 'bar'); + assert.equal(optionsService.options['cursorBlink'], true); - terminal.options = {}; + optionsService.options = clone(DEFAULT_OPTIONS); inputHandler.setCursorStyle(Params.fromArray([6]), collect); - assert.equal(terminal.options['cursorStyle'], 'bar'); - assert.equal(terminal.options['cursorBlink'], false); + assert.equal(optionsService.options['cursorStyle'], 'bar'); + assert.equal(optionsService.options['cursorBlink'], false); }); }); describe('setMode', () => { diff --git a/src/Terminal.ts b/src/Terminal.ts index aee393b3..8760a4dd 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -590,7 +590,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp this._compositionView = document.createElement('div'); this._compositionView.classList.add('composition-view'); - this._compositionHelper = new CompositionHelper(this.textarea, this._compositionView, this._bufferService, this.optionsService, this._charSizeService, this._coreService); + this._compositionHelper = this._instantiationService.createInstance(CompositionHelper, this.textarea, this._compositionView); this._helperContainer.appendChild(this._compositionView); // Performance: Add viewport and helper elements from the fragment diff --git a/src/common/services/InstantiationService.ts b/src/common/services/InstantiationService.ts index 037fbcfd..abce8ac5 100644 --- a/src/common/services/InstantiationService.ts +++ b/src/common/services/InstantiationService.ts @@ -55,23 +55,23 @@ export class InstantiationService implements IInstantiationService { public createInstance(ctor: any, ...args: any[]): any { const serviceDependencies = getServiceDependencies(ctor).sort((a, b) => a.index - b.index); - let serviceArgs: any[] = []; - for (const dependency of serviceDependencies) { - let service = this._services.get(dependency.id); - if (!service) { - throw new Error(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id}.`); - } - serviceArgs.push(service); - } - - let firstServiceArgPos = serviceDependencies.length > 0 ? serviceDependencies[0].index : args.length; - - // check for argument mismatches, adjust static args if needed - if (args.length !== firstServiceArgPos) { - throw new Error(`[createInstance] First service dependency of ${ctor.name} at position ${firstServiceArgPos + 1} conflicts with ${args.length} static arguments`); + const serviceArgs: any[] = []; + for (const dependency of serviceDependencies) { + const service = this._services.get(dependency.id); + if (!service) { + throw new Error(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id}.`); + } + serviceArgs.push(service); } - // now create the instance - return new ctor(...[...args, ...serviceArgs]); + const firstServiceArgPos = serviceDependencies.length > 0 ? serviceDependencies[0].index : args.length; + + // check for argument mismatches, adjust static args if needed + if (args.length !== firstServiceArgPos) { + throw new Error(`[createInstance] First service dependency of ${ctor.name} at position ${firstServiceArgPos + 1} conflicts with ${args.length} static arguments`); + } + + // now create the instance + return new ctor(...[...args, ...serviceArgs]); } } diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 3433af28..4ff0bc0c 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -42,7 +42,7 @@ export interface ICoreService { * - Scroll to the bottom of the buffer.s * - Fire the `onUserInput` event (so selection can be cleared). */ - triggerDataEvent(data: string, wasUserInput?: boolean): void; + triggerDataEvent(data: string, wasUserInput?: boolean): void; } export const IDirtyRowService = createDecorator('DirtyRowService'); @@ -64,54 +64,54 @@ export interface IServiceIdentifier { } export interface IConstructorSignature0 { - new(...services: { _serviceBrand: any; }[]): T; + new(...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature1 { - new(first: A1, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, ...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature2 { - new(first: A1, second: A2, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, ...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature3 { - new(first: A1, second: A2, third: A3, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, third: A3, ...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature4 { - new(first: A1, second: A2, third: A3, fourth: A4, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, ...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature5 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, ...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature6 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, ...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature7 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, ...services: { _serviceBrand: any; }[]): T; } export interface IConstructorSignature8 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { _serviceBrand: any; }[]): T; } export const IInstantiationService = createDecorator('InstantiationService'); export interface IInstantiationService { setService(id: IServiceIdentifier, instance: T): void; - createInstance(ctor: IConstructorSignature0): T; - createInstance(ctor: IConstructorSignature1, first: A1): T; - createInstance(ctor: IConstructorSignature2, first: A1, second: A2): T; - createInstance(ctor: IConstructorSignature3, first: A1, second: A2, third: A3): T; - createInstance(ctor: IConstructorSignature4, first: A1, second: A2, third: A3, fourth: A4): T; - createInstance(ctor: IConstructorSignature5, first: A1, second: A2, third: A3, fourth: A4, fifth: A5): T; - createInstance(ctor: IConstructorSignature6, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6): T; - createInstance(ctor: IConstructorSignature7, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7): T; - createInstance(ctor: IConstructorSignature8, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8): T; + createInstance(ctor: IConstructorSignature0): T; + createInstance(ctor: IConstructorSignature1, first: A1): T; + createInstance(ctor: IConstructorSignature2, first: A1, second: A2): T; + createInstance(ctor: IConstructorSignature3, first: A1, second: A2, third: A3): T; + createInstance(ctor: IConstructorSignature4, first: A1, second: A2, third: A3, fourth: A4): T; + createInstance(ctor: IConstructorSignature5, first: A1, second: A2, third: A3, fourth: A4, fifth: A5): T; + createInstance(ctor: IConstructorSignature6, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6): T; + createInstance(ctor: IConstructorSignature7, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7): T; + createInstance(ctor: IConstructorSignature8, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8): T; } export const ILogService = createDecorator('LogService'); diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 80d17cde..574d107a 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -26,12 +26,12 @@ export class Renderer extends Disposable implements IRenderer { constructor( private _colors: IColorSet, private readonly _terminal: ITerminal, - readonly _bufferService: IBufferService, + readonly bufferService: IBufferService, private readonly _charSizeService: ICharSizeService ) { super(); const allowTransparency = this._terminal.options.allowTransparency; - this._characterJoinerRegistry = new CharacterJoinerRegistry(this._bufferService); + this._characterJoinerRegistry = new CharacterJoinerRegistry(bufferService); this._renderLayers = [ new TextRenderLayer(this._terminal.screenElement, 0, this._colors, this._characterJoinerRegistry, allowTransparency), diff --git a/tslint.json b/tslint.json index 43662039..4445e97c 100644 --- a/tslint.json +++ b/tslint.json @@ -94,6 +94,8 @@ {"type": "member", "modifiers": ["private"], "format": "camelCase", "leadingUnderscore": "require"}, {"type": "variable", "modifiers": ["const"], "format": ["camelCase", "UPPER_CASE"]}, {"type": "variable", "modifiers": ["const", "export"], "filter": "^I.+Service$", "format": "PascalCase", "prefix": "I"}, + {"type": "member", "filter": "^_serviceBrand$", "leadingUnderscore": "require"}, + {"type": "property", "filter": "^_serviceBrand$", "leadingUnderscore": "require"}, {"type": "interface", "prefix": "I"} ], "no-else-after-return": { From a0e2c9b270a9c6aef7a9d4f875da382de0a62f10 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 23:45:25 -0700 Subject: [PATCH 08/12] Fix naming issue with serviceBrand --- src/browser/TestUtils.test.ts | 4 ++-- src/browser/services/CharSizeService.ts | 2 +- src/browser/services/MouseService.ts | 2 +- src/browser/services/RenderService.ts | 2 +- src/browser/services/SelectionService.ts | 2 +- src/browser/services/Services.ts | 10 ++++----- src/browser/services/SoundService.ts | 2 +- src/common/TestUtils.test.ts | 10 ++++----- src/common/services/BufferService.ts | 2 +- src/common/services/CoreService.ts | 2 +- src/common/services/DirtyRowService.ts | 2 +- src/common/services/LogService.ts | 2 +- src/common/services/OptionsService.ts | 2 +- src/common/services/Services.ts | 28 ++++++++++++------------ tslint.json | 2 -- 15 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index 70624e48..60861112 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -7,7 +7,7 @@ import { IEvent, EventEmitter } from 'common/EventEmitter'; import { ICharSizeService, IMouseService } from 'browser/services/Services'; export class MockCharSizeService implements ICharSizeService { - _serviceBrand: any; + serviceBrand: any; get hasValidSize(): boolean { return this.width > 0 && this.height > 0; } onCharSizeChange: IEvent = new EventEmitter().event; constructor(public width: number, public height: number) {} @@ -15,7 +15,7 @@ export class MockCharSizeService implements ICharSizeService { } export class MockMouseService implements IMouseService { - _serviceBrand: any; + serviceBrand: any; public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined { throw new Error('Not implemented'); } diff --git a/src/browser/services/CharSizeService.ts b/src/browser/services/CharSizeService.ts index 6f381c69..0749a420 100644 --- a/src/browser/services/CharSizeService.ts +++ b/src/browser/services/CharSizeService.ts @@ -8,7 +8,7 @@ import { IEvent, EventEmitter } from 'common/EventEmitter'; import { ICharSizeService } from 'browser/services/Services'; export class CharSizeService implements ICharSizeService { - _serviceBrand: any; + serviceBrand: any; public width: number = 0; public height: number = 0; diff --git a/src/browser/services/MouseService.ts b/src/browser/services/MouseService.ts index 306cde59..b0f8c358 100644 --- a/src/browser/services/MouseService.ts +++ b/src/browser/services/MouseService.ts @@ -7,7 +7,7 @@ import { ICharSizeService, IRenderService, IMouseService } from './Services'; import { getCoords, getRawByteCoords } from 'browser/input/Mouse'; export class MouseService implements IMouseService { - _serviceBrand: any; + serviceBrand: any; constructor( @IRenderService private readonly _renderService: IRenderService, diff --git a/src/browser/services/RenderService.ts b/src/browser/services/RenderService.ts index 72ce77a8..ecf20e28 100644 --- a/src/browser/services/RenderService.ts +++ b/src/browser/services/RenderService.ts @@ -14,7 +14,7 @@ import { IOptionsService } from 'common/services/Services'; import { ICharSizeService, IRenderService } from 'browser/services/Services'; export class RenderService extends Disposable implements IRenderService { - _serviceBrand: any; + serviceBrand: any; private _renderDebouncer: RenderDebouncer; private _screenDprMonitor: ScreenDprMonitor; diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index dd7fe48e..e4c703eb 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -67,7 +67,7 @@ export const enum SelectionMode { * when the selection is ready to be redrawn (on an animation frame). */ export class SelectionService implements ISelectionService { - _serviceBrand: any; + serviceBrand: any; protected _model: SelectionModel; diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts index 8c626e18..863f3a9e 100644 --- a/src/browser/services/Services.ts +++ b/src/browser/services/Services.ts @@ -11,7 +11,7 @@ import { createDecorator } from 'common/services/ServiceRegistry'; export const ICharSizeService = createDecorator('CharSizeService'); export interface ICharSizeService { - _serviceBrand: any; + serviceBrand: any; readonly width: number; readonly height: number; @@ -24,7 +24,7 @@ export interface ICharSizeService { export const IMouseService = createDecorator('MouseService'); export interface IMouseService { - _serviceBrand: any; + serviceBrand: any; getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined; getRawByteCoords(event: MouseEvent, element: HTMLElement, colCount: number, rowCount: number): { x: number, y: number } | undefined; @@ -32,7 +32,7 @@ export interface IMouseService { export const IRenderService = createDecorator('RenderService'); export interface IRenderService { - _serviceBrand: any; + serviceBrand: any; onDimensionsChange: IEvent; onRender: IEvent<{ start: number, end: number }>; @@ -60,7 +60,7 @@ export interface IRenderService { export const ISelectionService = createDecorator('SelectionService'); export interface ISelectionService { - _serviceBrand: any; + serviceBrand: any; readonly selectionText: string; readonly hasSelection: boolean; @@ -88,7 +88,7 @@ export interface ISelectionService { export const ISoundService = createDecorator('SoundService'); export interface ISoundService { - _serviceBrand: any; + serviceBrand: any; playBellSound(): void; } diff --git a/src/browser/services/SoundService.ts b/src/browser/services/SoundService.ts index 89353f45..1772c750 100644 --- a/src/browser/services/SoundService.ts +++ b/src/browser/services/SoundService.ts @@ -7,7 +7,7 @@ import { IOptionsService } from 'common/services/Services'; import { ISoundService } from 'browser/services/Services'; export class SoundService implements ISoundService { - _serviceBrand: any; + serviceBrand: any; private static _audioContext: AudioContext; diff --git a/src/common/TestUtils.test.ts b/src/common/TestUtils.test.ts index 60ae3225..ec36be2f 100644 --- a/src/common/TestUtils.test.ts +++ b/src/common/TestUtils.test.ts @@ -12,7 +12,7 @@ import { BufferSet } from 'common/buffer/BufferSet'; import { IDecPrivateModes } from 'common/Types'; export class MockBufferService implements IBufferService { - _serviceBrand: any; + serviceBrand: any; public get buffer(): IBuffer { return this.buffers.active; } public buffers: IBufferSet = {} as any; constructor( @@ -30,7 +30,7 @@ export class MockBufferService implements IBufferService { } export class MockCoreService implements ICoreService { - _serviceBrand: any; + serviceBrand: any; decPrivateModes: IDecPrivateModes = {} as any; onData: IEvent = new EventEmitter().event; onUserInput: IEvent = new EventEmitter().event; @@ -39,7 +39,7 @@ export class MockCoreService implements ICoreService { } export class MockDirtyRowService implements IDirtyRowService { - _serviceBrand: any; + serviceBrand: any; start: number = 0; end: number = 0; clearRange(): void {} @@ -49,7 +49,7 @@ export class MockDirtyRowService implements IDirtyRowService { } export class MockLogService implements ILogService { - _serviceBrand: any; + serviceBrand: any; debug(message: any, ...optionalParams: any[]): void {} info(message: any, ...optionalParams: any[]): void {} warn(message: any, ...optionalParams: any[]): void {} @@ -57,7 +57,7 @@ export class MockLogService implements ILogService { } export class MockOptionsService implements IOptionsService { - _serviceBrand: any; + serviceBrand: any; options: ITerminalOptions = clone(DEFAULT_OPTIONS); onOptionChange: IEvent = new EventEmitter().event; constructor(testOptions?: IPartialTerminalOptions) { diff --git a/src/common/services/BufferService.ts b/src/common/services/BufferService.ts index 130fbee2..c7b6afce 100644 --- a/src/common/services/BufferService.ts +++ b/src/common/services/BufferService.ts @@ -11,7 +11,7 @@ export const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars export const MINIMUM_ROWS = 1; export class BufferService implements IBufferService { - _serviceBrand: any; + serviceBrand: any; public cols: number; public rows: number; diff --git a/src/common/services/CoreService.ts b/src/common/services/CoreService.ts index 3887d95a..674cfedb 100644 --- a/src/common/services/CoreService.ts +++ b/src/common/services/CoreService.ts @@ -13,7 +13,7 @@ const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({ }); export class CoreService implements ICoreService { - _serviceBrand: any; + serviceBrand: any; public decPrivateModes: IDecPrivateModes; diff --git a/src/common/services/DirtyRowService.ts b/src/common/services/DirtyRowService.ts index 835d4069..0f2f14f5 100644 --- a/src/common/services/DirtyRowService.ts +++ b/src/common/services/DirtyRowService.ts @@ -6,7 +6,7 @@ import { IBufferService, IDirtyRowService } from 'common/services/Services'; export class DirtyRowService implements IDirtyRowService { - _serviceBrand: any; + serviceBrand: any; private _start!: number; private _end!: number; diff --git a/src/common/services/LogService.ts b/src/common/services/LogService.ts index 1e14f06c..6740ad4a 100644 --- a/src/common/services/LogService.ts +++ b/src/common/services/LogService.ts @@ -37,7 +37,7 @@ const optionsKeyToLogLevel: { [key: string]: LogLevel } = { const LOG_PREFIX = 'xterm.js: '; export class LogService implements ILogService { - _serviceBrand: any; + serviceBrand: any; private _logLevel!: LogLevel; diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 4f534dc4..9a5d2151 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -56,7 +56,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({ const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows']; export class OptionsService implements IOptionsService { - _serviceBrand: any; + serviceBrand: any; public options: ITerminalOptions; diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 4ff0bc0c..1af55e9a 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -10,7 +10,7 @@ import { createDecorator } from 'common/services/ServiceRegistry'; export const IBufferService = createDecorator('BufferService'); export interface IBufferService { - _serviceBrand: any; + serviceBrand: any; readonly cols: number; readonly rows: number; @@ -25,7 +25,7 @@ export interface IBufferService { export const ICoreService = createDecorator('CoreService'); export interface ICoreService { - _serviceBrand: any; + serviceBrand: any; readonly decPrivateModes: IDecPrivateModes; @@ -47,7 +47,7 @@ export interface ICoreService { export const IDirtyRowService = createDecorator('DirtyRowService'); export interface IDirtyRowService { - _serviceBrand: any; + serviceBrand: any; readonly start: number; readonly end: number; @@ -64,39 +64,39 @@ export interface IServiceIdentifier { } export interface IConstructorSignature0 { - new(...services: { _serviceBrand: any; }[]): T; + new(...services: { serviceBrand: any; }[]): T; } export interface IConstructorSignature1 { - new(first: A1, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, ...services: { serviceBrand: any; }[]): T; } export interface IConstructorSignature2 { - new(first: A1, second: A2, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, ...services: { serviceBrand: any; }[]): T; } export interface IConstructorSignature3 { - new(first: A1, second: A2, third: A3, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, third: A3, ...services: { serviceBrand: any; }[]): T; } export interface IConstructorSignature4 { - new(first: A1, second: A2, third: A3, fourth: A4, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, ...services: { serviceBrand: any; }[]): T; } export interface IConstructorSignature5 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, ...services: { serviceBrand: any; }[]): T; } export interface IConstructorSignature6 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, ...services: { serviceBrand: any; }[]): T; } export interface IConstructorSignature7 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, ...services: { serviceBrand: any; }[]): T; } export interface IConstructorSignature8 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { _serviceBrand: any; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { serviceBrand: any; }[]): T; } export const IInstantiationService = createDecorator('InstantiationService'); @@ -116,7 +116,7 @@ export interface IInstantiationService { export const ILogService = createDecorator('LogService'); export interface ILogService { - _serviceBrand: any; + serviceBrand: any; debug(message: any, ...optionalParams: any[]): void; info(message: any, ...optionalParams: any[]): void; @@ -126,7 +126,7 @@ export interface ILogService { export const IOptionsService = createDecorator('OptionsService'); export interface IOptionsService { - _serviceBrand: any; + serviceBrand: any; readonly options: ITerminalOptions; diff --git a/tslint.json b/tslint.json index 4445e97c..43662039 100644 --- a/tslint.json +++ b/tslint.json @@ -94,8 +94,6 @@ {"type": "member", "modifiers": ["private"], "format": "camelCase", "leadingUnderscore": "require"}, {"type": "variable", "modifiers": ["const"], "format": ["camelCase", "UPPER_CASE"]}, {"type": "variable", "modifiers": ["const", "export"], "filter": "^I.+Service$", "format": "PascalCase", "prefix": "I"}, - {"type": "member", "filter": "^_serviceBrand$", "leadingUnderscore": "require"}, - {"type": "property", "filter": "^_serviceBrand$", "leadingUnderscore": "require"}, {"type": "interface", "prefix": "I"} ], "no-else-after-return": { From fb6d4e2bfea968bac4c370afc70c3599edf0e3e2 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 23:54:44 -0700 Subject: [PATCH 09/12] Remove imports from out --- src/InputHandler.test.ts | 4 ++-- src/renderer/Renderer.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts index d4cebac1..38645a0b 100644 --- a/src/InputHandler.test.ts +++ b/src/InputHandler.test.ts @@ -15,8 +15,8 @@ import { AttributeData } from 'common/buffer/AttributeData'; import { Params } from 'common/parser/Params'; import { MockCoreService, MockBufferService, MockDirtyRowService, MockOptionsService, MockLogService } from 'common/TestUtils.test'; import { IBufferService } from 'common/services/Services'; -import { DEFAULT_OPTIONS } from '../out/common/services/OptionsService'; -import { clone } from '../out/common/Clone'; +import { DEFAULT_OPTIONS } from 'common/services/OptionsService'; +import { clone } from 'common/Clone'; function getCursor(term: TestTerminal): number[] { return [ diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 574d107a..2ed61b76 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -14,7 +14,7 @@ import { CharacterJoinerRegistry } from 'browser/renderer/CharacterJoinerRegistr import { Disposable } from 'common/Lifecycle'; import { IColorSet } from 'browser/Types'; import { ICharSizeService } from 'browser/services/Services'; -import { IBufferService } from '../../out/common/services/Services'; +import { IBufferService } from 'common/services/Services'; export class Renderer extends Disposable implements IRenderer { private _renderLayers: IRenderLayer[]; From 8deef83c60932102391777c43dd3f5db7d5bf8cd Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 00:16:20 -0700 Subject: [PATCH 10/12] Add tslint rule to prevent import from out Fixes #1996 --- package.json | 4 ++-- tslint.json | 4 ++++ yarn.lock | 63 +++++++++------------------------------------------- 3 files changed, 17 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index f26c010f..08357583 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "prepackage": "npm run build", "package": "webpack", "start": "node demo/start", - "lint": "tslint 'src/**/*.ts' './demo/**/*.ts' './addons/**/*.ts'", + "lint": "tslint 'src/**/*.ts' 'addons/**/*.ts'", "test": "npm run test-unit", "posttest": "npm run lint", "test-api": "mocha \"**/*.api.js\"", @@ -45,7 +45,7 @@ "puppeteer": "^1.15.0", "source-map-loader": "^0.2.4", "ts-loader": "^4.5.0", - "tslint": "^5.9.1", + "tslint": "^5.18.0", "tslint-consistent-codestyle": "^1.13.0", "typescript": "3.5", "utf8": "^3.0.0", diff --git a/tslint.json b/tslint.json index 43662039..cd790c14 100644 --- a/tslint.json +++ b/tslint.json @@ -20,6 +20,10 @@ true, "spaces" ], + "import-blacklist": [ + true, + [".*\\/out\\/.*"] + ], "interface-name": [ true, "always-prefix" diff --git a/yarn.lock b/yarn.lock index 1ac3af13..e804b9a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -419,11 +419,6 @@ ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -574,15 +569,6 @@ aws4@^1.6.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" integrity sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w== -babel-code-frame@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" - integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -818,17 +804,6 @@ chai@3.5.0: deep-eql "^0.1.3" type-detect "^1.0.0" -chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" @@ -1574,7 +1549,7 @@ escape-latex@1.2.0: resolved "https://registry.yarnpkg.com/escape-latex/-/escape-latex-1.2.0.tgz#07c03818cf7dac250cce517f4fda1b001ef2bca1" integrity sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw== -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -2078,13 +2053,6 @@ har-validator@~5.0.3: ajv "^5.1.0" har-schema "^2.0.0" -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -2527,17 +2495,12 @@ javascript-natural-sort@0.7.1: resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59" integrity sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k= -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= - js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.13.1, js-yaml@^3.13.1, js-yaml@^3.7.0: +js-yaml@3.13.1, js-yaml@^3.13.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -4323,11 +4286,6 @@ supports-color@6.0.0: dependencies: has-flag "^3.0.0" -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - supports-color@^5.3.0: version "5.4.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" @@ -4503,25 +4461,26 @@ tslint@^5.17.0: tslib "^1.8.0" tsutils "^2.29.0" -tslint@^5.9.1: - version "5.10.0" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.10.0.tgz#11e26bccb88afa02dd0d9956cae3d4540b5f54c3" - integrity sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM= +tslint@^5.18.0: + version "5.18.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.18.0.tgz#f61a6ddcf372344ac5e41708095bbf043a147ac6" + integrity sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w== dependencies: - babel-code-frame "^6.22.0" + "@babel/code-frame" "^7.0.0" builtin-modules "^1.1.1" chalk "^2.3.0" commander "^2.12.1" diff "^3.2.0" glob "^7.1.1" - js-yaml "^3.7.0" + js-yaml "^3.13.1" minimatch "^3.0.4" + mkdirp "^0.5.1" resolve "^1.3.2" semver "^5.3.0" tslib "^1.8.0" - tsutils "^2.12.1" + tsutils "^2.29.0" -tsutils@^2.12.1, tsutils@^2.24.0, tsutils@^2.27.0: +tsutils@^2.24.0, tsutils@^2.27.0: version "2.27.2" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.27.2.tgz#60ba88a23d6f785ec4b89c6e8179cac9b431f1c7" integrity sha512-qf6rmT84TFMuxAKez2pIfR8UCai49iQsfB7YWVjV1bKpy/d0PWT5rEOSM6La9PiHZ0k1RRZQiwVdVJfQ3BPHgg== From 1df737c3945b7e0260583556d628403d014de377 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 00:40:10 -0700 Subject: [PATCH 11/12] Fix decorator error --- src/AccessibilityManager.ts | 16 ++++++---------- src/Terminal.ts | 7 +++---- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/AccessibilityManager.ts b/src/AccessibilityManager.ts index 915a86d6..75393ea4 100644 --- a/src/AccessibilityManager.ts +++ b/src/AccessibilityManager.ts @@ -11,7 +11,7 @@ import { RenderDebouncer } from 'browser/RenderDebouncer'; import { addDisposableDomListener } from 'browser/Lifecycle'; import { Disposable } from 'common/Lifecycle'; import { ScreenDprMonitor } from 'browser/ScreenDprMonitor'; -import { IRenderDimensions } from 'browser/renderer/Types'; +import { IRenderService } from 'browser/services/Services'; const MAX_ROWS_TO_READ = 20; @@ -47,8 +47,8 @@ export class AccessibilityManager extends Disposable { private _charsToAnnounce: string = ''; constructor( - private _terminal: ITerminal, - private _dimensions: IRenderDimensions + private readonly _terminal: ITerminal, + private readonly _renderService: IRenderService ) { super(); this._accessibilityTreeRoot = document.createElement('div'); @@ -90,6 +90,7 @@ export class AccessibilityManager extends Disposable { this.register(this._terminal.onA11yTab(spaceCount => this._onTab(spaceCount))); this.register(this._terminal.onKey(e => this._onKey(e.key))); this.register(this._terminal.onBlur(() => this._clearLiveRegion())); + this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions())); this._screenDprMonitor = new ScreenDprMonitor(); this.register(this._screenDprMonitor); @@ -271,7 +272,7 @@ export class AccessibilityManager extends Disposable { } private _refreshRowsDimensions(): void { - if (!this._dimensions.actualCellHeight) { + if (!this._renderService.dimensions.actualCellHeight) { return; } if (this._rowElements.length !== this._terminal.rows) { @@ -282,13 +283,8 @@ export class AccessibilityManager extends Disposable { } } - public setDimensions(dimensions: IRenderDimensions): void { - this._dimensions = dimensions; - this._refreshRowsDimensions(); - } - private _refreshRowDimensions(element: HTMLElement): void { - element.style.height = `${this._dimensions.actualCellHeight}px`; + element.style.height = `${this._renderService.dimensions.actualCellHeight}px`; } private _announceCharacters(): void { diff --git a/src/Terminal.ts b/src/Terminal.ts index 8760a4dd..7a068c84 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -30,7 +30,7 @@ import { C0 } from 'common/data/EscapeSequences'; import { InputHandler } from './InputHandler'; import { Renderer } from './renderer/Renderer'; import { Linkifier } from 'browser/Linkifier'; -import { SelectionService } from './browser/services/SelectionService'; +import { SelectionService } from 'browser/services/SelectionService'; import * as Browser from 'common/Platform'; import { addDisposableDomListener } from 'browser/Lifecycle'; import * as Strings from 'browser/LocalizableStrings'; @@ -394,7 +394,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp case 'screenReaderMode': if (this.optionsService.options.screenReaderMode) { if (!this._accessibilityManager && this._renderService) { - this._accessibilityManager = new AccessibilityManager(this, this._renderService.dimensions); + this._accessibilityManager = new AccessibilityManager(this, this._renderService); } } else { if (this._accessibilityManager) { @@ -665,8 +665,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp if (this.options.screenReaderMode) { // Note that this must be done *after* the renderer is created in order to // ensure the correct order of the dprchange event - this._accessibilityManager = new AccessibilityManager(this, this._renderService.dimensions); - this._accessibilityManager.register(this._renderService.onDimensionsChange(e => this._accessibilityManager.setDimensions(e))); + this._accessibilityManager = new AccessibilityManager(this, this._renderService); } // Measure the character size From 2c50af356a9de25ad0b234a04ce3f2852735ed59 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 14 Jul 2019 08:53:03 -0700 Subject: [PATCH 12/12] Remove note in readme --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 0b4c64b9..d01a0f33 100644 --- a/README.md +++ b/README.md @@ -185,5 +185,3 @@ If you contribute code to this project, you are implicitly allowing your code to Copyright (c) 2017-2019, [The xterm.js authors](https://github.com/xtermjs/xterm.js/graphs/contributors) (MIT License)
Copyright (c) 2014-2017, SourceLair, Private Company ([www.sourcelair.com](https://www.sourcelair.com/home)) (MIT License)
Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) - -Some files in this code base are heavily influenced on implementations in [Visual Studio Code](https://github.com/Microsoft/vscode) (MIT License).