xref: /llvm-project/mlir/lib/Tools/lsp-server-support/Protocol.cpp (revision 469c58944d4f0f990ca8ee88ebcc37624801a36c)
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 "llvm/ADT/Hashing.h"
15 #include "llvm/ADT/SmallString.h"
16 #include "llvm/ADT/StringSwitch.h"
17 #include "llvm/Support/ErrorHandling.h"
18 #include "llvm/Support/Format.h"
19 #include "llvm/Support/FormatVariadic.h"
20 #include "llvm/Support/JSON.h"
21 #include "llvm/Support/Path.h"
22 #include "llvm/Support/raw_ostream.h"
23 
24 using namespace mlir;
25 using namespace mlir::lsp;
26 
27 // Helper that doesn't treat `null` and absent fields as failures.
28 template <typename T>
29 static bool mapOptOrNull(const llvm::json::Value &params,
30                          llvm::StringLiteral prop, T &out,
31                          llvm::json::Path path) {
32   const llvm::json::Object *o = params.getAsObject();
33   assert(o);
34 
35   // Field is missing or null.
36   auto *v = o->get(prop);
37   if (!v || v->getAsNull().hasValue())
38     return true;
39   return fromJSON(*v, out, path.field(prop));
40 }
41 
42 //===----------------------------------------------------------------------===//
43 // LSPError
44 //===----------------------------------------------------------------------===//
45 
46 char LSPError::ID;
47 
48 //===----------------------------------------------------------------------===//
49 // URIForFile
50 //===----------------------------------------------------------------------===//
51 
52 static bool isWindowsPath(StringRef path) {
53   return path.size() > 1 && llvm::isAlpha(path[0]) && path[1] == ':';
54 }
55 
56 static bool isNetworkPath(StringRef path) {
57   return path.size() > 2 && path[0] == path[1] &&
58          llvm::sys::path::is_separator(path[0]);
59 }
60 
61 static bool shouldEscapeInURI(unsigned char c) {
62   // Unreserved characters.
63   if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
64       (c >= '0' && c <= '9'))
65     return false;
66 
67   switch (c) {
68   case '-':
69   case '_':
70   case '.':
71   case '~':
72   // '/' is only reserved when parsing.
73   case '/':
74   // ':' is only reserved for relative URI paths, which we doesn't produce.
75   case ':':
76     return false;
77   }
78   return true;
79 }
80 
81 /// Encodes a string according to percent-encoding.
82 /// - Unreserved characters are not escaped.
83 /// - Reserved characters always escaped with exceptions like '/'.
84 /// - All other characters are escaped.
85 static void percentEncode(StringRef content, std::string &out) {
86   for (unsigned char c : content) {
87     if (shouldEscapeInURI(c)) {
88       out.push_back('%');
89       out.push_back(llvm::hexdigit(c / 16));
90       out.push_back(llvm::hexdigit(c % 16));
91     } else {
92       out.push_back(c);
93     }
94   }
95 }
96 
97 /// Decodes a string according to percent-encoding.
98 static std::string percentDecode(StringRef content) {
99   std::string result;
100   for (auto i = content.begin(), e = content.end(); i != e; ++i) {
101     if (*i != '%') {
102       result += *i;
103       continue;
104     }
105     if (*i == '%' && i + 2 < content.end() && llvm::isHexDigit(*(i + 1)) &&
106         llvm::isHexDigit(*(i + 2))) {
107       result.push_back(llvm::hexFromNibbles(*(i + 1), *(i + 2)));
108       i += 2;
109     } else {
110       result.push_back(*i);
111     }
112   }
113   return result;
114 }
115 
116 static bool isValidScheme(StringRef scheme) {
117   if (scheme.empty())
118     return false;
119   if (!llvm::isAlpha(scheme[0]))
120     return false;
121   return std::all_of(scheme.begin() + 1, scheme.end(), [](char c) {
122     return llvm::isAlnum(c) || c == '+' || c == '.' || c == '-';
123   });
124 }
125 
126 static llvm::Expected<std::string> uriFromAbsolutePath(StringRef absolutePath) {
127   std::string body;
128   StringRef authority;
129   StringRef root = llvm::sys::path::root_name(absolutePath);
130   if (isNetworkPath(root)) {
131     // Windows UNC paths e.g. \\server\share => file://server/share
132     authority = root.drop_front(2);
133     absolutePath.consume_front(root);
134   } else if (isWindowsPath(root)) {
135     // Windows paths e.g. X:\path => file:///X:/path
136     body = "/";
137   }
138   body += llvm::sys::path::convert_to_slash(absolutePath);
139 
140   std::string uri = "file:";
141   if (authority.empty() && body.empty())
142     return uri;
143 
144   // If authority if empty, we only print body if it starts with "/"; otherwise,
145   // the URI is invalid.
146   if (!authority.empty() || StringRef(body).startswith("/")) {
147     uri.append("//");
148     percentEncode(authority, uri);
149   }
150   percentEncode(body, uri);
151   return uri;
152 }
153 
154 static llvm::Expected<std::string> getAbsolutePath(StringRef authority,
155                                                    StringRef body) {
156   if (!body.startswith("/"))
157     return llvm::createStringError(
158         llvm::inconvertibleErrorCode(),
159         "File scheme: expect body to be an absolute path starting "
160         "with '/': " +
161             body);
162   SmallString<128> path;
163   if (!authority.empty()) {
164     // Windows UNC paths e.g. file://server/share => \\server\share
165     ("//" + authority).toVector(path);
166   } else if (isWindowsPath(body.substr(1))) {
167     // Windows paths e.g. file:///X:/path => X:\path
168     body.consume_front("/");
169   }
170   path.append(body);
171   llvm::sys::path::native(path);
172   return std::string(path);
173 }
174 
175 static llvm::Expected<std::string> parseFilePathFromURI(StringRef origUri) {
176   StringRef uri = origUri;
177 
178   // Decode the scheme of the URI.
179   size_t pos = uri.find(':');
180   if (pos == StringRef::npos)
181     return llvm::createStringError(llvm::inconvertibleErrorCode(),
182                                    "Scheme must be provided in URI: " +
183                                        origUri);
184   StringRef schemeStr = uri.substr(0, pos);
185   std::string uriScheme = percentDecode(schemeStr);
186   if (!isValidScheme(uriScheme))
187     return llvm::createStringError(llvm::inconvertibleErrorCode(),
188                                    "Invalid scheme: " + schemeStr +
189                                        " (decoded: " + uriScheme + ")");
190   uri = uri.substr(pos + 1);
191 
192   // Decode the authority of the URI.
193   std::string uriAuthority;
194   if (uri.consume_front("//")) {
195     pos = uri.find('/');
196     uriAuthority = percentDecode(uri.substr(0, pos));
197     uri = uri.substr(pos);
198   }
199 
200   // Decode the body of the URI.
201   std::string uriBody = percentDecode(uri);
202 
203   // Compute the absolute path for this uri.
204   if (uriScheme != "file" && uriScheme != "test") {
205     return llvm::createStringError(
206         llvm::inconvertibleErrorCode(),
207         "mlir-lsp-server only supports 'file' URI scheme for workspace files");
208   }
209   return getAbsolutePath(uriAuthority, uriBody);
210 }
211 
212 llvm::Expected<URIForFile> URIForFile::fromURI(StringRef uri) {
213   llvm::Expected<std::string> filePath = parseFilePathFromURI(uri);
214   if (!filePath)
215     return filePath.takeError();
216   return URIForFile(std::move(*filePath), uri.str());
217 }
218 
219 llvm::Expected<URIForFile> URIForFile::fromFile(StringRef absoluteFilepath) {
220   llvm::Expected<std::string> uri = uriFromAbsolutePath(absoluteFilepath);
221   if (!uri)
222     return uri.takeError();
223   return fromURI(*uri);
224 }
225 
226 bool mlir::lsp::fromJSON(const llvm::json::Value &value, URIForFile &result,
227                          llvm::json::Path path) {
228   if (Optional<StringRef> str = value.getAsString()) {
229     llvm::Expected<URIForFile> expectedURI = URIForFile::fromURI(*str);
230     if (!expectedURI) {
231       path.report("unresolvable URI");
232       consumeError(expectedURI.takeError());
233       return false;
234     }
235     result = std::move(*expectedURI);
236     return true;
237   }
238   return false;
239 }
240 
241 llvm::json::Value mlir::lsp::toJSON(const URIForFile &value) {
242   return value.uri();
243 }
244 
245 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const URIForFile &value) {
246   return os << value.uri();
247 }
248 
249 //===----------------------------------------------------------------------===//
250 // ClientCapabilities
251 //===----------------------------------------------------------------------===//
252 
253 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
254                          ClientCapabilities &result, llvm::json::Path path) {
255   const llvm::json::Object *o = value.getAsObject();
256   if (!o) {
257     path.report("expected object");
258     return false;
259   }
260   if (const llvm::json::Object *textDocument = o->getObject("textDocument")) {
261     if (const llvm::json::Object *documentSymbol =
262             textDocument->getObject("documentSymbol")) {
263       if (Optional<bool> hierarchicalSupport =
264               documentSymbol->getBoolean("hierarchicalDocumentSymbolSupport"))
265         result.hierarchicalDocumentSymbol = *hierarchicalSupport;
266     }
267   }
268   return true;
269 }
270 
271 //===----------------------------------------------------------------------===//
272 // InitializeParams
273 //===----------------------------------------------------------------------===//
274 
275 bool mlir::lsp::fromJSON(const llvm::json::Value &value, TraceLevel &result,
276                          llvm::json::Path path) {
277   if (Optional<StringRef> str = value.getAsString()) {
278     if (*str == "off") {
279       result = TraceLevel::Off;
280       return true;
281     }
282     if (*str == "messages") {
283       result = TraceLevel::Messages;
284       return true;
285     }
286     if (*str == "verbose") {
287       result = TraceLevel::Verbose;
288       return true;
289     }
290   }
291   return false;
292 }
293 
294 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
295                          InitializeParams &result, llvm::json::Path path) {
296   llvm::json::ObjectMapper o(value, path);
297   if (!o)
298     return false;
299   // We deliberately don't fail if we can't parse individual fields.
300   o.map("capabilities", result.capabilities);
301   o.map("trace", result.trace);
302   return true;
303 }
304 
305 //===----------------------------------------------------------------------===//
306 // TextDocumentItem
307 //===----------------------------------------------------------------------===//
308 
309 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
310                          TextDocumentItem &result, llvm::json::Path path) {
311   llvm::json::ObjectMapper o(value, path);
312   return o && o.map("uri", result.uri) &&
313          o.map("languageId", result.languageId) && o.map("text", result.text) &&
314          o.map("version", result.version);
315 }
316 
317 //===----------------------------------------------------------------------===//
318 // TextDocumentIdentifier
319 //===----------------------------------------------------------------------===//
320 
321 llvm::json::Value mlir::lsp::toJSON(const TextDocumentIdentifier &value) {
322   return llvm::json::Object{{"uri", value.uri}};
323 }
324 
325 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
326                          TextDocumentIdentifier &result,
327                          llvm::json::Path path) {
328   llvm::json::ObjectMapper o(value, path);
329   return o && o.map("uri", result.uri);
330 }
331 
332 //===----------------------------------------------------------------------===//
333 // VersionedTextDocumentIdentifier
334 //===----------------------------------------------------------------------===//
335 
336 llvm::json::Value
337 mlir::lsp::toJSON(const VersionedTextDocumentIdentifier &value) {
338   return llvm::json::Object{
339       {"uri", value.uri},
340       {"version", value.version},
341   };
342 }
343 
344 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
345                          VersionedTextDocumentIdentifier &result,
346                          llvm::json::Path path) {
347   llvm::json::ObjectMapper o(value, path);
348   return o && o.map("uri", result.uri) && o.map("version", result.version);
349 }
350 
351 //===----------------------------------------------------------------------===//
352 // Position
353 //===----------------------------------------------------------------------===//
354 
355 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Position &result,
356                          llvm::json::Path path) {
357   llvm::json::ObjectMapper o(value, path);
358   return o && o.map("line", result.line) &&
359          o.map("character", result.character);
360 }
361 
362 llvm::json::Value mlir::lsp::toJSON(const Position &value) {
363   return llvm::json::Object{
364       {"line", value.line},
365       {"character", value.character},
366   };
367 }
368 
369 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Position &value) {
370   return os << value.line << ':' << value.character;
371 }
372 
373 //===----------------------------------------------------------------------===//
374 // Range
375 //===----------------------------------------------------------------------===//
376 
377 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Range &result,
378                          llvm::json::Path path) {
379   llvm::json::ObjectMapper o(value, path);
380   return o && o.map("start", result.start) && o.map("end", result.end);
381 }
382 
383 llvm::json::Value mlir::lsp::toJSON(const Range &value) {
384   return llvm::json::Object{
385       {"start", value.start},
386       {"end", value.end},
387   };
388 }
389 
390 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Range &value) {
391   return os << value.start << '-' << value.end;
392 }
393 
394 //===----------------------------------------------------------------------===//
395 // Location
396 //===----------------------------------------------------------------------===//
397 
398 llvm::json::Value mlir::lsp::toJSON(const Location &value) {
399   return llvm::json::Object{
400       {"uri", value.uri},
401       {"range", value.range},
402   };
403 }
404 
405 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Location &value) {
406   return os << value.range << '@' << value.uri;
407 }
408 
409 //===----------------------------------------------------------------------===//
410 // TextDocumentPositionParams
411 //===----------------------------------------------------------------------===//
412 
413 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
414                          TextDocumentPositionParams &result,
415                          llvm::json::Path path) {
416   llvm::json::ObjectMapper o(value, path);
417   return o && o.map("textDocument", result.textDocument) &&
418          o.map("position", result.position);
419 }
420 
421 //===----------------------------------------------------------------------===//
422 // ReferenceParams
423 //===----------------------------------------------------------------------===//
424 
425 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
426                          ReferenceContext &result, llvm::json::Path path) {
427   llvm::json::ObjectMapper o(value, path);
428   return o && o.mapOptional("includeDeclaration", result.includeDeclaration);
429 }
430 
431 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
432                          ReferenceParams &result, llvm::json::Path path) {
433   TextDocumentPositionParams &base = result;
434   llvm::json::ObjectMapper o(value, path);
435   return fromJSON(value, base, path) && o &&
436          o.mapOptional("context", result.context);
437 }
438 
439 //===----------------------------------------------------------------------===//
440 // DidOpenTextDocumentParams
441 //===----------------------------------------------------------------------===//
442 
443 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
444                          DidOpenTextDocumentParams &result,
445                          llvm::json::Path path) {
446   llvm::json::ObjectMapper o(value, path);
447   return o && o.map("textDocument", result.textDocument);
448 }
449 
450 //===----------------------------------------------------------------------===//
451 // DidCloseTextDocumentParams
452 //===----------------------------------------------------------------------===//
453 
454 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
455                          DidCloseTextDocumentParams &result,
456                          llvm::json::Path path) {
457   llvm::json::ObjectMapper o(value, path);
458   return o && o.map("textDocument", result.textDocument);
459 }
460 
461 //===----------------------------------------------------------------------===//
462 // DidChangeTextDocumentParams
463 //===----------------------------------------------------------------------===//
464 
465 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
466                          TextDocumentContentChangeEvent &result,
467                          llvm::json::Path path) {
468   llvm::json::ObjectMapper o(value, path);
469   return o && o.map("range", result.range) &&
470          o.map("rangeLength", result.rangeLength) && o.map("text", result.text);
471 }
472 
473 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
474                          DidChangeTextDocumentParams &result,
475                          llvm::json::Path path) {
476   llvm::json::ObjectMapper o(value, path);
477   return o && o.map("textDocument", result.textDocument) &&
478          o.map("contentChanges", result.contentChanges);
479 }
480 
481 //===----------------------------------------------------------------------===//
482 // MarkupContent
483 //===----------------------------------------------------------------------===//
484 
485 static llvm::StringRef toTextKind(MarkupKind kind) {
486   switch (kind) {
487   case MarkupKind::PlainText:
488     return "plaintext";
489   case MarkupKind::Markdown:
490     return "markdown";
491   }
492   llvm_unreachable("Invalid MarkupKind");
493 }
494 
495 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, MarkupKind kind) {
496   return os << toTextKind(kind);
497 }
498 
499 llvm::json::Value mlir::lsp::toJSON(const MarkupContent &mc) {
500   if (mc.value.empty())
501     return nullptr;
502 
503   return llvm::json::Object{
504       {"kind", toTextKind(mc.kind)},
505       {"value", mc.value},
506   };
507 }
508 
509 //===----------------------------------------------------------------------===//
510 // Hover
511 //===----------------------------------------------------------------------===//
512 
513 llvm::json::Value mlir::lsp::toJSON(const Hover &hover) {
514   llvm::json::Object result{{"contents", toJSON(hover.contents)}};
515   if (hover.range.hasValue())
516     result["range"] = toJSON(*hover.range);
517   return std::move(result);
518 }
519 
520 //===----------------------------------------------------------------------===//
521 // DocumentSymbol
522 //===----------------------------------------------------------------------===//
523 
524 llvm::json::Value mlir::lsp::toJSON(const DocumentSymbol &symbol) {
525   llvm::json::Object result{{"name", symbol.name},
526                             {"kind", static_cast<int>(symbol.kind)},
527                             {"range", symbol.range},
528                             {"selectionRange", symbol.selectionRange}};
529 
530   if (!symbol.detail.empty())
531     result["detail"] = symbol.detail;
532   if (!symbol.children.empty())
533     result["children"] = symbol.children;
534   return std::move(result);
535 }
536 
537 //===----------------------------------------------------------------------===//
538 // DocumentSymbolParams
539 //===----------------------------------------------------------------------===//
540 
541 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
542                          DocumentSymbolParams &result, llvm::json::Path path) {
543   llvm::json::ObjectMapper o(value, path);
544   return o && o.map("textDocument", result.textDocument);
545 }
546 
547 //===----------------------------------------------------------------------===//
548 // DiagnosticRelatedInformation
549 //===----------------------------------------------------------------------===//
550 
551 llvm::json::Value mlir::lsp::toJSON(const DiagnosticRelatedInformation &info) {
552   return llvm::json::Object{
553       {"location", info.location},
554       {"message", info.message},
555   };
556 }
557 
558 //===----------------------------------------------------------------------===//
559 // Diagnostic
560 //===----------------------------------------------------------------------===//
561 
562 llvm::json::Value mlir::lsp::toJSON(const Diagnostic &diag) {
563   llvm::json::Object result{
564       {"range", diag.range},
565       {"severity", (int)diag.severity},
566       {"message", diag.message},
567   };
568   if (diag.category)
569     result["category"] = *diag.category;
570   if (!diag.source.empty())
571     result["source"] = diag.source;
572   if (diag.relatedInformation)
573     result["relatedInformation"] = *diag.relatedInformation;
574   return std::move(result);
575 }
576 
577 //===----------------------------------------------------------------------===//
578 // PublishDiagnosticsParams
579 //===----------------------------------------------------------------------===//
580 
581 llvm::json::Value mlir::lsp::toJSON(const PublishDiagnosticsParams &params) {
582   return llvm::json::Object{
583       {"uri", params.uri},
584       {"diagnostics", params.diagnostics},
585       {"version", params.version},
586   };
587 }
588 
589 //===----------------------------------------------------------------------===//
590 // TextEdit
591 //===----------------------------------------------------------------------===//
592 
593 bool mlir::lsp::fromJSON(const llvm::json::Value &value, TextEdit &result,
594                          llvm::json::Path path) {
595   llvm::json::ObjectMapper o(value, path);
596   return o && o.map("range", result.range) && o.map("newText", result.newText);
597 }
598 
599 llvm::json::Value mlir::lsp::toJSON(const TextEdit &value) {
600   return llvm::json::Object{
601       {"range", value.range},
602       {"newText", value.newText},
603   };
604 }
605 
606 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const TextEdit &value) {
607   os << value.range << " => \"";
608   llvm::printEscapedString(value.newText, os);
609   return os << '"';
610 }
611 
612 //===----------------------------------------------------------------------===//
613 // CompletionItemKind
614 //===----------------------------------------------------------------------===//
615 
616 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
617                          CompletionItemKind &result, llvm::json::Path path) {
618   if (Optional<int64_t> intValue = value.getAsInteger()) {
619     if (*intValue < static_cast<int>(CompletionItemKind::Text) ||
620         *intValue > static_cast<int>(CompletionItemKind::TypeParameter))
621       return false;
622     result = static_cast<CompletionItemKind>(*intValue);
623     return true;
624   }
625   return false;
626 }
627 
628 CompletionItemKind mlir::lsp::adjustKindToCapability(
629     CompletionItemKind kind,
630     CompletionItemKindBitset &supportedCompletionItemKinds) {
631   size_t kindVal = static_cast<size_t>(kind);
632   if (kindVal >= kCompletionItemKindMin &&
633       kindVal <= supportedCompletionItemKinds.size() &&
634       supportedCompletionItemKinds[kindVal])
635     return kind;
636 
637   // Provide some fall backs for common kinds that are close enough.
638   switch (kind) {
639   case CompletionItemKind::Folder:
640     return CompletionItemKind::File;
641   case CompletionItemKind::EnumMember:
642     return CompletionItemKind::Enum;
643   case CompletionItemKind::Struct:
644     return CompletionItemKind::Class;
645   default:
646     return CompletionItemKind::Text;
647   }
648 }
649 
650 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
651                          CompletionItemKindBitset &result,
652                          llvm::json::Path path) {
653   if (const llvm::json::Array *arrayValue = value.getAsArray()) {
654     for (size_t i = 0, e = arrayValue->size(); i < e; ++i) {
655       CompletionItemKind kindOut;
656       if (fromJSON((*arrayValue)[i], kindOut, path.index(i)))
657         result.set(size_t(kindOut));
658     }
659     return true;
660   }
661   return false;
662 }
663 
664 //===----------------------------------------------------------------------===//
665 // CompletionItem
666 //===----------------------------------------------------------------------===//
667 
668 llvm::json::Value mlir::lsp::toJSON(const CompletionItem &value) {
669   assert(!value.label.empty() && "completion item label is required");
670   llvm::json::Object result{{"label", value.label}};
671   if (value.kind != CompletionItemKind::Missing)
672     result["kind"] = static_cast<int>(value.kind);
673   if (!value.detail.empty())
674     result["detail"] = value.detail;
675   if (value.documentation)
676     result["documentation"] = value.documentation;
677   if (!value.sortText.empty())
678     result["sortText"] = value.sortText;
679   if (!value.filterText.empty())
680     result["filterText"] = value.filterText;
681   if (!value.insertText.empty())
682     result["insertText"] = value.insertText;
683   if (value.insertTextFormat != InsertTextFormat::Missing)
684     result["insertTextFormat"] = static_cast<int>(value.insertTextFormat);
685   if (value.textEdit)
686     result["textEdit"] = *value.textEdit;
687   if (!value.additionalTextEdits.empty()) {
688     result["additionalTextEdits"] =
689         llvm::json::Array(value.additionalTextEdits);
690   }
691   if (value.deprecated)
692     result["deprecated"] = value.deprecated;
693   return std::move(result);
694 }
695 
696 raw_ostream &mlir::lsp::operator<<(raw_ostream &os,
697                                    const CompletionItem &value) {
698   return os << value.label << " - " << toJSON(value);
699 }
700 
701 bool mlir::lsp::operator<(const CompletionItem &lhs,
702                           const CompletionItem &rhs) {
703   return (lhs.sortText.empty() ? lhs.label : lhs.sortText) <
704          (rhs.sortText.empty() ? rhs.label : rhs.sortText);
705 }
706 
707 //===----------------------------------------------------------------------===//
708 // CompletionList
709 //===----------------------------------------------------------------------===//
710 
711 llvm::json::Value mlir::lsp::toJSON(const CompletionList &value) {
712   return llvm::json::Object{
713       {"isIncomplete", value.isIncomplete},
714       {"items", llvm::json::Array(value.items)},
715   };
716 }
717 
718 //===----------------------------------------------------------------------===//
719 // CompletionContext
720 //===----------------------------------------------------------------------===//
721 
722 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
723                          CompletionContext &result, llvm::json::Path path) {
724   llvm::json::ObjectMapper o(value, path);
725   int triggerKind;
726   if (!o || !o.map("triggerKind", triggerKind) ||
727       !mapOptOrNull(value, "triggerCharacter", result.triggerCharacter, path))
728     return false;
729   result.triggerKind = static_cast<CompletionTriggerKind>(triggerKind);
730   return true;
731 }
732 
733 //===----------------------------------------------------------------------===//
734 // CompletionParams
735 //===----------------------------------------------------------------------===//
736 
737 bool mlir::lsp::fromJSON(const llvm::json::Value &value,
738                          CompletionParams &result, llvm::json::Path path) {
739   if (!fromJSON(value, static_cast<TextDocumentPositionParams &>(result), path))
740     return false;
741   if (const llvm::json::Value *context = value.getAsObject()->get("context"))
742     return fromJSON(*context, result.context, path.field("context"));
743   return true;
744 }
745 
746 //===----------------------------------------------------------------------===//
747 // ParameterInformation
748 //===----------------------------------------------------------------------===//
749 
750 llvm::json::Value mlir::lsp::toJSON(const ParameterInformation &value) {
751   assert((value.labelOffsets.hasValue() || !value.labelString.empty()) &&
752          "parameter information label is required");
753   llvm::json::Object result;
754   if (value.labelOffsets)
755     result["label"] = llvm::json::Array(
756         {value.labelOffsets->first, value.labelOffsets->second});
757   else
758     result["label"] = value.labelString;
759   if (!value.documentation.empty())
760     result["documentation"] = value.documentation;
761   return std::move(result);
762 }
763 
764 //===----------------------------------------------------------------------===//
765 // SignatureInformation
766 //===----------------------------------------------------------------------===//
767 
768 llvm::json::Value mlir::lsp::toJSON(const SignatureInformation &value) {
769   assert(!value.label.empty() && "signature information label is required");
770   llvm::json::Object result{
771       {"label", value.label},
772       {"parameters", llvm::json::Array(value.parameters)},
773   };
774   if (!value.documentation.empty())
775     result["documentation"] = value.documentation;
776   return std::move(result);
777 }
778 
779 raw_ostream &mlir::lsp::operator<<(raw_ostream &os,
780                                    const SignatureInformation &value) {
781   return os << value.label << " - " << toJSON(value);
782 }
783 
784 //===----------------------------------------------------------------------===//
785 // SignatureHelp
786 //===----------------------------------------------------------------------===//
787 
788 llvm::json::Value mlir::lsp::toJSON(const SignatureHelp &value) {
789   assert(value.activeSignature >= 0 &&
790          "Unexpected negative value for number of active signatures.");
791   assert(value.activeParameter >= 0 &&
792          "Unexpected negative value for active parameter index");
793   return llvm::json::Object{
794       {"activeSignature", value.activeSignature},
795       {"activeParameter", value.activeParameter},
796       {"signatures", llvm::json::Array(value.signatures)},
797   };
798 }
799