implement basic communication between the server and the client.
This commit is contained in:
@@ -43,4 +43,8 @@ struct TextDocumentItem {
|
||||
string text;
|
||||
};
|
||||
|
||||
struct TextDocumentIdentifier {
|
||||
DocumentUri uri;
|
||||
};
|
||||
|
||||
} // namespace clice::protocol
|
||||
|
||||
@@ -12,7 +12,7 @@ enum class SemanticTokenType : uint8_t {
|
||||
/// Represents a character literal.
|
||||
Char,
|
||||
/// Represents a string literal.
|
||||
String,
|
||||
string,
|
||||
/// Represents a C/C++ keyword (e.g., `int`, `class`, `struct`).
|
||||
Keyword,
|
||||
/// Represents a compiler built-in macro, function, or keyword (e.g., `__stdcall`,
|
||||
@@ -126,10 +126,10 @@ struct SemanticTokensClientCapabilities {
|
||||
};
|
||||
|
||||
/// The token types that the client supports.
|
||||
std::vector<String> tokenTypes;
|
||||
std::vector<string> tokenTypes;
|
||||
|
||||
/// The token modifiers that the client supports.
|
||||
std::vector<String> tokenModifiers;
|
||||
std::vector<string> tokenModifiers;
|
||||
|
||||
/// The formats the client supports.
|
||||
/// formats: TokenFormat[];
|
||||
@@ -149,10 +149,10 @@ struct SemanticTokensClientCapabilities {
|
||||
|
||||
struct SemanticTokensLegend {
|
||||
/// The token types a server uses.
|
||||
std::vector<String> tokenTypes;
|
||||
std::vector<string> tokenTypes;
|
||||
|
||||
/// The token modifiers a server uses.
|
||||
std::vector<String> tokenModifiers;
|
||||
std::vector<string> tokenModifiers;
|
||||
};
|
||||
|
||||
/// Server Capability:
|
||||
@@ -172,24 +172,20 @@ struct SemanticTokensOptions {
|
||||
/// Request:
|
||||
/// - method: `textDocument/semanticTokens/full`
|
||||
/// - params: `SemanticTokensParams` defined as follows:
|
||||
struct SemanticTokensParamsBody {
|
||||
|
||||
struct SemanticTokensParams {
|
||||
/// The text document.
|
||||
TextDocumentIdentifier textDocument;
|
||||
};
|
||||
|
||||
using SemanticTokensParams = Combine<
|
||||
// WorkDoneProgressParams,
|
||||
// PartialResultParams,
|
||||
SemanticTokensParamsBody>;
|
||||
|
||||
/// Response:
|
||||
/// - result: `SemanticTokens` defined as follows:
|
||||
struct SemanticTokens {
|
||||
/// An optional result id.
|
||||
String resultId;
|
||||
string resultId;
|
||||
|
||||
/// The actual tokens.
|
||||
std::vector<Integer> data;
|
||||
std::vector<integer> data;
|
||||
};
|
||||
|
||||
} // namespace clice::protocol
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
#include "../Basic.h"
|
||||
#include "../Language/SemanticToken.h"
|
||||
|
||||
namespace clice::protocol {
|
||||
|
||||
struct ServerCapabilities {
|
||||
std::string_view positionEncoding = "utf-16";
|
||||
SemanticTokensOptions semanticTokensProvider;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
struct InitializeResult {
|
||||
ServerCapabilities capabilities;
|
||||
|
||||
struct {
|
||||
std::string_view name = "clice";
|
||||
std::string_view version = "0.0.1";
|
||||
} serverInfo;
|
||||
};
|
||||
|
||||
} // namespace clice::protocol
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace clice {
|
||||
class Server {
|
||||
public:
|
||||
int run(int argc, const char** argv);
|
||||
|
||||
void handleMessage(std::string_view message);
|
||||
};
|
||||
|
||||
namespace global {
|
||||
|
||||
8
include/Server/Transport .h
Normal file
8
include/Server/Transport .h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
namespace clice {
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -19,17 +19,17 @@ constexpr inline bool is_integral_v =
|
||||
template <typename T>
|
||||
Object serialize(const T& object) {
|
||||
Object result;
|
||||
for_each(object, [&]<typename Value>(std::string_view name, Value& value) {
|
||||
for_each(object, [&]<typename Value>(llvm::StringRef name, Value& value) {
|
||||
if constexpr(is_array_v<Value>) {
|
||||
Array array;
|
||||
for(const auto& element: value) {
|
||||
array.push_back(serialize(element));
|
||||
}
|
||||
result.try_emplace(llvm::StringRef(name), std::move(array));
|
||||
result.try_emplace(name, std::move(array));
|
||||
} else if constexpr(std::is_constructible_v<json::Value, Value&>) {
|
||||
result.try_emplace(llvm::StringRef(name), value);
|
||||
result.try_emplace(name, value);
|
||||
} else {
|
||||
result.try_emplace(llvm::StringRef(name), serialize(value));
|
||||
result.try_emplace(name, serialize(value));
|
||||
}
|
||||
});
|
||||
return result;
|
||||
@@ -38,7 +38,7 @@ Object serialize(const T& object) {
|
||||
template <typename T>
|
||||
T deserialize(const Object& object) {
|
||||
T result;
|
||||
for_each(result, [&]<typename Value>(std::string_view name, Value& value) {
|
||||
for_each(result, [&]<typename Value>(llvm::StringRef name, Value& value) {
|
||||
if constexpr(is_array_v<Value>) {
|
||||
if(const auto* array = object.getArray(name)) {
|
||||
for(std::size_t i = 0; i < array->size(); ++i) {
|
||||
|
||||
@@ -1,12 +1,135 @@
|
||||
#include <uv.h>
|
||||
#include <Server/Server.h>
|
||||
#include <llvm/ADT/SmallString.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/sinks/basic_file_sink.h>
|
||||
#include <Support/FileSystem.h>
|
||||
#include <Server/Command.h>
|
||||
#include <Support/JSON.h>
|
||||
#include <Protocol/Lifecycle/Initialize.h>
|
||||
|
||||
namespace clice {
|
||||
|
||||
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()) {
|
||||
global::server.handleMessage(message);
|
||||
}
|
||||
} else if(nread < 0) {
|
||||
if(nread != UV_EOF) {
|
||||
spdlog::error("Read error: {}", uv_err_name(nread));
|
||||
}
|
||||
uv_close((uv_handle_t*)stream, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int Server::run(int argc, const char** argv) {
|
||||
// TODO:
|
||||
// set logger
|
||||
llvm::SmallString<128> temp;
|
||||
temp.append(path::parent_path((path::parent_path(argv[0]))));
|
||||
path::append(temp, "logs");
|
||||
auto error = llvm::sys::fs::make_absolute(temp);
|
||||
path::append(temp, "clice.log");
|
||||
auto logger = spdlog::basic_logger_mt("clice", std::string(temp.str()));
|
||||
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);
|
||||
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
|
||||
uv_loop_close(loop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Server::handleMessage(std::string_view message) {
|
||||
auto result = json::parse(message);
|
||||
if(!result) {
|
||||
spdlog::error("Error parsing JSON: {}", llvm::toString(result.takeError()));
|
||||
}
|
||||
|
||||
spdlog::info("Received message: {}", message);
|
||||
auto input = result->getAsObject();
|
||||
auto id = input->get("id");
|
||||
auto method = input->get("method");
|
||||
auto params = input->get("params");
|
||||
|
||||
json::Object response;
|
||||
response.try_emplace("jsonrpc", "2.0");
|
||||
response.try_emplace("id", *id);
|
||||
response.try_emplace("result", json::serialize(protocol::InitializeResult{}));
|
||||
|
||||
llvm::json::Value responseValue = std::move(response);
|
||||
|
||||
std::string s;
|
||||
llvm::raw_string_ostream stream(s);
|
||||
stream << responseValue;
|
||||
stream.flush();
|
||||
|
||||
s = "Content-Length: " + std::to_string(s.size()) + "\r\n\r\n" + s;
|
||||
|
||||
uv_buf_t buf = uv_buf_init(s.data(), s.size());
|
||||
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));
|
||||
}
|
||||
spdlog::info("Sent message: {}", s);
|
||||
}
|
||||
|
||||
namespace global {
|
||||
Server server;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user