diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 56e4f952c1c..39ef03719dd 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -13,10 +13,10 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { - IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, + IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, IExtensionIdentifier, InstallOperation, DefaultIconPath } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensioManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet, groupByExtension, ExtensionIdentifierWithVersion, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; @@ -33,7 +33,7 @@ import * as resources from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IFileService } from 'vs/platform/files/common/files'; -import { IExtensionManifest, ExtensionType, IExtension as IPlatformExtension, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; +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 { getIgnoredExtensions } from 'vs/platform/userDataSync/common/extensionsMerge'; @@ -511,7 +511,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, @IEditorService private readonly editorService: IEditorService, - @IExtensionManagementService private readonly extensionService: IExtensionManagementService, + @IWorkbenchExtensioManagementService private readonly extensionManagementService: IWorkbenchExtensioManagementService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, @IConfigurationService private readonly configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, @@ -629,7 +629,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension const options: IQueryOptions = CancellationToken.isCancellationToken(arg1) ? {} : arg1; const token: CancellationToken = CancellationToken.isCancellationToken(arg1) ? arg1 : arg2; options.text = options.text ? this.resolveQueryText(options.text) : options.text; - return this.extensionService.getExtensionsReport() + return this.extensionManagementService.getExtensionsReport() .then(report => { const maliciousSet = getMaliciousExtensionsSet(report); @@ -937,7 +937,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension location: ProgressLocation.Extensions, title: nls.localize('uninstallingExtension', 'Uninstalling extension....'), source: `${toUninstall.identifier.id}` - }, () => this.extensionService.uninstall(toUninstall).then(() => undefined)); + }, () => this.extensionManagementService.uninstall(toUninstall).then(() => undefined)); } installVersion(extension: IExtension, version: string): Promise { @@ -976,7 +976,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return this.progressService.withProgress({ location: ProgressLocation.Extensions, source: `${toReinstall.identifier.id}` - }, () => this.extensionService.reinstallFromGallery(toReinstall).then(() => this.local.filter(local => areSameExtensions(local.identifier, extension.identifier))[0])); + }, () => this.extensionManagementService.reinstallFromGallery(toReinstall).then(() => this.local.filter(local => areSameExtensions(local.identifier, extension.identifier))[0])); } isExtensionIgnoredToSync(extension: IExtension): boolean { @@ -1019,9 +1019,9 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } private async installFromVSIX(vsix: URI): Promise { - const manifest = await this.extensionService.getManifest(vsix); + const manifest = await this.extensionManagementService.getManifest(vsix); const existingExtension = this.local.find(local => areSameExtensions(local.identifier, { id: getGalleryExtensionId(manifest.publisher, manifest.name) })); - const { identifier } = await this.extensionService.install(vsix); + const { identifier } = await this.extensionManagementService.install(vsix); if (existingExtension && existingExtension.latestVersion !== manifest.version) { this.ignoreAutoUpdate(new ExtensionIdentifierWithVersion(identifier, manifest.version)); @@ -1034,8 +1034,11 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension this.installing.push(extension); this._onChange.fire(extension); try { - const extensionService = extension.server && extension.local && !isLanguagePackExtension(extension.local.manifest) ? extension.server.extensionManagementService : this.extensionService; - await extensionService.installFromGallery(gallery); + if (extension.state === ExtensionState.Installed && extension.local) { + await this.extensionManagementService.updateFromGallery(gallery, extension.local); + } else { + await this.extensionManagementService.installFromGallery(gallery); + } const ids: string[] | undefined = extension.identifier.uuid ? [extension.identifier.uuid] : undefined; const names: string[] | undefined = extension.identifier.uuid ? undefined : [extension.identifier.id]; this.queryGallery({ names, ids, pageSize: 1 }, CancellationToken.None); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts index 64197db0d5c..adf1279b705 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts @@ -6,17 +6,16 @@ import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtension, IScannedExtension, ExtensionType, ITranslatedScannedExtension } from 'vs/platform/extensions/common/extensions'; -import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { URI } from 'vs/base/common/uri'; -export const IExtensionManagementServerService = createDecorator('extensionManagementServerService'); - export interface IExtensionManagementServer { id: string; label: string; extensionManagementService: IExtensionManagementService; } +export const IExtensionManagementServerService = createDecorator('extensionManagementServerService'); export interface IExtensionManagementServerService { readonly _serviceBrand: undefined; readonly localExtensionManagementServer: IExtensionManagementServer | null; @@ -25,6 +24,12 @@ export interface IExtensionManagementServerService { getExtensionManagementServer(extension: IExtension): IExtensionManagementServer | null; } +export const IWorkbenchExtensioManagementService = createDecorator('extensionManagementService'); +export interface IWorkbenchExtensioManagementService extends IExtensionManagementService { + readonly _serviceBrand: undefined; + updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension): Promise; +} + export const enum EnablementState { DisabledByExtensionKind, DisabledByEnvironemt, diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index 10009a8b260..41afb8120f2 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -5,9 +5,9 @@ import { Event, EventMultiplexer } from 'vs/base/common/event'; import { - IExtensionManagementService, ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionGalleryService, INSTALL_ERROR_NOT_SUPPORTED + ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionGalleryService, INSTALL_ERROR_NOT_SUPPORTED, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionManagementServer, IExtensionManagementServerService, IWorkbenchExtensioManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionType, isLanguagePackExtension, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -21,7 +21,7 @@ import { Schemas } from 'vs/base/common/network'; import { IDownloadService } from 'vs/platform/download/common/download'; import { flatten } from 'vs/base/common/arrays'; -export class ExtensionManagementService extends Disposable implements IExtensionManagementService { +export class ExtensionManagementService extends Disposable implements IWorkbenchExtensioManagementService { declare readonly _serviceBrand: undefined; @@ -198,51 +198,45 @@ export class ExtensionManagementService extends Disposable implements IExtension return false; } - async installFromGallery(gallery: IGalleryExtension): Promise { - - // Only local server, install without any checks - if (this.servers.length === 1 && this.extensionManagementServerService.localExtensionManagementServer) { - return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); + async updateFromGallery(gallery: IGalleryExtension, extension: ILocalExtension): Promise { + const server = this.getServer(extension); + if (!server) { + return Promise.reject(`Invalid location ${extension.location.toString()}`); } + const servers: IExtensionManagementServer[] = []; + + // Update Language pack on all servers + if (isLanguagePackExtension(extension.manifest)) { + servers.push(...this.servers); + } else { + servers.push(server); + } + + return Promise.all(servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local); + } + + async installFromGallery(gallery: IGalleryExtension, installOptions?: InstallOptions): Promise { + const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None); if (!manifest) { return Promise.reject(localize('Manifest is not found', "Installing Extension {0} failed: Manifest is not found.", gallery.displayName || gallery.name)); } + const servers: IExtensionManagementServer[] = []; + // Install Language pack on all servers if (isLanguagePackExtension(manifest)) { - return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local); + servers.push(...this.servers); + } else { + const server = this.getExtensionManagementServerToInstall(manifest); + if (server) { + servers.push(server); + } } - // 1. Install on preferred location - - // Install UI preferred extension on local server - if (prefersExecuteOnUI(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.localExtensionManagementServer) { - return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); - } - // Install Workspace preferred extension on remote server - if (prefersExecuteOnWorkspace(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) { - return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery); - } - // Install Web preferred extension on web server - if (prefersExecuteOnWeb(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.webExtensionManagementServer) { - return this.extensionManagementServerService.webExtensionManagementServer.extensionManagementService.installFromGallery(gallery); - } - - // 2. Install on supported location - - // Install UI supported extension on local server - if (canExecuteOnUI(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.localExtensionManagementServer) { - return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); - } - // Install Workspace supported extension on remote server - if (canExecuteOnWorkspace(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) { - return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery); - } - // Install Web supported extension on web server - if (canExecuteOnWeb(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.webExtensionManagementServer) { - return this.extensionManagementServerService.webExtensionManagementServer.extensionManagementService.installFromGallery(gallery); + if (servers.length) { + return Promise.all(servers.map(server => server.extensionManagementService.installFromGallery(gallery, installOptions))).then(([local]) => local); } if (this.extensionManagementServerService.remoteExtensionManagementServer) { @@ -256,6 +250,46 @@ export class ExtensionManagementService extends Disposable implements IExtension return Promise.reject(error); } + private getExtensionManagementServerToInstall(manifest: IExtensionManifest): IExtensionManagementServer | undefined { + + // Only local server + if (this.servers.length === 1 && this.extensionManagementServerService.localExtensionManagementServer) { + return this.extensionManagementServerService.localExtensionManagementServer; + } + + // 1. Install on preferred location + + // Install UI preferred extension on local server + if (prefersExecuteOnUI(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.localExtensionManagementServer) { + return this.extensionManagementServerService.localExtensionManagementServer; + } + // Install Workspace preferred extension on remote server + if (prefersExecuteOnWorkspace(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) { + return this.extensionManagementServerService.remoteExtensionManagementServer; + } + // Install Web preferred extension on web server + if (prefersExecuteOnWeb(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.webExtensionManagementServer) { + return this.extensionManagementServerService.webExtensionManagementServer; + } + + // 2. Install on supported location + + // Install UI supported extension on local server + if (canExecuteOnUI(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.localExtensionManagementServer) { + return this.extensionManagementServerService.localExtensionManagementServer; + } + // Install Workspace supported extension on remote server + if (canExecuteOnWorkspace(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) { + return this.extensionManagementServerService.remoteExtensionManagementServer; + } + // Install Web supported extension on web server + if (canExecuteOnWeb(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.webExtensionManagementServer) { + return this.extensionManagementServerService.webExtensionManagementServer; + } + + return undefined; + } + getExtensionsReport(): Promise { if (this.extensionManagementServerService.localExtensionManagementServer) { return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getExtensionsReport(); diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts index e005befccac..2d61870275f 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { generateUuid } from 'vs/base/common/uuid'; -import { ILocalExtension, IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ILocalExtension, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { URI } from 'vs/base/common/uri'; import { ExtensionManagementService as BaseExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionManagementServer, IExtensionManagementServerService, IWorkbenchExtensioManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { Schemas } from 'vs/base/common/network'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDownloadService } from 'vs/platform/download/common/download'; @@ -39,4 +39,4 @@ export class ExtensionManagementService extends BaseExtensionManagementService { } } -registerSingleton(IExtensionManagementService, ExtensionManagementService); +registerSingleton(IWorkbenchExtensioManagementService, ExtensionManagementService); diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 22d0ae14060..509f8ac8ce3 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -58,8 +58,9 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService'; -import { IExtensionManagementService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionTipsService'; +import { IWorkbenchExtensioManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService'; import { ITunnelService, TunnelService } from 'vs/platform/remote/common/tunnel'; import { ILoggerService } from 'vs/platform/log/common/log'; @@ -79,7 +80,7 @@ import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { TitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart'; import { ITimerService, TimerService } from 'vs/workbench/services/timer/browser/timerService'; -registerSingleton(IExtensionManagementService, ExtensionManagementService); +registerSingleton(IWorkbenchExtensioManagementService, ExtensionManagementService); registerSingleton(IBackupFileService, BackupFileService); registerSingleton(IAccessibilityService, AccessibilityService, true); registerSingleton(IContextMenuService, ContextMenuService);