This repository has been archived by the owner on Nov 16, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
/
singleton.ts
88 lines (73 loc) · 3.01 KB
/
singleton.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License in the project root for license information.
* @author Microsoft
*/
import 'reflect-metadata'; // tslint:disable-line
import { Container, injectable } from 'inversify'; // tslint:disable-line
import * as vscode from 'vscode';
import { __ } from './i18n';
export const container: Container = new Container({ autoBindInjectable: true, defaultScope: 'Singleton' });
export const EXTENSION_CONTEXT: symbol = Symbol('vscode.ExtensionContext');
type Constructor<T> = new(...arg: any[]) => T;
/**
* Singleton base class.
* Please implement constructor as simple as possible - only for injecting required components.
* Most initialization logic (including async initialization) should occur in onActivate.
*/
@injectable()
export abstract class Singleton {
protected context: vscode.ExtensionContext = container.get(EXTENSION_CONTEXT);
private activated: boolean = false;
constructor() {
console.log(`Singleton ${this.constructor.name} constructed`);
container.bind(Singleton).toConstantValue(this);
}
public onActivate?(): Promise<void> | void;
public onDeactivate?(): Promise<void> | void;
public async ensureActivated(): Promise<this> {
if (this.onActivate && !this.activated) {
this.activated = true;
await this.onActivate();
console.log(`Singleton ${this.constructor.name} activated`);
}
return this;
}
}
let getSingletonDisabled: boolean = false;
let initializationFinish: boolean = false;
export function getSingleton<T extends Singleton>(clazz: Constructor<T>): Promise<T> | T {
if (!container.isBound(clazz)) {
container.bind(clazz).toSelf();
}
if (getSingletonDisabled) {
throw new Error('Getting async initialized Singleton in Singleton constructor is prohibited!');
}
return container.get<T>(clazz).ensureActivated();
}
export function bindExtensionContext(context: vscode.ExtensionContext): void {
if (container.isBound(EXTENSION_CONTEXT)) {
container.unbind(EXTENSION_CONTEXT);
}
container.bind(EXTENSION_CONTEXT).toConstantValue(context);
}
export async function initializeAll(singletonClasses: Constructor<Singleton>[]): Promise<void> {
if (!initializationFinish) {
getSingletonDisabled = true;
const allSingletons: Singleton[] = singletonClasses.map(clazz => container.get(clazz));
getSingletonDisabled = false;
await Promise.all(allSingletons.map(singleton => singleton.ensureActivated()));
initializationFinish = true;
}
}
export async function delay(ms: number): Promise<void> {
// tslint:disable-next-line: no-unnecessary-callback-wrapper
await new Promise((something) => setTimeout(() => something(), ms));
}
export async function dispose(): Promise<void> {
for (const singleton of container.getAll(Singleton)) {
if (singleton.onDeactivate) {
await singleton.onDeactivate();
}
}
}