From 87dd7d6a9c2f5fcc2bf3abe555fee79fbbde34bb Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 25 Sep 2020 22:23:34 +0200 Subject: [PATCH] Move `deltaExtensions` related methods up to `AbstractExtensionService` --- .../api/common/extHostExtensionService.ts | 2 +- .../api/common/shared/workspaceContains.ts | 6 +- .../extensions/browser/extensionService.ts | 15 +- .../common/abstractExtensionService.ts | 246 +++++++++++++++++- .../electron-browser/extensionService.ts | 242 ++--------------- 5 files changed, 269 insertions(+), 242 deletions(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 53d3612cc5d..311b529e5ad 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -506,7 +506,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme const host: IExtensionActivationHost = { folders: folders.map(folder => folder.uri), forceUsingSearch: localWithRemote, - exists: (path) => this._hostUtils.exists(path), + exists: (uri) => this._hostUtils.exists(uri.fsPath), checkExists: (folders, includes, token) => this._mainThreadWorkspaceProxy.$checkExists(folders, includes, token) }; diff --git a/src/vs/workbench/api/common/shared/workspaceContains.ts b/src/vs/workbench/api/common/shared/workspaceContains.ts index 7f883983ea7..629c9994a87 100644 --- a/src/vs/workbench/api/common/shared/workspaceContains.ts +++ b/src/vs/workbench/api/common/shared/workspaceContains.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'vs/base/common/path'; +import * as resources from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; @@ -19,7 +19,7 @@ export interface IExtensionActivationHost { readonly folders: readonly UriComponents[]; readonly forceUsingSearch: boolean; - exists(path: string): Promise; + exists(uri: URI): Promise; checkExists(folders: readonly UriComponents[], includes: string[], token: CancellationToken): Promise; } @@ -69,7 +69,7 @@ export function checkActivateWorkspaceContainsExtension(host: IExtensionActivati async function _activateIfFileName(host: IExtensionActivationHost, fileName: string, activate: (activationEvent: string) => void): Promise { // find exact path for (const uri of host.folders) { - if (await host.exists(path.join(URI.revive(uri).fsPath, fileName))) { + if (await host.exists(resources.joinPath(URI.revive(uri), fileName))) { // the file was found activate(`workspaceContains:${fileName}`); return; diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 9e979d28691..bac3d250878 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -19,13 +19,15 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost'; import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ExtensionIdentifier, IExtensionDescription, ExtensionKind } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription, ExtensionKind, IExtension } from 'vs/platform/extensions/common/extensions'; import { FetchFileSystemProvider } from 'vs/workbench/services/extensions/browser/webWorkerFileSystemProvider'; import { Schemas } from 'vs/base/common/network'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IUserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -41,6 +43,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService, @IFileService fileService: IFileService, @IProductService productService: IProductService, + @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IWorkspaceContextService contextService: IWorkspaceContextService, @IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, @IConfigurationService private readonly _configService: IConfigurationService, @@ -56,6 +60,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten extensionEnablementService, fileService, productService, + extensionManagementService, + contextService, ); this._runningLocation = new Map(); @@ -74,6 +80,13 @@ export class ExtensionService extends AbstractExtensionService implements IExten super.dispose(); } + protected async _scanSingleExtension(extension: IExtension): Promise { + return null; + } + + protected async _updateExtensionsOnExtHosts(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { + } + private _initFetchFileSystem(): void { const provider = new FetchFileSystemProvider(); this._disposables.add(this._fileService.registerProvider(Schemas.http, provider)); diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index c89897ab69c..ccd8680d38b 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { Barrier } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; @@ -20,11 +21,14 @@ 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 } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription, ExtensionType, ITranslatedScannedExtension, IExtension } 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'; import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; +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'; const hasOwnProperty = Object.hasOwnProperty; const NO_OP_VOID_PROMISE = Promise.resolve(undefined); @@ -39,6 +43,13 @@ export function parseScannedExtension(extension: ITranslatedScannedExtension): I }; } +class DeltaExtensionsQueueItem { + constructor( + public readonly toAdd: IExtension[], + public readonly toRemove: string[] + ) { } +} + export abstract class AbstractExtensionService extends Disposable implements IExtensionService { public _serviceBrand: undefined; @@ -66,6 +77,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx private readonly _proposedApiController: ProposedApiController; private readonly _isExtensionDevHost: boolean; protected readonly _isExtensionDevTestFromCli: boolean; + private _deltaExtensionsQueue: DeltaExtensionsQueueItem[]; + private _inHandleDeltaExtensions: boolean; // --- Members used per extension host process protected _extensionHostManagers: ExtensionHostManager[]; @@ -80,7 +93,9 @@ export abstract class AbstractExtensionService extends Disposable implements IEx @ITelemetryService protected readonly _telemetryService: ITelemetryService, @IWorkbenchExtensionEnablementService protected readonly _extensionEnablementService: IWorkbenchExtensionEnablementService, @IFileService protected readonly _fileService: IFileService, - @IProductService protected readonly _productService: IProductService + @IProductService protected readonly _productService: IProductService, + @IExtensionManagementService protected readonly _extensionManagementService: IExtensionManagementService, + @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, ) { super(); @@ -103,8 +118,225 @@ export abstract class AbstractExtensionService extends Disposable implements IEx const devOpts = parseExtensionDevOptions(this._environmentService); this._isExtensionDevHost = devOpts.isExtensionDevHost; this._isExtensionDevTestFromCli = devOpts.isExtensionDevTestFromCli; + + this._deltaExtensionsQueue = []; + this._inHandleDeltaExtensions = false; + + this._register(this._extensionEnablementService.onEnablementChanged((extensions) => { + let toAdd: IExtension[] = []; + let toRemove: string[] = []; + for (const extension of extensions) { + if (this._extensionEnablementService.isEnabled(extension)) { + // an extension has been enabled + toAdd.push(extension); + } else { + // an extension has been disabled + toRemove.push(extension.identifier.id); + } + } + this._handleDeltaExtensions(new DeltaExtensionsQueueItem(toAdd, toRemove)); + })); + + this._register(this._extensionManagementService.onDidInstallExtension((event) => { + if (event.local) { + if (this._extensionEnablementService.isEnabled(event.local)) { + // an extension has been installed + this._handleDeltaExtensions(new DeltaExtensionsQueueItem([event.local], [])); + } + } + })); + + this._register(this._extensionManagementService.onDidUninstallExtension((event) => { + if (!event.error) { + // an extension has been uninstalled + this._handleDeltaExtensions(new DeltaExtensionsQueueItem([], [event.identifier.id])); + } + })); + } + + //#region deltaExtensions + + private async _handleDeltaExtensions(item: DeltaExtensionsQueueItem): Promise { + this._deltaExtensionsQueue.push(item); + if (this._inHandleDeltaExtensions) { + // Let the current item finish, the new one will be picked up + return; + } + + while (this._deltaExtensionsQueue.length > 0) { + const item = this._deltaExtensionsQueue.shift()!; + try { + this._inHandleDeltaExtensions = true; + await this._deltaExtensions(item.toAdd, item.toRemove); + } finally { + this._inHandleDeltaExtensions = false; + } + } + } + + private async _deltaExtensions(_toAdd: IExtension[], _toRemove: string[]): Promise { + if (this._environmentService.configuration.remoteAuthority) { + return; + } + + let toAdd: IExtensionDescription[] = []; + for (let i = 0, len = _toAdd.length; i < len; i++) { + const extension = _toAdd[i]; + + if (!this._canAddExtension(extension)) { + continue; + } + + const extensionDescription = await this._scanSingleExtension(extension); + if (!extensionDescription) { + // could not scan extension... + continue; + } + + toAdd.push(extensionDescription); + } + + let toRemove: IExtensionDescription[] = []; + for (let i = 0, len = _toRemove.length; i < len; i++) { + const extensionId = _toRemove[i]; + const extensionDescription = this._registry.getExtensionDescription(extensionId); + if (!extensionDescription) { + // ignore disabling/uninstalling an extension which is not running + continue; + } + + if (!this.canRemoveExtension(extensionDescription)) { + // uses non-dynamic extension point or is activated + continue; + } + + toRemove.push(extensionDescription); + } + + if (toAdd.length === 0 && toRemove.length === 0) { + return; + } + + // Update the local registry + const result = this._registry.deltaExtensions(toAdd, toRemove.map(e => e.identifier)); + this._onDidChangeExtensions.fire(undefined); + + toRemove = toRemove.concat(result.removedDueToLooping); + if (result.removedDueToLooping.length > 0) { + this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); + } + + // enable or disable proposed API per extension + this._checkEnableProposedApi(toAdd); + + // Update extension points + this._doHandleExtensionPoints(([]).concat(toAdd).concat(toRemove)); + + // Update the extension host + this._updateExtensionsOnExtHosts(toAdd, toRemove.map(e => e.identifier)); + + for (let i = 0; i < toAdd.length; i++) { + this._activateAddedExtensionIfNeeded(toAdd[i]); + } + } + + public canAddExtension(extensionDescription: IExtensionDescription): boolean { + return this._canAddExtension(toExtension(extensionDescription)); + } + + protected _canAddExtension(extension: IExtension): boolean { + const extensionDescription = this._registry.getExtensionDescription(extension.identifier.id); + if (extensionDescription) { + // this extension is already running (most likely at a different version) + return false; + } + + // Check if extension is renamed + if (extension.identifier.uuid && this._registry.getAllExtensionDescriptions().some(e => e.uuid === extension.identifier.uuid)) { + return false; + } + + return true; + } + + public canRemoveExtension(extension: IExtensionDescription): boolean { + const extensionDescription = this._registry.getExtensionDescription(extension.identifier); + if (!extensionDescription) { + // ignore removing an extension which is not running + return false; + } + + if (this._extensionHostActiveExtensions.has(ExtensionIdentifier.toKey(extensionDescription.identifier))) { + // Extension is running, cannot remove it safely + return false; + } + + return true; + } + + private async _activateAddedExtensionIfNeeded(extensionDescription: IExtensionDescription): Promise { + let shouldActivate = false; + let shouldActivateReason: string | null = null; + let hasWorkspaceContains = false; + if (Array.isArray(extensionDescription.activationEvents)) { + for (let activationEvent of extensionDescription.activationEvents) { + // TODO@joao: there's no easy way to contribute this + if (activationEvent === 'onUri') { + activationEvent = `onUri:${ExtensionIdentifier.toKey(extensionDescription.identifier)}`; + } + + if (this._allRequestedActivateEvents.has(activationEvent)) { + // This activation event was fired before the extension was added + shouldActivate = true; + shouldActivateReason = activationEvent; + break; + } + + if (activationEvent === '*') { + shouldActivate = true; + shouldActivateReason = activationEvent; + break; + } + + if (/^workspaceContains/.test(activationEvent)) { + hasWorkspaceContains = true; + } + + if (activationEvent === 'onStartupFinished') { + shouldActivate = true; + shouldActivateReason = activationEvent; + break; + } + } + } + + if (shouldActivate) { + await Promise.all( + this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: shouldActivateReason! })) + ).then(() => { }); + } else if (hasWorkspaceContains) { + const workspace = await this._contextService.getCompleteWorkspace(); + const forceUsingSearch = !!this._environmentService.configuration.remoteAuthority; + const host: IWorkspaceContainsActivationHost = { + folders: workspace.folders.map(folder => folder.uri), + forceUsingSearch: forceUsingSearch, + exists: (uri) => this._fileService.exists(uri), + checkExists: (folders, includes, token) => this._instantiationService.invokeFunction((accessor) => checkGlobFileExists(accessor, folders, includes, token)) + }; + + const result = await checkActivateWorkspaceContainsExtension(host, extensionDescription); + if (!result) { + return; + } + + await Promise.all( + this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: result.activationEvent })) + ).then(() => { }); + } } + //#endregion + protected async _initialize(): Promise { perf.mark('willLoadExtensions'); this._startExtensionHosts(true, []); @@ -169,14 +401,6 @@ export abstract class AbstractExtensionService extends Disposable implements IEx //#region IExtensionService - public canAddExtension(extension: IExtensionDescription): boolean { - return false; - } - - public canRemoveExtension(extension: IExtensionDescription): boolean { - return false; - } - public restartExtensionHost(): void { this._stopExtensionHosts(); this._startExtensionHosts(false, Array.from(this._allRequestedActivateEvents.keys())); @@ -463,6 +687,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx protected abstract _createExtensionHosts(isInitialStart: boolean): IExtensionHost[]; protected abstract _scanAndHandleExtensions(): Promise; + protected abstract _scanSingleExtension(extension: IExtension): Promise; + protected abstract _updateExtensionsOnExtHosts(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise; public abstract _onExtensionHostExit(code: number): void; } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 98d13a948ed..088e023d5fe 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -25,7 +25,6 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IExtensionService, toExtension, ExtensionHostKind, IExtensionHost, webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager'; import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription, ExtensionKind } from 'vs/platform/extensions/common/extensions'; -import { Schemas } from 'vs/base/common/network'; import { IFileService } from 'vs/platform/files/common/files'; import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -37,18 +36,10 @@ import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost'; -import { IExtensionActivationHost as IWorkspaceContainsActivationHost, checkGlobFileExists, checkActivateWorkspaceContainsExtension } from 'vs/workbench/api/common/shared/workspaceContains'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { exists } from 'vs/base/node/pfs'; import { ILogService } from 'vs/platform/log/common/log'; import { CATEGORIES } from 'vs/workbench/common/actions'; - -class DeltaExtensionsQueueItem { - constructor( - public readonly toAdd: IExtension[], - public readonly toRemove: string[] - ) { } -} +import { Schemas } from 'vs/base/common/network'; export class ExtensionService extends AbstractExtensionService implements IExtensionService { @@ -56,7 +47,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten private readonly _remoteInitData: Map; private _runningLocation: Map; private readonly _extensionScanner: CachedExtensionScanner; - private _deltaExtensionsQueue: DeltaExtensionsQueueItem[]; constructor( @IInstantiationService instantiationService: IInstantiationService, @@ -66,7 +56,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService, @IFileService fileService: IFileService, @IProductService productService: IProductService, - @IExtensionManagementService private readonly _extensionManagementService: IExtensionManagementService, + @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IWorkspaceContextService contextService: IWorkspaceContextService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, @IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, @IConfigurationService private readonly _configurationService: IConfigurationService, @@ -76,7 +67,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IHostService private readonly _hostService: IHostService, @IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService, @IExtensionGalleryService private readonly _extensionGalleryService: IExtensionGalleryService, - @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @ILogService private readonly _logService: ILogService, ) { super( @@ -86,7 +76,9 @@ export class ExtensionService extends AbstractExtensionService implements IExten telemetryService, extensionEnablementService, fileService, - productService + productService, + extensionManagementService, + contextService, ); this._enableLocalWebWorker = this._configurationService.getValue(webWorkerExtHostConfig); @@ -95,38 +87,6 @@ export class ExtensionService extends AbstractExtensionService implements IExten this._runningLocation = new Map(); this._extensionScanner = instantiationService.createInstance(CachedExtensionScanner); - this._deltaExtensionsQueue = []; - - this._register(this._extensionEnablementService.onEnablementChanged((extensions) => { - let toAdd: IExtension[] = []; - let toRemove: string[] = []; - for (const extension of extensions) { - if (this._extensionEnablementService.isEnabled(extension)) { - // an extension has been enabled - toAdd.push(extension); - } else { - // an extension has been disabled - toRemove.push(extension.identifier.id); - } - } - this._handleDeltaExtensions(new DeltaExtensionsQueueItem(toAdd, toRemove)); - })); - - this._register(this._extensionManagementService.onDidInstallExtension((event) => { - if (event.local) { - if (this._extensionEnablementService.isEnabled(event.local)) { - // an extension has been installed - this._handleDeltaExtensions(new DeltaExtensionsQueueItem([event.local], [])); - } - } - })); - - this._register(this._extensionManagementService.onDidUninstallExtension((event) => { - if (!event.error) { - // an extension has been uninstalled - this._handleDeltaExtensions(new DeltaExtensionsQueueItem([], [event.identifier.id])); - } - })); // delay extension host creation and extension scanning // until the workbench is running. we cannot defer the @@ -163,101 +123,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten return null; } - //#region deltaExtensions - - private _inHandleDeltaExtensions = false; - private async _handleDeltaExtensions(item: DeltaExtensionsQueueItem): Promise { - this._deltaExtensionsQueue.push(item); - if (this._inHandleDeltaExtensions) { - // Let the current item finish, the new one will be picked up - return; - } - - while (this._deltaExtensionsQueue.length > 0) { - const item = this._deltaExtensionsQueue.shift()!; - try { - this._inHandleDeltaExtensions = true; - await this._deltaExtensions(item.toAdd, item.toRemove); - } finally { - this._inHandleDeltaExtensions = false; - } - } - } - - private async _deltaExtensions(_toAdd: IExtension[], _toRemove: string[]): Promise { - if (this._environmentService.configuration.remoteAuthority) { - return; - } - - let toAdd: IExtensionDescription[] = []; - for (let i = 0, len = _toAdd.length; i < len; i++) { - const extension = _toAdd[i]; - - if (!this._canAddExtension(extension)) { - continue; - } - - const extensionDescription = await this._extensionScanner.scanSingleExtension(extension.location.fsPath, extension.type === ExtensionType.System, this.createLogger()); - if (!extensionDescription) { - // could not scan extension... - continue; - } - - toAdd.push(extensionDescription); - } - - let toRemove: IExtensionDescription[] = []; - for (let i = 0, len = _toRemove.length; i < len; i++) { - const extensionId = _toRemove[i]; - const extensionDescription = this._registry.getExtensionDescription(extensionId); - if (!extensionDescription) { - // ignore disabling/uninstalling an extension which is not running - continue; - } - - if (!this._canRemoveExtension(extensionDescription)) { - // uses non-dynamic extension point or is activated - continue; - } - - toRemove.push(extensionDescription); - } - - if (toAdd.length === 0 && toRemove.length === 0) { - return; - } - - // Update the local registry - const result = this._registry.deltaExtensions(toAdd, toRemove.map(e => e.identifier)); - this._onDidChangeExtensions.fire(undefined); - - toRemove = toRemove.concat(result.removedDueToLooping); - if (result.removedDueToLooping.length > 0) { - this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); - } - - // enable or disable proposed API per extension - this._checkEnableProposedApi(toAdd); - - // Update extension points - this._doHandleExtensionPoints(([]).concat(toAdd).concat(toRemove)); - - // Update the extension host - const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess); - if (localProcessExtensionHost) { - await localProcessExtensionHost.deltaExtensions(toAdd, toRemove.map(e => e.identifier)); - } - - for (let i = 0; i < toAdd.length; i++) { - this._activateAddedExtensionIfNeeded(toAdd[i]); - } - } - - public canAddExtension(extensionDescription: IExtensionDescription): boolean { - return this._canAddExtension(toExtension(extensionDescription)); - } - - private _canAddExtension(extension: IExtension): boolean { + protected _canAddExtension(extension: IExtension): boolean { if (this._environmentService.configuration.remoteAuthority) { return false; } @@ -266,18 +132,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten return false; } - const extensionDescription = this._registry.getExtensionDescription(extension.identifier.id); - if (extensionDescription) { - // this extension is already running (most likely at a different version) - return false; - } - - // Check if extension is renamed - if (extension.identifier.uuid && this._registry.getAllExtensionDescriptions().some(e => e.uuid === extension.identifier.uuid)) { - return false; - } - - return true; + return super._canAddExtension(extension); } public canRemoveExtension(extension: IExtensionDescription): boolean { @@ -289,88 +144,21 @@ export class ExtensionService extends AbstractExtensionService implements IExten return false; } - const extensionDescription = this._registry.getExtensionDescription(extension.identifier); - if (!extensionDescription) { - // ignore removing an extension which is not running - return false; - } - - return this._canRemoveExtension(extensionDescription); + return super.canRemoveExtension(extension); } - private _canRemoveExtension(extension: IExtensionDescription): boolean { - if (this._extensionHostActiveExtensions.has(ExtensionIdentifier.toKey(extension.identifier))) { - // Extension is running, cannot remove it safely - return false; - } - return true; + protected _scanSingleExtension(extension: IExtension): Promise { + return this._extensionScanner.scanSingleExtension(extension.location.fsPath, extension.type === ExtensionType.System, this.createLogger()); } - private async _activateAddedExtensionIfNeeded(extensionDescription: IExtensionDescription): Promise { - - let shouldActivate = false; - let shouldActivateReason: string | null = null; - let hasWorkspaceContains = false; - if (Array.isArray(extensionDescription.activationEvents)) { - for (let activationEvent of extensionDescription.activationEvents) { - // TODO@joao: there's no easy way to contribute this - if (activationEvent === 'onUri') { - activationEvent = `onUri:${ExtensionIdentifier.toKey(extensionDescription.identifier)}`; - } - - if (this._allRequestedActivateEvents.has(activationEvent)) { - // This activation event was fired before the extension was added - shouldActivate = true; - shouldActivateReason = activationEvent; - break; - } - - if (activationEvent === '*') { - shouldActivate = true; - shouldActivateReason = activationEvent; - break; - } - - if (/^workspaceContains/.test(activationEvent)) { - hasWorkspaceContains = true; - } - - if (activationEvent === 'onStartupFinished') { - shouldActivate = true; - shouldActivateReason = activationEvent; - break; - } - } - } - - if (shouldActivate) { - await Promise.all( - this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: shouldActivateReason! })) - ).then(() => { }); - } else if (hasWorkspaceContains) { - const workspace = await this._contextService.getCompleteWorkspace(); - const forceUsingSearch = !!this._environmentService.configuration.remoteAuthority; - const host: IWorkspaceContainsActivationHost = { - folders: workspace.folders.map(folder => folder.uri), - forceUsingSearch: forceUsingSearch, - exists: (path) => exists(path), - checkExists: (folders, includes, token) => this._instantiationService.invokeFunction((accessor) => checkGlobFileExists(accessor, folders, includes, token)) - }; - - const result = await checkActivateWorkspaceContainsExtension(host, extensionDescription); - if (!result) { - return; - } - - await Promise.all( - this._extensionHostManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: result.activationEvent })) - ).then(() => { }); + protected async _updateExtensionsOnExtHosts(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { + const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess); + if (localProcessExtensionHost) { + await localProcessExtensionHost.deltaExtensions(toAdd, toRemove); } } - //#endregion - private async _scanAllLocalExtensions(): Promise { return flatten(await Promise.all([ this._extensionScanner.scannedExtensions, -- GitLab