Dependency Injection prototype

This commit is contained in:
Daniel Imms
2019-07-13 19:50:16 -07:00
parent c078f3db48
commit 21fbe25858
10 changed files with 160 additions and 15 deletions
+11 -4
View File
@@ -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();
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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);
}
+1 -1
View File
@@ -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();
}
@@ -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<IServiceIdentifier<any>, any>();
constructor(...entries: [IServiceIdentifier<any>, any][]) {
for (const [id, service] of entries) {
this.set(id, service);
}
}
set<T>(id: IServiceIdentifier<T>, instance: T): T {
const result = this._entries.get(id);
this._entries.set(id, instance);
return result;
}
forEach(callback: (id: IServiceIdentifier<any>, instance: any) => any): void {
this._entries.forEach((value, key) => callback(key, value));
}
has(id: IServiceIdentifier<any>): boolean {
return this._entries.has(id);
}
get<T>(id: IServiceIdentifier<T>): 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<T>(id: IServiceIdentifier<T>, instance: T): void {
this._services.set(id, instance);
}
public createInstance<T>(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 <T>new ctor(...[...args, ...serviceArgs]);
}
}
+43
View File
@@ -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<string, IServiceIdentifier<any>> = new Map();
export function getServiceDependencies(ctor: any): { id: IServiceIdentifier<any>, index: number, optional: boolean }[] {
return ctor[DI_DEPENDENCIES] || [];
}
export function createDecorator<T>(id: string): IServiceIdentifier<T> {
if (serviceRegistry.has(id)) {
return serviceRegistry.get(id)!;
}
const decorator = <any>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;
}
}
@@ -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<IBufferService>('BufferService');
export interface IBufferService {
readonly cols: number;
readonly rows: number;
@@ -19,6 +21,7 @@ export interface IBufferService {
reset(): void;
}
export const ICoreService = createDecorator<ICoreService>('CoreService');
export interface ICoreService {
readonly decPrivateModes: IDecPrivateModes;
@@ -48,6 +51,17 @@ export interface IDirtyRowService {
markAllDirty(): void;
}
export interface IServiceIdentifier<T> {
(...args: any[]): void;
type: T;
}
export const IInstantiationService = createDecorator<IInstantiationService>('InstantiationService');
export interface IInstantiationService {
setService<T>(id: IServiceIdentifier<T>, 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<IOptionsService>('OptionsService');
export interface IOptionsService {
readonly options: ITerminalOptions;
+3
View File
@@ -1,6 +1,9 @@
{
"extends": "../tsconfig-library-base",
"compilerOptions": {
"lib": [
"es2015"
],
"outDir": "../../out",
"types": [
"../../node_modules/@types/mocha"
+2 -1
View File
@@ -3,6 +3,7 @@
"compilerOptions": {
"composite": true,
"strict": true,
"declarationMap": true
"declarationMap": true,
"experimentalDecorators": true
}
}