loom

Форк
0
/
LanguageClient.cpp 
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

8
namespace simodo::lsp
9
{
10
using namespace simodo::inout;
11

12
inline const std::string content_length_const = "Content-Length: ";
13

14
LanguageClient::LanguageClient(std::string process_path, std::vector<std::string> process_args, 
15
                variable::Object initialize_params, inout::Logger_interface & logger,
16
                ServerRestartSupport_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

25
    if (!_ok)
26
        close();
27
}
28

29
LanguageClient::~LanguageClient()
30
{
31
    close();
32
    if (_sending_thread)
33
        _sending_thread->join();
34
    if (_response_thread)
35
        _response_thread->join();
36
}
37

38
InitializeResult LanguageClient::initialize_result() const
39
{ 
40
    std::lock_guard locker(_initialize_result_mutex);
41
    return _initialize_result; 
42
}
43

44
ServerCapabilities LanguageClient::server_capabilities() const
45
{ 
46
    std::lock_guard locker(_initialize_result_mutex);
47
    return _initialize_result.capabilities; 
48
}
49

50
SimodoCapabilities LanguageClient::simodo_capabilities() const
51
{
52
    std::lock_guard locker(_initialize_result_mutex);
53
    return _initialize_result.simodo; 
54
}
55

56
int64_t LanguageClient::exec(const std::string method, variable::Value params, RpcHandle handle, const void * sender)
57
{
58
    std::unique_ptr<variable::JsonRpc> rpc;
59

60
    if (handle)
61
        rpc = std::make_unique<variable::JsonRpc>(toU16(method), params, ++_last_id);
62
    else
63
        rpc = std::make_unique<variable::JsonRpc>(toU16(method), params);
64

65
    return exec(*rpc, handle, sender);
66
}
67

68
bool LanguageClient::done(uint64_t timeout_mills)
69
{
70
    if (!running())
71
        return true;
72

73
    uint64_t elapsed_mills = 0;
74

75
    while(elapsed_mills < timeout_mills) {
76
        if (_queue_for_sending.empty()) {
77
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
78
            if (_queue_for_sending.empty())
79
                return true;
80
        }
81
        else
82
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
83

84
        elapsed_mills += 500;
85
    }
86

87
    return false;
88
}
89

90
void LanguageClient::close()
91
{
92
    if (_stopping)
93
        return;
94

95
    if (_language_server) {
96
        if (waitResponse(exec("shutdown", {}, [](variable::JsonRpc, const void *){})))
97
            exec("exit", {});
98

99
        _stopping = true;
100

101
        std::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());
106
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
107

108
        if (_language_server->running(ec))
109
        {
110
            _language_server->terminate(ec);
111
        }
112
        _language_server->wait(ec);
113
    }
114
}
115

116
bool LanguageClient::registerListener(std::string method, RpcHandle listener)
117
{
118
    std::lock_guard locker(_commands_listeners_mutex);
119
    _commands_listeners[method] = listener;
120
    return true;
121
}
122

123
// ------------------------------------------------------------------------------------------
124
// private ----------------------------------------------------------------------------------
125
// ------------------------------------------------------------------------------------------
126

127
bool LanguageClient::start()
128
{
129
    bool ok = true;
130

131
    try {
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

149
        if (!_language_server->running()) {
150
            _log.critical("Unable to run language server (stop)");
151
            std::error_code ec;
152
            _language_server->terminate(ec);
153
            _language_server.reset();
154
            ok = false;
155
        }
156
    }
157
    catch(const std::exception& e) {
158
        _log.critical("Unable to run language server (stop)", e.what());
159
        _language_server.reset();
160
        ok = false;
161
    }
162
    
163
    if (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

167
        int64_t id = exec("initialize", _initialize_params, 
168
                          [this](variable::JsonRpc rpc, const void *){
169
                            if (!rpc.is_valid()) {
170
                                _log.critical("Initialize result error", rpc.origin());
171
                                return;
172
                            }
173
                            std::lock_guard locker(_initialize_result_mutex);
174
                            _initialize_result = parseInitializeResult(rpc.result());
175
                          });
176

177
        if (id > 0) {
178
            if (waitResponse(id))
179
                exec("initialized", {});
180
            else {
181
                _log.critical("Response timeout occurred (stop)");
182
                ok = false;
183
            }
184
        }
185
        else {
186
            _log.critical("Response sending error occurred (stop)");
187
            ok = false;
188
        }
189
    }
190

191
    return ok;
192
}
193

194
int64_t LanguageClient::exec(const variable::JsonRpc & rpc, RpcHandle handle, const void * sender)
195
{
196
    if (!_os.good() || !rpc.is_valid()) 
197
        return -1;
198

199
    int64_t id = rpc.id();
200

201
    if ((id > 0 && handle == nullptr) || (id == -1 && handle != nullptr))
202
        return -1;
203

204
    std::string json = variable::toJson(rpc.value(), true, true);
205
    _queue_for_sending.push(json);
206

207
    if (id > 0) {
208
        std::lock_guard locker(_commands_waiting_to_be_executed_mutex);
209
        _commands_waiting_to_be_executed.insert({id, {handle,sender}});
210
    }
211
    
212
    return id;
213
}
214

215
void LanguageClient::sender()
216
try
217
{
218
    _log.debug("Output pipe sender starting");
219
    while(_ok && !_stopping) {
220
        std::string json;
221
        bool        got = _queue_for_sending.pop_or_timeout(json,100);
222

223
        if (got) {
224
            if (_os.good()) {
225
                _os << content_length_const << json.length() 
226
                    << "\n\n"
227
                    << json;
228
                _os.flush();
229
            }
230
            else {
231
                _log.critical("Bad pipe state has occurred during send operation");
232
                _ok = false;
233
                break;
234
            }
235
        }
236
    }
237

238
    _log.debug("Output pipe sender ending");
239
}
240
catch(const std::exception & e)
241
{
242
    _log.critical("Exception has occurred during send operation", e.what());
243
    _ok = false;
244
}
245
catch(...)
246
{
247
    _log.critical("Exception has occurred during send operation");
248
    _ok = false;
249
}
250

251
void LanguageClient::response_listener()
252
{
253
    _log.debug("Input pipe listener starting");
254
    while(_ok && !_stopping) {
255
        std::string line;
256
        size_t      content_length = 0;
257
        auto is_empty = [](const std::string & line) -> bool
258
        {
259
            return line.empty()
260
                || (
261
                    line.size() == 1
262
                    && line.back() == '\r'
263
                );
264
        };
265
        while(_is.good() && std::getline(_is,line) && !is_empty(line)) {
266
            if (line.find(content_length_const) == 0)
267
                content_length = std::stol(line.substr(content_length_const.size()));
268
        }
269

270
        if (!_is.good()) {
271
            // Штатная ситуация
272
            // _log.info("Wrong input pipe state (stop listening)");
273
            _ok = false;
274
            break;
275
        }
276
        _log.debug("Ready receive content length " + std::to_string(content_length));
277

278
        std::string content;
279
        for(size_t i=0; i < content_length && _is.good(); ++i)
280
            content += _is.get();
281

282
        _log.debug("Ready work with content", content);
283

284
        if (content.length() != content_length || !verifyJson(content)) {
285
            _log.error("Wrong JSON-RPC content (try to recover input pipe)", content);
286
            continue;
287
        }
288

289
        RpcHandle                 handle = nullptr;
290
        const void *              sender = nullptr;
291
        const variable::JsonRpc rpc(content);
292
        if (!rpc.is_valid()) {
293
            _log.error("JSON-RPC structure is invalid", content);
294
        }
295
        else if (!rpc.method().empty()) {
296
            {
297
                std::lock_guard locker(_commands_listeners_mutex);
298
                auto            it = _commands_listeners.find(toU8(rpc.method()));
299
                if (it == _commands_listeners.end())
300
                    _log.warning("An command or notification was ignored", content);
301
                else
302
                    handle = it->second;
303
            }
304
            if (handle)
305
                handle(rpc, nullptr);
306
        }
307
        else {
308
            {
309
                std::lock_guard locker(_commands_waiting_to_be_executed_mutex);
310
                auto            it  = _commands_waiting_to_be_executed.find(rpc.id());
311
                if (it != _commands_waiting_to_be_executed.end()) {
312
                    handle = it->second.first;
313
                    sender = it->second.second;
314
                    _commands_waiting_to_be_executed.erase(it);
315
                }
316
                else
317
                    _log.error("An unexpected response was received", content);
318
            }
319
            if (handle) {
320
                handle(rpc, sender);
321
                _command_complete_condition.notify_one();
322
            }
323
        }
324
    }
325
    _log.debug("Input pipe listener ending");
326
}
327

328
bool LanguageClient::waitResponse(int64_t id, int timeout_mills)
329
{
330
    if (id <= 0)
331
        return false;
332

333
    std::unique_lock locker(_command_complete_condition_mutex);
334
    return _command_complete_condition.wait_for(locker, std::chrono::milliseconds(timeout_mills), [&] { 
335
            std::lock_guard locker(_commands_waiting_to_be_executed_mutex);
336
            return _commands_waiting_to_be_executed.find(id) == _commands_waiting_to_be_executed.end();
337
        });
338
}
339

340
bool LanguageClient::verifyJson(const std::string & str)
341
{
342
    return !str.empty() && str[0] == '{' && str[str.size()-1] == '}';
343
}
344

345
InitializeResult LanguageClient::parseInitializeResult(const variable::Value & result_value)
346
{
347
    InitializeResult res;
348

349
    if (result_value.type() != variable::ValueType::Object) {
350
        _log.error("LanguageClient::parseInitializeResult: unable to parse result 1");
351
        return res;
352
    }
353

354
    const variable::Value & capabilities_value  = result_value.getObject()->find(u"capabilities");
355
    const variable::Value & simodo_value        = result_value.getObject()->find(u"simodo");
356
    const variable::Value & serverInfo_value    = result_value.getObject()->find(u"serverInfo");
357

358
    if (capabilities_value.type() != variable::ValueType::Object) {
359
        _log.error("LanguageClient::parseInitializeResult: unable to parse result 2");
360
        return res;
361
    }
362

363
    const variable::Value & positionEncoding_value  = capabilities_value.getObject()->find(u"positionEncoding");
364
    const variable::Value & textDocumentSync_value  = capabilities_value.getObject()->find(u"textDocumentSync");
365
    const variable::Value & hoverProvider_value     = capabilities_value.getObject()->find(u"hoverProvider");
366
    const variable::Value & declarationProvider_value=capabilities_value.getObject()->find(u"declarationProvider");
367
    const variable::Value & definitionProvider_value= capabilities_value.getObject()->find(u"definitionProvider");
368
    const variable::Value & referencesProvider_value= capabilities_value.getObject()->find(u"referencesProvider");
369
    const variable::Value & documentSymbolProvider_value = capabilities_value.getObject()->find(u"documentSymbolProvider");
370
    const variable::Value & semanticTokensProvider_value = capabilities_value.getObject()->find(u"semanticTokensProvider");
371
    const variable::Value & completionProvider_value = capabilities_value.getObject()->find(u"completionProvider");
372

373
    if (positionEncoding_value.type() == variable::ValueType::String)
374
        res.capabilities.positionEncoding = positionEncoding_value.getString();
375

376
    if (textDocumentSync_value.type() == variable::ValueType::Object) {
377
        const variable::Value & openClose_value = textDocumentSync_value.getObject()->find(u"openClose");
378
        const variable::Value & change_value    = textDocumentSync_value.getObject()->find(u"change");
379

380
        if (openClose_value.type() == variable::ValueType::Bool)
381
            res.capabilities.textDocumentSync.openClose = openClose_value.getBool();
382
        if (change_value.type() == variable::ValueType::Int)
383
            res.capabilities.textDocumentSync.change = static_cast<TextDocumentSyncKind>(change_value.getInt());
384
    }
385

386
    if (hoverProvider_value.type() == variable::ValueType::Bool)
387
        res.capabilities.hoverProvider = hoverProvider_value.getBool();
388
    if (declarationProvider_value.type() == variable::ValueType::Bool)
389
        res.capabilities.declarationProvider = declarationProvider_value.getBool();
390
    if (definitionProvider_value.type() == variable::ValueType::Bool)
391
        res.capabilities.definitionProvider = definitionProvider_value.getBool();
392
    if (referencesProvider_value.type() == variable::ValueType::Bool)
393
        res.capabilities.referencesProvider = referencesProvider_value.getBool();
394
    if (documentSymbolProvider_value.type() == variable::ValueType::Bool)
395
        res.capabilities.documentSymbolProvider = documentSymbolProvider_value.getBool();
396

397
    if (semanticTokensProvider_value.type() == variable::ValueType::Object) {
398
        const variable::Value & legend_value = semanticTokensProvider_value.getObject()->find(u"legend");
399
        if (legend_value.type() == variable::ValueType::Object) {
400
            const variable::Value & tokenTypes_value = legend_value.getObject()->find(u"tokenTypes");
401
            const variable::Value & tokenModifiers_value = legend_value.getObject()->find(u"tokenModifiers");
402

403
            if (tokenTypes_value.type() == variable::ValueType::Array)
404
                for(auto & v : tokenTypes_value.getArray()->values())
405
                    if (v.type() == variable::ValueType::String)
406
                        res.capabilities.semanticTokensProvider.legend.tokenTypes.push_back(v.getString());
407

408
            if (tokenModifiers_value.type() == variable::ValueType::Array)
409
                for(auto & v : tokenModifiers_value.getArray()->values())
410
                    if (v.type() == variable::ValueType::String)
411
                        res.capabilities.semanticTokensProvider.legend.tokenModifiers.push_back(v.getString());
412
        }
413
    }
414

415
    if (completionProvider_value.isObject()) {
416
        const variable::Value & triggerCharacters_value = completionProvider_value.getObject()->find(u"triggerCharacters");
417
        const variable::Value & resolveProvider_value   = completionProvider_value.getObject()->find(u"resolveProvider");
418
        const variable::Value & completionItem_value    = completionProvider_value.getObject()->find(u"completionItem");
419

420
        if (triggerCharacters_value.isArray()) {
421
            const std::shared_ptr<variable::Array> triggerCharacters_array = triggerCharacters_value.getArray();
422
            for(const variable::Value & v : triggerCharacters_array->values())
423
                if (v.isString())
424
                    res.capabilities.completionProvider.triggerCharacters.push_back(v.getString());
425
        }
426

427
        if (resolveProvider_value.isBoolean())
428
            res.capabilities.completionProvider.resolveProvider = resolveProvider_value.getBool();
429

430
        if (completionItem_value.isObject()) {
431
            const variable::Value & labelDetailsSupport_value = completionItem_value.getObject()->find(u"labelDetailsSupport");
432
            if (labelDetailsSupport_value.isBoolean())
433
                res.capabilities.completionProvider.completionItem.labelDetailsSupport = labelDetailsSupport_value.getBool();
434
        }
435
    }
436

437
    if (simodo_value.type() == variable::ValueType::Object) {
438
        const variable::Value & runnerProvider_value    = simodo_value.getObject()->find(u"runnerProvider");
439
        const variable::Value & debugProvider_value     = simodo_value.getObject()->find(u"debugProvider");
440
        const variable::Value & runnerOptions_value     = simodo_value.getObject()->find(u"runnerOptions");
441

442
        if (runnerProvider_value.type() == variable::ValueType::Bool)
443
            res.simodo.runnerProvider = runnerProvider_value.getBool();
444
        if (debugProvider_value.type() == variable::ValueType::Bool)
445
            res.simodo.debugProvider = debugProvider_value.getBool();
446
        if (runnerOptions_value.isArray()) {
447
            const std::shared_ptr<variable::Array> runnerOptions_array = runnerOptions_value.getArray();
448
            for(const variable::Value & opt_value : runnerOptions_array->values())
449
                if (opt_value.isObject()) {
450
                    const variable::Value & languageID_value    = opt_value.getObject()->find(u"languageID");
451
                    const variable::Value & viewName_value     = opt_value.getObject()->find(u"viewName");
452

453
                    if (languageID_value.isString() && viewName_value.isString())
454
                        res.simodo.runnerOptions.push_back({languageID_value.getString(), viewName_value.getString()});
455
                }
456
        }
457
    }
458

459
    if (serverInfo_value.type() == variable::ValueType::Object) {
460
        const variable::Value & name_value      = serverInfo_value.getObject()->find(u"name");
461
        const variable::Value & version_value   = serverInfo_value.getObject()->find(u"version");
462

463
        if (name_value.type() == variable::ValueType::String)
464
            res.serverInfo.name = name_value.getString();
465
        if (version_value.type() == variable::ValueType::String)
466
            res.serverInfo.version = version_value.getString();
467
    }
468

469
    return res;
470
}
471

472
}

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.