xref: /llvm-project/mlir/lib/Tools/lsp-server-support/Protocol.cpp (revision 6187178e832c36639f1dee4f718c76a90eb91fd0)
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/StringSwitch.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().hasValue())
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 static bool isValidScheme(StringRef scheme) {
120   if (scheme.empty())
121     return false;
122   if (!llvm::isAlpha(scheme[0]))
123     return false;
124   return std::all_of(scheme.begin() + 1, scheme.end(), [](char c) {
125     return llvm::isAlnum(c) || c == '+' || c == '.' || c == '-';
126   });
127 }
128 
129 static llvm::Expected<std::string> uriFromAbsolutePath(StringRef absolutePath) {
130   std::string body;
131   StringRef authority;
132   StringRef root = llvm::sys::path::root_name(absolutePath);
133   if (isNetworkPath(root)) {
134     // Windows UNC paths e.g. \\server\share => file://server/share
135     authority = root.drop_front(2);
136     absolutePath.consume_front(root);
137   } else if (isWindowsPath(root)) {
138     // Windows paths e.g. X:\path => file:///X:/path
139     body = "/";
140   }
141   body += llvm::sys::path::convert_to_slash(absolutePath);
142 
143   std::string uri = "file:";
144   if (authority.empty() && body.empty())
145     return uri;
146 
147   // If authority if empty, we only print body if it starts with "/"; otherwise,
148   // the URI is invalid.
149   if (!authority.empty() || StringRef(body).startswith("/")) {
150     uri.append("//");
151     percentEncode(authority, uri);
152   }
153   percentEncode(body, uri);
154   return uri;
155 }
156 
157 static llvm::Expected<std::string> getAbsolutePath(StringRef authority,
158                                                    StringRef body) {
159   if (!body.startswith("/"))
160     return llvm::createStringError(
161         llvm::inconvertibleErrorCode(),
162         "File scheme: expect body to be an absolute path starting "
163         "with '/': " +
164             body);
165   SmallString<128> path;
166   if (!authority.empty()) {
167     // Windows UNC paths e.g. file://server/share => \\server\share
168     ("//" + authority).toVector(path);
169   } else if (isWindowsPath(body.substr(1))) {
170     // Windows paths e.g. file:///X:/path => X:\path
171     body.consume_front("/");
172   }
173   path.append(body);
174   llvm::sys::path::native(path);
175   return std::string(path);
176 }
177 
178 static llvm::Expected<std::string> parseFilePathFromURI(StringRef origUri) {
179   StringRef uri = origUri;
180 
181   // Decode the scheme of the URI.
182   size_t pos = uri.find(':');
183   if (pos == StringRef::npos)
184     return llvm::createStringError(llvm::inconvertibleErrorCode(),
185                                    "Scheme must be provided in URI: " +
186                                        origUri);
187   StringRef schemeStr = uri.substr(0, pos);
188   std::string uriScheme = percentDecode(schemeStr);
189   if (!isValidScheme(uriScheme))
190     return llvm::createStringError(llvm::inconvertibleErrorCode(),
191                                    "Invalid scheme: " + schemeStr +
192                                        " (decoded: " + uriScheme + ")");
193   uri = uri.substr(pos + 1);
194 
195   // Decode the authority of the URI.
196   std::string uriAuthority;
197   if (uri.consume_front("//")) {
198     pos = uri.find('/');
199     uriAuthority = percentDecode(uri.substr(0, pos));
200     uri = uri.substr(pos);
201   }
202 
203   // Decode the body of the URI.
204   std::string uriBody = percentDecode(uri);
205 
206   // Compute the absolute path for this uri.
207   if (uriScheme != "file" && uriScheme != "test") {
208     return llvm::createStringError(
209         llvm::inconvertibleErrorCode(),
210         "mlir-lsp-server only supports 'file' URI scheme for workspace files");
211   }
212   return getAbsolutePath(uriAuthority, uriBody);
213 }
214 
215 llvm::Expected<URIForFile> URIForFile::fromURI(StringRef uri) {
216   llvm::Expected<std::string> filePath = parseFilePathFromURI(uri);
217   if (!filePath)
218     return filePath.takeError();
219   return URIForFile(std::move(*filePath), uri.str());
220 }
221 
222 llvm::Expected<URIForFile> URIForFile::fromFile(StringRef absoluteFilepath) {
223   llvm::Expected<std::string> uri = uriFromAbsolutePath(absoluteFilepath);
224   if (!uri)
225     return uri.takeError();
226   return fromURI(*uri);
227 }
228 
229 bool mlir::lsp::fromJSON(const llvm::json::Value &value, URIForFile &result,
230                          llvm::json::Path path) {
231   if (Optional<StringRef> str = value.getAsString()) {
232     llvm::Expected<URIForFile> expectedURI = URIForFile::fromURI(*str);
233     if (!expectedURI) {
234       path.report("unresolvable URI");
235       consumeError(expectedURI.takeError());
236       return false;
237     }
238     result = std::move(*expectedURI);
239     return true;
240   }
241   return false;
242 }
243 
244 llvm::json::Value mlir::lsp::toJSON(const URIForFile &value) {
245   return value.uri();
246 }
247 
248 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const URIForFile &value) {
249   return os << value.uri();
250 }
251 
252 //===----------------------------------------------------------------------===//
253 // ClientCapabilities
254 //===----------------------------------------------------------------------===//
255 
256 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
257                          ClientCapabilities &result, llvm::json::Path path) {
258   const llvm::json::Object *o = value.getAsObject();
259   if (!o) {
260     path.report("expected object");
261     return false;
262   }
263   if (const llvm::json::Object *textDocument = o->getObject("textDocument")) {
264     if (const llvm::json::Object *documentSymbol =
265             textDocument->getObject("documentSymbol")) {
266       if (Optional<bool> hierarchicalSupport =
267               documentSymbol->getBoolean("hierarchicalDocumentSymbolSupport"))
268         result.hierarchicalDocumentSymbol = *hierarchicalSupport;
269     }
270   }
271   return true;
272 }
273 
274 //===----------------------------------------------------------------------===//
275 // InitializeParams
276 //===----------------------------------------------------------------------===//
277 
278 bool mlir::lsp::fromJSON(const llvm::json::Value &value, TraceLevel &result,
279                          llvm::json::Path path) {
280   if (Optional<StringRef> str = value.getAsString()) {
281     if (*str == "off") {
282       result = TraceLevel::Off;
283       return true;
284     }
285     if (*str == "messages") {
286       result = TraceLevel::Messages;
287       return true;
288     }
289     if (*str == "verbose") {
290       result = TraceLevel::Verbose;
291       return true;
292     }
293   }
294   return false;
295 }
296 
297 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
298                          InitializeParams &result, llvm::json::Path path) {
299   llvm::json::ObjectMapper o(value, path);
300   if (!o)
301     return false;
302   // We deliberately don't fail if we can't parse individual fields.
303   o.map("capabilities", result.capabilities);
304   o.map("trace", result.trace);
305   return true;
306 }
307 
308 //===----------------------------------------------------------------------===//
309 // TextDocumentItem
310 //===----------------------------------------------------------------------===//
311 
312 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
313                          TextDocumentItem &result, llvm::json::Path path) {
314   llvm::json::ObjectMapper o(value, path);
315   return o && o.map("uri", result.uri) &&
316          o.map("languageId", result.languageId) && o.map("text", result.text) &&
317          o.map("version", result.version);
318 }
319 
320 //===----------------------------------------------------------------------===//
321 // TextDocumentIdentifier
322 //===----------------------------------------------------------------------===//
323 
324 llvm::json::Value mlir::lsp::toJSON(const TextDocumentIdentifier &value) {
325   return llvm::json::Object{{"uri", value.uri}};
326 }
327 
328 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
329                          TextDocumentIdentifier &result,
330                          llvm::json::Path path) {
331   llvm::json::ObjectMapper o(value, path);
332   return o && o.map("uri", result.uri);
333 }
334 
335 //===----------------------------------------------------------------------===//
336 // VersionedTextDocumentIdentifier
337 //===----------------------------------------------------------------------===//
338 
339 llvm::json::Value
340 mlir::lsp::toJSON(const VersionedTextDocumentIdentifier &value) {
341   return llvm::json::Object{
342       {"uri", value.uri},
343       {"version", value.version},
344   };
345 }
346 
347 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
348                          VersionedTextDocumentIdentifier &result,
349                          llvm::json::Path path) {
350   llvm::json::ObjectMapper o(value, path);
351   return o && o.map("uri", result.uri) && o.map("version", result.version);
352 }
353 
354 //===----------------------------------------------------------------------===//
355 // Position
356 //===----------------------------------------------------------------------===//
357 
358 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Position &result,
359                          llvm::json::Path path) {
360   llvm::json::ObjectMapper o(value, path);
361   return o && o.map("line", result.line) &&
362          o.map("character", result.character);
363 }
364 
365 llvm::json::Value mlir::lsp::toJSON(const Position &value) {
366   return llvm::json::Object{
367       {"line", value.line},
368       {"character", value.character},
369   };
370 }
371 
372 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Position &value) {
373   return os << value.line << ':' << value.character;
374 }
375 
376 //===----------------------------------------------------------------------===//
377 // Range
378 //===----------------------------------------------------------------------===//
379 
380 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Range &result,
381                          llvm::json::Path path) {
382   llvm::json::ObjectMapper o(value, path);
383   return o && o.map("start", result.start) && o.map("end", result.end);
384 }
385 
386 llvm::json::Value mlir::lsp::toJSON(const Range &value) {
387   return llvm::json::Object{
388       {"start", value.start},
389       {"end", value.end},
390   };
391 }
392 
393 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Range &value) {
394   return os << value.start << '-' << value.end;
395 }
396 
397 //===----------------------------------------------------------------------===//
398 // Location
399 //===----------------------------------------------------------------------===//
400 
401 llvm::json::Value mlir::lsp::toJSON(const Location &value) {
402   return llvm::json::Object{
403       {"uri", value.uri},
404       {"range", value.range},
405   };
406 }
407 
408 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Location &value) {
409   return os << value.range << '@' << value.uri;
410 }
411 
412 //===----------------------------------------------------------------------===//
413 // TextDocumentPositionParams
414 //===----------------------------------------------------------------------===//
415 
416 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
417                          TextDocumentPositionParams &result,
418                          llvm::json::Path path) {
419   llvm::json::ObjectMapper o(value, path);
420   return o && o.map("textDocument", result.textDocument) &&
421          o.map("position", result.position);
422 }
423 
424 //===----------------------------------------------------------------------===//
425 // ReferenceParams
426 //===----------------------------------------------------------------------===//
427 
428 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
429                          ReferenceContext &result, llvm::json::Path path) {
430   llvm::json::ObjectMapper o(value, path);
431   return o && o.mapOptional("includeDeclaration", result.includeDeclaration);
432 }
433 
434 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
435                          ReferenceParams &result, llvm::json::Path path) {
436   TextDocumentPositionParams &base = result;
437   llvm::json::ObjectMapper o(value, path);
438   return fromJSON(value, base, path) && o &&
439          o.mapOptional("context", result.context);
440 }
441 
442 //===----------------------------------------------------------------------===//
443 // DidOpenTextDocumentParams
444 //===----------------------------------------------------------------------===//
445 
446 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
447                          DidOpenTextDocumentParams &result,
448                          llvm::json::Path path) {
449   llvm::json::ObjectMapper o(value, path);
450   return o && o.map("textDocument", result.textDocument);
451 }
452 
453 //===----------------------------------------------------------------------===//
454 // DidCloseTextDocumentParams
455 //===----------------------------------------------------------------------===//
456 
457 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
458                          DidCloseTextDocumentParams &result,
459                          llvm::json::Path path) {
460   llvm::json::ObjectMapper o(value, path);
461   return o && o.map("textDocument", result.textDocument);
462 }
463 
464 //===----------------------------------------------------------------------===//
465 // DidChangeTextDocumentParams
466 //===----------------------------------------------------------------------===//
467 
468 LogicalResult
469 TextDocumentContentChangeEvent::applyTo(std::string &contents) const {
470   // If there is no range, the full document changed.
471   if (!range) {
472     contents = text;
473     return success();
474   }
475 
476   // Try to map the replacement range to the content.
477   llvm::SourceMgr tmpScrMgr;
478   tmpScrMgr.AddNewSourceBuffer(llvm::MemoryBuffer::getMemBuffer(contents),
479                                SMLoc());
480   SMRange rangeLoc = range->getAsSMRange(tmpScrMgr);
481   if (!rangeLoc.isValid())
482     return failure();
483 
484   contents.replace(rangeLoc.Start.getPointer() - contents.data(),
485                    rangeLoc.End.getPointer() - rangeLoc.Start.getPointer(),
486                    text);
487   return success();
488 }
489 
490 LogicalResult TextDocumentContentChangeEvent::applyTo(
491     ArrayRef<TextDocumentContentChangeEvent> changes, std::string &contents) {
492   for (const auto &change : changes)
493     if (failed(change.applyTo(contents)))
494       return failure();
495   return success();
496 }
497 
498 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
499                          TextDocumentContentChangeEvent &result,
500                          llvm::json::Path path) {
501   llvm::json::ObjectMapper o(value, path);
502   return o && o.map("range", result.range) &&
503          o.map("rangeLength", result.rangeLength) && o.map("text", result.text);
504 }
505 
506 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
507                          DidChangeTextDocumentParams &result,
508                          llvm::json::Path path) {
509   llvm::json::ObjectMapper o(value, path);
510   return o && o.map("textDocument", result.textDocument) &&
511          o.map("contentChanges", result.contentChanges);
512 }
513 
514 //===----------------------------------------------------------------------===//
515 // MarkupContent
516 //===----------------------------------------------------------------------===//
517 
518 static llvm::StringRef toTextKind(MarkupKind kind) {
519   switch (kind) {
520   case MarkupKind::PlainText:
521     return "plaintext";
522   case MarkupKind::Markdown:
523     return "markdown";
524   }
525   llvm_unreachable("Invalid MarkupKind");
526 }
527 
528 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, MarkupKind kind) {
529   return os << toTextKind(kind);
530 }
531 
532 llvm::json::Value mlir::lsp::toJSON(const MarkupContent &mc) {
533   if (mc.value.empty())
534     return nullptr;
535 
536   return llvm::json::Object{
537       {"kind", toTextKind(mc.kind)},
538       {"value", mc.value},
539   };
540 }
541 
542 //===----------------------------------------------------------------------===//
543 // Hover
544 //===----------------------------------------------------------------------===//
545 
546 llvm::json::Value mlir::lsp::toJSON(const Hover &hover) {
547   llvm::json::Object result{{"contents", toJSON(hover.contents)}};
548   if (hover.range.hasValue())
549     result["range"] = toJSON(*hover.range);
550   return std::move(result);
551 }
552 
553 //===----------------------------------------------------------------------===//
554 // DocumentSymbol
555 //===----------------------------------------------------------------------===//
556 
557 llvm::json::Value mlir::lsp::toJSON(const DocumentSymbol &symbol) {
558   llvm::json::Object result{{"name", symbol.name},
559                             {"kind", static_cast<int>(symbol.kind)},
560                             {"range", symbol.range},
561                             {"selectionRange", symbol.selectionRange}};
562 
563   if (!symbol.detail.empty())
564     result["detail"] = symbol.detail;
565   if (!symbol.children.empty())
566     result["children"] = symbol.children;
567   return std::move(result);
568 }
569 
570 //===----------------------------------------------------------------------===//
571 // DocumentSymbolParams
572 //===----------------------------------------------------------------------===//
573 
574 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
575                          DocumentSymbolParams &result, llvm::json::Path path) {
576   llvm::json::ObjectMapper o(value, path);
577   return o && o.map("textDocument", result.textDocument);
578 }
579 
580 //===----------------------------------------------------------------------===//
581 // DiagnosticRelatedInformation
582 //===----------------------------------------------------------------------===//
583 
584 llvm::json::Value mlir::lsp::toJSON(const DiagnosticRelatedInformation &info) {
585   return llvm::json::Object{
586       {"location", info.location},
587       {"message", info.message},
588   };
589 }
590 
591 //===----------------------------------------------------------------------===//
592 // Diagnostic
593 //===----------------------------------------------------------------------===//
594 
595 llvm::json::Value mlir::lsp::toJSON(const Diagnostic &diag) {
596   llvm::json::Object result{
597       {"range", diag.range},
598       {"severity", (int)diag.severity},
599       {"message", diag.message},
600   };
601   if (diag.category)
602     result["category"] = *diag.category;
603   if (!diag.source.empty())
604     result["source"] = diag.source;
605   if (diag.relatedInformation)
606     result["relatedInformation"] = *diag.relatedInformation;
607   return std::move(result);
608 }
609 
610 //===----------------------------------------------------------------------===//
611 // PublishDiagnosticsParams
612 //===----------------------------------------------------------------------===//
613 
614 llvm::json::Value mlir::lsp::toJSON(const PublishDiagnosticsParams &params) {
615   return llvm::json::Object{
616       {"uri", params.uri},
617       {"diagnostics", params.diagnostics},
618       {"version", params.version},
619   };
620 }
621 
622 //===----------------------------------------------------------------------===//
623 // TextEdit
624 //===----------------------------------------------------------------------===//
625 
626 bool mlir::lsp::fromJSON(const llvm::json::Value &value, TextEdit &result,
627                          llvm::json::Path path) {
628   llvm::json::ObjectMapper o(value, path);
629   return o && o.map("range", result.range) && o.map("newText", result.newText);
630 }
631 
632 llvm::json::Value mlir::lsp::toJSON(const TextEdit &value) {
633   return llvm::json::Object{
634       {"range", value.range},
635       {"newText", value.newText},
636   };
637 }
638 
639 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const TextEdit &value) {
640   os << value.range << " => \"";
641   llvm::printEscapedString(value.newText, os);
642   return os << '"';
643 }
644 
645 //===----------------------------------------------------------------------===//
646 // CompletionItemKind
647 //===----------------------------------------------------------------------===//
648 
649 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
650                          CompletionItemKind &result, llvm::json::Path path) {
651   if (Optional<int64_t> intValue = value.getAsInteger()) {
652     if (*intValue < static_cast<int>(CompletionItemKind::Text) ||
653         *intValue > static_cast<int>(CompletionItemKind::TypeParameter))
654       return false;
655     result = static_cast<CompletionItemKind>(*intValue);
656     return true;
657   }
658   return false;
659 }
660 
661 CompletionItemKind mlir::lsp::adjustKindToCapability(
662     CompletionItemKind kind,
663     CompletionItemKindBitset &supportedCompletionItemKinds) {
664   size_t kindVal = static_cast<size_t>(kind);
665   if (kindVal >= kCompletionItemKindMin &&
666       kindVal <= supportedCompletionItemKinds.size() &&
667       supportedCompletionItemKinds[kindVal])
668     return kind;
669 
670   // Provide some fall backs for common kinds that are close enough.
671   switch (kind) {
672   case CompletionItemKind::Folder:
673     return CompletionItemKind::File;
674   case CompletionItemKind::EnumMember:
675     return CompletionItemKind::Enum;
676   case CompletionItemKind::Struct:
677     return CompletionItemKind::Class;
678   default:
679     return CompletionItemKind::Text;
680   }
681 }
682 
683 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
684                          CompletionItemKindBitset &result,
685                          llvm::json::Path path) {
686   if (const llvm::json::Array *arrayValue = value.getAsArray()) {
687     for (size_t i = 0, e = arrayValue->size(); i < e; ++i) {
688       CompletionItemKind kindOut;
689       if (fromJSON((*arrayValue)[i], kindOut, path.index(i)))
690         result.set(size_t(kindOut));
691     }
692     return true;
693   }
694   return false;
695 }
696 
697 //===----------------------------------------------------------------------===//
698 // CompletionItem
699 //===----------------------------------------------------------------------===//
700 
701 llvm::json::Value mlir::lsp::toJSON(const CompletionItem &value) {
702   assert(!value.label.empty() && "completion item label is required");
703   llvm::json::Object result{{"label", value.label}};
704   if (value.kind != CompletionItemKind::Missing)
705     result["kind"] = static_cast<int>(value.kind);
706   if (!value.detail.empty())
707     result["detail"] = value.detail;
708   if (value.documentation)
709     result["documentation"] = value.documentation;
710   if (!value.sortText.empty())
711     result["sortText"] = value.sortText;
712   if (!value.filterText.empty())
713     result["filterText"] = value.filterText;
714   if (!value.insertText.empty())
715     result["insertText"] = value.insertText;
716   if (value.insertTextFormat != InsertTextFormat::Missing)
717     result["insertTextFormat"] = static_cast<int>(value.insertTextFormat);
718   if (value.textEdit)
719     result["textEdit"] = *value.textEdit;
720   if (!value.additionalTextEdits.empty()) {
721     result["additionalTextEdits"] =
722         llvm::json::Array(value.additionalTextEdits);
723   }
724   if (value.deprecated)
725     result["deprecated"] = value.deprecated;
726   return std::move(result);
727 }
728 
729 raw_ostream &mlir::lsp::operator<<(raw_ostream &os,
730                                    const CompletionItem &value) {
731   return os << value.label << " - " << toJSON(value);
732 }
733 
734 bool mlir::lsp::operator<(const CompletionItem &lhs,
735                           const CompletionItem &rhs) {
736   return (lhs.sortText.empty() ? lhs.label : lhs.sortText) <
737          (rhs.sortText.empty() ? rhs.label : rhs.sortText);
738 }
739 
740 //===----------------------------------------------------------------------===//
741 // CompletionList
742 //===----------------------------------------------------------------------===//
743 
744 llvm::json::Value mlir::lsp::toJSON(const CompletionList &value) {
745   return llvm::json::Object{
746       {"isIncomplete", value.isIncomplete},
747       {"items", llvm::json::Array(value.items)},
748   };
749 }
750 
751 //===----------------------------------------------------------------------===//
752 // CompletionContext
753 //===----------------------------------------------------------------------===//
754 
755 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
756                          CompletionContext &result, llvm::json::Path path) {
757   llvm::json::ObjectMapper o(value, path);
758   int triggerKind;
759   if (!o || !o.map("triggerKind", triggerKind) ||
760       !mapOptOrNull(value, "triggerCharacter", result.triggerCharacter, path))
761     return false;
762   result.triggerKind = static_cast<CompletionTriggerKind>(triggerKind);
763   return true;
764 }
765 
766 //===----------------------------------------------------------------------===//
767 // CompletionParams
768 //===----------------------------------------------------------------------===//
769 
770 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
771                          CompletionParams &result, llvm::json::Path path) {
772   if (!fromJSON(value, static_cast<TextDocumentPositionParams &>(result), path))
773     return false;
774   if (const llvm::json::Value *context = value.getAsObject()->get("context"))
775     return fromJSON(*context, result.context, path.field("context"));
776   return true;
777 }
778 
779 //===----------------------------------------------------------------------===//
780 // ParameterInformation
781 //===----------------------------------------------------------------------===//
782 
783 llvm::json::Value mlir::lsp::toJSON(const ParameterInformation &value) {
784   assert((value.labelOffsets.hasValue() || !value.labelString.empty()) &&
785          "parameter information label is required");
786   llvm::json::Object result;
787   if (value.labelOffsets)
788     result["label"] = llvm::json::Array(
789         {value.labelOffsets->first, value.labelOffsets->second});
790   else
791     result["label"] = value.labelString;
792   if (!value.documentation.empty())
793     result["documentation"] = value.documentation;
794   return std::move(result);
795 }
796 
797 //===----------------------------------------------------------------------===//
798 // SignatureInformation
799 //===----------------------------------------------------------------------===//
800 
801 llvm::json::Value mlir::lsp::toJSON(const SignatureInformation &value) {
802   assert(!value.label.empty() && "signature information label is required");
803   llvm::json::Object result{
804       {"label", value.label},
805       {"parameters", llvm::json::Array(value.parameters)},
806   };
807   if (!value.documentation.empty())
808     result["documentation"] = value.documentation;
809   return std::move(result);
810 }
811 
812 raw_ostream &mlir::lsp::operator<<(raw_ostream &os,
813                                    const SignatureInformation &value) {
814   return os << value.label << " - " << toJSON(value);
815 }
816 
817 //===----------------------------------------------------------------------===//
818 // SignatureHelp
819 //===----------------------------------------------------------------------===//
820 
821 llvm::json::Value mlir::lsp::toJSON(const SignatureHelp &value) {
822   assert(value.activeSignature >= 0 &&
823          "Unexpected negative value for number of active signatures.");
824   assert(value.activeParameter >= 0 &&
825          "Unexpected negative value for active parameter index");
826   return llvm::json::Object{
827       {"activeSignature", value.activeSignature},
828       {"activeParameter", value.activeParameter},
829       {"signatures", llvm::json::Array(value.signatures)},
830   };
831 }
832 
833 //===----------------------------------------------------------------------===//
834 // DocumentLinkParams
835 //===----------------------------------------------------------------------===//
836 
837 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
838                          DocumentLinkParams &result, llvm::json::Path path) {
839   llvm::json::ObjectMapper o(value, path);
840   return o && o.map("textDocument", result.textDocument);
841 }
842 
843 //===----------------------------------------------------------------------===//
844 // DocumentLink
845 //===----------------------------------------------------------------------===//
846 
847 llvm::json::Value mlir::lsp::toJSON(const DocumentLink &value) {
848   return llvm::json::Object{
849       {"range", value.range},
850       {"target", value.target},
851   };
852 }
853