diff --git a/src/vs/workbench/api/browser/mainThreadCLICommands.ts b/src/vs/workbench/api/browser/mainThreadCLICommands.ts index 6e51ecda9cd5e..19fee9b47f96f 100644 --- a/src/vs/workbench/api/browser/mainThreadCLICommands.ts +++ b/src/vs/workbench/api/browser/mainThreadCLICommands.ts @@ -20,7 +20,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { canExecuteOnWorkspace } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { ExtensionKindController } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IExtensionManifest } from 'vs/workbench/workbench.web.api'; @@ -78,10 +78,12 @@ class RemoteExtensionCLIManagementService extends ExtensionManagementCLIService private _location: string | undefined; + private readonly _extensionKindController: ExtensionKindController; + constructor( @IExtensionManagementService extensionManagementService: IExtensionManagementService, - @IProductService private readonly productService: IProductService, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IProductService productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService, @ILocalizationsService localizationsService: ILocalizationsService, @ILabelService labelService: ILabelService, @@ -91,6 +93,8 @@ class RemoteExtensionCLIManagementService extends ExtensionManagementCLIService const remoteAuthority = envService.remoteAuthority; this._location = remoteAuthority ? labelService.getHostLabel(Schemas.vscodeRemote, remoteAuthority) : undefined; + + this._extensionKindController = new ExtensionKindController(productService, configurationService); } protected get location(): string | undefined { @@ -98,7 +102,7 @@ class RemoteExtensionCLIManagementService extends ExtensionManagementCLIService } protected validateExtensionKind(manifest: IExtensionManifest, output: CLIOutput): boolean { - if (!canExecuteOnWorkspace(manifest, this.productService, this.configurationService)) { + if (!this._extensionKindController.canExecuteOnWorkspace(manifest)) { output.log(localize('cannot be installed', "Cannot install the '{0}' extension because it is declared to not run in this setup.", getExtensionId(manifest.publisher, manifest.name))); return false; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 8e9c67e866cfd..631adce569fdb 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -45,7 +45,7 @@ import { alert } from 'vs/base/browser/ui/aria/aria'; import { coalesce } from 'vs/base/common/arrays'; import { IWorkbenchThemeService, IWorkbenchTheme, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IWorkbenchProductIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ILabelService } from 'vs/platform/label/common/label'; -import { prefersExecuteOnUI, prefersExecuteOnWorkspace, canExecuteOnUI, canExecuteOnWorkspace, prefersExecuteOnWeb } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { ExtensionKindController } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IProductService } from 'vs/platform/product/common/productService'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; @@ -435,6 +435,8 @@ export abstract class InstallInOtherServerAction extends ExtensionAction { private static readonly Class = `${ExtensionAction.LABEL_ACTION_CLASS} prominent install`; private static readonly InstallingClass = `${ExtensionAction.LABEL_ACTION_CLASS} install installing`; + private _extensionKindController: ExtensionKindController; + updateWhenCounterExtensionChanges: boolean = true; constructor( @@ -443,11 +445,13 @@ export abstract class InstallInOtherServerAction extends ExtensionAction { private readonly canInstallAnyWhere: boolean, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService, - @IProductService private readonly productService: IProductService, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IProductService productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, ) { super(id, InstallInOtherServerAction.INSTALL_LABEL, InstallInOtherServerAction.Class, false); this.update(); + + this._extensionKindController = new ExtensionKindController(productService, configurationService); } update(): void { @@ -489,28 +493,28 @@ export abstract class InstallInOtherServerAction extends ExtensionAction { } // Prefers to run on UI - if (this.server === this.extensionManagementServerService.localExtensionManagementServer && prefersExecuteOnUI(this.extension.local.manifest, this.productService, this.configurationService)) { + if (this.server === this.extensionManagementServerService.localExtensionManagementServer && this._extensionKindController.prefersExecuteOnUI(this.extension.local.manifest)) { return true; } // Prefers to run on Workspace - if (this.server === this.extensionManagementServerService.remoteExtensionManagementServer && prefersExecuteOnWorkspace(this.extension.local.manifest, this.productService, this.configurationService)) { + if (this.server === this.extensionManagementServerService.remoteExtensionManagementServer && this._extensionKindController.prefersExecuteOnWorkspace(this.extension.local.manifest)) { return true; } // Prefers to run on Web - if (this.server === this.extensionManagementServerService.webExtensionManagementServer && prefersExecuteOnWeb(this.extension.local.manifest, this.productService, this.configurationService)) { + if (this.server === this.extensionManagementServerService.webExtensionManagementServer && this._extensionKindController.prefersExecuteOnWeb(this.extension.local.manifest)) { return true; } if (this.canInstallAnyWhere) { // Can run on UI - if (this.server === this.extensionManagementServerService.localExtensionManagementServer && canExecuteOnUI(this.extension.local.manifest, this.productService, this.configurationService)) { + if (this.server === this.extensionManagementServerService.localExtensionManagementServer && this._extensionKindController.canExecuteOnUI(this.extension.local.manifest)) { return true; } // Can run on Workspace - if (this.server === this.extensionManagementServerService.remoteExtensionManagementServer && canExecuteOnWorkspace(this.extension.local.manifest, this.productService, this.configurationService)) { + if (this.server === this.extensionManagementServerService.remoteExtensionManagementServer && this._extensionKindController.canExecuteOnWorkspace(this.extension.local.manifest)) { return true; } } @@ -1193,18 +1197,22 @@ export class ReloadAction extends ExtensionAction { updateWhenCounterExtensionChanges: boolean = true; private _runningExtensions: IExtensionDescription[] | null = null; + private _extensionKindController: ExtensionKindController; + constructor( @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IHostService private readonly hostService: IHostService, @IExtensionService private readonly extensionService: IExtensionService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, - @IProductService private readonly productService: IProductService, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IProductService productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, ) { super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false); this._register(this.extensionService.onDidChangeExtensions(this.updateRunningExtensions, this)); this.updateRunningExtensions(); + + this._extensionKindController = new ExtensionKindController(productService, configurationService); } private updateRunningExtensions(): void { @@ -1272,7 +1280,7 @@ export class ReloadAction extends ExtensionAction { const extensionInOtherServer = this.extensionsWorkbenchService.installed.filter(e => areSameExtensions(e.identifier, this.extension!.identifier) && e.server !== this.extension!.server)[0]; if (extensionInOtherServer) { // This extension prefers to run on UI/Local side but is running in remote - if (runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer && prefersExecuteOnUI(this.extension.local!.manifest, this.productService, this.configurationService)) { + if (runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer && this._extensionKindController.prefersExecuteOnUI(this.extension.local!.manifest)) { this.enabled = true; this.label = localize('reloadRequired', "Reload Required"); this.tooltip = localize('enable locally', "Please reload Visual Studio Code to enable this extension locally."); @@ -1280,7 +1288,7 @@ export class ReloadAction extends ExtensionAction { } // This extension prefers to run on Workspace/Remote side but is running in local - if (runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer && prefersExecuteOnWorkspace(this.extension.local!.manifest, this.productService, this.configurationService)) { + if (runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer && this._extensionKindController.prefersExecuteOnWorkspace(this.extension.local!.manifest)) { this.enabled = true; this.label = localize('reloadRequired', "Reload Required"); this.tooltip = localize('enable remote', "Please reload Visual Studio Code to enable this extension in {0}.", this.extensionManagementServerService.remoteExtensionManagementServer?.label); @@ -1292,7 +1300,7 @@ export class ReloadAction extends ExtensionAction { if (this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer) { // This extension prefers to run on UI/Local side but is running in remote - if (prefersExecuteOnUI(this.extension.local!.manifest, this.productService, this.configurationService)) { + if (this._extensionKindController.prefersExecuteOnUI(this.extension.local!.manifest)) { this.enabled = true; this.label = localize('reloadRequired', "Reload Required"); this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to enable this extension."); @@ -1300,7 +1308,7 @@ export class ReloadAction extends ExtensionAction { } if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer) { // This extension prefers to run on Workspace/Remote side but is running in local - if (prefersExecuteOnWorkspace(this.extension.local!.manifest, this.productService, this.configurationService)) { + if (this._extensionKindController.prefersExecuteOnWorkspace(this.extension.local!.manifest)) { this.enabled = true; this.label = localize('reloadRequired', "Reload Required"); this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to enable this extension."); @@ -2064,13 +2072,15 @@ export class SystemDisabledWarningAction extends ExtensionAction { updateWhenCounterExtensionChanges: boolean = true; private _runningExtensions: IExtensionDescription[] | null = null; + private _extensionKindController: ExtensionKindController; + constructor( @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @ILabelService private readonly labelService: ILabelService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionService private readonly extensionService: IExtensionService, - @IProductService private readonly productService: IProductService, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IProductService productService: IProductService, + @IConfigurationService configurationService: IConfigurationService, @IWorkspaceTrustService private readonly workspaceTrustService: IWorkspaceTrustService ) { super('extensions.install', '', `${SystemDisabledWarningAction.CLASS} hide`, false); @@ -2078,6 +2088,8 @@ export class SystemDisabledWarningAction extends ExtensionAction { this._register(this.extensionService.onDidChangeExtensions(this.updateRunningExtensions, this)); this.updateRunningExtensions(); this.update(); + + this._extensionKindController = new ExtensionKindController(productService, configurationService); } private updateRunningExtensions(): void { @@ -2123,14 +2135,14 @@ export class SystemDisabledWarningAction extends ExtensionAction { const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier))[0]; const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(toExtension(runningExtension)) : null; if (this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer) { - if (prefersExecuteOnWorkspace(this.extension.local!.manifest, this.productService, this.configurationService)) { + if (this._extensionKindController.prefersExecuteOnWorkspace(this.extension.local!.manifest)) { this.class = `${SystemDisabledWarningAction.INFO_CLASS}`; this.tooltip = localize('disabled locally', "Extension is enabled on '{0}' and disabled locally.", this.extensionManagementServerService.remoteExtensionManagementServer.label); } return; } if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer) { - if (prefersExecuteOnUI(this.extension.local!.manifest, this.productService, this.configurationService)) { + if (this._extensionKindController.prefersExecuteOnUI(this.extension.local!.manifest)) { this.class = `${SystemDisabledWarningAction.INFO_CLASS}`; this.tooltip = localize('disabled remotely', "Extension is enabled locally and disabled on '{0}'.", this.extensionManagementServerService.remoteExtensionManagementServer.label); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 6cf58c3a33efe..a0897dfc4908a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -36,7 +36,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IExtensionManifest, ExtensionType, IExtension as IPlatformExtension } from 'vs/platform/extensions/common/extensions'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IProductService } from 'vs/platform/product/common/productService'; -import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { ExtensionKindController } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { FileAccess } from 'vs/base/common/network'; import { IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; import { IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; @@ -506,6 +506,8 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension private installing: IExtension[] = []; + private readonly extensionKindController: ExtensionKindController; + constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, @IEditorService private readonly editorService: IEditorService, @@ -568,6 +570,8 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension this.updateContexts(); this.updateActivity(); })); + + this.extensionKindController = new ExtensionKindController(productService, configurationService); } get local(): IExtension[] { @@ -694,7 +698,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return extensionsToChoose[0]; } - const extensionKinds = getExtensionKind(manifest, this.productService, this.configurationService); + const extensionKinds = this.extensionKindController.getExtensionKind(manifest); let extension = extensionsToChoose.find(extension => { for (const extensionKind of extensionKinds) { diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts index a165e2a432647..f1bf55fbe5ea7 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts @@ -14,7 +14,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtension, isAuthenticaionProviderExtension, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { ExtensionKindController } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IProductService } from 'vs/platform/product/common/productService'; import { StorageManager } from 'vs/platform/extensionManagement/common/extensionEnablementService'; @@ -40,6 +40,8 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench private readonly storageManger: StorageManager; private extensionsDisabledByTrustRequirement: IExtension[] = []; + private readonly extensionKindController: ExtensionKindController; + constructor( @IStorageService storageService: IStorageService, @IGlobalExtensionEnablementService protected readonly globalExtensionEnablementService: IGlobalExtensionEnablementService, @@ -48,7 +50,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IConfigurationService private readonly configurationService: IConfigurationService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, - @IProductService private readonly productService: IProductService, + @IProductService productService: IProductService, @IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService, @IUserDataSyncAccountService private readonly userDataSyncAccountService: IUserDataSyncAccountService, @ILifecycleService private readonly lifecycleService: ILifecycleService, @@ -86,6 +88,8 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench }]); }); } + + this.extensionKindController = new ExtensionKindController(productService, configurationService); } private get hasWorkspace(): boolean { @@ -244,7 +248,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench private _isDisabledByExtensionKind(extension: IExtension): boolean { if (this.extensionManagementServerService.remoteExtensionManagementServer || this.extensionManagementServerService.webExtensionManagementServer) { const server = this.extensionManagementServerService.getExtensionManagementServer(extension); - for (const extensionKind of getExtensionKind(extension.manifest, this.productService, this.configurationService)) { + for (const extensionKind of this.extensionKindController.getExtensionKind(extension.manifest)) { if (extensionKind === 'ui') { if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.localExtensionManagementServer === server) { return false; diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index 434c05518c74a..c6a2489375bf1 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -15,7 +15,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { CancellationToken } from 'vs/base/common/cancellation'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localize } from 'vs/nls'; -import { prefersExecuteOnUI, getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { ExtensionKindController } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IProductService } from 'vs/platform/product/common/productService'; import { Schemas } from 'vs/base/common/network'; import { IDownloadService } from 'vs/platform/download/common/download'; @@ -38,6 +38,8 @@ export class ExtensionManagementService extends Disposable implements IWorkbench protected readonly servers: IExtensionManagementServer[] = []; + protected readonly extensionKindController: ExtensionKindController; + constructor( @IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @@ -64,6 +66,8 @@ export class ExtensionManagementService extends Disposable implements IWorkbench this.onDidInstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onDidInstallExtension); return emitter; }, new EventMultiplexer())).event; this.onUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onUninstallExtension); return emitter; }, new EventMultiplexer())).event; this.onDidUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onDidUninstallExtension); return emitter; }, new EventMultiplexer())).event; + + this.extensionKindController = new ExtensionKindController(productService, configurationService); } async getInstalled(type?: ExtensionType): Promise { @@ -107,7 +111,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench private async uninstallInServer(extension: ILocalExtension, server: IExtensionManagementServer, options?: UninstallOptions): Promise { if (server === this.extensionManagementServerService.localExtensionManagementServer) { const installedExtensions = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.getInstalled(ExtensionType.User); - const dependentNonUIExtensions = installedExtensions.filter(i => !prefersExecuteOnUI(i.manifest, this.productService, this.configurationService) + const dependentNonUIExtensions = installedExtensions.filter(i => !this.extensionKindController.prefersExecuteOnUI(i.manifest) && i.manifest.extensionDependencies && i.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.identifier))); if (dependentNonUIExtensions.length) { return Promise.reject(new Error(this.getDependentsErrorMessage(extension, dependentNonUIExtensions))); @@ -178,7 +182,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench const [local] = await Promises.settled([this.extensionManagementServerService.localExtensionManagementServer, this.extensionManagementServerService.remoteExtensionManagementServer].map(server => this.installVSIX(vsix, server))); return local; } - if (prefersExecuteOnUI(manifest, this.productService, this.configurationService)) { + if (this.extensionKindController.prefersExecuteOnUI(manifest)) { // Install only on local server return this.installVSIX(vsix, this.extensionManagementServerService.localExtensionManagementServer); } @@ -293,7 +297,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench return this.extensionManagementServerService.localExtensionManagementServer; } - const extensionKind = getExtensionKind(manifest, this.productService, this.configurationService); + const extensionKind = this.extensionKindController.getExtensionKind(manifest); for (const kind of extensionKind) { if (kind === 'ui' && this.extensionManagementServerService.localExtensionManagementServer) { return this.extensionManagementServerService.localExtensionManagementServer; diff --git a/src/vs/workbench/services/extensionManagement/common/remoteExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/remoteExtensionManagementService.ts index 908ce5a2ab1ea..a16c16fe2b401 100644 --- a/src/vs/workbench/services/extensionManagement/common/remoteExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/remoteExtensionManagementService.ts @@ -5,7 +5,7 @@ import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IExtensionManagementService, IGalleryExtension, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { canExecuteOnWorkspace } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { ExtensionKindController } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IProductService } from 'vs/platform/product/common/productService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -13,6 +13,8 @@ import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagemen export class WebRemoteExtensionManagementService extends ExtensionManagementChannelClient implements IExtensionManagementService { + protected readonly extensionKindController: ExtensionKindController; + constructor( channel: IChannel, @IExtensionGalleryService protected readonly galleryService: IExtensionGalleryService, @@ -20,11 +22,13 @@ export class WebRemoteExtensionManagementService extends ExtensionManagementChan @IProductService protected readonly productService: IProductService ) { super(channel); + + this.extensionKindController = new ExtensionKindController(productService, configurationService); } async canInstall(extension: IGalleryExtension): Promise { const manifest = await this.galleryService.getManifest(extension, CancellationToken.None); - return !!manifest && canExecuteOnWorkspace(manifest, this.productService, this.configurationService); + return !!manifest && this.extensionKindController.canExecuteOnWorkspace(manifest); } } diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts index b011255714592..3509317377005 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts @@ -10,7 +10,6 @@ import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ILogService } from 'vs/platform/log/common/log'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { prefersExecuteOnUI } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; @@ -126,7 +125,7 @@ export class NativeRemoteExtensionManagementService extends WebRemoteExtensionMa for (let idx = 0; idx < extensions.length; idx++) { const extension = extensions[idx]; const manifest = manifests[idx]; - if (manifest && prefersExecuteOnUI(manifest, this.productService, this.configurationService) === uiExtension) { + if (manifest && this.extensionKindController.prefersExecuteOnUI(manifest) === uiExtension) { result.set(extension.identifier.id.toLowerCase(), extension); extensionsManifests.push(manifest); } diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index dc32e893c793c..a02e9ad9f98f0 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -50,8 +50,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten ) { super( new ExtensionRunningLocationClassifier( - productService, - configurationService, + manifest => this._getExtensionKind(manifest), (extensionKinds, isInstalledLocally, isInstalledRemotely) => ExtensionService.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely) ), instantiationService, diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 94f39dc0dbfb7..1f47dc9a37b87 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -21,7 +21,7 @@ import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensi import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; -import { ExtensionIdentifier, IExtensionDescription, ExtensionType, ITranslatedScannedExtension, IExtension, ExtensionKind, IExtensionContributions } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription, ExtensionType, ITranslatedScannedExtension, IExtension, ExtensionKind, IExtensionContributions, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -29,7 +29,7 @@ import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtens import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionActivationHost as IWorkspaceContainsActivationHost, checkGlobFileExists, checkActivateWorkspaceContainsExtension } from 'vs/workbench/api/common/shared/workspaceContains'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { ExtensionKindController } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Schemas } from 'vs/base/common/network'; @@ -98,6 +98,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx private _extensionHostActivationTimes: Map; private _extensionHostExtensionRuntimeErrors: Map; + private readonly _extensionKindController: ExtensionKindController; + constructor( protected readonly _runningLocationClassifier: ExtensionRunningLocationClassifier, @IInstantiationService protected readonly _instantiationService: IInstantiationService, @@ -137,6 +139,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx this._inHandleDeltaExtensions = false; this._runningLocation = new Map(); + this._extensionKindController = new ExtensionKindController(this._productService, this._configurationService); + this._register(this._extensionEnablementService.onEnablementChanged((extensions) => { let toAdd: IExtension[] = []; let toRemove: string[] = []; @@ -169,6 +173,10 @@ export abstract class AbstractExtensionService extends Disposable implements IEx })); } + protected _getExtensionKind(manifest: IExtensionManifest): ExtensionKind[] { + return this._extensionKindController.getExtensionKind(manifest); + } + protected _getExtensionHostManager(kind: ExtensionHostKind): ExtensionHostManager | null { for (const extensionHostManager of this._extensionHostManagers) { if (extensionHostManager.kind === kind) { @@ -277,7 +285,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx groupedToAdd[extensionHostKind] = filterByRunningLocation(toAdd, ext => ext.identifier, this._runningLocation, extensionRunningLocation); }; for (const extension of toAdd) { - const extensionKind = getExtensionKind(extension, this._productService, this._configurationService); + const extensionKind = this._getExtensionKind(extension); const isRemote = extension.extensionLocation.scheme === Schemas.vscodeRemote; const runningLocation = this._runningLocationClassifier.pickRunningLocation(extensionKind, !isRemote, isRemote); this._runningLocation.set(ExtensionIdentifier.toKey(extension.identifier), runningLocation); @@ -318,7 +326,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx return false; } - const extensionKind = getExtensionKind(extension.manifest, this._productService, this._configurationService); + const extensionKind = this._getExtensionKind(extension.manifest); const isRemote = extension.location.scheme === Schemas.vscodeRemote; const runningLocation = this._runningLocationClassifier.pickRunningLocation(extensionKind, !isRemote, isRemote); if (runningLocation === ExtensionRunningLocation.None) { @@ -771,16 +779,15 @@ export abstract class AbstractExtensionService extends Disposable implements IEx export class ExtensionRunningLocationClassifier { constructor( - @IProductService private readonly _productService: IProductService, - @IConfigurationService private readonly _configurationService: IConfigurationService, + public readonly getExtensionKind: (manifest: IExtensionManifest) => ExtensionKind[], public readonly pickRunningLocation: (extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean) => ExtensionRunningLocation, ) { } public determineRunningLocation(localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[]): Map { const allExtensionKinds = new Map(); - localExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), getExtensionKind(ext, this._productService, this._configurationService))); - remoteExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), getExtensionKind(ext, this._productService, this._configurationService))); + localExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), this.getExtensionKind(ext))); + remoteExtensions.forEach(ext => allExtensionKinds.set(ExtensionIdentifier.toKey(ext.identifier), this.getExtensionKind(ext))); const localExtensionsSet = new Set(); localExtensions.forEach(ext => localExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier))); diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts index 65e532ee58dfc..ce539f48eeae3 100644 --- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts +++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts @@ -10,56 +10,64 @@ import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/ex import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IProductService } from 'vs/platform/product/common/productService'; -export function prefersExecuteOnUI(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { - const extensionKind = getExtensionKind(manifest, productService, configurationService); - return (extensionKind.length > 0 && extensionKind[0] === 'ui'); -} - -export function prefersExecuteOnWorkspace(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { - const extensionKind = getExtensionKind(manifest, productService, configurationService); - return (extensionKind.length > 0 && extensionKind[0] === 'workspace'); -} - -export function prefersExecuteOnWeb(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { - const extensionKind = getExtensionKind(manifest, productService, configurationService); - return (extensionKind.length > 0 && extensionKind[0] === 'web'); -} -export function canExecuteOnUI(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { - const extensionKind = getExtensionKind(manifest, productService, configurationService); - return extensionKind.some(kind => kind === 'ui'); -} +export class ExtensionKindController { + constructor( + @IProductService private readonly productService: IProductService, + @IConfigurationService private readonly configurationService: IConfigurationService, + ) { + } + prefersExecuteOnUI(manifest: IExtensionManifest): boolean { + const extensionKind = this.getExtensionKind(manifest); + return (extensionKind.length > 0 && extensionKind[0] === 'ui'); + } -export function canExecuteOnWorkspace(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { - const extensionKind = getExtensionKind(manifest, productService, configurationService); - return extensionKind.some(kind => kind === 'workspace'); -} + prefersExecuteOnWorkspace(manifest: IExtensionManifest): boolean { + const extensionKind = this.getExtensionKind(manifest); + return (extensionKind.length > 0 && extensionKind[0] === 'workspace'); + } -export function canExecuteOnWeb(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { - const extensionKind = getExtensionKind(manifest, productService, configurationService); - return extensionKind.some(kind => kind === 'web'); -} + prefersExecuteOnWeb(manifest: IExtensionManifest): boolean { + const extensionKind = this.getExtensionKind(manifest); + return (extensionKind.length > 0 && extensionKind[0] === 'web'); + } -export function getExtensionKind(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): ExtensionKind[] { - // check in config - let result = getConfiguredExtensionKind(manifest, configurationService); - if (typeof result !== 'undefined') { - return toArray(result); + canExecuteOnUI(manifest: IExtensionManifest): boolean { + const extensionKind = this.getExtensionKind(manifest); + return extensionKind.some(kind => kind === 'ui'); } - // check product.json - result = getProductExtensionKind(manifest, productService); - if (typeof result !== 'undefined') { - return result; + canExecuteOnWorkspace(manifest: IExtensionManifest): boolean { + const extensionKind = this.getExtensionKind(manifest); + return extensionKind.some(kind => kind === 'workspace'); } - // check the manifest itself - result = manifest.extensionKind; - if (typeof result !== 'undefined') { - return toArray(result); + canExecuteOnWeb(manifest: IExtensionManifest): boolean { + const extensionKind = this.getExtensionKind(manifest); + return extensionKind.some(kind => kind === 'web'); } - return deduceExtensionKind(manifest); + getExtensionKind(manifest: IExtensionManifest): ExtensionKind[] { + // check in config + let result = getConfiguredExtensionKind(manifest, this.configurationService); + if (typeof result !== 'undefined') { + return toArray(result); + } + + // check product.json + result = getProductExtensionKind(manifest, this.productService); + if (typeof result !== 'undefined') { + return result; + } + + // check the manifest itself + result = manifest.extensionKind; + if (typeof result !== 'undefined') { + return toArray(result); + } + + return deduceExtensionKind(manifest); + } } export function deduceExtensionKind(manifest: IExtensionManifest): ExtensionKind[] { @@ -141,3 +149,4 @@ function toArray(extensionKind: ExtensionKind | ExtensionKind[]): ExtensionKind[ } return extensionKind === 'ui' ? ['ui', 'workspace'] : [extensionKind]; } + diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 6912fe5bf52aa..6695278dda22a 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -70,8 +70,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten ) { super( new ExtensionRunningLocationClassifier( - productService, - configurationService, + manifest => this._getExtensionKind(manifest), (extensionKinds, isInstalledLocally, isInstalledRemotely) => this._pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely) ), instantiationService,