forked from OSchip/llvm-project
[clangd] New conventions for JSON-marshalling functions, centralize machinery
Summary: - JSON<->Obj interface is now ADL functions, so they play nicely with enums - recursive vector/map parsing and ObjectMapper moved to JSONExpr and tested - renamed (un)parse to (de)serialize, since text -> JSON is called parse - Protocol.cpp gets a bit shorter Sorry for the giant patch, it's prety mechanical though Reviewers: ilya-biryukov Subscribers: klimek, cfe-commits Differential Revision: https://reviews.llvm.org/D40596 llvm-svn: 319478
This commit is contained in:
parent
d76814200b
commit
ff8b874548
|
@ -117,7 +117,7 @@ void ClangdLSPServer::onCommand(Ctx C, ExecuteCommandParams &Params) {
|
|||
// We don't need the response so id == 1 is OK.
|
||||
// Ideally, we would wait for the response and if there is no error, we
|
||||
// would reply success/failure to the original RPC.
|
||||
C.call("workspace/applyEdit", ApplyWorkspaceEditParams::unparse(ApplyEdit));
|
||||
C.call("workspace/applyEdit", ApplyEdit);
|
||||
} else {
|
||||
// We should not get here because ExecuteCommandParams would not have
|
||||
// parsed in the first place and this handler should not be called. But if
|
||||
|
@ -140,7 +140,7 @@ void ClangdLSPServer::onRename(Ctx C, RenameParams &Params) {
|
|||
std::vector<TextEdit> Edits = replacementsToEdits(Code, *Replacements);
|
||||
WorkspaceEdit WE;
|
||||
WE.changes = {{Params.textDocument.uri.uri, Edits}};
|
||||
C.reply(WorkspaceEdit::unparse(WE));
|
||||
C.reply(WE);
|
||||
}
|
||||
|
||||
void ClangdLSPServer::onDocumentDidClose(Ctx C,
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace json {
|
|||
// - booleans
|
||||
// - null: nullptr
|
||||
// - arrays: {"foo", 42.0, false}
|
||||
// - serializable things: any T with a T::unparse(const T&) -> Expr
|
||||
// - serializable things: types with toJSON(const T&)->Expr, found by ADL
|
||||
//
|
||||
// They can also be constructed from object/array helpers:
|
||||
// - json::obj is a type like map<StringExpr, Expr>
|
||||
|
@ -65,6 +65,28 @@ namespace json {
|
|||
// if (Optional<StringRef> Font = Opts->getString("font"))
|
||||
// assert(Opts->at("font").kind() == Expr::String);
|
||||
//
|
||||
// === Converting expressions to objects ===
|
||||
//
|
||||
// The convention is to have a deserializer function findable via ADL:
|
||||
// fromJSON(const json::Expr&, T&)->bool
|
||||
// Deserializers are provided for:
|
||||
// - bool
|
||||
// - int
|
||||
// - double
|
||||
// - std::string
|
||||
// - vector<T>, where T is deserializable
|
||||
// - map<string, T>, where T is deserializable
|
||||
// - Optional<T>, where T is deserializable
|
||||
//
|
||||
// ObjectMapper can help writing fromJSON() functions for object types:
|
||||
// bool fromJSON(const Expr &E, MyStruct &R) {
|
||||
// ObjectMapper O(E);
|
||||
// if (!O || !O.map("mandatory_field", R.MandatoryField))
|
||||
// return false;
|
||||
// O.map("optional_field", R.OptionalField);
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// === Serialization ===
|
||||
//
|
||||
// Exprs can be serialized to JSON:
|
||||
|
@ -127,12 +149,11 @@ public:
|
|||
Expr(T D) : Type(T_Number) {
|
||||
create<double>(D);
|
||||
}
|
||||
// Types with a static T::unparse function returning an Expr.
|
||||
// FIXME: should this be a free unparse() function found by ADL?
|
||||
// Types with a toJSON(const T&)->Expr function, found by ADL.
|
||||
template <typename T,
|
||||
typename = typename std::enable_if<std::is_same<
|
||||
Expr, decltype(T::unparse(*(const T *)nullptr))>::value>>
|
||||
Expr(const T &V) : Expr(T::unparse(V)) {}
|
||||
Expr, decltype(toJSON(*(const T *)nullptr))>::value>>
|
||||
Expr(const T &V) : Expr(toJSON(V)) {}
|
||||
|
||||
Expr &operator=(const Expr &M) {
|
||||
destroy();
|
||||
|
@ -432,6 +453,101 @@ inline Expr::ObjectExpr::ObjectExpr(std::initializer_list<KV> Properties) {
|
|||
using obj = Expr::ObjectExpr;
|
||||
using ary = Expr::ArrayExpr;
|
||||
|
||||
// Standard deserializers.
|
||||
inline bool fromJSON(const json::Expr &E, std::string &Out) {
|
||||
if (auto S = E.asString()) {
|
||||
Out = *S;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline bool fromJSON(const json::Expr &E, int &Out) {
|
||||
if (auto S = E.asInteger()) {
|
||||
Out = *S;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline bool fromJSON(const json::Expr &E, double &Out) {
|
||||
if (auto S = E.asNumber()) {
|
||||
Out = *S;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline bool fromJSON(const json::Expr &E, bool &Out) {
|
||||
if (auto S = E.asBoolean()) {
|
||||
Out = *S;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template <typename T>
|
||||
bool fromJSON(const json::Expr &E, llvm::Optional<T> &Out) {
|
||||
if (E.asNull()) {
|
||||
Out = llvm::None;
|
||||
return true;
|
||||
}
|
||||
T Result;
|
||||
if (!fromJSON(E, Result))
|
||||
return false;
|
||||
Out = std::move(Result);
|
||||
return true;
|
||||
}
|
||||
template <typename T> bool fromJSON(const json::Expr &E, std::vector<T> &Out) {
|
||||
if (auto *A = E.asArray()) {
|
||||
Out.clear();
|
||||
Out.resize(A->size());
|
||||
for (size_t I = 0; I < A->size(); ++I)
|
||||
if (!fromJSON((*A)[I], Out[I]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template <typename T>
|
||||
bool fromJSON(const json::Expr &E, std::map<std::string, T> &Out) {
|
||||
if (auto *O = E.asObject()) {
|
||||
Out.clear();
|
||||
for (const auto &KV : *O)
|
||||
if (!fromJSON(KV.second, Out[llvm::StringRef(KV.first)]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper for mapping JSON objects onto protocol structs.
|
||||
// See file header for example.
|
||||
class ObjectMapper {
|
||||
public:
|
||||
ObjectMapper(const json::Expr &E) : O(E.asObject()) {}
|
||||
|
||||
// True if the expression is an object.
|
||||
// Must be checked before calling map().
|
||||
operator bool() { return O; }
|
||||
|
||||
// Maps a property to a field, if it exists.
|
||||
template <typename T> bool map(const char *Prop, T &Out) {
|
||||
assert(*this && "Must check this is an object before calling map()");
|
||||
if (const json::Expr *E = O->get(Prop))
|
||||
return fromJSON(*E, Out);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Optional requires special handling, because missing keys are OK.
|
||||
template <typename T> bool map(const char *Prop, llvm::Optional<T> &Out) {
|
||||
assert(*this && "Must check this is an object before calling map()");
|
||||
if (const json::Expr *E = O->get(Prop))
|
||||
return fromJSON(*E, Out);
|
||||
Out = llvm::None;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const json::obj *O;
|
||||
};
|
||||
|
||||
llvm::Expected<Expr> parse(llvm::StringRef JSON);
|
||||
|
||||
class ParseError : public llvm::ErrorInfo<ParseError> {
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains the serialization code for the LSP structs.
|
||||
// FIXME: This is extremely repetetive and ugly. Is there a better way?
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
@ -21,151 +20,8 @@
|
|||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::clangd;
|
||||
|
||||
namespace {
|
||||
// Helper for mapping JSON objects onto our protocol structs. Intended use:
|
||||
// Optional<Result> parse(json::Expr E) {
|
||||
// ObjectParser O(E);
|
||||
// if (!O || !O.parse("mandatory_field", Result.MandatoryField))
|
||||
// return None;
|
||||
// O.parse("optional_field", Result.OptionalField);
|
||||
// return Result;
|
||||
// }
|
||||
// FIXME: the static methods here should probably become the public parse()
|
||||
// extension point. Overloading free functions allows us to uniformly handle
|
||||
// enums, vectors, etc.
|
||||
class ObjectParser {
|
||||
public:
|
||||
ObjectParser(const json::Expr &E) : O(E.asObject()) {}
|
||||
|
||||
// True if the expression is an object.
|
||||
operator bool() { return O; }
|
||||
|
||||
template <typename T> bool parse(const char *Prop, T &Out) {
|
||||
assert(*this && "Must check this is an object before calling parse()");
|
||||
if (const json::Expr *E = O->get(Prop))
|
||||
return parse(*E, Out);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Optional requires special handling, because missing keys are OK.
|
||||
template <typename T> bool parse(const char *Prop, llvm::Optional<T> &Out) {
|
||||
assert(*this && "Must check this is an object before calling parse()");
|
||||
if (const json::Expr *E = O->get(Prop))
|
||||
return parse(*E, Out);
|
||||
Out = None;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Primitives.
|
||||
static bool parse(const json::Expr &E, std::string &Out) {
|
||||
if (auto S = E.asString()) {
|
||||
Out = *S;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool parse(const json::Expr &E, int &Out) {
|
||||
if (auto S = E.asInteger()) {
|
||||
Out = *S;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool parse(const json::Expr &E, bool &Out) {
|
||||
if (auto S = E.asBoolean()) {
|
||||
Out = *S;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Types with a parse() function.
|
||||
template <typename T> static bool parse(const json::Expr &E, T &Out) {
|
||||
if (auto Parsed = std::remove_reference<T>::type::parse(E)) {
|
||||
Out = std::move(*Parsed);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Nullable values as Optional<T>.
|
||||
template <typename T>
|
||||
static bool parse(const json::Expr &E, llvm::Optional<T> &Out) {
|
||||
if (E.asNull()) {
|
||||
Out = None;
|
||||
return true;
|
||||
}
|
||||
T Result;
|
||||
if (!parse(E, Result))
|
||||
return false;
|
||||
Out = std::move(Result);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Array values with std::vector type.
|
||||
template <typename T>
|
||||
static bool parse(const json::Expr &E, std::vector<T> &Out) {
|
||||
if (auto *A = E.asArray()) {
|
||||
Out.clear();
|
||||
Out.resize(A->size());
|
||||
for (size_t I = 0; I < A->size(); ++I)
|
||||
if (!parse((*A)[I], Out[I]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Object values with std::map<std::string, ?>
|
||||
template <typename T>
|
||||
static bool parse(const json::Expr &E, std::map<std::string, T> &Out) {
|
||||
if (auto *O = E.asObject()) {
|
||||
for (const auto &KV : *O)
|
||||
if (!parse(KV.second, Out[StringRef(KV.first)]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Special cased enums, which can't have T::parse() functions.
|
||||
// FIXME: make everything free functions so there's no special casing.
|
||||
static bool parse(const json::Expr &E, TraceLevel &Out) {
|
||||
if (auto S = E.asString()) {
|
||||
if (*S == "off") {
|
||||
Out = TraceLevel::Off;
|
||||
return true;
|
||||
} else if (*S == "messages") {
|
||||
Out = TraceLevel::Messages;
|
||||
return true;
|
||||
} else if (*S == "verbose") {
|
||||
Out = TraceLevel::Verbose;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool parse(const json::Expr &E, FileChangeType &Out) {
|
||||
if (auto T = E.asInteger()) {
|
||||
if (*T < static_cast<int>(FileChangeType::Created) ||
|
||||
*T > static_cast<int>(FileChangeType::Deleted))
|
||||
return false;
|
||||
Out = static_cast<FileChangeType>(*T);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const json::obj *O;
|
||||
};
|
||||
} // namespace
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
|
||||
URI URI::fromUri(llvm::StringRef uri) {
|
||||
URI Result;
|
||||
|
@ -194,276 +50,224 @@ URI URI::fromFile(llvm::StringRef file) {
|
|||
return Result;
|
||||
}
|
||||
|
||||
llvm::Optional<URI> URI::parse(const json::Expr &E) {
|
||||
if (auto S = E.asString())
|
||||
return fromUri(*S);
|
||||
return None;
|
||||
bool fromJSON(const json::Expr &E, URI &R) {
|
||||
if (auto S = E.asString()) {
|
||||
R = URI::fromUri(*S);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
json::Expr URI::unparse(const URI &U) { return U.uri; }
|
||||
json::Expr toJSON(const URI &U) { return U.uri; }
|
||||
|
||||
llvm::Optional<TextDocumentIdentifier>
|
||||
TextDocumentIdentifier::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
TextDocumentIdentifier R;
|
||||
if (!O || !O.parse("uri", R.uri))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, TextDocumentIdentifier &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("uri", R.uri);
|
||||
}
|
||||
|
||||
llvm::Optional<Position> Position::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
Position R;
|
||||
if (!O || !O.parse("line", R.line) || !O.parse("character", R.character))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, Position &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("line", R.line) && O.map("character", R.character);
|
||||
}
|
||||
|
||||
json::Expr Position::unparse(const Position &P) {
|
||||
json::Expr toJSON(const Position &P) {
|
||||
return json::obj{
|
||||
{"line", P.line},
|
||||
{"character", P.character},
|
||||
};
|
||||
}
|
||||
|
||||
llvm::Optional<Range> Range::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
Range R;
|
||||
if (!O || !O.parse("start", R.start) || !O.parse("end", R.end))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, Range &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("start", R.start) && O.map("end", R.end);
|
||||
}
|
||||
|
||||
json::Expr Range::unparse(const Range &P) {
|
||||
json::Expr toJSON(const Range &P) {
|
||||
return json::obj{
|
||||
{"start", P.start},
|
||||
{"end", P.end},
|
||||
};
|
||||
}
|
||||
|
||||
json::Expr Location::unparse(const Location &P) {
|
||||
json::Expr toJSON(const Location &P) {
|
||||
return json::obj{
|
||||
{"uri", P.uri},
|
||||
{"range", P.range},
|
||||
};
|
||||
}
|
||||
|
||||
llvm::Optional<TextDocumentItem>
|
||||
TextDocumentItem::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
TextDocumentItem R;
|
||||
if (!O || !O.parse("uri", R.uri) || !O.parse("languageId", R.languageId) ||
|
||||
!O.parse("version", R.version) || !O.parse("text", R.text))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, TextDocumentItem &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("uri", R.uri) && O.map("languageId", R.languageId) &&
|
||||
O.map("version", R.version) && O.map("text", R.text);
|
||||
}
|
||||
|
||||
llvm::Optional<Metadata> Metadata::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
Metadata R;
|
||||
bool fromJSON(const json::Expr &Params, Metadata &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
if (!O)
|
||||
return None;
|
||||
O.parse("extraFlags", R.extraFlags);
|
||||
return R;
|
||||
return false;
|
||||
O.map("extraFlags", R.extraFlags);
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm::Optional<TextEdit> TextEdit::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
TextEdit R;
|
||||
if (!O || !O.parse("range", R.range) || !O.parse("newText", R.newText))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, TextEdit &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("range", R.range) && O.map("newText", R.newText);
|
||||
}
|
||||
|
||||
json::Expr TextEdit::unparse(const TextEdit &P) {
|
||||
json::Expr toJSON(const TextEdit &P) {
|
||||
return json::obj{
|
||||
{"range", P.range},
|
||||
{"newText", P.newText},
|
||||
};
|
||||
}
|
||||
|
||||
llvm::Optional<InitializeParams>
|
||||
InitializeParams::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
InitializeParams R;
|
||||
bool fromJSON(const json::Expr &E, TraceLevel &Out) {
|
||||
if (auto S = E.asString()) {
|
||||
if (*S == "off") {
|
||||
Out = TraceLevel::Off;
|
||||
return true;
|
||||
} else if (*S == "messages") {
|
||||
Out = TraceLevel::Messages;
|
||||
return true;
|
||||
} else if (*S == "verbose") {
|
||||
Out = TraceLevel::Verbose;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fromJSON(const json::Expr &Params, InitializeParams &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
if (!O)
|
||||
return None;
|
||||
return false;
|
||||
// We deliberately don't fail if we can't parse individual fields.
|
||||
// Failing to handle a slightly malformed initialize would be a disaster.
|
||||
O.parse("processId", R.processId);
|
||||
O.parse("rootUri", R.rootUri);
|
||||
O.parse("rootPath", R.rootPath);
|
||||
O.parse("trace", R.trace);
|
||||
O.map("processId", R.processId);
|
||||
O.map("rootUri", R.rootUri);
|
||||
O.map("rootPath", R.rootPath);
|
||||
O.map("trace", R.trace);
|
||||
// initializationOptions, capabilities unused
|
||||
return R;
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm::Optional<DidOpenTextDocumentParams>
|
||||
DidOpenTextDocumentParams::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
DidOpenTextDocumentParams R;
|
||||
if (!O || !O.parse("textDocument", R.textDocument) ||
|
||||
!O.parse("metadata", R.metadata))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, DidOpenTextDocumentParams &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("textDocument", R.textDocument) &&
|
||||
O.map("metadata", R.metadata);
|
||||
}
|
||||
|
||||
llvm::Optional<DidCloseTextDocumentParams>
|
||||
DidCloseTextDocumentParams::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
DidCloseTextDocumentParams R;
|
||||
if (!O || !O.parse("textDocument", R.textDocument))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, DidCloseTextDocumentParams &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("textDocument", R.textDocument);
|
||||
}
|
||||
|
||||
llvm::Optional<DidChangeTextDocumentParams>
|
||||
DidChangeTextDocumentParams::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
DidChangeTextDocumentParams R;
|
||||
if (!O || !O.parse("textDocument", R.textDocument) ||
|
||||
!O.parse("contentChanges", R.contentChanges))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, DidChangeTextDocumentParams &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("textDocument", R.textDocument) &&
|
||||
O.map("contentChanges", R.contentChanges);
|
||||
}
|
||||
|
||||
llvm::Optional<FileEvent> FileEvent::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
FileEvent R;
|
||||
if (!O || !O.parse("uri", R.uri) || !O.parse("type", R.type))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &E, FileChangeType &Out) {
|
||||
if (auto T = E.asInteger()) {
|
||||
if (*T < static_cast<int>(FileChangeType::Created) ||
|
||||
*T > static_cast<int>(FileChangeType::Deleted))
|
||||
return false;
|
||||
Out = static_cast<FileChangeType>(*T);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
llvm::Optional<DidChangeWatchedFilesParams>
|
||||
DidChangeWatchedFilesParams::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
DidChangeWatchedFilesParams R;
|
||||
if (!O || !O.parse("changes", R.changes))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, FileEvent &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("uri", R.uri) && O.map("type", R.type);
|
||||
}
|
||||
|
||||
llvm::Optional<TextDocumentContentChangeEvent>
|
||||
TextDocumentContentChangeEvent::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
TextDocumentContentChangeEvent R;
|
||||
if (!O || !O.parse("text", R.text))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, DidChangeWatchedFilesParams &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("changes", R.changes);
|
||||
}
|
||||
|
||||
llvm::Optional<FormattingOptions>
|
||||
FormattingOptions::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
FormattingOptions R;
|
||||
if (!O || !O.parse("tabSize", R.tabSize) ||
|
||||
!O.parse("insertSpaces", R.insertSpaces))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, TextDocumentContentChangeEvent &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("text", R.text);
|
||||
}
|
||||
|
||||
json::Expr FormattingOptions::unparse(const FormattingOptions &P) {
|
||||
bool fromJSON(const json::Expr &Params, FormattingOptions &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("tabSize", R.tabSize) &&
|
||||
O.map("insertSpaces", R.insertSpaces);
|
||||
}
|
||||
|
||||
json::Expr toJSON(const FormattingOptions &P) {
|
||||
return json::obj{
|
||||
{"tabSize", P.tabSize},
|
||||
{"insertSpaces", P.insertSpaces},
|
||||
};
|
||||
}
|
||||
|
||||
llvm::Optional<DocumentRangeFormattingParams>
|
||||
DocumentRangeFormattingParams::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
DocumentRangeFormattingParams R;
|
||||
if (!O || !O.parse("textDocument", R.textDocument) ||
|
||||
!O.parse("range", R.range) || !O.parse("options", R.options))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, DocumentRangeFormattingParams &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("textDocument", R.textDocument) &&
|
||||
O.map("range", R.range) && O.map("options", R.options);
|
||||
}
|
||||
|
||||
llvm::Optional<DocumentOnTypeFormattingParams>
|
||||
DocumentOnTypeFormattingParams::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
DocumentOnTypeFormattingParams R;
|
||||
if (!O || !O.parse("textDocument", R.textDocument) ||
|
||||
!O.parse("position", R.position) || !O.parse("ch", R.ch) ||
|
||||
!O.parse("options", R.options))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, DocumentOnTypeFormattingParams &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("textDocument", R.textDocument) &&
|
||||
O.map("position", R.position) && O.map("ch", R.ch) &&
|
||||
O.map("options", R.options);
|
||||
}
|
||||
|
||||
llvm::Optional<DocumentFormattingParams>
|
||||
DocumentFormattingParams::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
DocumentFormattingParams R;
|
||||
if (!O || !O.parse("textDocument", R.textDocument) ||
|
||||
!O.parse("options", R.options))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, DocumentFormattingParams &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("textDocument", R.textDocument) &&
|
||||
O.map("options", R.options);
|
||||
}
|
||||
|
||||
llvm::Optional<Diagnostic> Diagnostic::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
Diagnostic R;
|
||||
if (!O || !O.parse("range", R.range) || !O.parse("message", R.message))
|
||||
return None;
|
||||
O.parse("severity", R.severity);
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, Diagnostic &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
if (!O || !O.map("range", R.range) || !O.map("message", R.message))
|
||||
return false;
|
||||
O.map("severity", R.severity);
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm::Optional<CodeActionContext>
|
||||
CodeActionContext::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
CodeActionContext R;
|
||||
if (!O || !O.parse("diagnostics", R.diagnostics))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, CodeActionContext &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("diagnostics", R.diagnostics);
|
||||
}
|
||||
|
||||
llvm::Optional<CodeActionParams>
|
||||
CodeActionParams::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
CodeActionParams R;
|
||||
if (!O || !O.parse("textDocument", R.textDocument) ||
|
||||
!O.parse("range", R.range) || !O.parse("context", R.context))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, CodeActionParams &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("textDocument", R.textDocument) &&
|
||||
O.map("range", R.range) && O.map("context", R.context);
|
||||
}
|
||||
|
||||
llvm::Optional<WorkspaceEdit> WorkspaceEdit::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
WorkspaceEdit R;
|
||||
if (!O || !O.parse("changes", R.changes))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, WorkspaceEdit &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("changes", R.changes);
|
||||
}
|
||||
|
||||
const std::string ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND =
|
||||
"clangd.applyFix";
|
||||
|
||||
llvm::Optional<ExecuteCommandParams>
|
||||
ExecuteCommandParams::parse(const json::Expr &Params) {
|
||||
const json::obj *O = Params.asObject();
|
||||
if (!O)
|
||||
return None;
|
||||
bool fromJSON(const json::Expr &Params, ExecuteCommandParams &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
if (!O || !O.map("command", R.command))
|
||||
return false;
|
||||
|
||||
ExecuteCommandParams Result;
|
||||
if (auto Command = O->getString("command"))
|
||||
Result.command = *Command;
|
||||
auto Args = O->getArray("arguments");
|
||||
|
||||
if (Result.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND) {
|
||||
if (!Args || Args->size() != 1)
|
||||
return llvm::None;
|
||||
if (auto Parsed = WorkspaceEdit::parse(Args->front()))
|
||||
Result.workspaceEdit = std::move(*Parsed);
|
||||
else
|
||||
return llvm::None;
|
||||
} else
|
||||
return llvm::None; // Unrecognized command.
|
||||
return Result;
|
||||
auto Args = Params.asObject()->getArray("arguments");
|
||||
if (R.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND) {
|
||||
return Args && Args->size() == 1 &&
|
||||
fromJSON(Args->front(), R.workspaceEdit);
|
||||
}
|
||||
return false; // Unrecognized command.
|
||||
}
|
||||
|
||||
json::Expr WorkspaceEdit::unparse(const WorkspaceEdit &WE) {
|
||||
json::Expr toJSON(const WorkspaceEdit &WE) {
|
||||
if (!WE.changes)
|
||||
return json::obj{};
|
||||
json::obj FileChanges;
|
||||
|
@ -472,22 +276,17 @@ json::Expr WorkspaceEdit::unparse(const WorkspaceEdit &WE) {
|
|||
return json::obj{{"changes", std::move(FileChanges)}};
|
||||
}
|
||||
|
||||
json::Expr
|
||||
ApplyWorkspaceEditParams::unparse(const ApplyWorkspaceEditParams &Params) {
|
||||
json::Expr toJSON(const ApplyWorkspaceEditParams &Params) {
|
||||
return json::obj{{"edit", Params.edit}};
|
||||
}
|
||||
|
||||
llvm::Optional<TextDocumentPositionParams>
|
||||
TextDocumentPositionParams::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
TextDocumentPositionParams R;
|
||||
if (!O || !O.parse("textDocument", R.textDocument) ||
|
||||
!O.parse("position", R.position))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, TextDocumentPositionParams &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("textDocument", R.textDocument) &&
|
||||
O.map("position", R.position);
|
||||
}
|
||||
|
||||
json::Expr CompletionItem::unparse(const CompletionItem &CI) {
|
||||
json::Expr toJSON(const CompletionItem &CI) {
|
||||
assert(!CI.label.empty() && "completion item label is required");
|
||||
json::obj Result{{"label", CI.label}};
|
||||
if (CI.kind != CompletionItemKind::Missing)
|
||||
|
@ -511,19 +310,19 @@ json::Expr CompletionItem::unparse(const CompletionItem &CI) {
|
|||
return std::move(Result);
|
||||
}
|
||||
|
||||
bool clangd::operator<(const CompletionItem &L, const CompletionItem &R) {
|
||||
bool operator<(const CompletionItem &L, const CompletionItem &R) {
|
||||
return (L.sortText.empty() ? L.label : L.sortText) <
|
||||
(R.sortText.empty() ? R.label : R.sortText);
|
||||
}
|
||||
|
||||
json::Expr CompletionList::unparse(const CompletionList &L) {
|
||||
json::Expr toJSON(const CompletionList &L) {
|
||||
return json::obj{
|
||||
{"isIncomplete", L.isIncomplete},
|
||||
{"items", json::ary(L.items)},
|
||||
};
|
||||
}
|
||||
|
||||
json::Expr ParameterInformation::unparse(const ParameterInformation &PI) {
|
||||
json::Expr toJSON(const ParameterInformation &PI) {
|
||||
assert(!PI.label.empty() && "parameter information label is required");
|
||||
json::obj Result{{"label", PI.label}};
|
||||
if (!PI.documentation.empty())
|
||||
|
@ -531,7 +330,7 @@ json::Expr ParameterInformation::unparse(const ParameterInformation &PI) {
|
|||
return std::move(Result);
|
||||
}
|
||||
|
||||
json::Expr SignatureInformation::unparse(const SignatureInformation &SI) {
|
||||
json::Expr toJSON(const SignatureInformation &SI) {
|
||||
assert(!SI.label.empty() && "signature information label is required");
|
||||
json::obj Result{
|
||||
{"label", SI.label},
|
||||
|
@ -542,7 +341,7 @@ json::Expr SignatureInformation::unparse(const SignatureInformation &SI) {
|
|||
return std::move(Result);
|
||||
}
|
||||
|
||||
json::Expr SignatureHelp::unparse(const SignatureHelp &SH) {
|
||||
json::Expr toJSON(const SignatureHelp &SH) {
|
||||
assert(SH.activeSignature >= 0 &&
|
||||
"Unexpected negative value for number of active signatures.");
|
||||
assert(SH.activeParameter >= 0 &&
|
||||
|
@ -554,11 +353,11 @@ json::Expr SignatureHelp::unparse(const SignatureHelp &SH) {
|
|||
};
|
||||
}
|
||||
|
||||
llvm::Optional<RenameParams> RenameParams::parse(const json::Expr &Params) {
|
||||
ObjectParser O(Params);
|
||||
RenameParams R;
|
||||
if (!O || !O.parse("textDocument", R.textDocument) ||
|
||||
!O.parse("position", R.position) || !O.parse("newName", R.newName))
|
||||
return None;
|
||||
return R;
|
||||
bool fromJSON(const json::Expr &Params, RenameParams &R) {
|
||||
json::ObjectMapper O(Params);
|
||||
return O && O.map("textDocument", R.textDocument) &&
|
||||
O.map("position", R.position) && O.map("newName", R.newName);
|
||||
}
|
||||
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
// This is not meant to be a complete implementation, new interfaces are added
|
||||
// when they're needed.
|
||||
//
|
||||
// Each struct has a parse and unparse function, that converts back and forth
|
||||
// between the struct and a JSON representation.
|
||||
// Each struct has a toJSON and fromJSON function, that converts between
|
||||
// the struct and a JSON representation. (See JSONExpr.h)
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
@ -51,9 +51,6 @@ struct URI {
|
|||
static URI fromUri(llvm::StringRef uri);
|
||||
static URI fromFile(llvm::StringRef file);
|
||||
|
||||
static llvm::Optional<URI> parse(const json::Expr &U);
|
||||
static json::Expr unparse(const URI &U);
|
||||
|
||||
friend bool operator==(const URI &LHS, const URI &RHS) {
|
||||
return LHS.uri == RHS.uri;
|
||||
}
|
||||
|
@ -66,13 +63,14 @@ struct URI {
|
|||
return LHS.uri < RHS.uri;
|
||||
}
|
||||
};
|
||||
json::Expr toJSON(const URI &U);
|
||||
bool fromJSON(const json::Expr &, URI &);
|
||||
|
||||
struct TextDocumentIdentifier {
|
||||
/// The text document's URI.
|
||||
URI uri;
|
||||
|
||||
static llvm::Optional<TextDocumentIdentifier> parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, TextDocumentIdentifier &);
|
||||
|
||||
struct Position {
|
||||
/// Line position in a document (zero-based).
|
||||
|
@ -89,10 +87,9 @@ struct Position {
|
|||
return std::tie(LHS.line, LHS.character) <
|
||||
std::tie(RHS.line, RHS.character);
|
||||
}
|
||||
|
||||
static llvm::Optional<Position> parse(const json::Expr &Params);
|
||||
static json::Expr unparse(const Position &P);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, Position &);
|
||||
json::Expr toJSON(const Position &);
|
||||
|
||||
struct Range {
|
||||
/// The range's start position.
|
||||
|
@ -107,10 +104,9 @@ struct Range {
|
|||
friend bool operator<(const Range &LHS, const Range &RHS) {
|
||||
return std::tie(LHS.start, LHS.end) < std::tie(RHS.start, RHS.end);
|
||||
}
|
||||
|
||||
static llvm::Optional<Range> parse(const json::Expr &Params);
|
||||
static json::Expr unparse(const Range &P);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, Range &);
|
||||
json::Expr toJSON(const Range &);
|
||||
|
||||
struct Location {
|
||||
/// The text document's URI.
|
||||
|
@ -128,15 +124,13 @@ struct Location {
|
|||
friend bool operator<(const Location &LHS, const Location &RHS) {
|
||||
return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range);
|
||||
}
|
||||
|
||||
static json::Expr unparse(const Location &P);
|
||||
};
|
||||
json::Expr toJSON(const Location &);
|
||||
|
||||
struct Metadata {
|
||||
std::vector<std::string> extraFlags;
|
||||
|
||||
static llvm::Optional<Metadata> parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, Metadata &);
|
||||
|
||||
struct TextEdit {
|
||||
/// The range of the text document to be manipulated. To insert
|
||||
|
@ -146,10 +140,9 @@ struct TextEdit {
|
|||
/// The string to be inserted. For delete operations use an
|
||||
/// empty string.
|
||||
std::string newText;
|
||||
|
||||
static llvm::Optional<TextEdit> parse(const json::Expr &Params);
|
||||
static json::Expr unparse(const TextEdit &P);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, TextEdit &);
|
||||
json::Expr toJSON(const TextEdit &);
|
||||
|
||||
struct TextDocumentItem {
|
||||
/// The text document's URI.
|
||||
|
@ -163,21 +156,18 @@ struct TextDocumentItem {
|
|||
|
||||
/// The content of the opened text document.
|
||||
std::string text;
|
||||
|
||||
static llvm::Optional<TextDocumentItem> parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, TextDocumentItem &);
|
||||
|
||||
enum class TraceLevel {
|
||||
Off = 0,
|
||||
Messages = 1,
|
||||
Verbose = 2,
|
||||
};
|
||||
bool fromJSON(const json::Expr &E, TraceLevel &Out);
|
||||
|
||||
struct NoParams {
|
||||
static llvm::Optional<NoParams> parse(const json::Expr &Params) {
|
||||
return NoParams{};
|
||||
}
|
||||
};
|
||||
struct NoParams {};
|
||||
inline bool fromJSON(const json::Expr &, NoParams &) { return true; }
|
||||
using ShutdownParams = NoParams;
|
||||
using ExitParams = NoParams;
|
||||
|
||||
|
@ -208,8 +198,8 @@ struct InitializeParams {
|
|||
|
||||
/// The initial trace setting. If omitted trace is disabled ('off').
|
||||
llvm::Optional<TraceLevel> trace;
|
||||
static llvm::Optional<InitializeParams> parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, InitializeParams &);
|
||||
|
||||
struct DidOpenTextDocumentParams {
|
||||
/// The document that was opened.
|
||||
|
@ -217,26 +207,20 @@ struct DidOpenTextDocumentParams {
|
|||
|
||||
/// Extension storing per-file metadata, such as compilation flags.
|
||||
llvm::Optional<Metadata> metadata;
|
||||
|
||||
static llvm::Optional<DidOpenTextDocumentParams>
|
||||
parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, DidOpenTextDocumentParams &);
|
||||
|
||||
struct DidCloseTextDocumentParams {
|
||||
/// The document that was closed.
|
||||
TextDocumentIdentifier textDocument;
|
||||
|
||||
static llvm::Optional<DidCloseTextDocumentParams>
|
||||
parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, DidCloseTextDocumentParams &);
|
||||
|
||||
struct TextDocumentContentChangeEvent {
|
||||
/// The new text of the document.
|
||||
std::string text;
|
||||
|
||||
static llvm::Optional<TextDocumentContentChangeEvent>
|
||||
parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, TextDocumentContentChangeEvent &);
|
||||
|
||||
struct DidChangeTextDocumentParams {
|
||||
/// The document that did change. The version number points
|
||||
|
@ -246,10 +230,8 @@ struct DidChangeTextDocumentParams {
|
|||
|
||||
/// The actual content changes.
|
||||
std::vector<TextDocumentContentChangeEvent> contentChanges;
|
||||
|
||||
static llvm::Optional<DidChangeTextDocumentParams>
|
||||
parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, DidChangeTextDocumentParams &);
|
||||
|
||||
enum class FileChangeType {
|
||||
/// The file got created.
|
||||
|
@ -259,23 +241,21 @@ enum class FileChangeType {
|
|||
/// The file got deleted.
|
||||
Deleted = 3
|
||||
};
|
||||
bool fromJSON(const json::Expr &E, FileChangeType &Out);
|
||||
|
||||
struct FileEvent {
|
||||
/// The file's URI.
|
||||
URI uri;
|
||||
/// The change type.
|
||||
FileChangeType type;
|
||||
|
||||
static llvm::Optional<FileEvent> parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, FileEvent &);
|
||||
|
||||
struct DidChangeWatchedFilesParams {
|
||||
/// The actual file events.
|
||||
std::vector<FileEvent> changes;
|
||||
|
||||
static llvm::Optional<DidChangeWatchedFilesParams>
|
||||
parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, DidChangeWatchedFilesParams &);
|
||||
|
||||
struct FormattingOptions {
|
||||
/// Size of a tab in spaces.
|
||||
|
@ -283,10 +263,9 @@ struct FormattingOptions {
|
|||
|
||||
/// Prefer spaces over tabs.
|
||||
bool insertSpaces;
|
||||
|
||||
static llvm::Optional<FormattingOptions> parse(const json::Expr &Params);
|
||||
static json::Expr unparse(const FormattingOptions &P);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, FormattingOptions &);
|
||||
json::Expr toJSON(const FormattingOptions &);
|
||||
|
||||
struct DocumentRangeFormattingParams {
|
||||
/// The document to format.
|
||||
|
@ -297,10 +276,8 @@ struct DocumentRangeFormattingParams {
|
|||
|
||||
/// The format options
|
||||
FormattingOptions options;
|
||||
|
||||
static llvm::Optional<DocumentRangeFormattingParams>
|
||||
parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, DocumentRangeFormattingParams &);
|
||||
|
||||
struct DocumentOnTypeFormattingParams {
|
||||
/// The document to format.
|
||||
|
@ -314,10 +291,8 @@ struct DocumentOnTypeFormattingParams {
|
|||
|
||||
/// The format options.
|
||||
FormattingOptions options;
|
||||
|
||||
static llvm::Optional<DocumentOnTypeFormattingParams>
|
||||
parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, DocumentOnTypeFormattingParams &);
|
||||
|
||||
struct DocumentFormattingParams {
|
||||
/// The document to format.
|
||||
|
@ -325,10 +300,8 @@ struct DocumentFormattingParams {
|
|||
|
||||
/// The format options
|
||||
FormattingOptions options;
|
||||
|
||||
static llvm::Optional<DocumentFormattingParams>
|
||||
parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, DocumentFormattingParams &);
|
||||
|
||||
struct Diagnostic {
|
||||
/// The range at which the message applies.
|
||||
|
@ -358,16 +331,14 @@ struct Diagnostic {
|
|||
return std::tie(LHS.range, LHS.severity, LHS.message) <
|
||||
std::tie(RHS.range, RHS.severity, RHS.message);
|
||||
}
|
||||
|
||||
static llvm::Optional<Diagnostic> parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, Diagnostic &);
|
||||
|
||||
struct CodeActionContext {
|
||||
/// An array of diagnostics.
|
||||
std::vector<Diagnostic> diagnostics;
|
||||
|
||||
static llvm::Optional<CodeActionContext> parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, CodeActionContext &);
|
||||
|
||||
struct CodeActionParams {
|
||||
/// The document in which the command was invoked.
|
||||
|
@ -378,9 +349,8 @@ struct CodeActionParams {
|
|||
|
||||
/// Context carrying additional information.
|
||||
CodeActionContext context;
|
||||
|
||||
static llvm::Optional<CodeActionParams> parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, CodeActionParams &);
|
||||
|
||||
struct WorkspaceEdit {
|
||||
/// Holds changes to existing resources.
|
||||
|
@ -388,10 +358,9 @@ struct WorkspaceEdit {
|
|||
|
||||
/// Note: "documentChanges" is not currently used because currently there is
|
||||
/// no support for versioned edits.
|
||||
|
||||
static llvm::Optional<WorkspaceEdit> parse(const json::Expr &Params);
|
||||
static json::Expr unparse(const WorkspaceEdit &WE);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, WorkspaceEdit &);
|
||||
json::Expr toJSON(const WorkspaceEdit &WE);
|
||||
|
||||
/// Exact commands are not specified in the protocol so we define the
|
||||
/// ones supported by Clangd here. The protocol specifies the command arguments
|
||||
|
@ -411,14 +380,13 @@ struct ExecuteCommandParams {
|
|||
// Arguments
|
||||
|
||||
llvm::Optional<WorkspaceEdit> workspaceEdit;
|
||||
|
||||
static llvm::Optional<ExecuteCommandParams> parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, ExecuteCommandParams &);
|
||||
|
||||
struct ApplyWorkspaceEditParams {
|
||||
WorkspaceEdit edit;
|
||||
static json::Expr unparse(const ApplyWorkspaceEditParams &Params);
|
||||
};
|
||||
json::Expr toJSON(const ApplyWorkspaceEditParams &);
|
||||
|
||||
struct TextDocumentPositionParams {
|
||||
/// The text document.
|
||||
|
@ -426,10 +394,8 @@ struct TextDocumentPositionParams {
|
|||
|
||||
/// The position inside the text document.
|
||||
Position position;
|
||||
|
||||
static llvm::Optional<TextDocumentPositionParams>
|
||||
parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, TextDocumentPositionParams &);
|
||||
|
||||
/// The kind of a completion entry.
|
||||
enum class CompletionItemKind {
|
||||
|
@ -524,8 +490,8 @@ struct CompletionItem {
|
|||
//
|
||||
// data?: any - A data entry field that is preserved on a completion item
|
||||
// between a completion and a completion resolve request.
|
||||
static json::Expr unparse(const CompletionItem &P);
|
||||
};
|
||||
json::Expr toJSON(const CompletionItem &);
|
||||
|
||||
bool operator<(const CompletionItem &, const CompletionItem &);
|
||||
|
||||
|
@ -537,9 +503,8 @@ struct CompletionList {
|
|||
|
||||
/// The completion items.
|
||||
std::vector<CompletionItem> items;
|
||||
|
||||
static json::Expr unparse(const CompletionList &);
|
||||
};
|
||||
json::Expr toJSON(const CompletionList &);
|
||||
|
||||
/// A single parameter of a particular signature.
|
||||
struct ParameterInformation {
|
||||
|
@ -549,9 +514,8 @@ struct ParameterInformation {
|
|||
|
||||
/// The documentation of this parameter. Optional.
|
||||
std::string documentation;
|
||||
|
||||
static json::Expr unparse(const ParameterInformation &);
|
||||
};
|
||||
json::Expr toJSON(const ParameterInformation &);
|
||||
|
||||
/// Represents the signature of something callable.
|
||||
struct SignatureInformation {
|
||||
|
@ -564,9 +528,8 @@ struct SignatureInformation {
|
|||
|
||||
/// The parameters of this signature.
|
||||
std::vector<ParameterInformation> parameters;
|
||||
|
||||
static json::Expr unparse(const SignatureInformation &);
|
||||
};
|
||||
json::Expr toJSON(const SignatureInformation &);
|
||||
|
||||
/// Represents the signature of a callable.
|
||||
struct SignatureHelp {
|
||||
|
@ -579,9 +542,8 @@ struct SignatureHelp {
|
|||
|
||||
/// The active parameter of the active signature.
|
||||
int activeParameter = 0;
|
||||
|
||||
static json::Expr unparse(const SignatureHelp &);
|
||||
};
|
||||
json::Expr toJSON(const SignatureHelp &);
|
||||
|
||||
struct RenameParams {
|
||||
/// The document that was opened.
|
||||
|
@ -592,9 +554,8 @@ struct RenameParams {
|
|||
|
||||
/// The new name of the symbol.
|
||||
std::string newName;
|
||||
|
||||
static llvm::Optional<RenameParams> parse(const json::Expr &Params);
|
||||
};
|
||||
bool fromJSON(const json::Expr &, RenameParams &);
|
||||
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace {
|
|||
// Helper for attaching ProtocolCallbacks methods to a JSONRPCDispatcher.
|
||||
// Invoke like: Registerer("foo", &ProtocolCallbacks::onFoo)
|
||||
// onFoo should be: void onFoo(Ctx &C, FooParams &Params)
|
||||
// FooParams should have a static factory method: parse(const json::Expr&).
|
||||
// FooParams should have a fromJSON function.
|
||||
struct HandlerRegisterer {
|
||||
template <typename Param>
|
||||
void operator()(StringRef Method,
|
||||
|
@ -31,11 +31,9 @@ struct HandlerRegisterer {
|
|||
auto *Callbacks = this->Callbacks;
|
||||
Dispatcher.registerHandler(
|
||||
Method, [=](RequestContext C, const json::Expr &RawParams) {
|
||||
if (auto P = [&] {
|
||||
trace::Span Tracer("Parse");
|
||||
return std::decay<Param>::type::parse(RawParams);
|
||||
}()) {
|
||||
(Callbacks->*Handler)(std::move(C), *P);
|
||||
typename std::remove_reference<Param>::type P;
|
||||
if (fromJSON(RawParams, P)) {
|
||||
(Callbacks->*Handler)(std::move(C), P);
|
||||
} else {
|
||||
Out->log("Failed to decode " + Method + " request.\n");
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ Content-Length: 152
|
|||
# CHECK: {"displayTimeUnit":"ns","traceEvents":[
|
||||
# Start opening the doc.
|
||||
# CHECK: "name": "textDocument/didOpen"
|
||||
# CHECK: "ph": "E"
|
||||
# CHECK: "ph": "B"
|
||||
# Start building the preamble.
|
||||
# CHECK: "name": "Preamble"
|
||||
# CHECK: },
|
||||
|
|
|
@ -229,6 +229,64 @@ TEST(JSONTest, Inspection) {
|
|||
}
|
||||
}
|
||||
|
||||
// Sample struct with typical JSON-mapping rules.
|
||||
struct CustomStruct {
|
||||
CustomStruct() : B(false) {}
|
||||
CustomStruct(std::string S, llvm::Optional<int> I, bool B)
|
||||
: S(S), I(I), B(B) {}
|
||||
std::string S;
|
||||
llvm::Optional<int> I;
|
||||
bool B;
|
||||
};
|
||||
inline bool operator==(const CustomStruct &L, const CustomStruct &R) {
|
||||
return L.S == R.S && L.I == R.I && L.B == R.B;
|
||||
}
|
||||
inline std::ostream &operator<<(std::ostream &OS, const CustomStruct &S) {
|
||||
return OS << "(" << S.S << ", " << (S.I ? std::to_string(*S.I) : "None")
|
||||
<< ", " << S.B << ")";
|
||||
}
|
||||
bool fromJSON(const json::Expr &E, CustomStruct &R) {
|
||||
ObjectMapper O(E);
|
||||
if (!O || !O.map("str", R.S) || !O.map("int", R.I))
|
||||
return false;
|
||||
O.map("bool", R.B);
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(JSONTest, Deserialize) {
|
||||
std::map<std::string, std::vector<CustomStruct>> R;
|
||||
CustomStruct ExpectedStruct = {"foo", 42, true};
|
||||
std::map<std::string, std::vector<CustomStruct>> Expected;
|
||||
Expr J = obj{{"foo", ary{
|
||||
obj{
|
||||
{"str", "foo"},
|
||||
{"int", 42},
|
||||
{"bool", true},
|
||||
{"unknown", "ignored"},
|
||||
},
|
||||
obj{{"str", "bar"}},
|
||||
obj{
|
||||
{"str", "baz"},
|
||||
{"bool", "string"}, // OK, deserialize ignores.
|
||||
},
|
||||
}}};
|
||||
Expected["foo"] = {
|
||||
CustomStruct("foo", 42, true),
|
||||
CustomStruct("bar", llvm::None, false),
|
||||
CustomStruct("baz", llvm::None, false),
|
||||
};
|
||||
ASSERT_TRUE(fromJSON(J, R));
|
||||
EXPECT_EQ(R, Expected);
|
||||
|
||||
CustomStruct V;
|
||||
EXPECT_FALSE(fromJSON(nullptr, V)) << "Not an object " << V;
|
||||
EXPECT_FALSE(fromJSON(obj{}, V)) << "Missing required field " << V;
|
||||
EXPECT_FALSE(fromJSON(obj{{"str", 1}}, V)) << "Wrong type " << V;
|
||||
// Optional<T> must parse as the correct type if present.
|
||||
EXPECT_FALSE(fromJSON(obj{{"str", 1}, {"int", "string"}}, V))
|
||||
<< "Wrong type for Optional<T> " << V;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace json
|
||||
} // namespace clangd
|
||||
|
|
Loading…
Reference in New Issue