loom

Форк
0
/
LanguageClient.cpp 
474 строки · 19.5 Кб
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(const std::string & process_path, 
15
                    const std::vector<std::string> & process_args, 
16
                    const variable::Value & initialize_params, 
17
                    inout::Logger_interface & logger,
18
                    ServerRestartSupport_interface * )
19
        : _process_path(process_path)
20
        , _process_args(process_args)
21
        , _initialize_params(initialize_params)
22
        , _log(logger)
23
        // , _server_restart_support(server_restart_support)
24
    {
25
        _ok = start();
26

27
        if (!_ok)
28
            close();
29
    }
30

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

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

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

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

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

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

67
        return exec(*rpc, handle, sender);
68
    }
69

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

75
        uint64_t elapsed_mills = 0;
76

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

86
            elapsed_mills += 500;
87
        }
88

89
        return false;
90
    }
91

92
    void LanguageClient::close()
93
    {
94
        if (_stopping)
95
            return;
96

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

101
            _stopping = true;
102

103
            std::error_code ec;
104
            // @todo Перейти на boost::process::v2
105
            // _language_server->wait_for(std::chrono::milliseconds(100), ec);
106
            // if (ec)
107
            //     _log.error("Language server process stopping error", ec.message());
108
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
109

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

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

125
    // ------------------------------------------------------------------------------------------
126
    // private ----------------------------------------------------------------------------------
127
    // ------------------------------------------------------------------------------------------
128

129
    bool LanguageClient::start()
130
    {
131
        bool ok = true;
132

133
        try {
134
            _language_server = std::make_unique<bp::child>(
135
                                _process_path
136
                                , _process_args
137
                                , bp::std_in < _os
138
                                , bp::std_out > _is
139
    #ifdef CROSS_WIN
140
                                , bp::windows::hide
141
    #endif
142
                                , bp::on_exit([&](auto...)
143
                                {
144
                                    _is.close();
145
                                    _os.close();
146
                                    _log.info("Server stoped");
147
                                    _ok = false;
148
                                }
149
                                ));
150

151
            if (!_language_server->running()) {
152
                _log.critical("Unable to run language server (stop)");
153
                std::error_code ec;
154
                _language_server->terminate(ec);
155
                _language_server.reset();
156
                ok = false;
157
            }
158
        }
159
        catch(const std::exception& e) {
160
            _log.critical("Unable to run language server (stop)", e.what());
161
            _language_server.reset();
162
            ok = false;
163
        }
164
        
165
        if (ok) {
166
            _sending_thread  = std::make_unique<std::thread>(& LanguageClient::sender, this);
167
            _response_thread = std::make_unique<std::thread>(& LanguageClient::response_listener, this);
168

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

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

193
        return ok;
194
    }
195

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

201
        int64_t id = rpc.id();
202

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

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

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

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

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

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

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

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

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

284
            _log.debug("Ready work with content", content);
285

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

429
            if (resolveProvider_value.isBoolean())
430
                res.capabilities.completionProvider.resolveProvider = resolveProvider_value.getBool();
431

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

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

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

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

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

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

471
        return res;
472
    }
473

474
}

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

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

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

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