jkorous created this revision.
jkorous added reviewers: sammccall, arphaman.
Herald added subscribers: cfe-commits, dexonsmith, MaskRay, ioeric, 
ilya-biryukov, mgorny.

Combined, rebased and modified original XPC patches.

- https://reviews.llvm.org/D48559 [clangd] refactoring for XPC transport layer 
[NFCI]
- https://reviews.llvm.org/D48560 [clangd] JSON <-> XPC conversions
- https://reviews.llvm.org/D48562 [clangd] XPC transport layer

- Rebased on TOT.
- Made changes according to review.

Couple of questions are still open in reviews of original patches and more 
importantly the discussion about transport abstraction just started:
https://reviews.llvm.org/D48559#1167340
https://reviews.llvm.org/D49389

I thought it would be useful to have current state of our original approach 
online as it will be foundation for our implementation anyway (implementing 
whatever interface we converge to).


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D49548

Files:
  CMakeLists.txt
  ClangdLSPServer.cpp
  ClangdLSPServer.h
  DispatcherCommon.cpp
  DispatcherCommon.h
  Features.inc.in
  JSONRPCDispatcher.cpp
  JSONRPCDispatcher.h
  LSPOutput.h
  ProtocolHandlers.cpp
  ProtocolHandlers.h
  clangd/CMakeLists.txt
  clangd/xpc/CMakeLists.txt
  clangd/xpc/ConversionTests.cpp
  clangd/xpc/initialize.test
  lit.cfg
  lit.site.cfg.in
  tool/CMakeLists.txt
  tool/ClangdMain.cpp
  xpc/CMakeLists.txt
  xpc/Conversion.cpp
  xpc/Conversion.h
  xpc/README.txt
  xpc/XPCDispatcher.cpp
  xpc/XPCDispatcher.h
  xpc/cmake/Info.plist.in
  xpc/cmake/XPCServiceInfo.plist.in
  xpc/cmake/modules/CreateClangdXPCFramework.cmake
  xpc/framework/CMakeLists.txt
  xpc/framework/ClangdXPC.cpp
  xpc/test-client/CMakeLists.txt
  xpc/test-client/ClangdXPCTestClient.cpp

Index: clangd/xpc/ConversionTests.cpp
===================================================================
--- /dev/null
+++ clangd/xpc/ConversionTests.cpp
@@ -0,0 +1,454 @@
+//===-- ConversionTests.cpp  --------------------------*- C++ -*-----------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "xpc/Conversion.h"
+#include "gtest/gtest.h"
+
+#include <limits>
+
+namespace clang {
+namespace clangd {
+namespace {
+
+using namespace llvm;
+
+TEST(JsonXpcConversionTest, Null) {
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Value(nullptr)),
+      xpc_null_create()
+    )
+  );
+  EXPECT_TRUE(
+    json::Value(nullptr) == xpcToJson(xpc_null_create())
+  );
+}
+
+TEST(JsonXpcConversionTest, Bool) {
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Value(true)),
+      xpc_bool_create(true)
+    )
+  );
+  EXPECT_TRUE(
+    json::Value(false) == xpcToJson(xpc_bool_create(false))
+  );
+}
+
+TEST(JsonXpcConversionTest, Number) {
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Value(3.14)),
+      xpc_double_create(3.14)
+    )
+  );
+  EXPECT_TRUE(
+    json::Value(3.14) == xpcToJson(xpc_double_create(3.14))
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Value(42)),
+      xpc_double_create(42)
+    )
+  );
+  EXPECT_TRUE(
+    json::Value(42) == xpcToJson(xpc_double_create(42))
+  );
+  EXPECT_TRUE(
+    json::Value(42) == xpcToJson(xpc_int64_create(42))
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Value(-100)),
+      xpc_double_create(-100)
+    )
+  );
+  EXPECT_TRUE(
+    json::Value(-100) == xpcToJson(xpc_double_create(-100))
+  );
+
+  int64_t bigPositiveValue = std::numeric_limits<int>::max();
+  ++bigPositiveValue;
+  int64_t bigNegativeValue = std::numeric_limits<int>::min();
+  --bigNegativeValue;
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Value(bigPositiveValue)),
+      xpc_double_create(bigPositiveValue)
+    )
+  );
+  EXPECT_TRUE(
+    json::Value(bigPositiveValue) == xpcToJson(xpc_double_create(bigPositiveValue))
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Value(bigNegativeValue)),
+      xpc_double_create(bigNegativeValue)
+    )
+  );
+  EXPECT_TRUE(
+    json::Value(bigNegativeValue) == xpcToJson(xpc_double_create(bigNegativeValue))
+  );
+}
+
+TEST(JsonXpcConversionTest, String) {
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Value("foo")),
+      xpc_string_create("foo")
+    )
+  );
+  EXPECT_TRUE(
+    json::Value("foo") == xpcToJson(xpc_string_create("foo"))
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Value("")),
+      xpc_string_create("")
+    )
+  );
+  EXPECT_TRUE(
+    json::Value("") == xpcToJson(xpc_string_create(""))
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Value("123")),
+      xpc_string_create("123")
+    )
+  );
+  EXPECT_TRUE(
+    json::Value("123") == xpcToJson(xpc_string_create("123"))
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Value(" ")),
+      xpc_string_create(" ")
+    )
+  );
+  EXPECT_TRUE(
+    json::Value(" ") == xpcToJson(xpc_string_create(" "))
+  );
+
+  // Testing two different "patterns" just in case.
+  std::string kBStringOfAs("A", 1024);
+  std::string kBStringOfBs("B", 1024);
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Value(kBStringOfAs.c_str())),
+      xpc_string_create(kBStringOfAs.c_str())
+    )
+  );
+  EXPECT_TRUE(
+    json::Value(kBStringOfAs.c_str()) == xpcToJson(xpc_string_create(kBStringOfAs.c_str()))
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(json::Value(kBStringOfBs.c_str())),
+      xpc_string_create(kBStringOfBs.c_str())
+    )
+  );
+  EXPECT_TRUE(
+    json::Value(kBStringOfBs.c_str()) == xpcToJson(xpc_string_create(kBStringOfBs.c_str()))
+  );
+}
+
+TEST(JsonXpcConversionTest, Array) {
+  json::Value JsonArray{true, "foo", nullptr, 42};
+
+  xpc_object_t XpcArray = []() {
+    std::vector<xpc_object_t> XpcArrayElements;
+    XpcArrayElements.emplace_back(xpc_bool_create(true));
+    XpcArrayElements.emplace_back(xpc_string_create("foo"));
+    XpcArrayElements.emplace_back(xpc_null_create());
+    XpcArrayElements.emplace_back(xpc_double_create(42));
+
+    return xpc_array_create(XpcArrayElements.data(), XpcArrayElements.size());
+  }();
+
+  // Per-element checks just speed up eventual debugging - testing only one direction.
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc( (*JsonArray.getAsArray())[0] ),
+      xpc_array_get_value(XpcArray, 0)
+    )
+  );
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc( (*JsonArray.getAsArray())[1] ),
+      xpc_array_get_value(XpcArray, 1)
+    )
+  );
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc( (*JsonArray.getAsArray())[2] ),
+      xpc_array_get_value(XpcArray, 2)
+    )
+  );
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc( (*JsonArray.getAsArray())[3] ),
+      xpc_array_get_value(XpcArray, 3)
+    )
+  );
+
+  json::Value NestedJsonArray{ "foo", JsonArray, 123456789 };
+
+  xpc_object_t NestedXpcArray = [&](){
+    std::vector<xpc_object_t> NestedXpcArrayElements;
+    NestedXpcArrayElements.emplace_back(xpc_string_create("foo"));
+    NestedXpcArrayElements.emplace_back(XpcArray);
+    NestedXpcArrayElements.emplace_back(xpc_double_create(123456789));
+
+    return xpc_array_create(NestedXpcArrayElements.data(), NestedXpcArrayElements.size());
+  }();
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(NestedJsonArray),
+      NestedXpcArray
+    )
+  );
+  EXPECT_TRUE(
+    NestedJsonArray == xpcToJson(NestedXpcArray)
+  );
+}
+
+TEST(JsonXpcConversionTest, Dictionary) {
+  json::Value JsonDict(
+    json::Object{
+      {"a", true},
+      {"b", "foo"},
+      {"c", nullptr},
+      {"d", 42}
+    }
+  );
+
+  xpc_object_t XpcDict = []() {
+    std::vector<std::string> XpcDictKeys;
+    std::vector<xpc_object_t> XpcDictValues;
+    XpcDictKeys.emplace_back("a");
+    XpcDictValues.emplace_back(xpc_bool_create(true));
+    XpcDictKeys.emplace_back("b");
+    XpcDictValues.emplace_back(xpc_string_create("foo"));
+    XpcDictKeys.emplace_back("c");
+    XpcDictValues.emplace_back(xpc_null_create());
+    XpcDictKeys.emplace_back("d");
+    XpcDictValues.emplace_back(xpc_double_create(42));
+
+    std::vector<const char*> XpcDictKeysCStr = {
+      XpcDictKeys.at(0).c_str(),
+      XpcDictKeys.at(1).c_str(),
+      XpcDictKeys.at(2).c_str(),
+      XpcDictKeys.at(3).c_str()
+    };
+
+    return xpc_dictionary_create(XpcDictKeysCStr.data(), XpcDictValues.data(), XpcDictValues.size());
+  }();
+
+  // Per-element checks just speed up eventual debugging - testing only one direction.
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(*JsonDict.getAsObject()->get("a")),
+      xpc_dictionary_get_value(XpcDict, "a")
+    )
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(*JsonDict.getAsObject()->get("b")),
+      xpc_dictionary_get_value(XpcDict, "b")
+    )
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(*JsonDict.getAsObject()->get("c")),
+      xpc_dictionary_get_value(XpcDict, "c")
+    )
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(*JsonDict.getAsObject()->get("d")),
+      xpc_dictionary_get_value(XpcDict, "d")
+    )
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(JsonDict),
+      XpcDict
+    )
+  );
+
+  xpc_object_t NestedXpcDict = [&](){
+    std::vector<std::string> NestedXpcDictKeys;
+    std::vector<xpc_object_t> NestedXpcDictValues;
+    {
+      NestedXpcDictKeys.emplace_back("3");
+      NestedXpcDictValues.emplace_back(xpc_double_create(42.24));
+
+      NestedXpcDictKeys.emplace_back("3.1");
+      NestedXpcDictValues.emplace_back(XpcDict);
+
+      NestedXpcDictKeys.emplace_back("3.14");
+      NestedXpcDictValues.emplace_back(xpc_string_create("foo"));
+    }
+    std::vector<const char*> NestedXpcDictKeysCStr = {
+      NestedXpcDictKeys.at(0).c_str(),
+      NestedXpcDictKeys.at(1).c_str(),
+      NestedXpcDictKeys.at(2).c_str()
+    };
+    return xpc_dictionary_create(NestedXpcDictKeysCStr.data(), NestedXpcDictValues.data(), NestedXpcDictValues.size());
+  }();
+
+  json::Value NestedJsonDict(
+    json::Object{
+      {"3",     42.24},
+      {"3.1",   JsonDict},
+      {"3.14",  "foo"}
+    }
+  );
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(NestedJsonDict),
+      NestedXpcDict
+    )
+  );
+  EXPECT_TRUE(
+    NestedJsonDict == xpcToJson(NestedXpcDict)
+  );
+}
+
+TEST(JsonXpcConversionTest, ArrayContainingDictionary) {
+  json::Value JsonDict(
+    json::Object{
+      {"a", true},
+      {"b", "foo"},
+      {"c", nullptr},
+      {"d", 42}
+    }
+  );
+
+  json::Value JsonArray{true, "foo", nullptr, JsonDict, 42};
+
+  xpc_object_t XpcArray = [](){
+    xpc_object_t XpcDict = [](){
+      std::vector<std::string> XpcDictKeys;
+      std::vector<xpc_object_t> XpcDictValues;
+      XpcDictKeys.emplace_back("a");
+      XpcDictValues.emplace_back(xpc_bool_create(true));
+      XpcDictKeys.emplace_back("b");
+      XpcDictValues.emplace_back(xpc_string_create("foo"));
+      XpcDictKeys.emplace_back("c");
+      XpcDictValues.emplace_back(xpc_null_create());
+      XpcDictKeys.emplace_back("d");
+      XpcDictValues.emplace_back(xpc_double_create(42));
+
+      std::vector<const char*> XpcDictKeysCStr = {
+        XpcDictKeys.at(0).c_str(),
+        XpcDictKeys.at(1).c_str(),
+        XpcDictKeys.at(2).c_str(),
+        XpcDictKeys.at(3).c_str()
+      };
+
+      return xpc_dictionary_create(XpcDictKeysCStr.data(), XpcDictValues.data(), XpcDictValues.size());
+    }();
+
+    std::vector<xpc_object_t> XpcArrayElements;
+    XpcArrayElements.emplace_back(xpc_bool_create(true));
+    XpcArrayElements.emplace_back(xpc_string_create("foo"));
+    XpcArrayElements.emplace_back(xpc_null_create());
+    XpcArrayElements.emplace_back(XpcDict);
+    XpcArrayElements.emplace_back(xpc_double_create(42));
+
+    return xpc_array_create(XpcArrayElements.data(), XpcArrayElements.size());
+  }();
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(JsonArray),
+      XpcArray
+    )
+  );
+  EXPECT_TRUE(
+    JsonArray == xpcToJson(XpcArray)
+  );
+}
+
+TEST(JsonXpcConversionTest, DictionaryContainingArray) {
+  json::Value JsonDict(
+    json::Object{
+      {"a", true},
+      {"b", "foo"},
+      {"c", json::Value{true, "foo", nullptr, 42}},
+      {"d", nullptr},
+      {"e", 42}
+    }
+  );
+
+  xpc_object_t XpcDict = [](){
+    xpc_object_t XpcArray = [](){
+      std::vector<xpc_object_t> XpcArrayElements;
+      XpcArrayElements.emplace_back(xpc_bool_create(true));
+      XpcArrayElements.emplace_back(xpc_string_create("foo"));
+      XpcArrayElements.emplace_back(xpc_null_create());
+      XpcArrayElements.emplace_back(xpc_double_create(42));
+
+      return xpc_array_create(XpcArrayElements.data(), XpcArrayElements.size());
+    }();
+
+    std::vector<std::string> XpcDictKeys;
+    std::vector<xpc_object_t> XpcDictValues;
+    XpcDictKeys.emplace_back("a");
+    XpcDictValues.emplace_back(xpc_bool_create(true));
+    XpcDictKeys.emplace_back("b");
+    XpcDictValues.emplace_back(xpc_string_create("foo"));
+    XpcDictKeys.emplace_back("c");
+    XpcDictValues.emplace_back(XpcArray);
+    XpcDictKeys.emplace_back("d");
+    XpcDictValues.emplace_back(xpc_null_create());
+    XpcDictKeys.emplace_back("e");
+    XpcDictValues.emplace_back(xpc_double_create(42));
+
+    std::vector<const char*> XpcDictKeysCStr = {
+      XpcDictKeys.at(0).c_str(),
+      XpcDictKeys.at(1).c_str(),
+      XpcDictKeys.at(2).c_str(),
+      XpcDictKeys.at(3).c_str(),
+      XpcDictKeys.at(4).c_str()
+    };
+
+    return xpc_dictionary_create(XpcDictKeysCStr.data(), XpcDictValues.data(), XpcDictValues.size());
+  }();
+
+  EXPECT_TRUE(
+    xpc_equal(
+      jsonToXpc(JsonDict),
+      XpcDict
+    )
+  );
+  EXPECT_TRUE(
+    JsonDict == xpcToJson(XpcDict)
+  );
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: clangd/xpc/CMakeLists.txt
===================================================================
--- /dev/null
+++ clangd/xpc/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+get_filename_component(CLANGD_SOURCE_DIR
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../clangd REALPATH)
+include_directories(
+  ${CLANGD_SOURCE_DIR}
+  )
+
+add_extra_unittest(ClangdXpcTests
+  ConversionTests.cpp
+  )
+
+# FIXME: Factor out json::Expr from clangDaemon
+target_link_libraries(ClangdXpcTests
+  PRIVATE
+  clangdXpcJsonConversions
+  clangDaemon
+  LLVMSupport
+  LLVMTestingSupport
+  )
Index: clangd/CMakeLists.txt
===================================================================
--- clangd/CMakeLists.txt
+++ clangd/CMakeLists.txt
@@ -52,3 +52,7 @@
   LLVMSupport
   LLVMTestingSupport
   )
+
+if (CLANGD_BUILD_XPC)
+  add_subdirectory(xpc)
+endif ()
Index: lit.site.cfg.in
===================================================================
--- lit.site.cfg.in
+++ lit.site.cfg.in
@@ -11,6 +11,7 @@
 config.python_executable = "@PYTHON_EXECUTABLE@"
 config.target_triple = "@TARGET_TRIPLE@"
 config.clang_staticanalyzer = @CLANG_ENABLE_STATIC_ANALYZER@
+config.clangd_xpc_support = @CLANGD_BUILD_XPC@
 
 # Support substitution of the tools and libs dirs with user parameters. This is
 # used when we can't determine the tool dir at configuration time.
Index: lit.cfg
===================================================================
--- lit.cfg
+++ lit.cfg
@@ -117,6 +117,10 @@
 if platform.system() not in ['Windows']:
     config.available_features.add('ansi-escape-sequences')
 
+# XPC support for Clangd.
+if config.clangd_xpc_support:
+    config.available_features.add('clangd-xpc-support')
+
 if config.clang_staticanalyzer:
     config.available_features.add('static-analyzer')
     check_clang_tidy = os.path.join(
Index: clangd/xpc/initialize.test
===================================================================
--- /dev/null
+++ clangd/xpc/initialize.test
@@ -0,0 +1,10 @@
+# RUN: clangd-xpc-test-client < %s | FileCheck %s
+# REQUIRES: clangd-xpc-support
+
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootUri":"test:///workspace","capabilities":{},"trace":"off"}}
+# CHECK: {"id":0,"jsonrpc":"2.0","result":{"capabilities":{"codeActionProvider":true,"completionProvider":{"resolveProvider":false,"triggerCharacters":[".",">",":"]},"definitionProvider":true,"documentFormattingProvider":true,"documentHighlightProvider":true,"documentOnTypeFormattingProvider":{"firstTriggerCharacter":"}","moreTriggerCharacter":[]},"documentRangeFormattingProvider":true,"documentSymbolProvider":true,"executeCommandProvider":{"commands":["clangd.applyFix"]},"hoverProvider":true,"renameProvider":true,"signatureHelpProvider":{"triggerCharacters":["(",","]},"textDocumentSync":2,"workspaceSymbolProvider":true}}}
+
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+# CHECK: {"id":3,"jsonrpc":"2.0","result":null}
+
+{"jsonrpc":"2.0","method":"exit"}
Index: xpc/test-client/ClangdXPCTestClient.cpp
===================================================================
--- /dev/null
+++ xpc/test-client/ClangdXPCTestClient.cpp
@@ -0,0 +1,96 @@
+#include "xpc/Conversion.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string>
+#include <xpc/xpc.h>
+
+typedef const char *(*clangd_xpc_get_bundle_identifier_t)(void);
+
+using namespace llvm;
+using namespace clang;
+
+std::string getLibraryPath() {
+  Dl_info info;
+  if (dladdr((void *)(uintptr_t)getLibraryPath, &info) == 0)
+    llvm_unreachable("Call to dladdr() failed");
+  llvm::SmallString<128> LibClangPath;
+  LibClangPath = llvm::sys::path::parent_path(
+      llvm::sys::path::parent_path(info.dli_fname));
+  llvm::sys::path::append(LibClangPath, "lib", "ClangdXPC.framework",
+                          "ClangdXPC");
+  return LibClangPath.str();
+}
+
+static void dumpXPCObject(xpc_object_t Object, llvm::raw_ostream &OS) {
+  xpc_type_t Type = xpc_get_type(Object);
+  if (Type == XPC_TYPE_DICTIONARY) {
+    json::Value Json = clang::clangd::xpcToJson(Object);
+    OS << Json;
+  } else {
+    OS << "<UNKNOWN>";
+  }
+}
+
+int main(int argc, char *argv[]) {
+  // Open the ClangdXPC dylib in the framework.
+  std::string LibPath = getLibraryPath();
+  void *dlHandle = dlopen(LibPath.c_str(), RTLD_LOCAL | RTLD_FIRST);
+  if (!dlHandle)
+    return 1;
+
+  // Lookup the XPC service bundle name, and launch it.
+  clangd_xpc_get_bundle_identifier_t clangd_xpc_get_bundle_identifier =
+      (clangd_xpc_get_bundle_identifier_t)dlsym(
+          dlHandle, "clangd_xpc_get_bundle_identifier");
+  xpc_connection_t conn =
+      xpc_connection_create(clangd_xpc_get_bundle_identifier(), dispatch_get_main_queue());
+
+  // Dump the XPC events.
+  xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {
+    if (event == XPC_ERROR_CONNECTION_INVALID) {
+      llvm::errs() << "Received XPC_ERROR_CONNECTION_INVALID.";
+      exit(EXIT_SUCCESS);
+    }
+    if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
+      llvm::errs() << "Received XPC_ERROR_CONNECTION_INTERRUPTED.";
+      exit(EXIT_SUCCESS);
+    }
+
+    dumpXPCObject(event, llvm::outs());
+    llvm::outs() << "\n";
+  });
+
+  xpc_connection_resume(conn);
+
+  // Read the input to determine the things to send to clangd.
+  llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Stdin =
+      llvm::MemoryBuffer::getSTDIN();
+  if (!Stdin) {
+    llvm::errs() << "Failed to get STDIN!\n";
+    return 1;
+  }
+  for (llvm::line_iterator It(**Stdin, /*SkipBlanks=*/true,
+                              /*CommentMarker=*/'#');
+       !It.is_at_eof(); ++It) {
+    StringRef Line = *It;
+    if (auto Request = json::parse(Line)) {
+      xpc_object_t Object = clangd::jsonToXpc(*Request);
+      xpc_connection_send_message(conn, Object);
+    } else {
+      llvm::errs() << llvm::Twine("JSON parse error: ")
+                    << llvm::toString(Request.takeError());
+      return 1;
+    }
+  }
+
+  dispatch_main();
+
+  // dispatch_main() doesn't return
+  return EXIT_FAILURE;
+}
Index: xpc/test-client/CMakeLists.txt
===================================================================
--- /dev/null
+++ xpc/test-client/CMakeLists.txt
@@ -0,0 +1,26 @@
+include_directories(
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../
+)
+
+add_clang_tool(
+  clangd-xpc-test-client
+  ClangdXPCTestClient.cpp
+
+  DEPENDS ClangdXPC
+)
+
+set(LLVM_LINK_COMPONENTS
+    support
+)
+
+target_link_libraries(clangd-xpc-test-client
+  PRIVATE
+  clangBasic
+  clangDaemon
+  clangFormat
+  clangFrontend
+  clangSema
+  clangTooling
+  clangToolingCore
+  clangdXpcJsonConversions
+)
Index: xpc/framework/ClangdXPC.cpp
===================================================================
--- /dev/null
+++ xpc/framework/ClangdXPC.cpp
@@ -0,0 +1,5 @@
+
+/// Returns the bundle identifier of the Clangd XPC service.
+extern "C" const char *clangd_xpc_get_bundle_identifier() {
+  return "org.llvm.clangd";
+}
Index: xpc/framework/CMakeLists.txt
===================================================================
--- /dev/null
+++ xpc/framework/CMakeLists.txt
@@ -0,0 +1,9 @@
+
+set(SOURCES
+    ClangdXPC.cpp)
+add_clang_library(ClangdXPCLib SHARED
+  ${SOURCES}
+  DEPENDS
+  clangd
+)
+create_clangd_xpc_framework(ClangdXPCLib "ClangdXPC")
Index: xpc/cmake/modules/CreateClangdXPCFramework.cmake
===================================================================
--- /dev/null
+++ xpc/cmake/modules/CreateClangdXPCFramework.cmake
@@ -0,0 +1,73 @@
+# Creates the ClangdXPC framework.
+macro(create_clangd_xpc_framework target name)
+  set(CLANGD_FRAMEWORK_LOCATION "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${name}.framework")
+  set(CLANGD_FRAMEWORK_OUT_LOCATION "${CLANGD_FRAMEWORK_LOCATION}/Versions/A")
+
+  # Create the framework info PLIST.
+  set(CLANGD_XPC_FRAMEWORK_NAME "${name}")
+  configure_file(
+    "${CLANGD_XPC_SOURCE_DIR}/cmake/Info.plist.in"
+    "${CLANGD_XPC_BINARY_DIR}/${name}.Info.plist")
+
+  set(CLANGD_XPC_SERVICE_NAME "clangd")
+  set(CLANGD_XPC_SERVICE_OUT_LOCATION
+      "${CLANGD_FRAMEWORK_OUT_LOCATION}/XPCServices/${CLANGD_XPC_SERVICE_NAME}.xpc/Contents")
+
+  # Create the XPC service info PLIST.
+  set(CLANGD_XPC_SERVICE_BUNDLE_NAME "org.llvm.${CLANGD_XPC_SERVICE_NAME}")
+  configure_file(
+    "${CLANGD_XPC_SOURCE_DIR}/cmake/XPCServiceInfo.plist.in"
+    "${CLANGD_XPC_BINARY_DIR}/${name}Service.Info.plist")
+
+  # Create the custom command
+  add_custom_command(OUTPUT ${CLANGD_FRAMEWORK_LOCATION}
+    # Copy the PLIST.
+    COMMAND ${CMAKE_COMMAND} -E copy
+      "${CLANGD_XPC_BINARY_DIR}/${name}.Info.plist"
+      "${CLANGD_FRAMEWORK_OUT_LOCATION}/Resources/Info.plist"
+
+    # Copy the framework binary.
+    COMMAND ${CMAKE_COMMAND} -E copy
+       "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/lib${target}.dylib"
+       "${CLANGD_FRAMEWORK_OUT_LOCATION}/${name}"
+
+    # Copy the XPC Service PLIST.
+    COMMAND ${CMAKE_COMMAND} -E copy
+      "${CLANGD_XPC_BINARY_DIR}/${name}Service.Info.plist"
+      "${CLANGD_XPC_SERVICE_OUT_LOCATION}/Info.plist"
+
+    # Copy the Clangd binary.
+    COMMAND ${CMAKE_COMMAND} -E copy
+      "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/clangd"
+      "${CLANGD_XPC_SERVICE_OUT_LOCATION}/MacOS/clangd"
+
+     COMMAND ${CMAKE_COMMAND} -E create_symlink "A"
+     "${CLANGD_FRAMEWORK_LOCATION}/Versions/Current"
+
+     COMMAND ${CMAKE_COMMAND} -E create_symlink
+     "Versions/Current/Resources"
+     "${CLANGD_FRAMEWORK_LOCATION}/Resources"
+
+     COMMAND ${CMAKE_COMMAND} -E create_symlink
+     "Versions/Current/XPCServices"
+     "${CLANGD_FRAMEWORK_LOCATION}/XPCServices"
+
+     COMMAND ${CMAKE_COMMAND} -E create_symlink
+     "Versions/Current/${name}"
+     "${CLANGD_FRAMEWORK_LOCATION}/${name}"
+
+    DEPENDS
+      "${CLANGD_XPC_BINARY_DIR}/${name}.Info.plist"
+      "${CLANGD_XPC_BINARY_DIR}/${name}Service.Info.plist"
+      clangd
+    COMMENT "Creating ClangdXPC framework"
+    VERBATIM
+  )
+
+  add_custom_target(
+    ClangdXPC
+    DEPENDS
+    ${target}
+    ${CLANGD_FRAMEWORK_LOCATION}
+  )
+endmacro(create_clangd_xpc_framework)
Index: xpc/cmake/XPCServiceInfo.plist.in
===================================================================
--- /dev/null
+++ xpc/cmake/XPCServiceInfo.plist.in
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd";>
+<plist version="1.0">
+<dict>
+  <key>CFBundleExecutable</key>
+  <string>${CLANGD_XPC_SERVICE_NAME}</string>
+  <key>CFBundleIdentifier</key>
+  <string>${CLANGD_XPC_SERVICE_BUNDLE_NAME}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${CLANGD_XPC_SERVICE_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>XPC!</string>
+  <key>CFBundleVersion</key>
+  <string></string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.0</string>
+  <key>XPCService</key>
+  <dict>
+    <key>ServiceType</key>
+    <string>Application</string>
+    <key>EnvironmentVariables</key>
+    <dict>
+        <key>CLANGD_AS_XPC_SERVICE</key>
+        <string>1</string>
+    </dict>
+  </dict>
+</dict>
+</plist>
Index: xpc/cmake/Info.plist.in
===================================================================
--- /dev/null
+++ xpc/cmake/Info.plist.in
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd";>
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>English</string>
+  <key>CFBundleExecutable</key>
+  <string>${CLANGD_XPC_FRAMEWORK_NAME}</string>
+  <key>CFBundleIconFile</key>
+  <string></string>
+  <key>CFBundleIdentifier</key>
+  <string>org.llvm.${CLANGD_XPC_FRAMEWORK_NAME}</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>${CLANGD_XPC_FRAMEWORK_NAME}</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string></string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.0</string>
+  <key>CSResourcesFileMapped</key>
+  <true/>
+</dict>
+</plist>
Index: xpc/XPCDispatcher.h
===================================================================
--- /dev/null
+++ xpc/XPCDispatcher.h
@@ -0,0 +1,95 @@
+//===--- XPCDispatcher.h - Main XPC service entry point ---------*- 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_XPC_XPCDISPATCHER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_XPC_XPCDISPATCHER_H
+
+#include "Logger.h"
+#include "LSPOutput.h"
+#include "Protocol.h"
+#include "Trace.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/JSON.h"
+#include <iosfwd>
+#include <mutex>
+
+#include <xpc/xpc.h>
+
+namespace clang {
+namespace clangd {
+
+using namespace llvm;
+
+class XPCLSPOutput : public LSPOutput {
+public:
+  XPCLSPOutput(llvm::raw_ostream &Logs, Logger::Level MinLevel,
+    llvm::raw_ostream *InputMirror = nullptr)
+  : clientConnection(nullptr), MinLevel(MinLevel), Logs(Logs),
+    InputMirror{InputMirror} { }
+
+  /// Set's clientConnection for output.
+  /// Must be either reset every time XPC event handler is called or connection
+  /// has to be retained through xpc_retain().
+  void resetClientConnection(xpc_connection_t newClientConnection);
+
+  /// Emit an LSP message.
+  void writeMessage(const json::Value &Result) override;
+
+  /// Write a line to the logging stream.
+  void log(Level, const llvm::formatv_object_base &Message) override;
+
+  /// Mirror \p Message into InputMirror stream. Does nothing if InputMirror is
+  /// null.
+  /// Unlike other methods, mirrorInput is not thread-safe.
+  void mirrorInput(const Twine &) override;
+
+private:
+  xpc_connection_t clientConnection;
+  Logger::Level MinLevel;
+  llvm::raw_ostream &Logs;
+  llvm::raw_ostream *InputMirror;
+
+  std::mutex LogStreamMutex;
+};
+
+/// Parses the LSP "header" and calls the right registered Handler.
+class XPCDispatcher {
+public:
+  // A handler responds to requests for a particular method name.
+  using Handler = std::function<void(const json::Value &)>;
+
+  /// UnknownHandler is called when an unknown method is received.
+  XPCDispatcher(Handler UnknownHandler)
+    : UnknownHandler(std::move(UnknownHandler)) {}
+
+  /// Registers a Handler for the specified Method.
+  void registerHandler(StringRef Method, Handler H);
+
+  /// Parses a JSONRPC message and calls the Handler for it.
+  bool call(const json::Value &Message, LSPOutput &Out) const;
+
+private:
+  llvm::StringMap<Handler> Handlers;
+  Handler UnknownHandler;
+};
+
+/// Parses input queries from LSP client (coming through XPC connections) and
+/// runs call method of \p Dispatcher for each query.
+/// If \p InputJSONMirror is set every received request is logged in JSON form.
+/// After handling each query checks if \p IsDone is set true and exits
+/// the loop if it is.
+void runXPCServerLoop(XPCDispatcher &Dispatcher, XPCLSPOutput &Out,
+  bool &IsDone);
+
+} // namespace clangd
+} // namespace clang
+
+#endif
Index: xpc/XPCDispatcher.cpp
===================================================================
--- /dev/null
+++ xpc/XPCDispatcher.cpp
@@ -0,0 +1,164 @@
+//===--- XPCDispatcher.h - Main XPC service 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 "XPCDispatcher.h"
+
+#include "DispatcherCommon.h"
+#include "xpc/Conversion.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 <sstream>
+
+#include <xpc/xpc.h>
+
+using namespace clang;
+using namespace clangd;
+
+void XPCDispatcher::registerHandler(StringRef Method, Handler H) {
+  assert(!Handlers.count(Method) && "Handler already registered!");
+  Handlers[Method] = std::move(H);
+}
+
+bool XPCDispatcher::call(const json::Value &Message, LSPOutput &Out) const {
+  // Message must be an object with "jsonrpc":"2.0".
+  auto *Object = Message.getAsObject();
+  if (!Object || Object->getString("jsonrpc") != Optional<StringRef>("2.0"))
+    return false;
+  // ID may be any JSON value. If absent, this is a notification.
+  llvm::Optional<json::Value> ID;
+  if (auto *I = Object->get("id"))
+    ID = std::move(*I);
+  // Method must be given.
+  auto Method = Object->getString("method");
+  if (!Method)
+    return false;
+  // Params should be given, use null if not.
+  json::Value Params = nullptr;
+  if (auto *P = Object->get("params"))
+    Params = std::move(*P);
+
+  auto I = Handlers.find(*Method);
+  auto &Handler = I != Handlers.end() ? I->second : UnknownHandler;
+
+  // Create a Context that contains request information.
+  WithContextValue WithRequestOut(RequestOut, &Out);
+  llvm::Optional<WithContextValue> WithID;
+  if (ID)
+    WithID.emplace(RequestID, *ID);
+
+  // Create a tracing Span covering the whole request lifetime.
+  trace::Span Tracer(*Method);
+  if (ID)
+    SPAN_ATTACH(Tracer, "ID", *ID);
+  SPAN_ATTACH(Tracer, "Params", Params);
+
+  // Stash a reference to the span args, so later calls can add metadata.
+  WithContext WithRequestSpan(RequestSpan::stash(Tracer));
+  Handler(std::move(Params));
+  return true;
+}
+
+void XPCLSPOutput::resetClientConnection(xpc_connection_t newClientConnection) {
+  clientConnection = newClientConnection;
+}
+
+void XPCLSPOutput::writeMessage(const json::Value &Message) {
+  xpc_object_t response = jsonToXpc(Message);
+  xpc_connection_send_message(clientConnection, response);
+  xpc_release(response);
+}
+
+void XPCLSPOutput::log(Logger::Level Level,
+                     const llvm::formatv_object_base &Message) {
+  if (Level < MinLevel)
+    return;
+  llvm::sys::TimePoint<> Timestamp = std::chrono::system_clock::now();
+  trace::log(Message);
+  std::lock_guard<std::mutex> Guard(LogStreamMutex);
+  Logs << llvm::formatv("{0}[{1:%H:%M:%S.%L}] {2}\n", indicator(Level),
+                        Timestamp, Message);
+  Logs.flush();
+}
+
+void XPCLSPOutput::mirrorInput(const Twine &Message) {
+  if (!InputMirror)
+    return;
+
+  *InputMirror << Message;
+  InputMirror->flush();
+}
+
+// C "closure"
+namespace {
+  XPCDispatcher *DispatcherPtr = nullptr;
+  bool *IsDonePtr = nullptr;
+  XPCLSPOutput *OutPtr = nullptr;
+  bool HasTransactionBeginBeenCalled = false;
+
+  void connection_handler(xpc_connection_t clientConnection) {
+
+    xpc_connection_set_target_queue(clientConnection, dispatch_get_main_queue());
+
+    if(!HasTransactionBeginBeenCalled) {
+      // man xpc_main
+      // "If the service remains idle after a period of inactivity (defined by
+      //  the system), xpc_main() will exit the process."
+      // ...
+      // "Services may extend the default behavior using xpc_transaction_begin()..."
+      xpc_transaction_begin();
+      HasTransactionBeginBeenCalled = true;
+    }
+
+    OutPtr->resetClientConnection(clientConnection);
+
+    xpc_connection_set_event_handler(
+      clientConnection,
+      ^(xpc_object_t message) {
+        if (message == XPC_ERROR_CONNECTION_INVALID) {
+          // connection is being terminated
+          log("Received XPC_ERROR_CONNECTION_INVALID message - returning from the event_handler.");
+          return;
+        }
+
+        if (xpc_get_type(message) != XPC_TYPE_DICTIONARY) {
+          log("Received XPC message of unknown type - returning from the event_handler.");
+          return;
+        }
+
+        const json::Value Doc = xpcToJson(message);
+
+        vlog("<<< {0:2}\n", Doc);
+        OutPtr->mirrorInput(
+          llvm::formatv("{1}", Doc));
+
+        if (DispatcherPtr->call(Doc, *OutPtr)) {
+          if (*IsDonePtr) {
+            log("IsDonePtr == true. Returning from the event_handler.\n");
+            xpc_connection_cancel(xpc_dictionary_get_remote_connection(message));
+          }
+        } else
+          log("JSON dispatch failed!\n");
+      }
+    );
+
+    xpc_connection_resume(clientConnection);
+  }
+}
+
+void clangd::runXPCServerLoop(XPCDispatcher &Dispatcher, XPCLSPOutput &Out,
+  bool &IsDone) {
+  DispatcherPtr = &Dispatcher;
+  IsDonePtr = &IsDone;
+  OutPtr = &Out;
+
+  xpc_main(connection_handler);
+}
Index: xpc/README.txt
===================================================================
--- /dev/null
+++ xpc/README.txt
@@ -0,0 +1,6 @@
+This directory contains:
+- the XPC transport layer (alternative transport layer to JSON-RPC)
+- XPC framework wrapper that wraps around Clangd to make it a valid XPC service
+- XPC test-client
+
+MacOS only. Feature is guarded by CLANGD_BUILD_XPC, including whole xpc/ dir.
\ No newline at end of file
Index: xpc/Conversion.h
===================================================================
--- /dev/null
+++ xpc/Conversion.h
@@ -0,0 +1,25 @@
+//===--- Conversion.h - LSP data (de-)serialization through XPC -*- 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_XPC_XPCJSONCONVERSIONS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_XPC_XPCJSONCONVERSIONS_H
+
+#include "llvm/Support/JSON.h"
+#include <xpc/xpc.h>
+
+namespace clang {
+namespace clangd {
+
+xpc_object_t jsonToXpc(const llvm::json::Value& json);
+llvm::json::Value xpcToJson(const xpc_object_t& json);
+
+} // namespace clangd
+} // namespace clang
+
+#endif
Index: xpc/Conversion.cpp
===================================================================
--- /dev/null
+++ xpc/Conversion.cpp
@@ -0,0 +1,100 @@
+//===--- Conversion.cpp - LSP data (de-)serialization through XPC - C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "xpc/Conversion.h"
+
+#include "Logger.h"
+
+#include <string>
+#include <vector>
+
+using namespace clang;
+using namespace clangd;
+
+using namespace llvm;
+
+xpc_object_t clangd::jsonToXpc(const json::Value& json) {
+  switch(json.kind()) {
+    case json::Value::Null:    return xpc_null_create();
+    case json::Value::Boolean: return xpc_bool_create(json.getAsBoolean().getValue());
+    case json::Value::Number:  return xpc_double_create(json.getAsNumber().getValue());
+    case json::Value::String:  return xpc_string_create(json.getAsString().getValue().str().c_str());
+    case json::Value::Array:   {
+      std::vector<xpc_object_t> elements;
+      for(const auto& e : *json.getAsArray())
+        elements.emplace_back( jsonToXpc(e) );
+
+      return xpc_array_create(elements.data(), elements.size());
+    }
+    case json::Value::Object: {
+      const auto* const JsonObject = json.getAsObject();
+
+      std::vector<std::string> keys;
+      std::vector<const char*> keysCStr;
+      std::vector<xpc_object_t> values;
+
+      keys.reserve(JsonObject->size());
+      keysCStr.reserve(JsonObject->size());
+      values.reserve(JsonObject->size());
+
+      for(const auto& k_v : *JsonObject) {
+        keys.emplace_back(static_cast<llvm::StringRef>(k_v.first).str());
+        values.emplace_back(jsonToXpc(k_v.second));
+      }
+      // Get pointers only now so there's no possible re-allocation of keys.
+      for(const auto& k : keys)
+        keysCStr.emplace_back(k.c_str());
+
+      assert(keysCStr.size() == values.size() && "keys - data size mismatch");
+      return xpc_dictionary_create(keysCStr.data(), values.data(), keysCStr.size());
+    }
+  }
+  llvm_unreachable("unsupported JSON data type in jsonToXpc() conversion");
+}
+
+json::Value clangd::xpcToJson(const xpc_object_t& xpcObj) {
+  const xpc_type_t objType = xpc_get_type(xpcObj);
+  if (objType == XPC_TYPE_NULL)
+    return json::Value(nullptr);
+  if (objType == XPC_TYPE_BOOL)
+    return json::Value(xpc_bool_get_value(xpcObj));
+  if (objType == XPC_TYPE_DOUBLE)
+    return json::Value(xpc_double_get_value(xpcObj));
+  if (objType == XPC_TYPE_INT64)
+    return json::Value(xpc_int64_get_value(xpcObj));
+  if (objType == XPC_TYPE_UINT64)
+    return json::Value( static_cast<long long>(xpc_uint64_get_value(xpcObj)) );
+  if (objType == XPC_TYPE_STRING)
+    return json::Value(xpc_string_get_string_ptr(xpcObj));
+  if (objType == XPC_TYPE_ARRAY) {
+    __block json::Array result;
+    xpc_array_apply(
+      xpcObj,
+      ^bool(size_t /* index */, xpc_object_t value) {
+        result.emplace_back(xpcToJson(value));
+        return true;
+      }
+    );
+    return std::move(result);
+  }
+  if (objType == XPC_TYPE_DICTIONARY) {
+    __block json::Object result;
+    xpc_dictionary_apply(
+      xpcObj,
+      ^bool(const char *key, xpc_object_t value) {
+        result.try_emplace(json::ObjectKey(key), xpcToJson(value));
+        return true;
+      }
+    );
+    return std::move(result);
+  }
+  elog("unsupported JSON data type in xpcToJson() conversion");
+  llvm_unreachable("unsupported JSON data type in xpcToJson() conversion");
+  return json::Value(nullptr);
+}
\ No newline at end of file
Index: xpc/CMakeLists.txt
===================================================================
--- /dev/null
+++ xpc/CMakeLists.txt
@@ -0,0 +1,29 @@
+set(CLANGD_XPC_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+set(CLANGD_XPC_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
+
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
+include(CreateClangdXPCFramework)
+
+add_subdirectory(framework)
+add_subdirectory(test-client)
+
+include_directories(
+  ${CMAKE_CURRENT_SOURCE_DIR}/../
+)
+
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+# Needed by LLVM's CMake checks because this file defines multiple targets.
+set(LLVM_OPTIONAL_SOURCES Conversion.cpp XPCDispatcher.cpp)
+
+add_clang_library(clangdXpcJsonConversions
+  Conversion.cpp
+  )
+
+add_clang_library(clangdXpcDispatcher
+  XPCDispatcher.cpp
+  DEPENDS clangdXpcJsonConversions
+  LINK_LIBS clangdXpcJsonConversions
+  )
Index: tool/ClangdMain.cpp
===================================================================
--- tool/ClangdMain.cpp
+++ tool/ClangdMain.cpp
@@ -8,6 +8,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "ClangdLSPServer.h"
+#include "Features.inc"
 #include "JSONRPCDispatcher.h"
 #include "Path.h"
 #include "Trace.h"
@@ -20,11 +21,15 @@
 #include "llvm/Support/raw_ostream.h"
 #include "clang/Basic/Version.h"
 #include <cstdlib>
-#include <iostream>
 #include <memory>
 #include <string>
 #include <thread>
 
+#ifdef CLANGD_BUILD_XPC
+#include "xpc/XPCDispatcher.h"
+#include <cstdlib>
+#endif
+
 using namespace clang;
 using namespace clang::clangd;
 
@@ -231,12 +236,6 @@
   if (Tracer)
     TracingSession.emplace(*Tracer);
 
-  JSONOutput Out(llvm::outs(), llvm::errs(), LogLevel,
-                 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;
@@ -277,11 +276,44 @@
   CCOpts.BundleOverloads = CompletionStyle != Detailed;
   CCOpts.ShowOrigins = ShowOrigins;
 
-  // 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;
+
+#ifdef CLANGD_BUILD_XPC
+  if (getenv("CLANGD_AS_XPC_SERVICE")) {
+    XPCDispatcher Dispatcher([](const json::Value &Params) {
+      replyError(ErrorCode::MethodNotFound, "method not found");
+    });
+    registerCallbackHandlers(Dispatcher, /*Callbacks=*/LSPServer);
+
+    XPCLSPOutput Out(
+      llvm::errs(), LogLevel,
+      InputMirrorStream.hasValue() ? InputMirrorStream.getPointer(): nullptr);
+
+    clangd::LoggingSession LoggingSession(Out);
+
+    runXPCServerLoop(Dispatcher, Out, LSPServer.getIsDone());
+  } else
+#endif
+  {
+    JSONRPCDispatcher Dispatcher([](const json::Value &Params) {
+      replyError(ErrorCode::MethodNotFound, "method not found");
+    });
+    registerCallbackHandlers(Dispatcher, /*Callbacks=*/LSPServer);
+
+
+    JSONOutput Out(llvm::outs(), llvm::errs(), LogLevel,
+                 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: tool/CMakeLists.txt
===================================================================
--- tool/CMakeLists.txt
+++ tool/CMakeLists.txt
@@ -1,4 +1,5 @@
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/..)
 
 add_clang_tool(clangd
   ClangdMain.cpp
@@ -8,6 +9,11 @@
   support
   )
 
+set(CLANGD_XPC_LIBS "")
+if(CLANGD_BUILD_XPC)
+  list(APPEND CLANGD_XPC_LIBS "clangdXpcJsonConversions" "clangdXpcDispatcher")
+endif()
+
 target_link_libraries(clangd
   PRIVATE
   clangBasic
@@ -17,4 +23,5 @@
   clangSema
   clangTooling
   clangToolingCore
+  ${CLANGD_XPC_LIBS}
   )
Index: ProtocolHandlers.h
===================================================================
--- ProtocolHandlers.h
+++ ProtocolHandlers.h
@@ -7,11 +7,11 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// ProtocolHandlers translates incoming JSON requests from JSONRPCDispatcher
-// into method calls on ClangLSPServer.
+// ProtocolHandlers translates incoming LSP requests from JSONRPCDispatcher
+// or XPCDispatcher into method calls on ClangLSPServer.
 //
 // Currently it parses requests into objects, but the ClangLSPServer is
-// responsible for producing JSON responses. We should move that here, too.
+// responsible for producing LSP responses. We should move that here, too.
 //
 //===----------------------------------------------------------------------===//
 
@@ -23,6 +23,8 @@
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/raw_ostream.h"
 
+using namespace llvm;
+
 namespace clang {
 namespace clangd {
 
@@ -57,8 +59,64 @@
   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::Value &RawParams) {
+      typename std::remove_reference<Param>::type P;
+      if (fromJSON(RawParams, P)) {
+        (Callbacks->*Handler)(P);
+      } else {
+        elog("Failed to decode {0} request.", Method);
+      }
+    });
+  }
+
+  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("textDocument/documentSymbol", &ProtocolCallbacks::onDocumentSymbol);
+  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,78 +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;
-using namespace llvm;
-
-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::Value &RawParams) {
-      typename std::remove_reference<Param>::type P;
-      if (fromJSON(RawParams, P)) {
-        (Callbacks->*Handler)(P);
-      } else {
-        elog("Failed to decode {0} request.", Method);
-      }
-    });
-  }
-
-  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("textDocument/documentSymbol", &ProtocolCallbacks::onDocumentSymbol);
-  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 "Logger.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/Support/JSON.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 llvm::json::Value &Result) = 0;
+
+  /// Write a line to log.
+  virtual void log(Level, const llvm::formatv_object_base &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
 //
@@ -11,6 +11,7 @@
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H
 
 #include "Logger.h"
+#include "LSPOutput.h"
 #include "Protocol.h"
 #include "Trace.h"
 #include "clang/Basic/LLVM.h"
@@ -25,26 +26,24 @@
 
 /// 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,
              Logger::Level MinLevel, llvm::raw_ostream *InputMirror = nullptr,
              bool Pretty = false)
       : Pretty(Pretty), MinLevel(MinLevel), Outs(Outs), Logs(Logs),
         InputMirror(InputMirror) {}
 
   /// Emit a JSONRPC message.
-  void writeMessage(const llvm::json::Value &Result);
+  void writeMessage(const llvm::json::Value &Result) override;
 
   /// Write a line to the logging stream.
   void log(Level, const llvm::formatv_object_base &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;
@@ -84,7 +83,7 @@
   void registerHandler(StringRef Method, Handler H);
 
   /// Parses a JSONRPC message and calls the Handler for it.
-  bool call(const llvm::json::Value &Message, JSONOutput &Out) const;
+  bool call(const llvm::json::Value &Message, LSPOutput &Out) const;
 
 private:
   llvm::StringMap<Handler> Handlers;
@@ -107,7 +106,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,14 +1,14 @@
-//===--- 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 "ProtocolHandlers.h"
 #include "Trace.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
@@ -23,38 +23,6 @@
 using namespace clang;
 using namespace clangd;
 
-namespace {
-static Key<json::Value> 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(llvm::json::Object *Args) : Args(Args) {}
-  std::mutex Mu;
-  llvm::json::Object *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::Object 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::Value &Message) {
   std::string S;
   llvm::raw_string_ostream OS(S);
@@ -92,61 +60,6 @@
   InputMirror->flush();
 }
 
-void clangd::reply(json::Value &&Result) {
-  auto ID = Context::current().get(RequestID);
-  if (!ID) {
-    elog("Attempted to reply to a notification!");
-    return;
-  }
-  RequestSpan::attach([&](json::Object &Args) { Args["Reply"] = Result; });
-  log("--> reply({0})", *ID);
-  Context::current()
-      .getExisting(RequestOut)
-      ->writeMessage(json::Object{
-          {"jsonrpc", "2.0"},
-          {"id", *ID},
-          {"result", std::move(Result)},
-      });
-}
-
-void clangd::replyError(ErrorCode code, const llvm::StringRef &Message) {
-  elog("Error {0}: {1}", static_cast<int>(code), Message);
-  RequestSpan::attach([&](json::Object &Args) {
-    Args["Error"] = json::Object{{"code", static_cast<int>(code)},
-                                 {"message", Message.str()}};
-  });
-
-  if (auto ID = Context::current().get(RequestID)) {
-    log("--> reply({0}) error: {1}", *ID, Message);
-    Context::current()
-        .getExisting(RequestOut)
-        ->writeMessage(json::Object{
-            {"jsonrpc", "2.0"},
-            {"id", *ID},
-            {"error", json::Object{{"code", static_cast<int>(code)},
-                                   {"message", Message}}},
-        });
-  }
-}
-
-void clangd::call(StringRef Method, json::Value &&Params) {
-  RequestSpan::attach([&](json::Object &Args) {
-    Args["Call"] = json::Object{{"method", Method.str()}, {"params", Params}};
-  });
-  // FIXME: Generate/Increment IDs for every request so that we can get proper
-  // replies once we need to.
-  auto ID = 1;
-  log("--> {0}({1})", Method, ID);
-  Context::current()
-      .getExisting(RequestOut)
-      ->writeMessage(json::Object{
-          {"jsonrpc", "2.0"},
-          {"id", ID},
-          {"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);
@@ -170,7 +83,7 @@
 }
 
 bool JSONRPCDispatcher::call(const json::Value &Message,
-                             JSONOutput &Out) const {
+                            LSPOutput &Out) const {
   // Message must be an object with "jsonrpc":"2.0".
   auto *Object = Message.getAsObject();
   if (!Object || Object->getString("jsonrpc") != Optional<StringRef>("2.0"))
@@ -236,7 +149,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;
@@ -308,7 +221,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)) {
@@ -340,7 +253,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: Features.inc.in
===================================================================
--- /dev/null
+++ Features.inc.in
@@ -0,0 +1 @@
+#define CLANGD_BUILD_XPC @CLANGD_BUILD_XPC@
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 "Trace.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Chrono.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/SourceMgr.h"
+#include <istream>
+
+namespace clang {
+namespace clangd {
+
+extern Key<llvm::json::Value> 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(llvm::json::Object *Args) : Args(Args) {}
+  std::mutex Mu;
+  llvm::json::Object *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 llvm::json::Object 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(llvm::json::Value &&Message);
+
+void reply(llvm::json::Value &&Result);
+
+void replyError(ErrorCode code, const llvm::StringRef &Message);
+
+void call(StringRef Method, llvm::json::Value &&Params);
+
+}
+}
+
+#endif
\ No newline at end of file
Index: DispatcherCommon.cpp
===================================================================
--- /dev/null
+++ DispatcherCommon.cpp
@@ -0,0 +1,89 @@
+//===--- 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 {
+
+using namespace llvm;
+
+Key<json::Value> 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::Value &&Message) {
+  Context::current()
+      .getExisting(RequestOut)
+      ->writeMessage(Message);
+}
+
+void reply(json::Value &&Result) {
+  auto ID = Context::current().get(RequestID);
+  if (!ID) {
+    elog("Attempted to reply to a notification!");
+    return;
+  }
+  RequestSpan::attach([&](json::Object &Args) { Args["Reply"] = Result; });
+  log("--> reply({0})", *ID);
+  Context::current()
+      .getExisting(RequestOut)
+      ->writeMessage(json::Object{
+          {"jsonrpc", "2.0"},
+          {"id", *ID},
+          {"result", std::move(Result)},
+      });
+}
+
+void replyError(ErrorCode code, const llvm::StringRef &Message) {
+  elog("Error {0}: {1}", static_cast<int>(code), Message);
+  RequestSpan::attach([&](json::Object &Args) {
+    Args["Error"] = json::Object{{"code", static_cast<int>(code)},
+                                 {"message", Message.str()}};
+  });
+
+  if (auto ID = Context::current().get(RequestID)) {
+    log("--> reply({0}) error: {1}", *ID, Message);
+    Context::current()
+        .getExisting(RequestOut)
+        ->writeMessage(json::Object{
+            {"jsonrpc", "2.0"},
+            {"id", *ID},
+            {"error", json::Object{{"code", static_cast<int>(code)},
+                                   {"message", Message}}},
+        });
+  }
+}
+
+void call(StringRef Method, json::Value &&Params) {
+  RequestSpan::attach([&](json::Object &Args) {
+    Args["Call"] = json::Object{{"method", Method.str()}, {"params", Params}};
+  });
+  // FIXME: Generate/Increment IDs for every request so that we can get proper
+  // replies once we need to.
+  auto ID = 1;
+  log("--> {0}({1})", Method, ID);
+  Context::current()
+      .getExisting(RequestOut)
+      ->writeMessage(json::Object{
+          {"jsonrpc", "2.0"},
+          {"id", ID},
+          {"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;
@@ -83,7 +76,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"
@@ -413,33 +414,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::Value &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);
@@ -482,7 +463,7 @@
   }
 
   // Publish diagnostics.
-  Out.writeMessage(json::Object{
+  sendMessage(json::Object{
       {"jsonrpc", "2.0"},
       {"method", "textDocument/publishDiagnostics"},
       {"params",
@@ -498,3 +479,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
@@ -16,7 +16,8 @@
 string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} CLANG_TOOLS_DIR ${LLVM_RUNTIME_OUTPUT_INTDIR})
 
 llvm_canonicalize_cmake_booleans(
-  CLANG_ENABLE_STATIC_ANALYZER)
+  CLANG_ENABLE_STATIC_ANALYZER
+  CLANGD_BUILD_XPC)
 
 configure_lit_site_cfg(
   ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
@@ -72,6 +73,10 @@
     )
 endif()
 
+if(CLANGD_BUILD_XPC)
+  list(APPEND CLANG_TOOLS_TEST_DEPS clangd-xpc-test-client)
+endif()
+
 set(llvm_utils
   FileCheck count not
   )
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to