brintos

brintos / llvm-project-archived public Read only

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