propose extensions.allAcrossExtensionHosts and related APIs, https://github.com/microsoft/vscode/issues/145307

This commit is contained in:
Johannes 2022-04-21 14:45:38 +02:00
parent 9cd932c92f
commit ad4470522e
No known key found for this signature in database
GPG key ID: 6DEF802A22264FCA
7 changed files with 103 additions and 23 deletions

View file

@ -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> | 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<any> | undefined {
const desc = extensionRegistry.getExtensionDescription(extensionId);
if (desc) {
return new Extension(extensionService, extension.identifier, desc, extensionKind);
getExtension(extensionId: string, includeFromDifferentExtensionHosts?: boolean): vscode.Extension<any> | 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<any>[] {
return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, extension.identifier, desc, extensionKind));
const result: vscode.Extension<any>[] = [];
for (const desc of extensionInfo.mine.getAllExtensionDescriptions()) {
result.push(new Extension(extensionService, extension.identifier, desc, extensionKind, false));
}
return result;
},
get allAcrossExtensionHosts(): vscode.Extension<any>[] {
checkProposedApiEnabled(extension, 'extensionsAny');
const result: vscode.Extension<any>[] = [];
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;
}
};

View file

@ -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<T extends object | null | undefined> 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<T extends object | null | undefined> 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 <T>this.#extensionService.getExtensionExports(this.#identifier);
}
activate(): Thenable<T> {
return this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' }).then(() => this.exports);
async activate(): Promise<T> {
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;
}
}

View file

@ -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,
) {

View file

@ -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');

View file

@ -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');

View file

@ -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',

View file

@ -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<T> {
/**
* `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<T = any>(extensionId: string, includeDifferentExtensionHosts: boolean): Extension<T> | undefined;
/**
* All extensions across all extension hosts.
*
* @see {@link Extension.isFromDifferentExtensionHost}
*/
export const allAcrossExtensionHosts: readonly Extension<void>[];
}
}