From 5cc8ad802c42876928cd345bfba0832fde759fba Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 13 Jul 2019 22:02:13 -0700 Subject: [PATCH] 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 11ef2d6ee..5cda1d246 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 67584a362..60ae3225a 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 e7dd1b646..130fbee24 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 da60e7878..3887d95a9 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 b89fb918d..835d40691 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 98c23b6e4..037fbcfd1 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 f7480078a..1e14f06c1 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 7396491e5..4f534dc4a 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 837b9b809..3433af289 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 a6e8fca7c..80d17cde9 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),