Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/extensionManagement/common/extensionManagementChannelClient.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 { ILocalExtension, IGalleryExtension, InstallOptions, UninstallOptions, Metadata, InstallExtensionResult, InstallExtensionInfo, IProductVersion, UninstallExtensionInfo, DidUninstallExtensionEvent, DidUpdateExtensionMetadata, InstallExtensionEvent, UninstallExtensionEvent, IAllowedExtensionsService } from '../../../../platform/extensionManagement/common/extensionManagement.js';
7
import { URI } from '../../../../base/common/uri.js';
8
import { ExtensionIdentifier, ExtensionType, IExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js';
9
import { ExtensionManagementChannelClient as BaseExtensionManagementChannelClient } from '../../../../platform/extensionManagement/common/extensionManagementIpc.js';
10
import { IChannel } from '../../../../base/parts/ipc/common/ipc.js';
11
import { DidChangeUserDataProfileEvent, IUserDataProfileService } from '../../userDataProfile/common/userDataProfile.js';
12
import { Emitter } from '../../../../base/common/event.js';
13
import { delta } from '../../../../base/common/arrays.js';
14
import { compare } from '../../../../base/common/strings.js';
15
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
16
import { DidChangeProfileEvent, IProfileAwareExtensionManagementService } from './extensionManagement.js';
17
import { IProductService } from '../../../../platform/product/common/productService.js';
18
19
export abstract class ProfileAwareExtensionManagementChannelClient extends BaseExtensionManagementChannelClient implements IProfileAwareExtensionManagementService {
20
21
private readonly _onDidChangeProfile = this._register(new Emitter<{ readonly added: ILocalExtension[]; readonly removed: ILocalExtension[] }>());
22
readonly onDidChangeProfile = this._onDidChangeProfile.event;
23
24
private readonly _onDidProfileAwareInstallExtensions = this._register(new Emitter<readonly InstallExtensionResult[]>());
25
get onProfileAwareDidInstallExtensions() { return this._onDidProfileAwareInstallExtensions.event; }
26
27
private readonly _onDidProfileAwareUninstallExtension = this._register(new Emitter<DidUninstallExtensionEvent>());
28
get onProfileAwareDidUninstallExtension() { return this._onDidProfileAwareUninstallExtension.event; }
29
30
private readonly _onDidProfileAwareUpdateExtensionMetadata = this._register(new Emitter<DidUpdateExtensionMetadata>());
31
get onProfileAwareDidUpdateExtensionMetadata() { return this._onDidProfileAwareUpdateExtensionMetadata.event; }
32
33
constructor(channel: IChannel,
34
productService: IProductService,
35
allowedExtensionsService: IAllowedExtensionsService,
36
protected readonly userDataProfileService: IUserDataProfileService,
37
protected readonly uriIdentityService: IUriIdentityService,
38
) {
39
super(channel, productService, allowedExtensionsService);
40
this._register(userDataProfileService.onDidChangeCurrentProfile(e => {
41
if (!this.uriIdentityService.extUri.isEqual(e.previous.extensionsResource, e.profile.extensionsResource)) {
42
e.join(this.whenProfileChanged(e));
43
}
44
}));
45
}
46
47
protected override async onInstallExtensionEvent(data: InstallExtensionEvent): Promise<void> {
48
const result = this.filterEvent(data.profileLocation, data.applicationScoped ?? false);
49
if (result instanceof Promise ? await result : result) {
50
this._onInstallExtension.fire(data);
51
}
52
}
53
54
protected override async onDidInstallExtensionsEvent(results: readonly InstallExtensionResult[]): Promise<void> {
55
const filtered = [];
56
for (const e of results) {
57
const result = this.filterEvent(e.profileLocation, e.applicationScoped ?? e.local?.isApplicationScoped ?? false);
58
if (result instanceof Promise ? await result : result) {
59
filtered.push(e);
60
}
61
}
62
if (filtered.length) {
63
this._onDidInstallExtensions.fire(filtered);
64
}
65
this._onDidProfileAwareInstallExtensions.fire(results);
66
}
67
68
protected override async onUninstallExtensionEvent(data: UninstallExtensionEvent): Promise<void> {
69
const result = this.filterEvent(data.profileLocation, data.applicationScoped ?? false);
70
if (result instanceof Promise ? await result : result) {
71
this._onUninstallExtension.fire(data);
72
}
73
}
74
75
protected override async onDidUninstallExtensionEvent(data: DidUninstallExtensionEvent): Promise<void> {
76
const result = this.filterEvent(data.profileLocation, data.applicationScoped ?? false);
77
if (result instanceof Promise ? await result : result) {
78
this._onDidUninstallExtension.fire(data);
79
}
80
this._onDidProfileAwareUninstallExtension.fire(data);
81
}
82
83
protected override async onDidUpdateExtensionMetadataEvent(data: DidUpdateExtensionMetadata): Promise<void> {
84
const result = this.filterEvent(data.profileLocation, data.local?.isApplicationScoped ?? false);
85
if (result instanceof Promise ? await result : result) {
86
this._onDidUpdateExtensionMetadata.fire(data);
87
}
88
this._onDidProfileAwareUpdateExtensionMetadata.fire(data);
89
}
90
91
override async install(vsix: URI, installOptions?: InstallOptions): Promise<ILocalExtension> {
92
installOptions = { ...installOptions, profileLocation: await this.getProfileLocation(installOptions?.profileLocation) };
93
return super.install(vsix, installOptions);
94
}
95
96
override async installFromLocation(location: URI, profileLocation: URI): Promise<ILocalExtension> {
97
return super.installFromLocation(location, await this.getProfileLocation(profileLocation));
98
}
99
100
override async installFromGallery(extension: IGalleryExtension, installOptions?: InstallOptions): Promise<ILocalExtension> {
101
installOptions = { ...installOptions, profileLocation: await this.getProfileLocation(installOptions?.profileLocation) };
102
return super.installFromGallery(extension, installOptions);
103
}
104
105
override async installGalleryExtensions(extensions: InstallExtensionInfo[]): Promise<InstallExtensionResult[]> {
106
const infos: InstallExtensionInfo[] = [];
107
for (const extension of extensions) {
108
infos.push({ ...extension, options: { ...extension.options, profileLocation: await this.getProfileLocation(extension.options?.profileLocation) } });
109
}
110
return super.installGalleryExtensions(infos);
111
}
112
113
override async uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise<void> {
114
options = { ...options, profileLocation: await this.getProfileLocation(options?.profileLocation) };
115
return super.uninstall(extension, options);
116
}
117
118
override async uninstallExtensions(extensions: UninstallExtensionInfo[]): Promise<void> {
119
const infos: UninstallExtensionInfo[] = [];
120
for (const { extension, options } of extensions) {
121
infos.push({ extension, options: { ...options, profileLocation: await this.getProfileLocation(options?.profileLocation) } });
122
}
123
return super.uninstallExtensions(infos);
124
}
125
126
override async getInstalled(type: ExtensionType | null = null, extensionsProfileResource?: URI, productVersion?: IProductVersion): Promise<ILocalExtension[]> {
127
return super.getInstalled(type, await this.getProfileLocation(extensionsProfileResource), productVersion);
128
}
129
130
override async updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, extensionsProfileResource?: URI): Promise<ILocalExtension> {
131
return super.updateMetadata(local, metadata, await this.getProfileLocation(extensionsProfileResource));
132
}
133
134
override async toggleApplicationScope(local: ILocalExtension, fromProfileLocation: URI): Promise<ILocalExtension> {
135
return super.toggleApplicationScope(local, await this.getProfileLocation(fromProfileLocation));
136
}
137
138
override async copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void> {
139
return super.copyExtensions(await this.getProfileLocation(fromProfileLocation), await this.getProfileLocation(toProfileLocation));
140
}
141
142
private async whenProfileChanged(e: DidChangeUserDataProfileEvent): Promise<void> {
143
const previousProfileLocation = await this.getProfileLocation(e.previous.extensionsResource);
144
const currentProfileLocation = await this.getProfileLocation(e.profile.extensionsResource);
145
146
if (this.uriIdentityService.extUri.isEqual(previousProfileLocation, currentProfileLocation)) {
147
return;
148
}
149
150
const eventData = await this.switchExtensionsProfile(previousProfileLocation, currentProfileLocation);
151
this._onDidChangeProfile.fire(eventData);
152
}
153
154
protected async switchExtensionsProfile(previousProfileLocation: URI, currentProfileLocation: URI, preserveExtensions?: ExtensionIdentifier[]): Promise<DidChangeProfileEvent> {
155
const oldExtensions = await this.getInstalled(ExtensionType.User, previousProfileLocation);
156
const newExtensions = await this.getInstalled(ExtensionType.User, currentProfileLocation);
157
if (preserveExtensions?.length) {
158
const extensionsToInstall: IExtensionIdentifier[] = [];
159
for (const extension of oldExtensions) {
160
if (preserveExtensions.some(id => ExtensionIdentifier.equals(extension.identifier.id, id)) &&
161
!newExtensions.some(e => ExtensionIdentifier.equals(e.identifier.id, extension.identifier.id))) {
162
extensionsToInstall.push(extension.identifier);
163
}
164
}
165
if (extensionsToInstall.length) {
166
await this.installExtensionsFromProfile(extensionsToInstall, previousProfileLocation, currentProfileLocation);
167
}
168
}
169
return delta(oldExtensions, newExtensions, (a, b) => compare(`${ExtensionIdentifier.toKey(a.identifier.id)}@${a.manifest.version}`, `${ExtensionIdentifier.toKey(b.identifier.id)}@${b.manifest.version}`));
170
}
171
172
protected getProfileLocation(profileLocation: URI): Promise<URI>;
173
protected getProfileLocation(profileLocation?: URI): Promise<URI | undefined>;
174
protected async getProfileLocation(profileLocation?: URI): Promise<URI | undefined> {
175
return profileLocation ?? this.userDataProfileService.currentProfile.extensionsResource;
176
}
177
178
protected abstract filterEvent(profileLocation: URI, isApplicationScoped: boolean): boolean | Promise<boolean>;
179
}
180
181