385 lines
13 KiB
TypeScript
385 lines
13 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import { Event } from 'vs/base/common/event';
|
|
import Severity from 'vs/base/common/severity';
|
|
import { URI } from 'vs/base/common/uri';
|
|
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
|
import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
|
import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription, IExtensionContributions } from 'vs/platform/extensions/common/extensions';
|
|
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
|
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
|
|
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
|
|
import { ApiProposalName } from 'vs/workbench/services/extensions/common/extensionsApiProposals';
|
|
|
|
export const nullExtensionDescription = Object.freeze(<IExtensionDescription>{
|
|
identifier: new ExtensionIdentifier('nullExtensionDescription'),
|
|
name: 'Null Extension Description',
|
|
version: '0.0.0',
|
|
publisher: 'vscode',
|
|
engines: { vscode: '' },
|
|
extensionLocation: URI.parse('void:location'),
|
|
isBuiltin: false,
|
|
});
|
|
|
|
export type WebWorkerExtHostConfigValue = boolean | 'auto';
|
|
export const webWorkerExtHostConfig = 'extensions.webWorker';
|
|
|
|
export const IExtensionService = createDecorator<IExtensionService>('extensionService');
|
|
|
|
export interface IMessage {
|
|
type: Severity;
|
|
message: string;
|
|
extensionId: ExtensionIdentifier;
|
|
extensionPointId: string;
|
|
}
|
|
|
|
export const enum ExtensionRunningLocation {
|
|
None,
|
|
LocalProcess,
|
|
LocalWebWorker,
|
|
Remote
|
|
}
|
|
|
|
export function extensionRunningLocationToString(location: ExtensionRunningLocation) {
|
|
switch (location) {
|
|
case ExtensionRunningLocation.None:
|
|
return 'None';
|
|
case ExtensionRunningLocation.LocalProcess:
|
|
return 'LocalProcess';
|
|
case ExtensionRunningLocation.LocalWebWorker:
|
|
return 'LocalWebWorker';
|
|
case ExtensionRunningLocation.Remote:
|
|
return 'Remote';
|
|
}
|
|
}
|
|
|
|
export interface IExtensionsStatus {
|
|
messages: IMessage[];
|
|
activationTimes: ActivationTimes | undefined;
|
|
runtimeErrors: Error[];
|
|
runningLocation: ExtensionRunningLocation;
|
|
}
|
|
|
|
export class MissingExtensionDependency {
|
|
constructor(readonly dependency: string) { }
|
|
}
|
|
|
|
/**
|
|
* e.g.
|
|
* ```
|
|
* {
|
|
* startTime: 1511954813493000,
|
|
* endTime: 1511954835590000,
|
|
* deltas: [ 100, 1500, 123456, 1500, 100000 ],
|
|
* ids: [ 'idle', 'self', 'extension1', 'self', 'idle' ]
|
|
* }
|
|
* ```
|
|
*/
|
|
export interface IExtensionHostProfile {
|
|
/**
|
|
* Profiling start timestamp in microseconds.
|
|
*/
|
|
startTime: number;
|
|
/**
|
|
* Profiling end timestamp in microseconds.
|
|
*/
|
|
endTime: number;
|
|
/**
|
|
* Duration of segment in microseconds.
|
|
*/
|
|
deltas: number[];
|
|
/**
|
|
* Segment identifier: extension id or one of the four known strings.
|
|
*/
|
|
ids: ProfileSegmentId[];
|
|
|
|
/**
|
|
* Get the information as a .cpuprofile.
|
|
*/
|
|
data: object;
|
|
|
|
/**
|
|
* Get the aggregated time per segmentId
|
|
*/
|
|
getAggregatedTimes(): Map<ProfileSegmentId, number>;
|
|
}
|
|
|
|
export const enum ExtensionHostKind {
|
|
LocalProcess,
|
|
LocalWebWorker,
|
|
Remote
|
|
}
|
|
|
|
export function extensionHostKindToString(kind: ExtensionHostKind): string {
|
|
switch (kind) {
|
|
case ExtensionHostKind.LocalProcess: return 'LocalProcess';
|
|
case ExtensionHostKind.LocalWebWorker: return 'LocalWebWorker';
|
|
case ExtensionHostKind.Remote: return 'Remote';
|
|
}
|
|
}
|
|
|
|
export interface IExtensionHost {
|
|
readonly kind: ExtensionHostKind;
|
|
readonly remoteAuthority: string | null;
|
|
readonly lazyStart: boolean;
|
|
readonly onExit: Event<[number, string | null]>;
|
|
|
|
start(): Promise<IMessagePassingProtocol> | null;
|
|
getInspectPort(): number | undefined;
|
|
enableInspectPort(): Promise<boolean>;
|
|
dispose(): void;
|
|
}
|
|
|
|
export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): boolean {
|
|
if (!extension.enabledApiProposals) {
|
|
return false;
|
|
}
|
|
return extension.enabledApiProposals.includes(proposal);
|
|
}
|
|
|
|
export function checkProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): void {
|
|
if (!isProposedApiEnabled(extension, proposal)) {
|
|
throw new Error(`Extension '${extension.identifier.value}' CANNOT use API proposal: ${proposal}.\nIts package.json#enabledApiProposals-property declares: ${extension.enabledApiProposals?.join(', ') ?? '[]'} but NOT ${proposal}.\n The missing proposal MUST be added and you must start in extension development mode or use the following command line switch: --enable-proposed-api ${extension.identifier.value}`);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Extension id or one of the four known program states.
|
|
*/
|
|
export type ProfileSegmentId = string | 'idle' | 'program' | 'gc' | 'self';
|
|
|
|
export class ActivationTimes {
|
|
constructor(
|
|
public readonly codeLoadingTime: number,
|
|
public readonly activateCallTime: number,
|
|
public readonly activateResolvedTime: number,
|
|
public readonly activationReason: ExtensionActivationReason
|
|
) {
|
|
}
|
|
}
|
|
|
|
export class ExtensionPointContribution<T> {
|
|
readonly description: IExtensionDescription;
|
|
readonly value: T;
|
|
|
|
constructor(description: IExtensionDescription, value: T) {
|
|
this.description = description;
|
|
this.value = value;
|
|
}
|
|
}
|
|
|
|
export const ExtensionHostLogFileName = 'exthost';
|
|
|
|
export interface IWillActivateEvent {
|
|
readonly event: string;
|
|
readonly activation: Promise<void>;
|
|
}
|
|
|
|
export interface IResponsiveStateChangeEvent {
|
|
isResponsive: boolean;
|
|
}
|
|
|
|
export const enum ActivationKind {
|
|
Normal = 0,
|
|
Immediate = 1
|
|
}
|
|
|
|
export interface IExtensionService {
|
|
readonly _serviceBrand: undefined;
|
|
|
|
/**
|
|
* An event emitted when extensions are registered after their extension points got handled.
|
|
*
|
|
* This event will also fire on startup to signal the installed extensions.
|
|
*
|
|
* @returns the extensions that got registered
|
|
*/
|
|
onDidRegisterExtensions: Event<void>;
|
|
|
|
/**
|
|
* @event
|
|
* Fired when extensions status changes.
|
|
* The event contains the ids of the extensions that have changed.
|
|
*/
|
|
onDidChangeExtensionsStatus: Event<ExtensionIdentifier[]>;
|
|
|
|
/**
|
|
* Fired when the available extensions change (i.e. when extensions are added or removed).
|
|
*/
|
|
onDidChangeExtensions: Event<void>;
|
|
|
|
/**
|
|
* An event that is fired when activation happens.
|
|
*/
|
|
onWillActivateByEvent: Event<IWillActivateEvent>;
|
|
|
|
/**
|
|
* An event that is fired when an extension host changes its
|
|
* responsive-state.
|
|
*/
|
|
onDidChangeResponsiveChange: Event<IResponsiveStateChangeEvent>;
|
|
|
|
/**
|
|
* Send an activation event and activate interested extensions.
|
|
*
|
|
* This will wait for the normal startup of the extension host(s).
|
|
*
|
|
* In extraordinary circumstances, if the activation event needs to activate
|
|
* one or more extensions before the normal startup is finished, then you can use
|
|
* `ActivationKind.Immediate`. Please do not use this flag unless really necessary
|
|
* and you understand all consequences.
|
|
*/
|
|
activateByEvent(activationEvent: string, activationKind?: ActivationKind): Promise<void>;
|
|
|
|
/**
|
|
* An promise that resolves when the installed extensions are registered after
|
|
* their extension points got handled.
|
|
*/
|
|
whenInstalledExtensionsRegistered(): Promise<boolean>;
|
|
|
|
/**
|
|
* Return all registered extensions
|
|
*/
|
|
getExtensions(): Promise<IExtensionDescription[]>;
|
|
|
|
/**
|
|
* Return a specific extension
|
|
* @param id An extension id
|
|
*/
|
|
getExtension(id: string): Promise<IExtensionDescription | undefined>;
|
|
|
|
/**
|
|
* Returns `true` if the given extension can be added. Otherwise `false`.
|
|
* @param extension An extension
|
|
*/
|
|
canAddExtension(extension: IExtensionDescription): boolean;
|
|
|
|
/**
|
|
* Returns `true` if the given extension can be removed. Otherwise `false`.
|
|
* @param extension An extension
|
|
*/
|
|
canRemoveExtension(extension: IExtensionDescription): boolean;
|
|
|
|
/**
|
|
* Read all contributions to an extension point.
|
|
*/
|
|
readExtensionPointContributions<T extends IExtensionContributions[keyof IExtensionContributions]>(extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]>;
|
|
|
|
/**
|
|
* Get information about extensions status.
|
|
*/
|
|
getExtensionsStatus(): { [id: string]: IExtensionsStatus };
|
|
|
|
/**
|
|
* Return the inspect port or `0`, the latter means inspection
|
|
* is not possible.
|
|
*/
|
|
getInspectPort(tryEnableInspector: boolean): Promise<number>;
|
|
|
|
/**
|
|
* Stops the extension hosts.
|
|
*/
|
|
stopExtensionHosts(): void;
|
|
|
|
/**
|
|
* Restarts the extension host.
|
|
*/
|
|
restartExtensionHost(): Promise<void>;
|
|
|
|
/**
|
|
* Starts the extension hosts.
|
|
*/
|
|
startExtensionHosts(): Promise<void>;
|
|
|
|
/**
|
|
* Modify the environment of the remote extension host
|
|
* @param env New properties for the remote extension host
|
|
*/
|
|
setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
|
|
|
|
/**
|
|
* Please do not use!
|
|
* (This is public such that the extension host process can coordinate with and call back in the IExtensionService)
|
|
*/
|
|
_activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
|
|
/**
|
|
* Please do not use!
|
|
* (This is public such that the extension host process can coordinate with and call back in the IExtensionService)
|
|
*/
|
|
_onWillActivateExtension(extensionId: ExtensionIdentifier): void;
|
|
/**
|
|
* Please do not use!
|
|
* (This is public such that the extension host process can coordinate with and call back in the IExtensionService)
|
|
*/
|
|
_onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void;
|
|
/**
|
|
* Please do not use!
|
|
* (This is public such that the extension host process can coordinate with and call back in the IExtensionService)
|
|
*/
|
|
_onDidActivateExtensionError(extensionId: ExtensionIdentifier, error: Error): void;
|
|
/**
|
|
* Please do not use!
|
|
* (This is public such that the extension host process can coordinate with and call back in the IExtensionService)
|
|
*/
|
|
_onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void;
|
|
}
|
|
|
|
export interface ProfileSession {
|
|
stop(): Promise<IExtensionHostProfile>;
|
|
}
|
|
|
|
export function toExtension(extensionDescription: IExtensionDescription): IExtension {
|
|
return {
|
|
type: extensionDescription.isBuiltin ? ExtensionType.System : ExtensionType.User,
|
|
isBuiltin: extensionDescription.isBuiltin || extensionDescription.isUserBuiltin,
|
|
identifier: { id: getGalleryExtensionId(extensionDescription.publisher, extensionDescription.name), uuid: extensionDescription.uuid },
|
|
manifest: extensionDescription,
|
|
location: extensionDescription.extensionLocation,
|
|
};
|
|
}
|
|
|
|
export function toExtensionDescription(extension: IExtension, isUnderDevelopment?: boolean): IExtensionDescription {
|
|
return {
|
|
identifier: new ExtensionIdentifier(extension.identifier.id),
|
|
isBuiltin: extension.type === ExtensionType.System,
|
|
isUserBuiltin: extension.type === ExtensionType.User && extension.isBuiltin,
|
|
isUnderDevelopment: !!isUnderDevelopment,
|
|
extensionLocation: extension.location,
|
|
...extension.manifest,
|
|
uuid: extension.identifier.uuid
|
|
};
|
|
}
|
|
|
|
|
|
export class NullExtensionService implements IExtensionService {
|
|
declare readonly _serviceBrand: undefined;
|
|
onDidRegisterExtensions: Event<void> = Event.None;
|
|
onDidChangeExtensionsStatus: Event<ExtensionIdentifier[]> = Event.None;
|
|
onDidChangeExtensions: Event<void> = Event.None;
|
|
onWillActivateByEvent: Event<IWillActivateEvent> = Event.None;
|
|
onDidChangeResponsiveChange: Event<IResponsiveStateChangeEvent> = Event.None;
|
|
activateByEvent(_activationEvent: string): Promise<void> { return Promise.resolve(undefined); }
|
|
whenInstalledExtensionsRegistered(): Promise<boolean> { return Promise.resolve(true); }
|
|
getExtensions(): Promise<IExtensionDescription[]> { return Promise.resolve([]); }
|
|
getExtension() { return Promise.resolve(undefined); }
|
|
readExtensionPointContributions<T>(_extPoint: IExtensionPoint<T>): Promise<ExtensionPointContribution<T>[]> { return Promise.resolve(Object.create(null)); }
|
|
getExtensionsStatus(): { [id: string]: IExtensionsStatus; } { return Object.create(null); }
|
|
getInspectPort(_tryEnableInspector: boolean): Promise<number> { return Promise.resolve(0); }
|
|
stopExtensionHosts(): void { }
|
|
async restartExtensionHost(): Promise<void> { }
|
|
async startExtensionHosts(): Promise<void> { }
|
|
async setRemoteEnvironment(_env: { [key: string]: string | null }): Promise<void> { }
|
|
canAddExtension(): boolean { return false; }
|
|
canRemoveExtension(): boolean { return false; }
|
|
_activateById(_extensionId: ExtensionIdentifier, _reason: ExtensionActivationReason): Promise<void> { return Promise.resolve(); }
|
|
_onWillActivateExtension(_extensionId: ExtensionIdentifier): void { }
|
|
_onDidActivateExtension(_extensionId: ExtensionIdentifier, _codeLoadingTime: number, _activateCallTime: number, _activateResolvedTime: number, _activationReason: ExtensionActivationReason): void { }
|
|
_onDidActivateExtensionError(_extensionId: ExtensionIdentifier, _error: Error): void { }
|
|
_onExtensionRuntimeError(_extensionId: ExtensionIdentifier, _err: Error): void { }
|
|
_onExtensionHostExit(code: number): void { }
|
|
}
|