Path: blob/main/src/vs/workbench/services/extensionManagement/common/extensionManagementChannelClient.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { ILocalExtension, IGalleryExtension, InstallOptions, UninstallOptions, Metadata, InstallExtensionResult, InstallExtensionInfo, IProductVersion, UninstallExtensionInfo, DidUninstallExtensionEvent, DidUpdateExtensionMetadata, InstallExtensionEvent, UninstallExtensionEvent, IAllowedExtensionsService } from '../../../../platform/extensionManagement/common/extensionManagement.js';6import { URI } from '../../../../base/common/uri.js';7import { ExtensionIdentifier, ExtensionType, IExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js';8import { ExtensionManagementChannelClient as BaseExtensionManagementChannelClient } from '../../../../platform/extensionManagement/common/extensionManagementIpc.js';9import { IChannel } from '../../../../base/parts/ipc/common/ipc.js';10import { DidChangeUserDataProfileEvent, IUserDataProfileService } from '../../userDataProfile/common/userDataProfile.js';11import { Emitter } from '../../../../base/common/event.js';12import { delta } from '../../../../base/common/arrays.js';13import { compare } from '../../../../base/common/strings.js';14import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';15import { DidChangeProfileEvent, IProfileAwareExtensionManagementService } from './extensionManagement.js';16import { IProductService } from '../../../../platform/product/common/productService.js';1718export abstract class ProfileAwareExtensionManagementChannelClient extends BaseExtensionManagementChannelClient implements IProfileAwareExtensionManagementService {1920private readonly _onDidChangeProfile = this._register(new Emitter<{ readonly added: ILocalExtension[]; readonly removed: ILocalExtension[] }>());21readonly onDidChangeProfile = this._onDidChangeProfile.event;2223private readonly _onDidProfileAwareInstallExtensions = this._register(new Emitter<readonly InstallExtensionResult[]>());24get onProfileAwareDidInstallExtensions() { return this._onDidProfileAwareInstallExtensions.event; }2526private readonly _onDidProfileAwareUninstallExtension = this._register(new Emitter<DidUninstallExtensionEvent>());27get onProfileAwareDidUninstallExtension() { return this._onDidProfileAwareUninstallExtension.event; }2829private readonly _onDidProfileAwareUpdateExtensionMetadata = this._register(new Emitter<DidUpdateExtensionMetadata>());30get onProfileAwareDidUpdateExtensionMetadata() { return this._onDidProfileAwareUpdateExtensionMetadata.event; }3132constructor(channel: IChannel,33productService: IProductService,34allowedExtensionsService: IAllowedExtensionsService,35protected readonly userDataProfileService: IUserDataProfileService,36protected readonly uriIdentityService: IUriIdentityService,37) {38super(channel, productService, allowedExtensionsService);39this._register(userDataProfileService.onDidChangeCurrentProfile(e => {40if (!this.uriIdentityService.extUri.isEqual(e.previous.extensionsResource, e.profile.extensionsResource)) {41e.join(this.whenProfileChanged(e));42}43}));44}4546protected override async onInstallExtensionEvent(data: InstallExtensionEvent): Promise<void> {47const result = this.filterEvent(data.profileLocation, data.applicationScoped ?? false);48if (result instanceof Promise ? await result : result) {49this._onInstallExtension.fire(data);50}51}5253protected override async onDidInstallExtensionsEvent(results: readonly InstallExtensionResult[]): Promise<void> {54const filtered = [];55for (const e of results) {56const result = this.filterEvent(e.profileLocation, e.applicationScoped ?? e.local?.isApplicationScoped ?? false);57if (result instanceof Promise ? await result : result) {58filtered.push(e);59}60}61if (filtered.length) {62this._onDidInstallExtensions.fire(filtered);63}64this._onDidProfileAwareInstallExtensions.fire(results);65}6667protected override async onUninstallExtensionEvent(data: UninstallExtensionEvent): Promise<void> {68const result = this.filterEvent(data.profileLocation, data.applicationScoped ?? false);69if (result instanceof Promise ? await result : result) {70this._onUninstallExtension.fire(data);71}72}7374protected override async onDidUninstallExtensionEvent(data: DidUninstallExtensionEvent): Promise<void> {75const result = this.filterEvent(data.profileLocation, data.applicationScoped ?? false);76if (result instanceof Promise ? await result : result) {77this._onDidUninstallExtension.fire(data);78}79this._onDidProfileAwareUninstallExtension.fire(data);80}8182protected override async onDidUpdateExtensionMetadataEvent(data: DidUpdateExtensionMetadata): Promise<void> {83const result = this.filterEvent(data.profileLocation, data.local?.isApplicationScoped ?? false);84if (result instanceof Promise ? await result : result) {85this._onDidUpdateExtensionMetadata.fire(data);86}87this._onDidProfileAwareUpdateExtensionMetadata.fire(data);88}8990override async install(vsix: URI, installOptions?: InstallOptions): Promise<ILocalExtension> {91installOptions = { ...installOptions, profileLocation: await this.getProfileLocation(installOptions?.profileLocation) };92return super.install(vsix, installOptions);93}9495override async installFromLocation(location: URI, profileLocation: URI): Promise<ILocalExtension> {96return super.installFromLocation(location, await this.getProfileLocation(profileLocation));97}9899override async installFromGallery(extension: IGalleryExtension, installOptions?: InstallOptions): Promise<ILocalExtension> {100installOptions = { ...installOptions, profileLocation: await this.getProfileLocation(installOptions?.profileLocation) };101return super.installFromGallery(extension, installOptions);102}103104override async installGalleryExtensions(extensions: InstallExtensionInfo[]): Promise<InstallExtensionResult[]> {105const infos: InstallExtensionInfo[] = [];106for (const extension of extensions) {107infos.push({ ...extension, options: { ...extension.options, profileLocation: await this.getProfileLocation(extension.options?.profileLocation) } });108}109return super.installGalleryExtensions(infos);110}111112override async uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise<void> {113options = { ...options, profileLocation: await this.getProfileLocation(options?.profileLocation) };114return super.uninstall(extension, options);115}116117override async uninstallExtensions(extensions: UninstallExtensionInfo[]): Promise<void> {118const infos: UninstallExtensionInfo[] = [];119for (const { extension, options } of extensions) {120infos.push({ extension, options: { ...options, profileLocation: await this.getProfileLocation(options?.profileLocation) } });121}122return super.uninstallExtensions(infos);123}124125override async getInstalled(type: ExtensionType | null = null, extensionsProfileResource?: URI, productVersion?: IProductVersion): Promise<ILocalExtension[]> {126return super.getInstalled(type, await this.getProfileLocation(extensionsProfileResource), productVersion);127}128129override async updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, extensionsProfileResource?: URI): Promise<ILocalExtension> {130return super.updateMetadata(local, metadata, await this.getProfileLocation(extensionsProfileResource));131}132133override async toggleApplicationScope(local: ILocalExtension, fromProfileLocation: URI): Promise<ILocalExtension> {134return super.toggleApplicationScope(local, await this.getProfileLocation(fromProfileLocation));135}136137override async copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void> {138return super.copyExtensions(await this.getProfileLocation(fromProfileLocation), await this.getProfileLocation(toProfileLocation));139}140141private async whenProfileChanged(e: DidChangeUserDataProfileEvent): Promise<void> {142const previousProfileLocation = await this.getProfileLocation(e.previous.extensionsResource);143const currentProfileLocation = await this.getProfileLocation(e.profile.extensionsResource);144145if (this.uriIdentityService.extUri.isEqual(previousProfileLocation, currentProfileLocation)) {146return;147}148149const eventData = await this.switchExtensionsProfile(previousProfileLocation, currentProfileLocation);150this._onDidChangeProfile.fire(eventData);151}152153protected async switchExtensionsProfile(previousProfileLocation: URI, currentProfileLocation: URI, preserveExtensions?: ExtensionIdentifier[]): Promise<DidChangeProfileEvent> {154const oldExtensions = await this.getInstalled(ExtensionType.User, previousProfileLocation);155const newExtensions = await this.getInstalled(ExtensionType.User, currentProfileLocation);156if (preserveExtensions?.length) {157const extensionsToInstall: IExtensionIdentifier[] = [];158for (const extension of oldExtensions) {159if (preserveExtensions.some(id => ExtensionIdentifier.equals(extension.identifier.id, id)) &&160!newExtensions.some(e => ExtensionIdentifier.equals(e.identifier.id, extension.identifier.id))) {161extensionsToInstall.push(extension.identifier);162}163}164if (extensionsToInstall.length) {165await this.installExtensionsFromProfile(extensionsToInstall, previousProfileLocation, currentProfileLocation);166}167}168return delta(oldExtensions, newExtensions, (a, b) => compare(`${ExtensionIdentifier.toKey(a.identifier.id)}@${a.manifest.version}`, `${ExtensionIdentifier.toKey(b.identifier.id)}@${b.manifest.version}`));169}170171protected getProfileLocation(profileLocation: URI): Promise<URI>;172protected getProfileLocation(profileLocation?: URI): Promise<URI | undefined>;173protected async getProfileLocation(profileLocation?: URI): Promise<URI | undefined> {174return profileLocation ?? this.userDataProfileService.currentProfile.extensionsResource;175}176177protected abstract filterEvent(profileLocation: URI, isApplicationScoped: boolean): boolean | Promise<boolean>;178}179180181