diff options
Diffstat (limited to 'clangd/JSONRPCDispatcher.cpp')
-rw-r--r-- | clangd/JSONRPCDispatcher.cpp | 78 |
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; } |