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 ¶ms, 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 ¶ms) { 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