feat: 添加vscode编辑器
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
export class SyncDescriptor {
|
||||
constructor(ctor, staticArguments = [], supportsDelayedInstantiation = false) {
|
||||
this.ctor = ctor;
|
||||
this.staticArguments = staticArguments;
|
||||
this.supportsDelayedInstantiation = supportsDelayedInstantiation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { SyncDescriptor } from './descriptors.js';
|
||||
const _registry = [];
|
||||
export function registerSingleton(id, ctorOrDescriptor, supportsDelayedInstantiation) {
|
||||
if (!(ctorOrDescriptor instanceof SyncDescriptor)) {
|
||||
ctorOrDescriptor = new SyncDescriptor(ctorOrDescriptor, [], supportsDelayedInstantiation);
|
||||
}
|
||||
_registry.push([id, ctorOrDescriptor]);
|
||||
}
|
||||
export function getSingletonServiceDescriptors() {
|
||||
return _registry;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
export class Node {
|
||||
constructor(data) {
|
||||
this.incoming = new Map();
|
||||
this.outgoing = new Map();
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
export class Graph {
|
||||
constructor(_hashFn) {
|
||||
this._hashFn = _hashFn;
|
||||
this._nodes = new Map();
|
||||
// empty
|
||||
}
|
||||
roots() {
|
||||
const ret = [];
|
||||
for (const node of this._nodes.values()) {
|
||||
if (node.outgoing.size === 0) {
|
||||
ret.push(node);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
insertEdge(from, to) {
|
||||
const fromNode = this.lookupOrInsertNode(from);
|
||||
const toNode = this.lookupOrInsertNode(to);
|
||||
fromNode.outgoing.set(this._hashFn(to), toNode);
|
||||
toNode.incoming.set(this._hashFn(from), fromNode);
|
||||
}
|
||||
removeNode(data) {
|
||||
const key = this._hashFn(data);
|
||||
this._nodes.delete(key);
|
||||
for (const node of this._nodes.values()) {
|
||||
node.outgoing.delete(key);
|
||||
node.incoming.delete(key);
|
||||
}
|
||||
}
|
||||
lookupOrInsertNode(data) {
|
||||
const key = this._hashFn(data);
|
||||
let node = this._nodes.get(key);
|
||||
if (!node) {
|
||||
node = new Node(data);
|
||||
this._nodes.set(key, node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
isEmpty() {
|
||||
return this._nodes.size === 0;
|
||||
}
|
||||
toString() {
|
||||
const data = [];
|
||||
for (const [key, value] of this._nodes) {
|
||||
data.push(`${key}, (incoming)[${[...value.incoming.keys()].join(', ')}], (outgoing)[${[...value.outgoing.keys()].join(',')}]`);
|
||||
}
|
||||
return data.join('\n');
|
||||
}
|
||||
/**
|
||||
* This is brute force and slow and **only** be used
|
||||
* to trouble shoot.
|
||||
*/
|
||||
findCycleSlow() {
|
||||
for (const [id, node] of this._nodes) {
|
||||
const seen = new Set([id]);
|
||||
const res = this._findCycle(node, seen);
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
_findCycle(node, seen) {
|
||||
for (const [id, outgoing] of node.outgoing) {
|
||||
if (seen.has(id)) {
|
||||
return [...seen, id].join(' -> ');
|
||||
}
|
||||
seen.add(id);
|
||||
const value = this._findCycle(outgoing, seen);
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
seen.delete(id);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// ------ internal util
|
||||
export var _util;
|
||||
(function (_util) {
|
||||
_util.serviceIds = new Map();
|
||||
_util.DI_TARGET = '$di$target';
|
||||
_util.DI_DEPENDENCIES = '$di$dependencies';
|
||||
function getServiceDependencies(ctor) {
|
||||
return ctor[_util.DI_DEPENDENCIES] || [];
|
||||
}
|
||||
_util.getServiceDependencies = getServiceDependencies;
|
||||
})(_util || (_util = {}));
|
||||
export const IInstantiationService = createDecorator('instantiationService');
|
||||
function storeServiceDependency(id, target, index) {
|
||||
if (target[_util.DI_TARGET] === target) {
|
||||
target[_util.DI_DEPENDENCIES].push({ id, index });
|
||||
}
|
||||
else {
|
||||
target[_util.DI_DEPENDENCIES] = [{ id, index }];
|
||||
target[_util.DI_TARGET] = target;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The *only* valid way to create a {{ServiceIdentifier}}.
|
||||
*/
|
||||
export function createDecorator(serviceId) {
|
||||
if (_util.serviceIds.has(serviceId)) {
|
||||
return _util.serviceIds.get(serviceId);
|
||||
}
|
||||
const id = function (target, key, index) {
|
||||
if (arguments.length !== 3) {
|
||||
throw new Error('@IServiceName-decorator can only be used to decorate a parameter');
|
||||
}
|
||||
storeServiceDependency(id, target, index);
|
||||
};
|
||||
id.toString = () => serviceId;
|
||||
_util.serviceIds.set(serviceId, id);
|
||||
return id;
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { IdleValue } from '../../../base/common/async.js';
|
||||
import { illegalState } from '../../../base/common/errors.js';
|
||||
import { SyncDescriptor } from './descriptors.js';
|
||||
import { Graph } from './graph.js';
|
||||
import { IInstantiationService, _util } from './instantiation.js';
|
||||
import { ServiceCollection } from './serviceCollection.js';
|
||||
// TRACING
|
||||
const _enableTracing = false;
|
||||
class CyclicDependencyError extends Error {
|
||||
constructor(graph) {
|
||||
var _a;
|
||||
super('cyclic dependency between services');
|
||||
this.message = (_a = graph.findCycleSlow()) !== null && _a !== void 0 ? _a : `UNABLE to detect cycle, dumping graph: \n${graph.toString()}`;
|
||||
}
|
||||
}
|
||||
export class InstantiationService {
|
||||
constructor(services = new ServiceCollection(), strict = false, parent) {
|
||||
this._activeInstantiations = new Set();
|
||||
this._services = services;
|
||||
this._strict = strict;
|
||||
this._parent = parent;
|
||||
this._services.set(IInstantiationService, this);
|
||||
}
|
||||
createChild(services) {
|
||||
return new InstantiationService(services, this._strict, this);
|
||||
}
|
||||
invokeFunction(fn, ...args) {
|
||||
const _trace = Trace.traceInvocation(fn);
|
||||
let _done = false;
|
||||
try {
|
||||
const accessor = {
|
||||
get: (id) => {
|
||||
if (_done) {
|
||||
throw illegalState('service accessor is only valid during the invocation of its target method');
|
||||
}
|
||||
const result = this._getOrCreateServiceInstance(id, _trace);
|
||||
if (!result) {
|
||||
throw new Error(`[invokeFunction] unknown service '${id}'`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
return fn(accessor, ...args);
|
||||
}
|
||||
finally {
|
||||
_done = true;
|
||||
_trace.stop();
|
||||
}
|
||||
}
|
||||
createInstance(ctorOrDescriptor, ...rest) {
|
||||
let _trace;
|
||||
let result;
|
||||
if (ctorOrDescriptor instanceof SyncDescriptor) {
|
||||
_trace = Trace.traceCreation(ctorOrDescriptor.ctor);
|
||||
result = this._createInstance(ctorOrDescriptor.ctor, ctorOrDescriptor.staticArguments.concat(rest), _trace);
|
||||
}
|
||||
else {
|
||||
_trace = Trace.traceCreation(ctorOrDescriptor);
|
||||
result = this._createInstance(ctorOrDescriptor, rest, _trace);
|
||||
}
|
||||
_trace.stop();
|
||||
return result;
|
||||
}
|
||||
_createInstance(ctor, args = [], _trace) {
|
||||
// arguments defined by service decorators
|
||||
const serviceDependencies = _util.getServiceDependencies(ctor).sort((a, b) => a.index - b.index);
|
||||
const serviceArgs = [];
|
||||
for (const dependency of serviceDependencies) {
|
||||
const service = this._getOrCreateServiceInstance(dependency.id, _trace);
|
||||
if (!service) {
|
||||
this._throwIfStrict(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id}.`, false);
|
||||
}
|
||||
serviceArgs.push(service);
|
||||
}
|
||||
const firstServiceArgPos = serviceDependencies.length > 0 ? serviceDependencies[0].index : args.length;
|
||||
// check for argument mismatches, adjust static args if needed
|
||||
if (args.length !== firstServiceArgPos) {
|
||||
console.trace(`[createInstance] First service dependency of ${ctor.name} at position ${firstServiceArgPos + 1} conflicts with ${args.length} static arguments`);
|
||||
const delta = firstServiceArgPos - args.length;
|
||||
if (delta > 0) {
|
||||
args = args.concat(new Array(delta));
|
||||
}
|
||||
else {
|
||||
args = args.slice(0, firstServiceArgPos);
|
||||
}
|
||||
}
|
||||
// now create the instance
|
||||
return new ctor(...[...args, ...serviceArgs]);
|
||||
}
|
||||
_setServiceInstance(id, instance) {
|
||||
if (this._services.get(id) instanceof SyncDescriptor) {
|
||||
this._services.set(id, instance);
|
||||
}
|
||||
else if (this._parent) {
|
||||
this._parent._setServiceInstance(id, instance);
|
||||
}
|
||||
else {
|
||||
throw new Error('illegalState - setting UNKNOWN service instance');
|
||||
}
|
||||
}
|
||||
_getServiceInstanceOrDescriptor(id) {
|
||||
const instanceOrDesc = this._services.get(id);
|
||||
if (!instanceOrDesc && this._parent) {
|
||||
return this._parent._getServiceInstanceOrDescriptor(id);
|
||||
}
|
||||
else {
|
||||
return instanceOrDesc;
|
||||
}
|
||||
}
|
||||
_getOrCreateServiceInstance(id, _trace) {
|
||||
const thing = this._getServiceInstanceOrDescriptor(id);
|
||||
if (thing instanceof SyncDescriptor) {
|
||||
return this._safeCreateAndCacheServiceInstance(id, thing, _trace.branch(id, true));
|
||||
}
|
||||
else {
|
||||
_trace.branch(id, false);
|
||||
return thing;
|
||||
}
|
||||
}
|
||||
_safeCreateAndCacheServiceInstance(id, desc, _trace) {
|
||||
if (this._activeInstantiations.has(id)) {
|
||||
throw new Error(`illegal state - RECURSIVELY instantiating service '${id}'`);
|
||||
}
|
||||
this._activeInstantiations.add(id);
|
||||
try {
|
||||
return this._createAndCacheServiceInstance(id, desc, _trace);
|
||||
}
|
||||
finally {
|
||||
this._activeInstantiations.delete(id);
|
||||
}
|
||||
}
|
||||
_createAndCacheServiceInstance(id, desc, _trace) {
|
||||
const graph = new Graph(data => data.id.toString());
|
||||
let cycleCount = 0;
|
||||
const stack = [{ id, desc, _trace }];
|
||||
while (stack.length) {
|
||||
const item = stack.pop();
|
||||
graph.lookupOrInsertNode(item);
|
||||
// a weak but working heuristic for cycle checks
|
||||
if (cycleCount++ > 1000) {
|
||||
throw new CyclicDependencyError(graph);
|
||||
}
|
||||
// check all dependencies for existence and if they need to be created first
|
||||
for (const dependency of _util.getServiceDependencies(item.desc.ctor)) {
|
||||
const instanceOrDesc = this._getServiceInstanceOrDescriptor(dependency.id);
|
||||
if (!instanceOrDesc) {
|
||||
this._throwIfStrict(`[createInstance] ${id} depends on ${dependency.id} which is NOT registered.`, true);
|
||||
}
|
||||
if (instanceOrDesc instanceof SyncDescriptor) {
|
||||
const d = { id: dependency.id, desc: instanceOrDesc, _trace: item._trace.branch(dependency.id, true) };
|
||||
graph.insertEdge(item, d);
|
||||
stack.push(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (true) {
|
||||
const roots = graph.roots();
|
||||
// if there is no more roots but still
|
||||
// nodes in the graph we have a cycle
|
||||
if (roots.length === 0) {
|
||||
if (!graph.isEmpty()) {
|
||||
throw new CyclicDependencyError(graph);
|
||||
}
|
||||
break;
|
||||
}
|
||||
for (const { data } of roots) {
|
||||
// Repeat the check for this still being a service sync descriptor. That's because
|
||||
// instantiating a dependency might have side-effect and recursively trigger instantiation
|
||||
// so that some dependencies are now fullfilled already.
|
||||
const instanceOrDesc = this._getServiceInstanceOrDescriptor(data.id);
|
||||
if (instanceOrDesc instanceof SyncDescriptor) {
|
||||
// create instance and overwrite the service collections
|
||||
const instance = this._createServiceInstanceWithOwner(data.id, data.desc.ctor, data.desc.staticArguments, data.desc.supportsDelayedInstantiation, data._trace);
|
||||
this._setServiceInstance(data.id, instance);
|
||||
}
|
||||
graph.removeNode(data);
|
||||
}
|
||||
}
|
||||
return this._getServiceInstanceOrDescriptor(id);
|
||||
}
|
||||
_createServiceInstanceWithOwner(id, ctor, args = [], supportsDelayedInstantiation, _trace) {
|
||||
if (this._services.get(id) instanceof SyncDescriptor) {
|
||||
return this._createServiceInstance(ctor, args, supportsDelayedInstantiation, _trace);
|
||||
}
|
||||
else if (this._parent) {
|
||||
return this._parent._createServiceInstanceWithOwner(id, ctor, args, supportsDelayedInstantiation, _trace);
|
||||
}
|
||||
else {
|
||||
throw new Error(`illegalState - creating UNKNOWN service instance ${ctor.name}`);
|
||||
}
|
||||
}
|
||||
_createServiceInstance(ctor, args = [], _supportsDelayedInstantiation, _trace) {
|
||||
if (!_supportsDelayedInstantiation) {
|
||||
// eager instantiation
|
||||
return this._createInstance(ctor, args, _trace);
|
||||
}
|
||||
else {
|
||||
// Return a proxy object that's backed by an idle value. That
|
||||
// strategy is to instantiate services in our idle time or when actually
|
||||
// needed but not when injected into a consumer
|
||||
const idle = new IdleValue(() => this._createInstance(ctor, args, _trace));
|
||||
return new Proxy(Object.create(null), {
|
||||
get(target, key) {
|
||||
if (key in target) {
|
||||
return target[key];
|
||||
}
|
||||
const obj = idle.value;
|
||||
let prop = obj[key];
|
||||
if (typeof prop !== 'function') {
|
||||
return prop;
|
||||
}
|
||||
prop = prop.bind(obj);
|
||||
target[key] = prop;
|
||||
return prop;
|
||||
},
|
||||
set(_target, p, value) {
|
||||
idle.value[p] = value;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
_throwIfStrict(msg, printWarning) {
|
||||
if (printWarning) {
|
||||
console.warn(msg);
|
||||
}
|
||||
if (this._strict) {
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
export class Trace {
|
||||
constructor(type, name) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this._start = Date.now();
|
||||
this._dep = [];
|
||||
}
|
||||
static traceInvocation(ctor) {
|
||||
return !_enableTracing ? Trace._None : new Trace(1 /* TraceType.Invocation */, ctor.name || ctor.toString().substring(0, 42).replace(/\n/g, ''));
|
||||
}
|
||||
static traceCreation(ctor) {
|
||||
return !_enableTracing ? Trace._None : new Trace(0 /* TraceType.Creation */, ctor.name);
|
||||
}
|
||||
branch(id, first) {
|
||||
const child = new Trace(2 /* TraceType.Branch */, id.toString());
|
||||
this._dep.push([id, first, child]);
|
||||
return child;
|
||||
}
|
||||
stop() {
|
||||
const dur = Date.now() - this._start;
|
||||
Trace._totals += dur;
|
||||
let causedCreation = false;
|
||||
function printChild(n, trace) {
|
||||
const res = [];
|
||||
const prefix = new Array(n + 1).join('\t');
|
||||
for (const [id, first, child] of trace._dep) {
|
||||
if (first && child) {
|
||||
causedCreation = true;
|
||||
res.push(`${prefix}CREATES -> ${id}`);
|
||||
const nested = printChild(n + 1, child);
|
||||
if (nested) {
|
||||
res.push(nested);
|
||||
}
|
||||
}
|
||||
else {
|
||||
res.push(`${prefix}uses -> ${id}`);
|
||||
}
|
||||
}
|
||||
return res.join('\n');
|
||||
}
|
||||
const lines = [
|
||||
`${this.type === 0 /* TraceType.Creation */ ? 'CREATE' : 'CALL'} ${this.name}`,
|
||||
`${printChild(1, this)}`,
|
||||
`DONE, took ${dur.toFixed(2)}ms (grand total ${Trace._totals.toFixed(2)}ms)`
|
||||
];
|
||||
if (dur > 2 || causedCreation) {
|
||||
console.log(lines.join('\n'));
|
||||
}
|
||||
}
|
||||
}
|
||||
Trace._None = new class extends Trace {
|
||||
constructor() { super(-1, null); }
|
||||
stop() { }
|
||||
branch() { return this; }
|
||||
};
|
||||
Trace._totals = 0;
|
||||
//#endregion
|
||||
@@ -0,0 +1,20 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
export class ServiceCollection {
|
||||
constructor(...entries) {
|
||||
this._entries = new Map();
|
||||
for (const [id, service] of entries) {
|
||||
this.set(id, service);
|
||||
}
|
||||
}
|
||||
set(id, instanceOrDescriptor) {
|
||||
const result = this._entries.get(id);
|
||||
this._entries.set(id, instanceOrDescriptor);
|
||||
return result;
|
||||
}
|
||||
get(id) {
|
||||
return this._entries.get(id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user