loom

Форк
0
/
DocumentContext.cpp 
335 строк · 11.3 Кб
1
#include "simodo/lsp-server/DocumentContext.h"
2
#include "simodo/lsp-server/ServerContext.h"
3
#include "simodo/lsp-server/ReportCombiner.h"
4

5
#include "simodo/inout/convert/functions.h"
6
#include "simodo/lsp-client/CompletionItemKind.h"
7

8
#include <iostream>
9
#include <filesystem>
10
#include <future>
11
#include <algorithm>
12

13
namespace simodo::lsp
14
{
15

16
DocumentContext::DocumentContext(ServerContext & server, 
17
                                 const std::string & languageId,
18
                                 const variable::Object & textDocument_object)
19
    : _server(server)
20
    , _op(server.document_operation_factory().create(*this,languageId))
21
    , _valid(false)
22
{
23
    open(textDocument_object);
24
}
25

26
bool DocumentContext::open(const variable::Object & textDocument_object)
27
{
28
    _valid = false;
29

30
    const variable::Value & uri_value = textDocument_object.find(u"uri");
31
    if (uri_value.type() != variable::ValueType::String)
32
        return false;
33

34
    const variable::Value & languageId_value = textDocument_object.find(u"languageId");
35
    if (languageId_value.type() != variable::ValueType::String)
36
        return false;
37

38
    const variable::Value & version_value = textDocument_object.find(u"version");
39
    if (version_value.type() != variable::ValueType::Int)
40
        return false;
41

42
    const variable::Value & text_value = textDocument_object.find(u"text");
43
    if (text_value.type() != variable::ValueType::String)
44
        return false;
45

46
    std::u16string text = inout::decodeSpecialChars(text_value.getString());
47

48
    std::unique_lock<std::shared_timed_mutex> write_locker(_analyze_data_mutex);
49

50
    _uri = inout::toU8(uri_value.getString());
51
    _languageId = inout::toU8(languageId_value.getString());
52
    _text = text;
53
    _version = version_value.getInt();
54
    _file_name = convertUriToPath(_uri);
55

56
    return _valid = _is_opened = true;
57
}
58

59
bool DocumentContext::change(const variable::Object & doc_params)
60
{
61
    if (!_is_opened) {
62
        _server.log().error("DocumentContext::change: Document did not opened");
63
        return false;
64
    }
65

66
    const variable::Value & textDocument_value = doc_params.find(u"textDocument");
67
    if (textDocument_value.type() != variable::ValueType::Object) {
68
        _server.log().error("DocumentContext::change: Wrong params structure #1");
69
        return false;
70
    }
71

72
    std::shared_ptr<variable::Object> textDocument_object = textDocument_value.getObject();
73
    const variable::Value & version_value = textDocument_object->find(u"version");
74
    if (version_value.type() != variable::ValueType::Int) {
75
        _server.log().error("DocumentContext::change: Wrong params structure #2");
76
        return false;
77
    }
78

79
    /// @note Заготовка для распределённых вычислений + вероятность асинхронности при работе 
80
    /// с пулом потоков. Не обращаем внимания на запоздавшие старые версии изменений.
81
    if (version_value.getInt() <= _version) {
82
        _server.log().warning("DocumentContext::change: Document versions don't match");
83
        return false;
84
    }
85

86
    const variable::Value & contentChanges_value = doc_params.find(u"contentChanges");
87
    if (contentChanges_value.type() != variable::ValueType::Array) {
88
        _server.log().error("DocumentContext::change: Wrong params structure #3");
89
        return false;
90
    }
91

92
    std::shared_ptr<variable::Array> contentChanges = contentChanges_value.getArray();
93
    if (contentChanges->values().size() != 1 
94
     || contentChanges->values()[0].type() != variable::ValueType::Object) {
95
        _server.log().error("DocumentContext::change: Wrong params structure #4");
96
        return false;
97
    }
98

99
    std::shared_ptr<variable::Object> contentChangesEvent = contentChanges->values()[0].getObject();
100
    const variable::Value & text_value = contentChangesEvent->find(u"text");
101
    if (text_value.type() != variable::ValueType::String) {
102
        _server.log().error("DocumentContext::change: Wrong params structure #5");
103
        return false;
104
    }
105

106
    std::u16string text = inout::decodeSpecialChars(text_value.getString());
107

108
    std::unique_lock<std::shared_timed_mutex> write_locker(_analyze_data_mutex);
109

110
    _text = text;
111
    _version = version_value.getInt();
112

113
    return true;
114
}
115

116
bool DocumentContext::close()
117
{
118
    std::unique_lock<std::shared_timed_mutex> write_locker(_analyze_data_mutex);
119

120
    if (!_is_opened)
121
        return false;
122
    _is_opened = false;
123

124
    return true;
125
}
126

127
bool DocumentContext::analyze()
128
{
129
    std::unique_lock<std::shared_timed_mutex> write_locker(_analyze_data_mutex);
130

131
    if (!_valid)
132
        return false;
133
        
134
    ReportCombiner m;
135

136
    _op->analyze(_text, m);
137

138
    _server.sending().push(
139
        variable::JsonRpc {u"textDocument/publishDiagnostics", makeDiagnosticParams(m.messages())} );
140

141
    return true;
142
}
143

144
void DocumentContext::copyContent(std::u16string & content) const
145
{
146
    std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
147
    content = _text;
148
}
149

150
bool DocumentContext::checkDependency(const std::string & uri) 
151
{
152
    std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
153

154
    return _op->checkDependency(uri);
155
}
156

157
variable::Value DocumentContext::produceHoverResponse(const simodo::lsp::Position & pos)
158
{
159
    std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
160

161
    return _op->produceHoverResponse(pos);
162
}
163

164
variable::Value DocumentContext::produceGotoDeclarationResponse(const simodo::lsp::Position & pos)
165
{
166
    std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
167

168
    return _op->produceGotoDeclarationResponse(pos);
169
}
170

171
variable::Value DocumentContext::produceGotoDefinitionResponse(const simodo::lsp::Position & pos)
172
{
173
    std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
174

175
    return _op->produceGotoDefinitionResponse(pos);
176
}
177

178
variable::Value DocumentContext::produceCompletionResponse(const simodo::lsp::CompletionParams & completionParams)
179
{
180
    std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
181

182
    return _op->produceCompletionResponse(completionParams);
183
}
184

185
variable::Value DocumentContext::produceSemanticTokensResponse()
186
{
187
    std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
188

189
    return _op->produceSemanticTokensResponse();
190
}
191

192
variable::Value DocumentContext::produceDocumentSymbolsResponse()
193
{
194
    std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
195

196
    return _op->produceDocumentSymbolsResponse();
197
}
198

199
variable::Value DocumentContext::produceSimodoCommandResponse(const std::u16string & command_name)
200
{
201
    std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
202

203
    if (!_valid)
204
        return false;
205

206
    return _op->produceSimodoCommandResponse(command_name, _text);
207
}
208

209
////////////////////////////////////////////////////////////////////////////////////
210
// privates:
211

212
variable::Value DocumentContext::makeDiagnosticParams(const std::vector<MessageFullContent> & message_set) const
213
{
214
    std::vector<variable::Value> diagnostics;
215
    // std::u16string file_name_u16 = inout::toU16(_file_name);
216

217
    int i = 0;
218
    for(const MessageFullContent & m : message_set) {
219
        if (m.location.uri() == _file_name) {
220
            if (m.level >= inout::SeverityLevel::Error &&  i++ == 50) {
221
                /// @todo locker?
222
                _server.log().warning("Too many errors produced by analyze for '" + _uri + "'");
223
                break;
224
            }
225
            diagnostics.push_back(variable::Object {{
226
                                {u"range",      makeRange(m.location)},
227
                                {u"severity",   makeSeverity(m.level)},
228
                                {u"source",     u"SIMODO/loom"},
229
                                {u"message",    makeMessage(m)},
230
                            }});
231
        }
232
    }
233

234
    if (diagnostics.empty() && !message_set.empty()) {
235
        std::set<std::string> processed_files;
236
        /// \note Это значит, что возможно были ошибки в других файлах, нужно об этом сказать!
237
        for(const MessageFullContent & m : message_set) 
238
            if (m.level >= inout::SeverityLevel::Error && processed_files.find(m.location.uri()) == processed_files.end()) {
239
                diagnostics.push_back(variable::Object {{
240
                                    /// \todo У нас нет информации о месте использования файла. 
241
                                    {u"range",      makeRange({0,0},{1,0})},
242
                                    {u"severity",   makeSeverity(m.level)},
243
                                    {u"source",     u"SIMODO/loom"},
244
                                    {u"message",    u"Problems in the '" + inout::toU16(m.location.uri()) + u"' file: " + makeMessage(m)},
245
                                }});
246
                processed_files.insert(m.location.uri());
247
            }
248
    }
249

250
    std::shared_ptr<variable::Object> res = std::make_shared<variable::Object>();
251

252
    /// @todo _parser?
253
    res->variables().push_back({u"uri",         inout::toU16(_uri)});
254
    /// @todo _parser?
255
    res->variables().push_back({u"version",     _version});
256
    res->variables().push_back({u"diagnostics", variable::Array {diagnostics}});
257

258
    return res;
259
}
260

261
std::shared_ptr<variable::Object> 
262
    DocumentContext::makeRange(const inout::Location & loc)
263
{
264
    return makeRange({loc.range().start().line(),loc.range().start().character()},
265
                     {loc.range().end().line(),loc.range().end().character()});
266
}
267

268
std::shared_ptr<variable::Object> 
269
    DocumentContext::makeRange(std::pair<int64_t, int64_t> start, std::pair<int64_t, int64_t> end)
270
{
271
    std::shared_ptr<variable::Object> res = std::make_shared<variable::Object>();
272

273
    res->variables().push_back({u"start", makePosition(start.first,start.second)});
274
    res->variables().push_back({u"end", makePosition(end.first, end.second)});
275

276
    return res;
277
}
278

279
std::shared_ptr<variable::Object> 
280
    DocumentContext::makeRange(const inout::Range & range)
281
{
282
    std::shared_ptr<variable::Object> res = std::make_shared<variable::Object>();
283

284
    res->variables().push_back({u"start", makePosition(range.start().line(), range.start().character())});
285
    res->variables().push_back({u"end", makePosition(range.end().line(), range.end().character())});
286

287
    return res;
288
}
289

290
std::shared_ptr<simodo::variable::Object> 
291
    DocumentContext::makePosition(int64_t line, int64_t character)
292
{
293
    std::shared_ptr<variable::Object> res = std::make_shared<variable::Object>();
294

295
    res->variables().push_back({u"line", line});
296
    res->variables().push_back({u"character", character});
297

298
    return res;
299
}                            
300

301
int64_t DocumentContext::makeSeverity(inout::SeverityLevel level)
302
{
303
    int64_t res;
304
    switch(level)
305
    {
306
    case inout::SeverityLevel::Fatal:
307
    case inout::SeverityLevel::Error:
308
        res = 1; // Error 
309
        break;
310
    case inout::SeverityLevel::Warning:
311
        res = 2; // Warning 
312
        break;
313
    default:   
314
        res = 3; // Information 
315
        break;
316
    }
317
    return res;
318
}
319

320
std::u16string DocumentContext::makeMessage(const MessageFullContent & message)
321
{
322
    std::u16string res {inout::toU16(message.briefly)};
323

324
    if (!message.atlarge.empty())
325
        res += u'\n' + inout::toU16(message.atlarge);
326

327
    return inout::encodeSpecialChars(res);
328
}
329

330
std::string DocumentContext::convertUriToPath(const std::string & uri)
331
{
332
    return uri;
333
}
334

335
}

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

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

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

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