aboutsummaryrefslogtreecommitdiff
path: root/clangd/JSONRPCDispatcher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clangd/JSONRPCDispatcher.cpp')
-rw-r--r--clangd/JSONRPCDispatcher.cpp78
1 files changed, 56 insertions, 22 deletions
diff --git a/clangd/JSONRPCDispatcher.cpp b/clangd/JSONRPCDispatcher.cpp
index 121ddb9b..557fadba 100644
--- a/clangd/JSONRPCDispatcher.cpp
+++ b/clangd/JSONRPCDispatcher.cpp
@@ -8,9 +8,11 @@
//===----------------------------------------------------------------------===//
#include "JSONRPCDispatcher.h"
+#include "JSONExpr.h"
#include "ProtocolHandlers.h"
#include "Trace.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/YAMLParser.h"
#include <istream>
@@ -18,17 +20,22 @@
using namespace clang;
using namespace clangd;
-void JSONOutput::writeMessage(const Twine &Message) {
- llvm::SmallString<128> Storage;
- StringRef M = Message.toStringRef(Storage);
+void JSONOutput::writeMessage(const json::Expr &Message) {
+ std::string S;
+ llvm::raw_string_ostream OS(S);
+ if (Pretty)
+ OS << llvm::formatv("{0:2}", Message);
+ else
+ OS << Message;
+ OS.flush();
std::lock_guard<std::mutex> Guard(StreamMutex);
// Log without headers.
- Logs << "--> " << M << '\n';
+ Logs << "--> " << S << '\n';
Logs.flush();
// Emit message with header.
- Outs << "Content-Length: " << M.size() << "\r\n\r\n" << M;
+ Outs << "Content-Length: " << S.size() << "\r\n\r\n" << S;
Outs.flush();
}
@@ -47,25 +54,40 @@ void JSONOutput::mirrorInput(const Twine &Message) {
InputMirror->flush();
}
-void RequestContext::reply(const llvm::Twine &Result) {
- if (ID.empty()) {
+void RequestContext::reply(json::Expr &&Result) {
+ if (!ID) {
Out.log("Attempted to reply to a notification!\n");
return;
}
- Out.writeMessage(llvm::Twine(R"({"jsonrpc":"2.0","id":)") + ID +
- R"(,"result":)" + Result + "}");
+ Out.writeMessage(json::obj{
+ {"jsonrpc", "2.0"},
+ {"id", *ID},
+ {"result", std::move(Result)},
+ });
}
-void RequestContext::replyError(int code, const llvm::StringRef &Message) {
- Out.log("Error " + llvm::Twine(code) + ": " + Message + "\n");
- if (!ID.empty()) {
- Out.writeMessage(llvm::Twine(R"({"jsonrpc":"2.0","id":)") + ID +
- R"(,"error":{"code":)" + llvm::Twine(code) +
- R"(,"message":")" + llvm::yaml::escape(Message) +
- R"("}})");
+void RequestContext::replyError(ErrorCode code, const llvm::StringRef &Message) {
+ Out.log("Error " + Twine(static_cast<int>(code)) + ": " + Message + "\n");
+ if (ID) {
+ Out.writeMessage(json::obj{
+ {"jsonrpc", "2.0"},
+ {"id", *ID},
+ {"error", json::obj{{"code", static_cast<int>(code)}, {"message", Message}}},
+ });
}
}
+void RequestContext::call(StringRef Method, json::Expr &&Params) {
+ // FIXME: Generate/Increment IDs for every request so that we can get proper
+ // replies once we need to.
+ Out.writeMessage(json::obj{
+ {"jsonrpc", "2.0"},
+ {"id", 1},
+ {"method", Method},
+ {"params", std::move(Params)},
+ });
+}
+
void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) {
assert(!Handlers.count(Method) && "Handler already registered!");
Handlers[Method] = std::move(H);
@@ -73,7 +95,7 @@ void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) {
static void
callHandler(const llvm::StringMap<JSONRPCDispatcher::Handler> &Handlers,
- llvm::yaml::ScalarNode *Method, llvm::yaml::ScalarNode *Id,
+ llvm::yaml::ScalarNode *Method, llvm::Optional<json::Expr> ID,
llvm::yaml::MappingNode *Params,
const JSONRPCDispatcher::Handler &UnknownHandler, JSONOutput &Out) {
llvm::SmallString<64> MethodStorage;
@@ -81,7 +103,7 @@ callHandler(const llvm::StringMap<JSONRPCDispatcher::Handler> &Handlers,
auto I = Handlers.find(MethodStr);
auto &Handler = I != Handlers.end() ? I->second : UnknownHandler;
trace::Span Tracer(MethodStr);
- Handler(RequestContext(Out, Id ? Id->getRawValue() : ""), Params);
+ Handler(RequestContext(Out, std::move(ID)), Params);
}
bool JSONRPCDispatcher::call(StringRef Content, JSONOutput &Out) const {
@@ -99,7 +121,7 @@ bool JSONRPCDispatcher::call(StringRef Content, JSONOutput &Out) const {
llvm::yaml::ScalarNode *Version = nullptr;
llvm::yaml::ScalarNode *Method = nullptr;
llvm::yaml::MappingNode *Params = nullptr;
- llvm::yaml::ScalarNode *Id = nullptr;
+ llvm::Optional<json::Expr> ID;
for (auto &NextKeyValue : *Object) {
auto *KeyString =
dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
@@ -120,7 +142,19 @@ bool JSONRPCDispatcher::call(StringRef Content, JSONOutput &Out) const {
} else if (KeyValue == "method") {
Method = dyn_cast<llvm::yaml::ScalarNode>(Value);
} else if (KeyValue == "id") {
- Id = dyn_cast<llvm::yaml::ScalarNode>(Value);
+ // ID may be either a string or a number.
+ if (auto *IdNode = dyn_cast<llvm::yaml::ScalarNode>(Value)) {
+ llvm::SmallString<32> S;
+ llvm::StringRef V = IdNode->getValue(S);
+ if (IdNode->getRawValue().startswith("\"")) {
+ ID.emplace(V.str());
+ } else {
+ double D;
+ // FIXME: this is locale-sensitive.
+ if (llvm::to_float(V, D))
+ ID.emplace(D);
+ }
+ }
} else if (KeyValue == "params") {
if (!Method)
return false;
@@ -129,7 +163,7 @@ bool JSONRPCDispatcher::call(StringRef Content, JSONOutput &Out) const {
// because it will break clients that put the id after params. A possible
// fix would be to split the parsing and execution phases.
Params = dyn_cast<llvm::yaml::MappingNode>(Value);
- callHandler(Handlers, Method, Id, Params, UnknownHandler, Out);
+ callHandler(Handlers, Method, std::move(ID), Params, UnknownHandler, Out);
return true;
} else {
return false;
@@ -140,7 +174,7 @@ bool JSONRPCDispatcher::call(StringRef Content, JSONOutput &Out) const {
// leftovers.
if (!Method)
return false;
- callHandler(Handlers, Method, Id, nullptr, UnknownHandler, Out);
+ callHandler(Handlers, Method, std::move(ID), nullptr, UnknownHandler, Out);
return true;
}