Skip to content

Commit

Permalink
RPC Protocol ClientProxyHandler Init Events #13172
Browse files Browse the repository at this point in the history
* add an event to listen for init events on rpc proxy handler
* eager initialize DOCUMENTS_EXT Proxy when EDITORS_AND_DOCUMENTS_EXT
was initialized
  • Loading branch information
jfaltermeier committed Dec 14, 2023
1 parent 00e30c1 commit cfcfc68
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 11 deletions.
25 changes: 15 additions & 10 deletions packages/plugin-ext/src/common/proxy-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface RpcHandlerOptions {
}
export interface ProxyHandlerOptions extends RpcHandlerOptions {
channelProvider: () => Promise<Channel>,
onInitialize?: () => void,
}

export interface InvocationHandlerOptions extends RpcHandlerOptions {
Expand All @@ -40,26 +41,30 @@ export class ClientProxyHandler<T extends object> implements ProxyHandler<T> {

readonly id: string;
private readonly channelProvider: () => Promise<Channel>;
private readonly onInitialize?: () => void;
private readonly encoder: RpcMessageEncoder;
private readonly decoder: RpcMessageDecoder;

constructor(options: ProxyHandlerOptions) {
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];
Expand Down
33 changes: 32 additions & 1 deletion packages/plugin-ext/src/common/rpc-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ export interface RPCProtocol extends Disposable {
*/
set<T, R extends T>(identifier: ProxyIdentifier<T>, instance: R): R;

/**
* Sent when a proxy handler is initialized.
*/
onInitialize: Event<string>;

/**
* Makes sure that the proxy is eagerly initialized.
*/
initialize<T>(proxyId: ProxyIdentifier<T>): void;

}

export class ProxyIdentifier<T> {
Expand Down Expand Up @@ -80,9 +90,11 @@ export namespace ConnectionClosedError {
export class RPCProtocolImpl implements RPCProtocol {
private readonly locals = new Map<string, RpcInvocationHandler>();
private readonly proxies = new Map<string, any>();
private readonly handler = new Map<string, any>();
private readonly multiplexer: ChannelMultiplexer;
private readonly encoder = new MsgPackMessageEncoder();
private readonly decoder = new MsgPackMessageDecoder();
private readonly onInitializeEmitter: Emitter<string> = new Emitter();

private readonly toDispose = new DisposableCollection(
Disposable.create(() => { /* mark as no disposed */ })
Expand All @@ -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 {
Expand All @@ -114,7 +128,12 @@ export class RPCProtocolImpl implements RPCProtocol {
}

protected createProxy<T>(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);
}

Expand Down Expand Up @@ -147,6 +166,18 @@ export class RPCProtocolImpl implements RPCProtocol {
}
return instance;
}

get onInitialize(): Event<string> {
return this.onInitializeEmitter.event;
};

initialize<T>(proxyId: ProxyIdentifier<T>): void {
/* make sure proxy exists */
this.getProxy(proxyId);
/* init */
const handler = this.handler.get(proxyId.id);
handler.initializeRpc();
}
}

/**
Expand Down
5 changes: 5 additions & 0 deletions packages/plugin-ext/src/main/browser/documents-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions packages/plugin-ext/src/plugin/node/debug/debug.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@ 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;

describe('Debug API', () => {

describe('#asDebugSourceURI', () => {

const mockEmitter = new Emitter<string>();

const mockRPCProtocol: RPCProtocol = {
getProxy<T>(_proxyId: ProxyIdentifier<T>): T {
return {} as T;
Expand All @@ -34,6 +37,12 @@ describe('Debug API', () => {
},
dispose(): void {
// Nothing
},
get onInitialize(): Event<string> {
return mockEmitter.event;
},
initialize<T>(_proxyId: ProxyIdentifier<T>): void {
// Nothing
}
};

Expand Down

0 comments on commit cfcfc68

Please sign in to comment.