summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clangd/JSONRPCDispatcher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clangd/JSONRPCDispatcher.cpp')
-rw-r--r--clang-tools-extra/clangd/JSONRPCDispatcher.cpp124
1 files changed, 124 insertions, 0 deletions
diff --git a/clang-tools-extra/clangd/JSONRPCDispatcher.cpp b/clang-tools-extra/clangd/JSONRPCDispatcher.cpp
new file mode 100644
index 00000000000..5f82f0abc75
--- /dev/null
+++ b/clang-tools-extra/clangd/JSONRPCDispatcher.cpp
@@ -0,0 +1,124 @@
+//===--- JSONRPCDispatcher.cpp - Main JSON parser entry point -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "JSONRPCDispatcher.h"
+#include "ProtocolHandlers.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/YAMLParser.h"
+using namespace clang;
+using namespace clangd;
+
+void Handler::writeMessage(const Twine &Message) {
+ llvm::SmallString<128> Storage;
+ StringRef M = Message.toStringRef(Storage);
+
+ // Log without headers.
+ Logs << "--> " << M << '\n';
+ Logs.flush();
+
+ // Emit message with header.
+ Outs << "Content-Length: " << M.size() << "\r\n\r\n" << M;
+ Outs.flush();
+}
+
+void Handler::handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) {
+ Logs << "Method ignored.\n";
+ // Return that this method is unsupported.
+ writeMessage(
+ R"({"jsonrpc":"2.0","id":)" + ID +
+ R"(,"error":{"code":-32601}})");
+}
+
+void Handler::handleNotification(llvm::yaml::MappingNode *Params) {
+ Logs << "Notification ignored.\n";
+}
+
+void JSONRPCDispatcher::registerHandler(StringRef Method,
+ std::unique_ptr<Handler> H) {
+ assert(!Handlers.count(Method) && "Handler already registered!");
+ Handlers[Method] = std::move(H);
+}
+
+static void
+callHandler(const llvm::StringMap<std::unique_ptr<Handler>> &Handlers,
+ llvm::yaml::ScalarNode *Method, llvm::yaml::ScalarNode *Id,
+ llvm::yaml::MappingNode *Params, Handler *UnknownHandler) {
+ llvm::SmallString<10> MethodStorage;
+ auto I = Handlers.find(Method->getValue(MethodStorage));
+ auto *Handler = I != Handlers.end() ? I->second.get() : UnknownHandler;
+ if (Id)
+ Handler->handleMethod(Params, Id->getRawValue());
+ else
+ Handler->handleNotification(Params);
+}
+
+bool JSONRPCDispatcher::call(StringRef Content) const {
+ llvm::SourceMgr SM;
+ llvm::yaml::Stream YAMLStream(Content, SM);
+
+ auto Doc = YAMLStream.begin();
+ if (Doc == YAMLStream.end())
+ return false;
+
+ auto *Root = Doc->getRoot();
+ if (!Root)
+ return false;
+
+ auto *Object = dyn_cast<llvm::yaml::MappingNode>(Root);
+ if (!Object)
+ return false;
+
+ llvm::yaml::ScalarNode *Version = nullptr;
+ llvm::yaml::ScalarNode *Method = nullptr;
+ llvm::yaml::MappingNode *Params = nullptr;
+ llvm::yaml::ScalarNode *Id = nullptr;
+ for (auto &NextKeyValue : *Object) {
+ auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+ if (!KeyString)
+ return false;
+
+ llvm::SmallString<10> KeyStorage;
+ StringRef KeyValue = KeyString->getValue(KeyStorage);
+ llvm::yaml::Node *Value = NextKeyValue.getValue();
+ if (!Value)
+ return false;
+
+ if (KeyValue == "jsonrpc") {
+ // This should be "2.0". Always.
+ Version = dyn_cast<llvm::yaml::ScalarNode>(Value);
+ if (!Version || Version->getRawValue() != "\"2.0\"")
+ return false;
+ } else if (KeyValue == "method") {
+ Method = dyn_cast<llvm::yaml::ScalarNode>(Value);
+ } else if (KeyValue == "id") {
+ Id = dyn_cast<llvm::yaml::ScalarNode>(Value);
+ } else if (KeyValue == "params") {
+ if (!Method)
+ return false;
+ // We have to interleave the call of the function here, otherwise the
+ // YAMLParser will die because it can't go backwards. This is unfortunate
+ // 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.get());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // In case there was a request with no params, call the handler on the
+ // leftovers.
+ if (!Method)
+ return false;
+ callHandler(Handlers, Method, Id, nullptr, UnknownHandler.get());
+
+ return true;
+}
OpenPOWER on IntegriCloud