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