diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 1096349abd5..fb5bf4da95a 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -95,8 +95,13 @@ import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/serv import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug'; import { ExtHostNotebookProxyKernels } from 'vs/workbench/api/common/extHostNotebookProxyKernels'; +export interface IExtensionRegistries { + mine: ExtensionDescriptionRegistry; + all: ExtensionDescriptionRegistry; +} + export interface IExtensionApiFactory { - (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; + (extension: IExtensionDescription, extensionInfo: IExtensionRegistries, configProvider: ExtHostConfigProvider): typeof vscode; } /** @@ -196,7 +201,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // Register API-ish commands ExtHostApiCommands.register(extHostCommands); - return function (extension: IExtensionDescription, extensionRegistry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode { + return function (extension: IExtensionDescription, extensionInfo: IExtensionRegistries, configProvider: ExtHostConfigProvider): typeof vscode { // Check document selectors for being overly generic. Technically this isn't a problem but // in practice many extensions say they support `fooLang` but need fs-access to do so. Those @@ -360,10 +365,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I Object.freeze(env); } - const extensionKind = initData.remote.isRemote - ? extHostTypes.ExtensionKind.Workspace - : extHostTypes.ExtensionKind.UI; - + // namespace: tests const tests: typeof vscode.tests = { createTestController(provider, label, refreshHandler?: (token: vscode.CancellationToken) => Thenable | void) { return extHostTesting.createTestController(provider, label, refreshHandler); @@ -387,19 +389,50 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }; // namespace: extensions + const extensionKind = initData.remote.isRemote + ? extHostTypes.ExtensionKind.Workspace + : extHostTypes.ExtensionKind.UI; + const extensions: typeof vscode.extensions = { - getExtension(extensionId: string): vscode.Extension | undefined { - const desc = extensionRegistry.getExtensionDescription(extensionId); - if (desc) { - return new Extension(extensionService, extension.identifier, desc, extensionKind); + getExtension(extensionId: string, includeFromDifferentExtensionHosts?: boolean): vscode.Extension | undefined { + if (!isProposedApiEnabled(extension, 'extensionsAny')) { + includeFromDifferentExtensionHosts = false; + } + const mine = extensionInfo.mine.getExtensionDescription(extensionId); + if (mine) { + return new Extension(extensionService, extension.identifier, mine, extensionKind, false); + } + if (includeFromDifferentExtensionHosts) { + const foreign = extensionInfo.all.getExtensionDescription(extensionId); + if (foreign) { + return new Extension(extensionService, extension.identifier, foreign, extensionKind /* TODO@alexdima THIS IS WRONG */, true); + } } return undefined; }, get all(): vscode.Extension[] { - return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, extension.identifier, desc, extensionKind)); + const result: vscode.Extension[] = []; + for (const desc of extensionInfo.mine.getAllExtensionDescriptions()) { + result.push(new Extension(extensionService, extension.identifier, desc, extensionKind, false)); + } + return result; + }, + get allAcrossExtensionHosts(): vscode.Extension[] { + checkProposedApiEnabled(extension, 'extensionsAny'); + const result: vscode.Extension[] = []; + for (const desc of extensionInfo.mine.getAllExtensionDescriptions()) { + result.push(new Extension(extensionService, extension.identifier, desc, extensionKind, false)); + } + for (const desc of extensionInfo.all.getAllExtensionDescriptions()) { + result.push(new Extension(extensionService, extension.identifier, desc, extensionKind /* TODO@alexdima THIS IS WRONG */, true)); + } + return result; }, get onDidChange() { - return extensionRegistry.onDidChange; + if (isProposedApiEnabled(extension, 'extensionsAny')) { + return Event.any(extensionInfo.mine.onDidChange, extensionInfo.all.onDidChange); + } + return extensionInfo.mine.onDidChange; } }; diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 101f6f4a95b..4b674cb4581 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -513,7 +513,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme get extensionMode() { return extensionMode; }, get extension() { if (extension === undefined) { - extension = new Extension(that, extensionDescription.identifier, extensionDescription, extensionKind); + extension = new Extension(that, extensionDescription.identifier, extensionDescription, extensionKind, false); } return extension; }, @@ -983,8 +983,9 @@ export class Extension implements vscode.Ex readonly extensionPath: string; readonly packageJSON: IExtensionDescription; readonly extensionKind: vscode.ExtensionKind; + readonly isFromDifferentExtensionHost: boolean; - constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: ExtensionKind) { + constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: ExtensionKind, isFromDifferentExtensionHost: boolean) { this.#extensionService = extensionService; this.#originExtensionId = originExtensionId; this.#identifier = description.identifier; @@ -993,21 +994,27 @@ export class Extension implements vscode.Ex this.extensionPath = path.normalize(originalFSPath(description.extensionLocation)); this.packageJSON = description; this.extensionKind = kind; + this.isFromDifferentExtensionHost = isFromDifferentExtensionHost; } get isActive(): boolean { + // TODO@alexdima support this return this.#extensionService.isActivated(this.#identifier); } get exports(): T { - if (this.packageJSON.api === 'none') { + if (this.packageJSON.api === 'none' || this.isFromDifferentExtensionHost) { return undefined!; // Strict nulloverride - Public api } return this.#extensionService.getExtensionExports(this.#identifier); } - activate(): Thenable { - return this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' }).then(() => this.exports); + async activate(): Promise { + if (this.isFromDifferentExtensionHost) { + throw new Error('Cannot activate foreign extension'); // TODO@alexdima support this + } + await this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' }); + return this.exports; } } diff --git a/src/vs/workbench/api/common/extHostRequireInterceptor.ts b/src/vs/workbench/api/common/extHostRequireInterceptor.ts index 459f32df428..4a072952392 100644 --- a/src/vs/workbench/api/common/extHostRequireInterceptor.ts +++ b/src/vs/workbench/api/common/extHostRequireInterceptor.ts @@ -8,10 +8,9 @@ import { URI } from 'vs/base/common/uri'; import { MainThreadTelemetryShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import * as vscode from 'vscode'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IExtensionApiFactory } from 'vs/workbench/api/common/extHost.api.impl'; +import { IExtensionApiFactory, IExtensionRegistries } from 'vs/workbench/api/common/extHost.api.impl'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -41,7 +40,7 @@ export abstract class RequireInterceptor { constructor( private _apiFactory: IExtensionApiFactory, - private _extensionRegistry: ExtensionDescriptionRegistry, + private _extensionRegistry: IExtensionRegistries, @IInstantiationService private readonly _instaService: IInstantiationService, @IExtHostConfiguration private readonly _extHostConfiguration: IExtHostConfiguration, @IExtHostExtensionService private readonly _extHostExtensionService: IExtHostExtensionService, @@ -156,7 +155,7 @@ class VSCodeNodeModuleFactory implements INodeModuleFactory { constructor( private readonly _apiFactory: IExtensionApiFactory, private readonly _extensionPaths: ExtensionPaths, - private readonly _extensionRegistry: ExtensionDescriptionRegistry, + private readonly _extensionRegistry: IExtensionRegistries, private readonly _configProvider: ExtHostConfigProvider, private readonly _logService: ILogService, ) { diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 8521f23ca0a..62e0d6b4998 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -72,7 +72,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { } // Module loading tricks - const interceptor = this._instaService.createInstance(NodeModuleRequireInterceptor, extensionApiFactory, this._myRegistry); + const interceptor = this._instaService.createInstance(NodeModuleRequireInterceptor, extensionApiFactory, { mine: this._myRegistry, all: this._globalRegistry }); await interceptor.install(); performance.mark('code/extHost/didInitAPI'); diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index cc072dcdacd..a74fcfbc69b 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -44,7 +44,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { // initialize API and register actors const apiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors); - this._fakeModules = this._instaService.createInstance(WorkerRequireInterceptor, apiFactory, this._myRegistry); + this._fakeModules = this._instaService.createInstance(WorkerRequireInterceptor, apiFactory, { mine: this._myRegistry, all: this._globalRegistry }); await this._fakeModules.install(); performance.mark('code/extHost/didInitAPI'); diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 074e8fbee5b..ea97e90e13f 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -19,6 +19,7 @@ export const allApiProposals = Object.freeze({ documentFiltersExclusive: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts', editorInsets: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.editorInsets.d.ts', extensionRuntime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.extensionRuntime.d.ts', + extensionsAny: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.extensionsAny.d.ts', externalUriOpener: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.externalUriOpener.d.ts', fileSearchProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.fileSearchProvider.d.ts', findTextInFiles: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.findTextInFiles.d.ts', diff --git a/src/vscode-dts/vscode.proposed.extensionsAny.d.ts b/src/vscode-dts/vscode.proposed.extensionsAny.d.ts new file mode 100644 index 00000000000..378e324fa3f --- /dev/null +++ b/src/vscode-dts/vscode.proposed.extensionsAny.d.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/145307 + + export interface Extension { + + /** + * `true` when the extension is associated to another extension host. + * + * *Note* that an extension from another extension host cannot export + * API, e.g {@link Extension.exports its exports} are always `undefined`. + */ + readonly isFromDifferentExtensionHost: boolean; + } + + export namespace extensions { + + /** + * Get an extension by its full identifier in the form of: `publisher.name`. + * + * @param extensionId An extension identifier. + * @param includeDifferentExtensionHosts Include extensions from different extension host + * @return An extension or `undefined`. + */ + export function getExtension(extensionId: string, includeDifferentExtensionHosts: boolean): Extension | undefined; + + /** + * All extensions across all extension hosts. + * + * @see {@link Extension.isFromDifferentExtensionHost} + */ + export const allAcrossExtensionHosts: readonly Extension[]; + + } +}