#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_LSPBINDER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_LSPBINDER_H
#include "Protocol.h"
#include "support/Function.h"
#include "support/Logger.h"
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/JSON.h"
namespace clang {
namespace clangd {
class LSPBinder {
public:
using JSON = llvm::json::Value;
struct RawHandlers {
template <typename HandlerT>
using HandlerMap = llvm::StringMap<llvm::unique_function<HandlerT>>;
HandlerMap<void(JSON)> NotificationHandlers;
HandlerMap<void(JSON, Callback<JSON>)> MethodHandlers;
HandlerMap<void(JSON, Callback<JSON>)> CommandHandlers;
};
class RawOutgoing {
public:
virtual ~RawOutgoing() = default;
virtual void callMethod(llvm::StringRef Method, JSON Params,
Callback<JSON> Reply) = 0;
virtual void notify(llvm::StringRef Method, JSON Params) = 0;
};
LSPBinder(RawHandlers &Raw, RawOutgoing &Out) : Raw(Raw), Out(Out) {}
template <typename Param, typename Result, typename ThisT>
void method(llvm::StringLiteral Method, ThisT *This,
void (ThisT::*Handler)(const Param &, Callback<Result>));
template <typename Param, typename ThisT>
void notification(llvm::StringLiteral Method, ThisT *This,
void (ThisT::*Handler)(const Param &));
template <typename Param, typename Result, typename ThisT>
void command(llvm::StringLiteral Command, ThisT *This,
void (ThisT::*Handler)(const Param &, Callback<Result>));
template <typename P, typename R>
using OutgoingMethod = llvm::unique_function<void(const P &, Callback<R>)>;
class UntypedOutgoingMethod;
UntypedOutgoingMethod outgoingMethod(llvm::StringLiteral Method);
template <typename P>
using OutgoingNotification = llvm::unique_function<void(const P &)>;
class UntypedOutgoingNotification;
UntypedOutgoingNotification outgoingNotification(llvm::StringLiteral Method);
private:
template <typename T>
static llvm::Expected<T> parse(const llvm::json::Value &Raw,
llvm::StringRef PayloadName,
llvm::StringRef PayloadKind);
RawHandlers &Raw;
RawOutgoing &Out;
};
template <typename T>
llvm::Expected<T> LSPBinder::parse(const llvm::json::Value &Raw,
llvm::StringRef PayloadName,
llvm::StringRef PayloadKind) {
T Result;
llvm::json::Path::Root Root;
if (!fromJSON(Raw, Result, Root)) {
elog("Failed to decode {0} {1}: {2}", PayloadName, PayloadKind,
Root.getError());
std::string Context;
llvm::raw_string_ostream OS(Context);
Root.printErrorContext(Raw, OS);
vlog("{0}", OS.str());
return llvm::make_error<LSPError>(
llvm::formatv("failed to decode {0} {1}: {2}", PayloadName, PayloadKind,
fmt_consume(Root.getError())),
ErrorCode::InvalidParams);
}
return std::move(Result);
}
template <typename Param, typename Result, typename ThisT>
void LSPBinder::method(llvm::StringLiteral Method, ThisT *This,
void (ThisT::*Handler)(const Param &,
Callback<Result>)) {
Raw.MethodHandlers[Method] = [Method, Handler, This](JSON RawParams,
Callback<JSON> Reply) {
auto P = LSPBinder::parse<Param>(RawParams, Method, "request");
if (!P)
return Reply(P.takeError());
(This->*Handler)(*P, std::move(Reply));
};
}
template <typename Param, typename ThisT>
void LSPBinder::notification(llvm::StringLiteral Method, ThisT *This,
void (ThisT::*Handler)(const Param &)) {
Raw.NotificationHandlers[Method] = [Method, Handler, This](JSON RawParams) {
llvm::Expected<Param> P =
LSPBinder::parse<Param>(RawParams, Method, "request");
if (!P)
return llvm::consumeError(P.takeError());
(This->*Handler)(*P);
};
}
template <typename Param, typename Result, typename ThisT>
void LSPBinder::command(llvm::StringLiteral Method, ThisT *This,
void (ThisT::*Handler)(const Param &,
Callback<Result>)) {
Raw.CommandHandlers[Method] = [Method, Handler, This](JSON RawParams,
Callback<JSON> Reply) {
auto P = LSPBinder::parse<Param>(RawParams, Method, "command");
if (!P)
return Reply(P.takeError());
(This->*Handler)(*P, std::move(Reply));
};
}
class LSPBinder::UntypedOutgoingNotification {
llvm::StringLiteral Method;
RawOutgoing *Out;
UntypedOutgoingNotification(llvm::StringLiteral Method, RawOutgoing *Out)
: Method(Method), Out(Out) {}
friend UntypedOutgoingNotification
LSPBinder::outgoingNotification(llvm::StringLiteral);
public:
template <typename Request> operator OutgoingNotification<Request>() && {
return
[Method(Method), Out(Out)](Request R) { Out->notify(Method, JSON(R)); };
}
};
inline LSPBinder::UntypedOutgoingNotification
LSPBinder::outgoingNotification(llvm::StringLiteral Method) {
return UntypedOutgoingNotification(Method, &Out);
}
class LSPBinder::UntypedOutgoingMethod {
llvm::StringLiteral Method;
RawOutgoing *Out;
UntypedOutgoingMethod(llvm::StringLiteral Method, RawOutgoing *Out)
: Method(Method), Out(Out) {}
friend UntypedOutgoingMethod LSPBinder::outgoingMethod(llvm::StringLiteral);
public:
template <typename Request, typename Response>
operator OutgoingMethod<Request, Response>() && {
return [Method(Method), Out(Out)](Request R, Callback<Response> Reply) {
Out->callMethod(
Method, JSON(R),
[Reply(std::move(Reply)), Ctx(Context::current().clone()),
Method](llvm::Expected<JSON> RawRsp) mutable {
if (!RawRsp)
return Reply(RawRsp.takeError());
Reply(LSPBinder::parse<Response>(std::move(*RawRsp), Method,
"reply"));
});
};
}
};
inline LSPBinder::UntypedOutgoingMethod
LSPBinder::outgoingMethod(llvm::StringLiteral Method) {
return UntypedOutgoingMethod(Method, &Out);
}
}
}
#endif