src/languageTools/LanguageClient/ProtocolAdapter.js
/*
* Copyright (c) 2019 - present Adobe. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
/*global LanguageClientInfo*/
/*eslint-env es6, node*/
/*eslint max-len: ["error", { "code": 200 }]*/
/*eslint no-fallthrough: 0*/
"use strict";
var protocol = require("vscode-languageserver-protocol"),
Utils = require("./Utils"),
ToolingInfo = LanguageClientInfo.toolingInfo,
MESSAGE_FORMAT = {
BRACKETS: "brackets",
LSP: "lsp"
};
function _constructParamsAndRelay(relay, type, params) {
var _params = null,
handler = null;
//Check for param object format. We won't change anything if the object is preformatted.
if (params.format === MESSAGE_FORMAT.LSP) {
params.format = undefined;
_params = JSON.parse(JSON.stringify(params));
}
switch (type) {
case ToolingInfo.LANGUAGE_SERVICE.CUSTOM_REQUEST:
return sendCustomRequest(relay, params.type, params.params);
case ToolingInfo.LANGUAGE_SERVICE.CUSTOM_NOTIFICATION:
{
sendCustomNotification(relay, params.type, params.params);
break;
}
case ToolingInfo.SERVICE_REQUESTS.SHOW_SELECT_MESSAGE:
case ToolingInfo.SERVICE_REQUESTS.REGISTRATION_REQUEST:
case ToolingInfo.SERVICE_REQUESTS.UNREGISTRATION_REQUEST:
case ToolingInfo.SERVICE_REQUESTS.PROJECT_FOLDERS_REQUEST:
{
_params = {
type: type,
params: params
};
return relay(_params);
}
case ToolingInfo.SERVICE_NOTIFICATIONS.SHOW_MESSAGE:
case ToolingInfo.SERVICE_NOTIFICATIONS.LOG_MESSAGE:
case ToolingInfo.SERVICE_NOTIFICATIONS.TELEMETRY:
case ToolingInfo.SERVICE_NOTIFICATIONS.DIAGNOSTICS:
{
_params = {
type: type,
params: params
};
relay(_params);
break;
}
case ToolingInfo.SYNCHRONIZE_EVENTS.DOCUMENT_OPENED:
{
_params = _params || {
textDocument: {
uri: Utils.pathToUri(params.filePath),
languageId: params.languageId,
version: 1,
text: params.fileContent
}
};
didOpenTextDocument(relay, _params);
break;
}
case ToolingInfo.SYNCHRONIZE_EVENTS.DOCUMENT_CHANGED:
{
_params = _params || {
textDocument: {
uri: Utils.pathToUri(params.filePath),
version: 1
},
contentChanges: [{
text: params.fileContent
}]
};
didChangeTextDocument(relay, _params);
break;
}
case ToolingInfo.SYNCHRONIZE_EVENTS.DOCUMENT_SAVED:
{
if (!_params) {
_params = {
textDocument: {
uri: Utils.pathToUri(params.filePath)
}
};
if (params.fileContent) {
_params['text'] = params.fileContent;
}
}
didSaveTextDocument(relay, _params);
break;
}
case ToolingInfo.SYNCHRONIZE_EVENTS.DOCUMENT_CLOSED:
{
_params = _params || {
textDocument: {
uri: Utils.pathToUri(params.filePath)
}
};
didCloseTextDocument(relay, _params);
break;
}
case ToolingInfo.SYNCHRONIZE_EVENTS.PROJECT_FOLDERS_CHANGED:
{
var foldersAdded = params.foldersAdded || [],
foldersRemoved = params.foldersRemoved || [];
foldersAdded = Utils.convertToWorkspaceFolders(foldersAdded);
foldersRemoved = Utils.convertToWorkspaceFolders(foldersRemoved);
_params = _params || {
event: {
added: foldersAdded,
removed: foldersRemoved
}
};
didChangeWorkspaceFolders(relay, _params);
break;
}
case ToolingInfo.FEATURES.CODE_HINTS:
handler = completion;
case ToolingInfo.FEATURES.PARAMETER_HINTS:
handler = handler || signatureHelp;
case ToolingInfo.FEATURES.JUMP_TO_DECLARATION:
handler = handler || gotoDeclaration;
case ToolingInfo.FEATURES.JUMP_TO_DEFINITION:
handler = handler || gotoDefinition;
case ToolingInfo.FEATURES.JUMP_TO_IMPL:
{
handler = handler || gotoImplementation;
_params = _params || {
textDocument: {
uri: Utils.pathToUri(params.filePath)
},
position: Utils.convertToLSPPosition(params.cursorPos)
};
return handler(relay, _params);
}
case ToolingInfo.FEATURES.CODE_HINT_INFO:
{
return completionItemResolve(relay, params);
}
case ToolingInfo.FEATURES.FIND_REFERENCES:
{
_params = _params || {
textDocument: {
uri: Utils.pathToUri(params.filePath)
},
position: Utils.convertToLSPPosition(params.cursorPos),
context: {
includeDeclaration: params.includeDeclaration
}
};
return findReferences(relay, _params);
}
case ToolingInfo.FEATURES.DOCUMENT_SYMBOLS:
{
_params = _params || {
textDocument: {
uri: Utils.pathToUri(params.filePath)
}
};
return documentSymbol(relay, _params);
}
case ToolingInfo.FEATURES.PROJECT_SYMBOLS:
{
_params = _params || {
query: params.query
};
return workspaceSymbol(relay, _params);
}
}
}
/** For custom messages */
function onCustom(connection, type, handler) {
connection.onNotification(type, handler);
}
function sendCustomRequest(connection, type, params) {
return connection.sendRequest(type, params);
}
function sendCustomNotification(connection, type, params) {
connection.sendNotification(type, params);
}
/** For Notification messages */
function didOpenTextDocument(connection, params) {
connection.sendNotification(protocol.DidOpenTextDocumentNotification.type, params);
}
function didChangeTextDocument(connection, params) {
connection.sendNotification(protocol.DidChangeTextDocumentNotification.type, params);
}
function didCloseTextDocument(connection, params) {
connection.sendNotification(protocol.DidCloseTextDocumentNotification.type, params);
}
function didSaveTextDocument(connection, params) {
connection.sendNotification(protocol.DidSaveTextDocumentNotification.type, params);
}
function didChangeWorkspaceFolders(connection, params) {
connection.sendNotification(protocol.DidChangeWorkspaceFoldersNotification.type, params);
}
/** For Request messages */
function completion(connection, params) {
return connection.sendRequest(protocol.CompletionRequest.type, params);
}
function completionItemResolve(connection, params) {
return connection.sendRequest(protocol.CompletionResolveRequest.type, params);
}
function signatureHelp(connection, params) {
return connection.sendRequest(protocol.SignatureHelpRequest.type, params);
}
function gotoDefinition(connection, params) {
return connection.sendRequest(protocol.DefinitionRequest.type, params);
}
function gotoDeclaration(connection, params) {
return connection.sendRequest(protocol.DeclarationRequest.type, params);
}
function gotoImplementation(connection, params) {
return connection.sendRequest(protocol.ImplementationRequest.type, params);
}
function findReferences(connection, params) {
return connection.sendRequest(protocol.ReferencesRequest.type, params);
}
function documentSymbol(connection, params) {
return connection.sendRequest(protocol.DocumentSymbolRequest.type, params);
}
function workspaceSymbol(connection, params) {
return connection.sendRequest(protocol.WorkspaceSymbolRequest.type, params);
}
/**
* Server commands
*/
function initialize(connection, params) {
var rootPath = params.rootPath,
workspaceFolders = params.rootPaths;
if(!rootPath && workspaceFolders && Array.isArray(workspaceFolders)) {
rootPath = workspaceFolders[0];
}
if (!workspaceFolders) {
workspaceFolders = [rootPath];
}
if (workspaceFolders.length) {
workspaceFolders = Utils.convertToWorkspaceFolders(workspaceFolders);
}
var _params = {
rootPath: rootPath,
rootUri: Utils.pathToUri(rootPath),
processId: process.pid,
capabilities: params.capabilities,
workspaceFolders: workspaceFolders
};
return connection.sendRequest(protocol.InitializeRequest.type, _params);
}
function initialized(connection) {
connection.sendNotification(protocol.InitializedNotification.type);
}
function shutdown(connection) {
return connection.sendRequest(protocol.ShutdownRequest.type);
}
function exit(connection) {
connection.sendNotification(protocol.ExitNotification.type);
}
function processRequest(connection, message) {
return _constructParamsAndRelay(connection, message.type, message.params);
}
function processNotification(connection, message) {
_constructParamsAndRelay(connection, message.type, message.params);
}
function attachOnNotificationHandlers(connection, handler) {
function _callbackFactory(type) {
switch (type) {
case protocol.ShowMessageNotification.type:
return _constructParamsAndRelay.bind(null, handler, ToolingInfo.SERVICE_NOTIFICATIONS.SHOW_MESSAGE);
case protocol.LogMessageNotification.type:
return _constructParamsAndRelay.bind(null, handler, ToolingInfo.SERVICE_NOTIFICATIONS.LOG_MESSAGE);
case protocol.TelemetryEventNotification.type:
return _constructParamsAndRelay.bind(null, handler, ToolingInfo.SERVICE_NOTIFICATIONS.TELEMETRY);
case protocol.PublishDiagnosticsNotification.type:
return _constructParamsAndRelay.bind(null, handler, ToolingInfo.SERVICE_NOTIFICATIONS.DIAGNOSTICS);
}
}
connection.onNotification(protocol.ShowMessageNotification.type, _callbackFactory(protocol.ShowMessageNotification.type));
connection.onNotification(protocol.LogMessageNotification.type, _callbackFactory(protocol.LogMessageNotification.type));
connection.onNotification(protocol.TelemetryEventNotification.type, _callbackFactory(protocol.TelemetryEventNotification.type));
connection.onNotification(protocol.PublishDiagnosticsNotification.type, _callbackFactory(protocol.PublishDiagnosticsNotification.type));
connection.onNotification(function (type, params) {
var _params = {
type: type,
params: params
};
handler(_params);
});
}
function attachOnRequestHandlers(connection, handler) {
function _callbackFactory(type) {
switch (type) {
case protocol.ShowMessageRequest.type:
return _constructParamsAndRelay.bind(null, handler, ToolingInfo.SERVICE_REQUESTS.SHOW_SELECT_MESSAGE);
case protocol.RegistrationRequest.type:
return _constructParamsAndRelay.bind(null, handler, ToolingInfo.SERVICE_REQUESTS.REGISTRATION_REQUEST);
case protocol.UnregistrationRequest.type:
return _constructParamsAndRelay.bind(null, handler, ToolingInfo.SERVICE_REQUESTS.UNREGISTRATION_REQUEST);
case protocol.WorkspaceFoldersRequest.type:
return _constructParamsAndRelay.bind(null, handler, ToolingInfo.SERVICE_REQUESTS.PROJECT_FOLDERS_REQUEST);
}
}
connection.onRequest(protocol.ShowMessageRequest.type, _callbackFactory(protocol.ShowMessageRequest.type));
connection.onRequest(protocol.RegistrationRequest.type, _callbackFactory(protocol.RegistrationRequest.type));
// See https://github.com/Microsoft/vscode-languageserver-node/issues/199
connection.onRequest("client/registerFeature", _callbackFactory(protocol.RegistrationRequest.type));
connection.onRequest(protocol.UnregistrationRequest.type, _callbackFactory(protocol.UnregistrationRequest.type));
// See https://github.com/Microsoft/vscode-languageserver-node/issues/199
connection.onRequest("client/unregisterFeature", _callbackFactory(protocol.UnregistrationRequest.type));
connection.onRequest(protocol.WorkspaceFoldersRequest.type, _callbackFactory(protocol.WorkspaceFoldersRequest.type));
connection.onRequest(function (type, params) {
var _params = {
type: type,
params: params
};
return handler(_params);
});
}
exports.initialize = initialize;
exports.initialized = initialized;
exports.shutdown = shutdown;
exports.exit = exit;
exports.onCustom = onCustom;
exports.sendCustomRequest = sendCustomRequest;
exports.sendCustomNotification = sendCustomNotification;
exports.processRequest = processRequest;
exports.processNotification = processNotification;
exports.attachOnNotificationHandlers = attachOnNotificationHandlers;
exports.attachOnRequestHandlers = attachOnRequestHandlers;