1//===--- Protocol.cpp - Language Server Protocol Implementation -----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains the serialization code for the LSP structs.
10//
11//===----------------------------------------------------------------------===//
12
13#include "mlir/Tools/lsp-server-support/Protocol.h"
14#include "mlir/Tools/lsp-server-support/Logging.h"
15#include "llvm/ADT/Hashing.h"
16#include "llvm/ADT/SmallString.h"
17#include "llvm/ADT/StringExtras.h"
18#include "llvm/ADT/StringSet.h"
19#include "llvm/Support/ErrorHandling.h"
20#include "llvm/Support/Format.h"
21#include "llvm/Support/FormatVariadic.h"
22#include "llvm/Support/JSON.h"
23#include "llvm/Support/MemoryBuffer.h"
24#include "llvm/Support/Path.h"
25#include "llvm/Support/raw_ostream.h"
26
27using namespace mlir;
28using namespace mlir::lsp;
29
30// Helper that doesn't treat `null` and absent fields as failures.
31template <typename T>
32static bool mapOptOrNull(const llvm::json::Value &params,
33 llvm::StringLiteral prop, T &out,
34 llvm::json::Path path) {
35 const llvm::json::Object *o = params.getAsObject();
36 assert(o);
37
38 // Field is missing or null.
39 auto *v = o->get(K: prop);
40 if (!v || v->getAsNull())
41 return true;
42 return fromJSON(*v, out, path.field(Field: prop));
43}
44
45//===----------------------------------------------------------------------===//
46// LSPError
47//===----------------------------------------------------------------------===//
48
49char LSPError::ID;
50
51//===----------------------------------------------------------------------===//
52// URIForFile
53//===----------------------------------------------------------------------===//
54
55static bool isWindowsPath(StringRef path) {
56 return path.size() > 1 && llvm::isAlpha(C: path[0]) && path[1] == ':';
57}
58
59static bool isNetworkPath(StringRef path) {
60 return path.size() > 2 && path[0] == path[1] &&
61 llvm::sys::path::is_separator(value: path[0]);
62}
63
64static bool shouldEscapeInURI(unsigned char c) {
65 // Unreserved characters.
66 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
67 (c >= '0' && c <= '9'))
68 return false;
69
70 switch (c) {
71 case '-':
72 case '_':
73 case '.':
74 case '~':
75 // '/' is only reserved when parsing.
76 case '/':
77 // ':' is only reserved for relative URI paths, which we doesn't produce.
78 case ':':
79 return false;
80 }
81 return true;
82}
83
84/// Encodes a string according to percent-encoding.
85/// - Unreserved characters are not escaped.
86/// - Reserved characters always escaped with exceptions like '/'.
87/// - All other characters are escaped.
88static void percentEncode(StringRef content, std::string &out) {
89 for (unsigned char c : content) {
90 if (shouldEscapeInURI(c)) {
91 out.push_back(c: '%');
92 out.push_back(c: llvm::hexdigit(X: c / 16));
93 out.push_back(c: llvm::hexdigit(X: c % 16));
94 } else {
95 out.push_back(c: c);
96 }
97 }
98}
99
100/// Decodes a string according to percent-encoding.
101static std::string percentDecode(StringRef content) {
102 std::string result;
103 for (auto i = content.begin(), e = content.end(); i != e; ++i) {
104 if (*i != '%') {
105 result += *i;
106 continue;
107 }
108 if (*i == '%' && i + 2 < content.end() && llvm::isHexDigit(C: *(i + 1)) &&
109 llvm::isHexDigit(C: *(i + 2))) {
110 result.push_back(c: llvm::hexFromNibbles(MSB: *(i + 1), LSB: *(i + 2)));
111 i += 2;
112 } else {
113 result.push_back(c: *i);
114 }
115 }
116 return result;
117}
118
119/// Return the set containing the supported URI schemes.
120static StringSet<> &getSupportedSchemes() {
121 static StringSet<> schemes({"file", "test"});
122 return schemes;
123}
124
125/// Returns true if the given scheme is structurally valid, i.e. it does not
126/// contain any invalid scheme characters. This does not check that the scheme
127/// is actually supported.
128static bool isStructurallyValidScheme(StringRef scheme) {
129 if (scheme.empty())
130 return false;
131 if (!llvm::isAlpha(C: scheme[0]))
132 return false;
133 return llvm::all_of(Range: llvm::drop_begin(RangeOrContainer&: scheme), P: [](char c) {
134 return llvm::isAlnum(C: c) || c == '+' || c == '.' || c == '-';
135 });
136}
137
138static llvm::Expected<std::string> uriFromAbsolutePath(StringRef absolutePath,
139 StringRef scheme) {
140 std::string body;
141 StringRef authority;
142 StringRef root = llvm::sys::path::root_name(path: absolutePath);
143 if (isNetworkPath(path: root)) {
144 // Windows UNC paths e.g. \\server\share => file://server/share
145 authority = root.drop_front(N: 2);
146 absolutePath.consume_front(Prefix: root);
147 } else if (isWindowsPath(path: root)) {
148 // Windows paths e.g. X:\path => file:///X:/path
149 body = "/";
150 }
151 body += llvm::sys::path::convert_to_slash(path: absolutePath);
152
153 std::string uri = scheme.str() + ":";
154 if (authority.empty() && body.empty())
155 return uri;
156
157 // If authority if empty, we only print body if it starts with "/"; otherwise,
158 // the URI is invalid.
159 if (!authority.empty() || StringRef(body).starts_with(Prefix: "/")) {
160 uri.append(s: "//");
161 percentEncode(content: authority, out&: uri);
162 }
163 percentEncode(content: body, out&: uri);
164 return uri;
165}
166
167static llvm::Expected<std::string> getAbsolutePath(StringRef authority,
168 StringRef body) {
169 if (!body.starts_with(Prefix: "/"))
170 return llvm::createStringError(
171 EC: llvm::inconvertibleErrorCode(),
172 S: "File scheme: expect body to be an absolute path starting "
173 "with '/': " +
174 body);
175 SmallString<128> path;
176 if (!authority.empty()) {
177 // Windows UNC paths e.g. file://server/share => \\server\share
178 ("//" + authority).toVector(Out&: path);
179 } else if (isWindowsPath(path: body.substr(Start: 1))) {
180 // Windows paths e.g. file:///X:/path => X:\path
181 body.consume_front(Prefix: "/");
182 }
183 path.append(RHS: body);
184 llvm::sys::path::native(path);
185 return std::string(path);
186}
187
188static llvm::Expected<std::string> parseFilePathFromURI(StringRef origUri) {
189 StringRef uri = origUri;
190
191 // Decode the scheme of the URI.
192 size_t pos = uri.find(C: ':');
193 if (pos == StringRef::npos)
194 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
195 S: "Scheme must be provided in URI: " +
196 origUri);
197 StringRef schemeStr = uri.substr(Start: 0, N: pos);
198 std::string uriScheme = percentDecode(content: schemeStr);
199 if (!isStructurallyValidScheme(scheme: uriScheme))
200 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
201 S: "Invalid scheme: " + schemeStr +
202 " (decoded: " + uriScheme + ")");
203 uri = uri.substr(Start: pos + 1);
204
205 // Decode the authority of the URI.
206 std::string uriAuthority;
207 if (uri.consume_front(Prefix: "//")) {
208 pos = uri.find(C: '/');
209 uriAuthority = percentDecode(content: uri.substr(Start: 0, N: pos));
210 uri = uri.substr(Start: pos);
211 }
212
213 // Decode the body of the URI.
214 std::string uriBody = percentDecode(content: uri);
215
216 // Compute the absolute path for this uri.
217 if (!getSupportedSchemes().contains(key: uriScheme)) {
218 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
219 S: "unsupported URI scheme `" + uriScheme +
220 "' for workspace files");
221 }
222 return getAbsolutePath(authority: uriAuthority, body: uriBody);
223}
224
225llvm::Expected<URIForFile> URIForFile::fromURI(StringRef uri) {
226 llvm::Expected<std::string> filePath = parseFilePathFromURI(origUri: uri);
227 if (!filePath)
228 return filePath.takeError();
229 return URIForFile(std::move(*filePath), uri.str());
230}
231
232llvm::Expected<URIForFile> URIForFile::fromFile(StringRef absoluteFilepath,
233 StringRef scheme) {
234 llvm::Expected<std::string> uri =
235 uriFromAbsolutePath(absolutePath: absoluteFilepath, scheme);
236 if (!uri)
237 return uri.takeError();
238 return fromURI(uri: *uri);
239}
240
241StringRef URIForFile::scheme() const { return uri().split(Separator: ':').first; }
242
243void URIForFile::registerSupportedScheme(StringRef scheme) {
244 getSupportedSchemes().insert(key: scheme);
245}
246
247bool mlir::lsp::fromJSON(const llvm::json::Value &value, URIForFile &result,
248 llvm::json::Path path) {
249 if (std::optional<StringRef> str = value.getAsString()) {
250 llvm::Expected<URIForFile> expectedURI = URIForFile::fromURI(uri: *str);
251 if (!expectedURI) {
252 path.report(Message: "unresolvable URI");
253 consumeError(Err: expectedURI.takeError());
254 return false;
255 }
256 result = std::move(*expectedURI);
257 return true;
258 }
259 return false;
260}
261
262llvm::json::Value mlir::lsp::toJSON(const URIForFile &value) {
263 return value.uri();
264}
265
266raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const URIForFile &value) {
267 return os << value.uri();
268}
269
270//===----------------------------------------------------------------------===//
271// ClientCapabilities
272//===----------------------------------------------------------------------===//
273
274bool mlir::lsp::fromJSON(const llvm::json::Value &value,
275 ClientCapabilities &result, llvm::json::Path path) {
276 const llvm::json::Object *o = value.getAsObject();
277 if (!o) {
278 path.report(Message: "expected object");
279 return false;
280 }
281 if (const llvm::json::Object *textDocument = o->getObject(K: "textDocument")) {
282 if (const llvm::json::Object *documentSymbol =
283 textDocument->getObject(K: "documentSymbol")) {
284 if (std::optional<bool> hierarchicalSupport =
285 documentSymbol->getBoolean(K: "hierarchicalDocumentSymbolSupport"))
286 result.hierarchicalDocumentSymbol = *hierarchicalSupport;
287 }
288 if (auto *codeAction = textDocument->getObject(K: "codeAction")) {
289 if (codeAction->getObject(K: "codeActionLiteralSupport"))
290 result.codeActionStructure = true;
291 }
292 if (auto *window = textDocument->getObject(K: "window")) {
293 if (std::optional<bool> workDoneProgressSupport =
294 window->getBoolean(K: "workDoneProgress"))
295 result.workDoneProgress = *workDoneProgressSupport;
296 }
297 }
298 return true;
299}
300
301//===----------------------------------------------------------------------===//
302// ClientInfo
303//===----------------------------------------------------------------------===//
304
305bool mlir::lsp::fromJSON(const llvm::json::Value &value, ClientInfo &result,
306 llvm::json::Path path) {
307 llvm::json::ObjectMapper o(value, path);
308 if (!o || !o.map(Prop: "name", Out&: result.name))
309 return false;
310
311 // Don't fail if we can't parse version.
312 o.map(Prop: "version", Out&: result.version);
313 return true;
314}
315
316//===----------------------------------------------------------------------===//
317// InitializeParams
318//===----------------------------------------------------------------------===//
319
320bool mlir::lsp::fromJSON(const llvm::json::Value &value, TraceLevel &result,
321 llvm::json::Path path) {
322 if (std::optional<StringRef> str = value.getAsString()) {
323 if (*str == "off") {
324 result = TraceLevel::Off;
325 return true;
326 }
327 if (*str == "messages") {
328 result = TraceLevel::Messages;
329 return true;
330 }
331 if (*str == "verbose") {
332 result = TraceLevel::Verbose;
333 return true;
334 }
335 }
336 return false;
337}
338
339bool mlir::lsp::fromJSON(const llvm::json::Value &value,
340 InitializeParams &result, llvm::json::Path path) {
341 llvm::json::ObjectMapper o(value, path);
342 if (!o)
343 return false;
344 // We deliberately don't fail if we can't parse individual fields.
345 o.map(Prop: "capabilities", Out&: result.capabilities);
346 o.map(Prop: "trace", Out&: result.trace);
347 mapOptOrNull(params: value, prop: "clientInfo", out&: result.clientInfo, path);
348
349 return true;
350}
351
352//===----------------------------------------------------------------------===//
353// TextDocumentItem
354//===----------------------------------------------------------------------===//
355
356bool mlir::lsp::fromJSON(const llvm::json::Value &value,
357 TextDocumentItem &result, llvm::json::Path path) {
358 llvm::json::ObjectMapper o(value, path);
359 return o && o.map(Prop: "uri", Out&: result.uri) &&
360 o.map(Prop: "languageId", Out&: result.languageId) && o.map(Prop: "text", Out&: result.text) &&
361 o.map(Prop: "version", Out&: result.version);
362}
363
364//===----------------------------------------------------------------------===//
365// TextDocumentIdentifier
366//===----------------------------------------------------------------------===//
367
368llvm::json::Value mlir::lsp::toJSON(const TextDocumentIdentifier &value) {
369 return llvm::json::Object{{.K: "uri", .V: value.uri}};
370}
371
372bool mlir::lsp::fromJSON(const llvm::json::Value &value,
373 TextDocumentIdentifier &result,
374 llvm::json::Path path) {
375 llvm::json::ObjectMapper o(value, path);
376 return o && o.map(Prop: "uri", Out&: result.uri);
377}
378
379//===----------------------------------------------------------------------===//
380// VersionedTextDocumentIdentifier
381//===----------------------------------------------------------------------===//
382
383llvm::json::Value
384mlir::lsp::toJSON(const VersionedTextDocumentIdentifier &value) {
385 return llvm::json::Object{
386 {.K: "uri", .V: value.uri},
387 {.K: "version", .V: value.version},
388 };
389}
390
391bool mlir::lsp::fromJSON(const llvm::json::Value &value,
392 VersionedTextDocumentIdentifier &result,
393 llvm::json::Path path) {
394 llvm::json::ObjectMapper o(value, path);
395 return o && o.map(Prop: "uri", Out&: result.uri) && o.map(Prop: "version", Out&: result.version);
396}
397
398//===----------------------------------------------------------------------===//
399// Position
400//===----------------------------------------------------------------------===//
401
402bool mlir::lsp::fromJSON(const llvm::json::Value &value, Position &result,
403 llvm::json::Path path) {
404 llvm::json::ObjectMapper o(value, path);
405 return o && o.map(Prop: "line", Out&: result.line) &&
406 o.map(Prop: "character", Out&: result.character);
407}
408
409llvm::json::Value mlir::lsp::toJSON(const Position &value) {
410 return llvm::json::Object{
411 {.K: "line", .V: value.line},
412 {.K: "character", .V: value.character},
413 };
414}
415
416raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Position &value) {
417 return os << value.line << ':' << value.character;
418}
419
420//===----------------------------------------------------------------------===//
421// Range
422//===----------------------------------------------------------------------===//
423
424bool mlir::lsp::fromJSON(const llvm::json::Value &value, Range &result,
425 llvm::json::Path path) {
426 llvm::json::ObjectMapper o(value, path);
427 return o && o.map(Prop: "start", Out&: result.start) && o.map(Prop: "end", Out&: result.end);
428}
429
430llvm::json::Value mlir::lsp::toJSON(const Range &value) {
431 return llvm::json::Object{
432 {.K: "start", .V: value.start},
433 {.K: "end", .V: value.end},
434 };
435}
436
437raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Range &value) {
438 return os << value.start << '-' << value.end;
439}
440
441//===----------------------------------------------------------------------===//
442// Location
443//===----------------------------------------------------------------------===//
444
445bool mlir::lsp::fromJSON(const llvm::json::Value &value, Location &result,
446 llvm::json::Path path) {
447 llvm::json::ObjectMapper o(value, path);
448 return o && o.map(Prop: "uri", Out&: result.uri) && o.map(Prop: "range", Out&: result.range);
449}
450
451llvm::json::Value mlir::lsp::toJSON(const Location &value) {
452 return llvm::json::Object{
453 {.K: "uri", .V: value.uri},
454 {.K: "range", .V: value.range},
455 };
456}
457
458raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Location &value) {
459 return os << value.range << '@' << value.uri;
460}
461
462//===----------------------------------------------------------------------===//
463// TextDocumentPositionParams
464//===----------------------------------------------------------------------===//
465
466bool mlir::lsp::fromJSON(const llvm::json::Value &value,
467 TextDocumentPositionParams &result,
468 llvm::json::Path path) {
469 llvm::json::ObjectMapper o(value, path);
470 return o && o.map(Prop: "textDocument", Out&: result.textDocument) &&
471 o.map(Prop: "position", Out&: result.position);
472}
473
474//===----------------------------------------------------------------------===//
475// ReferenceParams
476//===----------------------------------------------------------------------===//
477
478bool mlir::lsp::fromJSON(const llvm::json::Value &value,
479 ReferenceContext &result, llvm::json::Path path) {
480 llvm::json::ObjectMapper o(value, path);
481 return o && o.mapOptional(Prop: "includeDeclaration", Out&: result.includeDeclaration);
482}
483
484bool mlir::lsp::fromJSON(const llvm::json::Value &value,
485 ReferenceParams &result, llvm::json::Path path) {
486 TextDocumentPositionParams &base = result;
487 llvm::json::ObjectMapper o(value, path);
488 return fromJSON(value, result&: base, path) && o &&
489 o.mapOptional(Prop: "context", Out&: result.context);
490}
491
492//===----------------------------------------------------------------------===//
493// DidOpenTextDocumentParams
494//===----------------------------------------------------------------------===//
495
496bool mlir::lsp::fromJSON(const llvm::json::Value &value,
497 DidOpenTextDocumentParams &result,
498 llvm::json::Path path) {
499 llvm::json::ObjectMapper o(value, path);
500 return o && o.map(Prop: "textDocument", Out&: result.textDocument);
501}
502
503//===----------------------------------------------------------------------===//
504// DidCloseTextDocumentParams
505//===----------------------------------------------------------------------===//
506
507bool mlir::lsp::fromJSON(const llvm::json::Value &value,
508 DidCloseTextDocumentParams &result,
509 llvm::json::Path path) {
510 llvm::json::ObjectMapper o(value, path);
511 return o && o.map(Prop: "textDocument", Out&: result.textDocument);
512}
513
514//===----------------------------------------------------------------------===//
515// DidChangeTextDocumentParams
516//===----------------------------------------------------------------------===//
517
518LogicalResult
519TextDocumentContentChangeEvent::applyTo(std::string &contents) const {
520 // If there is no range, the full document changed.
521 if (!range) {
522 contents = text;
523 return success();
524 }
525
526 // Try to map the replacement range to the content.
527 llvm::SourceMgr tmpScrMgr;
528 tmpScrMgr.AddNewSourceBuffer(F: llvm::MemoryBuffer::getMemBuffer(InputData: contents),
529 IncludeLoc: SMLoc());
530 SMRange rangeLoc = range->getAsSMRange(mgr&: tmpScrMgr);
531 if (!rangeLoc.isValid())
532 return failure();
533
534 contents.replace(pos: rangeLoc.Start.getPointer() - contents.data(),
535 n: rangeLoc.End.getPointer() - rangeLoc.Start.getPointer(),
536 str: text);
537 return success();
538}
539
540LogicalResult TextDocumentContentChangeEvent::applyTo(
541 ArrayRef<TextDocumentContentChangeEvent> changes, std::string &contents) {
542 for (const auto &change : changes)
543 if (failed(Result: change.applyTo(contents)))
544 return failure();
545 return success();
546}
547
548bool mlir::lsp::fromJSON(const llvm::json::Value &value,
549 TextDocumentContentChangeEvent &result,
550 llvm::json::Path path) {
551 llvm::json::ObjectMapper o(value, path);
552 return o && o.map(Prop: "range", Out&: result.range) &&
553 o.map(Prop: "rangeLength", Out&: result.rangeLength) && o.map(Prop: "text", Out&: result.text);
554}
555
556bool mlir::lsp::fromJSON(const llvm::json::Value &value,
557 DidChangeTextDocumentParams &result,
558 llvm::json::Path path) {
559 llvm::json::ObjectMapper o(value, path);
560 return o && o.map(Prop: "textDocument", Out&: result.textDocument) &&
561 o.map(Prop: "contentChanges", Out&: result.contentChanges);
562}
563
564//===----------------------------------------------------------------------===//
565// MarkupContent
566//===----------------------------------------------------------------------===//
567
568static llvm::StringRef toTextKind(MarkupKind kind) {
569 switch (kind) {
570 case MarkupKind::PlainText:
571 return "plaintext";
572 case MarkupKind::Markdown:
573 return "markdown";
574 }
575 llvm_unreachable("Invalid MarkupKind");
576}
577
578raw_ostream &mlir::lsp::operator<<(raw_ostream &os, MarkupKind kind) {
579 return os << toTextKind(kind);
580}
581
582llvm::json::Value mlir::lsp::toJSON(const MarkupContent &mc) {
583 if (mc.value.empty())
584 return nullptr;
585
586 return llvm::json::Object{
587 {.K: "kind", .V: toTextKind(kind: mc.kind)},
588 {.K: "value", .V: mc.value},
589 };
590}
591
592//===----------------------------------------------------------------------===//
593// Hover
594//===----------------------------------------------------------------------===//
595
596llvm::json::Value mlir::lsp::toJSON(const Hover &hover) {
597 llvm::json::Object result{{.K: "contents", .V: toJSON(mc: hover.contents)}};
598 if (hover.range)
599 result["range"] = toJSON(value: *hover.range);
600 return std::move(result);
601}
602
603//===----------------------------------------------------------------------===//
604// DocumentSymbol
605//===----------------------------------------------------------------------===//
606
607llvm::json::Value mlir::lsp::toJSON(const DocumentSymbol &symbol) {
608 llvm::json::Object result{{.K: "name", .V: symbol.name},
609 {.K: "kind", .V: static_cast<int>(symbol.kind)},
610 {.K: "range", .V: symbol.range},
611 {.K: "selectionRange", .V: symbol.selectionRange}};
612
613 if (!symbol.detail.empty())
614 result["detail"] = symbol.detail;
615 if (!symbol.children.empty())
616 result["children"] = symbol.children;
617 return std::move(result);
618}
619
620//===----------------------------------------------------------------------===//
621// DocumentSymbolParams
622//===----------------------------------------------------------------------===//
623
624bool mlir::lsp::fromJSON(const llvm::json::Value &value,
625 DocumentSymbolParams &result, llvm::json::Path path) {
626 llvm::json::ObjectMapper o(value, path);
627 return o && o.map(Prop: "textDocument", Out&: result.textDocument);
628}
629
630//===----------------------------------------------------------------------===//
631// DiagnosticRelatedInformation
632//===----------------------------------------------------------------------===//
633
634bool mlir::lsp::fromJSON(const llvm::json::Value &value,
635 DiagnosticRelatedInformation &result,
636 llvm::json::Path path) {
637 llvm::json::ObjectMapper o(value, path);
638 return o && o.map(Prop: "location", Out&: result.location) &&
639 o.map(Prop: "message", Out&: result.message);
640}
641
642llvm::json::Value mlir::lsp::toJSON(const DiagnosticRelatedInformation &info) {
643 return llvm::json::Object{
644 {.K: "location", .V: info.location},
645 {.K: "message", .V: info.message},
646 };
647}
648
649//===----------------------------------------------------------------------===//
650// Diagnostic
651//===----------------------------------------------------------------------===//
652
653llvm::json::Value mlir::lsp::toJSON(DiagnosticTag tag) {
654 return static_cast<int>(tag);
655}
656
657bool mlir::lsp::fromJSON(const llvm::json::Value &value, DiagnosticTag &result,
658 llvm::json::Path path) {
659 if (std::optional<int64_t> i = value.getAsInteger()) {
660 result = (DiagnosticTag)*i;
661 return true;
662 }
663
664 return false;
665}
666
667llvm::json::Value mlir::lsp::toJSON(const Diagnostic &diag) {
668 llvm::json::Object result{
669 {.K: "range", .V: diag.range},
670 {.K: "severity", .V: (int)diag.severity},
671 {.K: "message", .V: diag.message},
672 };
673 if (diag.category)
674 result["category"] = *diag.category;
675 if (!diag.source.empty())
676 result["source"] = diag.source;
677 if (diag.relatedInformation)
678 result["relatedInformation"] = *diag.relatedInformation;
679 if (!diag.tags.empty())
680 result["tags"] = diag.tags;
681 return std::move(result);
682}
683
684bool mlir::lsp::fromJSON(const llvm::json::Value &value, Diagnostic &result,
685 llvm::json::Path path) {
686 llvm::json::ObjectMapper o(value, path);
687 if (!o)
688 return false;
689 int severity = 0;
690 if (!mapOptOrNull(params: value, prop: "severity", out&: severity, path))
691 return false;
692 result.severity = (DiagnosticSeverity)severity;
693
694 return o.map(Prop: "range", Out&: result.range) && o.map(Prop: "message", Out&: result.message) &&
695 mapOptOrNull(params: value, prop: "category", out&: result.category, path) &&
696 mapOptOrNull(params: value, prop: "source", out&: result.source, path) &&
697 mapOptOrNull(params: value, prop: "relatedInformation", out&: result.relatedInformation,
698 path) &&
699 mapOptOrNull(params: value, prop: "tags", out&: result.tags, path);
700}
701
702//===----------------------------------------------------------------------===//
703// PublishDiagnosticsParams
704//===----------------------------------------------------------------------===//
705
706llvm::json::Value mlir::lsp::toJSON(const PublishDiagnosticsParams &params) {
707 return llvm::json::Object{
708 {.K: "uri", .V: params.uri},
709 {.K: "diagnostics", .V: params.diagnostics},
710 {.K: "version", .V: params.version},
711 };
712}
713
714//===----------------------------------------------------------------------===//
715// TextEdit
716//===----------------------------------------------------------------------===//
717
718bool mlir::lsp::fromJSON(const llvm::json::Value &value, TextEdit &result,
719 llvm::json::Path path) {
720 llvm::json::ObjectMapper o(value, path);
721 return o && o.map(Prop: "range", Out&: result.range) && o.map(Prop: "newText", Out&: result.newText);
722}
723
724llvm::json::Value mlir::lsp::toJSON(const TextEdit &value) {
725 return llvm::json::Object{
726 {.K: "range", .V: value.range},
727 {.K: "newText", .V: value.newText},
728 };
729}
730
731raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const TextEdit &value) {
732 os << value.range << " => \"";
733 llvm::printEscapedString(Name: value.newText, Out&: os);
734 return os << '"';
735}
736
737//===----------------------------------------------------------------------===//
738// CompletionItemKind
739//===----------------------------------------------------------------------===//
740
741bool mlir::lsp::fromJSON(const llvm::json::Value &value,
742 CompletionItemKind &result, llvm::json::Path path) {
743 if (std::optional<int64_t> intValue = value.getAsInteger()) {
744 if (*intValue < static_cast<int>(CompletionItemKind::Text) ||
745 *intValue > static_cast<int>(CompletionItemKind::TypeParameter))
746 return false;
747 result = static_cast<CompletionItemKind>(*intValue);
748 return true;
749 }
750 return false;
751}
752
753CompletionItemKind mlir::lsp::adjustKindToCapability(
754 CompletionItemKind kind,
755 CompletionItemKindBitset &supportedCompletionItemKinds) {
756 size_t kindVal = static_cast<size_t>(kind);
757 if (kindVal >= kCompletionItemKindMin &&
758 kindVal <= supportedCompletionItemKinds.size() &&
759 supportedCompletionItemKinds[kindVal])
760 return kind;
761
762 // Provide some fall backs for common kinds that are close enough.
763 switch (kind) {
764 case CompletionItemKind::Folder:
765 return CompletionItemKind::File;
766 case CompletionItemKind::EnumMember:
767 return CompletionItemKind::Enum;
768 case CompletionItemKind::Struct:
769 return CompletionItemKind::Class;
770 default:
771 return CompletionItemKind::Text;
772 }
773}
774
775bool mlir::lsp::fromJSON(const llvm::json::Value &value,
776 CompletionItemKindBitset &result,
777 llvm::json::Path path) {
778 if (const llvm::json::Array *arrayValue = value.getAsArray()) {
779 for (size_t i = 0, e = arrayValue->size(); i < e; ++i) {
780 CompletionItemKind kindOut;
781 if (fromJSON(value: (*arrayValue)[i], result&: kindOut, path: path.index(Index: i)))
782 result.set(position: size_t(kindOut));
783 }
784 return true;
785 }
786 return false;
787}
788
789//===----------------------------------------------------------------------===//
790// CompletionItem
791//===----------------------------------------------------------------------===//
792
793llvm::json::Value mlir::lsp::toJSON(const CompletionItem &value) {
794 assert(!value.label.empty() && "completion item label is required");
795 llvm::json::Object result{{.K: "label", .V: value.label}};
796 if (value.kind != CompletionItemKind::Missing)
797 result["kind"] = static_cast<int>(value.kind);
798 if (!value.detail.empty())
799 result["detail"] = value.detail;
800 if (value.documentation)
801 result["documentation"] = value.documentation;
802 if (!value.sortText.empty())
803 result["sortText"] = value.sortText;
804 if (!value.filterText.empty())
805 result["filterText"] = value.filterText;
806 if (!value.insertText.empty())
807 result["insertText"] = value.insertText;
808 if (value.insertTextFormat != InsertTextFormat::Missing)
809 result["insertTextFormat"] = static_cast<int>(value.insertTextFormat);
810 if (value.textEdit)
811 result["textEdit"] = *value.textEdit;
812 if (!value.additionalTextEdits.empty()) {
813 result["additionalTextEdits"] =
814 llvm::json::Array(value.additionalTextEdits);
815 }
816 if (value.deprecated)
817 result["deprecated"] = value.deprecated;
818 return std::move(result);
819}
820
821raw_ostream &mlir::lsp::operator<<(raw_ostream &os,
822 const CompletionItem &value) {
823 return os << value.label << " - " << toJSON(value);
824}
825
826bool mlir::lsp::operator<(const CompletionItem &lhs,
827 const CompletionItem &rhs) {
828 return (lhs.sortText.empty() ? lhs.label : lhs.sortText) <
829 (rhs.sortText.empty() ? rhs.label : rhs.sortText);
830}
831
832//===----------------------------------------------------------------------===//
833// CompletionList
834//===----------------------------------------------------------------------===//
835
836llvm::json::Value mlir::lsp::toJSON(const CompletionList &value) {
837 return llvm::json::Object{
838 {.K: "isIncomplete", .V: value.isIncomplete},
839 {.K: "items", .V: llvm::json::Array(value.items)},
840 };
841}
842
843//===----------------------------------------------------------------------===//
844// CompletionContext
845//===----------------------------------------------------------------------===//
846
847bool mlir::lsp::fromJSON(const llvm::json::Value &value,
848 CompletionContext &result, llvm::json::Path path) {
849 llvm::json::ObjectMapper o(value, path);
850 int triggerKind;
851 if (!o || !o.map(Prop: "triggerKind", Out&: triggerKind) ||
852 !mapOptOrNull(params: value, prop: "triggerCharacter", out&: result.triggerCharacter, path))
853 return false;
854 result.triggerKind = static_cast<CompletionTriggerKind>(triggerKind);
855 return true;
856}
857
858//===----------------------------------------------------------------------===//
859// CompletionParams
860//===----------------------------------------------------------------------===//
861
862bool mlir::lsp::fromJSON(const llvm::json::Value &value,
863 CompletionParams &result, llvm::json::Path path) {
864 if (!fromJSON(value, result&: static_cast<TextDocumentPositionParams &>(result), path))
865 return false;
866 if (const llvm::json::Value *context = value.getAsObject()->get(K: "context"))
867 return fromJSON(value: *context, result&: result.context, path: path.field(Field: "context"));
868 return true;
869}
870
871//===----------------------------------------------------------------------===//
872// ParameterInformation
873//===----------------------------------------------------------------------===//
874
875llvm::json::Value mlir::lsp::toJSON(const ParameterInformation &value) {
876 assert((value.labelOffsets || !value.labelString.empty()) &&
877 "parameter information label is required");
878 llvm::json::Object result;
879 if (value.labelOffsets)
880 result["label"] = llvm::json::Array(
881 {value.labelOffsets->first, value.labelOffsets->second});
882 else
883 result["label"] = value.labelString;
884 if (!value.documentation.empty())
885 result["documentation"] = value.documentation;
886 return std::move(result);
887}
888
889//===----------------------------------------------------------------------===//
890// SignatureInformation
891//===----------------------------------------------------------------------===//
892
893llvm::json::Value mlir::lsp::toJSON(const SignatureInformation &value) {
894 assert(!value.label.empty() && "signature information label is required");
895 llvm::json::Object result{
896 {.K: "label", .V: value.label},
897 {.K: "parameters", .V: llvm::json::Array(value.parameters)},
898 };
899 if (!value.documentation.empty())
900 result["documentation"] = value.documentation;
901 return std::move(result);
902}
903
904raw_ostream &mlir::lsp::operator<<(raw_ostream &os,
905 const SignatureInformation &value) {
906 return os << value.label << " - " << toJSON(value);
907}
908
909//===----------------------------------------------------------------------===//
910// SignatureHelp
911//===----------------------------------------------------------------------===//
912
913llvm::json::Value mlir::lsp::toJSON(const SignatureHelp &value) {
914 assert(value.activeSignature >= 0 &&
915 "Unexpected negative value for number of active signatures.");
916 assert(value.activeParameter >= 0 &&
917 "Unexpected negative value for active parameter index");
918 return llvm::json::Object{
919 {.K: "activeSignature", .V: value.activeSignature},
920 {.K: "activeParameter", .V: value.activeParameter},
921 {.K: "signatures", .V: llvm::json::Array(value.signatures)},
922 };
923}
924
925//===----------------------------------------------------------------------===//
926// DocumentLinkParams
927//===----------------------------------------------------------------------===//
928
929bool mlir::lsp::fromJSON(const llvm::json::Value &value,
930 DocumentLinkParams &result, llvm::json::Path path) {
931 llvm::json::ObjectMapper o(value, path);
932 return o && o.map(Prop: "textDocument", Out&: result.textDocument);
933}
934
935//===----------------------------------------------------------------------===//
936// DocumentLink
937//===----------------------------------------------------------------------===//
938
939llvm::json::Value mlir::lsp::toJSON(const DocumentLink &value) {
940 return llvm::json::Object{
941 {.K: "range", .V: value.range},
942 {.K: "target", .V: value.target},
943 };
944}
945
946//===----------------------------------------------------------------------===//
947// InlayHintsParams
948//===----------------------------------------------------------------------===//
949
950bool mlir::lsp::fromJSON(const llvm::json::Value &value,
951 InlayHintsParams &result, llvm::json::Path path) {
952 llvm::json::ObjectMapper o(value, path);
953 return o && o.map(Prop: "textDocument", Out&: result.textDocument) &&
954 o.map(Prop: "range", Out&: result.range);
955}
956
957//===----------------------------------------------------------------------===//
958// InlayHint
959//===----------------------------------------------------------------------===//
960
961llvm::json::Value mlir::lsp::toJSON(const InlayHint &value) {
962 return llvm::json::Object{{.K: "position", .V: value.position},
963 {.K: "kind", .V: (int)value.kind},
964 {.K: "label", .V: value.label},
965 {.K: "paddingLeft", .V: value.paddingLeft},
966 {.K: "paddingRight", .V: value.paddingRight}};
967}
968bool mlir::lsp::operator==(const InlayHint &lhs, const InlayHint &rhs) {
969 return std::tie(args: lhs.position, args: lhs.kind, args: lhs.label) ==
970 std::tie(args: rhs.position, args: rhs.kind, args: rhs.label);
971}
972bool mlir::lsp::operator<(const InlayHint &lhs, const InlayHint &rhs) {
973 return std::tie(args: lhs.position, args: lhs.kind, args: lhs.label) <
974 std::tie(args: rhs.position, args: rhs.kind, args: rhs.label);
975}
976
977llvm::raw_ostream &mlir::lsp::operator<<(llvm::raw_ostream &os,
978 InlayHintKind value) {
979 switch (value) {
980 case InlayHintKind::Parameter:
981 return os << "parameter";
982 case InlayHintKind::Type:
983 return os << "type";
984 }
985 llvm_unreachable("Unknown InlayHintKind");
986}
987
988//===----------------------------------------------------------------------===//
989// CodeActionContext
990//===----------------------------------------------------------------------===//
991
992bool mlir::lsp::fromJSON(const llvm::json::Value &value,
993 CodeActionContext &result, llvm::json::Path path) {
994 llvm::json::ObjectMapper o(value, path);
995 if (!o || !o.map(Prop: "diagnostics", Out&: result.diagnostics))
996 return false;
997 o.map(Prop: "only", Out&: result.only);
998 return true;
999}
1000
1001//===----------------------------------------------------------------------===//
1002// CodeActionParams
1003//===----------------------------------------------------------------------===//
1004
1005bool mlir::lsp::fromJSON(const llvm::json::Value &value,
1006 CodeActionParams &result, llvm::json::Path path) {
1007 llvm::json::ObjectMapper o(value, path);
1008 return o && o.map(Prop: "textDocument", Out&: result.textDocument) &&
1009 o.map(Prop: "range", Out&: result.range) && o.map(Prop: "context", Out&: result.context);
1010}
1011
1012//===----------------------------------------------------------------------===//
1013// WorkspaceEdit
1014//===----------------------------------------------------------------------===//
1015
1016bool mlir::lsp::fromJSON(const llvm::json::Value &value, WorkspaceEdit &result,
1017 llvm::json::Path path) {
1018 llvm::json::ObjectMapper o(value, path);
1019 return o && o.map(Prop: "changes", Out&: result.changes);
1020}
1021
1022llvm::json::Value mlir::lsp::toJSON(const WorkspaceEdit &value) {
1023 llvm::json::Object fileChanges;
1024 for (auto &change : value.changes)
1025 fileChanges[change.first] = llvm::json::Array(change.second);
1026 return llvm::json::Object{{.K: "changes", .V: std::move(fileChanges)}};
1027}
1028
1029//===----------------------------------------------------------------------===//
1030// CodeAction
1031//===----------------------------------------------------------------------===//
1032
1033const llvm::StringLiteral CodeAction::kQuickFix = "quickfix";
1034const llvm::StringLiteral CodeAction::kRefactor = "refactor";
1035const llvm::StringLiteral CodeAction::kInfo = "info";
1036
1037llvm::json::Value mlir::lsp::toJSON(const CodeAction &value) {
1038 llvm::json::Object codeAction{{.K: "title", .V: value.title}};
1039 if (value.kind)
1040 codeAction["kind"] = *value.kind;
1041 if (value.diagnostics)
1042 codeAction["diagnostics"] = llvm::json::Array(*value.diagnostics);
1043 if (value.isPreferred)
1044 codeAction["isPreferred"] = true;
1045 if (value.edit)
1046 codeAction["edit"] = *value.edit;
1047 return std::move(codeAction);
1048}
1049

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of mlir/lib/Tools/lsp-server-support/Protocol.cpp