From 1e64c6eb78afb6be6cb155a3360bba11d2cf7fdc Mon Sep 17 00:00:00 2001 From: ykiko Date: Sat, 6 Jul 2024 23:30:58 +0800 Subject: [PATCH] some update. --- .gitignore | 1 + .vscode/launch.json | 2 +- include/LSP/MessageBuffer.h | 37 ++++++++++++++++ include/LSP/Protocol.h | 2 +- include/LSP/Server.h | 1 + include/Support/Filesystem.h | 7 +++ include/Support/Reflection.h | 27 ++++-------- include/Support/Serialize.h | 60 +++++++++++++------------- src/LSP/Server.cpp | 70 +++++++++++++++++++++--------- src/main.cpp | 84 +++--------------------------------- 10 files changed, 142 insertions(+), 149 deletions(-) create mode 100644 include/LSP/MessageBuffer.h create mode 100644 include/Support/Filesystem.h diff --git a/.gitignore b/.gitignore index 04df90d1..8aa919a9 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ .cache/ build/ external/ +logs/ # vscode plugin vscode-clice/dist/ diff --git a/.vscode/launch.json b/.vscode/launch.json index 9ce69e7a..ad1279b8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "type": "lldb", "request": "launch", "name": "Debug", - "program": "${workspaceFolder}/build/libtooling", + "program": "${workspaceFolder}/build/clice", "args": [], "cwd": "${workspaceFolder}" } diff --git a/include/LSP/MessageBuffer.h b/include/LSP/MessageBuffer.h new file mode 100644 index 00000000..72478e37 --- /dev/null +++ b/include/LSP/MessageBuffer.h @@ -0,0 +1,37 @@ +#include +#include + +namespace clice { + +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; + } + } +}; + +} // namespace clice diff --git a/include/LSP/Protocol.h b/include/LSP/Protocol.h index 44ae001e..70743a28 100644 --- a/include/LSP/Protocol.h +++ b/include/LSP/Protocol.h @@ -481,7 +481,7 @@ struct ServerCapabilities { /// If omitted it defaults to 'utf-16'. /// /// possible values: ['utf-8', 'utf-16', 'utf-32'] in PositionEncodingKind - std::string_view positionEncoding = PositionEncodingKind::UTF8; + std::string_view positionEncoding = PositionEncodingKind::UTF16; /// Defines how text documents are synced. Is either a detailed structure /// defining each notification or for backwards compatibility the diff --git a/include/LSP/Server.h b/include/LSP/Server.h index bd01a09a..4d2579a4 100644 --- a/include/LSP/Server.h +++ b/include/LSP/Server.h @@ -10,6 +10,7 @@ extern class Server server; class Server { uv_loop_t* loop; uv_pipe_t stdin_pipe; + uv_pipe_t stdout_pipe; public: int start(); diff --git a/include/Support/Filesystem.h b/include/Support/Filesystem.h new file mode 100644 index 00000000..a9b7ce6d --- /dev/null +++ b/include/Support/Filesystem.h @@ -0,0 +1,7 @@ +#include + +namespace clice { + +namespace fs = std::filesystem; + +} diff --git a/include/Support/Reflection.h b/include/Support/Reflection.h index 186e9bc5..8fa4234c 100644 --- a/include/Support/Reflection.h +++ b/include/Support/Reflection.h @@ -123,40 +123,31 @@ constexpr auto collcet_members(Object&& object) { return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u}; } else if constexpr(count == 22) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v] = object; - return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, - &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v}; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v}; } else if constexpr(count == 23) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w] = object; - return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, - &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w}; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w}; } else if constexpr(count == 24) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x] = object; - return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, - &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x}; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x}; } else if constexpr(count == 25) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y] = object; - return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, - &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y}; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y}; } else if constexpr(count == 26) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z] = object; - return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, - &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z}; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z}; } else if constexpr(count == 27) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0] = object; - return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, - &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0}; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0}; } else if constexpr(count == 28) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0, _1] = object; - return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, - &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1}; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1}; } else if constexpr(count == 29) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0, _1, _2] = object; - return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, - &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1, &_2}; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1, &_2}; } else if constexpr(count == 30) { auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, _0, _1, _2, _3] = object; - return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, - &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1, &_2, &_3}; + return std::tuple{&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &x, &y, &z, &_0, &_1, &_2, &_3}; } else { static_assert(count <= 30, "Not supported member count"); } diff --git a/include/Support/Serialize.h b/include/Support/Serialize.h index bae3a3ee..b7a9e60a 100644 --- a/include/Support/Serialize.h +++ b/include/Support/Serialize.h @@ -1,8 +1,8 @@ -#include "Reflection.h" -#include -#include +#include -namespace clice { +#include + +namespace clice::impl { template constexpr inline bool is_optional_v = false; @@ -19,36 +19,36 @@ constexpr inline bool is_vector_like_v> = true; template constexpr inline bool is_vector_like_v> = true; +} // namespace clice::impl + +namespace clice { + +using nlohmann::json; + template -void print(Object&& object) { - using T = std::decay_t; - if constexpr(std::is_same_v) { - std::cout << (object ? "true" : "false") << std::endl; - } else if constexpr(std::is_integral_v || std::is_floating_point_v || - std::is_same_v || std::is_same_v) { - std::cout << object << std::endl; - } else if constexpr(std::is_enum_v) { - std::cout << static_cast>(object) << std::endl; - } else if constexpr(is_optional_v) { - if(object.has_value()) { - print(object.value()); - } else { - std::cout << "null" << std::endl; - } - } else if constexpr(is_vector_like_v) { - std::cout << "[" << std::endl; - for(auto&& value: object) { - print(value); - } - std::cout << "]" << std::endl; +auto serialize(const Object& object) { + if constexpr(std::is_fundamental_v || std::is_same_v || + std::is_same_v) { + return json(object); + } else if constexpr(impl::is_vector_like_v) { + return json(object); + } else if constexpr(std::is_enum_v) { + return static_cast>(object); } else { - std::cout << "{" << std::endl; - for_each(object, [&](auto&& name, auto&& value) { - std::cout << name << ": "; - print(value); + json json; + for_each(object, [&json](std::string_view name, auto& member) { + using Member = std::remove_cvref_t; + if constexpr(impl::is_optional_v) { + if(member.has_value()) { + json.emplace(name, serialize(member.value())); + } + } else { + json.emplace(name, serialize(member)); + } }); - std::cout << "}" << std::endl; + return json; } } + } // namespace clice diff --git a/src/LSP/Server.cpp b/src/LSP/Server.cpp index 6ae34396..dfdd7548 100644 --- a/src/LSP/Server.cpp +++ b/src/LSP/Server.cpp @@ -1,32 +1,44 @@ -#include #include -#include +#include +#include + +#include +#include namespace clice { Server server; +static MessageBuffer buffer; + +void on_read(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.handle_message(message); + buffer.clear(); + } + } else if(nread == UV_EOF) { + uv_read_stop(stream); + } else if(nread < 0) { + uv_read_stop(stream); + } + free(buf->base); +} int Server::start() { loop = uv_default_loop(); // initialize pipe and bind to stdin 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); auto alloc_buffer = [](uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { buf->base = (char*)std::malloc(suggested_size); buf->len = suggested_size; }; - auto on_read = [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { - if(nread > 0) { - server.handle_message(std::string_view(buf->base, nread)); - } else if(nread < 0) { - // TODO: error handling - } - std::free(buf->base); - }; - uv_read_start((uv_stream_t*)&stdin_pipe, alloc_buffer, on_read); // start the event loop @@ -34,8 +46,8 @@ int Server::start() { } int Server::exit() { - // close the pipe - // uv_close((uv_handle_t*)&stdin_pipe, NULL); + // uv_pipe_end(&stdin_pipe); + // uv_pipe_end(&stdout_pipe); // stop the event loop uv_stop(loop); @@ -43,15 +55,31 @@ int Server::exit() { } void Server::handle_message(std::string_view message) { - // std::string content = "{\"result\":\"" + std::string(message) + "\"}"; - std::string content = "{\"result\": \"Hello, World!\"}"; - std::string header = "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n"; + try { + auto input = json::parse(message); + int id = input["id"].get(); + std::string_view method = input["method"].get(); + logger::info("method: {}", method); - llvm::outs() << (header + content); + if(method == "initialize") { + // initialize(); + json json = { + {"id", id }, + {"result", clice::serialize(InitializeResult{})} + }; - std::ofstream out("/home/ykiko/Project/C++/clice/build/twitter.json", std::ios::app); - out << message; - out.close(); -} // namespace clice + auto stream = json.dump(); + std::string header = "Content-Length: " + std::to_string(stream.size()) + "\r\n\r\n"; + header += stream; + + uv_buf_t buf = uv_buf_init(header.data(), header.size()); + uv_write_t* req = (uv_write_t*)malloc(sizeof(uv_write_t)); + uv_write(req, (uv_stream_t*)&stdout_pipe, &buf, 1, NULL); + } + } catch(std::exception& e) { + logger::error("failed to parse JSON: {}", e.what()); + return; + } +} } // namespace clice diff --git a/src/main.cpp b/src/main.cpp index 4f887664..3e051171 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,84 +1,12 @@ #include -#include -#include #include +#include #include -uv_loop_t* loop = nullptr; -struct Task { - struct promise_type { - Task get_return_object() { return Task{Handle::from_promise(*this)}; } - - std::suspend_never initial_suspend() { return {}; } - - std::suspend_always final_suspend() noexcept { return {}; } - - void return_void() {} - - void unhandled_exception() { std::terminate(); } - }; - - struct Handle { - std::coroutine_handle coro; - - static Handle from_promise(promise_type& p) { - return Handle{std::coroutine_handle::from_promise(p)}; - } - - ~Handle() { - if(coro) - coro.destroy(); - } - }; - - Handle h; -}; - -struct uv_awaitable { - uv_work_t req; - std::function work_fn; - std::coroutine_handle<> handle; - - bool await_ready() { return false; } - - void await_suspend(std::coroutine_handle<> handle) { - this->handle = handle; - req.data = this; - std::cout << "Scheduling work..." << std::endl; - uv_queue_work( - loop, - &req, - [](uv_work_t* req) { - auto* self = static_cast(req->data); - self->work_fn(); - }, - [](uv_work_t* req, int status) { - auto& handle = static_cast(req->data)->handle; - if(handle.done()) { - std::cout << "Work done" << std::endl; - } else { - handle.resume(); - } - }); - } - - auto await_resume() { return 1; } -}; - -Task async_factorial(int n) { - long result = 1; - auto s = co_await uv_awaitable{.work_fn = [&result, n] { - for(int i = 1; i <= n; ++i) { - result *= i; - } - }}; - std::cout << "Factorial of " << n << " is " << result << std::endl; -} - -int main() { - clice::InitializeResult result; - clice::print(result); - // auto& server = clice::server; - // server.start(); +int main(int argc, char** argv) { + clice::logger::init(argv[0]); + auto& server = clice::server; + clice::logger::info("Starting server..."); + server.start(); return 0; }