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