[clangd] Refactor ProtocolHandlers to decouple them from ClangdLSPServer

Summary:
A refactoring to decouple ProtocolHandlers and Language Server input parsing
loop from the ClangdLSPServer.
The input parsing was extracted from `main` to a function(runLanguageServerLoop).
ProtocolHandlers now provide an interface to handle various LSP methods,
this interface is used by ClangdLSPServer.
Methods for code formatting were moved from ProtocolHandlers to ClangdServer.
ClangdLSPServer now provides a cleaner interface that only runs Language Server
input loop.

Reviewers: bkramer, krasimir

Reviewed By: krasimir

Subscribers: cfe-commits, klimek

Tags: #clang-tools-extra

Differential Revision: https://reviews.llvm.org/D33201

llvm-svn: 303173
diff --git a/clang-tools-extra/clangd/JSONRPCDispatcher.cpp b/clang-tools-extra/clangd/JSONRPCDispatcher.cpp
index f3045fb..be6be77 100644
--- a/clang-tools-extra/clangd/JSONRPCDispatcher.cpp
+++ b/clang-tools-extra/clangd/JSONRPCDispatcher.cpp
@@ -129,3 +129,61 @@
 
   return true;
 }
+
+void clangd::runLanguageServerLoop(std::istream &In, JSONOutput &Out,
+                                   JSONRPCDispatcher &Dispatcher,
+                                   bool &IsDone) {
+  while (In.good()) {
+    // A Language Server Protocol message starts with a HTTP header, delimited
+    // by \r\n.
+    std::string Line;
+    std::getline(In, Line);
+    if (!In.good() && errno == EINTR) {
+      In.clear();
+      continue;
+    }
+
+    // Skip empty lines.
+    llvm::StringRef LineRef(Line);
+    if (LineRef.trim().empty())
+      continue;
+
+    // We allow YAML-style comments. Technically this isn't part of the
+    // LSP specification, but makes writing tests easier.
+    if (LineRef.startswith("#"))
+      continue;
+
+    unsigned long long Len = 0;
+    // FIXME: Content-Type is a specified header, but does nothing.
+    // Content-Length is a mandatory header. It specifies the length of the
+    // following JSON.
+    if (LineRef.consume_front("Content-Length: "))
+      llvm::getAsUnsignedInteger(LineRef.trim(), 0, Len);
+
+    // Check if the next line only contains \r\n. If not this is another header,
+    // which we ignore.
+    char NewlineBuf[2];
+    In.read(NewlineBuf, 2);
+    if (std::memcmp(NewlineBuf, "\r\n", 2) != 0)
+      continue;
+
+    // Now read the JSON. Insert a trailing null byte as required by the YAML
+    // parser.
+    std::vector<char> JSON(Len + 1, '\0');
+    In.read(JSON.data(), Len);
+
+    if (Len > 0) {
+      llvm::StringRef JSONRef(JSON.data(), Len);
+      // Log the message.
+      Out.log("<-- " + JSONRef + "\n");
+
+      // Finally, execute the action for this JSON message.
+      if (!Dispatcher.call(JSONRef))
+        Out.log("JSON dispatch failed!\n");
+
+      // If we're done, exit the loop.
+      if (IsDone)
+        break;
+    }
+  }
+}