@codingame/monaco-vscode-extensions-service-override
Version:
VSCode public API plugged on the monaco editor - extensions service-override
522 lines (518 loc) • 21.1 kB
JavaScript
import { __decorate, __param } from '@codingame/monaco-vscode-api/external/tslib/tslib.es6';
import { IntervalTimer } from '@codingame/monaco-vscode-api/vscode/vs/base/common/async';
import { VSBuffer } from '@codingame/monaco-vscode-api/vscode/vs/base/common/buffer';
import { onUnexpectedError } from '@codingame/monaco-vscode-api/vscode/vs/base/common/errors';
import { Emitter } from '@codingame/monaco-vscode-api/vscode/vs/base/common/event';
import { Disposable } from '@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle';
import { StopWatch } from '@codingame/monaco-vscode-api/vscode/vs/base/common/stopwatch';
import { localize2 } from '@codingame/monaco-vscode-api/vscode/vs/nls';
import { Categories } from '@codingame/monaco-vscode-api/vscode/vs/platform/action/common/actionCommonCategories';
import { registerAction2, Action2 } from '@codingame/monaco-vscode-api/vscode/vs/platform/actions/common/actions';
import { IInstantiationService } from '@codingame/monaco-vscode-api/vscode/vs/platform/instantiation/common/instantiation';
import { ILogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log.service';
import { getRemoteAuthorityPrefix, RemoteAuthorityResolverErrorCode } from '@codingame/monaco-vscode-api/vscode/vs/platform/remote/common/remoteAuthorityResolver';
import { ITelemetryService } from '@codingame/monaco-vscode-api/vscode/vs/platform/telemetry/common/telemetry.service';
import { IEditorService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/editor/common/editorService.service';
import { IWorkbenchEnvironmentService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/environment/common/environmentService.service';
import { ExtHostCustomersRegistry } from './extHostCustomers.js';
import { extensionHostKindToString } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensionHostKind';
import { ActivationKind } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensions';
import { RPCProtocol, RequestInitiator } from '@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/rpcProtocol';
var ExtensionHostManager_1;
let ExtensionHostManager = ExtensionHostManager_1 = class ExtensionHostManager extends Disposable {
get pid() {
return this._extensionHost.pid;
}
get kind() {
return this._extensionHost.runningLocation.kind;
}
get startup() {
return this._extensionHost.startup;
}
get friendyName() {
return friendlyExtHostName(this.kind, this.pid);
}
constructor(
extensionHost,
initialActivationEvents,
_internalExtensionService,
_instantiationService,
_environmentService,
_telemetryService,
_logService
) {
super();
this._internalExtensionService = _internalExtensionService;
this._instantiationService = _instantiationService;
this._environmentService = _environmentService;
this._telemetryService = _telemetryService;
this._logService = _logService;
this._onDidChangeResponsiveState = this._register(( new Emitter()));
this.onDidChangeResponsiveState = this._onDidChangeResponsiveState.event;
this._hasStarted = false;
this._cachedActivationEvents = ( new Map());
this._resolvedActivationEvents = ( new Set());
this._rpcProtocol = null;
this._customers = [];
this._extensionHost = extensionHost;
this.onDidExit = this._extensionHost.onExit;
const startingTelemetryEvent = {
time: Date.now(),
action: "starting",
kind: extensionHostKindToString(this.kind)
};
this._telemetryService.publicLog2("extensionHostStartup", startingTelemetryEvent);
this._proxy = this._extensionHost.start().then(protocol => {
const successTelemetryEvent = {
time: Date.now(),
action: "success",
kind: extensionHostKindToString(this.kind)
};
this._telemetryService.publicLog2("extensionHostStartup", successTelemetryEvent);
return this._createExtensionHostCustomers(this.kind, protocol);
}, err => {
this._logService.error(
`Error received from starting extension host (kind: ${extensionHostKindToString(this.kind)})`
);
this._logService.error(err);
const failureTelemetryEvent = {
time: Date.now(),
action: "error",
kind: extensionHostKindToString(this.kind)
};
if (err && err.name) {
failureTelemetryEvent.errorName = err.name;
}
if (err && err.message) {
failureTelemetryEvent.errorMessage = err.message;
}
if (err && err.stack) {
failureTelemetryEvent.errorStack = err.stack;
}
this._telemetryService.publicLog2("extensionHostStartup", failureTelemetryEvent);
return null;
});
this._proxy.then(() => {
this._hasStarted = true;
initialActivationEvents.forEach(
activationEvent => this.activateByEvent(activationEvent, ActivationKind.Normal)
);
this._register(registerLatencyTestProvider({
measure: () => this.measure()
}));
});
}
async disconnect() {
await this._extensionHost?.disconnect?.();
}
dispose() {
this._extensionHost?.dispose();
this._rpcProtocol?.dispose();
for (let i = 0, len = this._customers.length; i < len; i++) {
const customer = this._customers[i];
try {
customer.dispose();
} catch (err) {
onUnexpectedError(err);
}
}
this._proxy = null;
super.dispose();
}
async measure() {
const proxy = await this._proxy;
if (!proxy) {
return null;
}
const latency = await this._measureLatency(proxy);
const down = await this._measureDown(proxy);
const up = await this._measureUp(proxy);
return {
remoteAuthority: this._extensionHost.remoteAuthority,
latency,
down,
up
};
}
get isReady() {
return this._hasStarted;
}
async ready() {
await this._proxy;
}
async _measureLatency(proxy) {
const COUNT = 10;
let sum = 0;
for (let i = 0; i < COUNT; i++) {
const sw = StopWatch.create();
await proxy.test_latency(i);
sw.stop();
sum += sw.elapsed();
}
return (sum / COUNT);
}
static _convert(byteCount, elapsedMillis) {
return (byteCount * 1000 * 8) / elapsedMillis;
}
async _measureUp(proxy) {
const SIZE = 10 * 1024 * 1024;
const buff = VSBuffer.alloc(SIZE);
const value = Math.ceil(Math.random() * 256);
for (let i = 0; i < buff.byteLength; i++) {
buff.writeUInt8(i, value);
}
const sw = StopWatch.create();
await proxy.test_up(buff);
sw.stop();
return ExtensionHostManager_1._convert(SIZE, sw.elapsed());
}
async _measureDown(proxy) {
const SIZE = 10 * 1024 * 1024;
const sw = StopWatch.create();
await proxy.test_down(SIZE);
sw.stop();
return ExtensionHostManager_1._convert(SIZE, sw.elapsed());
}
_createExtensionHostCustomers(kind, protocol) {
let logger = null;
if (this._environmentService.logExtensionHostCommunication) {
logger = ( new RPCLogger(kind));
} else if (TelemetryRPCLogger.isEnabled()) {
logger = ( new TelemetryRPCLogger(this._telemetryService));
}
this._rpcProtocol = ( new RPCProtocol(protocol, logger));
this._register(
this._rpcProtocol.onDidChangeResponsiveState(responsiveState => this._onDidChangeResponsiveState.fire(responsiveState))
);
let extensionHostProxy = null;
let mainProxyIdentifiers = [];
const extHostContext = {
remoteAuthority: this._extensionHost.remoteAuthority,
extensionHostKind: this.kind,
getProxy: identifier => ( this._rpcProtocol.getProxy(identifier)),
set: (identifier, instance) => this._rpcProtocol.set(identifier, instance),
dispose: () => this._rpcProtocol.dispose(),
assertRegistered: identifiers => this._rpcProtocol.assertRegistered(identifiers),
drain: () => this._rpcProtocol.drain(),
internalExtensionService: this._internalExtensionService,
_setExtensionHostProxy: value => {
extensionHostProxy = value;
},
_setAllMainProxyIdentifiers: value => {
mainProxyIdentifiers = value;
}
};
const namedCustomers = ExtHostCustomersRegistry.getNamedCustomers();
for (let i = 0, len = namedCustomers.length; i < len; i++) {
const [id, ctor] = namedCustomers[i];
try {
const instance = this._instantiationService.createInstance(ctor, extHostContext);
this._customers.push(instance);
this._rpcProtocol.set(id, instance);
} catch (err) {
this._logService.error(`Cannot instantiate named customer: '${id.sid}'`);
this._logService.error(err);
onUnexpectedError(err);
}
}
const customers = ExtHostCustomersRegistry.getCustomers();
for (const ctor of customers) {
try {
const instance = this._instantiationService.createInstance(ctor, extHostContext);
this._customers.push(instance);
} catch (err) {
this._logService.error(err);
onUnexpectedError(err);
}
}
if (!extensionHostProxy) {
throw ( new Error(`Missing IExtensionHostProxy!`));
}
this._rpcProtocol.assertRegistered(mainProxyIdentifiers);
return extensionHostProxy;
}
async activate(extension, reason) {
const proxy = await this._proxy;
if (!proxy) {
return false;
}
return proxy.activate(extension, reason);
}
activateByEvent(activationEvent, activationKind) {
if (!( this._cachedActivationEvents.has(activationEvent))) {
this._cachedActivationEvents.set(activationEvent, this._activateByEvent(activationEvent, activationKind));
}
return this._cachedActivationEvents.get(activationEvent);
}
activationEventIsDone(activationEvent) {
return ( this._resolvedActivationEvents.has(activationEvent));
}
async _activateByEvent(activationEvent, activationKind) {
if (!this._proxy) {
return;
}
const proxy = await this._proxy;
if (!proxy) {
return;
}
if (!this._extensionHost.extensions.containsActivationEvent(activationEvent)) {
this._resolvedActivationEvents.add(activationEvent);
return;
}
await proxy.activateByEvent(activationEvent, activationKind);
this._resolvedActivationEvents.add(activationEvent);
}
async getInspectPort(tryEnableInspector) {
if (this._extensionHost) {
if (tryEnableInspector) {
await this._extensionHost.enableInspectPort();
}
const port = this._extensionHost.getInspectPort();
if (port) {
return port;
}
}
return undefined;
}
async resolveAuthority(remoteAuthority, resolveAttempt) {
const sw = StopWatch.create(false);
const prefix = () => `[${extensionHostKindToString(this._extensionHost.runningLocation.kind)}${this._extensionHost.runningLocation.affinity}][resolveAuthority(${getRemoteAuthorityPrefix(remoteAuthority)},${resolveAttempt})][${sw.elapsed()}ms] `;
const logInfo = msg => this._logService.info(`${prefix()}${msg}`);
const logError = (msg, err = undefined) => this._logService.error(`${prefix()}${msg}`, err);
logInfo(`obtaining proxy...`);
const proxy = await this._proxy;
if (!proxy) {
logError(`no proxy`);
return {
type: "error",
error: {
message: `Cannot resolve authority`,
code: RemoteAuthorityResolverErrorCode.Unknown,
detail: undefined
}
};
}
logInfo(`invoking...`);
const intervalLogger = ( new IntervalTimer());
try {
intervalLogger.cancelAndSet(() => logInfo("waiting..."), 1000);
const resolverResult = await proxy.resolveAuthority(remoteAuthority, resolveAttempt);
intervalLogger.dispose();
if (resolverResult.type === "ok") {
logInfo(`returned ${resolverResult.value.authority.connectTo}`);
} else {
logError(`returned an error`, resolverResult.error);
}
return resolverResult;
} catch (err) {
intervalLogger.dispose();
logError(`returned an error`, err);
return {
type: "error",
error: {
message: err.message,
code: RemoteAuthorityResolverErrorCode.Unknown,
detail: err
}
};
}
}
async getCanonicalURI(remoteAuthority, uri) {
const proxy = await this._proxy;
if (!proxy) {
throw ( new Error(`Cannot resolve canonical URI`));
}
return proxy.getCanonicalURI(remoteAuthority, uri);
}
async start(extensionRegistryVersionId, allExtensions, myExtensions) {
const proxy = await this._proxy;
if (!proxy) {
return;
}
const deltaExtensions = this._extensionHost.extensions.set(extensionRegistryVersionId, allExtensions, myExtensions);
return proxy.startExtensionHost(deltaExtensions);
}
async extensionTestsExecute() {
const proxy = await this._proxy;
if (!proxy) {
throw ( new Error("Could not obtain Extension Host Proxy"));
}
return proxy.extensionTestsExecute();
}
representsRunningLocation(runningLocation) {
return this._extensionHost.runningLocation.equals(runningLocation);
}
async deltaExtensions(incomingExtensionsDelta) {
const proxy = await this._proxy;
if (!proxy) {
return;
}
const outgoingExtensionsDelta = this._extensionHost.extensions.delta(incomingExtensionsDelta);
if (!outgoingExtensionsDelta) {
return;
}
return proxy.deltaExtensions(outgoingExtensionsDelta);
}
containsExtension(extensionId) {
return this._extensionHost.extensions?.containsExtension(extensionId) ?? false;
}
async setRemoteEnvironment(env) {
const proxy = await this._proxy;
if (!proxy) {
return;
}
return proxy.setRemoteEnvironment(env);
}
};
ExtensionHostManager = ExtensionHostManager_1 = ( __decorate([( __param(3, IInstantiationService)), ( __param(4, IWorkbenchEnvironmentService)), ( __param(5, ITelemetryService)), ( __param(6, ILogService))], ExtensionHostManager));
function friendlyExtHostName(kind, pid) {
if (pid) {
return `${extensionHostKindToString(kind)} pid: ${pid}`;
}
return `${extensionHostKindToString(kind)}`;
}
const colorTables = [
["#2977B1", "#FC802D", "#34A13A", "#D3282F", "#9366BA"],
["#8B564C", "#E177C0", "#7F7F7F", "#BBBE3D", "#2EBECD"]
];
function prettyWithoutArrays(data) {
if (Array.isArray(data)) {
return data;
}
if (data && typeof data === "object" && typeof data.toString === "function") {
const result = ( data.toString());
if (result !== "[object Object]") {
return result;
}
}
return data;
}
function pretty(data) {
if (Array.isArray(data)) {
return ( data.map(prettyWithoutArrays));
}
return prettyWithoutArrays(data);
}
class RPCLogger {
constructor(_kind) {
this._kind = _kind;
this._totalIncoming = 0;
this._totalOutgoing = 0;
}
_log(direction, totalLength, msgLength, req, initiator, str, data) {
data = pretty(data);
const colorTable = colorTables[initiator];
const color = colorTable[req % colorTable.length] ;
let args = [
`%c[${extensionHostKindToString(this._kind)}][${direction}]%c[${String(totalLength).padStart(7)}]%c[len: ${String(msgLength).padStart(5)}]%c${String(req).padStart(5)} - ${str}`,
"color: darkgreen",
"color: grey",
"color: grey",
`color: ${color}`
];
if (/\($/.test(str)) {
args = args.concat(data);
args.push(")");
} else {
args.push(data);
}
console.log.apply(console, args);
}
logIncoming(msgLength, req, initiator, str, data) {
this._totalIncoming += msgLength;
this._log("Ext → Win", this._totalIncoming, msgLength, req, initiator, str, data);
}
logOutgoing(msgLength, req, initiator, str, data) {
this._totalOutgoing += msgLength;
this._log("Win → Ext", this._totalOutgoing, msgLength, req, initiator, str, data);
}
}
let TelemetryRPCLogger = class TelemetryRPCLogger {
static isEnabled() {
return Math.random() < 0.0001;
}
constructor(_telemetryService) {
this._telemetryService = _telemetryService;
this._pendingRequests = ( new Map());
}
logIncoming(msgLength, req, initiator, str) {
if (initiator === RequestInitiator.LocalSide && /^receiveReply(Err)?:/.test(str)) {
const requestStr = this._pendingRequests.get(req) ?? "unknown_reply";
this._pendingRequests.delete(req);
this._telemetryService.publicLog2("extensionhost.incoming", {
type: `${str} ${requestStr}`,
length: msgLength
});
}
if (initiator === RequestInitiator.OtherSide && /^receiveRequest /.test(str)) {
this._telemetryService.publicLog2("extensionhost.incoming", {
type: `${str}`,
length: msgLength
});
}
}
logOutgoing(msgLength, req, initiator, str) {
if (initiator === RequestInitiator.LocalSide && str.startsWith("request: ")) {
this._pendingRequests.set(req, str);
this._telemetryService.publicLog2("extensionhost.outgoing", {
type: str,
length: msgLength
});
}
}
};
TelemetryRPCLogger = ( __decorate([( __param(0, ITelemetryService))], TelemetryRPCLogger));
const providers = [];
function registerLatencyTestProvider(provider) {
providers.push(provider);
return {
dispose: () => {
for (let i = 0; i < providers.length; i++) {
if (providers[i] === provider) {
providers.splice(i, 1);
return;
}
}
}
};
}
function getLatencyTestProviders() {
return providers.slice(0);
}
registerAction2(class MeasureExtHostLatencyAction extends Action2 {
constructor() {
super({
id: "editor.action.measureExtHostLatency",
title: ( localize2(16643, "Measure Extension Host Latency")),
category: Categories.Developer,
f1: true
});
}
async run(accessor) {
const editorService = accessor.get(IEditorService);
const measurements = await Promise.all(( getLatencyTestProviders().map(provider => provider.measure())));
editorService.openEditor({
resource: undefined,
contents: ( measurements.map(MeasureExtHostLatencyAction._print)).join("\n\n"),
options: {
pinned: true
}
});
}
static _print(m) {
if (!m) {
return "";
}
return `${m.remoteAuthority ? `Authority: ${m.remoteAuthority}\n` : ``}Roundtrip latency: ${m.latency.toFixed(3)}ms\nUp: ${MeasureExtHostLatencyAction._printSpeed(m.up)}\nDown: ${MeasureExtHostLatencyAction._printSpeed(m.down)}\n`;
}
static _printSpeed(n) {
if (n <= 1024) {
return `${n} bps`;
}
if (n < 1024 * 1024) {
return `${(n / 1024).toFixed(1)} kbps`;
}
return `${(n / 1024 / 1024).toFixed(1)} Mbps`;
}
});
export { ExtensionHostManager, friendlyExtHostName };