Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/extensions/common/lazyCreateExtensionHostManager.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Barrier } from '../../../../base/common/async.js';
7
import { Emitter, Event } from '../../../../base/common/event.js';
8
import { Disposable } from '../../../../base/common/lifecycle.js';
9
import { URI } from '../../../../base/common/uri.js';
10
import { ExtensionIdentifier, IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';
11
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
12
import { ILogService } from '../../../../platform/log/common/log.js';
13
import { RemoteAuthorityResolverErrorCode } from '../../../../platform/remote/common/remoteAuthorityResolver.js';
14
import { ExtensionHostKind } from './extensionHostKind.js';
15
import { ExtensionHostManager, friendlyExtHostName } from './extensionHostManager.js';
16
import { IExtensionHostManager } from './extensionHostManagers.js';
17
import { IExtensionDescriptionDelta } from './extensionHostProtocol.js';
18
import { IResolveAuthorityResult } from './extensionHostProxy.js';
19
import { ExtensionRunningLocation } from './extensionRunningLocation.js';
20
import { ActivationKind, ExtensionActivationReason, ExtensionHostStartup, IExtensionHost, IExtensionInspectInfo, IInternalExtensionService } from './extensions.js';
21
import { ResponsiveState } from './rpcProtocol.js';
22
23
/**
24
* Waits until `start()` and only if it has extensions proceeds to really start.
25
*/
26
export class LazyCreateExtensionHostManager extends Disposable implements IExtensionHostManager {
27
28
public readonly onDidExit: Event<[number, string | null]>;
29
private readonly _onDidChangeResponsiveState: Emitter<ResponsiveState> = this._register(new Emitter<ResponsiveState>());
30
public readonly onDidChangeResponsiveState: Event<ResponsiveState> = this._onDidChangeResponsiveState.event;
31
32
private readonly _extensionHost: IExtensionHost;
33
private _startCalled: Barrier;
34
private _actual: ExtensionHostManager | null;
35
36
public get pid(): number | null {
37
if (this._actual) {
38
return this._actual.pid;
39
}
40
return null;
41
}
42
43
public get kind(): ExtensionHostKind {
44
return this._extensionHost.runningLocation.kind;
45
}
46
47
public get startup(): ExtensionHostStartup {
48
return this._extensionHost.startup;
49
}
50
51
public get friendyName(): string {
52
return friendlyExtHostName(this.kind, this.pid);
53
}
54
55
constructor(
56
extensionHost: IExtensionHost,
57
private readonly _initialActivationEvents: string[],
58
private readonly _internalExtensionService: IInternalExtensionService,
59
@IInstantiationService private readonly _instantiationService: IInstantiationService,
60
@ILogService private readonly _logService: ILogService
61
) {
62
super();
63
this._extensionHost = extensionHost;
64
this.onDidExit = extensionHost.onExit;
65
this._startCalled = new Barrier();
66
this._actual = null;
67
}
68
69
private _createActual(reason: string): ExtensionHostManager {
70
this._logService.info(`Creating lazy extension host (${this.friendyName}). Reason: ${reason}`);
71
this._actual = this._register(this._instantiationService.createInstance(ExtensionHostManager, this._extensionHost, this._initialActivationEvents, this._internalExtensionService));
72
this._register(this._actual.onDidChangeResponsiveState((e) => this._onDidChangeResponsiveState.fire(e)));
73
return this._actual;
74
}
75
76
private async _getOrCreateActualAndStart(reason: string): Promise<ExtensionHostManager> {
77
if (this._actual) {
78
// already created/started
79
return this._actual;
80
}
81
const actual = this._createActual(reason);
82
await actual.ready();
83
return actual;
84
}
85
86
public async ready(): Promise<void> {
87
await this._startCalled.wait();
88
if (this._actual) {
89
await this._actual.ready();
90
}
91
}
92
93
public async disconnect(): Promise<void> {
94
await this._actual?.disconnect();
95
}
96
97
public representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean {
98
return this._extensionHost.runningLocation.equals(runningLocation);
99
}
100
101
public async deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {
102
await this._startCalled.wait();
103
if (this._actual) {
104
return this._actual.deltaExtensions(extensionsDelta);
105
}
106
if (extensionsDelta.myToAdd.length > 0) {
107
const actual = this._createActual(`contains ${extensionsDelta.myToAdd.length} new extension(s) (installed or enabled): ${extensionsDelta.myToAdd.map(extId => extId.value)}`);
108
await actual.ready();
109
return;
110
}
111
}
112
113
public containsExtension(extensionId: ExtensionIdentifier): boolean {
114
return this._extensionHost.extensions?.containsExtension(extensionId) ?? false;
115
}
116
117
public async activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean> {
118
await this._startCalled.wait();
119
if (this._actual) {
120
return this._actual.activate(extension, reason);
121
}
122
return false;
123
}
124
125
public async activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {
126
if (activationKind === ActivationKind.Immediate) {
127
// this is an immediate request, so we cannot wait for start to be called
128
if (this._actual) {
129
return this._actual.activateByEvent(activationEvent, activationKind);
130
}
131
return;
132
}
133
await this._startCalled.wait();
134
if (this._actual) {
135
return this._actual.activateByEvent(activationEvent, activationKind);
136
}
137
}
138
139
public activationEventIsDone(activationEvent: string): boolean {
140
if (!this._startCalled.isOpen()) {
141
return false;
142
}
143
if (this._actual) {
144
return this._actual.activationEventIsDone(activationEvent);
145
}
146
return true;
147
}
148
149
public async getInspectPort(tryEnableInspector: boolean): Promise<IExtensionInspectInfo | undefined> {
150
await this._startCalled.wait();
151
return this._actual?.getInspectPort(tryEnableInspector);
152
}
153
154
public async resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {
155
await this._startCalled.wait();
156
if (this._actual) {
157
return this._actual.resolveAuthority(remoteAuthority, resolveAttempt);
158
}
159
return {
160
type: 'error',
161
error: {
162
message: `Cannot resolve authority`,
163
code: RemoteAuthorityResolverErrorCode.Unknown,
164
detail: undefined
165
}
166
};
167
}
168
169
public async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null> {
170
await this._startCalled.wait();
171
if (this._actual) {
172
return this._actual.getCanonicalURI(remoteAuthority, uri);
173
}
174
throw new Error(`Cannot resolve canonical URI`);
175
}
176
177
public async start(extensionRegistryVersionId: number, allExtensions: IExtensionDescription[], myExtensions: ExtensionIdentifier[]): Promise<void> {
178
if (myExtensions.length > 0) {
179
// there are actual extensions, so let's launch the extension host (auto-start)
180
const actual = this._createActual(`contains ${myExtensions.length} extension(s): ${myExtensions.map(extId => extId.value)}.`);
181
const result = actual.ready();
182
this._startCalled.open();
183
return result;
184
}
185
// there are no actual extensions running
186
this._startCalled.open();
187
}
188
189
public async extensionTestsExecute(): Promise<number> {
190
await this._startCalled.wait();
191
const actual = await this._getOrCreateActualAndStart(`execute tests.`);
192
return actual.extensionTestsExecute();
193
}
194
195
public async setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {
196
await this._startCalled.wait();
197
if (this._actual) {
198
return this._actual.setRemoteEnvironment(env);
199
}
200
}
201
}
202
203