loom
472 строки · 17.9 Кб
1#include "simodo/lsp-client/LanguageClient.h"
2
3#include <boost/asio.hpp>
4#ifdef CROSS_WIN
5#include <boost/process/windows.hpp>
6#endif
7
8namespace simodo::lsp
9{
10using namespace simodo::inout;
11
12inline const std::string content_length_const = "Content-Length: ";
13
14LanguageClient::LanguageClient(std::string process_path, std::vector<std::string> process_args,
15variable::Object initialize_params, inout::Logger_interface & logger,
16ServerRestartSupport_interface * )
17: _process_path(process_path)
18, _process_args(process_args)
19, _initialize_params(initialize_params)
20, _log(logger)
21// , _server_restart_support(server_restart_support)
22{
23_ok = start();
24
25if (!_ok)
26close();
27}
28
29LanguageClient::~LanguageClient()
30{
31close();
32if (_sending_thread)
33_sending_thread->join();
34if (_response_thread)
35_response_thread->join();
36}
37
38InitializeResult LanguageClient::initialize_result() const
39{
40std::lock_guard locker(_initialize_result_mutex);
41return _initialize_result;
42}
43
44ServerCapabilities LanguageClient::server_capabilities() const
45{
46std::lock_guard locker(_initialize_result_mutex);
47return _initialize_result.capabilities;
48}
49
50SimodoCapabilities LanguageClient::simodo_capabilities() const
51{
52std::lock_guard locker(_initialize_result_mutex);
53return _initialize_result.simodo;
54}
55
56int64_t LanguageClient::exec(const std::string method, variable::Value params, RpcHandle handle, const void * sender)
57{
58std::unique_ptr<variable::JsonRpc> rpc;
59
60if (handle)
61rpc = std::make_unique<variable::JsonRpc>(toU16(method), params, ++_last_id);
62else
63rpc = std::make_unique<variable::JsonRpc>(toU16(method), params);
64
65return exec(*rpc, handle, sender);
66}
67
68bool LanguageClient::done(uint64_t timeout_mills)
69{
70if (!running())
71return true;
72
73uint64_t elapsed_mills = 0;
74
75while(elapsed_mills < timeout_mills) {
76if (_queue_for_sending.empty()) {
77std::this_thread::sleep_for(std::chrono::milliseconds(500));
78if (_queue_for_sending.empty())
79return true;
80}
81else
82std::this_thread::sleep_for(std::chrono::milliseconds(500));
83
84elapsed_mills += 500;
85}
86
87return false;
88}
89
90void LanguageClient::close()
91{
92if (_stopping)
93return;
94
95if (_language_server) {
96if (waitResponse(exec("shutdown", {}, [](variable::JsonRpc, const void *){})))
97exec("exit", {});
98
99_stopping = true;
100
101std::error_code ec;
102// @todo Перейти на boost::process::v2
103// _language_server->wait_for(std::chrono::milliseconds(100), ec);
104// if (ec)
105// _log.error("Language server process stopping error", ec.message());
106std::this_thread::sleep_for(std::chrono::milliseconds(100));
107
108if (_language_server->running(ec))
109{
110_language_server->terminate(ec);
111}
112_language_server->wait(ec);
113}
114}
115
116bool LanguageClient::registerListener(std::string method, RpcHandle listener)
117{
118std::lock_guard locker(_commands_listeners_mutex);
119_commands_listeners[method] = listener;
120return true;
121}
122
123// ------------------------------------------------------------------------------------------
124// private ----------------------------------------------------------------------------------
125// ------------------------------------------------------------------------------------------
126
127bool LanguageClient::start()
128{
129bool ok = true;
130
131try {
132_language_server = std::make_unique<bp::child>(
133_process_path
134, _process_args
135, bp::std_in < _os
136, bp::std_out > _is
137#ifdef CROSS_WIN
138, bp::windows::hide
139#endif
140, bp::on_exit([&](auto...)
141{
142_is.close();
143_os.close();
144_log.info("Server stoped");
145_ok = false;
146}
147));
148
149if (!_language_server->running()) {
150_log.critical("Unable to run language server (stop)");
151std::error_code ec;
152_language_server->terminate(ec);
153_language_server.reset();
154ok = false;
155}
156}
157catch(const std::exception& e) {
158_log.critical("Unable to run language server (stop)", e.what());
159_language_server.reset();
160ok = false;
161}
162
163if (ok) {
164_sending_thread = std::make_unique<std::thread>(& LanguageClient::sender, this);
165_response_thread = std::make_unique<std::thread>(& LanguageClient::response_listener, this);
166
167int64_t id = exec("initialize", _initialize_params,
168[this](variable::JsonRpc rpc, const void *){
169if (!rpc.is_valid()) {
170_log.critical("Initialize result error", rpc.origin());
171return;
172}
173std::lock_guard locker(_initialize_result_mutex);
174_initialize_result = parseInitializeResult(rpc.result());
175});
176
177if (id > 0) {
178if (waitResponse(id))
179exec("initialized", {});
180else {
181_log.critical("Response timeout occurred (stop)");
182ok = false;
183}
184}
185else {
186_log.critical("Response sending error occurred (stop)");
187ok = false;
188}
189}
190
191return ok;
192}
193
194int64_t LanguageClient::exec(const variable::JsonRpc & rpc, RpcHandle handle, const void * sender)
195{
196if (!_os.good() || !rpc.is_valid())
197return -1;
198
199int64_t id = rpc.id();
200
201if ((id > 0 && handle == nullptr) || (id == -1 && handle != nullptr))
202return -1;
203
204std::string json = variable::toJson(rpc.value(), true, true);
205_queue_for_sending.push(json);
206
207if (id > 0) {
208std::lock_guard locker(_commands_waiting_to_be_executed_mutex);
209_commands_waiting_to_be_executed.insert({id, {handle,sender}});
210}
211
212return id;
213}
214
215void LanguageClient::sender()
216try
217{
218_log.debug("Output pipe sender starting");
219while(_ok && !_stopping) {
220std::string json;
221bool got = _queue_for_sending.pop_or_timeout(json,100);
222
223if (got) {
224if (_os.good()) {
225_os << content_length_const << json.length()
226<< "\n\n"
227<< json;
228_os.flush();
229}
230else {
231_log.critical("Bad pipe state has occurred during send operation");
232_ok = false;
233break;
234}
235}
236}
237
238_log.debug("Output pipe sender ending");
239}
240catch(const std::exception & e)
241{
242_log.critical("Exception has occurred during send operation", e.what());
243_ok = false;
244}
245catch(...)
246{
247_log.critical("Exception has occurred during send operation");
248_ok = false;
249}
250
251void LanguageClient::response_listener()
252{
253_log.debug("Input pipe listener starting");
254while(_ok && !_stopping) {
255std::string line;
256size_t content_length = 0;
257auto is_empty = [](const std::string & line) -> bool
258{
259return line.empty()
260|| (
261line.size() == 1
262&& line.back() == '\r'
263);
264};
265while(_is.good() && std::getline(_is,line) && !is_empty(line)) {
266if (line.find(content_length_const) == 0)
267content_length = std::stol(line.substr(content_length_const.size()));
268}
269
270if (!_is.good()) {
271// Штатная ситуация
272// _log.info("Wrong input pipe state (stop listening)");
273_ok = false;
274break;
275}
276_log.debug("Ready receive content length " + std::to_string(content_length));
277
278std::string content;
279for(size_t i=0; i < content_length && _is.good(); ++i)
280content += _is.get();
281
282_log.debug("Ready work with content", content);
283
284if (content.length() != content_length || !verifyJson(content)) {
285_log.error("Wrong JSON-RPC content (try to recover input pipe)", content);
286continue;
287}
288
289RpcHandle handle = nullptr;
290const void * sender = nullptr;
291const variable::JsonRpc rpc(content);
292if (!rpc.is_valid()) {
293_log.error("JSON-RPC structure is invalid", content);
294}
295else if (!rpc.method().empty()) {
296{
297std::lock_guard locker(_commands_listeners_mutex);
298auto it = _commands_listeners.find(toU8(rpc.method()));
299if (it == _commands_listeners.end())
300_log.warning("An command or notification was ignored", content);
301else
302handle = it->second;
303}
304if (handle)
305handle(rpc, nullptr);
306}
307else {
308{
309std::lock_guard locker(_commands_waiting_to_be_executed_mutex);
310auto it = _commands_waiting_to_be_executed.find(rpc.id());
311if (it != _commands_waiting_to_be_executed.end()) {
312handle = it->second.first;
313sender = it->second.second;
314_commands_waiting_to_be_executed.erase(it);
315}
316else
317_log.error("An unexpected response was received", content);
318}
319if (handle) {
320handle(rpc, sender);
321_command_complete_condition.notify_one();
322}
323}
324}
325_log.debug("Input pipe listener ending");
326}
327
328bool LanguageClient::waitResponse(int64_t id, int timeout_mills)
329{
330if (id <= 0)
331return false;
332
333std::unique_lock locker(_command_complete_condition_mutex);
334return _command_complete_condition.wait_for(locker, std::chrono::milliseconds(timeout_mills), [&] {
335std::lock_guard locker(_commands_waiting_to_be_executed_mutex);
336return _commands_waiting_to_be_executed.find(id) == _commands_waiting_to_be_executed.end();
337});
338}
339
340bool LanguageClient::verifyJson(const std::string & str)
341{
342return !str.empty() && str[0] == '{' && str[str.size()-1] == '}';
343}
344
345InitializeResult LanguageClient::parseInitializeResult(const variable::Value & result_value)
346{
347InitializeResult res;
348
349if (result_value.type() != variable::ValueType::Object) {
350_log.error("LanguageClient::parseInitializeResult: unable to parse result 1");
351return res;
352}
353
354const variable::Value & capabilities_value = result_value.getObject()->find(u"capabilities");
355const variable::Value & simodo_value = result_value.getObject()->find(u"simodo");
356const variable::Value & serverInfo_value = result_value.getObject()->find(u"serverInfo");
357
358if (capabilities_value.type() != variable::ValueType::Object) {
359_log.error("LanguageClient::parseInitializeResult: unable to parse result 2");
360return res;
361}
362
363const variable::Value & positionEncoding_value = capabilities_value.getObject()->find(u"positionEncoding");
364const variable::Value & textDocumentSync_value = capabilities_value.getObject()->find(u"textDocumentSync");
365const variable::Value & hoverProvider_value = capabilities_value.getObject()->find(u"hoverProvider");
366const variable::Value & declarationProvider_value=capabilities_value.getObject()->find(u"declarationProvider");
367const variable::Value & definitionProvider_value= capabilities_value.getObject()->find(u"definitionProvider");
368const variable::Value & referencesProvider_value= capabilities_value.getObject()->find(u"referencesProvider");
369const variable::Value & documentSymbolProvider_value = capabilities_value.getObject()->find(u"documentSymbolProvider");
370const variable::Value & semanticTokensProvider_value = capabilities_value.getObject()->find(u"semanticTokensProvider");
371const variable::Value & completionProvider_value = capabilities_value.getObject()->find(u"completionProvider");
372
373if (positionEncoding_value.type() == variable::ValueType::String)
374res.capabilities.positionEncoding = positionEncoding_value.getString();
375
376if (textDocumentSync_value.type() == variable::ValueType::Object) {
377const variable::Value & openClose_value = textDocumentSync_value.getObject()->find(u"openClose");
378const variable::Value & change_value = textDocumentSync_value.getObject()->find(u"change");
379
380if (openClose_value.type() == variable::ValueType::Bool)
381res.capabilities.textDocumentSync.openClose = openClose_value.getBool();
382if (change_value.type() == variable::ValueType::Int)
383res.capabilities.textDocumentSync.change = static_cast<TextDocumentSyncKind>(change_value.getInt());
384}
385
386if (hoverProvider_value.type() == variable::ValueType::Bool)
387res.capabilities.hoverProvider = hoverProvider_value.getBool();
388if (declarationProvider_value.type() == variable::ValueType::Bool)
389res.capabilities.declarationProvider = declarationProvider_value.getBool();
390if (definitionProvider_value.type() == variable::ValueType::Bool)
391res.capabilities.definitionProvider = definitionProvider_value.getBool();
392if (referencesProvider_value.type() == variable::ValueType::Bool)
393res.capabilities.referencesProvider = referencesProvider_value.getBool();
394if (documentSymbolProvider_value.type() == variable::ValueType::Bool)
395res.capabilities.documentSymbolProvider = documentSymbolProvider_value.getBool();
396
397if (semanticTokensProvider_value.type() == variable::ValueType::Object) {
398const variable::Value & legend_value = semanticTokensProvider_value.getObject()->find(u"legend");
399if (legend_value.type() == variable::ValueType::Object) {
400const variable::Value & tokenTypes_value = legend_value.getObject()->find(u"tokenTypes");
401const variable::Value & tokenModifiers_value = legend_value.getObject()->find(u"tokenModifiers");
402
403if (tokenTypes_value.type() == variable::ValueType::Array)
404for(auto & v : tokenTypes_value.getArray()->values())
405if (v.type() == variable::ValueType::String)
406res.capabilities.semanticTokensProvider.legend.tokenTypes.push_back(v.getString());
407
408if (tokenModifiers_value.type() == variable::ValueType::Array)
409for(auto & v : tokenModifiers_value.getArray()->values())
410if (v.type() == variable::ValueType::String)
411res.capabilities.semanticTokensProvider.legend.tokenModifiers.push_back(v.getString());
412}
413}
414
415if (completionProvider_value.isObject()) {
416const variable::Value & triggerCharacters_value = completionProvider_value.getObject()->find(u"triggerCharacters");
417const variable::Value & resolveProvider_value = completionProvider_value.getObject()->find(u"resolveProvider");
418const variable::Value & completionItem_value = completionProvider_value.getObject()->find(u"completionItem");
419
420if (triggerCharacters_value.isArray()) {
421const std::shared_ptr<variable::Array> triggerCharacters_array = triggerCharacters_value.getArray();
422for(const variable::Value & v : triggerCharacters_array->values())
423if (v.isString())
424res.capabilities.completionProvider.triggerCharacters.push_back(v.getString());
425}
426
427if (resolveProvider_value.isBoolean())
428res.capabilities.completionProvider.resolveProvider = resolveProvider_value.getBool();
429
430if (completionItem_value.isObject()) {
431const variable::Value & labelDetailsSupport_value = completionItem_value.getObject()->find(u"labelDetailsSupport");
432if (labelDetailsSupport_value.isBoolean())
433res.capabilities.completionProvider.completionItem.labelDetailsSupport = labelDetailsSupport_value.getBool();
434}
435}
436
437if (simodo_value.type() == variable::ValueType::Object) {
438const variable::Value & runnerProvider_value = simodo_value.getObject()->find(u"runnerProvider");
439const variable::Value & debugProvider_value = simodo_value.getObject()->find(u"debugProvider");
440const variable::Value & runnerOptions_value = simodo_value.getObject()->find(u"runnerOptions");
441
442if (runnerProvider_value.type() == variable::ValueType::Bool)
443res.simodo.runnerProvider = runnerProvider_value.getBool();
444if (debugProvider_value.type() == variable::ValueType::Bool)
445res.simodo.debugProvider = debugProvider_value.getBool();
446if (runnerOptions_value.isArray()) {
447const std::shared_ptr<variable::Array> runnerOptions_array = runnerOptions_value.getArray();
448for(const variable::Value & opt_value : runnerOptions_array->values())
449if (opt_value.isObject()) {
450const variable::Value & languageID_value = opt_value.getObject()->find(u"languageID");
451const variable::Value & viewName_value = opt_value.getObject()->find(u"viewName");
452
453if (languageID_value.isString() && viewName_value.isString())
454res.simodo.runnerOptions.push_back({languageID_value.getString(), viewName_value.getString()});
455}
456}
457}
458
459if (serverInfo_value.type() == variable::ValueType::Object) {
460const variable::Value & name_value = serverInfo_value.getObject()->find(u"name");
461const variable::Value & version_value = serverInfo_value.getObject()->find(u"version");
462
463if (name_value.type() == variable::ValueType::String)
464res.serverInfo.name = name_value.getString();
465if (version_value.type() == variable::ValueType::String)
466res.serverInfo.version = version_value.getString();
467}
468
469return res;
470}
471
472}