Sfoglia il codice sorgente

Refactor the language server structure. (#4721)

I'm trying to make the LSP look more like the rest of the toolchain. I'm
trying to separate the handlers from the transport layer, and remove the
multiple inheritance aspect. Also fixing some style issues, switching to
`Map`, and removing an unnecessary copt.

I'm using `Context` for the central object for consistency with other
portions of the toolchain. In order to get the `handle_*` files working,
I'm using LLVM's registry class. It has a quirk that I can't register
two registries in the same cpp file, so there are two one-line cpp
files.

In order to help show the delta (or lack thereof) for actual
implementation, I've copied server.cpp over handle_* and undone that in
two commits. See the third and fourth commits on the PR history for
that.
Jon Ross-Perkins 1 anno fa
parent
commit
96d836f965

+ 43 - 9
toolchain/language_server/BUILD

@@ -8,26 +8,60 @@ package(default_visibility = ["//visibility:public"])
 
 cc_library(
     name = "language_server",
-    srcs = [
-        "language_server.cpp",
-        "server.cpp",
-        "server.h",
-    ],
+    srcs = ["language_server.cpp"],
     hdrs = ["language_server.h"],
-    # Some parameters are unused in clangd headers.
-    copts = ["-Wno-unused-parameter"],
     deps = [
+        ":context",
+        ":incoming_messages",
+        ":outgoing_messages",
         "//common:error",
         "//common:ostream",
+        "@llvm-project//clang-tools-extra/clangd:ClangDaemon",
+    ],
+)
+
+cc_library(
+    name = "context",
+    hdrs = ["context.h"],
+    deps = [
+        "//common:map",
+        "@llvm-project//clang-tools-extra/clangd:ClangDaemon",
+    ],
+)
+
+cc_library(
+    name = "handle",
+    srcs = glob(["handle_*"]),
+    hdrs = ["handle.h"],
+    deps = [
+        ":context",
         "//toolchain/base:shared_value_stores",
         "//toolchain/diagnostics:null_diagnostics",
         "//toolchain/lex",
-        "//toolchain/lex:tokenized_buffer",
         "//toolchain/parse",
         "//toolchain/parse:node_kind",
         "//toolchain/parse:tree",
         "//toolchain/source:source_buffer",
+    ],
+)
+
+cc_library(
+    name = "incoming_messages",
+    srcs = ["incoming_messages.cpp"],
+    hdrs = ["incoming_messages.h"],
+    deps = [
+        ":context",
+        ":handle",
+        "//common:check",
+        "//common:map",
+        "@llvm-project//clang-tools-extra/clangd:ClangDaemon",
+    ],
+)
+
+cc_library(
+    name = "outgoing_messages",
+    hdrs = ["outgoing_messages.h"],
+    deps = [
         "@llvm-project//clang-tools-extra/clangd:ClangDaemon",
-        "@llvm-project//llvm:Support",
     ],
 )

+ 26 - 0
toolchain/language_server/context.h

@@ -0,0 +1,26 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TOOLCHAIN_LANGUAGE_SERVER_CONTEXT_H_
+#define CARBON_TOOLCHAIN_LANGUAGE_SERVER_CONTEXT_H_
+
+#include "clang-tools-extra/clangd/Protocol.h"
+#include "clang-tools-extra/clangd/support/Function.h"
+#include "common/map.h"
+
+namespace Carbon::LanguageServer {
+
+// Context for LSP call handling.
+class Context {
+ public:
+  auto files() -> Map<std::string, std::string>& { return files_; }
+
+ private:
+  // Content of files managed by the language client.
+  Map<std::string, std::string> files_;
+};
+
+}  // namespace Carbon::LanguageServer
+
+#endif  // CARBON_TOOLCHAIN_LANGUAGE_SERVER_CONTEXT_H_

+ 38 - 0
toolchain/language_server/handle.h

@@ -0,0 +1,38 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TOOLCHAIN_LANGUAGE_SERVER_HANDLE_H_
+#define CARBON_TOOLCHAIN_LANGUAGE_SERVER_HANDLE_H_
+
+#include "toolchain/language_server/context.h"
+
+namespace Carbon::LanguageServer {
+
+// Stores the content of newly-opened documents.
+auto HandleDidChangeTextDocument(
+    Context& context, const clang::clangd::DidChangeTextDocumentParams& params)
+    -> void;
+
+// Updates the content of already-open documents.
+auto HandleDidOpenTextDocument(
+    Context& context, const clang::clangd::DidOpenTextDocumentParams& params)
+    -> void;
+
+// Provides information about document symbols.
+auto HandleDocumentSymbol(
+    Context& context, const clang::clangd::DocumentSymbolParams& params,
+    llvm::function_ref<
+        void(llvm::Expected<std::vector<clang::clangd::DocumentSymbol>>)>
+        on_done) -> void;
+
+// Tells the client what features are supported.
+auto HandleInitialize(
+    Context& /*context*/,
+    const clang::clangd::NoParams& /*client_capabilities*/,
+    llvm::function_ref<void(llvm::Expected<llvm::json::Object>)> on_done)
+    -> void;
+
+}  // namespace Carbon::LanguageServer
+
+#endif  // CARBON_TOOLCHAIN_LANGUAGE_SERVER_HANDLE_H_

+ 92 - 0
toolchain/language_server/handle_document_symbol.cpp

@@ -0,0 +1,92 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/base/shared_value_stores.h"
+#include "toolchain/diagnostics/null_diagnostics.h"
+#include "toolchain/language_server/handle.h"
+#include "toolchain/lex/lex.h"
+#include "toolchain/parse/node_kind.h"
+#include "toolchain/parse/parse.h"
+#include "toolchain/parse/tree_and_subtrees.h"
+#include "toolchain/source/source_buffer.h"
+
+namespace Carbon::LanguageServer {
+
+// Returns the text of first child of kind Parse::NodeKind::IdentifierName.
+static auto GetIdentifierName(const SharedValueStores& value_stores,
+                              const Lex::TokenizedBuffer& tokens,
+                              const Parse::TreeAndSubtrees& tree_and_subtrees,
+                              Parse::NodeId node)
+    -> std::optional<llvm::StringRef> {
+  for (auto child : tree_and_subtrees.children(node)) {
+    if (tree_and_subtrees.tree().node_kind(child) ==
+        Parse::NodeKind::IdentifierName) {
+      auto token = tree_and_subtrees.tree().node_token(child);
+      if (tokens.GetKind(token) == Lex::TokenKind::Identifier) {
+        return value_stores.identifiers().Get(tokens.GetIdentifier(token));
+      }
+    }
+  }
+  return std::nullopt;
+}
+
+auto HandleDocumentSymbol(
+    Context& context, const clang::clangd::DocumentSymbolParams& params,
+    llvm::function_ref<
+        void(llvm::Expected<std::vector<clang::clangd::DocumentSymbol>>)>
+        on_done) -> void {
+  SharedValueStores value_stores;
+  llvm::vfs::InMemoryFileSystem vfs;
+  auto lookup = context.files().Lookup(params.textDocument.uri.file());
+  CARBON_CHECK(lookup);
+  vfs.addFile(lookup.key(), /*mtime=*/0,
+              llvm::MemoryBuffer::getMemBufferCopy(lookup.value()));
+
+  auto source =
+      SourceBuffer::MakeFromFile(vfs, lookup.key(), NullDiagnosticConsumer());
+  auto tokens = Lex::Lex(value_stores, *source, NullDiagnosticConsumer());
+  auto tree = Parse::Parse(tokens, NullDiagnosticConsumer(), nullptr);
+  Parse::TreeAndSubtrees tree_and_subtrees(tokens, tree);
+  std::vector<clang::clangd::DocumentSymbol> result;
+  for (const auto& node : tree.postorder()) {
+    clang::clangd::SymbolKind symbol_kind;
+    switch (tree.node_kind(node)) {
+      case Parse::NodeKind::FunctionDecl:
+      case Parse::NodeKind::FunctionDefinitionStart:
+        symbol_kind = clang::clangd::SymbolKind::Function;
+        break;
+      case Parse::NodeKind::Namespace:
+        symbol_kind = clang::clangd::SymbolKind::Namespace;
+        break;
+      case Parse::NodeKind::InterfaceDefinitionStart:
+      case Parse::NodeKind::NamedConstraintDefinitionStart:
+        symbol_kind = clang::clangd::SymbolKind::Interface;
+        break;
+      case Parse::NodeKind::ClassDefinitionStart:
+        symbol_kind = clang::clangd::SymbolKind::Class;
+        break;
+      default:
+        continue;
+    }
+
+    if (auto name =
+            GetIdentifierName(value_stores, tokens, tree_and_subtrees, node)) {
+      auto token = tree.node_token(node);
+      clang::clangd::Position pos{tokens.GetLineNumber(token) - 1,
+                                  tokens.GetColumnNumber(token) - 1};
+
+      clang::clangd::DocumentSymbol symbol{
+          .name = std::string(*name),
+          .kind = symbol_kind,
+          .range = {.start = pos, .end = pos},
+          .selectionRange = {.start = pos, .end = pos},
+      };
+
+      result.push_back(symbol);
+    }
+  }
+  on_done(result);
+}
+
+}  // namespace Carbon::LanguageServer

+ 20 - 0
toolchain/language_server/handle_initialize.cpp

@@ -0,0 +1,20 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/language_server/handle.h"
+
+namespace Carbon::LanguageServer {
+
+auto HandleInitialize(
+    Context& /*context*/,
+    const clang::clangd::NoParams& /*client_capabilities*/,
+    llvm::function_ref<void(llvm::Expected<llvm::json::Object>)> on_done)
+    -> void {
+  llvm::json::Object capabilities{{"documentSymbolProvider", true},
+                                  {"textDocumentSync", /*Full=*/1}};
+  llvm::json::Object reply{{"capabilities", std::move(capabilities)}};
+  on_done(reply);
+}
+
+}  // namespace Carbon::LanguageServer

+ 25 - 0
toolchain/language_server/handle_text_document.cpp

@@ -0,0 +1,25 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/language_server/handle.h"
+
+namespace Carbon::LanguageServer {
+
+auto HandleDidOpenTextDocument(
+    Context& context, const clang::clangd::DidOpenTextDocumentParams& params)
+    -> void {
+  context.files().Update(params.textDocument.uri.file(),
+                         params.textDocument.text);
+}
+
+auto HandleDidChangeTextDocument(
+    Context& context, const clang::clangd::DidChangeTextDocumentParams& params)
+    -> void {
+  // Full text is sent if full sync is specified in capabilities.
+  CARBON_CHECK(params.contentChanges.size() == 1);
+  context.files().Update(params.textDocument.uri.file(),
+                         params.contentChanges[0].text);
+}
+
+}  // namespace Carbon::LanguageServer

+ 107 - 0
toolchain/language_server/incoming_messages.cpp

@@ -0,0 +1,107 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/language_server/incoming_messages.h"
+
+#include "toolchain/language_server/handle.h"
+
+namespace Carbon::LanguageServer {
+
+// Parses a JSON value into a specific parameter type. The name of the method is
+// used when producing errors.
+template <typename ParamsT>
+inline auto Parse(llvm::StringRef name, const llvm::json::Value& raw_params)
+    -> llvm::Expected<ParamsT> {
+  ParamsT params;
+  llvm::json::Path::Root root;
+  if (!clang::clangd::fromJSON(raw_params, params, root)) {
+    return llvm::make_error<clang::clangd::LSPError>(
+        llvm::formatv("in call to `{0}`, JSON parse failed: {1}", name,
+                      llvm::fmt_consume(root.getError())),
+        clang::clangd::ErrorCode::InvalidParams);
+  }
+  return std::move(params);
+}
+
+template <typename ParamsT, typename ResultT>
+auto IncomingMessages::AddCallHandler(
+    llvm::StringRef name,
+    void (*handler)(Context&, const ParamsT&,
+                    llvm::function_ref<void(llvm::Expected<ResultT>)>))
+    -> void {
+  CallHandler parsing_handler =
+      [name, handler](
+          Context& context, llvm::json::Value raw_params,
+          llvm::function_ref<void(llvm::Expected<llvm::json::Value>)> on_done)
+      -> void {
+    auto params = Parse<ParamsT>(name, raw_params);
+    if (!params) {
+      on_done(params.takeError());
+      return;
+    }
+    handler(context, *params, on_done);
+  };
+  auto result = call_handlers_.Insert(name, parsing_handler);
+  CARBON_CHECK(result.is_inserted(), "Duplicate handler: {0}", name);
+}
+
+template <typename ParamsT>
+auto IncomingMessages::AddNotificationHandler(llvm::StringRef name,
+                                              void (*handler)(Context&,
+                                                              const ParamsT&))
+    -> void {
+  NotificationHandler parsing_handler =
+      [name, handler](Context& context, llvm::json::Value raw_params) -> void {
+    auto params = Parse<ParamsT>(name, raw_params);
+    if (!params) {
+      // TODO: Maybe we should do something more with this error?
+      llvm::consumeError(params.takeError());
+    }
+    handler(context, *params);
+  };
+  auto result = notification_handlers_.Insert(name, parsing_handler);
+  CARBON_CHECK(result.is_inserted(), "Duplicate handler: {0}", name);
+}
+
+IncomingMessages::IncomingMessages(clang::clangd::Transport* transport,
+                                   Context* context)
+    : transport_(transport), context_(context) {
+  AddCallHandler("textDocument/documentSymbol", &HandleDocumentSymbol);
+  AddCallHandler("initialize", &HandleInitialize);
+  AddNotificationHandler("textDocument/didChange",
+                         &HandleDidChangeTextDocument);
+  AddNotificationHandler("textDocument/didOpen", &HandleDidOpenTextDocument);
+}
+
+auto IncomingMessages::onCall(llvm::StringRef name, llvm::json::Value params,
+                              llvm::json::Value id) -> bool {
+  if (auto result = call_handlers_.Lookup(name)) {
+    (result.value())(*context_, std::move(params),
+                     [&](llvm::Expected<llvm::json::Value> reply) {
+                       transport_->reply(id, std::move(reply));
+                     });
+  } else {
+    transport_->reply(id, llvm::make_error<clang::clangd::LSPError>(
+                              llvm::formatv("call `{0}` not found", name),
+                              clang::clangd::ErrorCode::MethodNotFound));
+  }
+
+  return true;
+}
+
+auto IncomingMessages::onNotify(llvm::StringRef name, llvm::json::Value value)
+    -> bool {
+  if (name == "exit") {
+    return false;
+  }
+  if (auto result = notification_handlers_.Lookup(name)) {
+    (result.value())(*context_, std::move(value));
+  } else {
+    clang::clangd::log("notification `{0}` not found", name);
+  }
+
+  return true;
+}
+
+}  // namespace Carbon::LanguageServer

+ 76 - 0
toolchain/language_server/incoming_messages.h

@@ -0,0 +1,76 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TOOLCHAIN_LANGUAGE_SERVER_INCOMING_MESSAGES_H_
+#define CARBON_TOOLCHAIN_LANGUAGE_SERVER_INCOMING_MESSAGES_H_
+
+#include "clang-tools-extra/clangd/LSPBinder.h"
+#include "clang-tools-extra/clangd/Transport.h"
+#include "common/check.h"
+#include "common/map.h"
+#include "toolchain/language_server/context.h"
+
+namespace Carbon::LanguageServer {
+
+// Handles LSP messages from the client (IDE extension) by forwarding them to
+// `handlers_`.
+//
+// Handlers can return false to indicate server shutdown, although that's only
+// used for the `exit` notification.
+//
+// TODO: Consider adding multithreading support for calls.
+class IncomingMessages : public clang::clangd::Transport::MessageHandler {
+ public:
+  explicit IncomingMessages(clang::clangd::Transport* transport,
+                            Context* context);
+
+  // Dispatches calls to the appropriate entry in `call_handlers_`.
+  auto onCall(llvm::StringRef name, llvm::json::Value params,
+              llvm::json::Value id) -> bool override;
+
+  // Dispatches notifications to the appropriate entry in
+  // `notification_handlers_`, except for `exit` which directly returns false.
+  auto onNotify(llvm::StringRef name, llvm::json::Value value) -> bool override;
+
+  // Handles replies.
+  // TODO: Implement when needed.
+  auto onReply(llvm::json::Value /*id*/,
+               llvm::Expected<llvm::json::Value> /*result*/) -> bool override {
+    return true;
+  }
+
+ private:
+  // These are the signatures expected for handlers.
+  using CallHandler = std::function<void(
+      Context& context, llvm::json::Value raw_params,
+      llvm::function_ref<void(llvm::Expected<llvm::json::Value>)> on_done)>;
+  using NotificationHandler =
+      std::function<void(Context& context, llvm::json::Value raw_params)>;
+
+  template <typename ParamsT, typename ResultT>
+  auto AddCallHandler(
+      llvm::StringRef name,
+      void (*handler)(Context&, const ParamsT&,
+                      llvm::function_ref<void(llvm::Expected<ResultT>)>))
+      -> void;
+  template <typename ParamsT>
+  auto AddNotificationHandler(llvm::StringRef name,
+                              void (*handler)(Context&, const ParamsT&))
+      -> void;
+
+  // The connection to the client.
+  clang::clangd::Transport* transport_;
+  // The context for handlers.
+  Context* context_;
+
+  // Handlers for LSP calls.
+  Map<std::string, CallHandler> call_handlers_;
+
+  // Handlers for LSP notifications.
+  Map<std::string, NotificationHandler> notification_handlers_;
+};
+
+}  // namespace Carbon::LanguageServer
+
+#endif  // CARBON_TOOLCHAIN_LANGUAGE_SERVER_INCOMING_MESSAGES_H_

+ 24 - 3
toolchain/language_server/language_server.cpp

@@ -4,14 +4,35 @@
 
 #include "toolchain/language_server/language_server.h"
 
-#include "toolchain/language_server/server.h"
+#include "clang-tools-extra/clangd/LSPBinder.h"
+#include "clang-tools-extra/clangd/Transport.h"
+#include "toolchain/language_server/context.h"
+#include "toolchain/language_server/incoming_messages.h"
+#include "toolchain/language_server/outgoing_messages.h"
 
 namespace Carbon::LanguageServer {
 
 auto Run(std::FILE* input_stream, llvm::raw_ostream& output_stream)
     -> ErrorOr<Success> {
-  Server server(input_stream, output_stream);
-  return server.Run();
+  // Set up the connection.
+  std::unique_ptr<clang::clangd::Transport> transport(
+      clang::clangd::newJSONTransport(input_stream, output_stream,
+                                      /*InMirror=*/nullptr,
+                                      /*Pretty=*/true));
+  Context context;
+  IncomingMessages incoming(transport.get(), &context);
+  OutgoingMessages outgoing(transport.get());
+
+  // Run the server loop.
+  llvm::Error err = transport->loop(incoming);
+  if (err) {
+    std::string str;
+    llvm::raw_string_ostream out(str);
+    out << err;
+    return Error(str);
+  } else {
+    return Success();
+  }
 }
 
 }  // namespace Carbon::LanguageServer

+ 38 - 0
toolchain/language_server/outgoing_messages.h

@@ -0,0 +1,38 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TOOLCHAIN_LANGUAGE_SERVER_OUTGOING_MESSAGES_H_
+#define CARBON_TOOLCHAIN_LANGUAGE_SERVER_OUTGOING_MESSAGES_H_
+
+#include "clang-tools-extra/clangd/LSPBinder.h"
+#include "clang-tools-extra/clangd/Transport.h"
+
+namespace Carbon::LanguageServer {
+
+// Handles sending LSP messages to the client (IDE extension).
+class OutgoingMessages : public clang::clangd::LSPBinder::RawOutgoing {
+ public:
+  explicit OutgoingMessages(clang::clangd::Transport* transport)
+      : transport_(transport) {}
+
+  // Calls a method on the client.
+  // TODO: Implement when needed.
+  auto callMethod(llvm::StringRef /*method*/, llvm::json::Value /*params*/,
+                  clang::clangd::Callback<llvm::json::Value> /*reply*/)
+      -> void override {}
+
+  // Sets a notification to the client.
+  auto notify(llvm::StringRef method, llvm::json::Value params)
+      -> void override {
+    transport_->notify(method, params);
+  }
+
+ private:
+  // The connection to the client.
+  clang::clangd::Transport* transport_;
+};
+
+}  // namespace Carbon::LanguageServer
+
+#endif  // CARBON_TOOLCHAIN_LANGUAGE_SERVER_OUTGOING_MESSAGES_H_

+ 0 - 174
toolchain/language_server/server.cpp

@@ -1,174 +0,0 @@
-// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-#include "toolchain/language_server/server.h"
-
-#include "toolchain/base/shared_value_stores.h"
-#include "toolchain/diagnostics/null_diagnostics.h"
-#include "toolchain/lex/lex.h"
-#include "toolchain/parse/node_kind.h"
-#include "toolchain/parse/parse.h"
-#include "toolchain/parse/tree_and_subtrees.h"
-#include "toolchain/source/source_buffer.h"
-
-namespace Carbon::LanguageServer {
-
-Server::Server(std::FILE* input_stream, llvm::raw_ostream& output_stream)
-    : transport_(clang::clangd::newJSONTransport(input_stream, output_stream,
-                                                 /*InMirror=*/nullptr,
-                                                 /*Pretty=*/true)),
-      binder_(handlers_, *this) {
-  binder_.notification("textDocument/didOpen", this,
-                       &Server::OnDidOpenTextDocument);
-  binder_.notification("textDocument/didChange", this,
-                       &Server::OnDidChangeTextDocument);
-  binder_.method("initialize", this, &Server::OnInitialize);
-  binder_.method("textDocument/documentSymbol", this,
-                 &Server::OnDocumentSymbol);
-}
-
-auto Server::Run() -> ErrorOr<Success> {
-  llvm::Error err = transport_->loop(*this);
-  if (err) {
-    std::string str;
-    llvm::raw_string_ostream out(str);
-    out << err;
-    return Error(str);
-  } else {
-    return Success();
-  }
-}
-
-void Server::OnDidOpenTextDocument(
-    clang::clangd::DidOpenTextDocumentParams const& params) {
-  files_.emplace(params.textDocument.uri.file(), params.textDocument.text);
-}
-
-void Server::OnDidChangeTextDocument(
-    clang::clangd::DidChangeTextDocumentParams const& params) {
-  // Full text is sent if full sync is specified in capabilities.
-  CARBON_CHECK(params.contentChanges.size() == 1);
-  std::string file = params.textDocument.uri.file().str();
-  files_[file] = params.contentChanges[0].text;
-}
-
-void Server::OnInitialize(
-    clang::clangd::NoParams const& /*client_capabilities*/,
-    clang::clangd::Callback<llvm::json::Object> cb) {
-  llvm::json::Object capabilities{{"documentSymbolProvider", true},
-                                  {"textDocumentSync", /*Full=*/1}};
-
-  llvm::json::Object reply{{"capabilities", std::move(capabilities)}};
-  cb(reply);
-}
-
-auto Server::onNotify(llvm::StringRef method, llvm::json::Value value) -> bool {
-  if (method == "exit") {
-    return false;
-  }
-  if (auto handler = handlers_.NotificationHandlers.find(method);
-      handler != handlers_.NotificationHandlers.end()) {
-    handler->second(std::move(value));
-  } else {
-    clang::clangd::log("unhandled notification {0}", method);
-  }
-
-  return true;
-}
-
-auto Server::onCall(llvm::StringRef method, llvm::json::Value params,
-                    llvm::json::Value id) -> bool {
-  if (auto handler = handlers_.MethodHandlers.find(method);
-      handler != handlers_.MethodHandlers.end()) {
-    // TODO: Improve this if add threads.
-    handler->second(std::move(params),
-                    [&](llvm::Expected<llvm::json::Value> reply) {
-                      transport_->reply(id, std::move(reply));
-                    });
-  } else {
-    transport_->reply(
-        id, llvm::make_error<clang::clangd::LSPError>(
-                "method not found", clang::clangd::ErrorCode::MethodNotFound));
-  }
-
-  return true;
-}
-
-auto Server::onReply(llvm::json::Value /*id*/,
-                     llvm::Expected<llvm::json::Value> /*result*/) -> bool {
-  return true;
-}
-
-// Returns the text of first child of kind Parse::NodeKind::IdentifierName.
-static auto GetIdentifierName(const SharedValueStores& value_stores,
-                              const Lex::TokenizedBuffer& tokens,
-                              const Parse::TreeAndSubtrees& p,
-                              Parse::NodeId node)
-    -> std::optional<llvm::StringRef> {
-  for (auto ch : p.children(node)) {
-    if (p.tree().node_kind(ch) == Parse::NodeKind::IdentifierName) {
-      auto token = p.tree().node_token(ch);
-      if (tokens.GetKind(token) == Lex::TokenKind::Identifier) {
-        return value_stores.identifiers().Get(tokens.GetIdentifier(token));
-      }
-    }
-  }
-  return std::nullopt;
-}
-
-void Server::OnDocumentSymbol(
-    clang::clangd::DocumentSymbolParams const& params,
-    clang::clangd::Callback<std::vector<clang::clangd::DocumentSymbol>> cb) {
-  SharedValueStores value_stores;
-  llvm::vfs::InMemoryFileSystem vfs;
-  auto file = params.textDocument.uri.file().str();
-  vfs.addFile(file, /*mtime=*/0,
-              llvm::MemoryBuffer::getMemBufferCopy(files_.at(file)));
-
-  auto buf = SourceBuffer::MakeFromFile(vfs, file, NullDiagnosticConsumer());
-  auto lexed = Lex::Lex(value_stores, *buf, NullDiagnosticConsumer());
-  auto parsed = Parse::Parse(lexed, NullDiagnosticConsumer(), nullptr);
-  Parse::TreeAndSubtrees tree_and_subtrees(lexed, parsed);
-  std::vector<clang::clangd::DocumentSymbol> result;
-  for (const auto& node : parsed.postorder()) {
-    clang::clangd::SymbolKind symbol_kind;
-    switch (parsed.node_kind(node)) {
-      case Parse::NodeKind::FunctionDecl:
-      case Parse::NodeKind::FunctionDefinitionStart:
-        symbol_kind = clang::clangd::SymbolKind::Function;
-        break;
-      case Parse::NodeKind::Namespace:
-        symbol_kind = clang::clangd::SymbolKind::Namespace;
-        break;
-      case Parse::NodeKind::InterfaceDefinitionStart:
-      case Parse::NodeKind::NamedConstraintDefinitionStart:
-        symbol_kind = clang::clangd::SymbolKind::Interface;
-        break;
-      case Parse::NodeKind::ClassDefinitionStart:
-        symbol_kind = clang::clangd::SymbolKind::Class;
-        break;
-      default:
-        continue;
-    }
-
-    if (auto name =
-            GetIdentifierName(value_stores, lexed, tree_and_subtrees, node)) {
-      auto tok = parsed.node_token(node);
-      clang::clangd::Position pos{lexed.GetLineNumber(tok) - 1,
-                                  lexed.GetColumnNumber(tok) - 1};
-
-      clang::clangd::DocumentSymbol symbol{
-          .name = std::string(*name),
-          .kind = symbol_kind,
-          .range = {.start = pos, .end = pos},
-          .selectionRange = {.start = pos, .end = pos},
-      };
-
-      result.push_back(symbol);
-    }
-  }
-  cb(result);
-}
-
-}  // namespace Carbon::LanguageServer

+ 0 - 90
toolchain/language_server/server.h

@@ -1,90 +0,0 @@
-// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
-// Exceptions. See /LICENSE for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-#ifndef CARBON_TOOLCHAIN_LANGUAGE_SERVER_SERVER_H_
-#define CARBON_TOOLCHAIN_LANGUAGE_SERVER_SERVER_H_
-
-#include <memory>
-#include <unordered_map>
-#include <vector>
-
-#include "clang-tools-extra/clangd/LSPBinder.h"
-#include "clang-tools-extra/clangd/Protocol.h"
-#include "clang-tools-extra/clangd/Transport.h"
-#include "common/error.h"
-
-namespace Carbon::LanguageServer {
-
-// Provides a LSP implementation for Carbon.
-class Server : public clang::clangd::Transport::MessageHandler,
-               public clang::clangd::LSPBinder::RawOutgoing {
- public:
-  // Prepares the server to run on the provided streams.
-  explicit Server(std::FILE* input_stream, llvm::raw_ostream& output_stream);
-
-  // Runs the server in a loop, returning the result. Currently this always
-  // returns an error when the input stream is closed.
-  auto Run() -> ErrorOr<Success>;
-
-  // Transport::MessageHandler
-  // Handlers returns true to keep processing messages, or false to shut down.
-
-  // Handler called on notification by client.
-  auto onNotify(llvm::StringRef method, llvm::json::Value value)
-      -> bool override;
-  // Handler called on method call by client.
-  auto onCall(llvm::StringRef method, llvm::json::Value params,
-              llvm::json::Value id) -> bool override;
-  // Handler called on response of Transport::call.
-  auto onReply(llvm::json::Value id, llvm::Expected<llvm::json::Value> result)
-      -> bool override;
-
-  // LSPBinder::RawOutgoing
-
-  // Send method call to client
-  auto callMethod(llvm::StringRef method, llvm::json::Value params,
-                  clang::clangd::Callback<llvm::json::Value> reply)
-      -> void override {
-    // TODO: Implement when needed.
-  }
-
-  // Send notification to client
-  auto notify(llvm::StringRef method, llvm::json::Value params)
-      -> void override {
-    transport_->notify(method, params);
-  }
-
- private:
-  // Typed handlers for notifications and method calls by client.
-
-  // Client opened a document.
-  auto OnDidOpenTextDocument(
-      clang::clangd::DidOpenTextDocumentParams const& params) -> void;
-
-  // Client updated content of a document.
-  auto OnDidChangeTextDocument(
-      clang::clangd::DidChangeTextDocumentParams const& params) -> void;
-
-  // Capabilities negotiation
-  auto OnInitialize(clang::clangd::NoParams const& client_capabilities,
-                    clang::clangd::Callback<llvm::json::Object> cb) -> void;
-
-  // Code outline
-  auto OnDocumentSymbol(
-      clang::clangd::DocumentSymbolParams const& params,
-      clang::clangd::Callback<std::vector<clang::clangd::DocumentSymbol>> cb)
-      -> void;
-
-  const std::unique_ptr<clang::clangd::Transport> transport_;
-  // content of files managed by the language client.
-  std::unordered_map<std::string, std::string> files_;
-  // handlers for client methods and notifications
-  clang::clangd::LSPBinder::RawHandlers handlers_;
-  // Binds client calls to member methods.
-  clang::clangd::LSPBinder binder_;
-};
-
-}  // namespace Carbon::LanguageServer
-
-#endif  // CARBON_TOOLCHAIN_LANGUAGE_SERVER_SERVER_H_