Skip to content

Commit 42cd304

Browse files
committed
Simplify AbstractExtensionService
1 parent 55009ac commit 42cd304

4 files changed

Lines changed: 158 additions & 98 deletions

File tree

src/vs/platform/actions/test/common/menuService.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
1111
import { NullCommandService } from 'vs/platform/commands/common/commands';
1212
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
1313
import { AbstractExtensionService, ActivatedExtension } from 'vs/platform/extensions/common/abstractExtensionService';
14+
import { IExtensionPoint } from "vs/platform/extensions/common/extensionsRegistry";
15+
import { TPromise } from "vs/base/common/winjs.base";
16+
import { ExtensionPointContribution, IExtensionDescription } from "vs/platform/extensions/common/extensions";
1417

1518
// --- service instances
1619

@@ -24,6 +27,12 @@ const extensionService = new class extends AbstractExtensionService<ActivatedExt
2427
protected _actualActivateExtension() {
2528
return null;
2629
}
30+
public getExtensions(): TPromise<IExtensionDescription[]> {
31+
throw new Error('Not implemented');
32+
}
33+
public readExtensionPointContributions<T>(extPoint: IExtensionPoint<T>): TPromise<ExtensionPointContribution<T>[]> {
34+
throw new Error('Not implemented');
35+
}
2736
}(true);
2837

2938
const contextKeyService = new class extends MockContextKeyService {

src/vs/platform/extensions/common/abstractExtensionService.ts

Lines changed: 113 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
import * as nls from 'vs/nls';
88
import Severity from 'vs/base/common/severity';
99
import { TPromise } from 'vs/base/common/winjs.base';
10-
import { IExtensionDescription, IExtensionService, IExtensionsStatus, ExtensionPointContribution } from 'vs/platform/extensions/common/extensions';
11-
import { IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry';
10+
import { IExtensionDescription, IExtensionsStatus } from 'vs/platform/extensions/common/extensions';
1211

1312
const hasOwnProperty = Object.hasOwnProperty;
1413

@@ -30,107 +29,65 @@ interface IActivatingExtensionMap {
3029

3130
const NO_OP_VOID_PROMISE = TPromise.as<void>(void 0);
3231

33-
export abstract class AbstractExtensionService<T extends ActivatedExtension> implements IExtensionService {
34-
public _serviceBrand: any;
32+
export interface IExtensionsManagerHost<T extends ActivatedExtension> {
33+
showMessage(severity: Severity, message: string): void;
3534

36-
private _activatingExtensions: IActivatingExtensionMap;
37-
protected _activatedExtensions: IActivatedExtensionMap<T>;
38-
private _onReady: TPromise<boolean>;
39-
private _onReadyC: (v: boolean) => void;
40-
private _isReady: boolean;
41-
protected _registry: ExtensionDescriptionRegistry;
35+
createFailedExtension(): T;
36+
37+
actualActivateExtension(extensionDescription: IExtensionDescription): TPromise<T>;
38+
}
4239

40+
export class ExtensionsManager<T extends ActivatedExtension> {
41+
42+
private readonly _registry: ExtensionDescriptionRegistry;
43+
private readonly _host: IExtensionsManagerHost<T>;
44+
private readonly _activatingExtensions: IActivatingExtensionMap;
45+
private readonly _activatedExtensions: IActivatedExtensionMap<T>;
4346
/**
4447
* A map of already activated events to speed things up if the same activation event is triggered multiple times.
4548
*/
46-
private _alreadyActivatedEvents: { [activationEvent: string]: boolean; };
49+
private readonly _alreadyActivatedEvents: { [activationEvent: string]: boolean; };
4750

48-
constructor(isReadyByDefault: boolean) {
49-
if (isReadyByDefault) {
50-
this._isReady = true;
51-
this._onReady = TPromise.as(true);
52-
this._onReadyC = (v: boolean) => { /*no-op*/ };
53-
} else {
54-
this._isReady = false;
55-
this._onReady = new TPromise<boolean>((c, e, p) => {
56-
this._onReadyC = c;
57-
}, () => {
58-
console.warn('You should really not try to cancel this ready promise!');
59-
});
60-
}
51+
constructor(registry: ExtensionDescriptionRegistry, host: IExtensionsManagerHost<T>) {
52+
this._registry = registry;
53+
this._host = host;
6154
this._activatingExtensions = {};
6255
this._activatedExtensions = {};
63-
this._registry = new ExtensionDescriptionRegistry();
6456
this._alreadyActivatedEvents = Object.create(null);
6557
}
6658

67-
protected _triggerOnReady(): void {
68-
this._isReady = true;
69-
this._onReadyC(true);
70-
}
71-
72-
public onReady(): TPromise<boolean> {
73-
return this._onReady;
74-
}
75-
76-
public readExtensionPointContributions<T>(extPoint: IExtensionPoint<T>): TPromise<ExtensionPointContribution<T>[]> {
77-
return this.onReady().then(() => {
78-
let availableExtensions = this._registry.getAllExtensionDescriptions();
79-
80-
let result: ExtensionPointContribution<T>[] = [], resultLen = 0;
81-
for (let i = 0, len = availableExtensions.length; i < len; i++) {
82-
let desc = availableExtensions[i];
83-
84-
if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) {
85-
result[resultLen++] = new ExtensionPointContribution<T>(desc, desc.contributes[extPoint.name]);
86-
}
87-
}
88-
89-
return result;
90-
});
91-
}
92-
93-
public getExtensions(): TPromise<IExtensionDescription[]> {
94-
return this.onReady().then(() => {
95-
return this._registry.getAllExtensionDescriptions();
96-
});
59+
public isActivated(extensionId: string): boolean {
60+
return hasOwnProperty.call(this._activatedExtensions, extensionId);
9761
}
9862

99-
public getExtensionsStatus(): { [id: string]: IExtensionsStatus } {
100-
return null;
63+
public getActivatedExtension(extensionId: string): T {
64+
if (!hasOwnProperty.call(this._activatedExtensions, extensionId)) {
65+
throw new Error('Extension `' + extensionId + '` is not known or not activated');
66+
}
67+
return this._activatedExtensions[extensionId];
10168
}
10269

103-
public isActivated(extensionId: string): boolean {
104-
return hasOwnProperty.call(this._activatedExtensions, extensionId);
70+
public setActivatedExtension(extensionId: string, value: T): void {
71+
this._activatedExtensions[extensionId] = value;
10572
}
10673

10774
public activateByEvent(activationEvent: string): TPromise<void> {
10875
if (this._alreadyActivatedEvents[activationEvent]) {
10976
return NO_OP_VOID_PROMISE;
11077
}
111-
if (this._isReady) {
112-
return this._activateByEvent(activationEvent);
113-
} else {
114-
return this._onReady.then(() => this._activateByEvent(activationEvent));
115-
}
116-
}
117-
118-
private _activateByEvent(activationEvent: string): TPromise<void> {
11978
let activateExtensions = this._registry.getExtensionDescriptionsForActivationEvent(activationEvent);
12079
return this._activateExtensions(activateExtensions, 0).then(() => {
12180
this._alreadyActivatedEvents[activationEvent] = true;
12281
});
12382
}
12483

12584
public activateById(extensionId: string): TPromise<void> {
126-
return this._onReady.then(() => {
127-
let desc = this._registry.getExtensionDescription(extensionId);
128-
if (!desc) {
129-
throw new Error('Extension `' + extensionId + '` is not known');
130-
}
85+
let desc = this._registry.getExtensionDescription(extensionId);
86+
if (!desc) {
87+
throw new Error('Extension `' + extensionId + '` is not known');
88+
}
13189

132-
return this._activateExtensions([desc], 0);
133-
});
90+
return this._activateExtensions([desc], 0);
13491
}
13592

13693
/**
@@ -147,17 +104,17 @@ export abstract class AbstractExtensionService<T extends ActivatedExtension> imp
147104

148105
if (!depDesc) {
149106
// Error condition 1: unknown dependency
150-
this._showMessage(Severity.Error, nls.localize('unknownDep', "Extension `{1}` failed to activate. Reason: unknown dependency `{0}`.", depId, currentExtension.id));
151-
this._activatedExtensions[currentExtension.id] = this._createFailedExtension();
107+
this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Extension `{1}` failed to activate. Reason: unknown dependency `{0}`.", depId, currentExtension.id));
108+
this._activatedExtensions[currentExtension.id] = this._host.createFailedExtension();
152109
return;
153110
}
154111

155112
if (hasOwnProperty.call(this._activatedExtensions, depId)) {
156113
let dep = this._activatedExtensions[depId];
157114
if (dep.activationFailed) {
158115
// Error condition 2: a dependency has already failed activation
159-
this._showMessage(Severity.Error, nls.localize('failedDep1', "Extension `{1}` failed to activate. Reason: dependency `{0}` failed to activate.", depId, currentExtension.id));
160-
this._activatedExtensions[currentExtension.id] = this._createFailedExtension();
116+
this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Extension `{1}` failed to activate. Reason: dependency `{0}` failed to activate.", depId, currentExtension.id));
117+
this._activatedExtensions[currentExtension.id] = this._host.createFailedExtension();
161118
return;
162119
}
163120
} else {
@@ -189,8 +146,8 @@ export abstract class AbstractExtensionService<T extends ActivatedExtension> imp
189146
// More than 10 dependencies deep => most likely a dependency loop
190147
for (let i = 0, len = extensionDescriptions.length; i < len; i++) {
191148
// Error condition 3: dependency loop
192-
this._showMessage(Severity.Error, nls.localize('failedDep2', "Extension `{0}` failed to activate. Reason: more than 10 levels of dependencies (most likely a dependency loop).", extensionDescriptions[i].id));
193-
this._activatedExtensions[extensionDescriptions[i].id] = this._createFailedExtension();
149+
this._host.showMessage(Severity.Error, nls.localize('failedDep2', "Extension `{0}` failed to activate. Reason: more than 10 levels of dependencies (most likely a dependency loop).", extensionDescriptions[i].id));
150+
this._activatedExtensions[extensionDescriptions[i].id] = this._host.createFailedExtension();
194151
}
195152
return TPromise.as(void 0);
196153
}
@@ -224,7 +181,7 @@ export abstract class AbstractExtensionService<T extends ActivatedExtension> imp
224181
});
225182
}
226183

227-
protected _activateExtension(extensionDescription: IExtensionDescription): TPromise<void> {
184+
public _activateExtension(extensionDescription: IExtensionDescription): TPromise<void> {
228185
if (hasOwnProperty.call(this._activatedExtensions, extensionDescription.id)) {
229186
return TPromise.as(void 0);
230187
}
@@ -233,19 +190,91 @@ export abstract class AbstractExtensionService<T extends ActivatedExtension> imp
233190
return this._activatingExtensions[extensionDescription.id];
234191
}
235192

236-
this._activatingExtensions[extensionDescription.id] = this._actualActivateExtension(extensionDescription).then(null, (err) => {
237-
this._showMessage(Severity.Error, nls.localize('activationError', "Activating extension `{0}` failed: {1}.", extensionDescription.id, err.message));
193+
this._activatingExtensions[extensionDescription.id] = this._host.actualActivateExtension(extensionDescription).then(null, (err) => {
194+
this._host.showMessage(Severity.Error, nls.localize('activationError', "Activating extension `{0}` failed: {1}.", extensionDescription.id, err.message));
238195
console.error('Activating extension `' + extensionDescription.id + '` failed: ', err.message);
239196
console.log('Here is the error stack: ', err.stack);
240197
// Treat the extension as being empty
241-
return this._createFailedExtension();
198+
return this._host.createFailedExtension();
242199
}).then((x: T) => {
243200
this._activatedExtensions[extensionDescription.id] = x;
244201
delete this._activatingExtensions[extensionDescription.id];
245202
});
246203

247204
return this._activatingExtensions[extensionDescription.id];
248205
}
206+
}
207+
208+
export abstract class AbstractExtensionService<T extends ActivatedExtension> {
209+
public _serviceBrand: any;
210+
211+
private _onReady: TPromise<boolean>;
212+
private _onReadyC: (v: boolean) => void;
213+
private _isReady: boolean;
214+
protected _registry: ExtensionDescriptionRegistry;
215+
protected _manager: ExtensionsManager<T>;
216+
217+
constructor(isReadyByDefault: boolean) {
218+
if (isReadyByDefault) {
219+
this._isReady = true;
220+
this._onReady = TPromise.as(true);
221+
this._onReadyC = (v: boolean) => { /*no-op*/ };
222+
} else {
223+
this._isReady = false;
224+
this._onReady = new TPromise<boolean>((c, e, p) => {
225+
this._onReadyC = c;
226+
}, () => {
227+
console.warn('You should really not try to cancel this ready promise!');
228+
});
229+
}
230+
this._registry = new ExtensionDescriptionRegistry();
231+
this._manager = new ExtensionsManager(this._registry, {
232+
showMessage: (severity: Severity, message: string): void => {
233+
this._showMessage(severity, message);
234+
},
235+
236+
createFailedExtension: (): T => {
237+
return this._createFailedExtension();
238+
},
239+
240+
actualActivateExtension: (extensionDescription: IExtensionDescription): TPromise<T> => {
241+
return this._actualActivateExtension(extensionDescription);
242+
}
243+
});
244+
}
245+
246+
protected _triggerOnReady(): void {
247+
this._isReady = true;
248+
this._onReadyC(true);
249+
}
250+
251+
public onReady(): TPromise<boolean> {
252+
return this._onReady;
253+
}
254+
255+
public getExtensionsStatus(): { [id: string]: IExtensionsStatus } {
256+
return null;
257+
}
258+
259+
public isActivated(extensionId: string): boolean {
260+
return this._manager.isActivated(extensionId);
261+
}
262+
263+
public activateByEvent(activationEvent: string): TPromise<void> {
264+
if (this._isReady) {
265+
return this._manager.activateByEvent(activationEvent);
266+
} else {
267+
return this._onReady.then(() => this._manager.activateByEvent(activationEvent));
268+
}
269+
}
270+
271+
public activateById(extensionId: string): TPromise<void> {
272+
if (this._isReady) {
273+
return this._manager.activateById(extensionId);
274+
} else {
275+
return this._onReady.then(() => this._manager.activateById(extensionId));
276+
}
277+
}
249278

250279
protected abstract _showMessage(severity: Severity, message: string): void;
251280

src/vs/workbench/api/node/extHostExtensionService.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ import { createApiFactory, initializeExtensionApi } from 'vs/workbench/api/node/
1717
import { IThreadService } from 'vs/workbench/services/thread/common/threadService';
1818
import { MainContext, MainProcessExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData } from './extHost.protocol';
1919

20-
const hasOwnProperty = Object.hasOwnProperty;
21-
2220
/**
2321
* Represents the source code (module) of an extension.
2422
*/
@@ -201,16 +199,17 @@ export class ExtHostExtensionService extends AbstractExtensionService<ExtHostExt
201199
}
202200

203201
public get(extensionId: string): IExtensionAPI {
204-
if (!hasOwnProperty.call(this._activatedExtensions, extensionId)) {
205-
throw new Error('Extension `' + extensionId + '` is not known or not activated');
206-
}
207-
return this._activatedExtensions[extensionId].exports;
202+
return this._manager.getActivatedExtension(extensionId).exports;
208203
}
209204

210205
public deactivate(extensionId: string): TPromise<void> {
211206
let result: TPromise<void> = TPromise.as(void 0);
212207

213-
let extension = this._activatedExtensions[extensionId];
208+
if (!this._manager.isActivated(extensionId)) {
209+
return result;
210+
}
211+
212+
let extension = this._manager.getActivatedExtension(extensionId);
214213
if (!extension) {
215214
return result;
216215
}
@@ -333,7 +332,7 @@ export class ExtHostExtensionService extends AbstractExtensionService<ExtHostExt
333332
// -- called by main thread
334333

335334
public $activateExtension(extensionDescription: IExtensionDescription): TPromise<void> {
336-
return this._activateExtension(extensionDescription);
335+
return this._manager._activateExtension(extensionDescription);
337336
}
338337

339338
}

0 commit comments

Comments
 (0)