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