未验证 提交 d9b1bd4a 编写于 作者: S Sandeep Somavarapu 提交者: GitHub

Merge pull request #128532 from microsoft/sandy081/extensionManagement/participants

Run localizations cache update as extension management participants
......@@ -20,7 +20,8 @@ import {
InstallExtensionResult,
UninstallOptions,
IGalleryMetadata,
StatisticType
StatisticType,
IExtensionManagementParticipant
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, getMaliciousExtensionsSet, getGalleryExtensionTelemetryData, ExtensionIdentifierWithVersion, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { Event, Emitter } from 'vs/base/common/event';
......@@ -76,6 +77,8 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
protected _onDidUninstallExtension = this._register(new Emitter<DidUninstallExtensionEvent>());
onDidUninstallExtension: Event<DidUninstallExtensionEvent> = this._onDidUninstallExtension.event;
private readonly participants: IExtensionManagementParticipant[] = [];
constructor(
@IExtensionGalleryService protected readonly galleryService: IExtensionGalleryService,
@ITelemetryService protected readonly telemetryService: ITelemetryService,
......@@ -152,6 +155,10 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
return this.reportedExtensions;
}
registerParticipant(participant: IExtensionManagementParticipant): void {
this.participants.push(participant);
}
protected async installExtension(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions & InstallVSIXOptions): Promise<ILocalExtension> {
// only cache gallery extensions tasks
if (!URI.isUri(extension)) {
......@@ -217,10 +224,11 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
}
// Install extensions in parallel and wait until all extensions are installed / failed
const result = await Promise.allSettled(extensionsToInstall.map(async ({ task }) => {
await this.joinAllSettled(extensionsToInstall.map(async ({ task }) => {
const startTime = new Date().getTime();
try {
const local = await task.run();
await this.joinAllSettled(this.participants.map(participant => participant.postInstall(local, task.source, options, CancellationToken.None)));
if (!URI.isUri(task.source)) {
reportTelemetry(this.telemetryService, task.operation === InstallOperation.Update ? 'extensionGallery:update' : 'extensionGallery:install', getGalleryExtensionTelemetryData(task.source), new Date().getTime() - startTime, undefined);
}
......@@ -234,11 +242,6 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
throw error;
} finally { extensionsToInstallMap.delete(task.identifier.id.toLowerCase()); }
}));
// Collect the errors
const errors = result.reduce<any[]>((errors, r) => { if (r.status === 'rejected') { errors.push(r.reason); } return errors; }, []);
// If there are errors, throw the error.
if (errors.length) { throw joinErrors(errors); }
}
installResults.forEach(({ identifier }) => this.logService.info(`Extension installed successfully:`, identifier.id));
......@@ -289,6 +292,22 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
}
}
private async joinAllSettled<T>(promises: Promise<T>[]): Promise<T[]> {
const results: T[] = [];
const errors: any[] = [];
const promiseResults = await Promise.allSettled(promises);
for (const r of promiseResults) {
if (r.status === 'fulfilled') {
results.push(r.value);
} else {
errors.push(r.reason);
}
}
// If there are errors, throw the error.
if (errors.length) { throw joinErrors(errors); }
return results;
}
private async getAllDepsAndPackExtensionsToInstall(extensionIdentifier: IExtensionIdentifier, manifest: IExtensionManifest, getOnlyNewlyAddedFromExtensionPack: boolean): Promise<{ gallery: IGalleryExtension, manifest: IExtensionManifest }[]> {
if (!this.galleryService.isEnabled()) {
return [];
......@@ -413,9 +432,10 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
}
// Uninstall extensions in parallel and wait until all extensions are uninstalled / failed
const result = await Promise.allSettled(allTasks.map(async task => {
await this.joinAllSettled(allTasks.map(async task => {
try {
await task.run();
await this.joinAllSettled(this.participants.map(participant => participant.postUninstall(task.extension, options, CancellationToken.None)));
// only report if extension has a mapped gallery extension. UUID identifies the gallery extension.
if (task.extension.identifier.uuid) {
try {
......@@ -432,11 +452,6 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
}
}));
// Collect the errors
const errors = result.reduce<any[]>((errors, r) => { if (r.status === 'rejected') { errors.push(r.reason); } return errors; }, []);
// If there are errors, throw the error.
if (errors.length) { throw joinErrors(errors); }
} catch (e) {
const error = e instanceof ExtensionManagementError ? e : new ExtensionManagementError(getErrorMessage(e), ERROR_UNKNOWN);
for (const task of allTasks) {
......
......@@ -204,6 +204,11 @@ export type InstallOptions = { isBuiltin?: boolean, isMachineScoped?: boolean, d
export type InstallVSIXOptions = InstallOptions & { installOnlyNewlyAddedFromExtensionPack?: boolean };
export type UninstallOptions = { donotIncludePack?: boolean, donotCheckDependents?: boolean };
export interface IExtensionManagementParticipant {
postInstall(local: ILocalExtension, source: URI | IGalleryExtension, options: InstallOptions | InstallVSIXOptions, token: CancellationToken): Promise<void>;
postUninstall(local: ILocalExtension, options: UninstallOptions, token: CancellationToken): Promise<void>;
}
export const IExtensionManagementService = createDecorator<IExtensionManagementService>('extensionManagementService');
export interface IExtensionManagementService {
readonly _serviceBrand: undefined;
......@@ -226,6 +231,8 @@ export interface IExtensionManagementService {
updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise<ILocalExtension>;
updateExtensionScope(local: ILocalExtension, isMachineScoped: boolean): Promise<ILocalExtension>;
registerParticipant(pariticipant: IExtensionManagementParticipant): void;
}
export const DISABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/disabled';
......
......@@ -162,6 +162,8 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt
getExtensionsReport(): Promise<IReportedExtension[]> {
return Promise.resolve(this.channel.call('getExtensionsReport'));
}
registerParticipant() { throw new Error('Not Supported'); }
}
export class ExtensionTipsChannel implements IServerChannel {
......
......@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
export interface ILocalization {
languageId: string;
......@@ -22,8 +21,6 @@ export interface ITranslation {
export const ILocalizationsService = createDecorator<ILocalizationsService>('localizationsService');
export interface ILocalizationsService {
readonly _serviceBrand: undefined;
readonly onDidLanguagesChange: Event<void>;
getLanguageIds(): Promise<string[]>;
}
......
......@@ -5,7 +5,7 @@
import { Promises } from 'vs/base/node/pfs';
import { createHash } from 'crypto';
import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier, InstallExtensionResult } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
import { Disposable } from 'vs/base/common/lifecycle';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { Queue } from 'vs/base/common/async';
......@@ -13,7 +13,6 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens
import { ILogService } from 'vs/platform/log/common/log';
import { isValidLocalization, ILocalizationsService } from 'vs/platform/localizations/common/localizations';
import { distinct, equals } from 'vs/base/common/arrays';
import { Event, Emitter } from 'vs/base/common/event';
import { Schemas } from 'vs/base/common/network';
import { join } from 'vs/base/common/path';
......@@ -32,9 +31,6 @@ export class LocalizationsService extends Disposable implements ILocalizationsSe
private readonly cache: LanguagePacksCache;
private readonly _onDidLanguagesChange: Emitter<void> = this._register(new Emitter<void>());
readonly onDidLanguagesChange: Event<void> = this._onDidLanguagesChange.event;
constructor(
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@INativeEnvironmentService environmentService: INativeEnvironmentService,
......@@ -42,37 +38,36 @@ export class LocalizationsService extends Disposable implements ILocalizationsSe
) {
super();
this.cache = this._register(new LanguagePacksCache(environmentService, logService));
this._register(extensionManagementService.onDidInstallExtensions(e => this.onDidInstallExtensions(e)));
this._register(extensionManagementService.onDidUninstallExtension(({ identifier }) => this.onDidUninstallExtension(identifier)));
this.extensionManagementService.registerParticipant({
postInstall: async (extension: ILocalExtension): Promise<void> => {
return this.postInstallExtension(extension);
},
postUninstall: async (extension: ILocalExtension): Promise<void> => {
return this.postUninstallExtension(extension);
}
});
}
getLanguageIds(): Promise<string[]> {
return this.cache.getLanguagePacks()
.then(languagePacks => {
// Contributed languages are those installed via extension packs, so does not include English
const languages = ['en', ...Object.keys(languagePacks)];
return distinct(languages);
});
async getLanguageIds(): Promise<string[]> {
const languagePacks = await this.cache.getLanguagePacks();
// Contributed languages are those installed via extension packs, so does not include English
const languages = ['en', ...Object.keys(languagePacks)];
return distinct(languages);
}
private onDidInstallExtensions(results: readonly InstallExtensionResult[]): void {
for (const { local: extension } of results) {
if (extension && extension.manifest && extension.manifest.contributes && extension.manifest.contributes.localizations && extension.manifest.contributes.localizations.length) {
this.logService.debug('Adding language packs from the extension', extension.identifier.id);
this.update().then(changed => { if (changed) { this._onDidLanguagesChange.fire(); } });
}
private async postInstallExtension(extension: ILocalExtension): Promise<void> {
if (extension && extension.manifest && extension.manifest.contributes && extension.manifest.contributes.localizations && extension.manifest.contributes.localizations.length) {
this.logService.info('Adding language packs from the extension', extension.identifier.id);
await this.update();
}
}
private onDidUninstallExtension(identifier: IExtensionIdentifier): void {
this.cache.getLanguagePacks()
.then(languagePacks => {
if (Object.keys(languagePacks).some(language => languagePacks[language] && languagePacks[language].extensions.some(e => areSameExtensions(e.extensionIdentifier, identifier)))) {
this.logService.debug('Removing language packs from the extension', identifier.id);
this.update().then(changed => { if (changed) { this._onDidLanguagesChange.fire(); } });
}
});
private async postUninstallExtension(extension: ILocalExtension): Promise<void> {
const languagePacks = await this.cache.getLanguagePacks();
if (Object.keys(languagePacks).some(language => languagePacks[language] && languagePacks[language].extensions.some(e => areSameExtensions(e.extensionIdentifier, extension.identifier)))) {
this.logService.info('Removing language packs from the extension', extension.identifier.id);
await this.update();
}
}
async update(): Promise<boolean> {
......
......@@ -382,4 +382,6 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
return Promise.resolve();
}
registerParticipant() { throw new Error('Not Supported'); }
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册