Dependency Injection prototype
This commit is contained in:
+11
-4
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
{
|
||||
"extends": "../tsconfig-library-base",
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"es2015"
|
||||
],
|
||||
"outDir": "../../out",
|
||||
"types": [
|
||||
"../../node_modules/@types/mocha"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"strict": true,
|
||||
"declarationMap": true
|
||||
"declarationMap": true,
|
||||
"experimentalDecorators": true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user