astro-ghostcms/.pnpm-store/v3/files/e8/76954f42d19d4fc275efb567127...

1213 lines
50 KiB
Plaintext

"use strict";
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
Object.defineProperty(exports, "__esModule", { value: true });
exports.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;
const ral_1 = require("./ral");
const Is = require("./is");
const messages_1 = require("./messages");
const linkedMap_1 = require("./linkedMap");
const events_1 = require("./events");
const cancellation_1 = require("./cancellation");
var CancelNotification;
(function (CancelNotification) {
CancelNotification.type = new messages_1.NotificationType('$/cancelRequest');
})(CancelNotification || (CancelNotification = {}));
var ProgressToken;
(function (ProgressToken) {
function is(value) {
return typeof value === 'string' || typeof value === 'number';
}
ProgressToken.is = is;
})(ProgressToken || (exports.ProgressToken = ProgressToken = {}));
var ProgressNotification;
(function (ProgressNotification) {
ProgressNotification.type = new messages_1.NotificationType('$/progress');
})(ProgressNotification || (ProgressNotification = {}));
class ProgressType {
constructor() {
}
}
exports.ProgressType = ProgressType;
var StarRequestHandler;
(function (StarRequestHandler) {
function is(value) {
return Is.func(value);
}
StarRequestHandler.is = is;
})(StarRequestHandler || (StarRequestHandler = {}));
exports.NullLogger = Object.freeze({
error: () => { },
warn: () => { },
info: () => { },
log: () => { }
});
var Trace;
(function (Trace) {
Trace[Trace["Off"] = 0] = "Off";
Trace[Trace["Messages"] = 1] = "Messages";
Trace[Trace["Compact"] = 2] = "Compact";
Trace[Trace["Verbose"] = 3] = "Verbose";
})(Trace || (exports.Trace = Trace = {}));
var TraceValues;
(function (TraceValues) {
/**
* Turn tracing off.
*/
TraceValues.Off = 'off';
/**
* Trace messages only.
*/
TraceValues.Messages = 'messages';
/**
* Compact message tracing.
*/
TraceValues.Compact = 'compact';
/**
* Verbose message tracing.
*/
TraceValues.Verbose = 'verbose';
})(TraceValues || (exports.TraceValues = TraceValues = {}));
(function (Trace) {
function fromString(value) {
if (!Is.string(value)) {
return Trace.Off;
}
value = value.toLowerCase();
switch (value) {
case 'off':
return Trace.Off;
case 'messages':
return Trace.Messages;
case 'compact':
return Trace.Compact;
case 'verbose':
return Trace.Verbose;
default:
return Trace.Off;
}
}
Trace.fromString = fromString;
function toString(value) {
switch (value) {
case Trace.Off:
return 'off';
case Trace.Messages:
return 'messages';
case Trace.Compact:
return 'compact';
case Trace.Verbose:
return 'verbose';
default:
return 'off';
}
}
Trace.toString = toString;
})(Trace || (exports.Trace = Trace = {}));
var TraceFormat;
(function (TraceFormat) {
TraceFormat["Text"] = "text";
TraceFormat["JSON"] = "json";
})(TraceFormat || (exports.TraceFormat = TraceFormat = {}));
(function (TraceFormat) {
function fromString(value) {
if (!Is.string(value)) {
return TraceFormat.Text;
}
value = value.toLowerCase();
if (value === 'json') {
return TraceFormat.JSON;
}
else {
return TraceFormat.Text;
}
}
TraceFormat.fromString = fromString;
})(TraceFormat || (exports.TraceFormat = TraceFormat = {}));
var SetTraceNotification;
(function (SetTraceNotification) {
SetTraceNotification.type = new messages_1.NotificationType('$/setTrace');
})(SetTraceNotification || (exports.SetTraceNotification = SetTraceNotification = {}));
var LogTraceNotification;
(function (LogTraceNotification) {
LogTraceNotification.type = new messages_1.NotificationType('$/logTrace');
})(LogTraceNotification || (exports.LogTraceNotification = LogTraceNotification = {}));
var ConnectionErrors;
(function (ConnectionErrors) {
/**
* The connection is closed.
*/
ConnectionErrors[ConnectionErrors["Closed"] = 1] = "Closed";
/**
* The connection got disposed.
*/
ConnectionErrors[ConnectionErrors["Disposed"] = 2] = "Disposed";
/**
* The connection is already in listening mode.
*/
ConnectionErrors[ConnectionErrors["AlreadyListening"] = 3] = "AlreadyListening";
})(ConnectionErrors || (exports.ConnectionErrors = ConnectionErrors = {}));
class ConnectionError extends Error {
constructor(code, message) {
super(message);
this.code = code;
Object.setPrototypeOf(this, ConnectionError.prototype);
}
}
exports.ConnectionError = ConnectionError;
var ConnectionStrategy;
(function (ConnectionStrategy) {
function is(value) {
const candidate = value;
return candidate && Is.func(candidate.cancelUndispatched);
}
ConnectionStrategy.is = is;
})(ConnectionStrategy || (exports.ConnectionStrategy = ConnectionStrategy = {}));
var IdCancellationReceiverStrategy;
(function (IdCancellationReceiverStrategy) {
function is(value) {
const candidate = value;
return candidate && (candidate.kind === undefined || candidate.kind === 'id') && Is.func(candidate.createCancellationTokenSource) && (candidate.dispose === undefined || Is.func(candidate.dispose));
}
IdCancellationReceiverStrategy.is = is;
})(IdCancellationReceiverStrategy || (exports.IdCancellationReceiverStrategy = IdCancellationReceiverStrategy = {}));
var RequestCancellationReceiverStrategy;
(function (RequestCancellationReceiverStrategy) {
function is(value) {
const candidate = value;
return candidate && candidate.kind === 'request' && Is.func(candidate.createCancellationTokenSource) && (candidate.dispose === undefined || Is.func(candidate.dispose));
}
RequestCancellationReceiverStrategy.is = is;
})(RequestCancellationReceiverStrategy || (exports.RequestCancellationReceiverStrategy = RequestCancellationReceiverStrategy = {}));
var CancellationReceiverStrategy;
(function (CancellationReceiverStrategy) {
CancellationReceiverStrategy.Message = Object.freeze({
createCancellationTokenSource(_) {
return new cancellation_1.CancellationTokenSource();
}
});
function is(value) {
return IdCancellationReceiverStrategy.is(value) || RequestCancellationReceiverStrategy.is(value);
}
CancellationReceiverStrategy.is = is;
})(CancellationReceiverStrategy || (exports.CancellationReceiverStrategy = CancellationReceiverStrategy = {}));
var CancellationSenderStrategy;
(function (CancellationSenderStrategy) {
CancellationSenderStrategy.Message = Object.freeze({
sendCancellation(conn, id) {
return conn.sendNotification(CancelNotification.type, { id });
},
cleanup(_) { }
});
function is(value) {
const candidate = value;
return candidate && Is.func(candidate.sendCancellation) && Is.func(candidate.cleanup);
}
CancellationSenderStrategy.is = is;
})(CancellationSenderStrategy || (exports.CancellationSenderStrategy = CancellationSenderStrategy = {}));
var CancellationStrategy;
(function (CancellationStrategy) {
CancellationStrategy.Message = Object.freeze({
receiver: CancellationReceiverStrategy.Message,
sender: CancellationSenderStrategy.Message
});
function is(value) {
const candidate = value;
return candidate && CancellationReceiverStrategy.is(candidate.receiver) && CancellationSenderStrategy.is(candidate.sender);
}
CancellationStrategy.is = is;
})(CancellationStrategy || (exports.CancellationStrategy = CancellationStrategy = {}));
var MessageStrategy;
(function (MessageStrategy) {
function is(value) {
const candidate = value;
return candidate && Is.func(candidate.handleMessage);
}
MessageStrategy.is = is;
})(MessageStrategy || (exports.MessageStrategy = MessageStrategy = {}));
var ConnectionOptions;
(function (ConnectionOptions) {
function is(value) {
const candidate = value;
return candidate && (CancellationStrategy.is(candidate.cancellationStrategy) || ConnectionStrategy.is(candidate.connectionStrategy) || MessageStrategy.is(candidate.messageStrategy));
}
ConnectionOptions.is = is;
})(ConnectionOptions || (exports.ConnectionOptions = ConnectionOptions = {}));
var ConnectionState;
(function (ConnectionState) {
ConnectionState[ConnectionState["New"] = 1] = "New";
ConnectionState[ConnectionState["Listening"] = 2] = "Listening";
ConnectionState[ConnectionState["Closed"] = 3] = "Closed";
ConnectionState[ConnectionState["Disposed"] = 4] = "Disposed";
})(ConnectionState || (ConnectionState = {}));
function createMessageConnection(messageReader, messageWriter, _logger, options) {
const logger = _logger !== undefined ? _logger : exports.NullLogger;
let sequenceNumber = 0;
let notificationSequenceNumber = 0;
let unknownResponseSequenceNumber = 0;
const version = '2.0';
let starRequestHandler = undefined;
const requestHandlers = new Map();
let starNotificationHandler = undefined;
const notificationHandlers = new Map();
const progressHandlers = new Map();
let timer;
let messageQueue = new linkedMap_1.LinkedMap();
let responsePromises = new Map();
let knownCanceledRequests = new Set();
let requestTokens = new Map();
let trace = Trace.Off;
let traceFormat = TraceFormat.Text;
let tracer;
let state = ConnectionState.New;
const errorEmitter = new events_1.Emitter();
const closeEmitter = new events_1.Emitter();
const unhandledNotificationEmitter = new events_1.Emitter();
const unhandledProgressEmitter = new events_1.Emitter();
const disposeEmitter = new events_1.Emitter();
const cancellationStrategy = (options && options.cancellationStrategy) ? options.cancellationStrategy : CancellationStrategy.Message;
function createRequestQueueKey(id) {
if (id === null) {
throw new Error(`Can't send requests with id null since the response can't be correlated.`);
}
return 'req-' + id.toString();
}
function createResponseQueueKey(id) {
if (id === null) {
return 'res-unknown-' + (++unknownResponseSequenceNumber).toString();
}
else {
return 'res-' + id.toString();
}
}
function createNotificationQueueKey() {
return 'not-' + (++notificationSequenceNumber).toString();
}
function addMessageToQueue(queue, message) {
if (messages_1.Message.isRequest(message)) {
queue.set(createRequestQueueKey(message.id), message);
}
else if (messages_1.Message.isResponse(message)) {
queue.set(createResponseQueueKey(message.id), message);
}
else {
queue.set(createNotificationQueueKey(), message);
}
}
function cancelUndispatched(_message) {
return undefined;
}
function isListening() {
return state === ConnectionState.Listening;
}
function isClosed() {
return state === ConnectionState.Closed;
}
function isDisposed() {
return state === ConnectionState.Disposed;
}
function closeHandler() {
if (state === ConnectionState.New || state === ConnectionState.Listening) {
state = ConnectionState.Closed;
closeEmitter.fire(undefined);
}
// If the connection is disposed don't sent close events.
}
function readErrorHandler(error) {
errorEmitter.fire([error, undefined, undefined]);
}
function writeErrorHandler(data) {
errorEmitter.fire(data);
}
messageReader.onClose(closeHandler);
messageReader.onError(readErrorHandler);
messageWriter.onClose(closeHandler);
messageWriter.onError(writeErrorHandler);
function triggerMessageQueue() {
if (timer || messageQueue.size === 0) {
return;
}
timer = (0, ral_1.default)().timer.setImmediate(() => {
timer = undefined;
processMessageQueue();
});
}
function handleMessage(message) {
if (messages_1.Message.isRequest(message)) {
handleRequest(message);
}
else if (messages_1.Message.isNotification(message)) {
handleNotification(message);
}
else if (messages_1.Message.isResponse(message)) {
handleResponse(message);
}
else {
handleInvalidMessage(message);
}
}
function processMessageQueue() {
if (messageQueue.size === 0) {
return;
}
const message = messageQueue.shift();
try {
const messageStrategy = options?.messageStrategy;
if (MessageStrategy.is(messageStrategy)) {
messageStrategy.handleMessage(message, handleMessage);
}
else {
handleMessage(message);
}
}
finally {
triggerMessageQueue();
}
}
const callback = (message) => {
try {
// We have received a cancellation message. Check if the message is still in the queue
// and cancel it if allowed to do so.
if (messages_1.Message.isNotification(message) && message.method === CancelNotification.type.method) {
const cancelId = message.params.id;
const key = createRequestQueueKey(cancelId);
const toCancel = messageQueue.get(key);
if (messages_1.Message.isRequest(toCancel)) {
const strategy = options?.connectionStrategy;
const response = (strategy && strategy.cancelUndispatched) ? strategy.cancelUndispatched(toCancel, cancelUndispatched) : cancelUndispatched(toCancel);
if (response && (response.error !== undefined || response.result !== undefined)) {
messageQueue.delete(key);
requestTokens.delete(cancelId);
response.id = toCancel.id;
traceSendingResponse(response, message.method, Date.now());
messageWriter.write(response).catch(() => logger.error(`Sending response for canceled message failed.`));
return;
}
}
const cancellationToken = requestTokens.get(cancelId);
// The request is already running. Cancel the token
if (cancellationToken !== undefined) {
cancellationToken.cancel();
traceReceivedNotification(message);
return;
}
else {
// Remember the cancel but still queue the message to
// clean up state in process message.
knownCanceledRequests.add(cancelId);
}
}
addMessageToQueue(messageQueue, message);
}
finally {
triggerMessageQueue();
}
};
function handleRequest(requestMessage) {
if (isDisposed()) {
// we return here silently since we fired an event when the
// connection got disposed.
return;
}
function reply(resultOrError, method, startTime) {
const message = {
jsonrpc: version,
id: requestMessage.id
};
if (resultOrError instanceof messages_1.ResponseError) {
message.error = resultOrError.toJson();
}
else {
message.result = resultOrError === undefined ? null : resultOrError;
}
traceSendingResponse(message, method, startTime);
messageWriter.write(message).catch(() => logger.error(`Sending response failed.`));
}
function replyError(error, method, startTime) {
const message = {
jsonrpc: version,
id: requestMessage.id,
error: error.toJson()
};
traceSendingResponse(message, method, startTime);
messageWriter.write(message).catch(() => logger.error(`Sending response failed.`));
}
function replySuccess(result, method, startTime) {
// The JSON RPC defines that a response must either have a result or an error
// So we can't treat undefined as a valid response result.
if (result === undefined) {
result = null;
}
const message = {
jsonrpc: version,
id: requestMessage.id,
result: result
};
traceSendingResponse(message, method, startTime);
messageWriter.write(message).catch(() => logger.error(`Sending response failed.`));
}
traceReceivedRequest(requestMessage);
const element = requestHandlers.get(requestMessage.method);
let type;
let requestHandler;
if (element) {
type = element.type;
requestHandler = element.handler;
}
const startTime = Date.now();
if (requestHandler || starRequestHandler) {
const tokenKey = requestMessage.id ?? String(Date.now()); //
const cancellationSource = IdCancellationReceiverStrategy.is(cancellationStrategy.receiver)
? cancellationStrategy.receiver.createCancellationTokenSource(tokenKey)
: cancellationStrategy.receiver.createCancellationTokenSource(requestMessage);
if (requestMessage.id !== null && knownCanceledRequests.has(requestMessage.id)) {
cancellationSource.cancel();
}
if (requestMessage.id !== null) {
requestTokens.set(tokenKey, cancellationSource);
}
try {
let handlerResult;
if (requestHandler) {
if (requestMessage.params === undefined) {
if (type !== undefined && type.numberOfParams !== 0) {
replyError(new messages_1.ResponseError(messages_1.ErrorCodes.InvalidParams, `Request ${requestMessage.method} defines ${type.numberOfParams} params but received none.`), requestMessage.method, startTime);
return;
}
handlerResult = requestHandler(cancellationSource.token);
}
else if (Array.isArray(requestMessage.params)) {
if (type !== undefined && type.parameterStructures === messages_1.ParameterStructures.byName) {
replyError(new messages_1.ResponseError(messages_1.ErrorCodes.InvalidParams, `Request ${requestMessage.method} defines parameters by name but received parameters by position`), requestMessage.method, startTime);
return;
}
handlerResult = requestHandler(...requestMessage.params, cancellationSource.token);
}
else {
if (type !== undefined && type.parameterStructures === messages_1.ParameterStructures.byPosition) {
replyError(new messages_1.ResponseError(messages_1.ErrorCodes.InvalidParams, `Request ${requestMessage.method} defines parameters by position but received parameters by name`), requestMessage.method, startTime);
return;
}
handlerResult = requestHandler(requestMessage.params, cancellationSource.token);
}
}
else if (starRequestHandler) {
handlerResult = starRequestHandler(requestMessage.method, requestMessage.params, cancellationSource.token);
}
const promise = handlerResult;
if (!handlerResult) {
requestTokens.delete(tokenKey);
replySuccess(handlerResult, requestMessage.method, startTime);
}
else if (promise.then) {
promise.then((resultOrError) => {
requestTokens.delete(tokenKey);
reply(resultOrError, requestMessage.method, startTime);
}, error => {
requestTokens.delete(tokenKey);
if (error instanceof messages_1.ResponseError) {
replyError(error, requestMessage.method, startTime);
}
else if (error && Is.string(error.message)) {
replyError(new messages_1.ResponseError(messages_1.ErrorCodes.InternalError, `Request ${requestMessage.method} failed with message: ${error.message}`), requestMessage.method, startTime);
}
else {
replyError(new messages_1.ResponseError(messages_1.ErrorCodes.InternalError, `Request ${requestMessage.method} failed unexpectedly without providing any details.`), requestMessage.method, startTime);
}
});
}
else {
requestTokens.delete(tokenKey);
reply(handlerResult, requestMessage.method, startTime);
}
}
catch (error) {
requestTokens.delete(tokenKey);
if (error instanceof messages_1.ResponseError) {
reply(error, requestMessage.method, startTime);
}
else if (error && Is.string(error.message)) {
replyError(new messages_1.ResponseError(messages_1.ErrorCodes.InternalError, `Request ${requestMessage.method} failed with message: ${error.message}`), requestMessage.method, startTime);
}
else {
replyError(new messages_1.ResponseError(messages_1.ErrorCodes.InternalError, `Request ${requestMessage.method} failed unexpectedly without providing any details.`), requestMessage.method, startTime);
}
}
}
else {
replyError(new messages_1.ResponseError(messages_1.ErrorCodes.MethodNotFound, `Unhandled method ${requestMessage.method}`), requestMessage.method, startTime);
}
}
function handleResponse(responseMessage) {
if (isDisposed()) {
// See handle request.
return;
}
if (responseMessage.id === null) {
if (responseMessage.error) {
logger.error(`Received response message without id: Error is: \n${JSON.stringify(responseMessage.error, undefined, 4)}`);
}
else {
logger.error(`Received response message without id. No further error information provided.`);
}
}
else {
const key = responseMessage.id;
const responsePromise = responsePromises.get(key);
traceReceivedResponse(responseMessage, responsePromise);
if (responsePromise !== undefined) {
responsePromises.delete(key);
try {
if (responseMessage.error) {
const error = responseMessage.error;
responsePromise.reject(new messages_1.ResponseError(error.code, error.message, error.data));
}
else if (responseMessage.result !== undefined) {
responsePromise.resolve(responseMessage.result);
}
else {
throw new Error('Should never happen.');
}
}
catch (error) {
if (error.message) {
logger.error(`Response handler '${responsePromise.method}' failed with message: ${error.message}`);
}
else {
logger.error(`Response handler '${responsePromise.method}' failed unexpectedly.`);
}
}
}
}
}
function handleNotification(message) {
if (isDisposed()) {
// See handle request.
return;
}
let type = undefined;
let notificationHandler;
if (message.method === CancelNotification.type.method) {
const cancelId = message.params.id;
knownCanceledRequests.delete(cancelId);
traceReceivedNotification(message);
return;
}
else {
const element = notificationHandlers.get(message.method);
if (element) {
notificationHandler = element.handler;
type = element.type;
}
}
if (notificationHandler || starNotificationHandler) {
try {
traceReceivedNotification(message);
if (notificationHandler) {
if (message.params === undefined) {
if (type !== undefined) {
if (type.numberOfParams !== 0 && type.parameterStructures !== messages_1.ParameterStructures.byName) {
logger.error(`Notification ${message.method} defines ${type.numberOfParams} params but received none.`);
}
}
notificationHandler();
}
else if (Array.isArray(message.params)) {
// There are JSON-RPC libraries that send progress message as positional params although
// specified as named. So convert them if this is the case.
const params = message.params;
if (message.method === ProgressNotification.type.method && params.length === 2 && ProgressToken.is(params[0])) {
notificationHandler({ token: params[0], value: params[1] });
}
else {
if (type !== undefined) {
if (type.parameterStructures === messages_1.ParameterStructures.byName) {
logger.error(`Notification ${message.method} defines parameters by name but received parameters by position`);
}
if (type.numberOfParams !== message.params.length) {
logger.error(`Notification ${message.method} defines ${type.numberOfParams} params but received ${params.length} arguments`);
}
}
notificationHandler(...params);
}
}
else {
if (type !== undefined && type.parameterStructures === messages_1.ParameterStructures.byPosition) {
logger.error(`Notification ${message.method} defines parameters by position but received parameters by name`);
}
notificationHandler(message.params);
}
}
else if (starNotificationHandler) {
starNotificationHandler(message.method, message.params);
}
}
catch (error) {
if (error.message) {
logger.error(`Notification handler '${message.method}' failed with message: ${error.message}`);
}
else {
logger.error(`Notification handler '${message.method}' failed unexpectedly.`);
}
}
}
else {
unhandledNotificationEmitter.fire(message);
}
}
function handleInvalidMessage(message) {
if (!message) {
logger.error('Received empty message.');
return;
}
logger.error(`Received message which is neither a response nor a notification message:\n${JSON.stringify(message, null, 4)}`);
// Test whether we find an id to reject the promise
const responseMessage = message;
if (Is.string(responseMessage.id) || Is.number(responseMessage.id)) {
const key = responseMessage.id;
const responseHandler = responsePromises.get(key);
if (responseHandler) {
responseHandler.reject(new Error('The received response has neither a result nor an error property.'));
}
}
}
function stringifyTrace(params) {
if (params === undefined || params === null) {
return undefined;
}
switch (trace) {
case Trace.Verbose:
return JSON.stringify(params, null, 4);
case Trace.Compact:
return JSON.stringify(params);
default:
return undefined;
}
}
function traceSendingRequest(message) {
if (trace === Trace.Off || !tracer) {
return;
}
if (traceFormat === TraceFormat.Text) {
let data = undefined;
if ((trace === Trace.Verbose || trace === Trace.Compact) && message.params) {
data = `Params: ${stringifyTrace(message.params)}\n\n`;
}
tracer.log(`Sending request '${message.method} - (${message.id})'.`, data);
}
else {
logLSPMessage('send-request', message);
}
}
function traceSendingNotification(message) {
if (trace === Trace.Off || !tracer) {
return;
}
if (traceFormat === TraceFormat.Text) {
let data = undefined;
if (trace === Trace.Verbose || trace === Trace.Compact) {
if (message.params) {
data = `Params: ${stringifyTrace(message.params)}\n\n`;
}
else {
data = 'No parameters provided.\n\n';
}
}
tracer.log(`Sending notification '${message.method}'.`, data);
}
else {
logLSPMessage('send-notification', message);
}
}
function traceSendingResponse(message, method, startTime) {
if (trace === Trace.Off || !tracer) {
return;
}
if (traceFormat === TraceFormat.Text) {
let data = undefined;
if (trace === Trace.Verbose || trace === Trace.Compact) {
if (message.error && message.error.data) {
data = `Error data: ${stringifyTrace(message.error.data)}\n\n`;
}
else {
if (message.result) {
data = `Result: ${stringifyTrace(message.result)}\n\n`;
}
else if (message.error === undefined) {
data = 'No result returned.\n\n';
}
}
}
tracer.log(`Sending response '${method} - (${message.id})'. Processing request took ${Date.now() - startTime}ms`, data);
}
else {
logLSPMessage('send-response', message);
}
}
function traceReceivedRequest(message) {
if (trace === Trace.Off || !tracer) {
return;
}
if (traceFormat === TraceFormat.Text) {
let data = undefined;
if ((trace === Trace.Verbose || trace === Trace.Compact) && message.params) {
data = `Params: ${stringifyTrace(message.params)}\n\n`;
}
tracer.log(`Received request '${message.method} - (${message.id})'.`, data);
}
else {
logLSPMessage('receive-request', message);
}
}
function traceReceivedNotification(message) {
if (trace === Trace.Off || !tracer || message.method === LogTraceNotification.type.method) {
return;
}
if (traceFormat === TraceFormat.Text) {
let data = undefined;
if (trace === Trace.Verbose || trace === Trace.Compact) {
if (message.params) {
data = `Params: ${stringifyTrace(message.params)}\n\n`;
}
else {
data = 'No parameters provided.\n\n';
}
}
tracer.log(`Received notification '${message.method}'.`, data);
}
else {
logLSPMessage('receive-notification', message);
}
}
function traceReceivedResponse(message, responsePromise) {
if (trace === Trace.Off || !tracer) {
return;
}
if (traceFormat === TraceFormat.Text) {
let data = undefined;
if (trace === Trace.Verbose || trace === Trace.Compact) {
if (message.error && message.error.data) {
data = `Error data: ${stringifyTrace(message.error.data)}\n\n`;
}
else {
if (message.result) {
data = `Result: ${stringifyTrace(message.result)}\n\n`;
}
else if (message.error === undefined) {
data = 'No result returned.\n\n';
}
}
}
if (responsePromise) {
const error = message.error ? ` Request failed: ${message.error.message} (${message.error.code}).` : '';
tracer.log(`Received response '${responsePromise.method} - (${message.id})' in ${Date.now() - responsePromise.timerStart}ms.${error}`, data);
}
else {
tracer.log(`Received response ${message.id} without active response promise.`, data);
}
}
else {
logLSPMessage('receive-response', message);
}
}
function logLSPMessage(type, message) {
if (!tracer || trace === Trace.Off) {
return;
}
const lspMessage = {
isLSPMessage: true,
type,
message,
timestamp: Date.now()
};
tracer.log(lspMessage);
}
function throwIfClosedOrDisposed() {
if (isClosed()) {
throw new ConnectionError(ConnectionErrors.Closed, 'Connection is closed.');
}
if (isDisposed()) {
throw new ConnectionError(ConnectionErrors.Disposed, 'Connection is disposed.');
}
}
function throwIfListening() {
if (isListening()) {
throw new ConnectionError(ConnectionErrors.AlreadyListening, 'Connection is already listening');
}
}
function throwIfNotListening() {
if (!isListening()) {
throw new Error('Call listen() first.');
}
}
function undefinedToNull(param) {
if (param === undefined) {
return null;
}
else {
return param;
}
}
function nullToUndefined(param) {
if (param === null) {
return undefined;
}
else {
return param;
}
}
function isNamedParam(param) {
return param !== undefined && param !== null && !Array.isArray(param) && typeof param === 'object';
}
function computeSingleParam(parameterStructures, param) {
switch (parameterStructures) {
case messages_1.ParameterStructures.auto:
if (isNamedParam(param)) {
return nullToUndefined(param);
}
else {
return [undefinedToNull(param)];
}
case messages_1.ParameterStructures.byName:
if (!isNamedParam(param)) {
throw new Error(`Received parameters by name but param is not an object literal.`);
}
return nullToUndefined(param);
case messages_1.ParameterStructures.byPosition:
return [undefinedToNull(param)];
default:
throw new Error(`Unknown parameter structure ${parameterStructures.toString()}`);
}
}
function computeMessageParams(type, params) {
let result;
const numberOfParams = type.numberOfParams;
switch (numberOfParams) {
case 0:
result = undefined;
break;
case 1:
result = computeSingleParam(type.parameterStructures, params[0]);
break;
default:
result = [];
for (let i = 0; i < params.length && i < numberOfParams; i++) {
result.push(undefinedToNull(params[i]));
}
if (params.length < numberOfParams) {
for (let i = params.length; i < numberOfParams; i++) {
result.push(null);
}
}
break;
}
return result;
}
const connection = {
sendNotification: (type, ...args) => {
throwIfClosedOrDisposed();
let method;
let messageParams;
if (Is.string(type)) {
method = type;
const first = args[0];
let paramStart = 0;
let parameterStructures = messages_1.ParameterStructures.auto;
if (messages_1.ParameterStructures.is(first)) {
paramStart = 1;
parameterStructures = first;
}
let paramEnd = args.length;
const numberOfParams = paramEnd - paramStart;
switch (numberOfParams) {
case 0:
messageParams = undefined;
break;
case 1:
messageParams = computeSingleParam(parameterStructures, args[paramStart]);
break;
default:
if (parameterStructures === messages_1.ParameterStructures.byName) {
throw new Error(`Received ${numberOfParams} parameters for 'by Name' notification parameter structure.`);
}
messageParams = args.slice(paramStart, paramEnd).map(value => undefinedToNull(value));
break;
}
}
else {
const params = args;
method = type.method;
messageParams = computeMessageParams(type, params);
}
const notificationMessage = {
jsonrpc: version,
method: method,
params: messageParams
};
traceSendingNotification(notificationMessage);
return messageWriter.write(notificationMessage).catch((error) => {
logger.error(`Sending notification failed.`);
throw error;
});
},
onNotification: (type, handler) => {
throwIfClosedOrDisposed();
let method;
if (Is.func(type)) {
starNotificationHandler = type;
}
else if (handler) {
if (Is.string(type)) {
method = type;
notificationHandlers.set(type, { type: undefined, handler });
}
else {
method = type.method;
notificationHandlers.set(type.method, { type, handler });
}
}
return {
dispose: () => {
if (method !== undefined) {
notificationHandlers.delete(method);
}
else {
starNotificationHandler = undefined;
}
}
};
},
onProgress: (_type, token, handler) => {
if (progressHandlers.has(token)) {
throw new Error(`Progress handler for token ${token} already registered`);
}
progressHandlers.set(token, handler);
return {
dispose: () => {
progressHandlers.delete(token);
}
};
},
sendProgress: (_type, token, value) => {
// This should not await but simple return to ensure that we don't have another
// async scheduling. Otherwise one send could overtake another send.
return connection.sendNotification(ProgressNotification.type, { token, value });
},
onUnhandledProgress: unhandledProgressEmitter.event,
sendRequest: (type, ...args) => {
throwIfClosedOrDisposed();
throwIfNotListening();
let method;
let messageParams;
let token = undefined;
if (Is.string(type)) {
method = type;
const first = args[0];
const last = args[args.length - 1];
let paramStart = 0;
let parameterStructures = messages_1.ParameterStructures.auto;
if (messages_1.ParameterStructures.is(first)) {
paramStart = 1;
parameterStructures = first;
}
let paramEnd = args.length;
if (cancellation_1.CancellationToken.is(last)) {
paramEnd = paramEnd - 1;
token = last;
}
const numberOfParams = paramEnd - paramStart;
switch (numberOfParams) {
case 0:
messageParams = undefined;
break;
case 1:
messageParams = computeSingleParam(parameterStructures, args[paramStart]);
break;
default:
if (parameterStructures === messages_1.ParameterStructures.byName) {
throw new Error(`Received ${numberOfParams} parameters for 'by Name' request parameter structure.`);
}
messageParams = args.slice(paramStart, paramEnd).map(value => undefinedToNull(value));
break;
}
}
else {
const params = args;
method = type.method;
messageParams = computeMessageParams(type, params);
const numberOfParams = type.numberOfParams;
token = cancellation_1.CancellationToken.is(params[numberOfParams]) ? params[numberOfParams] : undefined;
}
const id = sequenceNumber++;
let disposable;
if (token) {
disposable = token.onCancellationRequested(() => {
const p = cancellationStrategy.sender.sendCancellation(connection, id);
if (p === undefined) {
logger.log(`Received no promise from cancellation strategy when cancelling id ${id}`);
return Promise.resolve();
}
else {
return p.catch(() => {
logger.log(`Sending cancellation messages for id ${id} failed`);
});
}
});
}
const requestMessage = {
jsonrpc: version,
id: id,
method: method,
params: messageParams
};
traceSendingRequest(requestMessage);
if (typeof cancellationStrategy.sender.enableCancellation === 'function') {
cancellationStrategy.sender.enableCancellation(requestMessage);
}
return new Promise(async (resolve, reject) => {
const resolveWithCleanup = (r) => {
resolve(r);
cancellationStrategy.sender.cleanup(id);
disposable?.dispose();
};
const rejectWithCleanup = (r) => {
reject(r);
cancellationStrategy.sender.cleanup(id);
disposable?.dispose();
};
const responsePromise = { method: method, timerStart: Date.now(), resolve: resolveWithCleanup, reject: rejectWithCleanup };
try {
await messageWriter.write(requestMessage);
responsePromises.set(id, responsePromise);
}
catch (error) {
logger.error(`Sending request failed.`);
// Writing the message failed. So we need to reject the promise.
responsePromise.reject(new messages_1.ResponseError(messages_1.ErrorCodes.MessageWriteError, error.message ? error.message : 'Unknown reason'));
throw error;
}
});
},
onRequest: (type, handler) => {
throwIfClosedOrDisposed();
let method = null;
if (StarRequestHandler.is(type)) {
method = undefined;
starRequestHandler = type;
}
else if (Is.string(type)) {
method = null;
if (handler !== undefined) {
method = type;
requestHandlers.set(type, { handler: handler, type: undefined });
}
}
else {
if (handler !== undefined) {
method = type.method;
requestHandlers.set(type.method, { type, handler });
}
}
return {
dispose: () => {
if (method === null) {
return;
}
if (method !== undefined) {
requestHandlers.delete(method);
}
else {
starRequestHandler = undefined;
}
}
};
},
hasPendingResponse: () => {
return responsePromises.size > 0;
},
trace: async (_value, _tracer, sendNotificationOrTraceOptions) => {
let _sendNotification = false;
let _traceFormat = TraceFormat.Text;
if (sendNotificationOrTraceOptions !== undefined) {
if (Is.boolean(sendNotificationOrTraceOptions)) {
_sendNotification = sendNotificationOrTraceOptions;
}
else {
_sendNotification = sendNotificationOrTraceOptions.sendNotification || false;
_traceFormat = sendNotificationOrTraceOptions.traceFormat || TraceFormat.Text;
}
}
trace = _value;
traceFormat = _traceFormat;
if (trace === Trace.Off) {
tracer = undefined;
}
else {
tracer = _tracer;
}
if (_sendNotification && !isClosed() && !isDisposed()) {
await connection.sendNotification(SetTraceNotification.type, { value: Trace.toString(_value) });
}
},
onError: errorEmitter.event,
onClose: closeEmitter.event,
onUnhandledNotification: unhandledNotificationEmitter.event,
onDispose: disposeEmitter.event,
end: () => {
messageWriter.end();
},
dispose: () => {
if (isDisposed()) {
return;
}
state = ConnectionState.Disposed;
disposeEmitter.fire(undefined);
const error = new messages_1.ResponseError(messages_1.ErrorCodes.PendingResponseRejected, 'Pending response rejected since connection got disposed');
for (const promise of responsePromises.values()) {
promise.reject(error);
}
responsePromises = new Map();
requestTokens = new Map();
knownCanceledRequests = new Set();
messageQueue = new linkedMap_1.LinkedMap();
// Test for backwards compatibility
if (Is.func(messageWriter.dispose)) {
messageWriter.dispose();
}
if (Is.func(messageReader.dispose)) {
messageReader.dispose();
}
},
listen: () => {
throwIfClosedOrDisposed();
throwIfListening();
state = ConnectionState.Listening;
messageReader.listen(callback);
},
inspect: () => {
// eslint-disable-next-line no-console
(0, ral_1.default)().console.log('inspect');
}
};
connection.onNotification(LogTraceNotification.type, (params) => {
if (trace === Trace.Off || !tracer) {
return;
}
const verbose = trace === Trace.Verbose || trace === Trace.Compact;
tracer.log(params.message, verbose ? params.verbose : undefined);
});
connection.onNotification(ProgressNotification.type, (params) => {
const handler = progressHandlers.get(params.token);
if (handler) {
handler(params.value);
}
else {
unhandledProgressEmitter.fire(params);
}
});
return connection;
}
exports.createMessageConnection = createMessageConnection;