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/ADT/StringSwitch.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).startswith("/")) { 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.startswith("/")) 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 (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 (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 // InitializeParams 299 //===----------------------------------------------------------------------===// 300 301 bool mlir::lsp::fromJSON(const llvm::json::Value &value, TraceLevel &result, 302 llvm::json::Path path) { 303 if (Optional<StringRef> str = value.getAsString()) { 304 if (*str == "off") { 305 result = TraceLevel::Off; 306 return true; 307 } 308 if (*str == "messages") { 309 result = TraceLevel::Messages; 310 return true; 311 } 312 if (*str == "verbose") { 313 result = TraceLevel::Verbose; 314 return true; 315 } 316 } 317 return false; 318 } 319 320 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 321 InitializeParams &result, llvm::json::Path path) { 322 llvm::json::ObjectMapper o(value, path); 323 if (!o) 324 return false; 325 // We deliberately don't fail if we can't parse individual fields. 326 o.map("capabilities", result.capabilities); 327 o.map("trace", result.trace); 328 return true; 329 } 330 331 //===----------------------------------------------------------------------===// 332 // TextDocumentItem 333 //===----------------------------------------------------------------------===// 334 335 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 336 TextDocumentItem &result, llvm::json::Path path) { 337 llvm::json::ObjectMapper o(value, path); 338 return o && o.map("uri", result.uri) && 339 o.map("languageId", result.languageId) && o.map("text", result.text) && 340 o.map("version", result.version); 341 } 342 343 //===----------------------------------------------------------------------===// 344 // TextDocumentIdentifier 345 //===----------------------------------------------------------------------===// 346 347 llvm::json::Value mlir::lsp::toJSON(const TextDocumentIdentifier &value) { 348 return llvm::json::Object{{"uri", value.uri}}; 349 } 350 351 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 352 TextDocumentIdentifier &result, 353 llvm::json::Path path) { 354 llvm::json::ObjectMapper o(value, path); 355 return o && o.map("uri", result.uri); 356 } 357 358 //===----------------------------------------------------------------------===// 359 // VersionedTextDocumentIdentifier 360 //===----------------------------------------------------------------------===// 361 362 llvm::json::Value 363 mlir::lsp::toJSON(const VersionedTextDocumentIdentifier &value) { 364 return llvm::json::Object{ 365 {"uri", value.uri}, 366 {"version", value.version}, 367 }; 368 } 369 370 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 371 VersionedTextDocumentIdentifier &result, 372 llvm::json::Path path) { 373 llvm::json::ObjectMapper o(value, path); 374 return o && o.map("uri", result.uri) && o.map("version", result.version); 375 } 376 377 //===----------------------------------------------------------------------===// 378 // Position 379 //===----------------------------------------------------------------------===// 380 381 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Position &result, 382 llvm::json::Path path) { 383 llvm::json::ObjectMapper o(value, path); 384 return o && o.map("line", result.line) && 385 o.map("character", result.character); 386 } 387 388 llvm::json::Value mlir::lsp::toJSON(const Position &value) { 389 return llvm::json::Object{ 390 {"line", value.line}, 391 {"character", value.character}, 392 }; 393 } 394 395 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Position &value) { 396 return os << value.line << ':' << value.character; 397 } 398 399 //===----------------------------------------------------------------------===// 400 // Range 401 //===----------------------------------------------------------------------===// 402 403 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Range &result, 404 llvm::json::Path path) { 405 llvm::json::ObjectMapper o(value, path); 406 return o && o.map("start", result.start) && o.map("end", result.end); 407 } 408 409 llvm::json::Value mlir::lsp::toJSON(const Range &value) { 410 return llvm::json::Object{ 411 {"start", value.start}, 412 {"end", value.end}, 413 }; 414 } 415 416 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Range &value) { 417 return os << value.start << '-' << value.end; 418 } 419 420 //===----------------------------------------------------------------------===// 421 // Location 422 //===----------------------------------------------------------------------===// 423 424 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Location &result, 425 llvm::json::Path path) { 426 llvm::json::ObjectMapper o(value, path); 427 return o && o.map("uri", result.uri) && o.map("range", result.range); 428 } 429 430 llvm::json::Value mlir::lsp::toJSON(const Location &value) { 431 return llvm::json::Object{ 432 {"uri", value.uri}, 433 {"range", value.range}, 434 }; 435 } 436 437 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Location &value) { 438 return os << value.range << '@' << value.uri; 439 } 440 441 //===----------------------------------------------------------------------===// 442 // TextDocumentPositionParams 443 //===----------------------------------------------------------------------===// 444 445 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 446 TextDocumentPositionParams &result, 447 llvm::json::Path path) { 448 llvm::json::ObjectMapper o(value, path); 449 return o && o.map("textDocument", result.textDocument) && 450 o.map("position", result.position); 451 } 452 453 //===----------------------------------------------------------------------===// 454 // ReferenceParams 455 //===----------------------------------------------------------------------===// 456 457 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 458 ReferenceContext &result, llvm::json::Path path) { 459 llvm::json::ObjectMapper o(value, path); 460 return o && o.mapOptional("includeDeclaration", result.includeDeclaration); 461 } 462 463 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 464 ReferenceParams &result, llvm::json::Path path) { 465 TextDocumentPositionParams &base = result; 466 llvm::json::ObjectMapper o(value, path); 467 return fromJSON(value, base, path) && o && 468 o.mapOptional("context", result.context); 469 } 470 471 //===----------------------------------------------------------------------===// 472 // DidOpenTextDocumentParams 473 //===----------------------------------------------------------------------===// 474 475 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 476 DidOpenTextDocumentParams &result, 477 llvm::json::Path path) { 478 llvm::json::ObjectMapper o(value, path); 479 return o && o.map("textDocument", result.textDocument); 480 } 481 482 //===----------------------------------------------------------------------===// 483 // DidCloseTextDocumentParams 484 //===----------------------------------------------------------------------===// 485 486 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 487 DidCloseTextDocumentParams &result, 488 llvm::json::Path path) { 489 llvm::json::ObjectMapper o(value, path); 490 return o && o.map("textDocument", result.textDocument); 491 } 492 493 //===----------------------------------------------------------------------===// 494 // DidChangeTextDocumentParams 495 //===----------------------------------------------------------------------===// 496 497 LogicalResult 498 TextDocumentContentChangeEvent::applyTo(std::string &contents) const { 499 // If there is no range, the full document changed. 500 if (!range) { 501 contents = text; 502 return success(); 503 } 504 505 // Try to map the replacement range to the content. 506 llvm::SourceMgr tmpScrMgr; 507 tmpScrMgr.AddNewSourceBuffer(llvm::MemoryBuffer::getMemBuffer(contents), 508 SMLoc()); 509 SMRange rangeLoc = range->getAsSMRange(tmpScrMgr); 510 if (!rangeLoc.isValid()) 511 return failure(); 512 513 contents.replace(rangeLoc.Start.getPointer() - contents.data(), 514 rangeLoc.End.getPointer() - rangeLoc.Start.getPointer(), 515 text); 516 return success(); 517 } 518 519 LogicalResult TextDocumentContentChangeEvent::applyTo( 520 ArrayRef<TextDocumentContentChangeEvent> changes, std::string &contents) { 521 for (const auto &change : changes) 522 if (failed(change.applyTo(contents))) 523 return failure(); 524 return success(); 525 } 526 527 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 528 TextDocumentContentChangeEvent &result, 529 llvm::json::Path path) { 530 llvm::json::ObjectMapper o(value, path); 531 return o && o.map("range", result.range) && 532 o.map("rangeLength", result.rangeLength) && o.map("text", result.text); 533 } 534 535 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 536 DidChangeTextDocumentParams &result, 537 llvm::json::Path path) { 538 llvm::json::ObjectMapper o(value, path); 539 return o && o.map("textDocument", result.textDocument) && 540 o.map("contentChanges", result.contentChanges); 541 } 542 543 //===----------------------------------------------------------------------===// 544 // MarkupContent 545 //===----------------------------------------------------------------------===// 546 547 static llvm::StringRef toTextKind(MarkupKind kind) { 548 switch (kind) { 549 case MarkupKind::PlainText: 550 return "plaintext"; 551 case MarkupKind::Markdown: 552 return "markdown"; 553 } 554 llvm_unreachable("Invalid MarkupKind"); 555 } 556 557 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, MarkupKind kind) { 558 return os << toTextKind(kind); 559 } 560 561 llvm::json::Value mlir::lsp::toJSON(const MarkupContent &mc) { 562 if (mc.value.empty()) 563 return nullptr; 564 565 return llvm::json::Object{ 566 {"kind", toTextKind(mc.kind)}, 567 {"value", mc.value}, 568 }; 569 } 570 571 //===----------------------------------------------------------------------===// 572 // Hover 573 //===----------------------------------------------------------------------===// 574 575 llvm::json::Value mlir::lsp::toJSON(const Hover &hover) { 576 llvm::json::Object result{{"contents", toJSON(hover.contents)}}; 577 if (hover.range) 578 result["range"] = toJSON(*hover.range); 579 return std::move(result); 580 } 581 582 //===----------------------------------------------------------------------===// 583 // DocumentSymbol 584 //===----------------------------------------------------------------------===// 585 586 llvm::json::Value mlir::lsp::toJSON(const DocumentSymbol &symbol) { 587 llvm::json::Object result{{"name", symbol.name}, 588 {"kind", static_cast<int>(symbol.kind)}, 589 {"range", symbol.range}, 590 {"selectionRange", symbol.selectionRange}}; 591 592 if (!symbol.detail.empty()) 593 result["detail"] = symbol.detail; 594 if (!symbol.children.empty()) 595 result["children"] = symbol.children; 596 return std::move(result); 597 } 598 599 //===----------------------------------------------------------------------===// 600 // DocumentSymbolParams 601 //===----------------------------------------------------------------------===// 602 603 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 604 DocumentSymbolParams &result, llvm::json::Path path) { 605 llvm::json::ObjectMapper o(value, path); 606 return o && o.map("textDocument", result.textDocument); 607 } 608 609 //===----------------------------------------------------------------------===// 610 // DiagnosticRelatedInformation 611 //===----------------------------------------------------------------------===// 612 613 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 614 DiagnosticRelatedInformation &result, 615 llvm::json::Path path) { 616 llvm::json::ObjectMapper o(value, path); 617 return o && o.map("location", result.location) && 618 o.map("message", result.message); 619 } 620 621 llvm::json::Value mlir::lsp::toJSON(const DiagnosticRelatedInformation &info) { 622 return llvm::json::Object{ 623 {"location", info.location}, 624 {"message", info.message}, 625 }; 626 } 627 628 //===----------------------------------------------------------------------===// 629 // Diagnostic 630 //===----------------------------------------------------------------------===// 631 632 llvm::json::Value mlir::lsp::toJSON(const Diagnostic &diag) { 633 llvm::json::Object result{ 634 {"range", diag.range}, 635 {"severity", (int)diag.severity}, 636 {"message", diag.message}, 637 }; 638 if (diag.category) 639 result["category"] = *diag.category; 640 if (!diag.source.empty()) 641 result["source"] = diag.source; 642 if (diag.relatedInformation) 643 result["relatedInformation"] = *diag.relatedInformation; 644 return std::move(result); 645 } 646 647 bool mlir::lsp::fromJSON(const llvm::json::Value &value, Diagnostic &result, 648 llvm::json::Path path) { 649 llvm::json::ObjectMapper o(value, path); 650 if (!o) 651 return false; 652 int severity = 0; 653 if (!mapOptOrNull(value, "severity", severity, path)) 654 return false; 655 result.severity = (DiagnosticSeverity)severity; 656 657 return o.map("range", result.range) && o.map("message", result.message) && 658 mapOptOrNull(value, "category", result.category, path) && 659 mapOptOrNull(value, "source", result.source, path) && 660 mapOptOrNull(value, "relatedInformation", result.relatedInformation, 661 path); 662 } 663 664 //===----------------------------------------------------------------------===// 665 // PublishDiagnosticsParams 666 //===----------------------------------------------------------------------===// 667 668 llvm::json::Value mlir::lsp::toJSON(const PublishDiagnosticsParams ¶ms) { 669 return llvm::json::Object{ 670 {"uri", params.uri}, 671 {"diagnostics", params.diagnostics}, 672 {"version", params.version}, 673 }; 674 } 675 676 //===----------------------------------------------------------------------===// 677 // TextEdit 678 //===----------------------------------------------------------------------===// 679 680 bool mlir::lsp::fromJSON(const llvm::json::Value &value, TextEdit &result, 681 llvm::json::Path path) { 682 llvm::json::ObjectMapper o(value, path); 683 return o && o.map("range", result.range) && o.map("newText", result.newText); 684 } 685 686 llvm::json::Value mlir::lsp::toJSON(const TextEdit &value) { 687 return llvm::json::Object{ 688 {"range", value.range}, 689 {"newText", value.newText}, 690 }; 691 } 692 693 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const TextEdit &value) { 694 os << value.range << " => \""; 695 llvm::printEscapedString(value.newText, os); 696 return os << '"'; 697 } 698 699 //===----------------------------------------------------------------------===// 700 // CompletionItemKind 701 //===----------------------------------------------------------------------===// 702 703 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 704 CompletionItemKind &result, llvm::json::Path path) { 705 if (Optional<int64_t> intValue = value.getAsInteger()) { 706 if (*intValue < static_cast<int>(CompletionItemKind::Text) || 707 *intValue > static_cast<int>(CompletionItemKind::TypeParameter)) 708 return false; 709 result = static_cast<CompletionItemKind>(*intValue); 710 return true; 711 } 712 return false; 713 } 714 715 CompletionItemKind mlir::lsp::adjustKindToCapability( 716 CompletionItemKind kind, 717 CompletionItemKindBitset &supportedCompletionItemKinds) { 718 size_t kindVal = static_cast<size_t>(kind); 719 if (kindVal >= kCompletionItemKindMin && 720 kindVal <= supportedCompletionItemKinds.size() && 721 supportedCompletionItemKinds[kindVal]) 722 return kind; 723 724 // Provide some fall backs for common kinds that are close enough. 725 switch (kind) { 726 case CompletionItemKind::Folder: 727 return CompletionItemKind::File; 728 case CompletionItemKind::EnumMember: 729 return CompletionItemKind::Enum; 730 case CompletionItemKind::Struct: 731 return CompletionItemKind::Class; 732 default: 733 return CompletionItemKind::Text; 734 } 735 } 736 737 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 738 CompletionItemKindBitset &result, 739 llvm::json::Path path) { 740 if (const llvm::json::Array *arrayValue = value.getAsArray()) { 741 for (size_t i = 0, e = arrayValue->size(); i < e; ++i) { 742 CompletionItemKind kindOut; 743 if (fromJSON((*arrayValue)[i], kindOut, path.index(i))) 744 result.set(size_t(kindOut)); 745 } 746 return true; 747 } 748 return false; 749 } 750 751 //===----------------------------------------------------------------------===// 752 // CompletionItem 753 //===----------------------------------------------------------------------===// 754 755 llvm::json::Value mlir::lsp::toJSON(const CompletionItem &value) { 756 assert(!value.label.empty() && "completion item label is required"); 757 llvm::json::Object result{{"label", value.label}}; 758 if (value.kind != CompletionItemKind::Missing) 759 result["kind"] = static_cast<int>(value.kind); 760 if (!value.detail.empty()) 761 result["detail"] = value.detail; 762 if (value.documentation) 763 result["documentation"] = value.documentation; 764 if (!value.sortText.empty()) 765 result["sortText"] = value.sortText; 766 if (!value.filterText.empty()) 767 result["filterText"] = value.filterText; 768 if (!value.insertText.empty()) 769 result["insertText"] = value.insertText; 770 if (value.insertTextFormat != InsertTextFormat::Missing) 771 result["insertTextFormat"] = static_cast<int>(value.insertTextFormat); 772 if (value.textEdit) 773 result["textEdit"] = *value.textEdit; 774 if (!value.additionalTextEdits.empty()) { 775 result["additionalTextEdits"] = 776 llvm::json::Array(value.additionalTextEdits); 777 } 778 if (value.deprecated) 779 result["deprecated"] = value.deprecated; 780 return std::move(result); 781 } 782 783 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, 784 const CompletionItem &value) { 785 return os << value.label << " - " << toJSON(value); 786 } 787 788 bool mlir::lsp::operator<(const CompletionItem &lhs, 789 const CompletionItem &rhs) { 790 return (lhs.sortText.empty() ? lhs.label : lhs.sortText) < 791 (rhs.sortText.empty() ? rhs.label : rhs.sortText); 792 } 793 794 //===----------------------------------------------------------------------===// 795 // CompletionList 796 //===----------------------------------------------------------------------===// 797 798 llvm::json::Value mlir::lsp::toJSON(const CompletionList &value) { 799 return llvm::json::Object{ 800 {"isIncomplete", value.isIncomplete}, 801 {"items", llvm::json::Array(value.items)}, 802 }; 803 } 804 805 //===----------------------------------------------------------------------===// 806 // CompletionContext 807 //===----------------------------------------------------------------------===// 808 809 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 810 CompletionContext &result, llvm::json::Path path) { 811 llvm::json::ObjectMapper o(value, path); 812 int triggerKind; 813 if (!o || !o.map("triggerKind", triggerKind) || 814 !mapOptOrNull(value, "triggerCharacter", result.triggerCharacter, path)) 815 return false; 816 result.triggerKind = static_cast<CompletionTriggerKind>(triggerKind); 817 return true; 818 } 819 820 //===----------------------------------------------------------------------===// 821 // CompletionParams 822 //===----------------------------------------------------------------------===// 823 824 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 825 CompletionParams &result, llvm::json::Path path) { 826 if (!fromJSON(value, static_cast<TextDocumentPositionParams &>(result), path)) 827 return false; 828 if (const llvm::json::Value *context = value.getAsObject()->get("context")) 829 return fromJSON(*context, result.context, path.field("context")); 830 return true; 831 } 832 833 //===----------------------------------------------------------------------===// 834 // ParameterInformation 835 //===----------------------------------------------------------------------===// 836 837 llvm::json::Value mlir::lsp::toJSON(const ParameterInformation &value) { 838 assert((value.labelOffsets || !value.labelString.empty()) && 839 "parameter information label is required"); 840 llvm::json::Object result; 841 if (value.labelOffsets) 842 result["label"] = llvm::json::Array( 843 {value.labelOffsets->first, value.labelOffsets->second}); 844 else 845 result["label"] = value.labelString; 846 if (!value.documentation.empty()) 847 result["documentation"] = value.documentation; 848 return std::move(result); 849 } 850 851 //===----------------------------------------------------------------------===// 852 // SignatureInformation 853 //===----------------------------------------------------------------------===// 854 855 llvm::json::Value mlir::lsp::toJSON(const SignatureInformation &value) { 856 assert(!value.label.empty() && "signature information label is required"); 857 llvm::json::Object result{ 858 {"label", value.label}, 859 {"parameters", llvm::json::Array(value.parameters)}, 860 }; 861 if (!value.documentation.empty()) 862 result["documentation"] = value.documentation; 863 return std::move(result); 864 } 865 866 raw_ostream &mlir::lsp::operator<<(raw_ostream &os, 867 const SignatureInformation &value) { 868 return os << value.label << " - " << toJSON(value); 869 } 870 871 //===----------------------------------------------------------------------===// 872 // SignatureHelp 873 //===----------------------------------------------------------------------===// 874 875 llvm::json::Value mlir::lsp::toJSON(const SignatureHelp &value) { 876 assert(value.activeSignature >= 0 && 877 "Unexpected negative value for number of active signatures."); 878 assert(value.activeParameter >= 0 && 879 "Unexpected negative value for active parameter index"); 880 return llvm::json::Object{ 881 {"activeSignature", value.activeSignature}, 882 {"activeParameter", value.activeParameter}, 883 {"signatures", llvm::json::Array(value.signatures)}, 884 }; 885 } 886 887 //===----------------------------------------------------------------------===// 888 // DocumentLinkParams 889 //===----------------------------------------------------------------------===// 890 891 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 892 DocumentLinkParams &result, llvm::json::Path path) { 893 llvm::json::ObjectMapper o(value, path); 894 return o && o.map("textDocument", result.textDocument); 895 } 896 897 //===----------------------------------------------------------------------===// 898 // DocumentLink 899 //===----------------------------------------------------------------------===// 900 901 llvm::json::Value mlir::lsp::toJSON(const DocumentLink &value) { 902 return llvm::json::Object{ 903 {"range", value.range}, 904 {"target", value.target}, 905 }; 906 } 907 908 //===----------------------------------------------------------------------===// 909 // InlayHintsParams 910 //===----------------------------------------------------------------------===// 911 912 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 913 InlayHintsParams &result, llvm::json::Path path) { 914 llvm::json::ObjectMapper o(value, path); 915 return o && o.map("textDocument", result.textDocument) && 916 o.map("range", result.range); 917 } 918 919 //===----------------------------------------------------------------------===// 920 // InlayHint 921 //===----------------------------------------------------------------------===// 922 923 llvm::json::Value mlir::lsp::toJSON(const InlayHint &value) { 924 return llvm::json::Object{{"position", value.position}, 925 {"kind", (int)value.kind}, 926 {"label", value.label}, 927 {"paddingLeft", value.paddingLeft}, 928 {"paddingRight", value.paddingRight}}; 929 } 930 bool mlir::lsp::operator==(const InlayHint &lhs, const InlayHint &rhs) { 931 return std::tie(lhs.position, lhs.kind, lhs.label) == 932 std::tie(rhs.position, rhs.kind, rhs.label); 933 } 934 bool mlir::lsp::operator<(const InlayHint &lhs, const InlayHint &rhs) { 935 return std::tie(lhs.position, lhs.kind, lhs.label) < 936 std::tie(rhs.position, rhs.kind, rhs.label); 937 } 938 939 llvm::raw_ostream &mlir::lsp::operator<<(llvm::raw_ostream &os, 940 InlayHintKind value) { 941 switch (value) { 942 case InlayHintKind::Parameter: 943 return os << "parameter"; 944 case InlayHintKind::Type: 945 return os << "type"; 946 } 947 llvm_unreachable("Unknown InlayHintKind"); 948 } 949 950 //===----------------------------------------------------------------------===// 951 // CodeActionContext 952 //===----------------------------------------------------------------------===// 953 954 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 955 CodeActionContext &result, llvm::json::Path path) { 956 llvm::json::ObjectMapper o(value, path); 957 if (!o || !o.map("diagnostics", result.diagnostics)) 958 return false; 959 o.map("only", result.only); 960 return true; 961 } 962 963 //===----------------------------------------------------------------------===// 964 // CodeActionParams 965 //===----------------------------------------------------------------------===// 966 967 bool mlir::lsp::fromJSON(const llvm::json::Value &value, 968 CodeActionParams &result, llvm::json::Path path) { 969 llvm::json::ObjectMapper o(value, path); 970 return o && o.map("textDocument", result.textDocument) && 971 o.map("range", result.range) && o.map("context", result.context); 972 } 973 974 //===----------------------------------------------------------------------===// 975 // WorkspaceEdit 976 //===----------------------------------------------------------------------===// 977 978 bool mlir::lsp::fromJSON(const llvm::json::Value &value, WorkspaceEdit &result, 979 llvm::json::Path path) { 980 llvm::json::ObjectMapper o(value, path); 981 return o && o.map("changes", result.changes); 982 } 983 984 llvm::json::Value mlir::lsp::toJSON(const WorkspaceEdit &value) { 985 llvm::json::Object fileChanges; 986 for (auto &change : value.changes) 987 fileChanges[change.first] = llvm::json::Array(change.second); 988 return llvm::json::Object{{"changes", std::move(fileChanges)}}; 989 } 990 991 //===----------------------------------------------------------------------===// 992 // CodeAction 993 //===----------------------------------------------------------------------===// 994 995 const llvm::StringLiteral CodeAction::kQuickFix = "quickfix"; 996 const llvm::StringLiteral CodeAction::kRefactor = "refactor"; 997 const llvm::StringLiteral CodeAction::kInfo = "info"; 998 999 llvm::json::Value mlir::lsp::toJSON(const CodeAction &value) { 1000 llvm::json::Object codeAction{{"title", value.title}}; 1001 if (value.kind) 1002 codeAction["kind"] = *value.kind; 1003 if (value.diagnostics) 1004 codeAction["diagnostics"] = llvm::json::Array(*value.diagnostics); 1005 if (value.isPreferred) 1006 codeAction["isPreferred"] = true; 1007 if (value.edit) 1008 codeAction["edit"] = *value.edit; 1009 return std::move(codeAction); 1010 } 1011