tsl-devkit/lsp-server/src/core/server.cppm

545 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module;
export module lsp.core.server;
import spdlog;
import tree_sitter;
import std;
import lsp.bridge.win32_stdio;
import lsp.core.dispatcher;
import lsp.protocol;
import lsp.codec.facade;
import lsp.language.ast;
import lsp.manager.manager_hub;
import lsp.manager.bootstrap;
import lsp.manager.events;
import lsp.scheduler.async_executor;
import lsp.provider.base.interface;
import lsp.provider.manifest;
namespace transform = lsp::codec;
export namespace lsp::core
{
class LspServer
{
public:
explicit LspServer(std::size_t concurrency = std::thread::hardware_concurrency(),
std::string interpreter_path = "");
~LspServer();
void Run();
private:
// 读取LSP消息
std::optional<std::string> ReadMessage();
// 处理LSP请求 - 返回序列化的响应或空字符串(对于通知)
void HandleMessage(const std::string& raw_message);
// 发送LSP消息响应/通知)
void SendMessage(const std::string& message);
// 处理不同类型的消息
void HandleRequest(const protocol::RequestMessage& request);
void HandleNotification(const protocol::NotificationMessage& notification);
void HandleResponse(const protocol::ResponseMessage& response);
// 生命周期事件处理
void OnLifecycleEvent(provider::ServerLifecycleEvent event);
// 判断是否需要同步处理
bool RequiresSyncProcessing(const std::string& method) const;
// 检查是否可以处理请求
bool CanProcessRequest(const std::string& method) const;
// 处理取消请求
void HandleCancelRequest(const protocol::NotificationMessage& notification);
private:
void InitializeManagerHub();
void RegisterProviders();
void RegisterDiagnosticsPublisher();
void PublishDiagnostics(const protocol::DocumentUri& uri,
std::optional<protocol::integer> version,
TSTree* tree,
const protocol::string& content);
void ClearDiagnostics(const protocol::DocumentUri& uri);
// 错误处理
void SendError(const protocol::RequestMessage& request, protocol::ErrorCodes code, const std::string& message);
void SendStateError(const protocol::RequestMessage& request);
private:
RequestDispatcher dispatcher_;
manager::ManagerHub manager_hub_;
scheduler::AsyncExecutor async_executor_;
std::string interpreter_path_;
std::atomic<bool> is_initialized_ = false;
std::atomic<bool> is_shutting_down_ = false;
std::mutex output_mutex_;
};
}
namespace lsp::core
{
LspServer::LspServer(std::size_t concurrency, std::string interpreter_path) : manager_hub_(),
async_executor_(concurrency),
interpreter_path_(std::move(interpreter_path))
{
spdlog::info("Initializing LSP server with {} worker threads", concurrency);
InitializeManagerHub();
RegisterProviders();
if (provider::kEnableDiagnosticsPublisher)
{
RegisterDiagnosticsPublisher();
}
else
{
spdlog::debug("Diagnostics publisher disabled (staged rollout)");
}
spdlog::debug("LSP server initialized with {} providers.", dispatcher_.GetAllSupportedMethods().size());
}
LspServer::~LspServer()
{
is_shutting_down_ = true;
spdlog::info("LSP server shutting down...");
}
void LspServer::Run()
{
spdlog::info("LSP server starting main loop...");
spdlog::info("Waiting for LSP messages on stdin...");
bridge::win32_stdio::SetStdioBinaryMode();
while (!is_shutting_down_)
{
try
{
std::optional<std::string> message = ReadMessage();
if (!message)
{
if (std::cin.eof())
{
spdlog::info("End of input stream, exiting main loop");
break; // EOF
}
spdlog::debug("No message received, continuing...");
std::this_thread::sleep_for(std::chrono::milliseconds(5));
continue;
}
HandleMessage(*message);
}
catch (const std::exception& e)
{
spdlog::error("Error in main loop: {}", e.what());
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
spdlog::info("LSP server main loop ended");
}
std::optional<std::string> LspServer::ReadMessage()
{
std::string line;
std::size_t content_length = 0;
// 读取 LSP Header
while (std::getline(std::cin, line))
{
// 去掉尾部 \\r
if (!line.empty() && line.back() == '\r')
{
line.pop_back();
}
if (line.empty())
{
break; // 空行表示 header 结束
}
if (line.rfind("Content-Length:", 0) == 0)
{
std::string length_str = line.substr(15); // 跳过 "Content-Length:"
std::size_t start = length_str.find_first_not_of(' ');
if (start != std::string::npos)
{
length_str = length_str.substr(start);
try
{
content_length = std::stoul(length_str);
spdlog::trace("Content-Length: {}", content_length);
}
catch (const std::exception& e)
{
spdlog::error("Failed to parse Content-Length: {}", e.what());
return std::nullopt;
}
}
}
}
if (content_length == 0)
{
spdlog::debug("No Content-Length found in header");
return std::nullopt;
}
// 读取内容体
std::string body(content_length, '\0');
std::cin.read(&body[0], content_length);
if (std::cin.gcount() != static_cast<std::streamsize>(content_length))
{
spdlog::error("Failed to read expected content length: {} bytes, got {} bytes", content_length, std::cin.gcount());
return std::nullopt;
}
spdlog::trace("Received message: {}", body);
return body;
}
void LspServer::HandleMessage(const std::string& raw_message)
{
auto any = transform::Deserialize<protocol::LSPAny>(raw_message);
if (!any || !any->Is<protocol::LSPObject>())
{
spdlog::warn("Failed to parse message: {}", raw_message);
return;
}
const auto& obj = any->Get<protocol::LSPObject>();
const bool has_id = obj.find("id") != obj.end();
const bool has_method = obj.find("method") != obj.end();
const bool has_result = obj.find("result") != obj.end();
const bool has_error = obj.find("error") != obj.end();
if (has_method && has_id)
{
if (auto request = transform::Deserialize<protocol::RequestMessage>(raw_message))
HandleRequest(*request);
else
spdlog::warn("Failed to parse request message");
return;
}
if (has_method)
{
if (auto notification = transform::Deserialize<protocol::NotificationMessage>(raw_message))
HandleNotification(*notification);
else
spdlog::warn("Failed to parse notification message");
return;
}
if (has_id && (has_result || has_error))
{
if (auto response = transform::Deserialize<protocol::ResponseMessage>(raw_message))
HandleResponse(*response);
else
spdlog::warn("Failed to parse response message");
return;
}
spdlog::warn("Unrecognized message: {}", raw_message);
}
void LspServer::SendMessage(const std::string& message)
{
if (message.empty())
return;
std::lock_guard<std::mutex> lock(output_mutex_);
std::cout << "Content-Length: " << message.size() << "\r\n\r\n"
<< message << std::flush;
}
void LspServer::HandleRequest(const protocol::RequestMessage& request)
{
spdlog::debug("Handling request: {}", request.method);
if (request.method == "shutdown")
{
auto response = dispatcher_.Dispatch(request);
SendMessage(response);
is_shutting_down_ = true;
return;
}
// 未初始化时的特殊处理
if (!is_initialized_ && request.method != "initialize")
{
SendStateError(request);
return;
}
auto response = dispatcher_.Dispatch(request);
SendMessage(response);
}
void LspServer::HandleNotification(const protocol::NotificationMessage& notification)
{
spdlog::debug("Handling notification: {}", notification.method);
if (notification.method == "exit")
{
is_shutting_down_ = true;
return;
}
// 处理取消请求
if (notification.method == "$/cancelRequest")
{
HandleCancelRequest(notification);
return;
}
// 未初始化时只接受 initialized/exit
if (!is_initialized_ && notification.method != "initialized" && notification.method != "exit")
{
spdlog::warn("Server not initialized; ignoring notification: {}", notification.method);
return;
}
dispatcher_.Dispatch(notification);
}
void LspServer::HandleResponse(const protocol::ResponseMessage& response)
{
std::string id = "<no id>";
if (response.id.has_value())
id = transform::debug::GetIdString(response.id.value());
spdlog::debug("Received response: {}", id);
// 当前服务器作为 client 的场景较少,这里暂时不处理
}
void LspServer::OnLifecycleEvent(provider::ServerLifecycleEvent event)
{
switch (event)
{
case provider::ServerLifecycleEvent::kInitialized:
is_initialized_ = true;
spdlog::info("Server initialized");
break;
case provider::ServerLifecycleEvent::kShutdown:
is_shutting_down_ = true;
spdlog::info("Server shutting down");
break;
default:
break;
}
}
bool LspServer::RequiresSyncProcessing(const std::string& method) const
{
static const std::unordered_set<std::string> kSyncMethods = {
"initialize",
"shutdown",
"exit",
"$/setTrace",
};
return kSyncMethods.contains(method);
}
bool LspServer::CanProcessRequest(const std::string& method) const
{
if (is_shutting_down_)
return false;
// 初始化前,只处理 initialize 请求
if (!is_initialized_ && method != "initialize")
return false;
return true;
}
void LspServer::HandleCancelRequest(const protocol::NotificationMessage& notification)
{
if (!notification.params.has_value())
return;
protocol::CancelParams params =
transform::FromLSPAny.template operator()<protocol::CancelParams>(notification.params.value());
const std::string id_string = std::visit([](const auto& value) -> std::string {
if constexpr (std::is_same_v<std::decay_t<decltype(value)>, int>)
return std::to_string(value);
else
return value;
},
params.id);
spdlog::debug("Cancel request received for id: {}", id_string);
// TODO: 实现请求取消逻辑
}
void LspServer::InitializeManagerHub()
{
manager_hub_.Initialize();
if (!interpreter_path_.empty())
{
std::filesystem::path base = interpreter_path_;
std::filesystem::path funcext_path = base / "funcext";
if (std::filesystem::exists(funcext_path))
{
manager::bootstrap::InitializeManagerHub(
manager_hub_,
async_executor_,
{ funcext_path.string() });
}
else
{
spdlog::warn("Interpreter funcext path does not exist: {}", funcext_path.string());
}
}
}
void LspServer::RegisterProviders()
{
dispatcher_.SetRequestScheduler(&async_executor_);
dispatcher_.SetManagerHub(&manager_hub_);
dispatcher_.RegisterLifecycleCallback([this](ServerLifecycleEvent event) {
OnLifecycleEvent(event);
});
spdlog::info("Registering LSP providers...");
provider::RegisterAllProviders(dispatcher_);
spdlog::info("Registered {} LSP providers", dispatcher_.GetAllSupportedMethods().size());
}
void LspServer::RegisterDiagnosticsPublisher()
{
auto& event_bus = manager_hub_.event_bus();
event_bus.Subscribe<manager::events::DocumentParsed>(
[this](const manager::events::DocumentParsed& event) {
PublishDiagnostics(event.item.uri, event.item.version, event.tree, event.item.text);
});
event_bus.Subscribe<manager::events::DocumentReparsed>(
[this](const manager::events::DocumentReparsed& event) {
PublishDiagnostics(event.item.uri, event.item.version, event.tree, event.item.text);
});
event_bus.Subscribe<manager::events::DocumentClosed>(
[this](const manager::events::DocumentClosed& event) {
ClearDiagnostics(event.textDocument.uri);
});
}
void LspServer::PublishDiagnostics(const protocol::DocumentUri& uri,
std::optional<protocol::integer> version,
TSTree* tree,
const protocol::string& content)
{
if (!tree)
{
spdlog::warn("Skip diagnostics publish: null syntax tree for {}", uri);
return;
}
language::ast::Deserializer deserializer;
auto errors = deserializer.DiagnoseSyntax(ts_tree_root_node(tree), content);
std::vector<protocol::Diagnostic> diagnostics;
diagnostics.reserve(errors.size());
for (const auto& error : errors)
{
protocol::Diagnostic diagnostic;
diagnostic.range.start.line = error.location.start_line;
diagnostic.range.start.character = error.location.start_column;
diagnostic.range.end.line = error.location.end_line;
diagnostic.range.end.character = error.location.end_column;
switch (error.severity)
{
case language::ast::ErrorSeverity::Warning:
diagnostic.severity = protocol::DiagnosticSeverity::Warning;
break;
case language::ast::ErrorSeverity::Fatal:
case language::ast::ErrorSeverity::Error:
default:
diagnostic.severity = protocol::DiagnosticSeverity::Error;
break;
}
diagnostic.source = "tsl";
diagnostic.message = error.message;
diagnostics.push_back(std::move(diagnostic));
}
protocol::PublishDiagnosticsParams params;
params.uri = uri;
params.version = version;
params.diagnostics = std::move(diagnostics);
protocol::NotificationMessage notification;
notification.method = "textDocument/publishDiagnostics";
notification.params = transform::ToLSPAny(params);
auto json = transform::Serialize(notification);
if (!json)
{
spdlog::warn("Failed to serialize diagnostics notification for {}", uri);
return;
}
SendMessage(*json);
}
void LspServer::ClearDiagnostics(const protocol::DocumentUri& uri)
{
protocol::PublishDiagnosticsParams params;
params.uri = uri;
params.version = std::nullopt;
params.diagnostics = {};
protocol::NotificationMessage notification;
notification.method = "textDocument/publishDiagnostics";
notification.params = transform::ToLSPAny(params);
auto json = transform::Serialize(notification);
if (!json)
{
spdlog::warn("Failed to serialize diagnostics clear notification for {}", uri);
return;
}
SendMessage(*json);
}
void LspServer::SendError(const protocol::RequestMessage& request, protocol::ErrorCodes code, const std::string& message)
{
protocol::ResponseMessage response;
response.id = request.id;
protocol::ResponseError error;
error.code = static_cast<protocol::integer>(code);
error.message = message;
response.error = error;
auto json = transform::Serialize(response);
if (json)
{
SendMessage(*json);
}
}
void LspServer::SendStateError(const protocol::RequestMessage& request)
{
SendError(request, protocol::ErrorCodes::ServerNotInitialized, "Server not initialized");
}
}