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