Files
clice/src/Server/Server.cpp
2025-11-08 00:37:07 +08:00

187 lines
7.0 KiB
C++

#include "Support/Logging.h"
#include "Server/Server.h"
namespace clice {
ActiveFileManager::ActiveFile& ActiveFileManager::lru_put_impl(llvm::StringRef path,
OpenFile file) {
/// If the file is not in the chain, create a new OpenFile.
if(items.size() >= capability) {
/// If the size exceeds the maximum size, remove the last element.
index.erase(items.back().first);
items.pop_back();
}
items.emplace_front(path, std::make_shared<OpenFile>(std::move(file)));
// fix the ownership of the StringRef of the path.
auto [added, _] = index.insert({path, items.begin()});
items.front().first = added->getKey();
return items.front().second;
}
ActiveFileManager::ActiveFile& ActiveFileManager::get_or_add(llvm::StringRef path) {
auto iter = index.find(path);
if(iter == index.end()) {
return lru_put_impl(path, OpenFile{});
}
// If the file is in the chain, move it to the front.
items.splice(items.begin(), items, iter->second);
return iter->second->second;
}
ActiveFileManager::ActiveFile& ActiveFileManager::add(llvm::StringRef path, OpenFile file) {
auto iter = index.find(path);
if(iter == index.end()) {
return lru_put_impl(path, std::move(file));
}
iter->second->second = std::make_shared<OpenFile>(std::move(file));
// If the file is in the chain, move it to the front.
items.splice(items.begin(), items, iter->second);
return iter->second->second;
}
async::Task<> Server::request(llvm::StringRef method, json::Value params) {
co_await async::net::write(json::Object{
{"jsonrpc", "2.0" },
{"id", server_request_id += 1},
{"method", method },
{"params", std::move(params) },
});
}
async::Task<> Server::notify(llvm::StringRef method, json::Value params) {
co_await async::net::write(json::Object{
{"jsonrpc", "2.0" },
{"method", method },
{"params", std::move(params)},
});
}
async::Task<> Server::response(json::Value id, json::Value result) {
co_await async::net::write(json::Object{
{"jsonrpc", "2.0" },
{"id", std::move(id) },
{"result", std::move(result)},
});
}
async::Task<> Server::response(json::Value id, proto::ErrorCodes code, llvm::StringRef message) {
json::Object error{
{"code", static_cast<int>(code)},
{"message", message },
};
co_await async::net::write(json::Object{
{"jsonrpc", "2.0" },
{"id", std::move(id) },
{"error", std::move(error)},
});
}
async::Task<> Server::registerCapacity(llvm::StringRef id,
llvm::StringRef method,
json::Value registerOptions) {
co_await request("client/registerCapability",
json::Object{
{"registrations",
json::Array{json::Object{
{"id", id},
{"method", method},
{"registerOptions", std::move(registerOptions)},
}}},
});
}
Server::Server() : indexer(database, config, kind) {
register_callback<&Server::on_initialize>("initialize");
register_callback<&Server::on_initialized>("initialized");
register_callback<&Server::on_shutdown>("shutdown");
register_callback<&Server::on_exit>("exit");
register_callback<&Server::on_did_open>("textDocument/didOpen");
register_callback<&Server::on_did_change>("textDocument/didChange");
register_callback<&Server::on_did_save>("textDocument/didSave");
register_callback<&Server::on_did_close>("textDocument/didClose");
register_callback<&Server::on_completion>("textDocument/completion");
register_callback<&Server::on_hover>("textDocument/hover");
register_callback<&Server::on_signature_help>("textDocument/signatureHelp");
register_callback<&Server::on_go_to_declaration>("textDocument/declaration");
register_callback<&Server::on_go_to_definition>("textDocument/definition");
register_callback<&Server::on_find_references>("textDocument/references");
register_callback<&Server::on_document_symbol>("textDocument/documentSymbol");
register_callback<&Server::on_document_link>("textDocument/documentLink");
register_callback<&Server::on_document_format>("textDocument/formatting");
register_callback<&Server::on_document_range_format>("textDocument/rangeFormatting");
register_callback<&Server::on_folding_range>("textDocument/foldingRange");
register_callback<&Server::on_semantic_token>("textDocument/semanticTokens/full");
register_callback<&Server::on_inlay_hint>("textDocument/inlayHint");
}
async::Task<> Server::on_receive(json::Value value) {
auto object = value.getAsObject();
if(!object) [[unlikely]] {
LOGGING_FATAL("Invalid LSP message, not an object: {}", value);
}
/// If the json object has an `id`, it's a request,
/// which needs a response. Otherwise, it's a notification.
auto id = object->get("id");
llvm::StringRef method;
if(auto result = object->getString("method")) {
method = *result;
} else [[unlikely]] {
LOGGING_WARN("Invalid LSP message, method not found: {}", value);
if(id) {
co_await response(std::move(*id),
proto::ErrorCodes::InvalidRequest,
"Method not found");
}
co_return;
}
json::Value params = json::Value(nullptr);
if(auto result = object->get("params")) {
params = std::move(*result);
}
/// Handle request and notification separately.
auto it = callbacks.find(method);
if(it == callbacks.end()) {
LOGGING_INFO("Ignore unhandled method: {}", method);
co_return;
}
if(id) {
auto current_id = client_request_id++;
auto start_time = std::chrono::steady_clock::now();
LOGGING_INFO("<-- Handling request: {}({})", method, current_id);
auto result = co_await it->second(*this, std::move(params));
co_await response(std::move(*id), std::move(result));
auto end_time = std::chrono::steady_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
LOGGING_INFO("--> Handled request: {}({}) {}ms", method, current_id, duration.count());
} else {
auto start_time = std::chrono::steady_clock::now();
LOGGING_INFO("<-- Handling notification: {}", method);
auto result = co_await it->second(*this, std::move(params));
auto end_time = std::chrono::steady_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
LOGGING_INFO("--> Handled notification: {} {}ms", method, duration.count());
}
co_return;
}
} // namespace clice