Support socket to transport.
This commit is contained in:
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
@@ -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",
|
||||
|
||||
@@ -3,17 +3,18 @@
|
||||
#include <Server/Option.h>
|
||||
#include <Server/Command.h>
|
||||
#include <Server/Scheduler.h>
|
||||
#include <Server/Transport .h>
|
||||
#include <Protocol/Protocol.h>
|
||||
|
||||
namespace clice {
|
||||
|
||||
struct Server {
|
||||
using Handler = llvm::unique_function<void(json::Value, json::Value)>;
|
||||
|
||||
Option option;
|
||||
Scheduler scheduler;
|
||||
CompilationDatabase CDB;
|
||||
llvm::StringMap<Handler> handlers;
|
||||
std::unique_ptr<Transport> transport;
|
||||
|
||||
static Server instance;
|
||||
|
||||
|
||||
@@ -1,8 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <uv.h>
|
||||
#include <string_view>
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
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
|
||||
|
||||
@@ -7,18 +7,21 @@
|
||||
namespace clice {
|
||||
|
||||
void Scheduler::dispatch(json::Value id, std::string_view method, json::Value value) {
|
||||
std::vector<const char*> 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<protocol::DidOpenTextDocumentParams>(value);
|
||||
std::vector<const char*> 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<protocol::DidChangeTextDocumentParams>(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<protocol::SemanticTokensParams>(value);
|
||||
auto AST = files[params.textDocument.uri].ast.get();
|
||||
|
||||
@@ -6,69 +6,14 @@
|
||||
#include <Support/FileSystem.h>
|
||||
#include <Server/Command.h>
|
||||
#include <Support/JSON.h>
|
||||
#include <Server/Transport .h>
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
|
||||
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<char> 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<Socket>(
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
128
src/Server/Transport.cpp
Normal file
128
src/Server/Transport.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <Server/Transport .h>
|
||||
#include <llvm/ADT/SmallVector.h>
|
||||
|
||||
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<char> 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<char, 4096> 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<uv_stream_t*>(&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<char*>(message.data()), message.size());
|
||||
|
||||
auto state = uv_write(&req, reinterpret_cast<uv_stream_t*>(&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<uv_stream_t*>(&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<char*>(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
|
||||
Reference in New Issue
Block a user