@theia/plugin-ext 1.33.0-next.5 → 1.33.0-next.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/lib/common/index.js +2 -0
  2. package/lib/common/index.js.map +1 -1
  3. package/lib/common/plugin-api-rpc.d.ts +5 -3
  4. package/lib/common/plugin-api-rpc.d.ts.map +1 -1
  5. package/lib/common/plugin-api-rpc.js +0 -2
  6. package/lib/common/plugin-api-rpc.js.map +1 -1
  7. package/lib/common/plugin-protocol.d.ts +4 -4
  8. package/lib/common/plugin-protocol.d.ts.map +1 -1
  9. package/lib/common/proxy-handler.d.ts +66 -0
  10. package/lib/common/proxy-handler.d.ts.map +1 -0
  11. package/lib/common/proxy-handler.js +98 -0
  12. package/lib/common/proxy-handler.js.map +1 -0
  13. package/lib/common/rpc-protocol.d.ts +25 -51
  14. package/lib/common/rpc-protocol.d.ts.map +1 -1
  15. package/lib/common/rpc-protocol.js +126 -323
  16. package/lib/common/rpc-protocol.js.map +1 -1
  17. package/lib/hosted/browser/hosted-plugin-watcher.d.ts +1 -1
  18. package/lib/hosted/browser/hosted-plugin-watcher.d.ts.map +1 -1
  19. package/lib/hosted/browser/hosted-plugin-watcher.js.map +1 -1
  20. package/lib/hosted/browser/hosted-plugin.d.ts.map +1 -1
  21. package/lib/hosted/browser/hosted-plugin.js +13 -8
  22. package/lib/hosted/browser/hosted-plugin.js.map +1 -1
  23. package/lib/hosted/browser/plugin-worker.d.ts.map +1 -1
  24. package/lib/hosted/browser/plugin-worker.js +14 -9
  25. package/lib/hosted/browser/plugin-worker.js.map +1 -1
  26. package/lib/hosted/browser/worker/worker-main.d.ts +1 -1
  27. package/lib/hosted/browser/worker/worker-main.d.ts.map +1 -1
  28. package/lib/hosted/browser/worker/worker-main.js +23 -22
  29. package/lib/hosted/browser/worker/worker-main.js.map +1 -1
  30. package/lib/hosted/node/hosted-plugin-process.d.ts +5 -4
  31. package/lib/hosted/node/hosted-plugin-process.d.ts.map +1 -1
  32. package/lib/hosted/node/hosted-plugin-process.js +20 -13
  33. package/lib/hosted/node/hosted-plugin-process.js.map +1 -1
  34. package/lib/hosted/node/hosted-plugin-protocol.d.ts +22 -0
  35. package/lib/hosted/node/hosted-plugin-protocol.d.ts.map +1 -0
  36. package/lib/hosted/node/hosted-plugin-protocol.js +37 -0
  37. package/lib/hosted/node/hosted-plugin-protocol.js.map +1 -0
  38. package/lib/hosted/node/hosted-plugin.d.ts +1 -1
  39. package/lib/hosted/node/hosted-plugin.d.ts.map +1 -1
  40. package/lib/hosted/node/hosted-plugin.js.map +1 -1
  41. package/lib/hosted/node/plugin-host.d.ts +1 -1
  42. package/lib/hosted/node/plugin-host.d.ts.map +1 -1
  43. package/lib/hosted/node/plugin-host.js +10 -21
  44. package/lib/hosted/node/plugin-host.js.map +1 -1
  45. package/lib/hosted/node/plugin-service.d.ts +1 -1
  46. package/lib/hosted/node/plugin-service.d.ts.map +1 -1
  47. package/lib/hosted/node/plugin-service.js.map +1 -1
  48. package/lib/main/browser/main-context.d.ts.map +1 -1
  49. package/lib/main/browser/main-context.js +0 -3
  50. package/lib/main/browser/main-context.js.map +1 -1
  51. package/lib/main/browser/tasks-main.d.ts.map +1 -1
  52. package/lib/main/browser/tasks-main.js +13 -9
  53. package/lib/main/browser/tasks-main.js.map +1 -1
  54. package/lib/plugin/type-converters.d.ts.map +1 -1
  55. package/lib/plugin/type-converters.js +6 -14
  56. package/lib/plugin/type-converters.js.map +1 -1
  57. package/lib/plugin/type-converters.spec.js +5 -4
  58. package/lib/plugin/type-converters.spec.js.map +1 -1
  59. package/lib/plugin/types-impl.d.ts +4 -8
  60. package/lib/plugin/types-impl.d.ts.map +1 -1
  61. package/lib/plugin/types-impl.js +8 -29
  62. package/lib/plugin/types-impl.js.map +1 -1
  63. package/package.json +25 -25
  64. package/src/common/index.ts +4 -0
  65. package/src/common/plugin-api-rpc.ts +5 -3
  66. package/src/common/plugin-protocol.ts +4 -4
  67. package/src/common/proxy-handler.ts +126 -0
  68. package/src/common/rpc-protocol.ts +128 -401
  69. package/src/hosted/browser/hosted-plugin-watcher.ts +4 -3
  70. package/src/hosted/browser/hosted-plugin.ts +17 -8
  71. package/src/hosted/browser/plugin-worker.ts +16 -10
  72. package/src/hosted/browser/worker/worker-main.ts +25 -26
  73. package/src/hosted/node/hosted-plugin-process.ts +25 -16
  74. package/src/hosted/node/hosted-plugin-protocol.ts +49 -0
  75. package/src/hosted/node/hosted-plugin.ts +1 -1
  76. package/src/hosted/node/plugin-host.ts +11 -22
  77. package/src/hosted/node/plugin-service.ts +1 -1
  78. package/src/main/browser/main-context.ts +0 -4
  79. package/src/main/browser/tasks-main.ts +13 -9
  80. package/src/plugin/type-converters.spec.ts +5 -4
  81. package/src/plugin/type-converters.ts +10 -12
  82. package/src/plugin/types-impl.ts +6 -26
@@ -22,14 +22,17 @@
22
22
 
23
23
  /* eslint-disable @typescript-eslint/no-explicit-any */
24
24
 
25
+ import { Channel, Disposable, DisposableCollection, ReadBuffer, WriteBuffer } from '@theia/core';
25
26
  import { Emitter, Event } from '@theia/core/lib/common/event';
26
- import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposable';
27
- import { Deferred } from '@theia/core/lib/common/promise-util';
27
+ import { ChannelMultiplexer, MessageProvider } from '@theia/core/lib/common/message-rpc/channel';
28
+ import { MsgPackMessageDecoder, MsgPackMessageEncoder } from '@theia/core/lib/common/message-rpc/rpc-message-encoder';
29
+ import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '@theia/core/lib/common/message-rpc/uint8-array-message-buffer';
30
+ import { ClientProxyHandler, RpcInvocationHandler } from './proxy-handler';
31
+ import { MsgPackExtensionManager } from '@theia/core/lib/common/message-rpc/msg-pack-extension-manager';
28
32
  import { URI as VSCodeURI } from '@theia/core/shared/vscode-uri';
29
33
  import URI from '@theia/core/lib/common/uri';
30
- import { CancellationToken, CancellationTokenSource } from '@theia/core/shared/vscode-languageserver-protocol';
31
- import { Range, Position } from '../plugin/types-impl';
32
34
  import { BinaryBuffer } from '@theia/core/lib/common/buffer';
35
+ import { Range, Position } from '../plugin/types-impl';
33
36
 
34
37
  export interface MessageConnection {
35
38
  send(msg: string): void;
@@ -76,49 +79,29 @@ export namespace ConnectionClosedError {
76
79
  }
77
80
 
78
81
  export class RPCProtocolImpl implements RPCProtocol {
79
-
80
- private readonly locals = new Map<string, any>();
82
+ private readonly locals = new Map<string, RpcInvocationHandler>();
81
83
  private readonly proxies = new Map<string, any>();
82
- private lastMessageId = 0;
83
- private readonly cancellationTokenSources = new Map<string, CancellationTokenSource>();
84
- private readonly pendingRPCReplies = new Map<string, Deferred<any>>();
85
- private readonly multiplexer: RPCMultiplexer;
86
-
87
- private replacer: (key: string | undefined, value: any) => any;
88
- private reviver: (key: string | undefined, value: any) => any;
84
+ private readonly multiplexer: ChannelMultiplexer;
85
+ private readonly encoder = new MsgPackMessageEncoder();
86
+ private readonly decoder = new MsgPackMessageDecoder();
89
87
 
90
88
  private readonly toDispose = new DisposableCollection(
91
89
  Disposable.create(() => { /* mark as no disposed */ })
92
90
  );
93
91
 
94
- constructor(connection: MessageConnection, transformations?: {
95
- replacer?: (key: string | undefined, value: any) => any,
96
- reviver?: (key: string | undefined, value: any) => any
97
- }) {
98
- this.toDispose.push(
99
- this.multiplexer = new RPCMultiplexer(connection)
100
- );
101
- this.multiplexer.onMessage(msg => this.receiveOneMessage(msg));
102
- this.toDispose.push(Disposable.create(() => {
103
- this.proxies.clear();
104
- for (const reply of this.pendingRPCReplies.values()) {
105
- reply.reject(ConnectionClosedError.create());
106
- }
107
- this.pendingRPCReplies.clear();
108
- }));
109
-
110
- this.reviver = transformations?.reviver || ObjectsTransferrer.reviver;
111
- this.replacer = transformations?.replacer || ObjectsTransferrer.replacer;
112
- }
113
-
114
- private get isDisposed(): boolean {
115
- return this.toDispose.disposed;
92
+ constructor(channel: Channel) {
93
+ this.toDispose.push(this.multiplexer = new ChannelMultiplexer(new BatchingChannel(channel)));
94
+ this.toDispose.push(Disposable.create(() => this.proxies.clear()));
116
95
  }
117
96
 
118
97
  dispose(): void {
119
98
  this.toDispose.dispose();
120
99
  }
121
100
 
101
+ protected get isDisposed(): boolean {
102
+ return this.toDispose.disposed;
103
+ }
104
+
122
105
  getProxy<T>(proxyId: ProxyIdentifier<T>): T {
123
106
  if (this.isDisposed) {
124
107
  throw ConnectionClosedError.create();
@@ -131,274 +114,125 @@ export class RPCProtocolImpl implements RPCProtocol {
131
114
  return proxy;
132
115
  }
133
116
 
134
- set<T, R extends T>(identifier: ProxyIdentifier<T>, instance: R): R {
135
- if (this.isDisposed) {
136
- throw ConnectionClosedError.create();
137
- }
138
- this.locals.set(identifier.id, instance);
139
- if (Disposable.is(instance)) {
140
- this.toDispose.push(instance);
141
- }
142
- this.toDispose.push(Disposable.create(() => this.locals.delete(identifier.id)));
143
- return instance;
144
- }
145
-
146
- private createProxy<T>(proxyId: string): T {
147
- const handler = {
148
- get: (target: any, name: string) => {
149
- if (!target[name] && name.charCodeAt(0) === 36 /* CharCode.DollarSign */) {
150
- target[name] = (...myArgs: any[]) =>
151
- this.remoteCall(proxyId, name, myArgs);
152
- }
153
- return target[name];
154
- }
155
- };
117
+ protected createProxy<T>(proxyId: string): T {
118
+ const handler = new ClientProxyHandler({ id: proxyId, encoder: this.encoder, decoder: this.decoder, channelProvider: () => this.multiplexer.open(proxyId) });
156
119
  return new Proxy(Object.create(null), handler);
157
120
  }
158
121
 
159
- private remoteCall(proxyId: string, methodName: string, args: any[]): Promise<any> {
122
+ set<T, R extends T>(identifier: ProxyIdentifier<T>, instance: R): R {
160
123
  if (this.isDisposed) {
161
- return Promise.reject(ConnectionClosedError.create());
162
- }
163
- const cancellationToken: CancellationToken | undefined = args.length && CancellationToken.is(args[args.length - 1]) ? args.pop() : undefined;
164
- if (cancellationToken && cancellationToken.isCancellationRequested) {
165
- return Promise.reject(canceled());
166
- }
167
-
168
- const callId = String(++this.lastMessageId);
169
- const result = new Deferred();
170
-
171
- if (cancellationToken) {
172
- args.push('add.cancellation.token');
173
- cancellationToken.onCancellationRequested(() =>
174
- this.multiplexer.send(this.cancel(callId))
175
- );
124
+ throw ConnectionClosedError.create();
176
125
  }
126
+ const invocationHandler = this.locals.get(identifier.id);
127
+ if (!invocationHandler) {
128
+ const handler = new RpcInvocationHandler({ id: identifier.id, target: instance, encoder: this.encoder, decoder: this.decoder });
177
129
 
178
- this.pendingRPCReplies.set(callId, result);
179
- this.multiplexer.send(this.request(callId, proxyId, methodName, args));
180
- return result.promise;
181
- }
182
-
183
- private receiveOneMessage(rawmsg: string): void {
184
- if (this.isDisposed) {
185
- return;
186
- }
187
- try {
188
- const msg = <RPCMessage>JSON.parse(rawmsg, this.reviver);
189
-
190
- switch (msg.type) {
191
- case MessageType.Request:
192
- this.receiveRequest(msg);
193
- break;
194
- case MessageType.Reply:
195
- this.receiveReply(msg);
196
- break;
197
- case MessageType.ReplyErr:
198
- this.receiveReplyErr(msg);
199
- break;
200
- case MessageType.Cancel:
201
- this.receiveCancel(msg);
202
- break;
130
+ const channel = this.multiplexer.getOpenChannel(identifier.id);
131
+ if (channel) {
132
+ handler.listen(channel);
133
+ } else {
134
+ const channelOpenListener = this.multiplexer.onDidOpenChannel(event => {
135
+ if (event.id === identifier.id) {
136
+ handler.listen(event.channel);
137
+ channelOpenListener.dispose();
138
+ }
139
+ });
203
140
  }
204
- } catch (e) {
205
- // exception does not show problematic content: log it!
206
- console.log('failed to parse message: ' + rawmsg);
207
- throw e;
208
- }
209
-
210
- }
211
-
212
- private receiveCancel(msg: CancelMessage): void {
213
- const cancellationTokenSource = this.cancellationTokenSources.get(msg.id);
214
- if (cancellationTokenSource) {
215
- cancellationTokenSource.cancel();
216
- }
217
- }
218
-
219
- private receiveRequest(msg: RequestMessage): void {
220
- const callId = msg.id;
221
- const proxyId = msg.proxyId;
222
- // convert `null` to `undefined`, since we don't use `null` in internal plugin APIs
223
- const args = msg.args.map(arg => arg === null ? undefined : arg); // eslint-disable-line no-null/no-null
224
-
225
- const addToken = args.length && args[args.length - 1] === 'add.cancellation.token' ? args.pop() : false;
226
- if (addToken) {
227
- const tokenSource = new CancellationTokenSource();
228
- this.cancellationTokenSources.set(callId, tokenSource);
229
- args.push(tokenSource.token);
230
- }
231
- const invocation = this.invokeHandler(proxyId, msg.method, args);
232
-
233
- invocation.then(result => {
234
- this.cancellationTokenSources.delete(callId);
235
- this.multiplexer.send(this.replyOK(callId, result));
236
- }, error => {
237
- this.cancellationTokenSources.delete(callId);
238
- this.multiplexer.send(this.replyErr(callId, error));
239
- });
240
- }
241
-
242
- private receiveReply(msg: ReplyMessage): void {
243
- const callId = msg.id;
244
- const pendingReply = this.pendingRPCReplies.get(callId);
245
- if (!pendingReply) {
246
- return;
247
- }
248
- this.pendingRPCReplies.delete(callId);
249
- pendingReply.resolve(msg.res);
250
- }
251
-
252
- private receiveReplyErr(msg: ReplyErrMessage): void {
253
- const callId = msg.id;
254
- const pendingReply = this.pendingRPCReplies.get(callId);
255
- if (!pendingReply) {
256
- return;
257
- }
258
- this.pendingRPCReplies.delete(callId);
259
-
260
- let err: Error | undefined = undefined;
261
- if (msg.err && msg.err.$isError) {
262
- err = new Error();
263
- err.name = msg.err.name;
264
- err.message = msg.err.message;
265
- err.stack = msg.err.stack;
266
- }
267
- pendingReply.reject(err);
268
- }
269
-
270
- private invokeHandler(proxyId: string, methodName: string, args: any[]): Promise<any> {
271
- try {
272
- return Promise.resolve(this.doInvokeHandler(proxyId, methodName, args));
273
- } catch (err) {
274
- return Promise.reject(err);
275
- }
276
- }
277
-
278
- private doInvokeHandler(proxyId: string, methodName: string, args: any[]): any {
279
- const actor = this.locals.get(proxyId);
280
- if (!actor) {
281
- throw new Error('Unknown actor ' + proxyId);
282
- }
283
- const method = actor[methodName];
284
- if (typeof method !== 'function') {
285
- throw new Error('Unknown method ' + methodName + ' on actor ' + proxyId);
286
- }
287
- return method.apply(actor, args);
288
- }
289
141
 
290
- private cancel(req: string): string {
291
- return `{"type":${MessageType.Cancel},"id":"${req}"}`;
292
- }
293
-
294
- private request(req: string, rpcId: string, method: string, args: any[]): string {
295
- return `{"type":${MessageType.Request},"id":"${req}","proxyId":"${rpcId}","method":"${method}","args":${JSON.stringify(args, this.replacer)}}`;
296
- }
297
-
298
- private replyOK(req: string, res: any): string {
299
- if (typeof res === 'undefined') {
300
- return `{"type":${MessageType.Reply},"id":"${req}"}`;
301
- }
302
- return `{"type":${MessageType.Reply},"id":"${req}","res":${safeStringify(res, this.replacer)}}`;
303
- }
142
+ this.locals.set(identifier.id, handler);
143
+ if (Disposable.is(instance)) {
144
+ this.toDispose.push(instance);
145
+ }
146
+ this.toDispose.push(Disposable.create(() => this.locals.delete(identifier.id)));
304
147
 
305
- private replyErr(req: string, err: any): string {
306
- err = typeof err === 'string' ? new Error(err) : err;
307
- if (err instanceof Error) {
308
- return `{"type":${MessageType.ReplyErr},"id":"${req}","err":${safeStringify(transformErrorForSerialization(err))}}`;
309
148
  }
310
- return `{"type":${MessageType.ReplyErr},"id":"${req}","err":null}`;
149
+ return instance;
311
150
  }
312
151
  }
313
152
 
314
- function canceled(): Error {
315
- const error = new Error('Canceled');
316
- error.name = error.message;
317
- return error;
318
- }
319
-
320
153
  /**
321
- * Sends/Receives multiple messages in one go:
154
+ * Wraps and underlying channel to send/receive multiple messages in one go:
322
155
  * - multiple messages to be sent from one stack get sent in bulk at `process.nextTick`.
323
156
  * - each incoming message is handled in a separate `process.nextTick`.
324
157
  */
325
- class RPCMultiplexer implements Disposable, MessageConnection {
326
-
327
- private readonly connection: MessageConnection;
328
- private readonly sendAccumulatedBound: () => void;
329
-
330
- private messagesToSend: string[];
158
+ export class BatchingChannel implements Channel {
159
+ protected messagesToSend: Uint8Array[] = [];
331
160
 
332
- private readonly messageEmitter = new Emitter<string>();
333
- private readonly toDispose = new DisposableCollection();
161
+ constructor(protected underlyingChannel: Channel) {
162
+ underlyingChannel.onMessage(msg => this.handleMessages(msg()));
163
+ }
334
164
 
335
- constructor(connection: MessageConnection) {
336
- this.connection = connection;
337
- this.sendAccumulatedBound = this.sendAccumulated.bind(this);
165
+ protected onMessageEmitter: Emitter<MessageProvider> = new Emitter();
166
+ get onMessage(): Event<MessageProvider> {
167
+ return this.onMessageEmitter.event;
168
+ };
338
169
 
339
- this.toDispose.push(Disposable.create(() => this.messagesToSend = []));
340
- this.toDispose.push(this.connection.onMessage((msg: string) => {
341
- const messages = JSON.parse(msg);
342
- for (const message of messages) {
343
- this.messageEmitter.fire(message);
344
- }
345
- }));
346
- this.toDispose.push(this.messageEmitter);
170
+ readonly onClose = this.underlyingChannel.onClose;
171
+ readonly onError = this.underlyingChannel.onError;
347
172
 
173
+ close(): void {
174
+ this.underlyingChannel.close();
175
+ this.onMessageEmitter.dispose();
348
176
  this.messagesToSend = [];
349
177
  }
350
178
 
351
- dispose(): void {
352
- this.toDispose.dispose();
353
- }
354
-
355
- get onMessage(): Event<string> {
356
- return this.messageEmitter.event;
179
+ getWriteBuffer(): WriteBuffer {
180
+ const writer = new Uint8ArrayWriteBuffer();
181
+ writer.onCommit(buffer => this.commitSingleMessage(buffer));
182
+ return writer;
357
183
  }
358
184
 
359
- private sendAccumulated(): void {
360
- const tmp = this.messagesToSend;
361
- this.messagesToSend = [];
362
- this.connection.send(JSON.stringify(tmp));
363
- }
185
+ protected commitSingleMessage(msg: Uint8Array): void {
364
186
 
365
- public send(msg: string): void {
366
- if (this.toDispose.disposed) {
367
- throw ConnectionClosedError.create();
368
- }
369
187
  if (this.messagesToSend.length === 0) {
370
188
  if (typeof setImmediate !== 'undefined') {
371
- setImmediate(this.sendAccumulatedBound);
189
+ setImmediate(() => this.sendAccumulated());
372
190
  } else {
373
- setTimeout(this.sendAccumulatedBound, 0);
191
+ setTimeout(() => this.sendAccumulated(), 0);
374
192
  }
375
193
  }
376
194
  this.messagesToSend.push(msg);
377
195
  }
196
+
197
+ protected sendAccumulated(): void {
198
+ const cachedMessages = this.messagesToSend;
199
+ this.messagesToSend = [];
200
+ const writer = this.underlyingChannel.getWriteBuffer();
201
+
202
+ if (cachedMessages.length > 0) {
203
+ writer.writeLength(cachedMessages.length);
204
+ cachedMessages.forEach(msg => {
205
+ writer.writeBytes(msg);
206
+ });
207
+
208
+ }
209
+ writer.commit();
210
+ }
211
+
212
+ protected handleMessages(buffer: ReadBuffer): void {
213
+ // Read in the list of messages and dispatch each message individually
214
+ const length = buffer.readLength();
215
+ if (length > 0) {
216
+ for (let index = 0; index < length; index++) {
217
+ const message = buffer.readBytes();
218
+ this.onMessageEmitter.fire(() => new Uint8ArrayReadBuffer(message));
219
+ }
220
+ }
221
+ }
378
222
  }
379
223
 
380
- /**
381
- * These functions are responsible for correct transferring objects via rpc channel.
382
- *
383
- * To reach that some specific kind of objects is converted to json in some custom way
384
- * and then, after receiving, revived to objects again,
385
- * so there is feeling that object was transferred via rpc channel.
386
- *
387
- * To distinguish between regular and altered objects, field $type is added to altered ones.
388
- * Also value of that field specifies kind of the object.
389
- */
390
- export namespace ObjectsTransferrer {
391
-
392
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
393
- export function replacer(key: string | undefined, value: any): any {
394
- if (value instanceof URI) {
395
- return {
396
- $type: SerializedObjectType.THEIA_URI,
397
- data: value.toString()
398
- } as SerializedObject;
399
- } else if (value instanceof Range) {
400
- const range = value as Range;
401
- const serializedValue = {
224
+ export function registerMsgPackExtensions(): void {
225
+ MsgPackExtensionManager.getInstance().registerExtensions(
226
+ {
227
+ class: URI,
228
+ tag: 2,
229
+ serialize: (instance: URI) => instance.toString(),
230
+ deserialize: data => new URI(data)
231
+ },
232
+ {
233
+ class: Range,
234
+ tag: 3,
235
+ serialize: (range: Range) => ({
402
236
  start: {
403
237
  line: range.start.line,
404
238
  character: range.start.character
@@ -407,140 +241,33 @@ export namespace ObjectsTransferrer {
407
241
  line: range.end.line,
408
242
  character: range.end.character
409
243
  }
410
- };
411
- return {
412
- $type: SerializedObjectType.THEIA_RANGE,
413
- data: JSON.stringify(serializedValue)
414
- } as SerializedObject;
415
- } else if (value && value['$mid'] === 1) {
416
- // Given value is VSCode URI
417
- // We cannot use instanceof here because VSCode URI has toJSON method which is invoked before this replacer.
418
- const uri = VSCodeURI.revive(value);
419
- return {
420
- $type: SerializedObjectType.VSCODE_URI,
421
- data: uri.toString()
422
- } as SerializedObject;
423
- } else if (value instanceof BinaryBuffer) {
424
- const bytes = [...value.buffer.values()];
425
- return {
426
- $type: SerializedObjectType.TEXT_BUFFER,
427
- data: JSON.stringify({ bytes })
428
- };
429
- }
430
-
431
- return value;
432
- }
433
-
434
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
435
- export function reviver(key: string | undefined, value: any): any {
436
- if (isSerializedObject(value)) {
437
- switch (value.$type) {
438
- case SerializedObjectType.THEIA_URI:
439
- return new URI(value.data);
440
- case SerializedObjectType.VSCODE_URI:
441
- return VSCodeURI.parse(value.data);
442
- case SerializedObjectType.THEIA_RANGE:
443
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
444
- const obj: any = JSON.parse(value.data);
445
- const start = new Position(obj.start.line, obj.start.character);
446
- const end = new Position(obj.end.line, obj.end.character);
447
- return new Range(start, end);
448
- case SerializedObjectType.TEXT_BUFFER:
449
- const data: { bytes: number[] } = JSON.parse(value.data);
450
- return BinaryBuffer.wrap(Uint8Array.from(data.bytes));
244
+ }),
245
+ deserialize: data => {
246
+ const start = new Position(data.start.line, data.start.character);
247
+ const end = new Position(data.end.line, data.end.character);
248
+ return new Range(start, end);
249
+ }
250
+ },
251
+ {
252
+ class: VSCodeURI,
253
+ tag: 4,
254
+ // eslint-disable-next-line arrow-body-style
255
+ serialize: (instance: URI) => {
256
+ return instance.toString();
257
+ },
258
+ deserialize: data => VSCodeURI.parse(data)
259
+ },
260
+ {
261
+ class: BinaryBuffer,
262
+ tag: 5,
263
+ // eslint-disable-next-line arrow-body-style
264
+ serialize: (instance: BinaryBuffer) => {
265
+ return instance.buffer;
266
+ },
267
+ // eslint-disable-next-line arrow-body-style
268
+ deserialize: buffer => {
269
+ return BinaryBuffer.wrap(buffer);
451
270
  }
452
271
  }
453
-
454
- return value;
455
- }
456
-
457
- }
458
-
459
- interface SerializedObject {
460
- $type: SerializedObjectType;
461
- data: string;
462
- }
463
-
464
- enum SerializedObjectType {
465
- THEIA_URI,
466
- VSCODE_URI,
467
- THEIA_RANGE,
468
- TEXT_BUFFER
469
- }
470
-
471
- function isSerializedObject(obj: unknown): obj is SerializedObject {
472
- const serializedObject = obj as SerializedObject;
473
- return !!obj && typeof obj === 'object' && serializedObject.$type !== undefined && serializedObject.data !== undefined;
474
- }
475
-
476
- export const enum MessageType {
477
- Request = 1,
478
- Reply = 2,
479
- ReplyErr = 3,
480
- Cancel = 4,
481
- Terminate = 5,
482
- Terminated = 6
483
- }
484
-
485
- class CancelMessage {
486
- type: MessageType.Cancel;
487
- id: string;
488
- }
489
-
490
- class RequestMessage {
491
- type: MessageType.Request;
492
- id: string;
493
- proxyId: string;
494
- method: string;
495
- args: any[];
496
- }
497
-
498
- class ReplyMessage {
499
- type: MessageType.Reply;
500
- id: string;
501
- res: any;
502
- }
503
-
504
- class ReplyErrMessage {
505
- type: MessageType.ReplyErr;
506
- id: string;
507
- err: SerializedError;
508
- }
509
-
510
- type RPCMessage = RequestMessage | ReplyMessage | ReplyErrMessage | CancelMessage;
511
-
512
- export interface SerializedError {
513
- readonly $isError: true;
514
- readonly name: string;
515
- readonly message: string;
516
- readonly stack: string;
517
- }
518
-
519
- export function transformErrorForSerialization(error: Error): SerializedError {
520
- if (error instanceof Error) {
521
- const { name, message } = error;
522
- const stack: string = (<any>error).stacktrace || error.stack;
523
- return {
524
- $isError: true,
525
- name,
526
- message,
527
- stack
528
- };
529
- }
530
-
531
- // return as is
532
- return error;
533
- }
534
-
535
- interface JSONStringifyReplacer {
536
- (key: string, value: any): any;
537
- }
538
-
539
- function safeStringify(obj: any, replacer?: JSONStringifyReplacer): string {
540
- try {
541
- return JSON.stringify(obj, replacer);
542
- } catch (err) {
543
- console.error('error stringifying response: ', err);
544
- return 'null';
545
- }
272
+ );
546
273
  }
@@ -21,7 +21,8 @@ import { LogPart } from '../../common/types';
21
21
 
22
22
  @injectable()
23
23
  export class HostedPluginWatcher {
24
- private onPostMessage = new Emitter<{ pluginHostId: string, message: string }>();
24
+ private onPostMessage = new Emitter<{ pluginHostId: string, message: Uint8Array }>();
25
+
25
26
  private onLogMessage = new Emitter<LogPart>();
26
27
 
27
28
  private readonly onDidDeployEmitter = new Emitter<void>();
@@ -31,7 +32,7 @@ export class HostedPluginWatcher {
31
32
  const messageEmitter = this.onPostMessage;
32
33
  const logEmitter = this.onLogMessage;
33
34
  return {
34
- postMessage(pluginHostId, message: string): Promise<void> {
35
+ postMessage(pluginHostId, message: Uint8Array): Promise<void> {
35
36
  messageEmitter.fire({ pluginHostId, message });
36
37
  return Promise.resolve();
37
38
  },
@@ -43,7 +44,7 @@ export class HostedPluginWatcher {
43
44
  };
44
45
  }
45
46
 
46
- get onPostMessageEvent(): Event<{ pluginHostId: string, message: string }> {
47
+ get onPostMessageEvent(): Event<{ pluginHostId: string, message: Uint8Array }> {
47
48
  return this.onPostMessage.event;
48
49
  }
49
50
 
@@ -66,6 +66,8 @@ import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/stan
66
66
  import { ILanguageService } from '@theia/monaco-editor-core/esm/vs/editor/common/languages/language';
67
67
  import { LanguageService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/languageService';
68
68
  import { Measurement, Stopwatch } from '@theia/core/lib/common';
69
+ import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '@theia/core/lib/common/message-rpc/uint8-array-message-buffer';
70
+ import { BasicChannel } from '@theia/core/lib/common/message-rpc/channel';
69
71
 
70
72
  export type PluginHost = 'frontend' | string;
71
73
  export type DebugActivationEvent = 'onDebugResolve' | 'onDebugInitialConfigurations' | 'onDebugAdapterProtocolTracker' | 'onDebugDynamicConfigurations';
@@ -534,18 +536,25 @@ export class HostedPluginSupport {
534
536
  }
535
537
 
536
538
  protected createServerRpc(pluginHostId: string): RPCProtocol {
537
- const emitter = new Emitter<string>();
539
+
540
+ const channel = new BasicChannel(() => {
541
+ const writer = new Uint8ArrayWriteBuffer();
542
+ writer.onCommit(buffer => {
543
+ this.server.onMessage(pluginHostId, buffer);
544
+ });
545
+ return writer;
546
+ });
547
+
548
+ // Create RPC protocol before adding the listener to the watcher to receive the watcher's cached messages after the rpc protocol was created.
549
+ const rpc = new RPCProtocolImpl(channel);
550
+
538
551
  this.watcher.onPostMessageEvent(received => {
539
552
  if (pluginHostId === received.pluginHostId) {
540
- emitter.fire(received.message);
541
- }
542
- });
543
- return new RPCProtocolImpl({
544
- onMessage: emitter.event,
545
- send: message => {
546
- this.server.onMessage(pluginHostId, message);
553
+ channel.onMessageEmitter.fire(() => new Uint8ArrayReadBuffer(received.message));
547
554
  }
548
555
  });
556
+
557
+ return rpc;
549
558
  }
550
559
 
551
560
  protected async updateStoragePath(): Promise<void> {