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