From c6f4d5c3d66c3acb97f59f991c340f2a6e2b81ec Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 28 Oct 2019 23:40:46 +0100 Subject: [PATCH] Support that extensionKind can be an array --- .../platform/extensions/common/extensions.ts | 2 +- .../platform/product/common/productService.ts | 3 +- .../extensions/browser/extensionsActions.ts | 6 +- .../electron-browser/remote.contribution.ts | 24 ++-- .../common/extensionEnablementService.ts | 4 +- .../common/extensionManagementService.ts | 8 +- .../extensions/browser/extensionService.ts | 10 +- .../extensions/common/extensionsRegistry.ts | 22 +-- .../extensions/common/extensionsUtil.ts | 133 +++++++++++++----- .../electron-browser/extensionService.ts | 42 ++++-- .../remoteExtensionManagementIpc.ts | 4 +- 11 files changed, 176 insertions(+), 82 deletions(-) diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 7424cccf046..99a37eaa9f2 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -143,7 +143,7 @@ export interface IExtensionManifest { readonly activationEvents?: string[]; readonly extensionDependencies?: string[]; readonly extensionPack?: string[]; - readonly extensionKind?: ExtensionKind; + readonly extensionKind?: ExtensionKind | ExtensionKind[]; readonly contributes?: IExtensionContributions; readonly repository?: { url: string; }; readonly bugs?: { url: string; }; diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index 5aa5c32d7ea..e905ecfa297 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ExtensionKind } from 'vs/platform/extensions/common/extensions'; export const IProductService = createDecorator('productService'); @@ -96,7 +97,7 @@ export interface IProductConfiguration { readonly portable?: string; - readonly uiExtensions?: readonly string[]; + readonly extensionKind?: { readonly [extensionId: string]: ExtensionKind | ExtensionKind[]; }; readonly extensionAllowedProposedApi?: readonly string[]; readonly msftInternalDomains?: string[]; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index f9741e10981..60e3786f299 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -54,7 +54,7 @@ import { alert } from 'vs/base/browser/ui/aria/aria'; import { coalesce } from 'vs/base/common/arrays'; import { IWorkbenchThemeService, COLOR_THEME_SETTING, ICON_THEME_SETTING, IFileIconTheme, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ILabelService } from 'vs/platform/label/common/label'; -import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { prefersExecuteOnUI } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -198,7 +198,7 @@ export class InstallAction extends ExtensionAction { this.tooltip = InstallAction.INSTALLING_LABEL; } else { if (this._manifest && this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { - if (isUIExtension(this._manifest, this.productService, this.configurationService)) { + if (prefersExecuteOnUI(this._manifest, this.productService, this.configurationService)) { this.label = `${InstallAction.INSTALL_LABEL} ${localize('locally', "Locally")}`; this.tooltip = `${InstallAction.INSTALL_LABEL} ${localize('locally', "Locally")}`; } else { @@ -1653,7 +1653,7 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { private async installExtension(extension: IExtension): Promise { try { if (extension.local && extension.gallery) { - if (isUIExtension(extension.local.manifest, this.productService, this.configurationService)) { + if (prefersExecuteOnUI(extension.local.manifest, this.productService, this.configurationService)) { if (this.extensionManagementServerService.localExtensionManagementServer) { await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(extension.gallery); return; diff --git a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts index 5770a6e4389..be4b4a65c8f 100644 --- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts @@ -365,6 +365,18 @@ workbenchContributionsRegistry.registerWorkbenchContribution(RemoteWindowActiveI workbenchContributionsRegistry.registerWorkbenchContribution(RemoteTelemetryEnablementUpdater, LifecyclePhase.Ready); workbenchContributionsRegistry.registerWorkbenchContribution(RemoteEmptyWorkbenchPresentation, LifecyclePhase.Starting); +const extensionKindSchema = { + type: 'string', + enum: [ + 'ui', + 'workspace' + ], + enumDescriptions: [ + nls.localize('ui', "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine."), + nls.localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote.") + ], +}; + Registry.as(ConfigurationExtensions.Configuration) .registerConfiguration({ id: 'remote', @@ -376,16 +388,8 @@ Registry.as(ConfigurationExtensions.Configuration) markdownDescription: nls.localize('remote.extensionKind', "Override the kind of an extension. `ui` extensions are installed and run on the local machine while `workspace` extensions are run on the remote. By overriding an extension's default kind using this setting, you specify if that extension should be installed and enabled locally or remotely."), patternProperties: { '([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$': { - type: 'string', - enum: [ - 'ui', - 'workspace' - ], - enumDescriptions: [ - nls.localize('ui', "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine."), - nls.localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote.") - ], - default: 'ui' + oneOf: [{ type: 'array', items: extensionKindSchema }, extensionKindSchema], + default: 'ui', }, }, default: { diff --git a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts index 8708f30a37c..c64ba121c87 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts @@ -15,7 +15,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { isUndefinedOrNull } from 'vs/base/common/types'; import { ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { canExecuteOnUI } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -147,7 +147,7 @@ export class ExtensionEnablementService extends Disposable implements IExtension private _isDisabledByExtensionKind(extension: IExtension): boolean { if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { - if (!isUIExtension(extension.manifest, this.productService, this.configurationService)) { + if (!canExecuteOnUI(extension.manifest, this.productService, this.configurationService)) { // workspace extensions must run on the remote, but UI extensions can run on either side const server = this.extensionManagementServerService.remoteExtensionManagementServer; return this.extensionManagementServerService.getExtensionManagementServer(extension.location) !== server; diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index 42a624ae731..63844f5d12a 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 { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { prefersExecuteOnUI } 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'; @@ -93,7 +93,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private async uninstallInServer(extension: ILocalExtension, server: IExtensionManagementServer, force?: boolean): Promise { if (server === this.extensionManagementServerService.localExtensionManagementServer) { const installedExtensions = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.getInstalled(ExtensionType.User); - const dependentNonUIExtensions = installedExtensions.filter(i => !isUIExtension(i.manifest, this.productService, this.configurationService) + const dependentNonUIExtensions = installedExtensions.filter(i => !prefersExecuteOnUI(i.manifest, this.productService, this.configurationService) && i.manifest.extensionDependencies && i.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.identifier))); if (dependentNonUIExtensions.length) { return Promise.reject(new Error(this.getDependentsErrorMessage(extension, dependentNonUIExtensions))); @@ -152,7 +152,7 @@ export class ExtensionManagementService extends Disposable implements IExtension const [local] = await Promise.all(this.servers.map(server => this.installVSIX(vsix, server))); return local; } - if (isUIExtension(manifest, this.productService, this.configurationService)) { + if (prefersExecuteOnUI(manifest, this.productService, this.configurationService)) { // Install only on local server return this.installVSIX(vsix, this.extensionManagementServerService.localExtensionManagementServer); } @@ -190,7 +190,7 @@ export class ExtensionManagementService extends Disposable implements IExtension // Install on both servers return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local); } - if (isUIExtension(manifest, this.productService, this.configurationService)) { + if (prefersExecuteOnUI(manifest, this.productService, this.configurationService)) { // Install only on local server return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); } diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 000e5f7b4ab..c29072929eb 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -20,7 +20,7 @@ import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEn import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { WebWorkerExtensionHostStarter } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter'; import { URI } from 'vs/base/common/uri'; -import { isWebExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { canExecuteOnWeb } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { FetchFileSystemProvider } from 'vs/workbench/services/extensions/browser/webWorkerFileSystemProvider'; @@ -85,14 +85,14 @@ export class ExtensionService extends AbstractExtensionService implements IExten protected _createExtensionHosts(_isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] { const result: ExtensionHostProcessManager[] = []; - const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => isWebExtension(ext, this._configService))); + const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => canExecuteOnWeb(ext, this._productService, this._configService))); const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.file(this._environmentService.logsPath).with({ scheme: this._environmentService.logFile.scheme })); const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, null, initialActivationEvents); result.push(webHostProcessManager); const remoteAgentConnection = this._remoteAgentService.getConnection(); if (remoteAgentConnection) { - const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !isWebExtension(ext, this._configService))); + const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !canExecuteOnWeb(ext, this._productService, this._configService))); const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, remoteExtensions, this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory); const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); result.push(remoteExtHostProcessManager); @@ -111,7 +111,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten let result: DeltaExtensionsResult; // local: only enabled and web'ish extension - localExtensions = localExtensions.filter(ext => this._isEnabled(ext) && isWebExtension(ext, this._configService)); + localExtensions = localExtensions.filter(ext => this._isEnabled(ext) && canExecuteOnWeb(ext, this._productService, this._configService)); this._checkEnableProposedApi(localExtensions); if (!remoteEnv) { @@ -119,7 +119,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten } else { // remote: only enabled and none-web'ish extension - remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !isWebExtension(extension, this._configService)); + remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !canExecuteOnWeb(extension, this._productService, this._configService)); this._checkEnableProposedApi(remoteEnv.extensions); // in case of overlap, the remote wins diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 4f2765ac997..e5d9951aa57 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -146,6 +146,18 @@ export class ExtensionPoint implements IExtensionPoint { } } +const extensionKindSchema = { + type: 'string', + enum: [ + 'ui', + 'workspace' + ], + enumDescriptions: [ + nls.localize('ui', "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine."), + nls.localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote.") + ], +}; + const schemaId = 'vscode://schemas/vscode-extensions'; export const schema = { properties: { @@ -346,15 +358,7 @@ export const schema = { }, extensionKind: { description: nls.localize('extensionKind', "Define the kind of an extension. `ui` extensions are installed and run on the local machine while `workspace` extensions are run on the remote."), - type: 'string', - enum: [ - 'ui', - 'workspace' - ], - enumDescriptions: [ - nls.localize('ui', "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine."), - nls.localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote.") - ], + oneOf: [{ type: 'array', items: extensionKindSchema }, extensionKindSchema], default: 'workspace' }, scripts: { diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts index 49b2d270c04..83fdf37fb67 100644 --- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts +++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts @@ -4,55 +4,114 @@ *--------------------------------------------------------------------------------------------*/ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManifest, ExtensionKind, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { getGalleryExtensionId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IProductService } from 'vs/platform/product/common/productService'; -export function isWebExtension(manifest: IExtensionManifest, configurationService: IConfigurationService): boolean { - const extensionKind = getExtensionKind(manifest, configurationService); - return extensionKind === 'web'; +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 isUIExtension(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { - const uiContributions = ExtensionsRegistry.getExtensionPoints().filter(e => e.defaultExtensionKind !== 'workspace').map(e => e.name); - const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name); - const extensionKind = getExtensionKind(manifest, configurationService); - switch (extensionKind) { - case 'ui': return true; - case 'workspace': return false; - default: { - // Tagged as UI extension in product - if (isNonEmptyArray(productService.uiExtensions) && productService.uiExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) { - return true; - } - // Not an UI extension if it has main - if (manifest.main) { - return false; - } - // Not an UI extension if it has dependencies or an extension pack - if (isNonEmptyArray(manifest.extensionDependencies) || isNonEmptyArray(manifest.extensionPack)) { - return false; - } - if (manifest.contributes) { - // Not an UI extension if it has no ui contributions - if (!uiContributions.length || Object.keys(manifest.contributes).some(contribution => uiContributions.indexOf(contribution) === -1)) { - return false; - } +export function canExecuteOnUI(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { + const extensionKind = getExtensionKind(manifest, productService, configurationService); + return extensionKind.some(kind => kind === 'ui'); +} + +export function canExecuteOnWeb(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { + const extensionKind = getExtensionKind(manifest, productService, configurationService); + return extensionKind.some(kind => kind === '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); + } + + // check the manifest itself + result = manifest.extensionKind; + if (typeof result !== 'undefined') { + return toArray(result); + } + + // check product.json + result = getProductExtensionKind(manifest, productService); + if (typeof result !== 'undefined') { + return toArray(result); + } + + // Not an UI extension if it has main + if (manifest.main) { + return ['workspace']; + } + + // Not an UI extension if it has dependencies or an extension pack + if (isNonEmptyArray(manifest.extensionDependencies) || isNonEmptyArray(manifest.extensionPack)) { + return ['workspace']; + } + + if (manifest.contributes) { + // Not an UI extension if it has no ui contributions + for (const contribution of Object.keys(manifest.contributes)) { + if (!isUIExtensionPoint(contribution)) { + return ['workspace']; } - return true; } } + + return ['ui', 'workspace']; +} + +let _uiExtensionPoints: Set | null = null; +function isUIExtensionPoint(extensionPoint: string): boolean { + if (_uiExtensionPoints === null) { + const uiExtensionPoints = new Set(); + ExtensionsRegistry.getExtensionPoints().filter(e => e.defaultExtensionKind !== 'workspace').forEach(e => { + uiExtensionPoints.add(e.name); + }); + _uiExtensionPoints = uiExtensionPoints; + } + return _uiExtensionPoints.has(extensionPoint); } -function getExtensionKind(manifest: IExtensionManifest, configurationService: IConfigurationService): string | undefined { +let _productExtensionKindsMap: Map | null = null; +function getProductExtensionKind(manifest: IExtensionManifest, productService: IProductService): ExtensionKind | ExtensionKind[] | undefined { + if (_productExtensionKindsMap === null) { + const productExtensionKindsMap = new Map(); + if (productService.extensionKind) { + for (const id of Object.keys(productService.extensionKind)) { + productExtensionKindsMap.set(ExtensionIdentifier.toKey(id), productService.extensionKind[id]); + } + } + _productExtensionKindsMap = productExtensionKindsMap; + } + const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name); - const configuredExtensionKinds = configurationService.getValue<{ [key: string]: string }>('remote.extensionKind') || {}; - for (const id of Object.keys(configuredExtensionKinds)) { - if (areSameExtensions({ id: extensionId }, { id })) { - return configuredExtensionKinds[id]; + return _productExtensionKindsMap.get(ExtensionIdentifier.toKey(extensionId)); +} + +let _configuredExtensionKindsMap: Map | null = null; +function getConfiguredExtensionKind(manifest: IExtensionManifest, configurationService: IConfigurationService): ExtensionKind | ExtensionKind[] | undefined { + if (_configuredExtensionKindsMap === null) { + const configuredExtensionKindsMap = new Map(); + const configuredExtensionKinds = configurationService.getValue<{ [key: string]: ExtensionKind | ExtensionKind[] }>('remote.extensionKind') || {}; + for (const id of Object.keys(configuredExtensionKinds)) { + configuredExtensionKindsMap.set(ExtensionIdentifier.toKey(id), configuredExtensionKinds[id]); } + _configuredExtensionKindsMap = configuredExtensionKindsMap; + } + + const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name); + return _configuredExtensionKindsMap.get(ExtensionIdentifier.toKey(extensionId)); +} + +function toArray(extensionKind: ExtensionKind | ExtensionKind[]): ExtensionKind[] { + if (Array.isArray(extensionKind)) { + return extensionKind; } - return manifest.extensionKind; + return [extensionKind]; } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 91c637543c0..bdcbe53192d 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -19,7 +19,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IInitDataProvider, RemoteExtensionHostClient } from 'vs/workbench/services/extensions/common/remoteExtensionHostClient'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { isUIExtension as isUIExtensionFunc } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -434,8 +434,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten } protected async _scanAndHandleExtensions(): Promise { - const isUIExtension = (extension: IExtensionDescription) => isUIExtensionFunc(extension, this._productService, this._configurationService); - this._extensionScanner.startScanningExtensions(this.createLogger()); const remoteAuthority = this._environmentService.configuration.remoteAuthority; @@ -505,14 +503,42 @@ export class ExtensionService extends AbstractExtensionService implements IExten // remove disabled extensions remoteEnv.extensions = remove(remoteEnv.extensions, extension => this._isDisabled(extension)); + // Determine where each extension will execute, based on extensionKind + const isInstalledLocally = new Set(); + localExtensions.forEach(ext => isInstalledLocally.add(ExtensionIdentifier.toKey(ext.identifier))); + + const isInstalledRemotely = new Set(); + remoteEnv.extensions.forEach(ext => isInstalledRemotely.add(ExtensionIdentifier.toKey(ext.identifier))); + + const enum RunningLocation { None, Local, Remote } + const pickRunningLocation = (extension: IExtensionDescription): RunningLocation => { + for (const extensionKind of getExtensionKind(extension, this._productService, this._configurationService)) { + if (extensionKind === 'ui') { + // a ui extension can run on both sides for now... + if (isInstalledLocally.has(ExtensionIdentifier.toKey(extension.identifier))) { + return RunningLocation.Local; + } + if (isInstalledRemotely.has(ExtensionIdentifier.toKey(extension.identifier))) { + return RunningLocation.Remote; + } + } else if (extensionKind === 'workspace') { + if (isInstalledRemotely.has(ExtensionIdentifier.toKey(extension.identifier))) { + return RunningLocation.Remote; + } + } + } + return RunningLocation.None; + }; + + const runningLocation = new Map(); + localExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext))); + remoteEnv.extensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext))); + // remove non-UI extensions from the local extensions - localExtensions = remove(localExtensions, extension => !extension.isBuiltin && !isUIExtension(extension)); + localExtensions = localExtensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === RunningLocation.Local); // in case of UI extensions overlap, the local extension wins - remoteEnv.extensions = remove(remoteEnv.extensions, localExtensions.filter(extension => isUIExtension(extension))); - - // in case of other extensions overlap, the remote extension wins - localExtensions = remove(localExtensions, remoteEnv.extensions); + remoteEnv.extensions = remoteEnv.extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === RunningLocation.Remote); // save for remote extension's init data this._remoteExtensionsEnvironmentData.set(remoteAuthority, remoteEnv); diff --git a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts index 7a83c73e05c..01d77a89124 100644 --- a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts +++ b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts @@ -11,7 +11,7 @@ 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 { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { prefersExecuteOnUI } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { values } from 'vs/base/common/map'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -116,7 +116,7 @@ export class RemoteExtensionManagementChannelClient extends ExtensionManagementC for (let idx = 0; idx < extensions.length; idx++) { const extension = extensions[idx]; const manifest = manifests[idx]; - if (manifest && isUIExtension(manifest, this.productService, this.configurationService) === uiExtension) { + if (manifest && prefersExecuteOnUI(manifest, this.productService, this.configurationService) === uiExtension) { result.set(extension.identifier.id.toLowerCase(), extension); extensionsManifests.push(manifest); } -- GitLab