diff --git a/.github/workflows/auto_label_prs.yaml b/.github/workflows/auto_label_prs.yaml index 4733b73ea1a2f..f98a3614854d7 100644 --- a/.github/workflows/auto_label_prs.yaml +++ b/.github/workflows/auto_label_prs.yaml @@ -63,7 +63,6 @@ jobs: toolchain: - 'common/**' - 'core/**' - - 'language_server/**' - 'testing/**' - 'toolchain/**' diff --git a/bazel/check_deps/BUILD b/bazel/check_deps/BUILD index 9f3dd566f88f8..26ec4b565ab97 100644 --- a/bazel/check_deps/BUILD +++ b/bazel/check_deps/BUILD @@ -15,7 +15,6 @@ filegroup( data = [ "//explorer", "//installers/local:carbon", - "//language_server", "//migrate_cpp:rewriter", "//migrate_cpp/cpp_refactoring", "//toolchain/install:carbon-busybox", diff --git a/language_server/main.cpp b/language_server/main.cpp deleted file mode 100644 index 1dfdf6470a05b..0000000000000 --- a/language_server/main.cpp +++ /dev/null @@ -1,10 +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 "language_server/language_server.h" - -auto main(int /*argc*/, char** /*argv*/) -> int { - Carbon::LS::LanguageServer::Start(); - return 0; -} diff --git a/toolchain/driver/BUILD b/toolchain/driver/BUILD index 375a30a7cd2ed..31f93e720ead8 100644 --- a/toolchain/driver/BUILD +++ b/toolchain/driver/BUILD @@ -91,6 +91,8 @@ cc_library( "driver_env.h", "format_subcommand.cpp", "format_subcommand.h", + "language_server_subcommand.cpp", + "language_server_subcommand.h", "link_subcommand.cpp", "link_subcommand.h", ], @@ -116,6 +118,7 @@ cc_library( "//toolchain/diagnostics:sorting_diagnostic_consumer", "//toolchain/format", "//toolchain/install:install_paths", + "//toolchain/language_server", "//toolchain/lex", "//toolchain/lower", "//toolchain/parse", diff --git a/toolchain/driver/driver.cpp b/toolchain/driver/driver.cpp index b1066b5e00361..3a4515e3e4c41 100644 --- a/toolchain/driver/driver.cpp +++ b/toolchain/driver/driver.cpp @@ -13,6 +13,7 @@ #include "toolchain/driver/clang_subcommand.h" #include "toolchain/driver/compile_subcommand.h" #include "toolchain/driver/format_subcommand.h" +#include "toolchain/driver/language_server_subcommand.h" #include "toolchain/driver/link_subcommand.h" namespace Carbon { @@ -28,6 +29,7 @@ struct Options { ClangSubcommand clang; CompileSubcommand compile; FormatSubcommand format; + LanguageServerSubcommand language_server; LinkSubcommand link; // On success, this is set to the subcommand to run. @@ -79,6 +81,11 @@ auto Options::Build(CommandLine::CommandBuilder& b) -> void { sub_b.Do([&] { subcommand = &format; }); }); + b.AddSubcommand(LanguageServerSubcommand::Info, + [&](CommandLine::CommandBuilder& sub_b) { + sub_b.Do([&] { subcommand = &language_server; }); + }); + b.AddSubcommand(LinkOptions::Info, [&](CommandLine::CommandBuilder& sub_b) { link.BuildOptions(sub_b); sub_b.Do([&] { subcommand = &link; }); diff --git a/toolchain/driver/language_server_subcommand.cpp b/toolchain/driver/language_server_subcommand.cpp new file mode 100644 index 0000000000000..f01853452a8d1 --- /dev/null +++ b/toolchain/driver/language_server_subcommand.cpp @@ -0,0 +1,28 @@ +// 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/driver/language_server_subcommand.h" + +#include "toolchain/language_server/language_server.h" + +namespace Carbon { + +constexpr CommandLine::CommandInfo LanguageServerSubcommand::Info = { + .name = "language-server", + .help = R"""( +Runs the language server. +)""", +}; + +auto LanguageServerSubcommand::Run(DriverEnv& driver_env) -> DriverResult { + // TODO: Consider a way to override stdin, but it's a `FILE*` so less + // convenient to work with. + auto err = LanguageServer::Run(stdin, driver_env.output_stream); + if (!err.ok()) { + driver_env.error_stream << "error: " << err.error() << "\n"; + } + return {.success = err.ok()}; +} + +} // namespace Carbon diff --git a/toolchain/driver/language_server_subcommand.h b/toolchain/driver/language_server_subcommand.h new file mode 100644 index 0000000000000..5cb85c613e81a --- /dev/null +++ b/toolchain/driver/language_server_subcommand.h @@ -0,0 +1,27 @@ +// 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_DRIVER_LANGUAGE_SERVER_SUBCOMMAND_H_ +#define CARBON_TOOLCHAIN_DRIVER_LANGUAGE_SERVER_SUBCOMMAND_H_ + +#include "common/command_line.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "toolchain/driver/codegen_options.h" +#include "toolchain/driver/driver_env.h" +#include "toolchain/driver/driver_subcommand.h" + +namespace Carbon { + +// Implements the link subcommand of the driver. +class LanguageServerSubcommand : public DriverSubcommand { + public: + static const CommandLine::CommandInfo Info; + + auto Run(DriverEnv& driver_env) -> DriverResult override; +}; + +} // namespace Carbon + +#endif // CARBON_TOOLCHAIN_DRIVER_LANGUAGE_SERVER_SUBCOMMAND_H_ diff --git a/language_server/BUILD b/toolchain/language_server/BUILD similarity index 76% rename from language_server/BUILD rename to toolchain/language_server/BUILD index 68fd83595f5df..ecc6928cd95e3 100644 --- a/language_server/BUILD +++ b/toolchain/language_server/BUILD @@ -2,25 +2,23 @@ # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -load("@rules_cc//cc:defs.bzl", "cc_binary") +load("@rules_cc//cc:defs.bzl", "cc_library") -package(default_visibility = [ - "//bazel/check_deps:__pkg__", - "//installers:__subpackages__", - "//language_server:__subpackages__", -]) +package(default_visibility = ["//visibility:public"]) -cc_binary( +cc_library( name = "language_server", srcs = [ "language_server.cpp", - "language_server.h", - "main.cpp", + "server.cpp", + "server.h", ], + hdrs = ["language_server.h"], # Some parameters are unused in clangd headers. copts = ["-Wno-unused-parameter"], deps = [ "//common:error", + "//common:ostream", "//toolchain/base:value_store", "//toolchain/diagnostics:null_diagnostics", "//toolchain/lex", diff --git a/toolchain/language_server/language_server.cpp b/toolchain/language_server/language_server.cpp new file mode 100644 index 0000000000000..64a31424e9a3d --- /dev/null +++ b/toolchain/language_server/language_server.cpp @@ -0,0 +1,17 @@ +// 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/language_server.h" + +#include "toolchain/language_server/server.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(); +} + +} // namespace Carbon::LanguageServer diff --git a/toolchain/language_server/language_server.h b/toolchain/language_server/language_server.h new file mode 100644 index 0000000000000..64be63c9aa972 --- /dev/null +++ b/toolchain/language_server/language_server.h @@ -0,0 +1,19 @@ +// 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_LANGUAGE_SERVER_H_ +#define CARBON_TOOLCHAIN_LANGUAGE_SERVER_LANGUAGE_SERVER_H_ + +#include "common/error.h" +#include "common/ostream.h" + +namespace Carbon::LanguageServer { + +// Start the language server. +auto Run(std::FILE* input_stream, llvm::raw_ostream& output_stream) + -> ErrorOr; + +} // namespace Carbon::LanguageServer + +#endif // CARBON_TOOLCHAIN_LANGUAGE_SERVER_LANGUAGE_SERVER_H_ diff --git a/language_server/language_server.cpp b/toolchain/language_server/server.cpp similarity index 74% rename from language_server/language_server.cpp rename to toolchain/language_server/server.cpp index 92337259c6198..f6dd9ffb39cb3 100644 --- a/language_server/language_server.cpp +++ b/toolchain/language_server/server.cpp @@ -2,9 +2,8 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#include "language_server/language_server.h" +#include "toolchain/language_server/server.h" -#include "clang-tools-extra/clangd/Protocol.h" #include "toolchain/base/value_store.h" #include "toolchain/diagnostics/null_diagnostics.h" #include "toolchain/lex/lex.h" @@ -13,22 +12,48 @@ #include "toolchain/parse/tree_and_subtrees.h" #include "toolchain/source/source_buffer.h" -namespace Carbon::LS { +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.success()) { + return Success(); + } else { + std::string str; + llvm::raw_string_ostream out(str); + out << err; + return Error(str); + } +} -void LanguageServer::OnDidOpenTextDocument( +void Server::OnDidOpenTextDocument( clang::clangd::DidOpenTextDocumentParams const& params) { files_.emplace(params.textDocument.uri.file(), params.textDocument.text); } -void LanguageServer::OnDidChangeTextDocument( +void Server::OnDidChangeTextDocument( clang::clangd::DidChangeTextDocumentParams const& params) { - // full text is sent if full sync is specified in capabilities. - assert(params.contentChanges.size() == 1); + // 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 LanguageServer::OnInitialize( +void Server::OnInitialize( clang::clangd::NoParams const& /*client_capabilities*/, clang::clangd::Callback cb) { llvm::json::Object capabilities{{"documentSymbolProvider", true}, @@ -38,8 +63,7 @@ void LanguageServer::OnInitialize( cb(reply); } -auto LanguageServer::onNotify(llvm::StringRef method, llvm::json::Value value) - -> bool { +auto Server::onNotify(llvm::StringRef method, llvm::json::Value value) -> bool { if (method == "exit") { return false; } @@ -53,8 +77,8 @@ auto LanguageServer::onNotify(llvm::StringRef method, llvm::json::Value value) return true; } -auto LanguageServer::onCall(llvm::StringRef method, llvm::json::Value params, - llvm::json::Value id) -> bool { +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 @@ -71,9 +95,8 @@ auto LanguageServer::onCall(llvm::StringRef method, llvm::json::Value params, return true; } -auto LanguageServer::onReply(llvm::json::Value /*id*/, - llvm::Expected /*result*/) - -> bool { +auto Server::onReply(llvm::json::Value /*id*/, + llvm::Expected /*result*/) -> bool { return true; } @@ -94,7 +117,7 @@ static auto GetIdentifierName(const SharedValueStores& value_stores, return std::nullopt; } -void LanguageServer::OnDocumentSymbol( +void Server::OnDocumentSymbol( clang::clangd::DocumentSymbolParams const& params, clang::clangd::Callback> cb) { SharedValueStores value_stores; @@ -148,19 +171,4 @@ void LanguageServer::OnDocumentSymbol( cb(result); } -void LanguageServer::Start() { - auto transport = - clang::clangd::newJSONTransport(stdin, llvm::outs(), nullptr, true); - LanguageServer ls(std::move(transport)); - clang::clangd::LSPBinder binder(ls.handlers_, ls); - binder.notification("textDocument/didOpen", &ls, - &LanguageServer::OnDidOpenTextDocument); - binder.notification("textDocument/didChange", &ls, - &LanguageServer::OnDidChangeTextDocument); - binder.method("initialize", &ls, &LanguageServer::OnInitialize); - binder.method("textDocument/documentSymbol", &ls, - &LanguageServer::OnDocumentSymbol); - auto error = ls.transport_->loop(ls); - llvm::errs() << "Error: " << error << "\n"; -} -} // namespace Carbon::LS +} // namespace Carbon::LanguageServer diff --git a/language_server/language_server.h b/toolchain/language_server/server.h similarity index 58% rename from language_server/language_server.h rename to toolchain/language_server/server.h index cb0457c412b4b..1cd436dc4cb66 100644 --- a/language_server/language_server.h +++ b/toolchain/language_server/server.h @@ -2,25 +2,30 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#ifndef CARBON_LANGUAGE_SERVER_LANGUAGE_SERVER_H_ -#define CARBON_LANGUAGE_SERVER_LANGUAGE_SERVER_H_ +#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 "clang-tools-extra/clangd/support/Function.h" -#include "toolchain/lex/tokenized_buffer.h" -#include "toolchain/parse/tree.h" -#include "toolchain/source/source_buffer.h" - -namespace Carbon::LS { -class LanguageServer : public clang::clangd::Transport::MessageHandler, - public clang::clangd::LSPBinder::RawOutgoing { +#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: - // Start the language server. - static void Start(); + // 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. @@ -38,46 +43,48 @@ class LanguageServer : public clang::clangd::Transport::MessageHandler, // LSPBinder::RawOutgoing // Send method call to client - void callMethod(llvm::StringRef method, llvm::json::Value params, - clang::clangd::Callback reply) override { + auto callMethod(llvm::StringRef method, llvm::json::Value params, + clang::clangd::Callback reply) + -> void override { // TODO: implement when needed } // Send notification to client - void notify(llvm::StringRef method, llvm::json::Value params) override { + auto notify(llvm::StringRef method, llvm::json::Value params) + -> void override { transport_->notify(method, params); } private: - 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_; - - explicit LanguageServer(std::unique_ptr transport) - : transport_(std::move(transport)) {} - // Typed handlers for notifications and method calls by client. // Client opened a document. - void OnDidOpenTextDocument( - clang::clangd::DidOpenTextDocumentParams const& params); + auto OnDidOpenTextDocument( + clang::clangd::DidOpenTextDocumentParams const& params) -> void; // Client updated content of a document. - void OnDidChangeTextDocument( - clang::clangd::DidChangeTextDocumentParams const& params); + auto OnDidChangeTextDocument( + clang::clangd::DidChangeTextDocumentParams const& params) -> void; // Capabilities negotiation - void OnInitialize(clang::clangd::NoParams const& client_capabilities, - clang::clangd::Callback cb); + auto OnInitialize(clang::clangd::NoParams const& client_capabilities, + clang::clangd::Callback cb) -> void; // Code outline - void OnDocumentSymbol( + auto OnDocumentSymbol( clang::clangd::DocumentSymbolParams const& params, - clang::clangd::Callback> cb); + 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::LS +} // namespace Carbon::LanguageServer -#endif // CARBON_LANGUAGE_SERVER_LANGUAGE_SERVER_H_ +#endif // CARBON_TOOLCHAIN_LANGUAGE_SERVER_SERVER_H_ diff --git a/utils/README.md b/utils/README.md index 1c7477d2b05c3..f0af6450ae997 100644 --- a/utils/README.md +++ b/utils/README.md @@ -21,5 +21,5 @@ developers and developers writing Carbon code. Any editor that supports Language server protocol and/or tree-sitter is supported. The editor just needs to be configured manually. -`bazel build language_server` produces the language server binary. +`bazel build //toolchain` produces the language server binary. `utils/treesitter` contains the treesitter grammar. diff --git a/utils/nvim/README.md b/utils/nvim/README.md index d860bc134a73e..fca6e4918d84c 100644 --- a/utils/nvim/README.md +++ b/utils/nvim/README.md @@ -11,7 +11,7 @@ Treesitter based syntax highlighting and language server client for Neovim. This requires neovim >= 0.9 and [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) to be installed. -1. Run `bazel build language_server` in project root. +1. Run `bazel build //toolchain` in project root. 2. Run `utils/nvim/setup.sh`. 3. Start nvim in carbon-lang root folder and open a carbon file. 4. View document symbols. If you have telescope.nvim installed, you can use diff --git a/utils/nvim/carbon.lua b/utils/nvim/carbon.lua index 188c78df7ce8d..35269725b5e5e 100644 --- a/utils/nvim/carbon.lua +++ b/utils/nvim/carbon.lua @@ -19,7 +19,7 @@ local util = require 'lspconfig.util' if not configs.carbon then configs.carbon = { default_config = { - cmd = { "./bazel-bin/language_server/language_server" }, + cmd = { "./bazel-bin/toolchain/install/run_carbon language-server" }, filetypes = { "carbon" }, root_dir = util.find_git_ancestor, } diff --git a/utils/vscode/README.md b/utils/vscode/README.md index 4d5ce9ad74a18..95b7894c33221 100644 --- a/utils/vscode/README.md +++ b/utils/vscode/README.md @@ -25,7 +25,7 @@ code --install-extension out/carbon.vsix ## Development -1. `bazel build language_server` in project root. +1. `bazel build //toolchain` in project root. 2. Open utils/vscode folder in VS Code. 3. Launch the extension using Run command (F5). 4. In the opened window, open the carbon-lang repository as folder. diff --git a/utils/vscode/src/extension.js b/utils/vscode/src/extension.js index 0bc57c9b76429..958a03e4f21ad 100644 --- a/utils/vscode/src/extension.js +++ b/utils/vscode/src/extension.js @@ -7,10 +7,11 @@ const { LanguageClient } = require('vscode-languageclient/node'); function activate(context) { - const command = './bazel-bin/language_server/language_server'; + const command = './bazel-bin/toolchain/install/run_carbon'; + const args = ['language-server']; const serverOptions = { - run: { command }, - debug: { command }, + run: { command: command, args: args }, + debug: { command: command, args: args }, }; const clientOptions = {