From cfcfc68b7089cdca876a0d5180415817f5f0482c Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Thu, 14 Dec 2023 15:27:33 +0100 Subject: [PATCH] RPC Protocol ClientProxyHandler Init Events #13172 * add an event to listen for init events on rpc proxy handler * eager initialize DOCUMENTS_EXT Proxy when EDITORS_AND_DOCUMENTS_EXT was initialized --- .../plugin-ext/src/common/proxy-handler.ts | 25 ++++++++------ .../plugin-ext/src/common/rpc-protocol.ts | 33 ++++++++++++++++++- .../src/main/browser/documents-main.ts | 5 +++ .../src/plugin/node/debug/debug.spec.ts | 9 +++++ 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/packages/plugin-ext/src/common/proxy-handler.ts b/packages/plugin-ext/src/common/proxy-handler.ts index 75beca868c6ec..0e2fe42cae60e 100644 --- a/packages/plugin-ext/src/common/proxy-handler.ts +++ b/packages/plugin-ext/src/common/proxy-handler.ts @@ -25,6 +25,7 @@ export interface RpcHandlerOptions { } export interface ProxyHandlerOptions extends RpcHandlerOptions { channelProvider: () => Promise, + onInitialize?: () => void, } export interface InvocationHandlerOptions extends RpcHandlerOptions { @@ -40,6 +41,7 @@ export class ClientProxyHandler implements ProxyHandler { readonly id: string; private readonly channelProvider: () => Promise; + private readonly onInitialize?: () => void; private readonly encoder: RpcMessageEncoder; private readonly decoder: RpcMessageDecoder; @@ -47,19 +49,22 @@ export class ClientProxyHandler implements ProxyHandler { Object.assign(this, options); } - private initializeRpc(): void { - const clientOptions: RpcProtocolOptions = { encoder: this.encoder, decoder: this.decoder, mode: 'clientOnly' }; - this.channelProvider().then(channel => { - const rpc = new RpcProtocol(channel, undefined, clientOptions); - this.rpcDeferred.resolve(rpc); - this.isRpcInitialized = true; - }); + initializeRpc(): void { + if (!this.isRpcInitialized) { + const clientOptions: RpcProtocolOptions = { encoder: this.encoder, decoder: this.decoder, mode: 'clientOnly' }; + this.channelProvider().then(channel => { + const rpc = new RpcProtocol(channel, undefined, clientOptions); + this.rpcDeferred.resolve(rpc); + this.isRpcInitialized = true; + if (this.onInitialize) { + this.onInitialize(); + } + }); + } } get(target: any, name: string, receiver: any): any { - if (!this.isRpcInitialized) { - this.initializeRpc(); - } + this.initializeRpc(); if (target[name] || name.charCodeAt(0) !== 36 /* CharCode.DollarSign */) { return target[name]; diff --git a/packages/plugin-ext/src/common/rpc-protocol.ts b/packages/plugin-ext/src/common/rpc-protocol.ts index 107c639ef2c60..452b44655a5e8 100644 --- a/packages/plugin-ext/src/common/rpc-protocol.ts +++ b/packages/plugin-ext/src/common/rpc-protocol.ts @@ -50,6 +50,16 @@ export interface RPCProtocol extends Disposable { */ set(identifier: ProxyIdentifier, instance: R): R; + /** + * Sent when a proxy handler is initialized. + */ + onInitialize: Event; + + /** + * Makes sure that the proxy is eagerly initialized. + */ + initialize(proxyId: ProxyIdentifier): void; + } export class ProxyIdentifier { @@ -80,9 +90,11 @@ export namespace ConnectionClosedError { export class RPCProtocolImpl implements RPCProtocol { private readonly locals = new Map(); private readonly proxies = new Map(); + private readonly handler = new Map(); private readonly multiplexer: ChannelMultiplexer; private readonly encoder = new MsgPackMessageEncoder(); private readonly decoder = new MsgPackMessageDecoder(); + private readonly onInitializeEmitter: Emitter = new Emitter(); private readonly toDispose = new DisposableCollection( Disposable.create(() => { /* mark as no disposed */ }) @@ -91,6 +103,8 @@ export class RPCProtocolImpl implements RPCProtocol { constructor(channel: Channel) { this.toDispose.push(this.multiplexer = new ChannelMultiplexer(new BatchingChannel(channel))); this.toDispose.push(Disposable.create(() => this.proxies.clear())); + this.toDispose.push(Disposable.create(() => this.handler.clear())); + this.toDispose.push(this.onInitializeEmitter); } dispose(): void { @@ -114,7 +128,12 @@ export class RPCProtocolImpl implements RPCProtocol { } protected createProxy(proxyId: string): T { - const handler = new ClientProxyHandler({ id: proxyId, encoder: this.encoder, decoder: this.decoder, channelProvider: () => this.multiplexer.open(proxyId) }); + const handler = new ClientProxyHandler({ + id: proxyId, encoder: this.encoder, decoder: this.decoder, + channelProvider: () => this.multiplexer.open(proxyId), + onInitialize: () => this.onInitializeEmitter.fire(proxyId), + }); + this.handler.set(proxyId, handler); return new Proxy(Object.create(null), handler); } @@ -147,6 +166,18 @@ export class RPCProtocolImpl implements RPCProtocol { } return instance; } + + get onInitialize(): Event { + return this.onInitializeEmitter.event; + }; + + initialize(proxyId: ProxyIdentifier): void { + /* make sure proxy exists */ + this.getProxy(proxyId); + /* init */ + const handler = this.handler.get(proxyId.id); + handler.initializeRpc(); + } } /** diff --git a/packages/plugin-ext/src/main/browser/documents-main.ts b/packages/plugin-ext/src/main/browser/documents-main.ts index 798738759d998..c2c2e70579165 100644 --- a/packages/plugin-ext/src/main/browser/documents-main.ts +++ b/packages/plugin-ext/src/main/browser/documents-main.ts @@ -98,6 +98,11 @@ export class DocumentsMainImpl implements DocumentsMain, Disposable { private untitledResourceResolver: UntitledResourceResolver, private languageService: MonacoLanguages, ) { + rpc.onInitialize(proxyId => { + if (proxyId === MAIN_RPC_CONTEXT.EDITORS_AND_DOCUMENTS_EXT.id) { + rpc.initialize(MAIN_RPC_CONTEXT.DOCUMENTS_EXT); + } + }); this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.DOCUMENTS_EXT); this.toDispose.push(editorsAndDocuments); diff --git a/packages/plugin-ext/src/plugin/node/debug/debug.spec.ts b/packages/plugin-ext/src/plugin/node/debug/debug.spec.ts index 16b2c330ac252..87802eedd494b 100644 --- a/packages/plugin-ext/src/plugin/node/debug/debug.spec.ts +++ b/packages/plugin-ext/src/plugin/node/debug/debug.spec.ts @@ -18,6 +18,7 @@ import * as chai from 'chai'; import { ProxyIdentifier, RPCProtocol } from '../../../common/rpc-protocol'; import { DebugExtImpl } from '../../debug/debug-ext'; +import { Emitter, Event } from '@theia/core'; const expect = chai.expect; @@ -25,6 +26,8 @@ describe('Debug API', () => { describe('#asDebugSourceURI', () => { + const mockEmitter = new Emitter(); + const mockRPCProtocol: RPCProtocol = { getProxy(_proxyId: ProxyIdentifier): T { return {} as T; @@ -34,6 +37,12 @@ describe('Debug API', () => { }, dispose(): void { // Nothing + }, + get onInitialize(): Event { + return mockEmitter.event; + }, + initialize(_proxyId: ProxyIdentifier): void { + // Nothing } };