545 lines
18 KiB
C++
545 lines
18 KiB
C++
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");
|
||
}
|
||
|
||
}
|