UNPKG

51.2 kBJavaScriptView Raw
1"use strict";
2/* --------------------------------------------------------------------------------------------
3 * Copyright (c) Microsoft Corporation. All rights reserved.
4 * Licensed under the MIT License. See License.txt in the project root for license information.
5 * ------------------------------------------------------------------------------------------ */
6Object.defineProperty(exports, "__esModule", { value: true });
7exports.createMessageConnection = exports.ConnectionOptions = exports.MessageStrategy = exports.CancellationStrategy = exports.CancellationSenderStrategy = exports.CancellationReceiverStrategy = exports.RequestCancellationReceiverStrategy = exports.IdCancellationReceiverStrategy = exports.ConnectionStrategy = exports.ConnectionError = exports.ConnectionErrors = exports.LogTraceNotification = exports.SetTraceNotification = exports.TraceFormat = exports.TraceValues = exports.Trace = exports.NullLogger = exports.ProgressType = exports.ProgressToken = void 0;
8const ral_1 = require("./ral");
9const Is = require("./is");
10const messages_1 = require("./messages");
11const linkedMap_1 = require("./linkedMap");
12const events_1 = require("./events");
13const cancellation_1 = require("./cancellation");
14var CancelNotification;
15(function (CancelNotification) {
16 CancelNotification.type = new messages_1.NotificationType('$/cancelRequest');
17})(CancelNotification || (CancelNotification = {}));
18var ProgressToken;
19(function (ProgressToken) {
20 function is(value) {
21 return typeof value === 'string' || typeof value === 'number';
22 }
23 ProgressToken.is = is;
24})(ProgressToken || (exports.ProgressToken = ProgressToken = {}));
25var ProgressNotification;
26(function (ProgressNotification) {
27 ProgressNotification.type = new messages_1.NotificationType('$/progress');
28})(ProgressNotification || (ProgressNotification = {}));
29class ProgressType {
30 constructor() {
31 }
32}
33exports.ProgressType = ProgressType;
34var StarRequestHandler;
35(function (StarRequestHandler) {
36 function is(value) {
37 return Is.func(value);
38 }
39 StarRequestHandler.is = is;
40})(StarRequestHandler || (StarRequestHandler = {}));
41exports.NullLogger = Object.freeze({
42 error: () => { },
43 warn: () => { },
44 info: () => { },
45 log: () => { }
46});
47var Trace;
48(function (Trace) {
49 Trace[Trace["Off"] = 0] = "Off";
50 Trace[Trace["Messages"] = 1] = "Messages";
51 Trace[Trace["Compact"] = 2] = "Compact";
52 Trace[Trace["Verbose"] = 3] = "Verbose";
53})(Trace || (exports.Trace = Trace = {}));
54var TraceValues;
55(function (TraceValues) {
56 /**
57 * Turn tracing off.
58 */
59 TraceValues.Off = 'off';
60 /**
61 * Trace messages only.
62 */
63 TraceValues.Messages = 'messages';
64 /**
65 * Compact message tracing.
66 */
67 TraceValues.Compact = 'compact';
68 /**
69 * Verbose message tracing.
70 */
71 TraceValues.Verbose = 'verbose';
72})(TraceValues || (exports.TraceValues = TraceValues = {}));
73(function (Trace) {
74 function fromString(value) {
75 if (!Is.string(value)) {
76 return Trace.Off;
77 }
78 value = value.toLowerCase();
79 switch (value) {
80 case 'off':
81 return Trace.Off;
82 case 'messages':
83 return Trace.Messages;
84 case 'compact':
85 return Trace.Compact;
86 case 'verbose':
87 return Trace.Verbose;
88 default:
89 return Trace.Off;
90 }
91 }
92 Trace.fromString = fromString;
93 function toString(value) {
94 switch (value) {
95 case Trace.Off:
96 return 'off';
97 case Trace.Messages:
98 return 'messages';
99 case Trace.Compact:
100 return 'compact';
101 case Trace.Verbose:
102 return 'verbose';
103 default:
104 return 'off';
105 }
106 }
107 Trace.toString = toString;
108})(Trace || (exports.Trace = Trace = {}));
109var TraceFormat;
110(function (TraceFormat) {
111 TraceFormat["Text"] = "text";
112 TraceFormat["JSON"] = "json";
113})(TraceFormat || (exports.TraceFormat = TraceFormat = {}));
114(function (TraceFormat) {
115 function fromString(value) {
116 if (!Is.string(value)) {
117 return TraceFormat.Text;
118 }
119 value = value.toLowerCase();
120 if (value === 'json') {
121 return TraceFormat.JSON;
122 }
123 else {
124 return TraceFormat.Text;
125 }
126 }
127 TraceFormat.fromString = fromString;
128})(TraceFormat || (exports.TraceFormat = TraceFormat = {}));
129var SetTraceNotification;
130(function (SetTraceNotification) {
131 SetTraceNotification.type = new messages_1.NotificationType('$/setTrace');
132})(SetTraceNotification || (exports.SetTraceNotification = SetTraceNotification = {}));
133var LogTraceNotification;
134(function (LogTraceNotification) {
135 LogTraceNotification.type = new messages_1.NotificationType('$/logTrace');
136})(LogTraceNotification || (exports.LogTraceNotification = LogTraceNotification = {}));
137var ConnectionErrors;
138(function (ConnectionErrors) {
139 /**
140 * The connection is closed.
141 */
142 ConnectionErrors[ConnectionErrors["Closed"] = 1] = "Closed";
143 /**
144 * The connection got disposed.
145 */
146 ConnectionErrors[ConnectionErrors["Disposed"] = 2] = "Disposed";
147 /**
148 * The connection is already in listening mode.
149 */
150 ConnectionErrors[ConnectionErrors["AlreadyListening"] = 3] = "AlreadyListening";
151})(ConnectionErrors || (exports.ConnectionErrors = ConnectionErrors = {}));
152class ConnectionError extends Error {
153 constructor(code, message) {
154 super(message);
155 this.code = code;
156 Object.setPrototypeOf(this, ConnectionError.prototype);
157 }
158}
159exports.ConnectionError = ConnectionError;
160var ConnectionStrategy;
161(function (ConnectionStrategy) {
162 function is(value) {
163 const candidate = value;
164 return candidate && Is.func(candidate.cancelUndispatched);
165 }
166 ConnectionStrategy.is = is;
167})(ConnectionStrategy || (exports.ConnectionStrategy = ConnectionStrategy = {}));
168var IdCancellationReceiverStrategy;
169(function (IdCancellationReceiverStrategy) {
170 function is(value) {
171 const candidate = value;
172 return candidate && (candidate.kind === undefined || candidate.kind === 'id') && Is.func(candidate.createCancellationTokenSource) && (candidate.dispose === undefined || Is.func(candidate.dispose));
173 }
174 IdCancellationReceiverStrategy.is = is;
175})(IdCancellationReceiverStrategy || (exports.IdCancellationReceiverStrategy = IdCancellationReceiverStrategy = {}));
176var RequestCancellationReceiverStrategy;
177(function (RequestCancellationReceiverStrategy) {
178 function is(value) {
179 const candidate = value;
180 return candidate && candidate.kind === 'request' && Is.func(candidate.createCancellationTokenSource) && (candidate.dispose === undefined || Is.func(candidate.dispose));
181 }
182 RequestCancellationReceiverStrategy.is = is;
183})(RequestCancellationReceiverStrategy || (exports.RequestCancellationReceiverStrategy = RequestCancellationReceiverStrategy = {}));
184var CancellationReceiverStrategy;
185(function (CancellationReceiverStrategy) {
186 CancellationReceiverStrategy.Message = Object.freeze({
187 createCancellationTokenSource(_) {
188 return new cancellation_1.CancellationTokenSource();
189 }
190 });
191 function is(value) {
192 return IdCancellationReceiverStrategy.is(value) || RequestCancellationReceiverStrategy.is(value);
193 }
194 CancellationReceiverStrategy.is = is;
195})(CancellationReceiverStrategy || (exports.CancellationReceiverStrategy = CancellationReceiverStrategy = {}));
196var CancellationSenderStrategy;
197(function (CancellationSenderStrategy) {
198 CancellationSenderStrategy.Message = Object.freeze({
199 sendCancellation(conn, id) {
200 return conn.sendNotification(CancelNotification.type, { id });
201 },
202 cleanup(_) { }
203 });
204 function is(value) {
205 const candidate = value;
206 return candidate && Is.func(candidate.sendCancellation) && Is.func(candidate.cleanup);
207 }
208 CancellationSenderStrategy.is = is;
209})(CancellationSenderStrategy || (exports.CancellationSenderStrategy = CancellationSenderStrategy = {}));
210var CancellationStrategy;
211(function (CancellationStrategy) {
212 CancellationStrategy.Message = Object.freeze({
213 receiver: CancellationReceiverStrategy.Message,
214 sender: CancellationSenderStrategy.Message
215 });
216 function is(value) {
217 const candidate = value;
218 return candidate && CancellationReceiverStrategy.is(candidate.receiver) && CancellationSenderStrategy.is(candidate.sender);
219 }
220 CancellationStrategy.is = is;
221})(CancellationStrategy || (exports.CancellationStrategy = CancellationStrategy = {}));
222var MessageStrategy;
223(function (MessageStrategy) {
224 function is(value) {
225 const candidate = value;
226 return candidate && Is.func(candidate.handleMessage);
227 }
228 MessageStrategy.is = is;
229})(MessageStrategy || (exports.MessageStrategy = MessageStrategy = {}));
230var ConnectionOptions;
231(function (ConnectionOptions) {
232 function is(value) {
233 const candidate = value;
234 return candidate && (CancellationStrategy.is(candidate.cancellationStrategy) || ConnectionStrategy.is(candidate.connectionStrategy) || MessageStrategy.is(candidate.messageStrategy));
235 }
236 ConnectionOptions.is = is;
237})(ConnectionOptions || (exports.ConnectionOptions = ConnectionOptions = {}));
238var ConnectionState;
239(function (ConnectionState) {
240 ConnectionState[ConnectionState["New"] = 1] = "New";
241 ConnectionState[ConnectionState["Listening"] = 2] = "Listening";
242 ConnectionState[ConnectionState["Closed"] = 3] = "Closed";
243 ConnectionState[ConnectionState["Disposed"] = 4] = "Disposed";
244})(ConnectionState || (ConnectionState = {}));
245function createMessageConnection(messageReader, messageWriter, _logger, options) {
246 const logger = _logger !== undefined ? _logger : exports.NullLogger;
247 let sequenceNumber = 0;
248 let notificationSequenceNumber = 0;
249 let unknownResponseSequenceNumber = 0;
250 const version = '2.0';
251 let starRequestHandler = undefined;
252 const requestHandlers = new Map();
253 let starNotificationHandler = undefined;
254 const notificationHandlers = new Map();
255 const progressHandlers = new Map();
256 let timer;
257 let messageQueue = new linkedMap_1.LinkedMap();
258 let responsePromises = new Map();
259 let knownCanceledRequests = new Set();
260 let requestTokens = new Map();
261 let trace = Trace.Off;
262 let traceFormat = TraceFormat.Text;
263 let tracer;
264 let state = ConnectionState.New;
265 const errorEmitter = new events_1.Emitter();
266 const closeEmitter = new events_1.Emitter();
267 const unhandledNotificationEmitter = new events_1.Emitter();
268 const unhandledProgressEmitter = new events_1.Emitter();
269 const disposeEmitter = new events_1.Emitter();
270 const cancellationStrategy = (options && options.cancellationStrategy) ? options.cancellationStrategy : CancellationStrategy.Message;
271 function createRequestQueueKey(id) {
272 if (id === null) {
273 throw new Error(`Can't send requests with id null since the response can't be correlated.`);
274 }
275 return 'req-' + id.toString();
276 }
277 function createResponseQueueKey(id) {
278 if (id === null) {
279 return 'res-unknown-' + (++unknownResponseSequenceNumber).toString();
280 }
281 else {
282 return 'res-' + id.toString();
283 }
284 }
285 function createNotificationQueueKey() {
286 return 'not-' + (++notificationSequenceNumber).toString();
287 }
288 function addMessageToQueue(queue, message) {
289 if (messages_1.Message.isRequest(message)) {
290 queue.set(createRequestQueueKey(message.id), message);
291 }
292 else if (messages_1.Message.isResponse(message)) {
293 queue.set(createResponseQueueKey(message.id), message);
294 }
295 else {
296 queue.set(createNotificationQueueKey(), message);
297 }
298 }
299 function cancelUndispatched(_message) {
300 return undefined;
301 }
302 function isListening() {
303 return state === ConnectionState.Listening;
304 }
305 function isClosed() {
306 return state === ConnectionState.Closed;
307 }
308 function isDisposed() {
309 return state === ConnectionState.Disposed;
310 }
311 function closeHandler() {
312 if (state === ConnectionState.New || state === ConnectionState.Listening) {
313 state = ConnectionState.Closed;
314 closeEmitter.fire(undefined);
315 }
316 // If the connection is disposed don't sent close events.
317 }
318 function readErrorHandler(error) {
319 errorEmitter.fire([error, undefined, undefined]);
320 }
321 function writeErrorHandler(data) {
322 errorEmitter.fire(data);
323 }
324 messageReader.onClose(closeHandler);
325 messageReader.onError(readErrorHandler);
326 messageWriter.onClose(closeHandler);
327 messageWriter.onError(writeErrorHandler);
328 function triggerMessageQueue() {
329 if (timer || messageQueue.size === 0) {
330 return;
331 }
332 timer = (0, ral_1.default)().timer.setImmediate(() => {
333 timer = undefined;
334 processMessageQueue();
335 });
336 }
337 function handleMessage(message) {
338 if (messages_1.Message.isRequest(message)) {
339 handleRequest(message);
340 }
341 else if (messages_1.Message.isNotification(message)) {
342 handleNotification(message);
343 }
344 else if (messages_1.Message.isResponse(message)) {
345 handleResponse(message);
346 }
347 else {
348 handleInvalidMessage(message);
349 }
350 }
351 function processMessageQueue() {
352 if (messageQueue.size === 0) {
353 return;
354 }
355 const message = messageQueue.shift();
356 try {
357 const messageStrategy = options?.messageStrategy;
358 if (MessageStrategy.is(messageStrategy)) {
359 messageStrategy.handleMessage(message, handleMessage);
360 }
361 else {
362 handleMessage(message);
363 }
364 }
365 finally {
366 triggerMessageQueue();
367 }
368 }
369 const callback = (message) => {
370 try {
371 // We have received a cancellation message. Check if the message is still in the queue
372 // and cancel it if allowed to do so.
373 if (messages_1.Message.isNotification(message) && message.method === CancelNotification.type.method) {
374 const cancelId = message.params.id;
375 const key = createRequestQueueKey(cancelId);
376 const toCancel = messageQueue.get(key);
377 if (messages_1.Message.isRequest(toCancel)) {
378 const strategy = options?.connectionStrategy;
379 const response = (strategy && strategy.cancelUndispatched) ? strategy.cancelUndispatched(toCancel, cancelUndispatched) : cancelUndispatched(toCancel);
380 if (response && (response.error !== undefined || response.result !== undefined)) {
381 messageQueue.delete(key);
382 requestTokens.delete(cancelId);
383 response.id = toCancel.id;
384 traceSendingResponse(response, message.method, Date.now());
385 messageWriter.write(response).catch(() => logger.error(`Sending response for canceled message failed.`));
386 return;
387 }
388 }
389 const cancellationToken = requestTokens.get(cancelId);
390 // The request is already running. Cancel the token
391 if (cancellationToken !== undefined) {
392 cancellationToken.cancel();
393 traceReceivedNotification(message);
394 return;
395 }
396 else {
397 // Remember the cancel but still queue the message to
398 // clean up state in process message.
399 knownCanceledRequests.add(cancelId);
400 }
401 }
402 addMessageToQueue(messageQueue, message);
403 }
404 finally {
405 triggerMessageQueue();
406 }
407 };
408 function handleRequest(requestMessage) {
409 if (isDisposed()) {
410 // we return here silently since we fired an event when the
411 // connection got disposed.
412 return;
413 }
414 function reply(resultOrError, method, startTime) {
415 const message = {
416 jsonrpc: version,
417 id: requestMessage.id
418 };
419 if (resultOrError instanceof messages_1.ResponseError) {
420 message.error = resultOrError.toJson();
421 }
422 else {
423 message.result = resultOrError === undefined ? null : resultOrError;
424 }
425 traceSendingResponse(message, method, startTime);
426 messageWriter.write(message).catch(() => logger.error(`Sending response failed.`));
427 }
428 function replyError(error, method, startTime) {
429 const message = {
430 jsonrpc: version,
431 id: requestMessage.id,
432 error: error.toJson()
433 };
434 traceSendingResponse(message, method, startTime);
435 messageWriter.write(message).catch(() => logger.error(`Sending response failed.`));
436 }
437 function replySuccess(result, method, startTime) {
438 // The JSON RPC defines that a response must either have a result or an error
439 // So we can't treat undefined as a valid response result.
440 if (result === undefined) {
441 result = null;
442 }
443 const message = {
444 jsonrpc: version,
445 id: requestMessage.id,
446 result: result
447 };
448 traceSendingResponse(message, method, startTime);
449 messageWriter.write(message).catch(() => logger.error(`Sending response failed.`));
450 }
451 traceReceivedRequest(requestMessage);
452 const element = requestHandlers.get(requestMessage.method);
453 let type;
454 let requestHandler;
455 if (element) {
456 type = element.type;
457 requestHandler = element.handler;
458 }
459 const startTime = Date.now();
460 if (requestHandler || starRequestHandler) {
461 const tokenKey = requestMessage.id ?? String(Date.now()); //
462 const cancellationSource = IdCancellationReceiverStrategy.is(cancellationStrategy.receiver)
463 ? cancellationStrategy.receiver.createCancellationTokenSource(tokenKey)
464 : cancellationStrategy.receiver.createCancellationTokenSource(requestMessage);
465 if (requestMessage.id !== null && knownCanceledRequests.has(requestMessage.id)) {
466 cancellationSource.cancel();
467 }
468 if (requestMessage.id !== null) {
469 requestTokens.set(tokenKey, cancellationSource);
470 }
471 try {
472 let handlerResult;
473 if (requestHandler) {
474 if (requestMessage.params === undefined) {
475 if (type !== undefined && type.numberOfParams !== 0) {
476 replyError(new messages_1.ResponseError(messages_1.ErrorCodes.InvalidParams, `Request ${requestMessage.method} defines ${type.numberOfParams} params but received none.`), requestMessage.method, startTime);
477 return;
478 }
479 handlerResult = requestHandler(cancellationSource.token);
480 }
481 else if (Array.isArray(requestMessage.params)) {
482 if (type !== undefined && type.parameterStructures === messages_1.ParameterStructures.byName) {
483 replyError(new messages_1.ResponseError(messages_1.ErrorCodes.InvalidParams, `Request ${requestMessage.method} defines parameters by name but received parameters by position`), requestMessage.method, startTime);
484 return;
485 }
486 handlerResult = requestHandler(...requestMessage.params, cancellationSource.token);
487 }
488 else {
489 if (type !== undefined && type.parameterStructures === messages_1.ParameterStructures.byPosition) {
490 replyError(new messages_1.ResponseError(messages_1.ErrorCodes.InvalidParams, `Request ${requestMessage.method} defines parameters by position but received parameters by name`), requestMessage.method, startTime);
491 return;
492 }
493 handlerResult = requestHandler(requestMessage.params, cancellationSource.token);
494 }
495 }
496 else if (starRequestHandler) {
497 handlerResult = starRequestHandler(requestMessage.method, requestMessage.params, cancellationSource.token);
498 }
499 const promise = handlerResult;
500 if (!handlerResult) {
501 requestTokens.delete(tokenKey);
502 replySuccess(handlerResult, requestMessage.method, startTime);
503 }
504 else if (promise.then) {
505 promise.then((resultOrError) => {
506 requestTokens.delete(tokenKey);
507 reply(resultOrError, requestMessage.method, startTime);
508 }, error => {
509 requestTokens.delete(tokenKey);
510 if (error instanceof messages_1.ResponseError) {
511 replyError(error, requestMessage.method, startTime);
512 }
513 else if (error && Is.string(error.message)) {
514 replyError(new messages_1.ResponseError(messages_1.ErrorCodes.InternalError, `Request ${requestMessage.method} failed with message: ${error.message}`), requestMessage.method, startTime);
515 }
516 else {
517 replyError(new messages_1.ResponseError(messages_1.ErrorCodes.InternalError, `Request ${requestMessage.method} failed unexpectedly without providing any details.`), requestMessage.method, startTime);
518 }
519 });
520 }
521 else {
522 requestTokens.delete(tokenKey);
523 reply(handlerResult, requestMessage.method, startTime);
524 }
525 }
526 catch (error) {
527 requestTokens.delete(tokenKey);
528 if (error instanceof messages_1.ResponseError) {
529 reply(error, requestMessage.method, startTime);
530 }
531 else if (error && Is.string(error.message)) {
532 replyError(new messages_1.ResponseError(messages_1.ErrorCodes.InternalError, `Request ${requestMessage.method} failed with message: ${error.message}`), requestMessage.method, startTime);
533 }
534 else {
535 replyError(new messages_1.ResponseError(messages_1.ErrorCodes.InternalError, `Request ${requestMessage.method} failed unexpectedly without providing any details.`), requestMessage.method, startTime);
536 }
537 }
538 }
539 else {
540 replyError(new messages_1.ResponseError(messages_1.ErrorCodes.MethodNotFound, `Unhandled method ${requestMessage.method}`), requestMessage.method, startTime);
541 }
542 }
543 function handleResponse(responseMessage) {
544 if (isDisposed()) {
545 // See handle request.
546 return;
547 }
548 if (responseMessage.id === null) {
549 if (responseMessage.error) {
550 logger.error(`Received response message without id: Error is: \n${JSON.stringify(responseMessage.error, undefined, 4)}`);
551 }
552 else {
553 logger.error(`Received response message without id. No further error information provided.`);
554 }
555 }
556 else {
557 const key = responseMessage.id;
558 const responsePromise = responsePromises.get(key);
559 traceReceivedResponse(responseMessage, responsePromise);
560 if (responsePromise !== undefined) {
561 responsePromises.delete(key);
562 try {
563 if (responseMessage.error) {
564 const error = responseMessage.error;
565 responsePromise.reject(new messages_1.ResponseError(error.code, error.message, error.data));
566 }
567 else if (responseMessage.result !== undefined) {
568 responsePromise.resolve(responseMessage.result);
569 }
570 else {
571 throw new Error('Should never happen.');
572 }
573 }
574 catch (error) {
575 if (error.message) {
576 logger.error(`Response handler '${responsePromise.method}' failed with message: ${error.message}`);
577 }
578 else {
579 logger.error(`Response handler '${responsePromise.method}' failed unexpectedly.`);
580 }
581 }
582 }
583 }
584 }
585 function handleNotification(message) {
586 if (isDisposed()) {
587 // See handle request.
588 return;
589 }
590 let type = undefined;
591 let notificationHandler;
592 if (message.method === CancelNotification.type.method) {
593 const cancelId = message.params.id;
594 knownCanceledRequests.delete(cancelId);
595 traceReceivedNotification(message);
596 return;
597 }
598 else {
599 const element = notificationHandlers.get(message.method);
600 if (element) {
601 notificationHandler = element.handler;
602 type = element.type;
603 }
604 }
605 if (notificationHandler || starNotificationHandler) {
606 try {
607 traceReceivedNotification(message);
608 if (notificationHandler) {
609 if (message.params === undefined) {
610 if (type !== undefined) {
611 if (type.numberOfParams !== 0 && type.parameterStructures !== messages_1.ParameterStructures.byName) {
612 logger.error(`Notification ${message.method} defines ${type.numberOfParams} params but received none.`);
613 }
614 }
615 notificationHandler();
616 }
617 else if (Array.isArray(message.params)) {
618 // There are JSON-RPC libraries that send progress message as positional params although
619 // specified as named. So convert them if this is the case.
620 const params = message.params;
621 if (message.method === ProgressNotification.type.method && params.length === 2 && ProgressToken.is(params[0])) {
622 notificationHandler({ token: params[0], value: params[1] });
623 }
624 else {
625 if (type !== undefined) {
626 if (type.parameterStructures === messages_1.ParameterStructures.byName) {
627 logger.error(`Notification ${message.method} defines parameters by name but received parameters by position`);
628 }
629 if (type.numberOfParams !== message.params.length) {
630 logger.error(`Notification ${message.method} defines ${type.numberOfParams} params but received ${params.length} arguments`);
631 }
632 }
633 notificationHandler(...params);
634 }
635 }
636 else {
637 if (type !== undefined && type.parameterStructures === messages_1.ParameterStructures.byPosition) {
638 logger.error(`Notification ${message.method} defines parameters by position but received parameters by name`);
639 }
640 notificationHandler(message.params);
641 }
642 }
643 else if (starNotificationHandler) {
644 starNotificationHandler(message.method, message.params);
645 }
646 }
647 catch (error) {
648 if (error.message) {
649 logger.error(`Notification handler '${message.method}' failed with message: ${error.message}`);
650 }
651 else {
652 logger.error(`Notification handler '${message.method}' failed unexpectedly.`);
653 }
654 }
655 }
656 else {
657 unhandledNotificationEmitter.fire(message);
658 }
659 }
660 function handleInvalidMessage(message) {
661 if (!message) {
662 logger.error('Received empty message.');
663 return;
664 }
665 logger.error(`Received message which is neither a response nor a notification message:\n${JSON.stringify(message, null, 4)}`);
666 // Test whether we find an id to reject the promise
667 const responseMessage = message;
668 if (Is.string(responseMessage.id) || Is.number(responseMessage.id)) {
669 const key = responseMessage.id;
670 const responseHandler = responsePromises.get(key);
671 if (responseHandler) {
672 responseHandler.reject(new Error('The received response has neither a result nor an error property.'));
673 }
674 }
675 }
676 function stringifyTrace(params) {
677 if (params === undefined || params === null) {
678 return undefined;
679 }
680 switch (trace) {
681 case Trace.Verbose:
682 return JSON.stringify(params, null, 4);
683 case Trace.Compact:
684 return JSON.stringify(params);
685 default:
686 return undefined;
687 }
688 }
689 function traceSendingRequest(message) {
690 if (trace === Trace.Off || !tracer) {
691 return;
692 }
693 if (traceFormat === TraceFormat.Text) {
694 let data = undefined;
695 if ((trace === Trace.Verbose || trace === Trace.Compact) && message.params) {
696 data = `Params: ${stringifyTrace(message.params)}\n\n`;
697 }
698 tracer.log(`Sending request '${message.method} - (${message.id})'.`, data);
699 }
700 else {
701 logLSPMessage('send-request', message);
702 }
703 }
704 function traceSendingNotification(message) {
705 if (trace === Trace.Off || !tracer) {
706 return;
707 }
708 if (traceFormat === TraceFormat.Text) {
709 let data = undefined;
710 if (trace === Trace.Verbose || trace === Trace.Compact) {
711 if (message.params) {
712 data = `Params: ${stringifyTrace(message.params)}\n\n`;
713 }
714 else {
715 data = 'No parameters provided.\n\n';
716 }
717 }
718 tracer.log(`Sending notification '${message.method}'.`, data);
719 }
720 else {
721 logLSPMessage('send-notification', message);
722 }
723 }
724 function traceSendingResponse(message, method, startTime) {
725 if (trace === Trace.Off || !tracer) {
726 return;
727 }
728 if (traceFormat === TraceFormat.Text) {
729 let data = undefined;
730 if (trace === Trace.Verbose || trace === Trace.Compact) {
731 if (message.error && message.error.data) {
732 data = `Error data: ${stringifyTrace(message.error.data)}\n\n`;
733 }
734 else {
735 if (message.result) {
736 data = `Result: ${stringifyTrace(message.result)}\n\n`;
737 }
738 else if (message.error === undefined) {
739 data = 'No result returned.\n\n';
740 }
741 }
742 }
743 tracer.log(`Sending response '${method} - (${message.id})'. Processing request took ${Date.now() - startTime}ms`, data);
744 }
745 else {
746 logLSPMessage('send-response', message);
747 }
748 }
749 function traceReceivedRequest(message) {
750 if (trace === Trace.Off || !tracer) {
751 return;
752 }
753 if (traceFormat === TraceFormat.Text) {
754 let data = undefined;
755 if ((trace === Trace.Verbose || trace === Trace.Compact) && message.params) {
756 data = `Params: ${stringifyTrace(message.params)}\n\n`;
757 }
758 tracer.log(`Received request '${message.method} - (${message.id})'.`, data);
759 }
760 else {
761 logLSPMessage('receive-request', message);
762 }
763 }
764 function traceReceivedNotification(message) {
765 if (trace === Trace.Off || !tracer || message.method === LogTraceNotification.type.method) {
766 return;
767 }
768 if (traceFormat === TraceFormat.Text) {
769 let data = undefined;
770 if (trace === Trace.Verbose || trace === Trace.Compact) {
771 if (message.params) {
772 data = `Params: ${stringifyTrace(message.params)}\n\n`;
773 }
774 else {
775 data = 'No parameters provided.\n\n';
776 }
777 }
778 tracer.log(`Received notification '${message.method}'.`, data);
779 }
780 else {
781 logLSPMessage('receive-notification', message);
782 }
783 }
784 function traceReceivedResponse(message, responsePromise) {
785 if (trace === Trace.Off || !tracer) {
786 return;
787 }
788 if (traceFormat === TraceFormat.Text) {
789 let data = undefined;
790 if (trace === Trace.Verbose || trace === Trace.Compact) {
791 if (message.error && message.error.data) {
792 data = `Error data: ${stringifyTrace(message.error.data)}\n\n`;
793 }
794 else {
795 if (message.result) {
796 data = `Result: ${stringifyTrace(message.result)}\n\n`;
797 }
798 else if (message.error === undefined) {
799 data = 'No result returned.\n\n';
800 }
801 }
802 }
803 if (responsePromise) {
804 const error = message.error ? ` Request failed: ${message.error.message} (${message.error.code}).` : '';
805 tracer.log(`Received response '${responsePromise.method} - (${message.id})' in ${Date.now() - responsePromise.timerStart}ms.${error}`, data);
806 }
807 else {
808 tracer.log(`Received response ${message.id} without active response promise.`, data);
809 }
810 }
811 else {
812 logLSPMessage('receive-response', message);
813 }
814 }
815 function logLSPMessage(type, message) {
816 if (!tracer || trace === Trace.Off) {
817 return;
818 }
819 const lspMessage = {
820 isLSPMessage: true,
821 type,
822 message,
823 timestamp: Date.now()
824 };
825 tracer.log(lspMessage);
826 }
827 function throwIfClosedOrDisposed() {
828 if (isClosed()) {
829 throw new ConnectionError(ConnectionErrors.Closed, 'Connection is closed.');
830 }
831 if (isDisposed()) {
832 throw new ConnectionError(ConnectionErrors.Disposed, 'Connection is disposed.');
833 }
834 }
835 function throwIfListening() {
836 if (isListening()) {
837 throw new ConnectionError(ConnectionErrors.AlreadyListening, 'Connection is already listening');
838 }
839 }
840 function throwIfNotListening() {
841 if (!isListening()) {
842 throw new Error('Call listen() first.');
843 }
844 }
845 function undefinedToNull(param) {
846 if (param === undefined) {
847 return null;
848 }
849 else {
850 return param;
851 }
852 }
853 function nullToUndefined(param) {
854 if (param === null) {
855 return undefined;
856 }
857 else {
858 return param;
859 }
860 }
861 function isNamedParam(param) {
862 return param !== undefined && param !== null && !Array.isArray(param) && typeof param === 'object';
863 }
864 function computeSingleParam(parameterStructures, param) {
865 switch (parameterStructures) {
866 case messages_1.ParameterStructures.auto:
867 if (isNamedParam(param)) {
868 return nullToUndefined(param);
869 }
870 else {
871 return [undefinedToNull(param)];
872 }
873 case messages_1.ParameterStructures.byName:
874 if (!isNamedParam(param)) {
875 throw new Error(`Received parameters by name but param is not an object literal.`);
876 }
877 return nullToUndefined(param);
878 case messages_1.ParameterStructures.byPosition:
879 return [undefinedToNull(param)];
880 default:
881 throw new Error(`Unknown parameter structure ${parameterStructures.toString()}`);
882 }
883 }
884 function computeMessageParams(type, params) {
885 let result;
886 const numberOfParams = type.numberOfParams;
887 switch (numberOfParams) {
888 case 0:
889 result = undefined;
890 break;
891 case 1:
892 result = computeSingleParam(type.parameterStructures, params[0]);
893 break;
894 default:
895 result = [];
896 for (let i = 0; i < params.length && i < numberOfParams; i++) {
897 result.push(undefinedToNull(params[i]));
898 }
899 if (params.length < numberOfParams) {
900 for (let i = params.length; i < numberOfParams; i++) {
901 result.push(null);
902 }
903 }
904 break;
905 }
906 return result;
907 }
908 const connection = {
909 sendNotification: (type, ...args) => {
910 throwIfClosedOrDisposed();
911 let method;
912 let messageParams;
913 if (Is.string(type)) {
914 method = type;
915 const first = args[0];
916 let paramStart = 0;
917 let parameterStructures = messages_1.ParameterStructures.auto;
918 if (messages_1.ParameterStructures.is(first)) {
919 paramStart = 1;
920 parameterStructures = first;
921 }
922 let paramEnd = args.length;
923 const numberOfParams = paramEnd - paramStart;
924 switch (numberOfParams) {
925 case 0:
926 messageParams = undefined;
927 break;
928 case 1:
929 messageParams = computeSingleParam(parameterStructures, args[paramStart]);
930 break;
931 default:
932 if (parameterStructures === messages_1.ParameterStructures.byName) {
933 throw new Error(`Received ${numberOfParams} parameters for 'by Name' notification parameter structure.`);
934 }
935 messageParams = args.slice(paramStart, paramEnd).map(value => undefinedToNull(value));
936 break;
937 }
938 }
939 else {
940 const params = args;
941 method = type.method;
942 messageParams = computeMessageParams(type, params);
943 }
944 const notificationMessage = {
945 jsonrpc: version,
946 method: method,
947 params: messageParams
948 };
949 traceSendingNotification(notificationMessage);
950 return messageWriter.write(notificationMessage).catch((error) => {
951 logger.error(`Sending notification failed.`);
952 throw error;
953 });
954 },
955 onNotification: (type, handler) => {
956 throwIfClosedOrDisposed();
957 let method;
958 if (Is.func(type)) {
959 starNotificationHandler = type;
960 }
961 else if (handler) {
962 if (Is.string(type)) {
963 method = type;
964 notificationHandlers.set(type, { type: undefined, handler });
965 }
966 else {
967 method = type.method;
968 notificationHandlers.set(type.method, { type, handler });
969 }
970 }
971 return {
972 dispose: () => {
973 if (method !== undefined) {
974 notificationHandlers.delete(method);
975 }
976 else {
977 starNotificationHandler = undefined;
978 }
979 }
980 };
981 },
982 onProgress: (_type, token, handler) => {
983 if (progressHandlers.has(token)) {
984 throw new Error(`Progress handler for token ${token} already registered`);
985 }
986 progressHandlers.set(token, handler);
987 return {
988 dispose: () => {
989 progressHandlers.delete(token);
990 }
991 };
992 },
993 sendProgress: (_type, token, value) => {
994 // This should not await but simple return to ensure that we don't have another
995 // async scheduling. Otherwise one send could overtake another send.
996 return connection.sendNotification(ProgressNotification.type, { token, value });
997 },
998 onUnhandledProgress: unhandledProgressEmitter.event,
999 sendRequest: (type, ...args) => {
1000 throwIfClosedOrDisposed();
1001 throwIfNotListening();
1002 let method;
1003 let messageParams;
1004 let token = undefined;
1005 if (Is.string(type)) {
1006 method = type;
1007 const first = args[0];
1008 const last = args[args.length - 1];
1009 let paramStart = 0;
1010 let parameterStructures = messages_1.ParameterStructures.auto;
1011 if (messages_1.ParameterStructures.is(first)) {
1012 paramStart = 1;
1013 parameterStructures = first;
1014 }
1015 let paramEnd = args.length;
1016 if (cancellation_1.CancellationToken.is(last)) {
1017 paramEnd = paramEnd - 1;
1018 token = last;
1019 }
1020 const numberOfParams = paramEnd - paramStart;
1021 switch (numberOfParams) {
1022 case 0:
1023 messageParams = undefined;
1024 break;
1025 case 1:
1026 messageParams = computeSingleParam(parameterStructures, args[paramStart]);
1027 break;
1028 default:
1029 if (parameterStructures === messages_1.ParameterStructures.byName) {
1030 throw new Error(`Received ${numberOfParams} parameters for 'by Name' request parameter structure.`);
1031 }
1032 messageParams = args.slice(paramStart, paramEnd).map(value => undefinedToNull(value));
1033 break;
1034 }
1035 }
1036 else {
1037 const params = args;
1038 method = type.method;
1039 messageParams = computeMessageParams(type, params);
1040 const numberOfParams = type.numberOfParams;
1041 token = cancellation_1.CancellationToken.is(params[numberOfParams]) ? params[numberOfParams] : undefined;
1042 }
1043 const id = sequenceNumber++;
1044 let disposable;
1045 if (token) {
1046 disposable = token.onCancellationRequested(() => {
1047 const p = cancellationStrategy.sender.sendCancellation(connection, id);
1048 if (p === undefined) {
1049 logger.log(`Received no promise from cancellation strategy when cancelling id ${id}`);
1050 return Promise.resolve();
1051 }
1052 else {
1053 return p.catch(() => {
1054 logger.log(`Sending cancellation messages for id ${id} failed`);
1055 });
1056 }
1057 });
1058 }
1059 const requestMessage = {
1060 jsonrpc: version,
1061 id: id,
1062 method: method,
1063 params: messageParams
1064 };
1065 traceSendingRequest(requestMessage);
1066 if (typeof cancellationStrategy.sender.enableCancellation === 'function') {
1067 cancellationStrategy.sender.enableCancellation(requestMessage);
1068 }
1069 return new Promise(async (resolve, reject) => {
1070 const resolveWithCleanup = (r) => {
1071 resolve(r);
1072 cancellationStrategy.sender.cleanup(id);
1073 disposable?.dispose();
1074 };
1075 const rejectWithCleanup = (r) => {
1076 reject(r);
1077 cancellationStrategy.sender.cleanup(id);
1078 disposable?.dispose();
1079 };
1080 const responsePromise = { method: method, timerStart: Date.now(), resolve: resolveWithCleanup, reject: rejectWithCleanup };
1081 try {
1082 responsePromises.set(id, responsePromise);
1083 await messageWriter.write(requestMessage);
1084 }
1085 catch (error) {
1086 // Writing the message failed. So we need to delete it from the response promises and
1087 // reject it.
1088 responsePromises.delete(id);
1089 responsePromise.reject(new messages_1.ResponseError(messages_1.ErrorCodes.MessageWriteError, error.message ? error.message : 'Unknown reason'));
1090 logger.error(`Sending request failed.`);
1091 throw error;
1092 }
1093 });
1094 },
1095 onRequest: (type, handler) => {
1096 throwIfClosedOrDisposed();
1097 let method = null;
1098 if (StarRequestHandler.is(type)) {
1099 method = undefined;
1100 starRequestHandler = type;
1101 }
1102 else if (Is.string(type)) {
1103 method = null;
1104 if (handler !== undefined) {
1105 method = type;
1106 requestHandlers.set(type, { handler: handler, type: undefined });
1107 }
1108 }
1109 else {
1110 if (handler !== undefined) {
1111 method = type.method;
1112 requestHandlers.set(type.method, { type, handler });
1113 }
1114 }
1115 return {
1116 dispose: () => {
1117 if (method === null) {
1118 return;
1119 }
1120 if (method !== undefined) {
1121 requestHandlers.delete(method);
1122 }
1123 else {
1124 starRequestHandler = undefined;
1125 }
1126 }
1127 };
1128 },
1129 hasPendingResponse: () => {
1130 return responsePromises.size > 0;
1131 },
1132 trace: async (_value, _tracer, sendNotificationOrTraceOptions) => {
1133 let _sendNotification = false;
1134 let _traceFormat = TraceFormat.Text;
1135 if (sendNotificationOrTraceOptions !== undefined) {
1136 if (Is.boolean(sendNotificationOrTraceOptions)) {
1137 _sendNotification = sendNotificationOrTraceOptions;
1138 }
1139 else {
1140 _sendNotification = sendNotificationOrTraceOptions.sendNotification || false;
1141 _traceFormat = sendNotificationOrTraceOptions.traceFormat || TraceFormat.Text;
1142 }
1143 }
1144 trace = _value;
1145 traceFormat = _traceFormat;
1146 if (trace === Trace.Off) {
1147 tracer = undefined;
1148 }
1149 else {
1150 tracer = _tracer;
1151 }
1152 if (_sendNotification && !isClosed() && !isDisposed()) {
1153 await connection.sendNotification(SetTraceNotification.type, { value: Trace.toString(_value) });
1154 }
1155 },
1156 onError: errorEmitter.event,
1157 onClose: closeEmitter.event,
1158 onUnhandledNotification: unhandledNotificationEmitter.event,
1159 onDispose: disposeEmitter.event,
1160 end: () => {
1161 messageWriter.end();
1162 },
1163 dispose: () => {
1164 if (isDisposed()) {
1165 return;
1166 }
1167 state = ConnectionState.Disposed;
1168 disposeEmitter.fire(undefined);
1169 const error = new messages_1.ResponseError(messages_1.ErrorCodes.PendingResponseRejected, 'Pending response rejected since connection got disposed');
1170 for (const promise of responsePromises.values()) {
1171 promise.reject(error);
1172 }
1173 responsePromises = new Map();
1174 requestTokens = new Map();
1175 knownCanceledRequests = new Set();
1176 messageQueue = new linkedMap_1.LinkedMap();
1177 // Test for backwards compatibility
1178 if (Is.func(messageWriter.dispose)) {
1179 messageWriter.dispose();
1180 }
1181 if (Is.func(messageReader.dispose)) {
1182 messageReader.dispose();
1183 }
1184 },
1185 listen: () => {
1186 throwIfClosedOrDisposed();
1187 throwIfListening();
1188 state = ConnectionState.Listening;
1189 messageReader.listen(callback);
1190 },
1191 inspect: () => {
1192 // eslint-disable-next-line no-console
1193 (0, ral_1.default)().console.log('inspect');
1194 }
1195 };
1196 connection.onNotification(LogTraceNotification.type, (params) => {
1197 if (trace === Trace.Off || !tracer) {
1198 return;
1199 }
1200 const verbose = trace === Trace.Verbose || trace === Trace.Compact;
1201 tracer.log(params.message, verbose ? params.verbose : undefined);
1202 });
1203 connection.onNotification(ProgressNotification.type, (params) => {
1204 const handler = progressHandlers.get(params.token);
1205 if (handler) {
1206 handler(params.value);
1207 }
1208 else {
1209 unhandledProgressEmitter.fire(params);
1210 }
1211 });
1212 return connection;
1213}
1214exports.createMessageConnection = createMessageConnection;