Skip to content

Commit 6f672e5

Browse files
committed
scanner service: take profile as input
1 parent d4447ae commit 6f672e5

File tree

7 files changed

+118
-88
lines changed

7 files changed

+118
-88
lines changed

src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts

Lines changed: 50 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { IBuiltinExtensionsScannerService, ExtensionType, IExtensionIdentifier, IExtension, IExtensionManifest, TargetPlatform, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
6+
import { IBuiltinExtensionsScannerService, ExtensionType, IExtensionIdentifier, IExtension, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
77
import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
88
import { IScannedExtension, IWebExtensionsScannerService, ScanOptions } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
99
import { isWeb, Language } from 'vs/base/common/platform';
@@ -33,17 +33,16 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
3333
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
3434
import { basename } from 'vs/base/common/path';
3535
import { IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage';
36-
import { delta, isNonEmptyArray } from 'vs/base/common/arrays';
36+
import { isNonEmptyArray } from 'vs/base/common/arrays';
3737
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
3838
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
3939
import { IProductService } from 'vs/platform/product/common/productService';
4040
import { validateExtensionManifest } from 'vs/platform/extensions/common/extensionValidator';
4141
import Severity from 'vs/base/common/severity';
4242
import { IStringDictionary } from 'vs/base/common/collections';
43-
import { DidChangeUserDataProfileEvent, IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
44-
import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
45-
import { Emitter } from 'vs/base/common/event';
46-
import { compare } from 'vs/base/common/strings';
43+
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
44+
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
45+
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
4746

4847
type GalleryExtensionInfo = { readonly id: string; preRelease?: boolean; migrateStorageFrom?: string };
4948
type ExtensionInfo = { readonly id: string; preRelease: boolean };
@@ -89,9 +88,6 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
8988
private readonly customBuiltinExtensionsCacheResource: URI | undefined = undefined;
9089
private readonly resourcesAccessQueueMap = new ResourceMap<Queue<IWebExtension[]>>();
9190

92-
private readonly _onDidChangeProfileExtensions = this._register(new Emitter<{ readonly added: IScannedExtension[]; readonly removed: IScannedExtension[] }>());
93-
readonly onDidChangeProfileExtensions = this._onDidChangeProfileExtensions.event;
94-
9591
constructor(
9692
@IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService,
9793
@IBuiltinExtensionsScannerService private readonly builtinExtensionsScannerService: IBuiltinExtensionsScannerService,
@@ -103,8 +99,8 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
10399
@IExtensionStorageService private readonly extensionStorageService: IExtensionStorageService,
104100
@IStorageService private readonly storageService: IStorageService,
105101
@IProductService private readonly productService: IProductService,
106-
@IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService,
107102
@IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService,
103+
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
108104
@ILifecycleService lifecycleService: ILifecycleService,
109105
) {
110106
super();
@@ -115,7 +111,6 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
115111

116112
// Eventually update caches
117113
lifecycleService.when(LifecyclePhase.Eventually).then(() => this.updateCaches());
118-
this._register(userDataProfileService.onDidChangeCurrentProfile(e => e.join(this.whenProfileChanged(e))));
119114
}
120115
}
121116

@@ -377,7 +372,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
377372
return this.readSystemExtensions();
378373
}
379374

380-
async scanUserExtensions(scanOptions?: ScanOptions): Promise<IScannedExtension[]> {
375+
async scanUserExtensions(profileLocation?: URI, scanOptions?: ScanOptions): Promise<IScannedExtension[]> {
381376
const extensions = new Map<string, IScannedExtension>();
382377

383378
// Custom builtin extensions defined through `additionalBuiltinExtensions` API
@@ -387,7 +382,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
387382
}
388383

389384
// User Installed extensions
390-
const installedExtensions = await this.scanInstalledExtensions(this.userDataProfileService.currentProfile, scanOptions);
385+
const installedExtensions = await this.scanInstalledExtensions(profileLocation, scanOptions);
391386
for (const extension of installedExtensions) {
392387
extensions.set(extension.identifier.id.toLowerCase(), extension);
393388
}
@@ -416,17 +411,17 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
416411
return result;
417412
}
418413

419-
async scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType): Promise<IScannedExtension | null> {
414+
async scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType, profileLocation?: URI): Promise<IScannedExtension | null> {
420415
if (extensionType === ExtensionType.System) {
421416
const systemExtensions = await this.scanSystemExtensions();
422417
return systemExtensions.find(e => e.location.toString() === extensionLocation.toString()) || null;
423418
}
424-
const userExtensions = await this.scanUserExtensions();
419+
const userExtensions = await this.scanUserExtensions(profileLocation);
425420
return userExtensions.find(e => e.location.toString() === extensionLocation.toString()) || null;
426421
}
427422

428-
async scanMetadata(extensionLocation: URI): Promise<Metadata | undefined> {
429-
const extension = await this.scanExistingExtension(extensionLocation, ExtensionType.User);
423+
async scanMetadata(extensionLocation: URI, profileLocation?: URI): Promise<Metadata | undefined> {
424+
const extension = await this.scanExistingExtension(extensionLocation, ExtensionType.User, profileLocation);
430425
return extension?.metadata;
431426
}
432427

@@ -443,26 +438,38 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
443438
return null;
444439
}
445440

446-
async addExtensionFromGallery(galleryExtension: IGalleryExtension, metadata?: Metadata): Promise<IScannedExtension> {
441+
async addExtensionFromGallery(galleryExtension: IGalleryExtension, metadata: Metadata, profileLocation?: URI): Promise<IScannedExtension> {
447442
const webExtension = await this.toWebExtensionFromGallery(galleryExtension, metadata);
448-
return this.addWebExtension(webExtension);
443+
return this.addWebExtension(webExtension, profileLocation);
449444
}
450445

451-
async addExtension(location: URI, metadata?: Metadata): Promise<IScannedExtension> {
446+
async addExtension(location: URI, metadata: Metadata, profileLocation?: URI): Promise<IScannedExtension> {
452447
const webExtension = await this.toWebExtension(location, undefined, undefined, undefined, undefined, undefined, metadata);
453-
return this.addWebExtension(webExtension);
448+
return this.addWebExtension(webExtension, profileLocation);
454449
}
455450

456-
async removeExtension(extension: IScannedExtension): Promise<void> {
457-
const profile = extension.metadata?.isApplicationScoped ? this.userDataProfilesService.defaultProfile : this.userDataProfileService.currentProfile;
458-
await this.writeInstalledExtensions(profile, installedExtensions => installedExtensions.filter(installedExtension => !areSameExtensions(installedExtension.identifier, extension.identifier)));
451+
async removeExtension(extension: IScannedExtension, profileLocation?: URI): Promise<void> {
452+
await this.writeInstalledExtensions(profileLocation, installedExtensions => installedExtensions.filter(installedExtension => !areSameExtensions(installedExtension.identifier, extension.identifier)));
459453
}
460454

461-
private async addWebExtension(webExtension: IWebExtension): Promise<IScannedExtension> {
455+
async copyExtensions(fromProfileLocation: URI, toProfileLocation: URI, filter: (extension: IScannedExtension) => boolean): Promise<void> {
456+
const extensionsToCopy: IWebExtension[] = [];
457+
const fromWebExtensions = await this.readInstalledExtensions(fromProfileLocation);
458+
await Promise.all(fromWebExtensions.map(async webExtension => {
459+
const scannedExtension = await this.toScannedExtension(webExtension, false);
460+
if (filter(scannedExtension)) {
461+
extensionsToCopy.push(webExtension);
462+
}
463+
}));
464+
if (extensionsToCopy.length) {
465+
await this.addToInstalledExtensions(extensionsToCopy, toProfileLocation);
466+
}
467+
}
468+
469+
private async addWebExtension(webExtension: IWebExtension, profileLocation?: URI): Promise<IScannedExtension> {
462470
const isSystem = !!(await this.scanSystemExtensions()).find(e => areSameExtensions(e.identifier, webExtension.identifier));
463471
const isBuiltin = !!webExtension.metadata?.isBuiltin;
464472
const extension = await this.toScannedExtension(webExtension, isBuiltin);
465-
const profile = webExtension.metadata?.isApplicationScoped ? this.userDataProfilesService.defaultProfile : this.userDataProfileService.currentProfile;
466473

467474
if (isSystem) {
468475
await this.writeSystemExtensionsCache(systemExtensions => {
@@ -483,37 +490,37 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
483490
return customBuiltinExtensions;
484491
});
485492

486-
const installedExtensions = await this.readInstalledExtensions(profile);
493+
const installedExtensions = await this.readInstalledExtensions(profileLocation);
487494
// Also add to installed extensions if it is installed to update its version
488495
if (installedExtensions.some(e => areSameExtensions(e.identifier, webExtension.identifier))) {
489-
await this.addToInstalledExtensions([webExtension], profile);
496+
await this.addToInstalledExtensions([webExtension], profileLocation);
490497
}
491498
return extension;
492499
}
493500

494501
// Add to installed extensions
495-
await this.addToInstalledExtensions([webExtension], profile);
502+
await this.addToInstalledExtensions([webExtension], profileLocation);
496503
return extension;
497504
}
498505

499-
private async addToInstalledExtensions(webExtensions: IWebExtension[], profile: IUserDataProfile): Promise<void> {
500-
await this.writeInstalledExtensions(profile, installedExtensions => {
506+
private async addToInstalledExtensions(webExtensions: IWebExtension[], profileLocation?: URI): Promise<void> {
507+
await this.writeInstalledExtensions(profileLocation, installedExtensions => {
501508
// Remove the existing extension to avoid duplicates
502509
installedExtensions = installedExtensions.filter(installedExtension => webExtensions.some(extension => !areSameExtensions(installedExtension.identifier, extension.identifier)));
503510
installedExtensions.push(...webExtensions);
504511
return installedExtensions;
505512
});
506513
}
507514

508-
private async scanInstalledExtensions(profile: IUserDataProfile, scanOptions?: ScanOptions): Promise<IScannedExtension[]> {
509-
let installedExtensions = await this.readInstalledExtensions(profile);
515+
private async scanInstalledExtensions(profileLocation?: URI, scanOptions?: ScanOptions): Promise<IScannedExtension[]> {
516+
let installedExtensions = await this.readInstalledExtensions(profileLocation);
510517

511518
// If current profile is not a default profile, then add the application extensions to the list
512-
if (!profile.isDefault) {
519+
if (this.userDataProfilesService.defaultProfile.extensionsResource && !this.uriIdentityService.extUri.isEqual(profileLocation, this.userDataProfilesService.defaultProfile.extensionsResource)) {
513520
// Remove application extensions from the non default profile
514521
installedExtensions = installedExtensions.filter(i => !i.metadata?.isApplicationScoped);
515522
// Add application extensions from the default profile to the list
516-
const defaultProfileExtensions = await this.readInstalledExtensions(this.userDataProfilesService.defaultProfile);
523+
const defaultProfileExtensions = await this.readInstalledExtensions(this.userDataProfilesService.defaultProfile.extensionsResource);
517524
installedExtensions.push(...defaultProfileExtensions.filter(i => i.metadata?.isApplicationScoped));
518525
}
519526

@@ -713,15 +720,15 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
713720
return this._migratePackageNLSUrisPromise;
714721
}
715722

716-
private async readInstalledExtensions(profile: IUserDataProfile): Promise<IWebExtension[]> {
717-
if (profile.isDefault) {
723+
private async readInstalledExtensions(profileLocation?: URI): Promise<IWebExtension[]> {
724+
if (this.uriIdentityService.extUri.isEqual(profileLocation, this.userDataProfilesService.defaultProfile.extensionsResource)) {
718725
await this.migratePackageNLSUris();
719726
}
720-
return this.withWebExtensions(profile.extensionsResource);
727+
return this.withWebExtensions(profileLocation);
721728
}
722729

723-
private writeInstalledExtensions(profile: IUserDataProfile, updateFn: (extensions: IWebExtension[]) => IWebExtension[]): Promise<IWebExtension[]> {
724-
return this.withWebExtensions(profile.extensionsResource, updateFn);
730+
private writeInstalledExtensions(profileLocation: URI | undefined, updateFn: (extensions: IWebExtension[]) => IWebExtension[]): Promise<IWebExtension[]> {
731+
return this.withWebExtensions(profileLocation, updateFn);
725732
}
726733

727734
private readCustomBuiltinExtensionsCache(): Promise<IWebExtension[]> {
@@ -819,7 +826,6 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
819826
}
820827

821828
private registerActions(): void {
822-
const that = this;
823829
this._register(registerAction2(class extends Action2 {
824830
constructor() {
825831
super({
@@ -831,24 +837,13 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
831837
});
832838
}
833839
run(serviceAccessor: ServicesAccessor): void {
834-
serviceAccessor.get(IEditorService).openEditor({ resource: that.userDataProfileService.currentProfile.extensionsResource });
840+
const editorService = serviceAccessor.get(IEditorService);
841+
const userDataProfileService = serviceAccessor.get(IUserDataProfileService);
842+
editorService.openEditor({ resource: userDataProfileService.currentProfile.extensionsResource });
835843
}
836844
}));
837845
}
838846

839-
private async whenProfileChanged(e: DidChangeUserDataProfileEvent): Promise<void> {
840-
if (e.preserveData) {
841-
const extensions = (await this.readInstalledExtensions(e.previous)).filter(e => !e.metadata?.isApplicationScoped); /* remove application scoped extensions */
842-
await this.addToInstalledExtensions(extensions, e.profile);
843-
} else {
844-
const oldExtensions = await this.scanInstalledExtensions(e.previous);
845-
const newExtensions = await this.scanInstalledExtensions(e.profile);
846-
const { added, removed } = delta(oldExtensions, newExtensions, (a, b) => compare(`${ExtensionIdentifier.toKey(a.identifier.id)}@${a.manifest.version}`, `${ExtensionIdentifier.toKey(b.identifier.id)}@${b.manifest.version}`));
847-
if (added.length || removed.length) {
848-
this._onDidChangeProfileExtensions.fire({ added, removed });
849-
}
850-
}
851-
}
852847
}
853848

854849
registerSingleton(IWebExtensionsScannerService, WebExtensionsScannerService);

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,17 +157,16 @@ export const IWebExtensionsScannerService = createDecorator<IWebExtensionsScanne
157157
export interface IWebExtensionsScannerService {
158158
readonly _serviceBrand: undefined;
159159

160-
readonly onDidChangeProfileExtensions: Event<{ readonly added: IScannedExtension[]; readonly removed: IScannedExtension[] }>;
161-
162160
scanSystemExtensions(): Promise<IExtension[]>;
163-
scanUserExtensions(options?: ScanOptions): Promise<IScannedExtension[]>;
161+
scanUserExtensions(profileLocation: URI | undefined, options?: ScanOptions): Promise<IScannedExtension[]>;
164162
scanExtensionsUnderDevelopment(): Promise<IExtension[]>;
165-
scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType): Promise<IScannedExtension | null>;
163+
scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType, profileLocation: URI | undefined): Promise<IScannedExtension | null>;
166164

167-
addExtension(location: URI, metadata?: Metadata): Promise<IScannedExtension>;
168-
addExtensionFromGallery(galleryExtension: IGalleryExtension, metadata?: Metadata): Promise<IScannedExtension>;
169-
removeExtension(extension: IScannedExtension, version?: string): Promise<void>;
165+
addExtension(location: URI, metadata: Metadata, profileLocation: URI | undefined): Promise<IScannedExtension>;
166+
addExtensionFromGallery(galleryExtension: IGalleryExtension, metadata: Metadata, profileLocation: URI | undefined): Promise<IScannedExtension>;
167+
removeExtension(extension: IScannedExtension, profileLocation: URI | undefined): Promise<void>;
168+
copyExtensions(fromProfileLocation: URI, toProfileLocation: URI, filter: (extension: IScannedExtension) => boolean): Promise<void>;
170169

171-
scanMetadata(extensionLocation: URI): Promise<Metadata | undefined>;
170+
scanMetadata(extensionLocation: URI, profileLocation: URI | undefined): Promise<Metadata | undefined>;
172171
scanExtensionManifest(extensionLocation: URI): Promise<IExtensionManifest | null>;
173172
}

0 commit comments

Comments
 (0)