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