This commit is contained in:
Sandeep Somavarapu 2024-05-31 15:19:11 +02:00 committed by GitHub
parent 699cf5b3ee
commit e258ea4d8f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 174 additions and 138 deletions

View file

@ -18,7 +18,8 @@ import {
IExtensionsControlManifest, StatisticType, isTargetPlatformCompatible, TargetPlatformToString, ExtensionManagementErrorCode,
InstallOptions, UninstallOptions, Metadata, InstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, IExtensionManagementService, InstallExtensionInfo, EXTENSION_INSTALL_DEP_PACK_CONTEXT, ExtensionGalleryError,
IProductVersion, ExtensionGalleryErrorCode,
EXTENSION_INSTALL_SOURCE_CONTEXT
EXTENSION_INSTALL_SOURCE_CONTEXT,
DidUpdateExtensionMetadata
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, ExtensionKey, getGalleryExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType, IExtensionManifest, isApplicationScopedExtension, TargetPlatform } from 'vs/platform/extensions/common/extensions';
@ -73,7 +74,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
protected _onDidUninstallExtension = this._register(new Emitter<DidUninstallExtensionEvent>());
get onDidUninstallExtension() { return this._onDidUninstallExtension.event; }
protected readonly _onDidUpdateExtensionMetadata = this._register(new Emitter<ILocalExtension>());
protected readonly _onDidUpdateExtensionMetadata = this._register(new Emitter<DidUpdateExtensionMetadata>());
get onDidUpdateExtensionMetadata() { return this._onDidUpdateExtensionMetadata.event; }
private readonly participants: IExtensionManagementParticipant[] = [];
@ -129,7 +130,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
const compatible = await this.checkAndGetCompatibleVersion(extension, !!options?.installGivenVersion, !!options?.installPreReleaseVersion, options.productVersion ?? { version: this.productService.version, date: this.productService.date });
installableExtensions.push({ ...compatible, options });
} catch (error) {
results.push({ identifier: extension.identifier, operation: InstallOperation.Install, source: extension, error });
results.push({ identifier: extension.identifier, operation: InstallOperation.Install, source: extension, error, profileLocation: options.profileLocation ?? this.getCurrentExtensionsManifestLocation() });
}
}));
@ -160,7 +161,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
const existing = (await this.getInstalled(ExtensionType.User, profile.extensionsResource))
.find(e => areSameExtensions(e.identifier, extension.identifier));
if (existing) {
this._onDidUpdateExtensionMetadata.fire(existing);
this._onDidUpdateExtensionMetadata.fire({ local: existing, profileLocation: profile.extensionsResource });
} else {
this._onDidUninstallExtension.fire({ identifier: extension.identifier, profileLocation: profile.extensionsResource });
}
@ -784,7 +785,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
abstract reinstallFromGallery(extension: ILocalExtension): Promise<ILocalExtension>;
abstract cleanUp(): Promise<void>;
abstract updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation?: URI): Promise<ILocalExtension>;
abstract updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation: URI): Promise<ILocalExtension>;
protected abstract getCurrentExtensionsManifestLocation(): URI;
protected abstract createInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallExtensionTaskOptions): IInstallExtensionTask;

View file

@ -381,7 +381,7 @@ export interface IExtensionGalleryService {
export interface InstallExtensionEvent {
readonly identifier: IExtensionIdentifier;
readonly source: URI | IGalleryExtension;
readonly profileLocation?: URI;
readonly profileLocation: URI;
readonly applicationScoped?: boolean;
readonly workspaceScoped?: boolean;
}
@ -393,14 +393,14 @@ export interface InstallExtensionResult {
readonly local?: ILocalExtension;
readonly error?: Error;
readonly context?: IStringDictionary<any>;
readonly profileLocation?: URI;
readonly profileLocation: URI;
readonly applicationScoped?: boolean;
readonly workspaceScoped?: boolean;
}
export interface UninstallExtensionEvent {
readonly identifier: IExtensionIdentifier;
readonly profileLocation?: URI;
readonly profileLocation: URI;
readonly applicationScoped?: boolean;
readonly workspaceScoped?: boolean;
}
@ -408,11 +408,16 @@ export interface UninstallExtensionEvent {
export interface DidUninstallExtensionEvent {
readonly identifier: IExtensionIdentifier;
readonly error?: string;
readonly profileLocation?: URI;
readonly profileLocation: URI;
readonly applicationScoped?: boolean;
readonly workspaceScoped?: boolean;
}
export interface DidUpdateExtensionMetadata {
readonly profileLocation: URI;
readonly local: ILocalExtension;
}
export const enum ExtensionGalleryErrorCode {
Timeout = 'Timeout',
Cancelled = 'Cancelled',
@ -487,7 +492,14 @@ export type InstallOptions = {
*/
context?: IStringDictionary<any>;
};
export type UninstallOptions = { readonly donotIncludePack?: boolean; readonly donotCheckDependents?: boolean; readonly versionOnly?: boolean; readonly remove?: boolean; readonly profileLocation?: URI };
export type UninstallOptions = {
readonly profileLocation?: URI;
readonly donotIncludePack?: boolean;
readonly donotCheckDependents?: boolean;
readonly versionOnly?: boolean;
readonly remove?: boolean;
};
export interface IExtensionManagementParticipant {
postInstall(local: ILocalExtension, source: URI | IGalleryExtension, options: InstallOptions, token: CancellationToken): Promise<void>;
@ -504,7 +516,7 @@ export interface IExtensionManagementService {
onDidInstallExtensions: Event<readonly InstallExtensionResult[]>;
onUninstallExtension: Event<UninstallExtensionEvent>;
onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
onDidUpdateExtensionMetadata: Event<ILocalExtension>;
onDidUpdateExtensionMetadata: Event<DidUpdateExtensionMetadata>;
zip(extension: ILocalExtension): Promise<URI>;
unzip(zipLocation: URI): Promise<IExtensionIdentifier>;
@ -521,7 +533,7 @@ export interface IExtensionManagementService {
getInstalled(type?: ExtensionType, profileLocation?: URI, productVersion?: IProductVersion): Promise<ILocalExtension[]>;
getExtensionsControlManifest(): Promise<IExtensionsControlManifest>;
copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise<void>;
updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation?: URI): Promise<ILocalExtension>;
updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation: URI): Promise<ILocalExtension>;
download(extension: IGalleryExtension, operation: InstallOperation, donotVerifySignature: boolean): Promise<URI>;

View file

@ -9,7 +9,7 @@ import { cloneAndChange } from 'vs/base/common/objects';
import { URI, UriComponents } from 'vs/base/common/uri';
import { DefaultURITransformer, IURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc';
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { IExtensionIdentifier, IExtensionTipsService, IGalleryExtension, ILocalExtension, IExtensionsControlManifest, isTargetPlatformCompatible, InstallOptions, UninstallOptions, Metadata, IExtensionManagementService, DidUninstallExtensionEvent, InstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, InstallOperation, InstallExtensionInfo, IProductVersion } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionIdentifier, IExtensionTipsService, IGalleryExtension, ILocalExtension, IExtensionsControlManifest, isTargetPlatformCompatible, InstallOptions, UninstallOptions, Metadata, IExtensionManagementService, DidUninstallExtensionEvent, InstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, InstallOperation, InstallExtensionInfo, IProductVersion, DidUpdateExtensionMetadata } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI;
@ -43,7 +43,7 @@ export class ExtensionManagementChannel implements IServerChannel {
onDidInstallExtensions: Event<readonly InstallExtensionResult[]>;
onUninstallExtension: Event<UninstallExtensionEvent>;
onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
onDidUpdateExtensionMetadata: Event<ILocalExtension>;
onDidUpdateExtensionMetadata: Event<DidUpdateExtensionMetadata>;
constructor(private service: IExtensionManagementService, private getUriTransformer: (requestContext: any) => IURITransformer | null) {
this.onInstallExtension = Event.buffer(service.onInstallExtension, true);
@ -89,7 +89,12 @@ export class ExtensionManagementChannel implements IServerChannel {
});
}
case 'onDidUpdateExtensionMetadata': {
return Event.map<ILocalExtension, ILocalExtension>(this.onDidUpdateExtensionMetadata, e => transformOutgoingExtension(e, uriTransformer));
return Event.map<DidUpdateExtensionMetadata, DidUpdateExtensionMetadata>(this.onDidUpdateExtensionMetadata, e => {
return {
local: transformOutgoingExtension(e.local, uriTransformer),
profileLocation: transformOutgoingURI(e.profileLocation, uriTransformer)
};
});
}
}
@ -168,7 +173,11 @@ export class ExtensionManagementChannel implements IServerChannel {
}
}
export type ExtensionEventResult = InstallExtensionEvent | InstallExtensionResult | UninstallExtensionEvent | DidUninstallExtensionEvent;
export interface ExtensionEventResult {
readonly profileLocation: URI;
readonly local?: ILocalExtension;
readonly applicationScoped?: boolean;
}
export class ExtensionManagementChannelClient extends Disposable implements IExtensionManagementService {
@ -186,7 +195,7 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt
private readonly _onDidUninstallExtension = this._register(new Emitter<DidUninstallExtensionEvent>());
get onDidUninstallExtension() { return this._onDidUninstallExtension.event; }
private readonly _onDidUpdateExtensionMetadata = this._register(new Emitter<ILocalExtension>());
private readonly _onDidUpdateExtensionMetadata = this._register(new Emitter<DidUpdateExtensionMetadata>());
get onDidUpdateExtensionMetadata() { return this._onDidUpdateExtensionMetadata.event; }
constructor(private readonly channel: IChannel) {
@ -195,16 +204,12 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt
this._register(this.channel.listen<readonly InstallExtensionResult[]>('onDidInstallExtensions')(results => this.fireEvent(this._onDidInstallExtensions, results.map(e => ({ ...e, local: e.local ? transformIncomingExtension(e.local, null) : e.local, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source, profileLocation: URI.revive(e.profileLocation) })))));
this._register(this.channel.listen<UninstallExtensionEvent>('onUninstallExtension')(e => this.fireEvent(this._onUninstallExtension, { ...e, profileLocation: URI.revive(e.profileLocation) })));
this._register(this.channel.listen<DidUninstallExtensionEvent>('onDidUninstallExtension')(e => this.fireEvent(this._onDidUninstallExtension, { ...e, profileLocation: URI.revive(e.profileLocation) })));
this._register(this.channel.listen<ILocalExtension>('onDidUpdateExtensionMetadata')(e => this._onDidUpdateExtensionMetadata.fire(transformIncomingExtension(e, null))));
this._register(this.channel.listen<DidUpdateExtensionMetadata>('onDidUpdateExtensionMetadata')(e => this.fireEvent(this._onDidUpdateExtensionMetadata, { profileLocation: URI.revive(e.profileLocation), local: transformIncomingExtension(e.local, null) })));
}
protected fireEvent(event: Emitter<InstallExtensionEvent>, data: InstallExtensionEvent): void;
protected fireEvent(event: Emitter<readonly InstallExtensionResult[]>, data: InstallExtensionResult[]): void;
protected fireEvent(event: Emitter<UninstallExtensionEvent>, data: UninstallExtensionEvent): void;
protected fireEvent(event: Emitter<DidUninstallExtensionEvent>, data: DidUninstallExtensionEvent): void;
protected fireEvent(event: Emitter<ExtensionEventResult>, data: ExtensionEventResult): void;
protected fireEvent(event: Emitter<ExtensionEventResult[]>, data: ExtensionEventResult[]): void;
protected fireEvent<E>(event: Emitter<E>, data: E): void {
protected fireEvent<E extends ExtensionEventResult>(event: Emitter<E>, data: E): void;
protected fireEvent<E extends ExtensionEventResult>(event: Emitter<readonly E[]>, data: E[]): void;
protected fireEvent<E extends ExtensionEventResult>(event: Emitter<E | E[]>, data: E | E[]): void {
event.fire(data);
}

View file

@ -186,7 +186,7 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
return extensionsToInstall;
}
async updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation: URI = this.userDataProfilesService.defaultProfile.extensionsResource): Promise<ILocalExtension> {
async updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation: URI): Promise<ILocalExtension> {
this.logService.trace('ExtensionManagementService#updateMetadata', local.identifier.id);
if (metadata.isPreReleaseVersion) {
metadata.preRelease = true;
@ -204,7 +204,7 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
}
local = await this.extensionsScanner.updateMetadata(local, metadata, profileLocation);
this.manifestCache.invalidate(profileLocation);
this._onDidUpdateExtensionMetadata.fire(local);
this._onDidUpdateExtensionMetadata.fire({ local, profileLocation });
return local;
}

View file

@ -536,6 +536,7 @@ class Extensions extends Disposable {
@IExtensionGalleryService private readonly galleryService: IExtensionGalleryService,
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
@IWorkbenchExtensionManagementService private readonly workbenchExtensionManagementService: IWorkbenchExtensionManagementService,
@IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
@ -544,7 +545,7 @@ class Extensions extends Disposable {
this._register(server.extensionManagementService.onDidInstallExtensions(e => this.onDidInstallExtensions(e)));
this._register(server.extensionManagementService.onUninstallExtension(e => this.onUninstallExtension(e.identifier)));
this._register(server.extensionManagementService.onDidUninstallExtension(e => this.onDidUninstallExtension(e)));
this._register(server.extensionManagementService.onDidUpdateExtensionMetadata(e => this.onDidUpdateExtensionMetadata(e)));
this._register(server.extensionManagementService.onDidUpdateExtensionMetadata(e => this.onDidUpdateExtensionMetadata(e.local)));
this._register(server.extensionManagementService.onDidChangeProfile(() => this.reset()));
this._register(extensionEnablementService.onEnablementChanged(e => this.onEnablementChanged(e)));
this._register(Event.any(this.onChange, this.onReset)(() => this._local = undefined));
@ -666,7 +667,7 @@ class Extensions extends Disposable {
const galleryWithLocalVersion: IGalleryExtension | undefined = (await this.galleryService.getExtensions([{ ...localExtension.identifier, version: localExtension.manifest.version }], CancellationToken.None))[0];
isPreReleaseVersion = !!galleryWithLocalVersion?.properties?.isPreReleaseVersion;
}
return this.server.extensionManagementService.updateMetadata(localExtension, { id: gallery.identifier.uuid, publisherDisplayName: gallery.publisherDisplayName, publisherId: gallery.publisherId, isPreReleaseVersion });
return this.server.extensionManagementService.updateMetadata(localExtension, { id: gallery.identifier.uuid, publisherDisplayName: gallery.publisherDisplayName, publisherId: gallery.publisherId, isPreReleaseVersion }, this.userDataProfileService.currentProfile.extensionsResource);
}
canInstall(galleryExtension: IGalleryExtension): Promise<boolean> {

View file

@ -60,6 +60,9 @@ import { IUpdateService, State } from 'vs/platform/update/common/update';
import { IFileService } from 'vs/platform/files/common/files';
import { FileService } from 'vs/platform/files/common/fileService';
import { Mutable } from 'vs/base/common/types';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService';
import { toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
let instantiationService: TestInstantiationService;
let installEvent: Emitter<InstallExtensionEvent>,
@ -126,6 +129,7 @@ function setupTest(disposables: Pick<DisposableStore, 'add'>) {
}
});
instantiationService.stub(IUserDataProfileService, disposables.add(new UserDataProfileService(toUserDataProfile('test', 'test', URI.file('foo'), URI.file('cache')))));
instantiationService.stub(IWorkbenchExtensionEnablementService, disposables.add(new TestExtensionEnablementService(instantiationService)));
instantiationService.stub(ILabelService, { onDidChangeFormatters: disposables.add(new Emitter<IFormatterChangeEvent>()).event });
@ -187,7 +191,7 @@ suite('ExtensionsActions', () => {
return workbenchService.queryGallery(CancellationToken.None)
.then((paged) => {
testObject.extension = paged.firstPage[0];
installEvent.fire({ identifier: gallery.identifier, source: gallery });
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
assert.ok(!testObject.enabled);
assert.strictEqual('Installing', testObject.label);
@ -217,8 +221,8 @@ suite('ExtensionsActions', () => {
return instantiationService.get(IExtensionsWorkbenchService).queryLocal()
.then(extensions => {
uninstallEvent.fire({ identifier: local.identifier });
didUninstallEvent.fire({ identifier: local.identifier });
uninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
didUninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
testObject.extension = extensions[0];
assert.ok(!testObject.enabled);
});
@ -232,8 +236,8 @@ suite('ExtensionsActions', () => {
return instantiationService.get(IExtensionsWorkbenchService).queryLocal()
.then(extensions => {
uninstallEvent.fire({ identifier: local.identifier });
didUninstallEvent.fire({ identifier: local.identifier });
uninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
didUninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
testObject.extension = extensions[0];
assert.ok(!testObject.enabled);
});
@ -255,7 +259,7 @@ suite('ExtensionsActions', () => {
return instantiationService.get(IExtensionsWorkbenchService).queryLocal()
.then(extensions => {
testObject.extension = extensions[0];
uninstallEvent.fire({ identifier: local.identifier });
uninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
assert.ok(!testObject.enabled);
assert.strictEqual('Uninstalling', testObject.label);
assert.strictEqual('extension-action label uninstall uninstalling', testObject.class);
@ -303,7 +307,7 @@ suite('ExtensionsActions', () => {
const gallery = aGalleryExtension('a');
const extension = extensions[0];
extension.gallery = gallery;
installEvent.fire({ identifier: gallery.identifier, source: gallery });
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
testObject.extension = extension;
assert.ok(!testObject.enabled);
});
@ -318,9 +322,9 @@ suite('ExtensionsActions', () => {
const paged = await instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None);
testObject.extension = paged.firstPage[0];
installEvent.fire({ identifier: gallery.identifier, source: gallery });
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
const promise = Event.toPromise(testObject.onDidChange);
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery) }]);
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery), profileLocation: null! }]);
await promise;
assert.ok(testObject.enabled);
@ -429,7 +433,7 @@ suite('ExtensionsActions', () => {
c();
}
}));
installEvent.fire({ identifier: local.identifier, source: gallery });
installEvent.fire({ identifier: local.identifier, source: gallery, profileLocation: null! });
});
});
@ -480,7 +484,7 @@ suite('ExtensionsActions', () => {
.then(page => {
testObject.extension = page.firstPage[0];
installEvent.fire({ identifier: gallery.identifier, source: gallery });
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
assert.ok(!testObject.enabled);
assert.strictEqual('extension-action icon manage codicon codicon-extensions-manage hide', testObject.class);
assert.strictEqual('Manage', testObject.tooltip);
@ -495,9 +499,9 @@ suite('ExtensionsActions', () => {
const paged = await instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None);
testObject.extension = paged.firstPage[0];
installEvent.fire({ identifier: gallery.identifier, source: gallery });
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
const promise = Event.toPromise(testObject.onDidChange);
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery) }]);
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery), profileLocation: null! }]);
await promise;
assert.ok(testObject.enabled);
@ -529,7 +533,7 @@ suite('ExtensionsActions', () => {
return instantiationService.get(IExtensionsWorkbenchService).queryLocal()
.then(extensions => {
testObject.extension = extensions[0];
uninstallEvent.fire({ identifier: local.identifier });
uninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
assert.ok(!testObject.enabled);
assert.strictEqual('extension-action icon manage codicon codicon-extensions-manage', testObject.class);
@ -735,7 +739,7 @@ suite('ExtensionsActions', () => {
testObject.extension = page.firstPage[0];
disposables.add(instantiationService.createInstance(ExtensionContainers, [testObject]));
installEvent.fire({ identifier: gallery.identifier, source: gallery });
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
assert.ok(!testObject.enabled);
});
});
@ -748,7 +752,7 @@ suite('ExtensionsActions', () => {
.then(extensions => {
const testObject: ExtensionsActions.EnableDropDownAction = disposables.add(instantiationService.createInstance(ExtensionsActions.EnableDropDownAction));
testObject.extension = extensions[0];
uninstallEvent.fire({ identifier: local.identifier });
uninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
assert.ok(!testObject.enabled);
});
});
@ -929,7 +933,7 @@ suite('ExtensionsActions', () => {
const testObject: ExtensionsActions.DisableGloballyAction = disposables.add(instantiationService.createInstance(ExtensionsActions.DisableGloballyAction));
testObject.extension = page.firstPage[0];
disposables.add(instantiationService.createInstance(ExtensionContainers, [testObject]));
installEvent.fire({ identifier: gallery.identifier, source: gallery });
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
assert.ok(!testObject.enabled);
});
});
@ -948,7 +952,7 @@ suite('ExtensionsActions', () => {
const testObject: ExtensionsActions.DisableGloballyAction = disposables.add(instantiationService.createInstance(ExtensionsActions.DisableGloballyAction));
testObject.extension = extensions[0];
disposables.add(instantiationService.createInstance(ExtensionContainers, [testObject]));
uninstallEvent.fire({ identifier: local.identifier });
uninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
assert.ok(!testObject.enabled);
});
});
@ -976,7 +980,7 @@ suite('ExtensionRuntimeStateAction', () => {
instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery));
const paged = await workbenchService.queryGallery(CancellationToken.None);
testObject.extension = paged.firstPage[0];
installEvent.fire({ identifier: gallery.identifier, source: gallery });
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
assert.ok(!testObject.enabled);
});
@ -989,7 +993,7 @@ suite('ExtensionRuntimeStateAction', () => {
const extensions = await instantiationService.get(IExtensionsWorkbenchService).queryLocal();
testObject.extension = extensions[0];
uninstallEvent.fire({ identifier: local.identifier });
uninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
assert.ok(!testObject.enabled);
});
@ -1010,9 +1014,9 @@ suite('ExtensionRuntimeStateAction', () => {
testObject.extension = paged.firstPage[0];
assert.ok(!testObject.enabled);
installEvent.fire({ identifier: gallery.identifier, source: gallery });
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
const promise = Event.toPromise(testObject.onDidChange);
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery) }]);
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery), profileLocation: null! }]);
await promise;
assert.ok(testObject.enabled);
assert.strictEqual(testObject.tooltip, `Please restart extensions to enable this extension.`);
@ -1035,8 +1039,8 @@ suite('ExtensionRuntimeStateAction', () => {
testObject.extension = paged.firstPage[0];
assert.ok(!testObject.enabled);
installEvent.fire({ identifier: gallery.identifier, source: gallery });
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery) }]);
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery), profileLocation: null! }]);
assert.ok(!testObject.enabled);
});
@ -1056,10 +1060,10 @@ suite('ExtensionRuntimeStateAction', () => {
testObject.extension = paged.firstPage[0];
const identifier = gallery.identifier;
installEvent.fire({ identifier, source: gallery });
didInstallEvent.fire([{ identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, { identifier }) }]);
uninstallEvent.fire({ identifier });
didUninstallEvent.fire({ identifier });
installEvent.fire({ identifier, source: gallery, profileLocation: null! });
didInstallEvent.fire([{ identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, { identifier }), profileLocation: null! }]);
uninstallEvent.fire({ identifier, profileLocation: null! });
didUninstallEvent.fire({ identifier, profileLocation: null! });
assert.ok(!testObject.enabled);
});
@ -1080,8 +1084,8 @@ suite('ExtensionRuntimeStateAction', () => {
const extensions = await instantiationService.get(IExtensionsWorkbenchService).queryLocal();
testObject.extension = extensions[0];
uninstallEvent.fire({ identifier: local.identifier });
didUninstallEvent.fire({ identifier: local.identifier });
uninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
didUninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
assert.ok(testObject.enabled);
assert.strictEqual(testObject.tooltip, `Please restart extensions to complete the uninstallation of this extension.`);
});
@ -1101,8 +1105,8 @@ suite('ExtensionRuntimeStateAction', () => {
const extensions = await instantiationService.get(IExtensionsWorkbenchService).queryLocal();
testObject.extension = extensions[0];
uninstallEvent.fire({ identifier: local.identifier });
didUninstallEvent.fire({ identifier: local.identifier });
uninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
didUninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
assert.ok(!testObject.enabled);
});
@ -1121,13 +1125,13 @@ suite('ExtensionRuntimeStateAction', () => {
const extensions = await instantiationService.get(IExtensionsWorkbenchService).queryLocal();
testObject.extension = extensions[0];
uninstallEvent.fire({ identifier: local.identifier });
didUninstallEvent.fire({ identifier: local.identifier });
uninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
didUninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
const gallery = aGalleryExtension('a');
const identifier = gallery.identifier;
installEvent.fire({ identifier, source: gallery });
didInstallEvent.fire([{ identifier, source: gallery, operation: InstallOperation.Install, local }]);
installEvent.fire({ identifier, source: gallery, profileLocation: null! });
didInstallEvent.fire([{ identifier, source: gallery, operation: InstallOperation.Install, local, profileLocation: null! }]);
assert.ok(!testObject.enabled);
});
@ -1156,8 +1160,8 @@ suite('ExtensionRuntimeStateAction', () => {
}
}));
const gallery = aGalleryExtension('a', { uuid: local.identifier.id, version: '1.0.2' });
installEvent.fire({ identifier: gallery.identifier, source: gallery });
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery) }]);
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery), profileLocation: null! }]);
});
});
@ -1179,8 +1183,8 @@ suite('ExtensionRuntimeStateAction', () => {
testObject.extension = extensions[0];
const gallery = aGalleryExtension('a', { identifier: local.identifier, version: '1.0.2' });
installEvent.fire({ identifier: gallery.identifier, source: gallery });
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Update, local: aLocalExtension('a', gallery, gallery) }]);
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Update, local: aLocalExtension('a', gallery, gallery), profileLocation: null! }]);
assert.ok(!testObject.enabled);
});
@ -1290,8 +1294,8 @@ suite('ExtensionRuntimeStateAction', () => {
testObject.extension = extensions[0];
const gallery = aGalleryExtension('a', { identifier: local.identifier, version: '1.0.2' });
installEvent.fire({ identifier: gallery.identifier, source: gallery });
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery) }]);
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, gallery), profileLocation: null! }]);
await workbenchService.setEnablement(extensions[0], EnablementState.EnabledGlobally);
await testObject.update();
assert.ok(testObject.enabled);
@ -1315,8 +1319,8 @@ suite('ExtensionRuntimeStateAction', () => {
testObject.extension = paged.firstPage[0];
assert.ok(!testObject.enabled);
installEvent.fire({ identifier: gallery.identifier, source: gallery });
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', { ...gallery, ...{ contributes: <IExtensionContributions>{ localizations: [{ languageId: 'de', translations: [] }] } } }, gallery) }]);
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', { ...gallery, ...{ contributes: <IExtensionContributions>{ localizations: [{ languageId: 'de', translations: [] }] } } }, gallery), profileLocation: null! }]);
assert.ok(!testObject.enabled);
});
@ -1337,8 +1341,8 @@ suite('ExtensionRuntimeStateAction', () => {
testObject.extension = extensions[0];
const gallery = aGalleryExtension('a', { uuid: local.identifier.id, version: '1.0.2' });
installEvent.fire({ identifier: gallery.identifier, source: gallery });
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', { ...gallery, ...{ contributes: <IExtensionContributions>{ localizations: [{ languageId: 'de', translations: [] }] } } }, gallery) }]);
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension('a', { ...gallery, ...{ contributes: <IExtensionContributions>{ localizations: [{ languageId: 'de', translations: [] }] } } }, gallery), profileLocation: null! }]);
assert.ok(!testObject.enabled);
});
@ -1378,7 +1382,7 @@ suite('ExtensionRuntimeStateAction', () => {
const remoteExtension = aLocalExtension('a', { extensionKind: ['workspace'] }, { location: URI.file('pub.a').with({ scheme: Schemas.vscodeRemote }) });
const localExtensionManagementService = createExtensionManagementService([localExtension]);
const uninstallEvent = new Emitter<UninstallExtensionEvent>();
const onDidUninstallEvent = new Emitter<{ identifier: IExtensionIdentifier }>();
const onDidUninstallEvent = new Emitter<{ identifier: IExtensionIdentifier; profileLocation: URI }>();
localExtensionManagementService.onUninstallExtension = uninstallEvent.event;
localExtensionManagementService.onDidUninstallExtension = onDidUninstallEvent.event;
const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, localExtensionManagementService, createExtensionManagementService([remoteExtension]));
@ -1404,8 +1408,8 @@ suite('ExtensionRuntimeStateAction', () => {
assert.ok(testObject.extension);
assert.ok(!testObject.enabled);
uninstallEvent.fire({ identifier: localExtension.identifier });
didUninstallEvent.fire({ identifier: localExtension.identifier });
uninstallEvent.fire({ identifier: localExtension.identifier, profileLocation: null! });
didUninstallEvent.fire({ identifier: localExtension.identifier, profileLocation: null! });
assert.ok(!testObject.enabled);
});
@ -1442,7 +1446,7 @@ suite('ExtensionRuntimeStateAction', () => {
const remoteExtension = aLocalExtension('a', { extensionKind: ['workspace'] }, { location: URI.file('pub.a').with({ scheme: Schemas.vscodeRemote }) });
const promise = Event.toPromise(testObject.onDidChange);
onDidInstallEvent.fire([{ identifier: remoteExtension.identifier, local: remoteExtension, operation: InstallOperation.Install }]);
onDidInstallEvent.fire([{ identifier: remoteExtension.identifier, local: remoteExtension, operation: InstallOperation.Install, profileLocation: null! }]);
await promise;
assert.ok(testObject.enabled);
@ -1481,7 +1485,7 @@ suite('ExtensionRuntimeStateAction', () => {
const localExtension = aLocalExtension('a', { extensionKind: ['ui'] }, { location: URI.file('pub.a') });
const promise = Event.toPromise(Event.filter(testObject.onDidChange, () => testObject.enabled));
onDidInstallEvent.fire([{ identifier: localExtension.identifier, local: localExtension, operation: InstallOperation.Install }]);
onDidInstallEvent.fire([{ identifier: localExtension.identifier, local: localExtension, operation: InstallOperation.Install, profileLocation: null! }]);
await promise;
assert.ok(testObject.enabled);
@ -1757,7 +1761,7 @@ suite('RemoteInstallAction', () => {
assert.strictEqual('Install in remote', testObject.label);
assert.strictEqual('extension-action label prominent install', testObject.class);
onInstallExtension.fire({ identifier: localWorkspaceExtension.identifier, source: gallery });
onInstallExtension.fire({ identifier: localWorkspaceExtension.identifier, source: gallery, profileLocation: null! });
assert.ok(testObject.enabled);
assert.strictEqual('Installing', testObject.label);
assert.strictEqual('extension-action label install installing', testObject.class);
@ -1790,14 +1794,14 @@ suite('RemoteInstallAction', () => {
assert.strictEqual('Install in remote', testObject.label);
assert.strictEqual('extension-action label prominent install', testObject.class);
onInstallExtension.fire({ identifier: localWorkspaceExtension.identifier, source: gallery });
onInstallExtension.fire({ identifier: localWorkspaceExtension.identifier, source: gallery, profileLocation: null! });
assert.ok(testObject.enabled);
assert.strictEqual('Installing', testObject.label);
assert.strictEqual('extension-action label install installing', testObject.class);
const installedExtension = aLocalExtension('a', { extensionKind: ['workspace'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
const promise = Event.toPromise(testObject.onDidChange);
onDidInstallEvent.fire([{ identifier: installedExtension.identifier, local: installedExtension, operation: InstallOperation.Install }]);
onDidInstallEvent.fire([{ identifier: installedExtension.identifier, local: installedExtension, operation: InstallOperation.Install, profileLocation: null! }]);
await promise;
assert.ok(!testObject.enabled);
});
@ -1987,7 +1991,7 @@ suite('RemoteInstallAction', () => {
assert.ok(testObject.enabled);
assert.strictEqual('Install in remote', testObject.label);
uninstallEvent.fire({ identifier: localWorkspaceExtension.identifier });
uninstallEvent.fire({ identifier: localWorkspaceExtension.identifier, profileLocation: null! });
assert.ok(!testObject.enabled);
});
@ -2131,7 +2135,7 @@ suite('RemoteInstallAction', () => {
assert.ok(testObject.enabled);
assert.strictEqual('Install in remote', testObject.label);
uninstallEvent.fire({ identifier: languagePackExtension.identifier });
uninstallEvent.fire({ identifier: languagePackExtension.identifier, profileLocation: null! });
assert.ok(!testObject.enabled);
});
});
@ -2209,7 +2213,7 @@ suite('LocalInstallAction', () => {
assert.strictEqual('Install Locally', testObject.label);
assert.strictEqual('extension-action label prominent install', testObject.class);
onInstallExtension.fire({ identifier: remoteUIExtension.identifier, source: gallery });
onInstallExtension.fire({ identifier: remoteUIExtension.identifier, source: gallery, profileLocation: null! });
assert.ok(testObject.enabled);
assert.strictEqual('Installing', testObject.label);
assert.strictEqual('extension-action label install installing', testObject.class);
@ -2242,14 +2246,14 @@ suite('LocalInstallAction', () => {
assert.strictEqual('Install Locally', testObject.label);
assert.strictEqual('extension-action label prominent install', testObject.class);
onInstallExtension.fire({ identifier: remoteUIExtension.identifier, source: gallery });
onInstallExtension.fire({ identifier: remoteUIExtension.identifier, source: gallery, profileLocation: null! });
assert.ok(testObject.enabled);
assert.strictEqual('Installing', testObject.label);
assert.strictEqual('extension-action label install installing', testObject.class);
const installedExtension = aLocalExtension('a', { extensionKind: ['ui'] }, { location: URI.file(`pub.a`) });
const promise = Event.toPromise(testObject.onDidChange);
onDidInstallEvent.fire([{ identifier: installedExtension.identifier, local: installedExtension, operation: InstallOperation.Install }]);
onDidInstallEvent.fire([{ identifier: installedExtension.identifier, local: installedExtension, operation: InstallOperation.Install, profileLocation: null! }]);
await promise;
assert.ok(!testObject.enabled);
});
@ -2399,7 +2403,7 @@ suite('LocalInstallAction', () => {
assert.ok(testObject.enabled);
assert.strictEqual('Install Locally', testObject.label);
uninstallEvent.fire({ identifier: remoteUIExtension.identifier });
uninstallEvent.fire({ identifier: remoteUIExtension.identifier, profileLocation: null! });
assert.ok(!testObject.enabled);
});
@ -2522,7 +2526,7 @@ suite('LocalInstallAction', () => {
assert.ok(testObject.enabled);
assert.strictEqual('Install Locally', testObject.label);
uninstallEvent.fire({ identifier: languagePackExtension.identifier });
uninstallEvent.fire({ identifier: languagePackExtension.identifier, profileLocation: null! });
assert.ok(!testObject.enabled);
});
@ -2638,7 +2642,7 @@ function createExtensionManagementService(installed: ILocalExtension[] = []): IP
getInstalled: () => Promise.resolve<ILocalExtension[]>(installed),
canInstall: async (extension: IGalleryExtension) => { return true; },
installFromGallery: (extension: IGalleryExtension) => Promise.reject(new Error('not supported')),
updateMetadata: async (local: Mutable<ILocalExtension>, metadata: Partial<Metadata>) => {
updateMetadata: async (local: Mutable<ILocalExtension>, metadata: Partial<Metadata>, profileLocation: URI) => {
local.identifier.uuid = metadata.id;
local.publisherDisplayName = metadata.publisherDisplayName!;
local.publisherId = metadata.publisherId!;

View file

@ -51,6 +51,9 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/uti
import { IUpdateService, State } from 'vs/platform/update/common/update';
import { IFileService } from 'vs/platform/files/common/files';
import { FileService } from 'vs/platform/files/common/fileService';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService';
import { toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
suite('ExtensionsViews Tests', () => {
@ -122,6 +125,7 @@ suite('ExtensionsViews Tests', () => {
});
instantiationService.stub(IWorkbenchExtensionEnablementService, disposableStore.add(new TestExtensionEnablementService(instantiationService)));
instantiationService.stub(IUserDataProfileService, disposableStore.add(new UserDataProfileService(toUserDataProfile('test', 'test', URI.file('foo'), URI.file('cache')))));
const reasons: { [key: string]: any } = {};
reasons[workspaceRecommendationA.identifier.id] = { reasonId: ExtensionRecommendationReason.Workspace };

View file

@ -54,6 +54,9 @@ import { Mutable } from 'vs/base/common/types';
import { IUpdateService, State } from 'vs/platform/update/common/update';
import { IFileService } from 'vs/platform/files/common/files';
import { FileService } from 'vs/platform/files/common/fileService';
import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
suite('ExtensionsWorkbenchServiceTest', () => {
@ -89,6 +92,7 @@ suite('ExtensionsWorkbenchServiceTest', () => {
stubConfiguration();
instantiationService.stub(IRemoteAgentService, RemoteAgentService);
instantiationService.stub(IUserDataProfileService, disposableStore.add(new UserDataProfileService(toUserDataProfile('test', 'test', URI.file('foo'), URI.file('cache')))));
instantiationService.stub(IWorkbenchExtensionManagementService, {
onDidInstallExtensions: didInstallEvent.event,
@ -374,7 +378,7 @@ suite('ExtensionsWorkbenchServiceTest', () => {
const identifier = gallery.identifier;
// Installing
installEvent.fire({ identifier, source: gallery });
installEvent.fire({ identifier, source: gallery, profileLocation: null! });
const local = testObject.local;
assert.strictEqual(1, local.length);
const actual = local[0];
@ -382,18 +386,18 @@ suite('ExtensionsWorkbenchServiceTest', () => {
assert.strictEqual(ExtensionState.Installing, actual.state);
// Installed
didInstallEvent.fire([{ identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension(gallery.name, gallery, { identifier }) }]);
didInstallEvent.fire([{ identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension(gallery.name, gallery, { identifier }), profileLocation: null! }]);
assert.strictEqual(ExtensionState.Installed, actual.state);
assert.strictEqual(1, testObject.local.length);
testObject.uninstall(actual);
// Uninstalling
uninstallEvent.fire({ identifier });
uninstallEvent.fire({ identifier, profileLocation: null! });
assert.strictEqual(ExtensionState.Uninstalling, actual.state);
// Uninstalled
didUninstallEvent.fire({ identifier });
didUninstallEvent.fire({ identifier, profileLocation: null! });
assert.strictEqual(ExtensionState.Uninstalled, actual.state);
assert.strictEqual(0, testObject.local.length);
@ -416,8 +420,8 @@ suite('ExtensionsWorkbenchServiceTest', () => {
testObject = await aWorkbenchService();
const target = testObject.local[0];
testObject.uninstall(target);
uninstallEvent.fire({ identifier: local.identifier });
didUninstallEvent.fire({ identifier: local.identifier });
uninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
didUninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
assert.ok(!(await testObject.canInstall(target)));
});
@ -455,11 +459,11 @@ suite('ExtensionsWorkbenchServiceTest', () => {
const extension = page.firstPage[0];
assert.strictEqual(ExtensionState.Uninstalled, extension.state);
installEvent.fire({ identifier: gallery.identifier, source: gallery });
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
const promise = Event.toPromise(testObject.onChange);
// Installed
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension(gallery.name, gallery, gallery) }]);
didInstallEvent.fire([{ identifier: gallery.identifier, source: gallery, operation: InstallOperation.Install, local: aLocalExtension(gallery.name, gallery, gallery), profileLocation: null! }]);
await promise;
});
@ -477,7 +481,7 @@ suite('ExtensionsWorkbenchServiceTest', () => {
disposableStore.add(testObject.onChange(target));
// Installing
installEvent.fire({ identifier: gallery.identifier, source: gallery });
installEvent.fire({ identifier: gallery.identifier, source: gallery, profileLocation: null! });
assert.ok(target.calledOnce);
});
@ -491,7 +495,7 @@ suite('ExtensionsWorkbenchServiceTest', () => {
testObject.uninstall(testObject.local[0]);
disposableStore.add(testObject.onChange(target));
uninstallEvent.fire({ identifier: local.identifier });
uninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
assert.ok(target.calledOnce);
});
@ -503,9 +507,9 @@ suite('ExtensionsWorkbenchServiceTest', () => {
const target = sinon.spy();
testObject.uninstall(testObject.local[0]);
uninstallEvent.fire({ identifier: local.identifier });
uninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
disposableStore.add(testObject.onChange(target));
didUninstallEvent.fire({ identifier: local.identifier });
didUninstallEvent.fire({ identifier: local.identifier, profileLocation: null! });
assert.ok(target.calledOnce);
});
@ -1018,7 +1022,7 @@ suite('ExtensionsWorkbenchServiceTest', () => {
testObject = await aWorkbenchService();
const local = aLocalExtension('pub.a');
await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledGlobally);
didInstallEvent.fire([{ local, identifier: local.identifier, operation: InstallOperation.Update }]);
didInstallEvent.fire([{ local, identifier: local.identifier, operation: InstallOperation.Update, profileLocation: null! }]);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
const actual = await testObject.queryLocal();
assert.strictEqual(actual[0].enablementState, EnablementState.DisabledGlobally);
@ -1028,7 +1032,7 @@ suite('ExtensionsWorkbenchServiceTest', () => {
testObject = await aWorkbenchService();
const local = aLocalExtension('pub.a');
await instantiationService.get(IWorkbenchExtensionEnablementService).setEnablement([local], EnablementState.DisabledWorkspace);
didInstallEvent.fire([{ local, identifier: local.identifier, operation: InstallOperation.Update }]);
didInstallEvent.fire([{ local, identifier: local.identifier, operation: InstallOperation.Update, profileLocation: null! }]);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
const actual = await testObject.queryLocal();
assert.strictEqual(actual[0].enablementState, EnablementState.DisabledWorkspace);
@ -1740,7 +1744,7 @@ suite('ExtensionsWorkbenchServiceTest', () => {
onDidUpdateExtensionMetadata: Event.None,
getInstalled: () => Promise.resolve<ILocalExtension[]>(installed),
installFromGallery: (extension: IGalleryExtension) => Promise.reject(new Error('not supported')),
updateMetadata: async (local: Mutable<ILocalExtension>, metadata: Partial<Metadata>) => {
updateMetadata: async (local: Mutable<ILocalExtension>, metadata: Partial<Metadata>, profileLocation: URI) => {
local.identifier.uuid = metadata.id;
local.publisherDisplayName = metadata.publisherDisplayName!;
local.publisherId = metadata.publisherId!;

View file

@ -78,6 +78,7 @@ export interface IWorkbenchExtensionManagementService extends IProfileAwareExten
installResourceExtension(extension: IResourceExtension, installOptions: InstallOptions): Promise<ILocalExtension>;
updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension, installOptions?: InstallOptions): Promise<ILocalExtension>;
updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>): Promise<ILocalExtension>;
}
export const extensionsConfigurationNodeBase = {

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILocalExtension, IGalleryExtension, InstallOptions, UninstallOptions, Metadata, DidUninstallExtensionEvent, InstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, InstallExtensionInfo, IProductVersion } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ILocalExtension, IGalleryExtension, InstallOptions, UninstallOptions, Metadata, InstallExtensionResult, InstallExtensionInfo, IProductVersion } from 'vs/platform/extensionManagement/common/extensionManagement';
import { URI } from 'vs/base/common/uri';
import { ExtensionIdentifier, ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtensionManagementChannelClient as BaseExtensionManagementChannelClient, ExtensionEventResult } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
@ -32,19 +32,15 @@ export abstract class ProfileAwareExtensionManagementChannelClient extends BaseE
}));
}
protected override fireEvent(event: Emitter<InstallExtensionEvent>, data: InstallExtensionEvent): Promise<void>;
protected override fireEvent(event: Emitter<readonly InstallExtensionResult[]>, data: InstallExtensionResult[]): Promise<void>;
protected override fireEvent(event: Emitter<UninstallExtensionEvent>, data: UninstallExtensionEvent): Promise<void>;
protected override fireEvent(event: Emitter<DidUninstallExtensionEvent>, data: DidUninstallExtensionEvent): Promise<void>;
protected override fireEvent(event: Emitter<ExtensionEventResult>, data: ExtensionEventResult): Promise<void>;
protected override fireEvent(event: Emitter<ExtensionEventResult[]>, data: ExtensionEventResult[]): Promise<void>;
protected override async fireEvent<E extends ExtensionEventResult>(event: Emitter<E>, data: E): Promise<void>;
protected override async fireEvent<E extends ExtensionEventResult>(event: Emitter<readonly E[]>, data: E[]): Promise<void>;
protected override async fireEvent(arg0: any, arg1: any): Promise<void> {
if (Array.isArray(arg1)) {
const event = arg0 as Emitter<ExtensionEventResult[]>;
const data = arg1 as ExtensionEventResult[];
const filtered = [];
for (const e of data) {
const result = this.filterEvent(e);
const result = this.filterEvent(e.profileLocation, e.applicationScoped ?? e.local?.isApplicationScoped ?? false);
if (result instanceof Promise ? await result : result) {
filtered.push(e);
}
@ -55,7 +51,7 @@ export abstract class ProfileAwareExtensionManagementChannelClient extends BaseE
} else {
const event = arg0 as Emitter<ExtensionEventResult>;
const data = arg1 as ExtensionEventResult;
const result = this.filterEvent(data);
const result = this.filterEvent(data.profileLocation, data.applicationScoped ?? data.local?.isApplicationScoped ?? false);
if (result instanceof Promise ? await result : result) {
event.fire(data);
}
@ -141,5 +137,5 @@ export abstract class ProfileAwareExtensionManagementChannelClient extends BaseE
return profileLocation ?? this.userDataProfileService.currentProfile.extensionsResource;
}
protected abstract filterEvent(e: ExtensionEventResult): boolean | Promise<boolean>;
protected abstract filterEvent(profileLocation: URI, isApplicationScoped: boolean): boolean | Promise<boolean>;
}

View file

@ -7,7 +7,8 @@ import { Emitter, Event, EventMultiplexer } from 'vs/base/common/event';
import {
ILocalExtension, IGalleryExtension, IExtensionIdentifier, IExtensionsControlManifest, IExtensionGalleryService, InstallOptions, UninstallOptions, InstallExtensionResult, ExtensionManagementError, ExtensionManagementErrorCode, Metadata, InstallOperation, EXTENSION_INSTALL_SOURCE_CONTEXT, InstallExtensionInfo,
IProductVersion,
ExtensionInstallSource
ExtensionInstallSource,
DidUpdateExtensionMetadata
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { DidChangeProfileForServerEvent, DidUninstallExtensionOnServerEvent, IExtensionManagementServer, IExtensionManagementServerService, InstallExtensionOnServerEvent, IResourceExtension, IWorkbenchExtensionManagementService, UninstallExtensionOnServerEvent } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionType, isLanguagePackExtension, IExtensionManifest, getWorkspaceSupportTypeMessage, TargetPlatform } from 'vs/platform/extensions/common/extensions';
@ -60,7 +61,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
private readonly _onDidUninstallExtension = this._register(new Emitter<DidUninstallExtensionOnServerEvent>());
readonly onDidUninstallExtension: Event<DidUninstallExtensionOnServerEvent>;
readonly onDidUpdateExtensionMetadata: Event<ILocalExtension>;
readonly onDidUpdateExtensionMetadata: Event<DidUpdateExtensionMetadata>;
readonly onDidChangeProfile: Event<DidChangeProfileForServerEvent>;
readonly onDidEnableExtensions: Event<ILocalExtension[]>;
@ -117,7 +118,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
this._register(onDidUninstallExtensionEventMultiplexer.add(this._onDidUninstallExtension.event));
this.onDidUninstallExtension = onDidUninstallExtensionEventMultiplexer.event;
const onDidUpdateExtensionMetadaEventMultiplexer = this._register(new EventMultiplexer<ILocalExtension>());
const onDidUpdateExtensionMetadaEventMultiplexer = this._register(new EventMultiplexer<DidUpdateExtensionMetadata>());
this.onDidUpdateExtensionMetadata = onDidUpdateExtensionMetadaEventMultiplexer.event;
const onDidChangeProfileEventMultiplexer = this._register(new EventMultiplexer<DidChangeProfileForServerEvent>());
@ -217,10 +218,10 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
return Promise.reject(`Invalid location ${extension.location.toString()}`);
}
updateMetadata(extension: ILocalExtension, metadata: Partial<Metadata>, profileLocation?: URI): Promise<ILocalExtension> {
updateMetadata(extension: ILocalExtension, metadata: Partial<Metadata>): Promise<ILocalExtension> {
const server = this.getServer(extension);
if (server) {
return server.extensionManagementService.updateMetadata(extension, metadata, profileLocation);
return server.extensionManagementService.updateMetadata(extension, metadata, this.userDataProfileService.currentProfile.extensionsResource);
}
return Promise.reject(`Invalid location ${extension.location.toString()}`);
}
@ -400,7 +401,12 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
exensions.push({ extension, options });
}
} catch (error) {
results.set(extension.identifier.id.toLowerCase(), { identifier: extension.identifier, source: extension, error, operation: InstallOperation.Install });
results.set(extension.identifier.id.toLowerCase(), {
identifier: extension.identifier,
source: extension, error,
operation: InstallOperation.Install,
profileLocation: options.profileLocation ?? this.userDataProfileService.currentProfile.extensionsResource
});
}
}));
@ -517,7 +523,8 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
identifier: extension.identifier,
server,
applicationScoped: false,
workspaceScoped: true
workspaceScoped: true,
profileLocation: this.userDataProfileService.currentProfile.extensionsResource
});
try {
@ -531,7 +538,8 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
identifier: extension.identifier,
server,
applicationScoped: false,
workspaceScoped: true
workspaceScoped: true,
profileLocation: this.userDataProfileService.currentProfile.extensionsResource
});
} catch (error) {
this.logService.error(`Failed to uninstall the workspace extension ${extension.identifier.id} from ${extension.location.toString()}`, getErrorMessage(error));
@ -540,7 +548,8 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
server,
error,
applicationScoped: false,
workspaceScoped: true
workspaceScoped: true,
profileLocation: this.userDataProfileService.currentProfile.extensionsResource
});
throw error;
}

View file

@ -11,7 +11,6 @@ import { IRemoteUserDataProfilesService } from 'vs/workbench/services/userDataPr
import { ProfileAwareExtensionManagementChannelClient } from 'vs/workbench/services/extensionManagement/common/extensionManagementChannelClient';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { ExtensionEventResult } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export class RemoteExtensionManagementService extends ProfileAwareExtensionManagementChannelClient implements IProfileAwareExtensionManagementService {
@ -26,15 +25,15 @@ export class RemoteExtensionManagementService extends ProfileAwareExtensionManag
super(channel, userDataProfileService, uriIdentityService);
}
protected async filterEvent(e: ExtensionEventResult): Promise<boolean> {
if (e.applicationScoped) {
protected async filterEvent(profileLocation: URI, applicationScoped: boolean): Promise<boolean> {
if (applicationScoped) {
return true;
}
if (!e.profileLocation && this.userDataProfileService.currentProfile.isDefault) {
if (!profileLocation && this.userDataProfileService.currentProfile.isDefault) {
return true;
}
const currentRemoteProfile = await this.remoteUserDataProfilesService.getRemoteProfile(this.userDataProfileService.currentProfile);
if (this.uriIdentityService.extUri.isEqual(currentRemoteProfile.extensionsResource, e.profileLocation)) {
if (this.uriIdentityService.extUri.isEqual(currentRemoteProfile.extensionsResource, profileLocation)) {
return true;
}
return false;

View file

@ -149,7 +149,7 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe
return result;
}
async updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation?: URI): Promise<ILocalExtension> {
async updateMetadata(local: ILocalExtension, metadata: Partial<Metadata>, profileLocation: URI): Promise<ILocalExtension> {
// unset if false
if (metadata.isMachineScoped === false) {
metadata.isMachineScoped = undefined;
@ -160,9 +160,9 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe
if (metadata.pinned === false) {
metadata.pinned = undefined;
}
const updatedExtension = await this.webExtensionsScannerService.updateMetadata(local, metadata, profileLocation ?? this.userDataProfileService.currentProfile.extensionsResource);
const updatedExtension = await this.webExtensionsScannerService.updateMetadata(local, metadata, profileLocation);
const updatedLocalExtension = toLocalExtension(updatedExtension);
this._onDidUpdateExtensionMetadata.fire(updatedLocalExtension);
this._onDidUpdateExtensionMetadata.fire({ local: updatedLocalExtension, profileLocation });
return updatedLocalExtension;
}

View file

@ -33,8 +33,8 @@ export class NativeExtensionManagementService extends ProfileAwareExtensionManag
super(channel, userDataProfileService, uriIdentityService);
}
protected filterEvent({ profileLocation, applicationScoped }: { readonly profileLocation?: URI; readonly applicationScoped?: boolean }): boolean {
return applicationScoped || this.uriIdentityService.extUri.isEqual(this.userDataProfileService.currentProfile.extensionsResource, profileLocation);
protected filterEvent(profileLocation: URI, isApplicationScoped: boolean): boolean {
return isApplicationScoped || this.uriIdentityService.extUri.isEqual(this.userDataProfileService.currentProfile.extensionsResource, profileLocation);
}
override async install(vsix: URI, options?: InstallOptions): Promise<ILocalExtension> {

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as sinon from 'sinon';
import { IExtensionManagementService, DidUninstallExtensionEvent, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementService, DidUninstallExtensionEvent, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, DidUpdateExtensionMetadata } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService, ExtensionInstallLocation, IProfileAwareExtensionManagementService, DidChangeProfileEvent } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionEnablementService } from 'vs/workbench/services/extensionManagement/browser/extensionEnablementService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
@ -70,7 +70,7 @@ export class TestExtensionEnablementService extends ExtensionEnablementService {
onUninstallExtension: disposables.add(new Emitter<UninstallExtensionEvent>()).event,
onDidUninstallExtension: disposables.add(new Emitter<DidUninstallExtensionEvent>()).event,
onDidChangeProfile: disposables.add(new Emitter<DidChangeProfileEvent>()).event,
onDidUpdateExtensionMetadata: disposables.add(new Emitter<ILocalExtension>()).event,
onDidUpdateExtensionMetadata: disposables.add(new Emitter<DidUpdateExtensionMetadata>()).event,
},
}, null, null));
const extensionManagementService = disposables.add(instantiationService.createInstance(ExtensionManagementService));
@ -456,7 +456,7 @@ suite('ExtensionEnablementService Test', () => {
await testObject.setEnablement([extension], EnablementState.DisabledWorkspace);
await testObject.setEnablement([extension], EnablementState.DisabledGlobally);
didUninstallEvent.fire({ identifier: { id: 'pub.a' } });
didUninstallEvent.fire({ identifier: { id: 'pub.a' }, profileLocation: null! });
assert.ok(testObject.isEnabled(extension));
assert.strictEqual(testObject.getEnablementState(extension), EnablementState.EnabledGlobally);