与语言服务器通信(语言服务器协议)

Communicate with Language Server (Languager Server Protocoll)

我想在一个简单的自写文本编辑器中测试语言服务器协议。但是我不太确定我是否正确理解如何写入服务器并从中读取。我想用 C++ 来做这个。

出于测试目的,我在此示例中使用了 Qt。但是,如果您使用另一个库,这也可以。作为服务器,我安装了 ccls(我用 atom 测试它时它正在工作)。

所以这是我的一般想法: 1.将服务器作为进程启动 2.根据规范定义一个json文件用于初始化 3.转成String发送给客户端 3. 等待响应(应该是 InitializeResult)

#include <QCoreApplication>
#include <QProcess>
#include <iostream>
#include <QFile>

int main(int argc, char* argv[]) {
  QCoreApplication app(argc, argv);

  QFile file {"src/initializeRequest.json"};
  file.open(QIODevice::ReadOnly);

  QProcess* myProcess = new QProcess(&app);
  myProcess->start("ccls", QStringList {});

  std::cout << myProcess->write(file.readAll()) << '\n';
  std::cout << myProcess->readAll().toStdString();
  file.close();
  return app.exec();
}

但实际上我什至不确定这些消息(didOpen、initialzeRequest 等)是否真的作为文件发送。根据语言服务器协议网站,它们是描述 json 文件的接口...但我没有找到有关它们如何发送的任何信息

所以,如果有人能告诉我我是否在正确的轨道上(我尝试发送实际文件),以及是否有人能向我展示与服务器的最简单通信,我将不胜感激,这样我就会得到回应(看看它是否有效)。

我做了很多实验并创建了一个最小的工作示例。在此示例中,我向 ccls(C++ 语言服务器)发送初始化请求并读取响应。 我使用 QProcess 来启动服务器(但当然可以使用不同的东西)和 rapidjson (https://github.com/Tencent/rapidjson) 来构建 json 文件。

#include <QCoreApplication>
#include <QProcess>
#include <iostream>
#include <sstream>

#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"

int main(int argc, char* argv[]) {
  QCoreApplication app {argc, argv};

  //Start server as a QProcess
  QProcess* server = new QProcess {&app};
  server->start("ccls", QStringList {"-log-file=/tmp/ccls2.log", "-init={}"});
  server->waitForStarted(-1);
  std::cout << "Server started" << '\n';

  //Construct json Request
  rapidjson::StringBuffer output {};
  rapidjson::Writer<rapidjson::StringBuffer> writer {output};

  writer.StartObject();
  writer.Key("jsonrpc");
  writer.String("2.0");
  writer.Key("id");
  writer.Int(0);
  writer.Key("method");
  writer.String("initialize");
  writer.Key("params");
  writer.StartObject();
  writer.Key("processId");
  writer.Int(0);
  writer.Key("rootUri");
  writer.String("home");
  writer.Key("capabilities");
  writer.StartObject();
  writer.EndObject();
  writer.EndObject();
  writer.EndObject();

  std::string content = output.GetString();
  std::ostringstream oss;
  oss << "Content-Length: " << content.length() << "\r\n" << "\r\n";
  std::string header = oss.str();

  std::cout << header << content << '\n';

  //Send request to server
  server->write(header.c_str());
  server->write(content.c_str());

  //Wait for response and read it
  server->waitForReadyRead(-1);
  std::cout << "Server has sent response" << '\n';
  std::cout << server->readAll().toStdString() << '\n';

  return app.exec();
}

输出:

Server started
Content-Length: 106

{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":0,"rootUri":"home","capabilities":{}}}
Server has sent response
Content-Length: 1046

{"jsonrpc":"2.0","id":0,"result":{"capabilities":{"textDocumentSync":{"openClose":true,"change":2,"willSave":false,"willSaveWaitUntil":false,"
save":{"includeText":false}},"hoverProvider":true,"completionProvider":{"resolveProvider":false,"triggerCharacters":[".",":",">","#","<","\"",
"/"]},"signatureHelpProvider":{"triggerCharacters":["(",","]},"definitionProvider":true,"implementationProvider":true,"typeDefinitionProvider"
:true,"referencesProvider":true,"documentHighlightProvider":true,"documentSymbolProvider":true,"workspaceSymbolProvider":true,"codeActionProvi
der":{"codeActionKinds":["quickfix"]},"codeLensProvider":{"resolveProvider":false},"documentFormattingProvider":true,"documentRangeFormattingP
rovider":true,"documentOnTypeFormattingProvider":{"firstTriggerCharacter":"}","moreTriggerCharacter":[]},"renameProvider":true,"documentLinkPr
ovider":{"resolveProvider":true},"foldingRangeProvider":true,"executeCommandProvider":{"commands":["ccls.xref"]},"workspace":{"workspaceFolder
s":{"supported":true,"changeNotifications":true}}}}}

正如我所说,我想向所有有相同问题的人展示一个最小的工作示例。当然,为 Request 等创建结构是有意义的。