From cfcb3f488ba05d59fc39b28479cf7a4ecb184054 Mon Sep 17 00:00:00 2001 From: ykiko Date: Fri, 13 Sep 2024 18:59:07 +0800 Subject: [PATCH] Support socket to transport. --- .vscode/launch.json | 6 ++ include/Server/Server.h | 3 +- include/Server/Transport .h | 28 +++++++- src/Server/Scheduler.cpp | 15 +++-- src/Server/Server.cpp | 85 ++++-------------------- src/Server/Transport.cpp | 128 ++++++++++++++++++++++++++++++++++++ 6 files changed, 184 insertions(+), 81 deletions(-) create mode 100644 src/Server/Transport.cpp diff --git a/.vscode/launch.json b/.vscode/launch.json index c98acd13..ff9c6735 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,12 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "clice_socket", + "program": "./build/bin/clice" + }, { "type": "lldb", "request": "attach", diff --git a/include/Server/Server.h b/include/Server/Server.h index 43c4d757..156b9bd0 100644 --- a/include/Server/Server.h +++ b/include/Server/Server.h @@ -3,17 +3,18 @@ #include #include #include +#include #include namespace clice { struct Server { using Handler = llvm::unique_function; - Option option; Scheduler scheduler; CompilationDatabase CDB; llvm::StringMap handlers; + std::unique_ptr transport; static Server instance; diff --git a/include/Server/Transport .h b/include/Server/Transport .h index 79e09427..af086da8 100644 --- a/include/Server/Transport .h +++ b/include/Server/Transport .h @@ -1,8 +1,34 @@ #pragma once +#include +#include + namespace clice { +class Transport { +public: + virtual void write(std::string_view message) = 0; +}; +class Pipe : public Transport { +public: + Pipe(uv_loop_t* loop, void (*callback)(std::string_view)); + void write(std::string_view message) override; -} \ No newline at end of file +private: + uv_pipe_t stdin_pipe; + uv_pipe_t stdout_pipe; +}; + +class Socket : public Transport { +public: + Socket(uv_loop_t* loop, void (*callback)(std::string_view), const char* host, unsigned int port); + + void write(std::string_view message) override; + +private: + uv_tcp_t server; +}; + +} // namespace clice diff --git a/src/Server/Scheduler.cpp b/src/Server/Scheduler.cpp index c2fabdf4..871098a4 100644 --- a/src/Server/Scheduler.cpp +++ b/src/Server/Scheduler.cpp @@ -7,18 +7,21 @@ namespace clice { void Scheduler::dispatch(json::Value id, std::string_view method, json::Value value) { + std::vector compileArgs = { + "clang++", + "-std=c++20", + "main.cpp", + "-resource-dir=/home/ykiko/C++/clice2/build/lib/clang/20", + }; if(method == "textDocument/didOpen") { auto params = json::deserialize(value); - std::vector compileArgs = { - "clang++", - "-std=c++20", - "main.cpp", - "-resource-dir=/home/ykiko/C++/clice2/build/lib/clang/20", - }; auto AST = ParsedAST::build("main.cpp", params.textDocument.text, compileArgs); + spdlog::info("read file: {}", params.textDocument.text); files[params.textDocument.uri].ast = std::move(AST); } else if(method == "textDocument/didChange") { auto params = json::deserialize(value); + auto AST = ParsedAST::build("main.cpp", params.contentChanges.back().text, compileArgs); + files[params.textDocument.uri].ast = std::move(AST); } else if(method == "textDocument/semanticTokens/full") { auto params = json::deserialize(value); auto AST = files[params.textDocument.uri].ast.get(); diff --git a/src/Server/Server.cpp b/src/Server/Server.cpp index 0c0ee5e0..1a28b173 100644 --- a/src/Server/Server.cpp +++ b/src/Server/Server.cpp @@ -6,69 +6,14 @@ #include #include #include +#include +#include namespace clice { Server Server::instance; static uv_loop_t* loop; -static uv_pipe_t stdin_pipe; -static uv_pipe_t stdout_pipe; - -class Buffer { - std::vector buffer; - std::size_t max = 0; - -public: - void write(std::string_view message) { buffer.insert(buffer.end(), message.begin(), message.end()); } - - std::string_view read() { - std::string_view view = std::string_view(buffer.data(), buffer.size()); - auto start = view.find("Content-Length: ") + 16; - auto end = view.find("\r\n\r\n"); - - if(start != std::string_view::npos && end != std::string_view::npos) { - std::size_t length = std::stoul(std::string(view.substr(start, end - start))); - if(view.size() >= length + end + 4) { - this->max = length + end + 4; - return view.substr(end + 4, length); - } - } - - return {}; - } - - void clear() { - if(max != 0) { - buffer.erase(buffer.begin(), buffer.begin() + max); - max = 0; - } - } -}; - -void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { - static llvm::SmallString<4096> buffer; - buffer.resize(suggested_size); - buf->base = buffer.data(); - buf->len = buffer.size(); -} - -Buffer buffer; - -void read_stdin(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { - if(nread > 0) { - buffer.write(std::string_view(buf->base, nread)); - if(auto message = buffer.read(); !message.empty()) { - Server::instance.handleMessage(message); - buffer.clear(); - } - } else if(nread < 0) { - if(nread != UV_EOF) { - spdlog::error("Read error: {}", uv_err_name(nread)); - } - uv_close((uv_handle_t*)stream, NULL); - } -} Server::Server() { handlers.try_emplace("initialize", [](json::Value id, json::Value value) { @@ -85,7 +30,7 @@ auto Server::initialize(protocol::InitializeParams params) -> protocol::Initiali } int Server::run(int argc, const char** argv) { - std::this_thread::sleep_for(std::chrono::seconds(5)); + std::this_thread::sleep_for(std::chrono::seconds(2)); option.argc = argc; option.argv = argv; @@ -104,18 +49,18 @@ int Server::run(int argc, const char** argv) { std::string logFileName = "clice_" + timeStream.str() + ".log"; path::append(temp, logFileName); - auto logger = spdlog::basic_logger_mt("clice", std::string(temp.str())); + auto logger = spdlog::stdout_color_mt("clice"); logger->flush_on(spdlog::level::trace); spdlog::set_default_logger(logger); loop = uv_default_loop(); - uv_pipe_init(loop, &stdin_pipe, 0); - uv_pipe_open(&stdin_pipe, 0); - - uv_pipe_init(loop, &stdout_pipe, 0); - uv_pipe_open(&stdout_pipe, 1); - - uv_read_start((uv_stream_t*)&stdin_pipe, alloc_buffer, read_stdin); + transport = std::make_unique( + loop, + [](std::string_view message) { + instance.handleMessage(message); + }, + "127.0.0.1", + 50505); uv_run(loop, UV_RUN_DEFAULT); @@ -164,13 +109,7 @@ void Server::response(json::Value id, json::Value result) { s = "Content-Length: " + std::to_string(s.size()) + "\r\n\r\n" + s; // FIXME: use more flexible way to do this. - static uv_buf_t buf; - buf = uv_buf_init(s.data(), s.size()); - static uv_write_t req; - auto state = uv_write(&req, (uv_stream_t*)&stdout_pipe, &buf, 1, NULL); - if(state < 0) { - spdlog::error("Error writing to stdout: {}", uv_strerror(state)); - } + transport->write(s); spdlog::info("Response: {}", s); } diff --git a/src/Server/Transport.cpp b/src/Server/Transport.cpp new file mode 100644 index 00000000..62b4fd91 --- /dev/null +++ b/src/Server/Transport.cpp @@ -0,0 +1,128 @@ +#include +#include +#include + +namespace clice { + +/// NOTE: Receiving and sending messages are only done in the main thread. +/// so it's safe to use the static variable as a buffer. + +namespace { + +class MessageBuffer { + std::vector buffer; + std::size_t max = 0; + +public: + void write(std::string_view message) { buffer.insert(buffer.end(), message.begin(), message.end()); } + + std::string_view read() { + std::string_view view = std::string_view(buffer.data(), buffer.size()); + auto start = view.find("Content-Length: ") + 16; + auto end = view.find("\r\n\r\n"); + + if(start != std::string_view::npos && end != std::string_view::npos) { + std::size_t length = std::stoul(std::string(view.substr(start, end - start))); + if(view.size() >= length + end + 4) { + this->max = length + end + 4; + return view.substr(end + 4, length); + } + } + + return {}; + } + + void clear() { + if(max != 0) { + buffer.erase(buffer.begin(), buffer.begin() + max); + max = 0; + } + } +}; + +void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { + static llvm::SmallVector buffer; + buffer.resize(suggested_size); + buf->base = buffer.data(); + buf->len = buffer.size(); +} + +// callback for reading data. +static void (*unique_callback)(std::string_view) = nullptr; + +void read_callback(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { + static MessageBuffer buffer; + if(nread > 0) { + buffer.write(std::string_view(buf->base, nread)); + if(auto message = buffer.read(); !message.empty()) { + unique_callback(message); + buffer.clear(); + } + } else if(nread < 0) { + // if(nread != UV_EOF) { + // spdlog::error("Read error: {}", uv_err_name(nread)); + // } + uv_close((uv_handle_t*)stream, NULL); + } +} + +} // namespace + +Pipe::Pipe(uv_loop_t* loop, void (*callback)(std::string_view)) { + uv_pipe_init(loop, &stdin_pipe, 0); + uv_pipe_init(loop, &stdout_pipe, 0); + + uv_pipe_open(&stdin_pipe, 0); + uv_pipe_open(&stdout_pipe, 1); + + unique_callback = callback; + uv_read_start(reinterpret_cast(&stdin_pipe), alloc_buffer, read_callback); +} + +void Pipe::write(std::string_view message) { + static uv_buf_t buf; + static uv_write_t req; + buf = uv_buf_init(const_cast(message.data()), message.size()); + + auto state = uv_write(&req, reinterpret_cast(&stdout_pipe), &buf, 1, NULL); + if(state < 0) { + spdlog::error("Error writing to stdout: {}", uv_strerror(state)); + } +} + +static uv_tcp_t unique_client; + +Socket::Socket(uv_loop_t* loop, void (*callback)(std::string_view), const char* host, unsigned int port) { + uv_tcp_init(loop, &server); + sockaddr_in addr; + uv_ip4_addr(host, port, &addr); + uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0); + unique_callback = callback; + uv_listen(reinterpret_cast(&server), 1, [](uv_stream_t* server, int status) { + if(status < 0) { + spdlog::error("Listen error: {}", uv_strerror(status)); + return; + } + + spdlog::info("Server listening on port {}", 50505); + uv_tcp_init(uv_default_loop(), (uv_tcp_t*)&unique_client); + if(uv_accept(server, (uv_stream_t*)&unique_client) == 0) { + uv_read_start((uv_stream_t*)&unique_client, alloc_buffer, read_callback); + } else { + uv_close((uv_handle_t*)&unique_client, NULL); + } + }); +} + +void Socket::write(std::string_view message) { + static uv_buf_t buf; + static uv_write_t req; + buf = uv_buf_init(const_cast(message.data()), message.size()); + + auto state = uv_write(&req, (uv_stream_t*)&unique_client, &buf, 1, NULL); + if(state < 0) { + spdlog::error("Error writing to socket: {}", uv_strerror(state)); + } +} + +} // namespace clice