jkorous created this revision.
jkorous added a project: clang-tools-extra.
Herald added subscribers: cfe-commits, MaskRay, ioeric, ilya-biryukov, mgorny.

Hi all,

We finally finished a self-contained first version of our implementation of 
alternative transport layer for macOS based on XPC.

To enable this I did couple of changes to current clangd design. Generally I 
aimed for small amount of disruption and tried to keep it simple.

The notable changes are:

- enabled registration of request handlers to different dispatchers by 
templatizing registerCallbackHandlers()
- factored out parts of JsonDispatcher that could be reused (created 
DispatcherCommon.h/cpp)
- created abstraction over JsonOutput (class LSPOutput)
- removed of ClangdLSPServer::run() method so server can be run with different 
dispatcher
- published IsDone and ShutdownRequestReceived through methods in 
ClangdLSPServer class interface to support new dispatcher

We are also putting up the transport layer implementation itself for a review 
soon so it will be more obvious where are we going to and what motivated some 
of these changes.

Big thanks in advance to all reviewers!

Jan


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D48559

Files:
  CMakeLists.txt
  ClangdLSPServer.cpp
  ClangdLSPServer.h
  DispatcherCommon.cpp
  DispatcherCommon.h
  JSONRPCDispatcher.cpp
  JSONRPCDispatcher.h
  LSPOutput.h
  ProtocolHandlers.cpp
  ProtocolHandlers.h
  tool/ClangdMain.cpp

Index: tool/ClangdMain.cpp
===================================================================
--- tool/ClangdMain.cpp
+++ tool/ClangdMain.cpp
@@ -19,7 +19,6 @@
 #include "llvm/Support/Signals.h"
 #include "llvm/Support/raw_ostream.h"
 #include <cstdlib>
-#include <iostream>
 #include <memory>
 #include <string>
 #include <thread>
@@ -207,12 +206,6 @@
   if (Tracer)
     TracingSession.emplace(*Tracer);
 
-  JSONOutput Out(llvm::outs(), llvm::errs(),
-                 InputMirrorStream ? InputMirrorStream.getPointer() : nullptr,
-                 PrettyPrint);
-
-  clangd::LoggingSession LoggingSession(Out);
-
   // If --compile-commands-dir arg was invoked, check value and override default
   // path.
   llvm::Optional<Path> CompileCommandsDirPath;
@@ -252,11 +245,27 @@
   CCOpts.Limit = LimitResults;
   CCOpts.BundleOverloads = CompletionStyle != Detailed;
 
-  // Initialize and run ClangdLSPServer.
-  ClangdLSPServer LSPServer(Out, CCOpts, CompileCommandsDirPath, Opts);
-  constexpr int NoShutdownRequestErrorCode = 1;
+  ClangdLSPServer LSPServer(CCOpts, CompileCommandsDirPath, Opts);
+
   llvm::set_thread_name("clangd.main");
   // Change stdin to binary to not lose \r\n on windows.
   llvm::sys::ChangeStdinToBinary();
-  return LSPServer.run(stdin, InputStyle) ? 0 : NoShutdownRequestErrorCode;
+  {
+    JSONRPCDispatcher Dispatcher([](const json::Expr &Params) {
+      replyError(ErrorCode::MethodNotFound, "method not found");
+    });
+    registerCallbackHandlers(Dispatcher, /*Callbacks=*/LSPServer);
+
+
+    JSONOutput Out(llvm::outs(), llvm::errs(),
+                 InputMirrorStream ? InputMirrorStream.getPointer() : nullptr,
+                 PrettyPrint);
+
+    clangd::LoggingSession LoggingSession(Out);
+
+    runJSONRPCServerLoop(stdin, Out, InputStyle, Dispatcher, LSPServer.getIsDone());
+  }
+
+  constexpr int NoShutdownRequestErrorCode = 1;
+  return LSPServer.getShutdownRequestReceived() ? 0 : NoShutdownRequestErrorCode;
 }
Index: ProtocolHandlers.h
===================================================================
--- ProtocolHandlers.h
+++ ProtocolHandlers.h
@@ -56,8 +56,63 @@
   virtual void onChangeConfiguration(DidChangeConfigurationParams &Params) = 0;
 };
 
-void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher,
-                              ProtocolCallbacks &Callbacks);
+// Helper for attaching ProtocolCallbacks methods to a JSONRPCDispatcher.
+// Invoke like: Registerer("foo", &ProtocolCallbacks::onFoo)
+// onFoo should be: void onFoo(Ctx &C, FooParams &Params)
+// FooParams should have a fromJSON function.
+template<typename DispatcherType>
+struct HandlerRegisterer {
+  template <typename Param>
+  void operator()(StringRef Method, void (ProtocolCallbacks::*Handler)(Param)) {
+    // Capture pointers by value, as the lambda will outlive this object.
+    auto *Callbacks = this->Callbacks;
+    Dispatcher.registerHandler(Method, [=](const json::Expr &RawParams) {
+      typename std::remove_reference<Param>::type P;
+      if (fromJSON(RawParams, P)) {
+        (Callbacks->*Handler)(P);
+      } else {
+        log("Failed to decode " + Method + " request.");
+      }
+    });
+  }
+
+  DispatcherType &Dispatcher;
+  ProtocolCallbacks *Callbacks;
+};
+
+template<typename DispatcherType>
+void registerCallbackHandlers(DispatcherType &Dispatcher,
+                              ProtocolCallbacks &Callbacks) {
+  HandlerRegisterer<DispatcherType> Register{Dispatcher, &Callbacks};
+
+  Register("initialize", &ProtocolCallbacks::onInitialize);
+  Register("shutdown", &ProtocolCallbacks::onShutdown);
+  Register("exit", &ProtocolCallbacks::onExit);
+  Register("textDocument/didOpen", &ProtocolCallbacks::onDocumentDidOpen);
+  Register("textDocument/didClose", &ProtocolCallbacks::onDocumentDidClose);
+  Register("textDocument/didChange", &ProtocolCallbacks::onDocumentDidChange);
+  Register("textDocument/rangeFormatting",
+           &ProtocolCallbacks::onDocumentRangeFormatting);
+  Register("textDocument/onTypeFormatting",
+           &ProtocolCallbacks::onDocumentOnTypeFormatting);
+  Register("textDocument/formatting", &ProtocolCallbacks::onDocumentFormatting);
+  Register("textDocument/codeAction", &ProtocolCallbacks::onCodeAction);
+  Register("textDocument/completion", &ProtocolCallbacks::onCompletion);
+  Register("textDocument/signatureHelp", &ProtocolCallbacks::onSignatureHelp);
+  Register("textDocument/definition", &ProtocolCallbacks::onGoToDefinition);
+  Register("textDocument/switchSourceHeader",
+           &ProtocolCallbacks::onSwitchSourceHeader);
+  Register("textDocument/rename", &ProtocolCallbacks::onRename);
+  Register("textDocument/hover", &ProtocolCallbacks::onHover);
+  Register("workspace/didChangeWatchedFiles", &ProtocolCallbacks::onFileEvent);
+  Register("workspace/executeCommand", &ProtocolCallbacks::onCommand);
+  Register("textDocument/documentHighlight",
+           &ProtocolCallbacks::onDocumentHighlight);
+  Register("workspace/didChangeConfiguration",
+           &ProtocolCallbacks::onChangeConfiguration);
+  Register("workspace/symbol", &ProtocolCallbacks::onWorkspaceSymbol);
+}
+
 
 } // namespace clangd
 } // namespace clang
Index: clangd/ProtocolHandlers.cpp
===================================================================
--- clangd/ProtocolHandlers.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-//===--- ProtocolHandlers.cpp - LSP callbacks -----------------------------===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ProtocolHandlers.h"
-#include "ClangdLSPServer.h"
-#include "ClangdServer.h"
-#include "DraftStore.h"
-#include "Trace.h"
-
-using namespace clang;
-using namespace clang::clangd;
-
-namespace {
-
-// Helper for attaching ProtocolCallbacks methods to a JSONRPCDispatcher.
-// Invoke like: Registerer("foo", &ProtocolCallbacks::onFoo)
-// onFoo should be: void onFoo(Ctx &C, FooParams &Params)
-// FooParams should have a fromJSON function.
-struct HandlerRegisterer {
-  template <typename Param>
-  void operator()(StringRef Method, void (ProtocolCallbacks::*Handler)(Param)) {
-    // Capture pointers by value, as the lambda will outlive this object.
-    auto *Callbacks = this->Callbacks;
-    Dispatcher.registerHandler(Method, [=](const json::Expr &RawParams) {
-      typename std::remove_reference<Param>::type P;
-      if (fromJSON(RawParams, P)) {
-        (Callbacks->*Handler)(P);
-      } else {
-        log("Failed to decode " + Method + " request.");
-      }
-    });
-  }
-
-  JSONRPCDispatcher &Dispatcher;
-  ProtocolCallbacks *Callbacks;
-};
-
-} // namespace
-
-void clangd::registerCallbackHandlers(JSONRPCDispatcher &Dispatcher,
-                                      ProtocolCallbacks &Callbacks) {
-  HandlerRegisterer Register{Dispatcher, &Callbacks};
-
-  Register("initialize", &ProtocolCallbacks::onInitialize);
-  Register("shutdown", &ProtocolCallbacks::onShutdown);
-  Register("exit", &ProtocolCallbacks::onExit);
-  Register("textDocument/didOpen", &ProtocolCallbacks::onDocumentDidOpen);
-  Register("textDocument/didClose", &ProtocolCallbacks::onDocumentDidClose);
-  Register("textDocument/didChange", &ProtocolCallbacks::onDocumentDidChange);
-  Register("textDocument/rangeFormatting",
-           &ProtocolCallbacks::onDocumentRangeFormatting);
-  Register("textDocument/onTypeFormatting",
-           &ProtocolCallbacks::onDocumentOnTypeFormatting);
-  Register("textDocument/formatting", &ProtocolCallbacks::onDocumentFormatting);
-  Register("textDocument/codeAction", &ProtocolCallbacks::onCodeAction);
-  Register("textDocument/completion", &ProtocolCallbacks::onCompletion);
-  Register("textDocument/signatureHelp", &ProtocolCallbacks::onSignatureHelp);
-  Register("textDocument/definition", &ProtocolCallbacks::onGoToDefinition);
-  Register("textDocument/switchSourceHeader",
-           &ProtocolCallbacks::onSwitchSourceHeader);
-  Register("textDocument/rename", &ProtocolCallbacks::onRename);
-  Register("textDocument/hover", &ProtocolCallbacks::onHover);
-  Register("workspace/didChangeWatchedFiles", &ProtocolCallbacks::onFileEvent);
-  Register("workspace/executeCommand", &ProtocolCallbacks::onCommand);
-  Register("textDocument/documentHighlight",
-           &ProtocolCallbacks::onDocumentHighlight);
-  Register("workspace/didChangeConfiguration",
-           &ProtocolCallbacks::onChangeConfiguration);
-  Register("workspace/symbol", &ProtocolCallbacks::onWorkspaceSymbol);
-}
Index: LSPOutput.h
===================================================================
--- /dev/null
+++ LSPOutput.h
@@ -0,0 +1,42 @@
+//===--- LSPOutput.h - LSP output interface ---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_LSPOUTPUT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_LSPOUTPUT_H
+
+#include "JSONExpr.h"
+#include "Logger.h"
+#include "clang/Basic/LLVM.h"
+
+namespace clang {
+namespace clangd {
+
+/// Encapsulates output and logs streams and provides thread-safe access to
+/// them.
+class LSPOutput : public Logger {
+  // FIXME(ibiryukov): figure out if we can shrink the public interface of
+  // LSPOutput now that we pass Context everywhere.
+public:
+  /// Emit LSP message.
+  virtual void writeMessage(const json::Expr &Result) = 0;
+
+  /// Write a line to log.
+  virtual void log(const Twine &Message) = 0;
+
+  /// FIXME: Does it make sense for all implementations?
+  /// Mirror \p Message into InputMirror stream. Does nothing if InputMirror is
+  /// null.
+  /// Unlike other methods of LSPOutput, mirrorInput is not thread-safe.
+  virtual void mirrorInput(const Twine &Message) = 0;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif
Index: JSONRPCDispatcher.h
===================================================================
--- JSONRPCDispatcher.h
+++ JSONRPCDispatcher.h
@@ -1,4 +1,4 @@
-//===--- JSONRPCDispatcher.h - Main JSON parser entry point -----*- C++ -*-===//
+//===--- JSONRPCDispatcher.h - Main JSON RPC entry point --------*- C++ -*-===//
 //
 //                     The LLVM Compiler Infrastructure
 //
@@ -12,6 +12,7 @@
 
 #include "JSONExpr.h"
 #include "Logger.h"
+#include "LSPOutput.h"
 #include "Protocol.h"
 #include "Trace.h"
 #include "clang/Basic/LLVM.h"
@@ -25,24 +26,22 @@
 
 /// Encapsulates output and logs streams and provides thread-safe access to
 /// them.
-class JSONOutput : public Logger {
-  // FIXME(ibiryukov): figure out if we can shrink the public interface of
-  // JSONOutput now that we pass Context everywhere.
+class JSONOutput : public LSPOutput {
 public:
   JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs,
              llvm::raw_ostream *InputMirror = nullptr, bool Pretty = false)
       : Pretty(Pretty), Outs(Outs), Logs(Logs), InputMirror(InputMirror) {}
 
   /// Emit a JSONRPC message.
-  void writeMessage(const json::Expr &Result);
+  void writeMessage(const json::Expr &Result) override;
 
   /// Write a line to the logging stream.
   void log(const Twine &Message) override;
 
   /// Mirror \p Message into InputMirror stream. Does nothing if InputMirror is
   /// null.
   /// Unlike other methods of JSONOutput, mirrorInput is not thread-safe.
-  void mirrorInput(const Twine &Message);
+  void mirrorInput(const Twine &Message) override;
 
   // Whether output should be pretty-printed.
   const bool Pretty;
@@ -81,7 +80,7 @@
   void registerHandler(StringRef Method, Handler H);
 
   /// Parses a JSONRPC message and calls the Handler for it.
-  bool call(const json::Expr &Message, JSONOutput &Out) const;
+  bool call(const json::Expr &Message, LSPOutput &Out) const;
 
 private:
   llvm::StringMap<Handler> Handlers;
@@ -104,7 +103,7 @@
 /// replacements of \r\n with \n.
 /// We use C-style FILE* for reading as std::istream has unclear interaction
 /// with signals, which are sent by debuggers on some OSs.
-void runLanguageServerLoop(std::FILE *In, JSONOutput &Out,
+void runJSONRPCServerLoop(std::FILE *In, JSONOutput &Out,
                            JSONStreamStyle InputStyle,
                            JSONRPCDispatcher &Dispatcher, bool &IsDone);
 
Index: JSONRPCDispatcher.cpp
===================================================================
--- JSONRPCDispatcher.cpp
+++ JSONRPCDispatcher.cpp
@@ -1,15 +1,15 @@
-//===--- JSONRPCDispatcher.cpp - Main JSON parser entry point -------------===//
+//===--- JSONRPCDispatcher.h - Main JSON RPC entry point --------*- C++ -*-===//
 //
 //                     The LLVM Compiler Infrastructure
 //
 // This file is distributed under the University of Illinois Open Source
 // License. See LICENSE.TXT for details.
 //
 //===----------------------------------------------------------------------===//
 
+#include "DispatcherCommon.h"
 #include "JSONRPCDispatcher.h"
 #include "JSONExpr.h"
-#include "ProtocolHandlers.h"
 #include "Trace.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
@@ -21,38 +21,6 @@
 using namespace clang;
 using namespace clangd;
 
-namespace {
-static Key<json::Expr> RequestID;
-static Key<JSONOutput *> RequestOut;
-
-// When tracing, we trace a request and attach the repsonse in reply().
-// Because the Span isn't available, we find the current request using Context.
-class RequestSpan {
-  RequestSpan(json::obj *Args) : Args(Args) {}
-  std::mutex Mu;
-  json::obj *Args;
-  static Key<std::unique_ptr<RequestSpan>> RSKey;
-
-public:
-  // Return a context that's aware of the enclosing request, identified by Span.
-  static Context stash(const trace::Span &Span) {
-    return Context::current().derive(
-        RSKey, std::unique_ptr<RequestSpan>(new RequestSpan(Span.Args)));
-  }
-
-  // If there's an enclosing request and the tracer is interested, calls \p F
-  // with a json::obj where request info can be added.
-  template <typename Func> static void attach(Func &&F) {
-    auto *RequestArgs = Context::current().get(RSKey);
-    if (!RequestArgs || !*RequestArgs || !(*RequestArgs)->Args)
-      return;
-    std::lock_guard<std::mutex> Lock((*RequestArgs)->Mu);
-    F(*(*RequestArgs)->Args);
-  }
-};
-Key<std::unique_ptr<RequestSpan>> RequestSpan::RSKey;
-} // namespace
-
 void JSONOutput::writeMessage(const json::Expr &Message) {
   std::string S;
   llvm::raw_string_ostream OS(S);
@@ -86,63 +54,12 @@
   InputMirror->flush();
 }
 
-void clangd::reply(json::Expr &&Result) {
-  auto ID = Context::current().get(RequestID);
-  if (!ID) {
-    log("Attempted to reply to a notification!");
-    return;
-  }
-  RequestSpan::attach([&](json::obj &Args) { Args["Reply"] = Result; });
-  Context::current()
-      .getExisting(RequestOut)
-      ->writeMessage(json::obj{
-          {"jsonrpc", "2.0"},
-          {"id", *ID},
-          {"result", std::move(Result)},
-      });
-}
-
-void clangd::replyError(ErrorCode code, const llvm::StringRef &Message) {
-  log("Error " + Twine(static_cast<int>(code)) + ": " + Message);
-  RequestSpan::attach([&](json::obj &Args) {
-    Args["Error"] =
-        json::obj{{"code", static_cast<int>(code)}, {"message", Message.str()}};
-  });
-
-  if (auto ID = Context::current().get(RequestID)) {
-    Context::current()
-        .getExisting(RequestOut)
-        ->writeMessage(json::obj{
-            {"jsonrpc", "2.0"},
-            {"id", *ID},
-            {"error",
-             json::obj{{"code", static_cast<int>(code)}, {"message", Message}}},
-        });
-  }
-}
-
-void clangd::call(StringRef Method, json::Expr &&Params) {
-  // FIXME: Generate/Increment IDs for every request so that we can get proper
-  // replies once we need to.
-  RequestSpan::attach([&](json::obj &Args) {
-    Args["Call"] = json::obj{{"method", Method.str()}, {"params", Params}};
-  });
-  Context::current()
-      .getExisting(RequestOut)
-      ->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);
 }
 
-bool JSONRPCDispatcher::call(const json::Expr &Message, JSONOutput &Out) const {
+bool JSONRPCDispatcher::call(const json::Expr &Message, LSPOutput &Out) const {
   // Message must be an object with "jsonrpc":"2.0".
   auto *Object = Message.asObject();
   if (!Object || Object->getString("jsonrpc") != Optional<StringRef>("2.0"))
@@ -208,7 +125,7 @@
 //  - ferror() or feof() are set.
 //  - Content-Length is missing or empty (protocol error)
 static llvm::Optional<std::string> readStandardMessage(std::FILE *In,
-                                                       JSONOutput &Out) {
+                                                       LSPOutput &Out) {
   // A Language Server Protocol message starts with a set of HTTP headers,
   // delimited  by \r\n, and terminated by an empty line (\r\n).
   unsigned long long ContentLength = 0;
@@ -279,7 +196,7 @@
 // This is a testing path, so favor simplicity over performance here.
 // When returning None, feof() or ferror() will be set.
 static llvm::Optional<std::string> readDelimitedMessage(std::FILE *In,
-                                                        JSONOutput &Out) {
+                                                        LSPOutput &Out) {
   std::string JSON;
   std::string Line;
   while (readLine(In, Line)) {
@@ -311,7 +228,7 @@
 // sometimes hang rather than exit on other OSes. The interaction between
 // istreams and signals isn't well-specified, so it's hard to get this right.
 // The C APIs seem to be clearer in this respect.
-void clangd::runLanguageServerLoop(std::FILE *In, JSONOutput &Out,
+void clangd::runJSONRPCServerLoop(std::FILE *In, JSONOutput &Out,
                                    JSONStreamStyle InputStyle,
                                    JSONRPCDispatcher &Dispatcher,
                                    bool &IsDone) {
Index: DispatcherCommon.h
===================================================================
--- /dev/null
+++ DispatcherCommon.h
@@ -0,0 +1,62 @@
+//===--- DispatcherCommon.h - Shared code for dispatchers -----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_DISPATCHERCOMMON_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_DISPATCHERCOMMON_H
+
+#include "JSONRPCDispatcher.h"
+#include "JSONExpr.h"
+#include "Trace.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Chrono.h"
+#include "llvm/Support/SourceMgr.h"
+#include <istream>
+
+namespace clang {
+namespace clangd {
+
+extern Key<json::Expr> RequestID;
+extern Key<LSPOutput *> RequestOut;
+
+// When tracing, we trace a request and attach the repsonse in reply().
+// Because the Span isn't available, we find the current request using Context.
+class RequestSpan {
+  RequestSpan(json::obj *Args) : Args(Args) {}
+  std::mutex Mu;
+  json::obj *Args;
+  static Key<std::unique_ptr<RequestSpan>> RSKey;
+
+public:
+  // Return a context that's aware of the enclosing request, identified by Span.
+  static Context stash(const trace::Span &Span);
+
+  // If there's an enclosing request and the tracer is interested, calls \p F
+  // with a json::obj where request info can be added.
+  template <typename Func> static void attach(Func &&F) {
+    auto *RequestArgs = Context::current().get(RSKey);
+    if (!RequestArgs || !*RequestArgs || !(*RequestArgs)->Args)
+      return;
+    std::lock_guard<std::mutex> Lock((*RequestArgs)->Mu);
+    F(*(*RequestArgs)->Args);
+  }
+};
+
+void sendMessage(json::Expr &&Message);
+
+void reply(json::Expr &&Result);
+
+void replyError(ErrorCode code, const llvm::StringRef &Message);
+
+void call(StringRef Method, json::Expr &&Params);
+
+}
+}
+
+#endif
\ No newline at end of file
Index: DispatcherCommon.cpp
===================================================================
--- /dev/null
+++ DispatcherCommon.cpp
@@ -0,0 +1,83 @@
+//===--- DispatcherCommon.h - Shared code for dispatchers -----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DispatcherCommon.h"
+
+namespace clang {
+namespace clangd {
+
+Key<json::Expr> RequestID;
+Key<LSPOutput *> RequestOut;
+
+Context RequestSpan::stash(const trace::Span &Span) {
+  return Context::current().derive(
+      RSKey, std::unique_ptr<RequestSpan>(new RequestSpan(Span.Args)));
+}
+
+Key<std::unique_ptr<RequestSpan>> RequestSpan::RSKey;
+
+void sendMessage(json::Expr &&Message) {
+  Context::current()
+      .getExisting(RequestOut)
+      ->writeMessage(Message);
+}
+
+void reply(json::Expr &&Result) {
+  auto ID = Context::current().get(RequestID);
+  if (!ID) {
+    log("Attempted to reply to a notification!");
+    return;
+  }
+  RequestSpan::attach([&](json::obj &Args) { Args["Reply"] = Result; });
+  Context::current()
+      .getExisting(RequestOut)
+      ->writeMessage(json::obj{
+          {"jsonrpc", "2.0"},
+          {"id", *ID},
+          {"result", std::move(Result)},
+      });
+}
+
+void replyError(ErrorCode code, const llvm::StringRef &Message) {
+  log("Error " + Twine(static_cast<int>(code)) + ": " + Message);
+  RequestSpan::attach([&](json::obj &Args) {
+    Args["Error"] =
+        json::obj{{"code", static_cast<int>(code)}, {"message", Message.str()}};
+  });
+
+  if (auto ID = Context::current().get(RequestID)) {
+    Context::current()
+        .getExisting(RequestOut)
+        ->writeMessage(json::obj{
+            {"jsonrpc", "2.0"},
+            {"id", *ID},
+            {"error",
+             json::obj{{"code", static_cast<int>(code)}, {"message", Message}}},
+        });
+  }
+}
+
+void call(StringRef Method, json::Expr &&Params) {
+  // FIXME: Generate/Increment IDs for every request so that we can get proper
+  // replies once we need to.
+  RequestSpan::attach([&](json::obj &Args) {
+    Args["Call"] = json::obj{{"method", Method.str()}, {"params", Params}};
+  });
+  Context::current()
+      .getExisting(RequestOut)
+      ->writeMessage(json::obj{
+          {"jsonrpc", "2.0"},
+          {"id", 1},
+          {"method", Method},
+          {"params", std::move(Params)},
+      });
+}
+
+}
+}
Index: ClangdLSPServer.h
===================================================================
--- ClangdLSPServer.h
+++ ClangdLSPServer.h
@@ -23,29 +23,22 @@
 namespace clang {
 namespace clangd {
 
-class JSONOutput;
+class LSPOutput;
 class SymbolIndex;
 
 /// This class provides implementation of an LSP server, glueing the JSON
 /// dispatch and ClangdServer together.
-class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks {
+class ClangdLSPServer : private DiagnosticsConsumer, public ProtocolCallbacks {
 public:
   /// If \p CompileCommandsDir has a value, compile_commands.json will be
   /// loaded only from \p CompileCommandsDir. Otherwise, clangd will look
   /// for compile_commands.json in all parent directories of each file.
-  ClangdLSPServer(JSONOutput &Out, const clangd::CodeCompleteOptions &CCOpts,
+  ClangdLSPServer(const clangd::CodeCompleteOptions &CCOpts,
                   llvm::Optional<Path> CompileCommandsDir,
                   const ClangdServer::Options &Opts);
 
-  /// Run LSP server loop, receiving input for it from \p In. \p In must be
-  /// opened in binary mode. Output will be written using Out variable passed to
-  /// class constructor. This method must not be executed more than once for
-  /// each instance of ClangdLSPServer.
-  ///
-  /// \return Whether we received a 'shutdown' request before an 'exit' request.
-  bool run(std::FILE *In,
-           JSONStreamStyle InputStyle = JSONStreamStyle::Standard);
-
+  bool& getIsDone();
+  bool getShutdownRequestReceived() const;
 private:
   // Implement DiagnosticsConsumer.
   void onDiagnosticsReady(PathRef File, std::vector<Diag> Diagnostics) override;
@@ -82,7 +75,6 @@
   /// compilation database is changed.
   void reparseOpenedFiles();
 
-  JSONOutput &Out;
   /// Used to indicate that the 'shutdown' request was received from the
   /// Language Server client.
   bool ShutdownRequestReceived = false;
Index: ClangdLSPServer.cpp
===================================================================
--- ClangdLSPServer.cpp
+++ ClangdLSPServer.cpp
@@ -9,6 +9,7 @@
 
 #include "ClangdLSPServer.h"
 #include "Diagnostics.h"
+#include "DispatcherCommon.h"
 #include "JSONRPCDispatcher.h"
 #include "SourceCode.h"
 #include "URI.h"
@@ -394,33 +395,13 @@
   }
 }
 
-ClangdLSPServer::ClangdLSPServer(JSONOutput &Out,
-                                 const clangd::CodeCompleteOptions &CCOpts,
+ClangdLSPServer::ClangdLSPServer(const clangd::CodeCompleteOptions &CCOpts,
                                  llvm::Optional<Path> CompileCommandsDir,
                                  const ClangdServer::Options &Opts)
-    : Out(Out), NonCachedCDB(std::move(CompileCommandsDir)), CDB(NonCachedCDB),
+    : NonCachedCDB(std::move(CompileCommandsDir)), CDB(NonCachedCDB),
       CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()),
       Server(CDB, FSProvider, /*DiagConsumer=*/*this, Opts) {}
 
-bool ClangdLSPServer::run(std::FILE *In, JSONStreamStyle InputStyle) {
-  assert(!IsDone && "Run was called before");
-
-  // Set up JSONRPCDispatcher.
-  JSONRPCDispatcher Dispatcher([](const json::Expr &Params) {
-    replyError(ErrorCode::MethodNotFound, "method not found");
-  });
-  registerCallbackHandlers(Dispatcher, /*Callbacks=*/*this);
-
-  // Run the Language Server loop.
-  runLanguageServerLoop(In, Out, InputStyle, Dispatcher, IsDone);
-
-  // Make sure IsDone is set to true after this method exits to ensure assertion
-  // at the start of the method fires if it's ever executed again.
-  IsDone = true;
-
-  return ShutdownRequestReceived;
-}
-
 std::vector<Fix> ClangdLSPServer::getFixes(StringRef File,
                                            const clangd::Diagnostic &D) {
   std::lock_guard<std::mutex> Lock(FixItsMutex);
@@ -463,7 +444,7 @@
   }
 
   // Publish diagnostics.
-  Out.writeMessage(json::obj{
+  sendMessage(json::obj{
       {"jsonrpc", "2.0"},
       {"method", "textDocument/publishDiagnostics"},
       {"params",
@@ -479,3 +460,6 @@
     Server.addDocument(FilePath, *DraftMgr.getDraft(FilePath),
                        WantDiagnostics::Auto);
 }
+
+bool& ClangdLSPServer::getIsDone() { return IsDone; }
+bool ClangdLSPServer::getShutdownRequestReceived() const { return ShutdownRequestReceived; }
\ No newline at end of file
Index: CMakeLists.txt
===================================================================
--- CMakeLists.txt
+++ CMakeLists.txt
@@ -17,6 +17,7 @@
   Compiler.cpp
   Context.cpp
   Diagnostics.cpp
+  DispatcherCommon.cpp
   DraftStore.cpp
   FindSymbols.cpp
   FuzzyMatch.cpp
@@ -26,7 +27,6 @@
   JSONRPCDispatcher.cpp
   Logger.cpp
   Protocol.cpp
-  ProtocolHandlers.cpp
   Quality.cpp
   SourceCode.cpp
   Threading.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to