xref: /llvm-project/mlir/lib/Tools/lsp-server-support/Protocol.cpp (revision f7b8a70e7a1738e0fc6574e3cf8faa4fa1f34eba)
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 "Protocol.h"
14 #include "Logging.h"
15 #include "mlir/Support/LogicalResult.h"
16 #include "llvm/ADT/Hashing.h"
17 #include "llvm/ADT/SmallString.h"
18 #include "llvm/ADT/StringSet.h"
19 #include "llvm/ADT/StringSwitch.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).startswith("/")) {
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.startswith("/"))
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 (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 (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 // InitializeParams
299 //===----------------------------------------------------------------------===//
300 
301 bool mlir::lsp::fromJSON(const llvm::json::Value &value, TraceLevel &result,
302                          llvm::json::Path path) {
303   if (Optional<StringRef> str = value.getAsString()) {
304     if (*str == "off") {
305       result = TraceLevel::Off;
306       return true;
307     }
308     if (*str == "messages") {
309       result = TraceLevel::Messages;
310       return true;
311     }
312     if (*str == "verbose") {
313       result = TraceLevel::Verbose;
314       return true;
315     }
316   }
317   return false;
318 }
319 
320 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
321                          InitializeParams &result, llvm::json::Path path) {
322   llvm::json::ObjectMapper o(value, path);
323   if (!o)
324     return false;
325   // We deliberately don't fail if we can't parse individual fields.
326   o.map("capabilities", result.capabilities);
327   o.map("trace", result.trace);
328   return true;
329 }
330 
331 //===----------------------------------------------------------------------===//
332 // TextDocumentItem
333 //===----------------------------------------------------------------------===//
334 
335 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
336                          TextDocumentItem &result, llvm::json::Path path) {
337   llvm::json::ObjectMapper o(value, path);
338   return o && o.map("uri", result.uri) &&
339          o.map("languageId", result.languageId) && o.map("text", result.text) &&
340          o.map("version", result.version);
341 }
342 
343 //===----------------------------------------------------------------------===//
344 // TextDocumentIdentifier
345 //===----------------------------------------------------------------------===//
346 
347 llvm::json::Value mlir::lsp::toJSON(const TextDocumentIdentifier &value) {
348   return llvm::json::Object{{"uri", value.uri}};
349 }
350 
351 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
352                          TextDocumentIdentifier &result,
353                          llvm::json::Path path) {
354   llvm::json::ObjectMapper o(value, path);
355   return o && o.map("uri", result.uri);
356 }
357 
358 //===----------------------------------------------------------------------===//
359 // VersionedTextDocumentIdentifier
360 //===----------------------------------------------------------------------===//
361 
362 llvm::json::Value
363 mlir::lsp::toJSON(const VersionedTextDocumentIdentifier &value) {
364   return llvm::json::Object{
365       {"uri", value.uri},
366       {"version", value.version},
367   };
368 }
369 
370 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
371                          VersionedTextDocumentIdentifier &result,
372                          llvm::json::Path path) {
373   llvm::json::ObjectMapper o(value, path);
374   return o && o.map("uri", result.uri) && o.map("version", result.version);
375 }
376 
377 //===----------------------------------------------------------------------===//
378 // Position
379 //===----------------------------------------------------------------------===//
380 
381 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Position &result,
382                          llvm::json::Path path) {
383   llvm::json::ObjectMapper o(value, path);
384   return o && o.map("line", result.line) &&
385          o.map("character", result.character);
386 }
387 
388 llvm::json::Value mlir::lsp::toJSON(const Position &value) {
389   return llvm::json::Object{
390       {"line", value.line},
391       {"character", value.character},
392   };
393 }
394 
395 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Position &value) {
396   return os << value.line << ':' << value.character;
397 }
398 
399 //===----------------------------------------------------------------------===//
400 // Range
401 //===----------------------------------------------------------------------===//
402 
403 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Range &result,
404                          llvm::json::Path path) {
405   llvm::json::ObjectMapper o(value, path);
406   return o && o.map("start", result.start) && o.map("end", result.end);
407 }
408 
409 llvm::json::Value mlir::lsp::toJSON(const Range &value) {
410   return llvm::json::Object{
411       {"start", value.start},
412       {"end", value.end},
413   };
414 }
415 
416 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Range &value) {
417   return os << value.start << '-' << value.end;
418 }
419 
420 //===----------------------------------------------------------------------===//
421 // Location
422 //===----------------------------------------------------------------------===//
423 
424 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Location &result,
425                          llvm::json::Path path) {
426   llvm::json::ObjectMapper o(value, path);
427   return o && o.map("uri", result.uri) && o.map("range", result.range);
428 }
429 
430 llvm::json::Value mlir::lsp::toJSON(const Location &value) {
431   return llvm::json::Object{
432       {"uri", value.uri},
433       {"range", value.range},
434   };
435 }
436 
437 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Location &value) {
438   return os << value.range << '@' << value.uri;
439 }
440 
441 //===----------------------------------------------------------------------===//
442 // TextDocumentPositionParams
443 //===----------------------------------------------------------------------===//
444 
445 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
446                          TextDocumentPositionParams &result,
447                          llvm::json::Path path) {
448   llvm::json::ObjectMapper o(value, path);
449   return o && o.map("textDocument", result.textDocument) &&
450          o.map("position", result.position);
451 }
452 
453 //===----------------------------------------------------------------------===//
454 // ReferenceParams
455 //===----------------------------------------------------------------------===//
456 
457 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
458                          ReferenceContext &result, llvm::json::Path path) {
459   llvm::json::ObjectMapper o(value, path);
460   return o && o.mapOptional("includeDeclaration", result.includeDeclaration);
461 }
462 
463 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
464                          ReferenceParams &result, llvm::json::Path path) {
465   TextDocumentPositionParams &base = result;
466   llvm::json::ObjectMapper o(value, path);
467   return fromJSON(value, base, path) && o &&
468          o.mapOptional("context", result.context);
469 }
470 
471 //===----------------------------------------------------------------------===//
472 // DidOpenTextDocumentParams
473 //===----------------------------------------------------------------------===//
474 
475 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
476                          DidOpenTextDocumentParams &result,
477                          llvm::json::Path path) {
478   llvm::json::ObjectMapper o(value, path);
479   return o && o.map("textDocument", result.textDocument);
480 }
481 
482 //===----------------------------------------------------------------------===//
483 // DidCloseTextDocumentParams
484 //===----------------------------------------------------------------------===//
485 
486 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
487                          DidCloseTextDocumentParams &result,
488                          llvm::json::Path path) {
489   llvm::json::ObjectMapper o(value, path);
490   return o && o.map("textDocument", result.textDocument);
491 }
492 
493 //===----------------------------------------------------------------------===//
494 // DidChangeTextDocumentParams
495 //===----------------------------------------------------------------------===//
496 
497 LogicalResult
498 TextDocumentContentChangeEvent::applyTo(std::string &contents) const {
499   // If there is no range, the full document changed.
500   if (!range) {
501     contents = text;
502     return success();
503   }
504 
505   // Try to map the replacement range to the content.
506   llvm::SourceMgr tmpScrMgr;
507   tmpScrMgr.AddNewSourceBuffer(llvm::MemoryBuffer::getMemBuffer(contents),
508                                SMLoc());
509   SMRange rangeLoc = range->getAsSMRange(tmpScrMgr);
510   if (!rangeLoc.isValid())
511     return failure();
512 
513   contents.replace(rangeLoc.Start.getPointer() - contents.data(),
514                    rangeLoc.End.getPointer() - rangeLoc.Start.getPointer(),
515                    text);
516   return success();
517 }
518 
519 LogicalResult TextDocumentContentChangeEvent::applyTo(
520     ArrayRef<TextDocumentContentChangeEvent> changes, std::string &contents) {
521   for (const auto &change : changes)
522     if (failed(change.applyTo(contents)))
523       return failure();
524   return success();
525 }
526 
527 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
528                          TextDocumentContentChangeEvent &result,
529                          llvm::json::Path path) {
530   llvm::json::ObjectMapper o(value, path);
531   return o && o.map("range", result.range) &&
532          o.map("rangeLength", result.rangeLength) && o.map("text", result.text);
533 }
534 
535 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
536                          DidChangeTextDocumentParams &result,
537                          llvm::json::Path path) {
538   llvm::json::ObjectMapper o(value, path);
539   return o && o.map("textDocument", result.textDocument) &&
540          o.map("contentChanges", result.contentChanges);
541 }
542 
543 //===----------------------------------------------------------------------===//
544 // MarkupContent
545 //===----------------------------------------------------------------------===//
546 
547 static llvm::StringRef toTextKind(MarkupKind kind) {
548   switch (kind) {
549   case MarkupKind::PlainText:
550     return "plaintext";
551   case MarkupKind::Markdown:
552     return "markdown";
553   }
554   llvm_unreachable("Invalid MarkupKind");
555 }
556 
557 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, MarkupKind kind) {
558   return os << toTextKind(kind);
559 }
560 
561 llvm::json::Value mlir::lsp::toJSON(const MarkupContent &mc) {
562   if (mc.value.empty())
563     return nullptr;
564 
565   return llvm::json::Object{
566       {"kind", toTextKind(mc.kind)},
567       {"value", mc.value},
568   };
569 }
570 
571 //===----------------------------------------------------------------------===//
572 // Hover
573 //===----------------------------------------------------------------------===//
574 
575 llvm::json::Value mlir::lsp::toJSON(const Hover &hover) {
576   llvm::json::Object result{{"contents", toJSON(hover.contents)}};
577   if (hover.range)
578     result["range"] = toJSON(*hover.range);
579   return std::move(result);
580 }
581 
582 //===----------------------------------------------------------------------===//
583 // DocumentSymbol
584 //===----------------------------------------------------------------------===//
585 
586 llvm::json::Value mlir::lsp::toJSON(const DocumentSymbol &symbol) {
587   llvm::json::Object result{{"name", symbol.name},
588                             {"kind", static_cast<int>(symbol.kind)},
589                             {"range", symbol.range},
590                             {"selectionRange", symbol.selectionRange}};
591 
592   if (!symbol.detail.empty())
593     result["detail"] = symbol.detail;
594   if (!symbol.children.empty())
595     result["children"] = symbol.children;
596   return std::move(result);
597 }
598 
599 //===----------------------------------------------------------------------===//
600 // DocumentSymbolParams
601 //===----------------------------------------------------------------------===//
602 
603 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
604                          DocumentSymbolParams &result, llvm::json::Path path) {
605   llvm::json::ObjectMapper o(value, path);
606   return o && o.map("textDocument", result.textDocument);
607 }
608 
609 //===----------------------------------------------------------------------===//
610 // DiagnosticRelatedInformation
611 //===----------------------------------------------------------------------===//
612 
613 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
614                          DiagnosticRelatedInformation &result,
615                          llvm::json::Path path) {
616   llvm::json::ObjectMapper o(value, path);
617   return o && o.map("location", result.location) &&
618          o.map("message", result.message);
619 }
620 
621 llvm::json::Value mlir::lsp::toJSON(const DiagnosticRelatedInformation &info) {
622   return llvm::json::Object{
623       {"location", info.location},
624       {"message", info.message},
625   };
626 }
627 
628 //===----------------------------------------------------------------------===//
629 // Diagnostic
630 //===----------------------------------------------------------------------===//
631 
632 llvm::json::Value mlir::lsp::toJSON(const Diagnostic &diag) {
633   llvm::json::Object result{
634       {"range", diag.range},
635       {"severity", (int)diag.severity},
636       {"message", diag.message},
637   };
638   if (diag.category)
639     result["category"] = *diag.category;
640   if (!diag.source.empty())
641     result["source"] = diag.source;
642   if (diag.relatedInformation)
643     result["relatedInformation"] = *diag.relatedInformation;
644   return std::move(result);
645 }
646 
647 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Diagnostic &result,
648                          llvm::json::Path path) {
649   llvm::json::ObjectMapper o(value, path);
650   if (!o)
651     return false;
652   int severity = 0;
653   if (!mapOptOrNull(value, "severity", severity, path))
654     return false;
655   result.severity = (DiagnosticSeverity)severity;
656 
657   return o.map("range", result.range) && o.map("message", result.message) &&
658          mapOptOrNull(value, "category", result.category, path) &&
659          mapOptOrNull(value, "source", result.source, path) &&
660          mapOptOrNull(value, "relatedInformation", result.relatedInformation,
661                       path);
662 }
663 
664 //===----------------------------------------------------------------------===//
665 // PublishDiagnosticsParams
666 //===----------------------------------------------------------------------===//
667 
668 llvm::json::Value mlir::lsp::toJSON(const PublishDiagnosticsParams &params) {
669   return llvm::json::Object{
670       {"uri", params.uri},
671       {"diagnostics", params.diagnostics},
672       {"version", params.version},
673   };
674 }
675 
676 //===----------------------------------------------------------------------===//
677 // TextEdit
678 //===----------------------------------------------------------------------===//
679 
680 bool mlir::lsp::fromJSON(const llvm::json::Value &value, TextEdit &result,
681                          llvm::json::Path path) {
682   llvm::json::ObjectMapper o(value, path);
683   return o && o.map("range", result.range) && o.map("newText", result.newText);
684 }
685 
686 llvm::json::Value mlir::lsp::toJSON(const TextEdit &value) {
687   return llvm::json::Object{
688       {"range", value.range},
689       {"newText", value.newText},
690   };
691 }
692 
693 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const TextEdit &value) {
694   os << value.range << " => \"";
695   llvm::printEscapedString(value.newText, os);
696   return os << '"';
697 }
698 
699 //===----------------------------------------------------------------------===//
700 // CompletionItemKind
701 //===----------------------------------------------------------------------===//
702 
703 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
704                          CompletionItemKind &result, llvm::json::Path path) {
705   if (Optional<int64_t> intValue = value.getAsInteger()) {
706     if (*intValue < static_cast<int>(CompletionItemKind::Text) ||
707         *intValue > static_cast<int>(CompletionItemKind::TypeParameter))
708       return false;
709     result = static_cast<CompletionItemKind>(*intValue);
710     return true;
711   }
712   return false;
713 }
714 
715 CompletionItemKind mlir::lsp::adjustKindToCapability(
716     CompletionItemKind kind,
717     CompletionItemKindBitset &supportedCompletionItemKinds) {
718   size_t kindVal = static_cast<size_t>(kind);
719   if (kindVal >= kCompletionItemKindMin &&
720       kindVal <= supportedCompletionItemKinds.size() &&
721       supportedCompletionItemKinds[kindVal])
722     return kind;
723 
724   // Provide some fall backs for common kinds that are close enough.
725   switch (kind) {
726   case CompletionItemKind::Folder:
727     return CompletionItemKind::File;
728   case CompletionItemKind::EnumMember:
729     return CompletionItemKind::Enum;
730   case CompletionItemKind::Struct:
731     return CompletionItemKind::Class;
732   default:
733     return CompletionItemKind::Text;
734   }
735 }
736 
737 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
738                          CompletionItemKindBitset &result,
739                          llvm::json::Path path) {
740   if (const llvm::json::Array *arrayValue = value.getAsArray()) {
741     for (size_t i = 0, e = arrayValue->size(); i < e; ++i) {
742       CompletionItemKind kindOut;
743       if (fromJSON((*arrayValue)[i], kindOut, path.index(i)))
744         result.set(size_t(kindOut));
745     }
746     return true;
747   }
748   return false;
749 }
750 
751 //===----------------------------------------------------------------------===//
752 // CompletionItem
753 //===----------------------------------------------------------------------===//
754 
755 llvm::json::Value mlir::lsp::toJSON(const CompletionItem &value) {
756   assert(!value.label.empty() && "completion item label is required");
757   llvm::json::Object result{{"label", value.label}};
758   if (value.kind != CompletionItemKind::Missing)
759     result["kind"] = static_cast<int>(value.kind);
760   if (!value.detail.empty())
761     result["detail"] = value.detail;
762   if (value.documentation)
763     result["documentation"] = value.documentation;
764   if (!value.sortText.empty())
765     result["sortText"] = value.sortText;
766   if (!value.filterText.empty())
767     result["filterText"] = value.filterText;
768   if (!value.insertText.empty())
769     result["insertText"] = value.insertText;
770   if (value.insertTextFormat != InsertTextFormat::Missing)
771     result["insertTextFormat"] = static_cast<int>(value.insertTextFormat);
772   if (value.textEdit)
773     result["textEdit"] = *value.textEdit;
774   if (!value.additionalTextEdits.empty()) {
775     result["additionalTextEdits"] =
776         llvm::json::Array(value.additionalTextEdits);
777   }
778   if (value.deprecated)
779     result["deprecated"] = value.deprecated;
780   return std::move(result);
781 }
782 
783 raw_ostream &mlir::lsp::operator<<(raw_ostream &os,
784                                    const CompletionItem &value) {
785   return os << value.label << " - " << toJSON(value);
786 }
787 
788 bool mlir::lsp::operator<(const CompletionItem &lhs,
789                           const CompletionItem &rhs) {
790   return (lhs.sortText.empty() ? lhs.label : lhs.sortText) <
791          (rhs.sortText.empty() ? rhs.label : rhs.sortText);
792 }
793 
794 //===----------------------------------------------------------------------===//
795 // CompletionList
796 //===----------------------------------------------------------------------===//
797 
798 llvm::json::Value mlir::lsp::toJSON(const CompletionList &value) {
799   return llvm::json::Object{
800       {"isIncomplete", value.isIncomplete},
801       {"items", llvm::json::Array(value.items)},
802   };
803 }
804 
805 //===----------------------------------------------------------------------===//
806 // CompletionContext
807 //===----------------------------------------------------------------------===//
808 
809 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
810                          CompletionContext &result, llvm::json::Path path) {
811   llvm::json::ObjectMapper o(value, path);
812   int triggerKind;
813   if (!o || !o.map("triggerKind", triggerKind) ||
814       !mapOptOrNull(value, "triggerCharacter", result.triggerCharacter, path))
815     return false;
816   result.triggerKind = static_cast<CompletionTriggerKind>(triggerKind);
817   return true;
818 }
819 
820 //===----------------------------------------------------------------------===//
821 // CompletionParams
822 //===----------------------------------------------------------------------===//
823 
824 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
825                          CompletionParams &result, llvm::json::Path path) {
826   if (!fromJSON(value, static_cast<TextDocumentPositionParams &>(result), path))
827     return false;
828   if (const llvm::json::Value *context = value.getAsObject()->get("context"))
829     return fromJSON(*context, result.context, path.field("context"));
830   return true;
831 }
832 
833 //===----------------------------------------------------------------------===//
834 // ParameterInformation
835 //===----------------------------------------------------------------------===//
836 
837 llvm::json::Value mlir::lsp::toJSON(const ParameterInformation &value) {
838   assert((value.labelOffsets || !value.labelString.empty()) &&
839          "parameter information label is required");
840   llvm::json::Object result;
841   if (value.labelOffsets)
842     result["label"] = llvm::json::Array(
843         {value.labelOffsets->first, value.labelOffsets->second});
844   else
845     result["label"] = value.labelString;
846   if (!value.documentation.empty())
847     result["documentation"] = value.documentation;
848   return std::move(result);
849 }
850 
851 //===----------------------------------------------------------------------===//
852 // SignatureInformation
853 //===----------------------------------------------------------------------===//
854 
855 llvm::json::Value mlir::lsp::toJSON(const SignatureInformation &value) {
856   assert(!value.label.empty() && "signature information label is required");
857   llvm::json::Object result{
858       {"label", value.label},
859       {"parameters", llvm::json::Array(value.parameters)},
860   };
861   if (!value.documentation.empty())
862     result["documentation"] = value.documentation;
863   return std::move(result);
864 }
865 
866 raw_ostream &mlir::lsp::operator<<(raw_ostream &os,
867                                    const SignatureInformation &value) {
868   return os << value.label << " - " << toJSON(value);
869 }
870 
871 //===----------------------------------------------------------------------===//
872 // SignatureHelp
873 //===----------------------------------------------------------------------===//
874 
875 llvm::json::Value mlir::lsp::toJSON(const SignatureHelp &value) {
876   assert(value.activeSignature >= 0 &&
877          "Unexpected negative value for number of active signatures.");
878   assert(value.activeParameter >= 0 &&
879          "Unexpected negative value for active parameter index");
880   return llvm::json::Object{
881       {"activeSignature", value.activeSignature},
882       {"activeParameter", value.activeParameter},
883       {"signatures", llvm::json::Array(value.signatures)},
884   };
885 }
886 
887 //===----------------------------------------------------------------------===//
888 // DocumentLinkParams
889 //===----------------------------------------------------------------------===//
890 
891 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
892                          DocumentLinkParams &result, llvm::json::Path path) {
893   llvm::json::ObjectMapper o(value, path);
894   return o && o.map("textDocument", result.textDocument);
895 }
896 
897 //===----------------------------------------------------------------------===//
898 // DocumentLink
899 //===----------------------------------------------------------------------===//
900 
901 llvm::json::Value mlir::lsp::toJSON(const DocumentLink &value) {
902   return llvm::json::Object{
903       {"range", value.range},
904       {"target", value.target},
905   };
906 }
907 
908 //===----------------------------------------------------------------------===//
909 // InlayHintsParams
910 //===----------------------------------------------------------------------===//
911 
912 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
913                          InlayHintsParams &result, llvm::json::Path path) {
914   llvm::json::ObjectMapper o(value, path);
915   return o && o.map("textDocument", result.textDocument) &&
916          o.map("range", result.range);
917 }
918 
919 //===----------------------------------------------------------------------===//
920 // InlayHint
921 //===----------------------------------------------------------------------===//
922 
923 llvm::json::Value mlir::lsp::toJSON(const InlayHint &value) {
924   return llvm::json::Object{{"position", value.position},
925                             {"kind", (int)value.kind},
926                             {"label", value.label},
927                             {"paddingLeft", value.paddingLeft},
928                             {"paddingRight", value.paddingRight}};
929 }
930 bool mlir::lsp::operator==(const InlayHint &lhs, const InlayHint &rhs) {
931   return std::tie(lhs.position, lhs.kind, lhs.label) ==
932          std::tie(rhs.position, rhs.kind, rhs.label);
933 }
934 bool mlir::lsp::operator<(const InlayHint &lhs, const InlayHint &rhs) {
935   return std::tie(lhs.position, lhs.kind, lhs.label) <
936          std::tie(rhs.position, rhs.kind, rhs.label);
937 }
938 
939 llvm::raw_ostream &mlir::lsp::operator<<(llvm::raw_ostream &os,
940                                          InlayHintKind value) {
941   switch (value) {
942   case InlayHintKind::Parameter:
943     return os << "parameter";
944   case InlayHintKind::Type:
945     return os << "type";
946   }
947   llvm_unreachable("Unknown InlayHintKind");
948 }
949 
950 //===----------------------------------------------------------------------===//
951 // CodeActionContext
952 //===----------------------------------------------------------------------===//
953 
954 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
955                          CodeActionContext &result, llvm::json::Path path) {
956   llvm::json::ObjectMapper o(value, path);
957   if (!o || !o.map("diagnostics", result.diagnostics))
958     return false;
959   o.map("only", result.only);
960   return true;
961 }
962 
963 //===----------------------------------------------------------------------===//
964 // CodeActionParams
965 //===----------------------------------------------------------------------===//
966 
967 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
968                          CodeActionParams &result, llvm::json::Path path) {
969   llvm::json::ObjectMapper o(value, path);
970   return o && o.map("textDocument", result.textDocument) &&
971          o.map("range", result.range) && o.map("context", result.context);
972 }
973 
974 //===----------------------------------------------------------------------===//
975 // WorkspaceEdit
976 //===----------------------------------------------------------------------===//
977 
978 bool mlir::lsp::fromJSON(const llvm::json::Value &value, WorkspaceEdit &result,
979                          llvm::json::Path path) {
980   llvm::json::ObjectMapper o(value, path);
981   return o && o.map("changes", result.changes);
982 }
983 
984 llvm::json::Value mlir::lsp::toJSON(const WorkspaceEdit &value) {
985   llvm::json::Object fileChanges;
986   for (auto &change : value.changes)
987     fileChanges[change.first] = llvm::json::Array(change.second);
988   return llvm::json::Object{{"changes", std::move(fileChanges)}};
989 }
990 
991 //===----------------------------------------------------------------------===//
992 // CodeAction
993 //===----------------------------------------------------------------------===//
994 
995 const llvm::StringLiteral CodeAction::kQuickFix = "quickfix";
996 const llvm::StringLiteral CodeAction::kRefactor = "refactor";
997 const llvm::StringLiteral CodeAction::kInfo = "info";
998 
999 llvm::json::Value mlir::lsp::toJSON(const CodeAction &value) {
1000   llvm::json::Object codeAction{{"title", value.title}};
1001   if (value.kind)
1002     codeAction["kind"] = *value.kind;
1003   if (value.diagnostics)
1004     codeAction["diagnostics"] = llvm::json::Array(*value.diagnostics);
1005   if (value.isPreferred)
1006     codeAction["isPreferred"] = true;
1007   if (value.edit)
1008     codeAction["edit"] = *value.edit;
1009   return std::move(codeAction);
1010 }
1011