loom
335 строк · 11.2 Кб
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/CompletionItemKind.h"
7
8#include <iostream>
9#include <filesystem>
10#include <future>
11#include <algorithm>
12
13namespace simodo::lsp
14{
15
16DocumentContext::DocumentContext(ServerContext & server,
17const std::string & languageId,
18const variable::Object & textDocument_object)
19: _server(server)
20, _op(server.document_operation_factory().create(*this,languageId))
21, _valid(false)
22{
23open(textDocument_object);
24}
25
26bool DocumentContext::open(const variable::Object & textDocument_object)
27{
28_valid = false;
29
30const variable::Value & uri_value = textDocument_object.find(u"uri");
31if (uri_value.type() != variable::ValueType::String)
32return false;
33
34const variable::Value & languageId_value = textDocument_object.find(u"languageId");
35if (languageId_value.type() != variable::ValueType::String)
36return false;
37
38const variable::Value & version_value = textDocument_object.find(u"version");
39if (version_value.type() != variable::ValueType::Int)
40return false;
41
42const variable::Value & text_value = textDocument_object.find(u"text");
43if (text_value.type() != variable::ValueType::String)
44return false;
45
46std::u16string text = inout::decodeSpecialChars(text_value.getString());
47
48std::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
56return _valid = _is_opened = true;
57}
58
59bool DocumentContext::change(const variable::Object & doc_params)
60{
61if (!_is_opened) {
62_server.log().error("DocumentContext::change: Document did not opened");
63return false;
64}
65
66const variable::Value & textDocument_value = doc_params.find(u"textDocument");
67if (textDocument_value.type() != variable::ValueType::Object) {
68_server.log().error("DocumentContext::change: Wrong params structure #1");
69return false;
70}
71
72std::shared_ptr<variable::Object> textDocument_object = textDocument_value.getObject();
73const variable::Value & version_value = textDocument_object->find(u"version");
74if (version_value.type() != variable::ValueType::Int) {
75_server.log().error("DocumentContext::change: Wrong params structure #2");
76return false;
77}
78
79/// @note Заготовка для распределённых вычислений + вероятность асинхронности при работе
80/// с пулом потоков. Не обращаем внимания на запоздавшие старые версии изменений.
81if (version_value.getInt() <= _version) {
82_server.log().warning("DocumentContext::change: Document versions don't match");
83return false;
84}
85
86const variable::Value & contentChanges_value = doc_params.find(u"contentChanges");
87if (contentChanges_value.type() != variable::ValueType::Array) {
88_server.log().error("DocumentContext::change: Wrong params structure #3");
89return false;
90}
91
92std::shared_ptr<variable::Array> contentChanges = contentChanges_value.getArray();
93if (contentChanges->values().size() != 1
94|| contentChanges->values()[0].type() != variable::ValueType::Object) {
95_server.log().error("DocumentContext::change: Wrong params structure #4");
96return false;
97}
98
99std::shared_ptr<variable::Object> contentChangesEvent = contentChanges->values()[0].getObject();
100const variable::Value & text_value = contentChangesEvent->find(u"text");
101if (text_value.type() != variable::ValueType::String) {
102_server.log().error("DocumentContext::change: Wrong params structure #5");
103return false;
104}
105
106std::u16string text = inout::decodeSpecialChars(text_value.getString());
107
108std::unique_lock<std::shared_timed_mutex> write_locker(_analyze_data_mutex);
109
110_text = text;
111_version = version_value.getInt();
112
113return true;
114}
115
116bool DocumentContext::close()
117{
118std::unique_lock<std::shared_timed_mutex> write_locker(_analyze_data_mutex);
119
120if (!_is_opened)
121return false;
122_is_opened = false;
123
124return true;
125}
126
127bool DocumentContext::analyze()
128{
129std::unique_lock<std::shared_timed_mutex> write_locker(_analyze_data_mutex);
130
131if (!_valid)
132return false;
133
134ReportCombiner m;
135
136_op->analyze(_text, m);
137
138_server.sending().push(
139variable::JsonRpc {u"textDocument/publishDiagnostics", makeDiagnosticParams(m.messages())} );
140
141return true;
142}
143
144void DocumentContext::copyContent(std::u16string & content) const
145{
146std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
147content = _text;
148}
149
150bool DocumentContext::checkDependency(const std::string & uri)
151{
152std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
153
154return _op->checkDependency(uri);
155}
156
157variable::Value DocumentContext::produceHoverResponse(const simodo::lsp::Position & pos)
158{
159std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
160
161return _op->produceHoverResponse(pos);
162}
163
164variable::Value DocumentContext::produceGotoDeclarationResponse(const simodo::lsp::Position & pos)
165{
166std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
167
168return _op->produceGotoDeclarationResponse(pos);
169}
170
171variable::Value DocumentContext::produceGotoDefinitionResponse(const simodo::lsp::Position & pos)
172{
173std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
174
175return _op->produceGotoDefinitionResponse(pos);
176}
177
178variable::Value DocumentContext::produceCompletionResponse(const simodo::lsp::CompletionParams & completionParams)
179{
180std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
181
182return _op->produceCompletionResponse(completionParams);
183}
184
185variable::Value DocumentContext::produceSemanticTokensResponse()
186{
187std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
188
189return _op->produceSemanticTokensResponse();
190}
191
192variable::Value DocumentContext::produceDocumentSymbolsResponse()
193{
194std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
195
196return _op->produceDocumentSymbolsResponse();
197}
198
199variable::Value DocumentContext::produceSimodoCommandResponse(const std::u16string & command_name)
200{
201std::shared_lock<std::shared_timed_mutex> read_locker(_analyze_data_mutex);
202
203if (!_valid)
204return false;
205
206return _op->produceSimodoCommandResponse(command_name, _text);
207}
208
209////////////////////////////////////////////////////////////////////////////////////
210// privates:
211
212variable::Value DocumentContext::makeDiagnosticParams(const std::vector<MessageFullContent> & message_set) const
213{
214std::vector<variable::Value> diagnostics;
215// std::u16string file_name_u16 = inout::toU16(_file_name);
216
217int i = 0;
218for(const MessageFullContent & m : message_set) {
219if (m.location.uri() == _file_name) {
220if (m.level >= inout::SeverityLevel::Error && i++ == 50) {
221/// @todo locker?
222_server.log().warning("Too many errors produced by analyze for '" + _uri + "'");
223break;
224}
225diagnostics.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
234if (diagnostics.empty() && !message_set.empty()) {
235std::set<std::string> processed_files;
236/// \note Это значит, что возможно были ошибки в других файлах, нужно об этом сказать!
237for(const MessageFullContent & m : message_set)
238if (m.level >= inout::SeverityLevel::Error && processed_files.find(m.location.uri()) == processed_files.end()) {
239diagnostics.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}});
246processed_files.insert(m.location.uri());
247}
248}
249
250std::shared_ptr<variable::Object> res = std::make_shared<variable::Object>();
251
252/// @todo _parser?
253res->add({u"uri", inout::toU16(_uri)});
254/// @todo _parser?
255res->add({u"version", _version});
256res->add({u"diagnostics", variable::Array {diagnostics}});
257
258return res;
259}
260
261std::shared_ptr<variable::Object>
262DocumentContext::makeRange(const inout::Location & loc)
263{
264return makeRange({loc.range().start().line(),loc.range().start().character()},
265{loc.range().end().line(),loc.range().end().character()});
266}
267
268std::shared_ptr<variable::Object>
269DocumentContext::makeRange(std::pair<int64_t, int64_t> start, std::pair<int64_t, int64_t> end)
270{
271std::shared_ptr<variable::Object> res = std::make_shared<variable::Object>();
272
273res->add({u"start", makePosition(start.first,start.second)});
274res->add({u"end", makePosition(end.first, end.second)});
275
276return res;
277}
278
279std::shared_ptr<variable::Object>
280DocumentContext::makeRange(const inout::Range & range)
281{
282std::shared_ptr<variable::Object> res = std::make_shared<variable::Object>();
283
284res->add({u"start", makePosition(range.start().line(), range.start().character())});
285res->add({u"end", makePosition(range.end().line(), range.end().character())});
286
287return res;
288}
289
290std::shared_ptr<simodo::variable::Object>
291DocumentContext::makePosition(int64_t line, int64_t character)
292{
293std::shared_ptr<variable::Object> res = std::make_shared<variable::Object>();
294
295res->add({u"line", line});
296res->add({u"character", character});
297
298return res;
299}
300
301int64_t DocumentContext::makeSeverity(inout::SeverityLevel level)
302{
303int64_t res;
304switch(level)
305{
306case inout::SeverityLevel::Fatal:
307case inout::SeverityLevel::Error:
308res = 1; // Error
309break;
310case inout::SeverityLevel::Warning:
311res = 2; // Warning
312break;
313default:
314res = 3; // Information
315break;
316}
317return res;
318}
319
320std::u16string DocumentContext::makeMessage(const MessageFullContent & message)
321{
322std::u16string res {inout::toU16(message.briefly)};
323
324if (!message.atlarge.empty())
325res += u'\n' + inout::toU16(message.atlarge);
326
327return inout::encodeSpecialChars(res);
328}
329
330std::string DocumentContext::convertUriToPath(const std::string & uri)
331{
332return uri;
333}
334
335}