diff --git a/packages/cxx-gen-lsp/src/gen_fwd_h.ts b/packages/cxx-gen-lsp/src/gen_fwd_h.ts index e9fc295f..5ed44616 100644 --- a/packages/cxx-gen-lsp/src/gen_fwd_h.ts +++ b/packages/cxx-gen-lsp/src/gen_fwd_h.ts @@ -45,6 +45,10 @@ class LSPObject { class LSPRequest : public LSPObject { public: using LSPObject::LSPObject; + + [[nodiscard]] auto method() const -> std::string { + return repr_->at("method").get(); + } }; class LSPResponse : public LSPObject { diff --git a/packages/cxx-gen-lsp/src/gen_requests_h.ts b/packages/cxx-gen-lsp/src/gen_requests_h.ts index 82d4f906..c24d1887 100644 --- a/packages/cxx-gen-lsp/src/gen_requests_h.ts +++ b/packages/cxx-gen-lsp/src/gen_requests_h.ts @@ -107,31 +107,20 @@ export function gen_requests_h({ model, outputDirectory }: { model: MetaModel; o emit(); emit(`template `); - emit(`auto visitRequest(Visitor&& visitor, const LSPRequest& request, const std::string_view& method) -> void {`); + emit(`auto visit(Visitor&& visitor, const LSPRequest& request) -> void {`); emit(`#define PROCESS_REQUEST_TYPE(NAME, METHOD) \\`); - emit(` if (method == METHOD) \\`); + emit(` if (request.method() == METHOD) \\`); emit(` return visitor(static_cast(request));`); emit(); - emit(`FOR_EACH_LSP_REQUEST_TYPE(PROCESS_REQUEST_TYPE)`); - emit(); - emit(`#undef PROCESS_REQUEST_TYPE`); - emit(); - emit(` lsp_runtime_error("unknown request type");`); - emit(`}`); - emit(); - emit(`template `); - emit( - `auto visitNotification(Visitor&& visitor, const LSPRequest& notification, const std::string_view& method) -> void {`, - ); emit(`#define PROCESS_NOTIFICATION_TYPE(NAME, METHOD) \\`); - emit(` if (method == METHOD) \\`); - emit(` return visitor(static_cast(notification));`); + emit(` if (request.method() == METHOD) \\`); + emit(` return visitor(static_cast(request));`); emit(); + emit(`FOR_EACH_LSP_REQUEST_TYPE(PROCESS_REQUEST_TYPE)`); emit(`FOR_EACH_LSP_NOTIFICATION_TYPE(PROCESS_NOTIFICATION_TYPE)`); emit(); + emit(`#undef PROCESS_REQUEST_TYPE`); emit(`#undef PROCESS_NOTIFICATION_TYPE`); - emit(); - emit(` lsp_runtime_error("unknown notification type");`); emit(`}`); emit(); diff --git a/src/frontend/cxx/lsp_server.cc b/src/frontend/cxx/lsp_server.cc index 0793bb09..a095f27d 100644 --- a/src/frontend/cxx/lsp_server.cc +++ b/src/frontend/cxx/lsp_server.cc @@ -106,13 +106,6 @@ auto readHeaders(std::istream& input) -> Headers { return headers; } -void sendToClient(const json& message, std::ostream& output = std::cout) { - const auto text = message.dump(); - output << "Content-Length: " << text.size() << "\r\n\r\n"; - output << text; - output.flush(); -}; - struct Input { struct Diagnostics final : cxx::DiagnosticsClient { json messages = json::array(); @@ -279,31 +272,131 @@ struct Input { } }; -auto nextRequest() -> std::optional { - auto& input = std::cin; +class Server { + std::istream& input; - const auto headers = readHeaders(input); + public: + Server() : input(std::cin) {} - // Get Content-Length - const auto it = headers.find("Content-Length"); + auto start() -> int { + while (input.good()) { + auto req = nextRequest(); - if (it == headers.end()) { - return std::nullopt; - }; + if (!req.has_value()) { + continue; + } - const auto contentLength = std::stoi(it->value); + visit(*this, LSPRequest(req.value())); + } - // Read content - std::string content(contentLength, '\0'); - input.read(content.data(), content.size()); + return 0; + } - // Parse JSON - auto request = json::parse(content); - return request; -} + auto nextRequest() -> std::optional { + const auto headers = readHeaders(input); + + // Get Content-Length + const auto it = headers.find("Content-Length"); + + if (it == headers.end()) { + return std::nullopt; + }; + + const auto contentLength = std::stoi(it->value); + + // Read content + std::string content(contentLength, '\0'); + input.read(content.data(), content.size()); + + // Parse JSON + auto request = json::parse(content); + return request; + } + + void sendToClient(const json& message, std::ostream& output = std::cout) { + const auto text = message.dump(); + output << "Content-Length: " << text.size() << "\r\n\r\n"; + output << text; + output.flush(); + } + + void sendToClient( + const LSPObject& result, + std::optional> id = std::nullopt, + std::ostream& output = std::cout) { + auto response = json::object(); + response["jsonrpc"] = "2.0"; + + if (id.has_value()) { + if (std::holds_alternative(id.value())) { + response["id"] = std::get(id.value()); + } else { + response["id"] = std::get(id.value()); + } + } + + response["result"] = result; + + sendToClient(response); + } + + // + // notifications + // + void operator()(const InitializedNotification& notification) { + std::cerr << std::format("Did receive InitializedNotification\n"); + } + + void operator()(const ExitNotification& notification) { + std::cerr << std::format("Did receive ExitNotification\n"); + } + + void operator()(const DidOpenTextDocumentNotification& notification) { + std::cerr << std::format("Did receive DidOpenTextDocumentNotification\n"); + } + + void operator()(const DidCloseTextDocumentNotification& notification) { + std::cerr << std::format("Did receive DidCloseTextDocumentNotification\n"); + } + + void operator()(const DidChangeTextDocumentNotification& notification) { + std::cerr << std::format("Did receive DidChangeTextDocumentNotification\n"); + } + + // + // life cycle requests + // + + void operator()(const InitializeRequest& request) { + std::cerr << std::format("Did receive InitializeRequest\n"); + auto storage = json::object(); + InitializeResult result(storage); + result.serverInfo().name("cxx-lsp").version("0.0.1"); + result.capabilities().textDocumentSync(TextDocumentSyncKind::kFull); + + sendToClient(result, request.id()); + } + + void operator()(const ShutdownRequest& request) { + std::cerr << std::format("Did receive ShutdownRequest\n"); + + json storage; + LSPObject result(storage); + + sendToClient(result, request.id()); + } + + // + // Other requests + // + void operator()(const LSPRequest& request) { + std::cerr << "Request: " << request.method() << "\n"; + } +}; int startServer(const CLI& cli) { - cxx_runtime_error("not implemented"); + Server server; + server.start(); return 0; } diff --git a/src/lsp/cxx/lsp/fwd.h b/src/lsp/cxx/lsp/fwd.h index 80a46492..1d7cc756 100644 --- a/src/lsp/cxx/lsp/fwd.h +++ b/src/lsp/cxx/lsp/fwd.h @@ -644,6 +644,10 @@ class LSPObject { class LSPRequest : public LSPObject { public: using LSPObject::LSPObject; + + [[nodiscard]] auto method() const -> std::string { + return repr_->at("method").get(); + } }; class LSPResponse : public LSPObject { diff --git a/src/lsp/cxx/lsp/requests.h b/src/lsp/cxx/lsp/requests.h index d52acb4f..04531073 100644 --- a/src/lsp/cxx/lsp/requests.h +++ b/src/lsp/cxx/lsp/requests.h @@ -2544,31 +2544,20 @@ class ProgressNotification final : public LSPRequest { }; template -auto visitRequest(Visitor&& visitor, const LSPRequest& request, - const std::string_view& method) -> void { +auto visit(Visitor&& visitor, const LSPRequest& request) -> void { #define PROCESS_REQUEST_TYPE(NAME, METHOD) \ - if (method == METHOD) \ + if (request.method() == METHOD) \ return visitor(static_cast(request)); - FOR_EACH_LSP_REQUEST_TYPE(PROCESS_REQUEST_TYPE) - -#undef PROCESS_REQUEST_TYPE - - lsp_runtime_error("unknown request type"); -} - -template -auto visitNotification(Visitor&& visitor, const LSPRequest& notification, - const std::string_view& method) -> void { #define PROCESS_NOTIFICATION_TYPE(NAME, METHOD) \ - if (method == METHOD) \ - return visitor(static_cast(notification)); + if (request.method() == METHOD) \ + return visitor(static_cast(request)); + FOR_EACH_LSP_REQUEST_TYPE(PROCESS_REQUEST_TYPE) FOR_EACH_LSP_NOTIFICATION_TYPE(PROCESS_NOTIFICATION_TYPE) +#undef PROCESS_REQUEST_TYPE #undef PROCESS_NOTIFICATION_TYPE - - lsp_runtime_error("unknown notification type"); } } // namespace cxx::lsp