diff --git a/toolchain/language_server/BUILD b/toolchain/language_server/BUILD index f9acb5376361b..e149c61e0c95c 100644 --- a/toolchain/language_server/BUILD +++ b/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", ], ) diff --git a/toolchain/language_server/context.h b/toolchain/language_server/context.h new file mode 100644 index 0000000000000..ba240f78f874f --- /dev/null +++ b/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& { return files_; } + + private: + // Content of files managed by the language client. + Map files_; +}; + +} // namespace Carbon::LanguageServer + +#endif // CARBON_TOOLCHAIN_LANGUAGE_SERVER_CONTEXT_H_ diff --git a/toolchain/language_server/handle.h b/toolchain/language_server/handle.h new file mode 100644 index 0000000000000..80f3eca5e6309 --- /dev/null +++ b/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>)> + on_done) -> void; + +// Tells the client what features are supported. +auto HandleInitialize( + Context& /*context*/, + const clang::clangd::NoParams& /*client_capabilities*/, + llvm::function_ref)> on_done) + -> void; + +} // namespace Carbon::LanguageServer + +#endif // CARBON_TOOLCHAIN_LANGUAGE_SERVER_HANDLE_H_ diff --git a/toolchain/language_server/handle_document_symbol.cpp b/toolchain/language_server/handle_document_symbol.cpp new file mode 100644 index 0000000000000..dc86cd8389817 --- /dev/null +++ b/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 { + 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>)> + 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 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 diff --git a/toolchain/language_server/handle_initialize.cpp b/toolchain/language_server/handle_initialize.cpp new file mode 100644 index 0000000000000..77884820853bf --- /dev/null +++ b/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)> 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 diff --git a/toolchain/language_server/handle_text_document.cpp b/toolchain/language_server/handle_text_document.cpp new file mode 100644 index 0000000000000..9faa92af73652 --- /dev/null +++ b/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 diff --git a/toolchain/language_server/incoming_messages.cpp b/toolchain/language_server/incoming_messages.cpp new file mode 100644 index 0000000000000..f628a43c99578 --- /dev/null +++ b/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 +inline auto Parse(llvm::StringRef name, const llvm::json::Value& raw_params) + -> llvm::Expected { + ParamsT params; + llvm::json::Path::Root root; + if (!clang::clangd::fromJSON(raw_params, params, root)) { + return llvm::make_error( + 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 +auto IncomingMessages::AddCallHandler( + llvm::StringRef name, + void (*handler)(Context&, const ParamsT&, + llvm::function_ref)>)) + -> void { + CallHandler parsing_handler = + [name, handler]( + Context& context, llvm::json::Value raw_params, + llvm::function_ref)> on_done) + -> void { + auto params = Parse(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 +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(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 reply) { + transport_->reply(id, std::move(reply)); + }); + } else { + transport_->reply(id, llvm::make_error( + 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 diff --git a/toolchain/language_server/incoming_messages.h b/toolchain/language_server/incoming_messages.h new file mode 100644 index 0000000000000..fcf90a71109ab --- /dev/null +++ b/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 /*result*/) -> bool override { + return true; + } + + private: + // These are the signatures expected for handlers. + using CallHandler = std::function)> on_done)>; + using NotificationHandler = + std::function; + + template + auto AddCallHandler( + llvm::StringRef name, + void (*handler)(Context&, const ParamsT&, + llvm::function_ref)>)) + -> void; + template + 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 call_handlers_; + + // Handlers for LSP notifications. + Map notification_handlers_; +}; + +} // namespace Carbon::LanguageServer + +#endif // CARBON_TOOLCHAIN_LANGUAGE_SERVER_INCOMING_MESSAGES_H_ diff --git a/toolchain/language_server/language_server.cpp b/toolchain/language_server/language_server.cpp index 64a31424e9a3d..285a30ee8c759 100644 --- a/toolchain/language_server/language_server.cpp +++ b/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 { - Server server(input_stream, output_stream); - return server.Run(); + // Set up the connection. + std::unique_ptr 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 diff --git a/toolchain/language_server/outgoing_messages.h b/toolchain/language_server/outgoing_messages.h new file mode 100644 index 0000000000000..4111fe2f35b92 --- /dev/null +++ b/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 /*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_ diff --git a/toolchain/language_server/server.cpp b/toolchain/language_server/server.cpp deleted file mode 100644 index b2553992c2266..0000000000000 --- a/toolchain/language_server/server.cpp +++ /dev/null @@ -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 { - 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 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 reply) { - transport_->reply(id, std::move(reply)); - }); - } else { - transport_->reply( - id, llvm::make_error( - "method not found", clang::clangd::ErrorCode::MethodNotFound)); - } - - return true; -} - -auto Server::onReply(llvm::json::Value /*id*/, - llvm::Expected /*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 { - 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> 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 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 diff --git a/toolchain/language_server/server.h b/toolchain/language_server/server.h deleted file mode 100644 index 08afe932e65cc..0000000000000 --- a/toolchain/language_server/server.h +++ /dev/null @@ -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 -#include -#include - -#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; - - // 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 result) - -> bool override; - - // LSPBinder::RawOutgoing - - // Send method call to client - auto callMethod(llvm::StringRef method, llvm::json::Value params, - clang::clangd::Callback 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 cb) -> void; - - // Code outline - auto OnDocumentSymbol( - clang::clangd::DocumentSymbolParams const& params, - clang::clangd::Callback> cb) - -> void; - - const std::unique_ptr transport_; - // content of files managed by the language client. - std::unordered_map 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_