Skip to content

Commit d9b1bd4

Browse files
authored
Merge pull request microsoft#128532 from microsoft/sandy081/extensionManagement/participants
Run localizations cache update as extension management participants
2 parents 0d0a8f1 + 1c6fea5 commit d9b1bd4

File tree

6 files changed

+63
-45
lines changed

6 files changed

+63
-45
lines changed

src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import {
2020
InstallExtensionResult,
2121
UninstallOptions,
2222
IGalleryMetadata,
23-
StatisticType
23+
StatisticType,
24+
IExtensionManagementParticipant
2425
} from 'vs/platform/extensionManagement/common/extensionManagement';
2526
import { areSameExtensions, getMaliciousExtensionsSet, getGalleryExtensionTelemetryData, ExtensionIdentifierWithVersion, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
2627
import { Event, Emitter } from 'vs/base/common/event';
@@ -76,6 +77,8 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
7677
protected _onDidUninstallExtension = this._register(new Emitter<DidUninstallExtensionEvent>());
7778
onDidUninstallExtension: Event<DidUninstallExtensionEvent> = this._onDidUninstallExtension.event;
7879

80+
private readonly participants: IExtensionManagementParticipant[] = [];
81+
7982
constructor(
8083
@IExtensionGalleryService protected readonly galleryService: IExtensionGalleryService,
8184
@ITelemetryService protected readonly telemetryService: ITelemetryService,
@@ -152,6 +155,10 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
152155
return this.reportedExtensions;
153156
}
154157

158+
registerParticipant(participant: IExtensionManagementParticipant): void {
159+
this.participants.push(participant);
160+
}
161+
155162
protected async installExtension(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions & InstallVSIXOptions): Promise<ILocalExtension> {
156163
// only cache gallery extensions tasks
157164
if (!URI.isUri(extension)) {
@@ -217,10 +224,11 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
217224
}
218225

219226
// Install extensions in parallel and wait until all extensions are installed / failed
220-
const result = await Promise.allSettled(extensionsToInstall.map(async ({ task }) => {
227+
await this.joinAllSettled(extensionsToInstall.map(async ({ task }) => {
221228
const startTime = new Date().getTime();
222229
try {
223230
const local = await task.run();
231+
await this.joinAllSettled(this.participants.map(participant => participant.postInstall(local, task.source, options, CancellationToken.None)));
224232
if (!URI.isUri(task.source)) {
225233
reportTelemetry(this.telemetryService, task.operation === InstallOperation.Update ? 'extensionGallery:update' : 'extensionGallery:install', getGalleryExtensionTelemetryData(task.source), new Date().getTime() - startTime, undefined);
226234
}
@@ -234,11 +242,6 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
234242
throw error;
235243
} finally { extensionsToInstallMap.delete(task.identifier.id.toLowerCase()); }
236244
}));
237-
238-
// Collect the errors
239-
const errors = result.reduce<any[]>((errors, r) => { if (r.status === 'rejected') { errors.push(r.reason); } return errors; }, []);
240-
// If there are errors, throw the error.
241-
if (errors.length) { throw joinErrors(errors); }
242245
}
243246

244247
installResults.forEach(({ identifier }) => this.logService.info(`Extension installed successfully:`, identifier.id));
@@ -289,6 +292,22 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
289292
}
290293
}
291294

295+
private async joinAllSettled<T>(promises: Promise<T>[]): Promise<T[]> {
296+
const results: T[] = [];
297+
const errors: any[] = [];
298+
const promiseResults = await Promise.allSettled(promises);
299+
for (const r of promiseResults) {
300+
if (r.status === 'fulfilled') {
301+
results.push(r.value);
302+
} else {
303+
errors.push(r.reason);
304+
}
305+
}
306+
// If there are errors, throw the error.
307+
if (errors.length) { throw joinErrors(errors); }
308+
return results;
309+
}
310+
292311
private async getAllDepsAndPackExtensionsToInstall(extensionIdentifier: IExtensionIdentifier, manifest: IExtensionManifest, getOnlyNewlyAddedFromExtensionPack: boolean): Promise<{ gallery: IGalleryExtension, manifest: IExtensionManifest }[]> {
293312
if (!this.galleryService.isEnabled()) {
294313
return [];
@@ -413,9 +432,10 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
413432
}
414433

415434
// Uninstall extensions in parallel and wait until all extensions are uninstalled / failed
416-
const result = await Promise.allSettled(allTasks.map(async task => {
435+
await this.joinAllSettled(allTasks.map(async task => {
417436
try {
418437
await task.run();
438+
await this.joinAllSettled(this.participants.map(participant => participant.postUninstall(task.extension, options, CancellationToken.None)));
419439
// only report if extension has a mapped gallery extension. UUID identifies the gallery extension.
420440
if (task.extension.identifier.uuid) {
421441
try {
@@ -432,11 +452,6 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
432452
}
433453
}));
434454

435-
// Collect the errors
436-
const errors = result.reduce<any[]>((errors, r) => { if (r.status === 'rejected') { errors.push(r.reason); } return errors; }, []);
437-
// If there are errors, throw the error.
438-
if (errors.length) { throw joinErrors(errors); }
439-
440455
} catch (e) {
441456
const error = e instanceof ExtensionManagementError ? e : new ExtensionManagementError(getErrorMessage(e), ERROR_UNKNOWN);
442457
for (const task of allTasks) {

src/vs/platform/extensionManagement/common/extensionManagement.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,11 @@ export type InstallOptions = { isBuiltin?: boolean, isMachineScoped?: boolean, d
204204
export type InstallVSIXOptions = InstallOptions & { installOnlyNewlyAddedFromExtensionPack?: boolean };
205205
export type UninstallOptions = { donotIncludePack?: boolean, donotCheckDependents?: boolean };
206206

207+
export interface IExtensionManagementParticipant {
208+
postInstall(local: ILocalExtension, source: URI | IGalleryExtension, options: InstallOptions | InstallVSIXOptions, token: CancellationToken): Promise<void>;
209+
postUninstall(local: ILocalExtension, options: UninstallOptions, token: CancellationToken): Promise<void>;
210+
}
211+
207212
export const IExtensionManagementService = createDecorator<IExtensionManagementService>('extensionManagementService');
208213
export interface IExtensionManagementService {
209214
readonly _serviceBrand: undefined;
@@ -226,6 +231,8 @@ export interface IExtensionManagementService {
226231

227232
updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise<ILocalExtension>;
228233
updateExtensionScope(local: ILocalExtension, isMachineScoped: boolean): Promise<ILocalExtension>;
234+
235+
registerParticipant(pariticipant: IExtensionManagementParticipant): void;
229236
}
230237

231238
export const DISABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/disabled';

src/vs/platform/extensionManagement/common/extensionManagementIpc.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt
162162
getExtensionsReport(): Promise<IReportedExtension[]> {
163163
return Promise.resolve(this.channel.call('getExtensionsReport'));
164164
}
165+
166+
registerParticipant() { throw new Error('Not Supported'); }
165167
}
166168

167169
export class ExtensionTipsChannel implements IServerChannel {

src/vs/platform/localizations/common/localizations.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
7-
import { Event } from 'vs/base/common/event';
87

98
export interface ILocalization {
109
languageId: string;
@@ -22,8 +21,6 @@ export interface ITranslation {
2221
export const ILocalizationsService = createDecorator<ILocalizationsService>('localizationsService');
2322
export interface ILocalizationsService {
2423
readonly _serviceBrand: undefined;
25-
26-
readonly onDidLanguagesChange: Event<void>;
2724
getLanguageIds(): Promise<string[]>;
2825
}
2926

src/vs/platform/localizations/node/localizations.ts

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@
55

66
import { Promises } from 'vs/base/node/pfs';
77
import { createHash } from 'crypto';
8-
import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier, InstallExtensionResult } from 'vs/platform/extensionManagement/common/extensionManagement';
8+
import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
99
import { Disposable } from 'vs/base/common/lifecycle';
1010
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
1111
import { Queue } from 'vs/base/common/async';
1212
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
1313
import { ILogService } from 'vs/platform/log/common/log';
1414
import { isValidLocalization, ILocalizationsService } from 'vs/platform/localizations/common/localizations';
1515
import { distinct, equals } from 'vs/base/common/arrays';
16-
import { Event, Emitter } from 'vs/base/common/event';
1716
import { Schemas } from 'vs/base/common/network';
1817
import { join } from 'vs/base/common/path';
1918

@@ -32,47 +31,43 @@ export class LocalizationsService extends Disposable implements ILocalizationsSe
3231

3332
private readonly cache: LanguagePacksCache;
3433

35-
private readonly _onDidLanguagesChange: Emitter<void> = this._register(new Emitter<void>());
36-
readonly onDidLanguagesChange: Event<void> = this._onDidLanguagesChange.event;
37-
3834
constructor(
3935
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
4036
@INativeEnvironmentService environmentService: INativeEnvironmentService,
4137
@ILogService private readonly logService: ILogService
4238
) {
4339
super();
4440
this.cache = this._register(new LanguagePacksCache(environmentService, logService));
45-
46-
this._register(extensionManagementService.onDidInstallExtensions(e => this.onDidInstallExtensions(e)));
47-
this._register(extensionManagementService.onDidUninstallExtension(({ identifier }) => this.onDidUninstallExtension(identifier)));
41+
this.extensionManagementService.registerParticipant({
42+
postInstall: async (extension: ILocalExtension): Promise<void> => {
43+
return this.postInstallExtension(extension);
44+
},
45+
postUninstall: async (extension: ILocalExtension): Promise<void> => {
46+
return this.postUninstallExtension(extension);
47+
}
48+
});
4849
}
4950

50-
getLanguageIds(): Promise<string[]> {
51-
return this.cache.getLanguagePacks()
52-
.then(languagePacks => {
53-
// Contributed languages are those installed via extension packs, so does not include English
54-
const languages = ['en', ...Object.keys(languagePacks)];
55-
return distinct(languages);
56-
});
51+
async getLanguageIds(): Promise<string[]> {
52+
const languagePacks = await this.cache.getLanguagePacks();
53+
// Contributed languages are those installed via extension packs, so does not include English
54+
const languages = ['en', ...Object.keys(languagePacks)];
55+
return distinct(languages);
5756
}
5857

59-
private onDidInstallExtensions(results: readonly InstallExtensionResult[]): void {
60-
for (const { local: extension } of results) {
61-
if (extension && extension.manifest && extension.manifest.contributes && extension.manifest.contributes.localizations && extension.manifest.contributes.localizations.length) {
62-
this.logService.debug('Adding language packs from the extension', extension.identifier.id);
63-
this.update().then(changed => { if (changed) { this._onDidLanguagesChange.fire(); } });
64-
}
58+
private async postInstallExtension(extension: ILocalExtension): Promise<void> {
59+
if (extension && extension.manifest && extension.manifest.contributes && extension.manifest.contributes.localizations && extension.manifest.contributes.localizations.length) {
60+
this.logService.info('Adding language packs from the extension', extension.identifier.id);
61+
await this.update();
6562
}
6663
}
6764

68-
private onDidUninstallExtension(identifier: IExtensionIdentifier): void {
69-
this.cache.getLanguagePacks()
70-
.then(languagePacks => {
71-
if (Object.keys(languagePacks).some(language => languagePacks[language] && languagePacks[language].extensions.some(e => areSameExtensions(e.extensionIdentifier, identifier)))) {
72-
this.logService.debug('Removing language packs from the extension', identifier.id);
73-
this.update().then(changed => { if (changed) { this._onDidLanguagesChange.fire(); } });
74-
}
75-
});
65+
private async postUninstallExtension(extension: ILocalExtension): Promise<void> {
66+
const languagePacks = await this.cache.getLanguagePacks();
67+
if (Object.keys(languagePacks).some(language => languagePacks[language] && languagePacks[language].extensions.some(e => areSameExtensions(e.extensionIdentifier, extension.identifier)))) {
68+
this.logService.info('Removing language packs from the extension', extension.identifier.id);
69+
await this.update();
70+
}
7671
}
7772

7873
async update(): Promise<boolean> {

src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,4 +382,6 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
382382

383383
return Promise.resolve();
384384
}
385+
386+
registerParticipant() { throw new Error('Not Supported'); }
385387
}

0 commit comments

Comments
 (0)