refactor
This commit is contained in:
parent
3cc1fccc81
commit
0c4619261d
|
|
@ -77,9 +77,9 @@ set(SOURCES
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/utils/args_parser.cpp
|
src/utils/args_parser.cpp
|
||||||
src/language/tsl_keywords.cpp
|
src/language/tsl_keywords.cpp
|
||||||
src/lsp/dispacther.cpp
|
src/core/dispacther.cpp
|
||||||
src/lsp/server.cpp
|
src/core/server.cpp
|
||||||
src/lsp/request_scheduler.cpp
|
src/scheduler/request_scheduler.cpp
|
||||||
src/provider/base/provider_registry.cpp
|
src/provider/base/provider_registry.cpp
|
||||||
src/provider/base/provider_interface.cpp
|
src/provider/base/provider_interface.cpp
|
||||||
src/provider/initialize/initialize_provider.cpp
|
src/provider/initialize/initialize_provider.cpp
|
||||||
|
|
@ -88,6 +88,8 @@ set(SOURCES
|
||||||
src/provider/text_document/did_change_provider.cpp
|
src/provider/text_document/did_change_provider.cpp
|
||||||
src/provider/text_document/completion_provider.cpp
|
src/provider/text_document/completion_provider.cpp
|
||||||
src/provider/shutdown/shutdown_provider.cpp
|
src/provider/shutdown/shutdown_provider.cpp
|
||||||
|
src/provider/exit/exit_provider.cpp
|
||||||
|
src/provider/cancel_request/cancel_request_provider.cpp
|
||||||
src/provider/trace/set_trace_provider.cpp)
|
src/provider/trace/set_trace_provider.cpp)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} ${SOURCES})
|
add_executable(${PROJECT_NAME} ${SOURCES})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,193 @@
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include "./dispacther.hpp"
|
||||||
|
#include "../protocol/transform/facade.hpp"
|
||||||
|
|
||||||
|
namespace lsp::core
|
||||||
|
{
|
||||||
|
void RequestDispatcher::SetRequestScheduler(scheduler::RequestScheduler* scheduler)
|
||||||
|
{
|
||||||
|
scheduler_ = scheduler;
|
||||||
|
spdlog::debug("RequestScheduler set in dispatcher");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestDispatcher::RegisterRequestProvider(std::shared_ptr<providers::IRequestProvider> provider)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::shared_mutex> lock(providers_mutex_);
|
||||||
|
std::string method = provider->GetMethod();
|
||||||
|
|
||||||
|
providers_[method] = provider;
|
||||||
|
spdlog::info("Registered request provider '{}' for method: {}", provider->GetProviderName(), method);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestDispatcher::RegisterNotificationProvider(std::shared_ptr<providers::INotificationProvider> provider)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::shared_mutex> lock(notification_providers_mutex_);
|
||||||
|
std::string method = provider->GetMethod();
|
||||||
|
|
||||||
|
notification_providers_[method] = provider;
|
||||||
|
spdlog::info("Registered notification provider '{}' for method: {}", provider->GetProviderName(), method);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestDispatcher::RegisterLifecycleCallback(LifecycleCallback callback)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(callbacks_mutex_);
|
||||||
|
lifecycle_callbacks_.push_back(std::move(callback));
|
||||||
|
spdlog::debug("Registered lifecycle callback");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RequestDispatcher::Dispatch(const protocol::RequestMessage& request)
|
||||||
|
{
|
||||||
|
providers::ProviderContext context(
|
||||||
|
scheduler_,
|
||||||
|
[this](ServerLifecycleEvent event)
|
||||||
|
{
|
||||||
|
NotifyAllLifecycleListeners(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
std::shared_lock<std::shared_mutex> lock(providers_mutex_);
|
||||||
|
auto it = providers_.find(request.method);
|
||||||
|
if (it != providers_.end())
|
||||||
|
{
|
||||||
|
auto provider = it->second;
|
||||||
|
lock.unlock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
spdlog::debug("Dispatching request '{}' to provider '{}'", request.method, provider->GetProviderName());
|
||||||
|
return provider->ProvideResponse(request, context);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
spdlog::error("Provider error for method {}: {}", request.method, e.what());
|
||||||
|
return BuildErrorResponseMessage(request, protocol::ErrorCode::kInternalError, e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return HandleUnknownRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestDispatcher::Dispatch(const protocol::NotificationMessage& notification)
|
||||||
|
{
|
||||||
|
// 创建 provider context
|
||||||
|
providers::ProviderContext context(
|
||||||
|
scheduler_,
|
||||||
|
[this](ServerLifecycleEvent event)
|
||||||
|
{
|
||||||
|
NotifyAllLifecycleListeners(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
std::shared_lock<std::shared_mutex> lock(notification_providers_mutex_);
|
||||||
|
|
||||||
|
// 先尝试精确匹配
|
||||||
|
auto it = notification_providers_.find(notification.method);
|
||||||
|
if (it != notification_providers_.end())
|
||||||
|
{
|
||||||
|
auto provider = it->second;
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
spdlog::debug("Dispatching notification '{}' to provider '{}'", notification.method, provider->GetProviderName());
|
||||||
|
provider->HandleNotification(notification, context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
spdlog::error("Notification provider '{}' threw exception for method '{}': {}", provider->GetProviderName(), notification.method, e.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HandleUnknownNotification(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequestDispatcher::SupportsRequest(const std::string& method) const
|
||||||
|
{
|
||||||
|
std::shared_lock<std::shared_mutex> lock(providers_mutex_);
|
||||||
|
return providers_.find(method) != providers_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> RequestDispatcher::GetSupportedRequests() const
|
||||||
|
{
|
||||||
|
std::shared_lock<std::shared_mutex> lock(providers_mutex_);
|
||||||
|
std::vector<std::string> methods;
|
||||||
|
methods.reserve(providers_.size());
|
||||||
|
for (const auto& [method, _] : providers_)
|
||||||
|
methods.push_back(method);
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> RequestDispatcher::GetSupportedNotifications() const
|
||||||
|
{
|
||||||
|
std::shared_lock<std::shared_mutex> lock(notification_providers_mutex_);
|
||||||
|
std::vector<std::string> methods;
|
||||||
|
methods.reserve(notification_providers_.size());
|
||||||
|
for (const auto& [method, provider] : notification_providers_)
|
||||||
|
methods.push_back(method);
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> RequestDispatcher::GetAllSupportedMethods() const
|
||||||
|
{
|
||||||
|
auto requests = GetSupportedRequests();
|
||||||
|
auto notifications = GetSupportedNotifications();
|
||||||
|
|
||||||
|
// 合并两个列表
|
||||||
|
requests.insert(requests.end(), notifications.begin(), notifications.end());
|
||||||
|
return requests;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RequestDispatcher::BuildErrorResponseMessage(const protocol::RequestMessage& request, protocol::ErrorCode code, const std::string& message)
|
||||||
|
{
|
||||||
|
return providers::IRequestProvider::BuildErrorResponseMessage(request, code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestDispatcher::NotifyAllLifecycleListeners(ServerLifecycleEvent event)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(callbacks_mutex_);
|
||||||
|
|
||||||
|
std::string event_name;
|
||||||
|
switch (event)
|
||||||
|
{
|
||||||
|
case ServerLifecycleEvent::kInitializing:
|
||||||
|
event_name = "Initializing";
|
||||||
|
break;
|
||||||
|
case ServerLifecycleEvent::kInitialized:
|
||||||
|
event_name = "Initialized";
|
||||||
|
break;
|
||||||
|
case ServerLifecycleEvent::kInitializeFailed:
|
||||||
|
event_name = "InitializeFailed";
|
||||||
|
break;
|
||||||
|
case ServerLifecycleEvent::kShuttingDown:
|
||||||
|
event_name = "ShuttingDown";
|
||||||
|
break;
|
||||||
|
case ServerLifecycleEvent::kShutdown:
|
||||||
|
event_name = "Shutdown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::info("Lifecycle event: {}", event_name);
|
||||||
|
|
||||||
|
for (const auto& callback : lifecycle_callbacks_)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
callback(event);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
spdlog::error("Lifecycle callback error: {}", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RequestDispatcher::HandleUnknownRequest(const protocol::RequestMessage& request)
|
||||||
|
{
|
||||||
|
return BuildErrorResponseMessage(request, protocol::ErrorCode::kMethodNotFound, "Method not found: " + request.method);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestDispatcher::HandleUnknownNotification(const protocol::NotificationMessage& notification)
|
||||||
|
{
|
||||||
|
spdlog::debug("No handler found for notification: {}", notification.method);
|
||||||
|
// 通知没有响应,所以只记录日志
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
#pragma once
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <shared_mutex>
|
||||||
|
#include "../protocol/protocol.hpp"
|
||||||
|
#include "../provider/base/provider_interface.hpp"
|
||||||
|
|
||||||
|
namespace lsp::core
|
||||||
|
{
|
||||||
|
|
||||||
|
using ServerLifecycleEvent = providers::ServerLifecycleEvent;
|
||||||
|
using LifecycleCallback = providers::LifecycleCallback;
|
||||||
|
|
||||||
|
class RequestDispatcher
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RequestDispatcher() = default;
|
||||||
|
~RequestDispatcher() = default;
|
||||||
|
|
||||||
|
void SetRequestScheduler(scheduler::RequestScheduler* scheduler);
|
||||||
|
|
||||||
|
void RegisterRequestProvider(std::shared_ptr<providers::IRequestProvider> provider);
|
||||||
|
void RegisterNotificationProvider(std::shared_ptr<providers::INotificationProvider> provider);
|
||||||
|
|
||||||
|
void RegisterLifecycleCallback(LifecycleCallback callback);
|
||||||
|
|
||||||
|
std::string Dispatch(const protocol::RequestMessage& request);
|
||||||
|
void Dispatch(const protocol::NotificationMessage& notification);
|
||||||
|
|
||||||
|
bool SupportsRequest(const std::string& method) const;
|
||||||
|
bool SupportsNotification(const std::string& method) const;
|
||||||
|
std::vector<std::string> GetSupportedRequests() const;
|
||||||
|
std::vector<std::string> GetSupportedNotifications() const;
|
||||||
|
std::vector<std::string> GetAllSupportedMethods() const;
|
||||||
|
|
||||||
|
std::string BuildErrorResponseMessage(const protocol::RequestMessage& request, protocol::ErrorCode code, const std::string& message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void NotifyAllLifecycleListeners(ServerLifecycleEvent event);
|
||||||
|
std::string HandleUnknownRequest(const protocol::RequestMessage& request);
|
||||||
|
void HandleUnknownNotification(const protocol::NotificationMessage& notification);
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::shared_mutex providers_mutex_;
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<providers::IRequestProvider>> providers_;
|
||||||
|
|
||||||
|
mutable std::shared_mutex notification_providers_mutex_;
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<providers::INotificationProvider>> notification_providers_;
|
||||||
|
|
||||||
|
std::mutex callbacks_mutex_;
|
||||||
|
std::vector<LifecycleCallback> lifecycle_callbacks_;
|
||||||
|
|
||||||
|
scheduler::RequestScheduler* scheduler_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
#include "request_scheduler.hpp"
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
@ -8,15 +7,18 @@
|
||||||
#endif
|
#endif
|
||||||
#include "../provider/base/provider_registry.hpp"
|
#include "../provider/base/provider_registry.hpp"
|
||||||
#include "../protocol/transform/facade.hpp"
|
#include "../protocol/transform/facade.hpp"
|
||||||
|
#include "../scheduler/request_scheduler.hpp"
|
||||||
#include "./server.hpp"
|
#include "./server.hpp"
|
||||||
|
|
||||||
namespace lsp
|
namespace lsp::core
|
||||||
{
|
{
|
||||||
LspServer::LspServer(size_t concurrency) :
|
LspServer::LspServer(size_t concurrency) :
|
||||||
scheduler_(4)
|
scheduler_(4)
|
||||||
{
|
{
|
||||||
spdlog::info("Initializing LSP server with {} worker threads", concurrency);
|
spdlog::info("Initializing LSP server with {} worker threads", concurrency);
|
||||||
|
|
||||||
|
dispatcher_.SetRequestScheduler(&scheduler_);
|
||||||
|
|
||||||
dispatcher_.RegisterLifecycleCallback(
|
dispatcher_.RegisterLifecycleCallback(
|
||||||
[this](providers::ServerLifecycleEvent event) {
|
[this](providers::ServerLifecycleEvent event) {
|
||||||
OnLifecycleEvent(event);
|
OnLifecycleEvent(event);
|
||||||
|
|
@ -27,7 +29,7 @@ namespace lsp
|
||||||
SendResponse(response);
|
SendResponse(response);
|
||||||
});
|
});
|
||||||
|
|
||||||
spdlog::debug("LSP server initialized with {} providers.", dispatcher_.GetSupportedMethods().size());
|
spdlog::debug("LSP server initialized with {} providers.", dispatcher_.GetAllSupportedMethods().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
LspServer::~LspServer()
|
LspServer::~LspServer()
|
||||||
|
|
@ -53,6 +55,11 @@ namespace lsp
|
||||||
std::optional<std::string> message = ReadMessage();
|
std::optional<std::string> message = ReadMessage();
|
||||||
if (!message)
|
if (!message)
|
||||||
{
|
{
|
||||||
|
if (std::cin.eof())
|
||||||
|
{
|
||||||
|
spdlog::info("End of input stream, exiting main loop");
|
||||||
|
break; // EOF
|
||||||
|
}
|
||||||
spdlog::debug("No message received, continuing...");
|
spdlog::debug("No message received, continuing...");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -129,71 +136,14 @@ namespace lsp
|
||||||
|
|
||||||
void LspServer::HandleMessage(const std::string& raw_message)
|
void LspServer::HandleMessage(const std::string& raw_message)
|
||||||
{
|
{
|
||||||
try
|
if (auto request = transform::Deserialize<protocol::RequestMessage>(raw_message))
|
||||||
{
|
HandleRequest(*request);
|
||||||
// 解析 JSON 判断消息类型
|
else if (auto notification = transform::Deserialize<protocol::NotificationMessage>(raw_message))
|
||||||
glz::json_t doc;
|
HandleNotification(*notification);
|
||||||
auto error = glz::read_json(doc, raw_message);
|
else if (auto response = transform::Deserialize<protocol::ResponseMessage>(raw_message))
|
||||||
if (error)
|
HandleResponse(*response);
|
||||||
{
|
|
||||||
spdlog::error("Failed to parse message as JSON: {}", glz::format_error(error, raw_message));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& obj = doc.get<glz::json_t::object_t>();
|
|
||||||
bool has_method = obj.contains("method");
|
|
||||||
|
|
||||||
if (has_method)
|
|
||||||
{
|
|
||||||
bool has_id = obj.contains("id");
|
|
||||||
|
|
||||||
if (has_id)
|
|
||||||
{
|
|
||||||
// RequestMessage
|
|
||||||
protocol::RequestMessage request;
|
|
||||||
error = glz::read_json(request, raw_message);
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
spdlog::error("Failed to parse request: {}", glz::format_error(error, raw_message));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
HandleRequest(request);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
spdlog::error("Failed to deserialize message as any LSP message type");
|
||||||
// NotificationMessage
|
|
||||||
protocol::NotificationMessage notification;
|
|
||||||
error = glz::read_json(notification, raw_message);
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
spdlog::error("Failed to parse notification: {}", glz::format_error(error, raw_message));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
HandleNotification(notification);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (obj.contains("id") && (obj.contains("result") || obj.contains("error")))
|
|
||||||
{
|
|
||||||
// ResponseMessage
|
|
||||||
protocol::ResponseMessage response;
|
|
||||||
error = glz::read_json(response, raw_message);
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
spdlog::error("Failed to parse response: {}",
|
|
||||||
glz::format_error(error, raw_message));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
HandleResponse(response);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
spdlog::error("Unknown message type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
spdlog::error("Failed to handle message: {}", e.what());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LspServer::HandleRequest(const protocol::RequestMessage& request)
|
void LspServer::HandleRequest(const protocol::RequestMessage& request)
|
||||||
|
|
@ -204,29 +154,10 @@ namespace lsp
|
||||||
// 检查是否可以处理请求
|
// 检查是否可以处理请求
|
||||||
if (!CanProcessRequest(request.method))
|
if (!CanProcessRequest(request.method))
|
||||||
{
|
{
|
||||||
protocol::ErrorCode error_code;
|
SendStateError(request);
|
||||||
std::string message;
|
|
||||||
|
|
||||||
if (!is_initialized_)
|
|
||||||
{
|
|
||||||
error_code = protocol::ErrorCode::kServerNotInitialized;
|
|
||||||
message = "Server not initialized";
|
|
||||||
}
|
|
||||||
else if (is_shutting_down_)
|
|
||||||
{
|
|
||||||
error_code = protocol::ErrorCode::kInvalidRequest;
|
|
||||||
message = "Server is shutting down, only 'exit' is allowed";
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
error_code = protocol::ErrorCode::kInternalError;
|
|
||||||
message = "Request not allowed in current state";
|
|
||||||
}
|
|
||||||
|
|
||||||
SendResponse(dispatcher_.BuildErrorResponseMessage(request, error_code, message));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 决定同步还是异步处理
|
// 决定同步还是异步处理
|
||||||
if (RequiresSyncProcessing(request.method))
|
if (RequiresSyncProcessing(request.method))
|
||||||
{
|
{
|
||||||
|
|
@ -256,36 +187,19 @@ namespace lsp
|
||||||
spdlog::debug("Processing request method: {}", request.method);
|
spdlog::debug("Processing request method: {}", request.method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void LspServer::HandleNotification(const protocol::NotificationMessage& notification)
|
void LspServer::HandleNotification(const protocol::NotificationMessage& notification)
|
||||||
{
|
{
|
||||||
spdlog::debug("Processing notification - method: {}", notification.method);
|
spdlog::debug("Processing notification - method: {}", notification.method);
|
||||||
|
|
||||||
// 处理 $/ 开头的通知
|
try
|
||||||
if (notification.method.starts_with("$/"))
|
|
||||||
{
|
{
|
||||||
if (notification.method == "$/cancelRequest")
|
dispatcher_.Dispatch(notification);
|
||||||
{
|
|
||||||
HandleCancelRequest(notification);
|
|
||||||
}
|
}
|
||||||
else
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
spdlog::debug("Ignoring protocol-specific notification: {}", notification.method);
|
spdlog::error("Notification processing failed for '{}': {}", notification.method, e.what());
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理标准通知
|
|
||||||
if (notification.method == "initialized")
|
|
||||||
{
|
|
||||||
spdlog::info("Client acknowledged initialization");
|
|
||||||
}
|
|
||||||
else if (notification.method == "exit")
|
|
||||||
{
|
|
||||||
spdlog::info("Exit notification received");
|
|
||||||
is_shutting_down_ = true;
|
|
||||||
// 给一点时间让任务完成
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
std::exit(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -339,15 +253,11 @@ namespace lsp
|
||||||
{
|
{
|
||||||
// 未初始化状态
|
// 未初始化状态
|
||||||
if (!is_initialized_)
|
if (!is_initialized_)
|
||||||
{
|
|
||||||
return method == "initialize" || method == "exit";
|
return method == "initialize" || method == "exit";
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭中状态
|
// 关闭中状态
|
||||||
if (is_shutting_down_)
|
if (is_shutting_down_)
|
||||||
{
|
|
||||||
return method == "exit";
|
return method == "exit";
|
||||||
}
|
|
||||||
|
|
||||||
// 正常状态 - 接受所有请求
|
// 正常状态 - 接受所有请求
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -373,4 +283,21 @@ namespace lsp
|
||||||
spdlog::trace("Response sent - length: {}", byte_length);
|
spdlog::trace("Response sent - length: {}", byte_length);
|
||||||
spdlog::trace("Response sent - body: {}", response);
|
spdlog::trace("Response sent - body: {}", response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LspServer::SendError(const protocol::RequestMessage& request, protocol::ErrorCode code, const std::string& message)
|
||||||
|
{
|
||||||
|
spdlog::warn("Sending error response - method: {}, code: {}, message: {}", request.method, static_cast<int>(code), message);
|
||||||
|
std::string error_response = dispatcher_.BuildErrorResponseMessage(request, code, message);
|
||||||
|
SendResponse(error_response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LspServer::SendStateError(const protocol::RequestMessage& request)
|
||||||
|
{
|
||||||
|
if (!is_initialized_)
|
||||||
|
SendError(request, protocol::ErrorCode::kServerNotInitialized, "Server not initialized");
|
||||||
|
else if (is_shutting_down_)
|
||||||
|
SendError(request, protocol::ErrorCode::kInvalidRequest, "Server is shutting down, only 'exit' is allowed");
|
||||||
|
else
|
||||||
|
SendError(request, protocol::ErrorCode::kInternalError, "Request not allowed in current state");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,10 +3,10 @@
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "./dispacther.hpp"
|
#include "./dispacther.hpp"
|
||||||
#include "./request_scheduler.hpp"
|
#include "../scheduler/request_scheduler.hpp"
|
||||||
#include "../provider/base/provider_registry.hpp"
|
#include "../provider/base/provider_registry.hpp"
|
||||||
|
|
||||||
namespace lsp
|
namespace lsp::core
|
||||||
{
|
{
|
||||||
class LspServer
|
class LspServer
|
||||||
{
|
{
|
||||||
|
|
@ -42,9 +42,13 @@ namespace lsp
|
||||||
// 处理取消请求
|
// 处理取消请求
|
||||||
void HandleCancelRequest(const protocol::NotificationMessage& notification);
|
void HandleCancelRequest(const protocol::NotificationMessage& notification);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SendError(const protocol::RequestMessage& request, protocol::ErrorCode code, const std::string& message);
|
||||||
|
void SendStateError(const protocol::RequestMessage& request);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RequestDispatcher dispatcher_;
|
RequestDispatcher dispatcher_;
|
||||||
RequestScheduler scheduler_;
|
scheduler::RequestScheduler scheduler_;
|
||||||
|
|
||||||
std::atomic<bool> is_initialized_ = false;
|
std::atomic<bool> is_initialized_ = false;
|
||||||
std::atomic<bool> is_shutting_down_ = false;
|
std::atomic<bool> is_shutting_down_ = false;
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
#include <mutex>
|
|
||||||
#include "./dispacther.hpp"
|
|
||||||
#include "../protocol/transform/facade.hpp"
|
|
||||||
|
|
||||||
namespace lsp
|
|
||||||
{
|
|
||||||
void RequestDispatcher::RegisterProvider(std::shared_ptr<providers::ILspProvider> provider)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::shared_mutex> lock(providers_mutex_);
|
|
||||||
std::string method = provider->GetMethod();
|
|
||||||
|
|
||||||
// 如果是生命周期感知的 Provider,设置回调
|
|
||||||
if (auto lifecycle_aware = std::dynamic_pointer_cast<providers::ILifecycleAwareProvider>(provider))
|
|
||||||
{
|
|
||||||
lifecycle_aware->SetLifecycleCallback(
|
|
||||||
[this](ServerLifecycleEvent event) {
|
|
||||||
NotifyAllLifecycleListeners(event);
|
|
||||||
});
|
|
||||||
spdlog::debug("Registered lifecycle-aware provider for method: {}", method);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
spdlog::debug("Registered standard provider for method: {}", method);
|
|
||||||
}
|
|
||||||
providers_[method] = provider;
|
|
||||||
spdlog::debug("Registered provider for method: {}", method);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RequestDispatcher::RegisterLifecycleCallback(LifecycleCallback callback)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(callbacks_mutex_);
|
|
||||||
lifecycle_callbacks_.push_back(std::move(callback));
|
|
||||||
spdlog::debug("Registered lifecycle callback, total callbacks: {}", lifecycle_callbacks_.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string RequestDispatcher::Dispatch(const protocol::RequestMessage& request)
|
|
||||||
{
|
|
||||||
std::shared_lock<std::shared_mutex> lock(providers_mutex_);
|
|
||||||
auto it = providers_.find(request.method);
|
|
||||||
if (it != providers_.end())
|
|
||||||
{
|
|
||||||
auto provider = it->second;
|
|
||||||
lock.unlock();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return provider->ProvideResponse(request);
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
spdlog::error("Provider error for method {}: {}", request.method, e.what());
|
|
||||||
return BuildErrorResponseMessage(request, protocol::ErrorCode::kInternalError, e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return HandleUnknownMethod(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RequestDispatcher::SupportsMethod(const std::string& method) const
|
|
||||||
{
|
|
||||||
std::shared_lock<std::shared_mutex> lock(providers_mutex_);
|
|
||||||
return providers_.find(method) != providers_.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> RequestDispatcher::GetSupportedMethods() const
|
|
||||||
{
|
|
||||||
std::shared_lock<std::shared_mutex> lock(providers_mutex_);
|
|
||||||
std::vector<std::string> methods;
|
|
||||||
methods.reserve(providers_.size());
|
|
||||||
for (const auto& [method, _] : providers_)
|
|
||||||
{
|
|
||||||
methods.push_back(method);
|
|
||||||
}
|
|
||||||
return methods;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RequestDispatcher::NotifyAllLifecycleListeners(ServerLifecycleEvent event)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(callbacks_mutex_);
|
|
||||||
|
|
||||||
std::string event_name;
|
|
||||||
switch (event)
|
|
||||||
{
|
|
||||||
case ServerLifecycleEvent::kInitializing:
|
|
||||||
event_name = "Initializing";
|
|
||||||
break;
|
|
||||||
case ServerLifecycleEvent::kInitialized:
|
|
||||||
event_name = "Initialized";
|
|
||||||
break;
|
|
||||||
case ServerLifecycleEvent::kInitializeFailed:
|
|
||||||
event_name = "InitializeFailed";
|
|
||||||
break;
|
|
||||||
case ServerLifecycleEvent::kShuttingDown:
|
|
||||||
event_name = "ShuttingDown";
|
|
||||||
break;
|
|
||||||
case ServerLifecycleEvent::kShutdown:
|
|
||||||
event_name = "Shutdown";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
spdlog::info("Lifecycle event: {}", event_name);
|
|
||||||
|
|
||||||
for (const auto& callback : lifecycle_callbacks_)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
callback(event);
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
spdlog::error("Lifecycle callback error: {}", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string RequestDispatcher::HandleUnknownMethod(const protocol::RequestMessage& request)
|
|
||||||
{
|
|
||||||
return BuildErrorResponseMessage(request, protocol::ErrorCode::kMethodNotFound, "Method not found: " + request.method);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string RequestDispatcher::BuildErrorResponseMessage(const protocol::RequestMessage& request, protocol::ErrorCode code, const std::string& message)
|
|
||||||
{
|
|
||||||
return providers::ILspProvider::BuildErrorResponseMessage(request, code, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include <shared_mutex>
|
|
||||||
#include "../protocol/protocol.hpp"
|
|
||||||
#include "../provider/base/provider_interface.hpp"
|
|
||||||
|
|
||||||
namespace lsp
|
|
||||||
{
|
|
||||||
|
|
||||||
using ServerLifecycleEvent = providers::ServerLifecycleEvent;
|
|
||||||
using LifecycleCallback = providers::LifecycleCallback;
|
|
||||||
|
|
||||||
class RequestDispatcher
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RequestDispatcher() = default;
|
|
||||||
|
|
||||||
void RegisterProvider(std::shared_ptr<providers::ILspProvider> provider);
|
|
||||||
void RegisterLifecycleCallback(LifecycleCallback callback);
|
|
||||||
std::string Dispatch(const protocol::RequestMessage& request);
|
|
||||||
bool SupportsMethod(const std::string& method) const;
|
|
||||||
std::vector<std::string> GetSupportedMethods() const;
|
|
||||||
|
|
||||||
std::string BuildErrorResponseMessage(const protocol::RequestMessage& request, protocol::ErrorCode code, const std::string& message);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void NotifyAllLifecycleListeners(ServerLifecycleEvent event);
|
|
||||||
std::string HandleUnknownMethod(const protocol::RequestMessage& request);
|
|
||||||
|
|
||||||
private:
|
|
||||||
mutable std::shared_mutex providers_mutex_;
|
|
||||||
std::unordered_map<std::string, std::shared_ptr<providers::ILspProvider>> providers_;
|
|
||||||
|
|
||||||
std::mutex callbacks_mutex_;
|
|
||||||
std::vector<LifecycleCallback> lifecycle_callbacks_;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include <spdlog/sinks/stdout_sinks.h>
|
#include <spdlog/sinks/stdout_sinks.h>
|
||||||
#include <spdlog/sinks/basic_file_sink.h>
|
#include <spdlog/sinks/basic_file_sink.h>
|
||||||
#include "./lsp/server.hpp"
|
#include "./core/server.hpp"
|
||||||
#include "./utils/args_parser.hpp"
|
#include "./utils/args_parser.hpp"
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
|
|
@ -23,7 +21,7 @@ int main(int argc, char* argv[])
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
spdlog::info("TSL-LSP server starting...");
|
spdlog::info("TSL-LSP server starting...");
|
||||||
lsp::LspServer server(config.thread_count);
|
lsp::core::LspServer server(config.thread_count);
|
||||||
server.Run();
|
server.Run();
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,12 @@ namespace lsp::transform
|
||||||
{
|
{
|
||||||
|
|
||||||
// ===== 全局便利函数 =====
|
// ===== 全局便利函数 =====
|
||||||
|
template<typename T>
|
||||||
|
std::optional<T> Deserialize(const std::string& json);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::optional<std::string> Serialize(const T& obj);
|
||||||
|
|
||||||
// 基本类型
|
// 基本类型
|
||||||
template<typename T>
|
template<typename T>
|
||||||
protocol::LSPAny LSPAny(const T& obj);
|
protocol::LSPAny LSPAny(const T& obj);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,28 @@
|
||||||
|
|
||||||
namespace lsp::transform
|
namespace lsp::transform
|
||||||
{
|
{
|
||||||
|
template<typename T>
|
||||||
|
inline std::optional<T> Deserialize(const std::string& json)
|
||||||
|
{
|
||||||
|
T obj;
|
||||||
|
auto ce = glz::read_json(obj, json);
|
||||||
|
if (ce)
|
||||||
|
return std::nullopt;
|
||||||
|
else
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::optional<std::string> Serialize(const T& obj)
|
||||||
|
{
|
||||||
|
std::string json;
|
||||||
|
auto ce = glz::write_json(obj, json);
|
||||||
|
if (ce)
|
||||||
|
return std::nullopt;
|
||||||
|
else
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline protocol::LSPAny LSPAny(const T& obj)
|
inline protocol::LSPAny LSPAny(const T& obj)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
#include "./provider_interface.hpp"
|
#include "./provider_interface.hpp"
|
||||||
|
|
||||||
namespace lsp::providers
|
namespace lsp::providers
|
||||||
{
|
{
|
||||||
|
|
||||||
std::string ILspProvider::BuildErrorResponseMessage(const protocol::RequestMessage& request, protocol::ErrorCode code, const std::string& message)
|
std::string IRequestProvider::BuildErrorResponseMessage(const protocol::RequestMessage& request, protocol::ErrorCode code, const std::string& message)
|
||||||
{
|
{
|
||||||
protocol::ResponseMessage response;
|
protocol::ResponseMessage response;
|
||||||
response.id = request.id;
|
response.id = request.id;
|
||||||
|
|
@ -23,18 +22,4 @@ namespace lsp::providers
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ILifecycleAwareProvider::SetLifecycleCallback(LifecycleCallback callback)
|
|
||||||
{
|
|
||||||
lifecycle_callback_ = std::move(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ILifecycleAwareProvider::NotifyLifecycleEvent(ServerLifecycleEvent event)
|
|
||||||
{
|
|
||||||
if (lifecycle_callback_)
|
|
||||||
{
|
|
||||||
lifecycle_callback_(event);
|
|
||||||
spdlog::debug("Provider {} triggered event: {}", GetProviderName(), static_cast<int>(event));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
#include "../../protocol/protocol.hpp"
|
#include "../../protocol/protocol.hpp"
|
||||||
|
#include "../../scheduler/request_scheduler.hpp"
|
||||||
|
|
||||||
namespace lsp::providers
|
namespace lsp::providers
|
||||||
{
|
{
|
||||||
|
|
@ -15,37 +17,57 @@ namespace lsp::providers
|
||||||
|
|
||||||
using LifecycleCallback = std::function<void(ServerLifecycleEvent)>;
|
using LifecycleCallback = std::function<void(ServerLifecycleEvent)>;
|
||||||
|
|
||||||
|
class ProviderContext;
|
||||||
|
|
||||||
// LSP请求提供者接口基类
|
// LSP请求提供者接口基类
|
||||||
class ILspProvider
|
class IProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~ILspProvider() = default;
|
virtual ~IProvider() = default;
|
||||||
|
|
||||||
// 处理LSP请求
|
|
||||||
virtual std::string ProvideResponse(const protocol::RequestMessage& request) = 0;
|
|
||||||
// 获取支持的LSP方法名
|
// 获取支持的LSP方法名
|
||||||
virtual std::string GetMethod() const = 0;
|
virtual std::string GetMethod() const = 0;
|
||||||
// 获取提供者名称(用于日志和调试)
|
// 获取提供者名称(用于日志和调试)
|
||||||
virtual std::string GetProviderName() const = 0;
|
virtual std::string GetProviderName() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// LSP 请求处理器接口
|
||||||
|
class IRequestProvider : public IProvider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IRequestProvider() = default;
|
||||||
|
|
||||||
|
// 处理LSP请求
|
||||||
|
virtual std::string ProvideResponse(const protocol::RequestMessage& request, ProviderContext& context) = 0;
|
||||||
static std::string BuildErrorResponseMessage(const protocol::RequestMessage& request, protocol::ErrorCode code, const std::string& message);
|
static std::string BuildErrorResponseMessage(const protocol::RequestMessage& request, protocol::ErrorCode code, const std::string& message);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 生命周期感知的 Provider 接口
|
// LSP 通知处理器接口
|
||||||
class ILifecycleAwareProvider : public ILspProvider
|
class INotificationProvider : public IProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~ILifecycleAwareProvider() = default;
|
virtual ~INotificationProvider() = default;
|
||||||
|
|
||||||
// 设置生命周期回调
|
// 处理LSP通知
|
||||||
void SetLifecycleCallback(LifecycleCallback callback);
|
virtual void HandleNotification(const protocol::NotificationMessage& notification, ProviderContext& context) = 0;
|
||||||
|
|
||||||
protected:
|
|
||||||
// 触发生命周期事件
|
|
||||||
void NotifyLifecycleEvent(ServerLifecycleEvent event);
|
|
||||||
|
|
||||||
private:
|
|
||||||
LifecycleCallback lifecycle_callback_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ProviderContext
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ProviderContext(scheduler::RequestScheduler* scheduler, LifecycleCallback lifecycle_callback) :
|
||||||
|
scheduler_(scheduler), lifecycle_callback_(lifecycle_callback) {}
|
||||||
|
|
||||||
|
scheduler::RequestScheduler* GetScheduler() const { return scheduler_; }
|
||||||
|
|
||||||
|
void TriggerLifecycleEvent(ServerLifecycleEvent event) const
|
||||||
|
{
|
||||||
|
if (lifecycle_callback_)
|
||||||
|
lifecycle_callback_(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
scheduler::RequestScheduler* scheduler_;
|
||||||
|
LifecycleCallback lifecycle_callback_;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,13 @@
|
||||||
#include "../text_document/completion_provider.hpp"
|
#include "../text_document/completion_provider.hpp"
|
||||||
#include "../trace/set_trace_provider.hpp"
|
#include "../trace/set_trace_provider.hpp"
|
||||||
#include "../shutdown/shutdown_provider.hpp"
|
#include "../shutdown/shutdown_provider.hpp"
|
||||||
|
#include "../cancel_request/cancel_request_provider.hpp"
|
||||||
|
#include "../exit/exit_provider.hpp"
|
||||||
|
|
||||||
namespace lsp::providers
|
namespace lsp::providers
|
||||||
{
|
{
|
||||||
|
|
||||||
void RegisterAllProviders(RequestDispatcher& dispatcher)
|
void RegisterAllProviders(core::RequestDispatcher& dispatcher)
|
||||||
{
|
{
|
||||||
spdlog::info("Registering LSP providers...");
|
spdlog::info("Registering LSP providers...");
|
||||||
|
|
||||||
|
|
@ -20,10 +22,12 @@ namespace lsp::providers
|
||||||
RegisterProvider<text_document::DidOpenProvider>(dispatcher);
|
RegisterProvider<text_document::DidOpenProvider>(dispatcher);
|
||||||
RegisterProvider<text_document::DidChangeProvider>(dispatcher);
|
RegisterProvider<text_document::DidChangeProvider>(dispatcher);
|
||||||
RegisterProvider<text_document::CompletionProvider>(dispatcher);
|
RegisterProvider<text_document::CompletionProvider>(dispatcher);
|
||||||
RegisterProvider<trace::SetTraceProvider>(dispatcher);
|
RegisterProvider<set_trace::SetTraceProvider>(dispatcher);
|
||||||
RegisterProvider<shutdown::ShutdownProvider>(dispatcher);
|
RegisterProvider<shutdown::ShutdownProvider>(dispatcher);
|
||||||
|
RegisterProvider<cancel_request::CancelRequestProvider>(dispatcher);
|
||||||
|
RegisterProvider<exit::ExitProvider>(dispatcher);
|
||||||
|
|
||||||
spdlog::info("Successfully registered {} LSP providers", dispatcher.GetSupportedMethods().size());
|
spdlog::info("Successfully registered {} LSP providers", dispatcher.GetAllSupportedMethods().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,32 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include "../../lsp/dispacther.hpp"
|
#include <type_traits>
|
||||||
|
#include "../../core/dispacther.hpp"
|
||||||
#include "./provider_interface.hpp"
|
#include "./provider_interface.hpp"
|
||||||
|
|
||||||
namespace lsp::providers
|
namespace lsp::providers
|
||||||
{
|
{
|
||||||
// 模板函数:注册provider
|
// 注册请求处理器的模板函数
|
||||||
template<typename ProviderClass>
|
template<typename ProviderClass>
|
||||||
void RegisterProvider(RequestDispatcher& dispatcher)
|
typename std::enable_if_t<std::is_base_of_v<IRequestProvider, ProviderClass>>
|
||||||
|
RegisterProvider(core::RequestDispatcher& dispatcher)
|
||||||
{
|
{
|
||||||
static_assert(std::is_base_of_v<ILspProvider, ProviderClass>,
|
|
||||||
"Provider must inherit from ILspProvider");
|
|
||||||
|
|
||||||
auto provider = std::make_shared<ProviderClass>();
|
auto provider = std::make_shared<ProviderClass>();
|
||||||
dispatcher.RegisterProvider(provider);
|
dispatcher.RegisterRequestProvider(provider);
|
||||||
|
spdlog::info("Registered request provider '{}' for method: {}", provider->GetProviderName(), provider->GetMethod());
|
||||||
|
}
|
||||||
|
|
||||||
spdlog::info("Registering {} for method: {}", provider->GetProviderName(), provider->GetMethod());
|
// 注册通知处理器的模板函数
|
||||||
|
template<typename ProviderClass>
|
||||||
|
typename std::enable_if_t<std::is_base_of_v<INotificationProvider, ProviderClass>>
|
||||||
|
RegisterProvider(core::RequestDispatcher& dispatcher)
|
||||||
|
{
|
||||||
|
auto provider = std::make_shared<ProviderClass>();
|
||||||
|
dispatcher.RegisterNotificationProvider(provider);
|
||||||
|
spdlog::info("Registered notification provider '{}' for method: {}", provider->GetProviderName(), provider->GetMethod());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量注册provider
|
// 批量注册provider
|
||||||
void RegisterAllProviders(RequestDispatcher& dispatcher);
|
void RegisterAllProviders(core::RequestDispatcher& dispatcher);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
#include "./cancel_request_provider.hpp"
|
||||||
|
|
||||||
|
namespace lsp::providers::cancel_request
|
||||||
|
{
|
||||||
|
std::string CancelRequestProvider::GetMethod() const
|
||||||
|
{
|
||||||
|
return "$/cancelRequest";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CancelRequestProvider::GetProviderName() const
|
||||||
|
{
|
||||||
|
return "CancelRequestProvider";
|
||||||
|
}
|
||||||
|
|
||||||
|
void CancelRequestProvider::HandleNotification(const protocol::NotificationMessage& notification, ProviderContext& context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto params = transform::As<protocol::CancelParams>(notification.params.value());
|
||||||
|
std::string id_to_cancel = transform::debug::GetIdString(params.id);
|
||||||
|
spdlog::info("Processing cancel request for ID: {}", id_to_cancel);
|
||||||
|
|
||||||
|
if (auto* scheduler = context.GetScheduler())
|
||||||
|
{
|
||||||
|
bool cancelled = scheduler->Cancel(id_to_cancel);
|
||||||
|
spdlog::info("Cancel request {} result: {}", id_to_cancel, cancelled ? "success" : "not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
spdlog::error("Error handling cancel request: {}", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include "../base/provider_interface.hpp"
|
||||||
|
#include "../../scheduler/request_scheduler.hpp"
|
||||||
|
#include "../../protocol/transform/facade.hpp"
|
||||||
|
|
||||||
|
namespace lsp::providers::cancel_request
|
||||||
|
{
|
||||||
|
class CancelRequestProvider : public INotificationProvider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string GetMethod() const override;
|
||||||
|
std::string GetProviderName() const override;
|
||||||
|
void HandleNotification(const protocol::NotificationMessage& notification, ProviderContext& context) override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
#include "./exit_provider.hpp"
|
||||||
|
|
||||||
|
namespace lsp::providers::exit
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string ExitProvider::GetMethod() const
|
||||||
|
{
|
||||||
|
return "exit";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ExitProvider::GetProviderName() const
|
||||||
|
{
|
||||||
|
return "ExitProvider";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExitProvider::HandleNotification(const protocol::NotificationMessage& notification, ProviderContext& context)
|
||||||
|
{
|
||||||
|
// static_cast<void>(context);
|
||||||
|
spdlog::debug("ExitProvider: Providing response for method {}", notification.method);
|
||||||
|
spdlog::info("Exit notification received");
|
||||||
|
|
||||||
|
// 触发生命周期事件
|
||||||
|
context.TriggerLifecycleEvent(ServerLifecycleEvent::kShuttingDown);
|
||||||
|
|
||||||
|
// 给一些时间完成清理
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
context.TriggerLifecycleEvent(ServerLifecycleEvent::kShutdown);
|
||||||
|
|
||||||
|
std::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
#pragma once
|
||||||
|
#include "../base/provider_interface.hpp"
|
||||||
|
|
||||||
|
namespace lsp::providers::exit
|
||||||
|
{
|
||||||
|
class ExitProvider : public INotificationProvider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string GetMethod() const override;
|
||||||
|
std::string GetProviderName() const override;
|
||||||
|
void HandleNotification(const protocol::NotificationMessage& notification, ProviderContext& context) override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,17 @@
|
||||||
|
|
||||||
namespace lsp::providers::initialize
|
namespace lsp::providers::initialize
|
||||||
{
|
{
|
||||||
std::string InitializeProvider::ProvideResponse(const protocol::RequestMessage& request)
|
std::string InitializeProvider::GetMethod() const
|
||||||
|
{
|
||||||
|
return "initialize";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string InitializeProvider::GetProviderName() const
|
||||||
|
{
|
||||||
|
return "InitializeProvider";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string InitializeProvider::ProvideResponse(const protocol::RequestMessage& request, ProviderContext& context)
|
||||||
{
|
{
|
||||||
spdlog::debug("InitializeProvider: Providing response for method {}", request.method);
|
spdlog::debug("InitializeProvider: Providing response for method {}", request.method);
|
||||||
protocol::ResponseMessage response;
|
protocol::ResponseMessage response;
|
||||||
|
|
@ -14,23 +24,13 @@ namespace lsp::providers::initialize
|
||||||
auto ec = glz::write_json(response, json);
|
auto ec = glz::write_json(response, json);
|
||||||
if (ec)
|
if (ec)
|
||||||
{
|
{
|
||||||
NotifyLifecycleEvent(ServerLifecycleEvent::kInitializeFailed);
|
context.TriggerLifecycleEvent(ServerLifecycleEvent::kInitializeFailed);
|
||||||
return BuildErrorResponseMessage(request, protocol::ErrorCode::kInternalError, "Internal error");
|
return BuildErrorResponseMessage(request, protocol::ErrorCode::kInternalError, "Internal error");
|
||||||
}
|
}
|
||||||
NotifyLifecycleEvent(ServerLifecycleEvent::kInitialized);
|
context.TriggerLifecycleEvent(ServerLifecycleEvent::kInitialized);
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string InitializeProvider::GetMethod() const
|
|
||||||
{
|
|
||||||
return "initialize";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string InitializeProvider::GetProviderName() const
|
|
||||||
{
|
|
||||||
return "InitializeProvider";
|
|
||||||
}
|
|
||||||
|
|
||||||
protocol::InitializeResult InitializeProvider::BuildInitializeResult()
|
protocol::InitializeResult InitializeProvider::BuildInitializeResult()
|
||||||
{
|
{
|
||||||
protocol::InitializeResult result;
|
protocol::InitializeResult result;
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,11 @@
|
||||||
namespace lsp::providers::initialize
|
namespace lsp::providers::initialize
|
||||||
{
|
{
|
||||||
using namespace lsp;
|
using namespace lsp;
|
||||||
class InitializeProvider : public ILifecycleAwareProvider
|
class InitializeProvider : public IRequestProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InitializeProvider() = default;
|
InitializeProvider() = default;
|
||||||
std::string ProvideResponse(const protocol::RequestMessage& request) override;
|
std::string ProvideResponse(const protocol::RequestMessage& request, ProviderContext& context) override;
|
||||||
std::string GetMethod() const override;
|
std::string GetMethod() const override;
|
||||||
std::string GetProviderName() const override;
|
std::string GetProviderName() const override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,6 @@
|
||||||
namespace lsp::providers::initialized
|
namespace lsp::providers::initialized
|
||||||
{
|
{
|
||||||
|
|
||||||
std::string InitializedProvider::ProvideResponse(const protocol::RequestMessage& request)
|
|
||||||
{
|
|
||||||
spdlog::debug("InitializeProvider: Providing response for method {}", request.method);
|
|
||||||
std::string json;
|
|
||||||
glz::obj empty_obj{}; // glaze的对象类型
|
|
||||||
auto ec = glz::write_json(empty_obj, json);
|
|
||||||
return ec ? BuildErrorResponseMessage(request, protocol::ErrorCode::kInternalError, "Internal error") : json;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string InitializedProvider::GetMethod() const
|
std::string InitializedProvider::GetMethod() const
|
||||||
{
|
{
|
||||||
return "initialized";
|
return "initialized";
|
||||||
|
|
@ -23,4 +14,10 @@ namespace lsp::providers::initialized
|
||||||
return "InitializedProvider";
|
return "InitializedProvider";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InitializedProvider::HandleNotification(const protocol::NotificationMessage& notification, ProviderContext& context)
|
||||||
|
{
|
||||||
|
static_cast<void>(context); // 如果不需要上下文,可以忽略
|
||||||
|
spdlog::debug("InitializeProvider: Providing response for method {}", notification.method);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
|
|
||||||
namespace lsp::providers::initialized
|
namespace lsp::providers::initialized
|
||||||
{
|
{
|
||||||
class InitializedProvider : public ILifecycleAwareProvider
|
class InitializedProvider : public INotificationProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InitializedProvider() = default;
|
InitializedProvider() = default;
|
||||||
std::string ProvideResponse(const protocol::RequestMessage& request) override;
|
|
||||||
std::string GetMethod() const override;
|
std::string GetMethod() const override;
|
||||||
std::string GetProviderName() const override;
|
std::string GetProviderName() const override;
|
||||||
|
void HandleNotification(const protocol::NotificationMessage& notification, ProviderContext& context) override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,24 @@
|
||||||
|
|
||||||
namespace lsp::providers::shutdown
|
namespace lsp::providers::shutdown
|
||||||
{
|
{
|
||||||
std::string ShutdownProvider::ProvideResponse(const protocol::RequestMessage& request)
|
std::string ShutdownProvider::GetMethod() const
|
||||||
|
{
|
||||||
|
return "shutdown";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ShutdownProvider::GetProviderName() const
|
||||||
|
{
|
||||||
|
return "ShutdownProvider";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ShutdownProvider::ProvideResponse(const protocol::RequestMessage& request, ProviderContext& context)
|
||||||
{
|
{
|
||||||
spdlog::debug("ShutdownProvider: Providing response for method {}", request.method);
|
spdlog::debug("ShutdownProvider: Providing response for method {}", request.method);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 触发关闭事件
|
// 触发关闭事件
|
||||||
NotifyLifecycleEvent(ServerLifecycleEvent::kShuttingDown);
|
context.TriggerLifecycleEvent(ServerLifecycleEvent::kShuttingDown);
|
||||||
|
|
||||||
// 构建响应 - shutdown 返回 null
|
// 构建响应 - shutdown 返回 null
|
||||||
protocol::ResponseMessage response;
|
protocol::ResponseMessage response;
|
||||||
|
|
@ -34,13 +44,4 @@ namespace lsp::providers::shutdown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ShutdownProvider::GetMethod() const
|
|
||||||
{
|
|
||||||
return "shutdown";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ShutdownProvider::GetProviderName() const
|
|
||||||
{
|
|
||||||
return "ShutdownProvider";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
||||||
|
|
||||||
namespace lsp::providers::shutdown
|
namespace lsp::providers::shutdown
|
||||||
{
|
{
|
||||||
class ShutdownProvider : public ILifecycleAwareProvider
|
class ShutdownProvider : public IRequestProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ShutdownProvider() = default;
|
ShutdownProvider() = default;
|
||||||
|
|
||||||
std::string ProvideResponse(const protocol::RequestMessage& request) override;
|
|
||||||
std::string GetMethod() const override;
|
std::string GetMethod() const override;
|
||||||
std::string GetProviderName() const override;
|
std::string GetProviderName() const override;
|
||||||
|
std::string ProvideResponse(const protocol::RequestMessage& request, ProviderContext& context) override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,25 @@
|
||||||
|
|
||||||
namespace lsp::providers::text_document
|
namespace lsp::providers::text_document
|
||||||
{
|
{
|
||||||
std::string CompletionProvider::ProvideResponse(const protocol::RequestMessage& request)
|
std::string CompletionProvider::GetMethod() const
|
||||||
{
|
{
|
||||||
|
return "textDocument/completion";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CompletionProvider::GetProviderName() const
|
||||||
|
{
|
||||||
|
return "CompletionProvider";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CompletionProvider::ProvideResponse(const protocol::RequestMessage& request, ProviderContext& context)
|
||||||
|
{
|
||||||
|
static_cast<void>(context);
|
||||||
spdlog::debug("CompletionProvider: Providing response for method {}", request.method);
|
spdlog::debug("CompletionProvider: Providing response for method {}", request.method);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 验证请求是否包含参数
|
// 验证请求是否包含参数
|
||||||
if (!request.params.has_value()) {
|
if (!request.params.has_value())
|
||||||
|
{
|
||||||
spdlog::warn("{}: Missing params in request", GetProviderName());
|
spdlog::warn("{}: Missing params in request", GetProviderName());
|
||||||
return BuildErrorResponseMessage(request, protocol::ErrorCode::kInvalidParams, "Missing params");
|
return BuildErrorResponseMessage(request, protocol::ErrorCode::kInvalidParams, "Missing params");
|
||||||
}
|
}
|
||||||
|
|
@ -42,15 +54,6 @@ namespace lsp::providers::text_document
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CompletionProvider::GetMethod() const
|
|
||||||
{
|
|
||||||
return "textDocument/completion";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CompletionProvider::GetProviderName() const
|
|
||||||
{
|
|
||||||
return "CompletionProvider";
|
|
||||||
}
|
|
||||||
|
|
||||||
protocol::CompletionList CompletionProvider::BuildCompletionResponse(const protocol::CompletionParams& params)
|
protocol::CompletionList CompletionProvider::BuildCompletionResponse(const protocol::CompletionParams& params)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,14 @@
|
||||||
|
|
||||||
namespace lsp::providers::text_document
|
namespace lsp::providers::text_document
|
||||||
{
|
{
|
||||||
class CompletionProvider : public ILspProvider
|
class CompletionProvider : public IRequestProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CompletionProvider() = default;
|
CompletionProvider() = default;
|
||||||
|
|
||||||
std::string ProvideResponse(const protocol::RequestMessage& request) override;
|
|
||||||
std::string GetMethod() const override;
|
std::string GetMethod() const override;
|
||||||
std::string GetProviderName() const override;
|
std::string GetProviderName() const override;
|
||||||
|
std::string ProvideResponse(const protocol::RequestMessage& request, ProviderContext& context) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// 构建完整的补全响应
|
// 构建完整的补全响应
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,9 @@
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include "./did_change_provider.hpp"
|
#include "./did_change_provider.hpp"
|
||||||
#include "./did_open_provider.hpp"
|
|
||||||
|
|
||||||
namespace lsp::providers::text_document
|
namespace lsp::providers::text_document
|
||||||
{
|
{
|
||||||
|
|
||||||
std::string DidChangeProvider::ProvideResponse(const protocol::RequestMessage& request)
|
|
||||||
{
|
|
||||||
spdlog::debug("DidChangeProvider: Providing response for method {}", request.method);
|
|
||||||
std::string json;
|
|
||||||
glz::obj empty_obj{}; // glaze的对象类型
|
|
||||||
auto ec = glz::write_json(empty_obj, json);
|
|
||||||
return ec ? BuildErrorResponseMessage(request, protocol::ErrorCode::kInternalError, "Internal error") : json;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string DidChangeProvider::GetMethod() const
|
std::string DidChangeProvider::GetMethod() const
|
||||||
{
|
{
|
||||||
return "textDocument/didChange";
|
return "textDocument/didChange";
|
||||||
|
|
@ -24,4 +14,10 @@ namespace lsp::providers::text_document
|
||||||
return "DidChangeProvider";
|
return "DidChangeProvider";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DidChangeProvider::HandleNotification(const protocol::NotificationMessage& notification, ProviderContext& context)
|
||||||
|
{
|
||||||
|
static_cast<void>(context);
|
||||||
|
spdlog::debug("DidChangeProvider: Providing response for method {}", notification.method);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@
|
||||||
|
|
||||||
namespace lsp::providers::text_document
|
namespace lsp::providers::text_document
|
||||||
{
|
{
|
||||||
class DidChangeProvider : public ILspProvider
|
class DidChangeProvider : public INotificationProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DidChangeProvider() = default;
|
DidChangeProvider() = default;
|
||||||
std::string ProvideResponse(const protocol::RequestMessage& request) override;
|
|
||||||
std::string GetMethod() const override;
|
std::string GetMethod() const override;
|
||||||
std::string GetProviderName() const override;
|
std::string GetProviderName() const override;
|
||||||
|
void HandleNotification(const protocol::NotificationMessage& notification, ProviderContext& context) override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,6 @@
|
||||||
|
|
||||||
namespace lsp::providers::text_document
|
namespace lsp::providers::text_document
|
||||||
{
|
{
|
||||||
std::string DidOpenProvider::ProvideResponse(const protocol::RequestMessage& request)
|
|
||||||
{
|
|
||||||
spdlog::debug("DidOpenProvider: Providing response for method {}", request.method);
|
|
||||||
std::string json;
|
|
||||||
glz::obj empty_obj{}; // glaze的对象类型
|
|
||||||
auto ec = glz::write_json(empty_obj, json);
|
|
||||||
return ec ? BuildErrorResponseMessage(request, protocol::ErrorCode::kInternalError, "Internal error") : json;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string DidOpenProvider::GetMethod() const
|
std::string DidOpenProvider::GetMethod() const
|
||||||
{
|
{
|
||||||
return "textDocument/didOpen";
|
return "textDocument/didOpen";
|
||||||
|
|
@ -22,4 +13,10 @@ namespace lsp::providers::text_document
|
||||||
return "DidOpenProvider";
|
return "DidOpenProvider";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DidOpenProvider::HandleNotification(const protocol::NotificationMessage& notification, ProviderContext& context)
|
||||||
|
{
|
||||||
|
static_cast<void>(context);
|
||||||
|
spdlog::debug("DidOpenProvider: Providing response for method {}", notification.method);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@
|
||||||
|
|
||||||
namespace lsp::providers::text_document
|
namespace lsp::providers::text_document
|
||||||
{
|
{
|
||||||
class DidOpenProvider : public ILspProvider
|
class DidOpenProvider : public INotificationProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DidOpenProvider() = default;
|
DidOpenProvider() = default;
|
||||||
std::string ProvideResponse(const protocol::RequestMessage& request) override;
|
|
||||||
std::string GetMethod() const override;
|
std::string GetMethod() const override;
|
||||||
std::string GetProviderName() const override;
|
std::string GetProviderName() const override;
|
||||||
|
void HandleNotification(const protocol::NotificationMessage& notification, ProviderContext& context) override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,8 @@
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include "./set_trace_provider.hpp"
|
#include "./set_trace_provider.hpp"
|
||||||
|
|
||||||
namespace lsp::providers::trace
|
namespace lsp::providers::set_trace
|
||||||
{
|
{
|
||||||
|
|
||||||
std::string SetTraceProvider::ProvideResponse(const protocol::RequestMessage& request)
|
|
||||||
{
|
|
||||||
spdlog::debug("SetTraceProvider: Providing response for method {}", request.method);
|
|
||||||
std::string json;
|
|
||||||
glz::obj empty_obj{}; // glaze的对象类型
|
|
||||||
auto ec = glz::write_json(empty_obj, json);
|
|
||||||
return ec ? BuildErrorResponseMessage(request, protocol::ErrorCode::kInternalError, "Internal error") : json;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string SetTraceProvider::GetMethod() const
|
std::string SetTraceProvider::GetMethod() const
|
||||||
{
|
{
|
||||||
return "$/setTrace";
|
return "$/setTrace";
|
||||||
|
|
@ -23,4 +13,10 @@ namespace lsp::providers::trace
|
||||||
return "SetTraceProvider";
|
return "SetTraceProvider";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetTraceProvider::HandleNotification(const protocol::NotificationMessage& notification, ProviderContext& context)
|
||||||
|
{
|
||||||
|
static_cast<void>(context);
|
||||||
|
spdlog::debug("SetTraceProvider: Providing response for method {}", notification.method);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "../base/provider_interface.hpp"
|
#include "../base/provider_interface.hpp"
|
||||||
|
|
||||||
namespace lsp::providers::trace
|
namespace lsp::providers::set_trace
|
||||||
{
|
{
|
||||||
class SetTraceProvider : public ILspProvider
|
class SetTraceProvider : public INotificationProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SetTraceProvider() = default;
|
SetTraceProvider() = default;
|
||||||
std::string ProvideResponse(const protocol::RequestMessage& request) override;
|
|
||||||
std::string GetMethod() const override;
|
std::string GetMethod() const override;
|
||||||
std::string GetProviderName() const override;
|
std::string GetProviderName() const override;
|
||||||
|
void HandleNotification(const protocol::NotificationMessage& notification, ProviderContext& context) override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
#include "./request_scheduler.hpp"
|
#include "./request_scheduler.hpp"
|
||||||
|
|
||||||
namespace lsp
|
namespace lsp::scheduler
|
||||||
{
|
{
|
||||||
RequestScheduler::RequestScheduler(size_t concurrency) :
|
RequestScheduler::RequestScheduler(size_t concurrency) :
|
||||||
executor_(concurrency)
|
executor_(concurrency)
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <taskflow/taskflow.hpp>
|
#include <taskflow/taskflow.hpp>
|
||||||
|
|
||||||
namespace lsp
|
namespace lsp::scheduler
|
||||||
{
|
{
|
||||||
class RequestScheduler
|
class RequestScheduler
|
||||||
{
|
{
|
||||||
|
|
@ -0,0 +1,821 @@
|
||||||
|
#include <algorithm>
|
||||||
|
#include "./document.hpp"
|
||||||
|
|
||||||
|
namespace lsp::services
|
||||||
|
{
|
||||||
|
// ===== Document 实现 =====
|
||||||
|
|
||||||
|
Document::Document(const protocol::TextDocumentItem& item) :
|
||||||
|
item_(item), last_modified_time_(std::chrono::system_clock::now())
|
||||||
|
{
|
||||||
|
UpdateInternalState();
|
||||||
|
spdlog::trace("Created document: {} (version {}, {} bytes)", item_.uri, item_.version, item_.text.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::SetContent(int32_t newVersion, const std::string& newText)
|
||||||
|
{
|
||||||
|
item_.version = newVersion;
|
||||||
|
item_.text = newText;
|
||||||
|
is_dirty_ = true;
|
||||||
|
last_modified_time_ = std::chrono::system_clock::now();
|
||||||
|
UpdateInternalState();
|
||||||
|
|
||||||
|
spdlog::trace("Document {} updated to version {} ({} bytes)", item_.uri, item_.version, item_.text.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::ApplyContentChange(protocol::integer version, const std::vector<protocol::TextDocumentContentChangeEvent>& changes)
|
||||||
|
{
|
||||||
|
// 应用所有变更
|
||||||
|
for (const auto& change : changes)
|
||||||
|
{
|
||||||
|
ApplyContentChange(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
item_.version = version;
|
||||||
|
is_dirty_ = true;
|
||||||
|
last_modified_time_ = std::chrono::system_clock::now();
|
||||||
|
UpdateInternalState();
|
||||||
|
|
||||||
|
spdlog::trace("Document {} updated to version {} with {} changes", item_.uri, item_.version, changes.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::ApplyContentChange(const protocol::TextDocumentContentChangeEvent& change)
|
||||||
|
{
|
||||||
|
// 增量更新
|
||||||
|
size_t startOffset = PositionToOffset(change.range.start);
|
||||||
|
size_t endOffset = PositionToOffset(change.range.end);
|
||||||
|
|
||||||
|
// 替换指定范围
|
||||||
|
item_.text = item_.text.substr(0, startOffset) + change.text + item_.text.substr(endOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Document::PositionToOffset(const protocol::Position& position) const
|
||||||
|
{
|
||||||
|
if (position.line >= lines_.size())
|
||||||
|
{
|
||||||
|
return item_.text.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t offset = line_offsets_[position.line];
|
||||||
|
|
||||||
|
// 根据编码计算字符偏移
|
||||||
|
if (encoding_ == protocol::PositionEncodingKindLiterals::UTF8)
|
||||||
|
{
|
||||||
|
// 直接使用字节偏移
|
||||||
|
offset += std::min(static_cast<size_t>(position.character), lines_[position.line].length());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// UTF-16 或 UTF-32
|
||||||
|
offset += CharacterToByteOffset(lines_[position.line], position.character);
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol::Position Document::OffsetToPosition(size_t offset) const
|
||||||
|
{
|
||||||
|
protocol::Position pos;
|
||||||
|
pos.line = 0;
|
||||||
|
pos.character = 0;
|
||||||
|
|
||||||
|
// 二分查找行号
|
||||||
|
auto it = std::upper_bound(line_offsets_.begin(), line_offsets_.end(), offset);
|
||||||
|
if (it != line_offsets_.begin())
|
||||||
|
{
|
||||||
|
--it;
|
||||||
|
pos.line = static_cast<int32_t>(std::distance(line_offsets_.begin(), it));
|
||||||
|
size_t lineOffset = *it;
|
||||||
|
size_t byteOffset = offset - lineOffset;
|
||||||
|
|
||||||
|
// 根据编码计算字符位置
|
||||||
|
if (encoding_ == protocol::PositionEncodingKindLiterals::UTF8)
|
||||||
|
{
|
||||||
|
pos.character = static_cast<int32_t>(byteOffset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos.character = ByteOffsetToCharacter(lines_[pos.line], byteOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Document::GetTextInRange(const protocol::Range& range) const
|
||||||
|
{
|
||||||
|
size_t start = PositionToOffset(range.start);
|
||||||
|
size_t end = PositionToOffset(range.end);
|
||||||
|
|
||||||
|
if (start >= item_.text.length())
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
end = std::min(end, item_.text.length());
|
||||||
|
return item_.text.substr(start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<char> Document::GetCharAt(const protocol::Position& position) const
|
||||||
|
{
|
||||||
|
if (position.line >= lines_.size())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& line = lines_[position.line];
|
||||||
|
size_t byteOffset = CharacterToByteOffset(line, position.character);
|
||||||
|
|
||||||
|
if (byteOffset >= line.length())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return line[byteOffset];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Document::GetLine(size_t lineNumber) const
|
||||||
|
{
|
||||||
|
if (lineNumber < lines_.size())
|
||||||
|
{
|
||||||
|
return lines_[lineNumber];
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Document::GetLineAt(const protocol::Position& position) const
|
||||||
|
{
|
||||||
|
return GetLine(position.line);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Document::GetWordAt(const protocol::Position& position) const
|
||||||
|
{
|
||||||
|
if (position.line >= lines_.size())
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& line = lines_[position.line];
|
||||||
|
size_t bytePos = CharacterToByteOffset(line, position.character);
|
||||||
|
|
||||||
|
// 找到单词边界
|
||||||
|
size_t start = bytePos;
|
||||||
|
while (start > 0 && IsWordChar(line[start - 1]))
|
||||||
|
{
|
||||||
|
--start;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t end = bytePos;
|
||||||
|
while (end < line.length() && IsWordChar(line[end]))
|
||||||
|
{
|
||||||
|
++end;
|
||||||
|
}
|
||||||
|
|
||||||
|
return line.substr(start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol::Range Document::GetWordRangeAt(const protocol::Position& position) const
|
||||||
|
{
|
||||||
|
if (position.line >= lines_.size())
|
||||||
|
{
|
||||||
|
return protocol::Range{ position, position };
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& line = lines_[position.line];
|
||||||
|
size_t bytePos = CharacterToByteOffset(line, position.character);
|
||||||
|
|
||||||
|
// 找到单词边界
|
||||||
|
size_t start = bytePos;
|
||||||
|
while (start > 0 && IsWordChar(line[start - 1]))
|
||||||
|
{
|
||||||
|
--start;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t end = bytePos;
|
||||||
|
while (end < line.length() && IsWordChar(line[end]))
|
||||||
|
{
|
||||||
|
++end;
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol::Range range;
|
||||||
|
range.start.line = position.line;
|
||||||
|
range.start.character = ByteOffsetToCharacter(line, start);
|
||||||
|
range.end.line = position.line;
|
||||||
|
range.end.character = ByteOffsetToCharacter(line, end);
|
||||||
|
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::UpdateInternalState()
|
||||||
|
{
|
||||||
|
UpdateLines();
|
||||||
|
UpdateLineOffsets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::UpdateLines()
|
||||||
|
{
|
||||||
|
lines_.clear();
|
||||||
|
|
||||||
|
size_t start = 0;
|
||||||
|
for (size_t i = 0; i < item_.text.length(); ++i)
|
||||||
|
{
|
||||||
|
if (item_.text[i] == '\n')
|
||||||
|
{
|
||||||
|
lines_.push_back(item_.text.substr(start, i - start));
|
||||||
|
start = i + 1;
|
||||||
|
}
|
||||||
|
else if (item_.text[i] == '\r')
|
||||||
|
{
|
||||||
|
if (i + 1 < item_.text.length() && item_.text[i + 1] == '\n')
|
||||||
|
{
|
||||||
|
lines_.push_back(item_.text.substr(start, i - start));
|
||||||
|
start = i + 2;
|
||||||
|
++i; // Skip \n
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lines_.push_back(item_.text.substr(start, i - start));
|
||||||
|
start = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加最后一行
|
||||||
|
if (start <= item_.text.length())
|
||||||
|
{
|
||||||
|
lines_.push_back(item_.text.substr(start));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::UpdateLineOffsets()
|
||||||
|
{
|
||||||
|
line_offsets_.clear();
|
||||||
|
line_offsets_.reserve(lines_.size() + 1);
|
||||||
|
|
||||||
|
size_t offset = 0;
|
||||||
|
line_offsets_.push_back(0);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < item_.text.length(); ++i)
|
||||||
|
{
|
||||||
|
if (item_.text[i] == '\n')
|
||||||
|
{
|
||||||
|
line_offsets_.push_back(i + 1);
|
||||||
|
}
|
||||||
|
else if (item_.text[i] == '\r')
|
||||||
|
{
|
||||||
|
if (i + 1 < item_.text.length() && item_.text[i + 1] == '\n')
|
||||||
|
{
|
||||||
|
line_offsets_.push_back(i + 2);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
line_offsets_.push_back(i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Document::IsWordChar(char c) const
|
||||||
|
{
|
||||||
|
return std::isalnum(static_cast<unsigned char>(c)) || c == '_' || c == '$';
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Document::CharacterToByteOffset(const std::string& line, int32_t character) const
|
||||||
|
{
|
||||||
|
if (encoding_ == protocol::PositionEncodingKindLiterals::UTF8)
|
||||||
|
{
|
||||||
|
return std::min(static_cast<size_t>(character), line.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTF-16 编码:需要正确计算
|
||||||
|
size_t byteOffset = 0;
|
||||||
|
int32_t charCount = 0;
|
||||||
|
|
||||||
|
while (byteOffset < line.length() && charCount < character)
|
||||||
|
{
|
||||||
|
unsigned char c = line[byteOffset];
|
||||||
|
|
||||||
|
if (encoding_ == protocol::PositionEncodingKindLiterals::UTF16)
|
||||||
|
{
|
||||||
|
// UTF-16: 计算代码单元
|
||||||
|
if ((c & 0x80) == 0)
|
||||||
|
{
|
||||||
|
// ASCII
|
||||||
|
byteOffset += 1;
|
||||||
|
charCount += 1;
|
||||||
|
}
|
||||||
|
else if ((c & 0xE0) == 0xC0)
|
||||||
|
{
|
||||||
|
// 2字节UTF-8 -> 1个UTF-16单元
|
||||||
|
byteOffset += 2;
|
||||||
|
charCount += 1;
|
||||||
|
}
|
||||||
|
else if ((c & 0xF0) == 0xE0)
|
||||||
|
{
|
||||||
|
// 3字节UTF-8 -> 1个UTF-16单元
|
||||||
|
byteOffset += 3;
|
||||||
|
charCount += 1;
|
||||||
|
}
|
||||||
|
else if ((c & 0xF8) == 0xF0)
|
||||||
|
{
|
||||||
|
// 4字节UTF-8 -> 2个UTF-16单元(代理对)
|
||||||
|
byteOffset += 4;
|
||||||
|
charCount += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
byteOffset += 1; // 错误情况
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // UTF32
|
||||||
|
{
|
||||||
|
// UTF-32: 每个Unicode代码点算一个
|
||||||
|
if ((c & 0x80) == 0)
|
||||||
|
{
|
||||||
|
byteOffset += 1;
|
||||||
|
}
|
||||||
|
else if ((c & 0xE0) == 0xC0)
|
||||||
|
{
|
||||||
|
byteOffset += 2;
|
||||||
|
}
|
||||||
|
else if ((c & 0xF0) == 0xE0)
|
||||||
|
{
|
||||||
|
byteOffset += 3;
|
||||||
|
}
|
||||||
|
else if ((c & 0xF8) == 0xF0)
|
||||||
|
{
|
||||||
|
byteOffset += 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
byteOffset += 1;
|
||||||
|
}
|
||||||
|
charCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return byteOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t Document::ByteOffsetToCharacter(const std::string& line, size_t byteOffset) const
|
||||||
|
{
|
||||||
|
if (encoding_ == protocol::PositionEncodingKindLiterals::UTF8)
|
||||||
|
{
|
||||||
|
return static_cast<int32_t>(byteOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t charCount = 0;
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
|
while (pos < byteOffset && pos < line.length())
|
||||||
|
{
|
||||||
|
unsigned char c = line[pos];
|
||||||
|
|
||||||
|
if (encoding_ == protocol::PositionEncodingKindLiterals::UTF16)
|
||||||
|
{
|
||||||
|
if ((c & 0x80) == 0)
|
||||||
|
{
|
||||||
|
pos += 1;
|
||||||
|
charCount += 1;
|
||||||
|
}
|
||||||
|
else if ((c & 0xE0) == 0xC0)
|
||||||
|
{
|
||||||
|
pos += 2;
|
||||||
|
charCount += 1;
|
||||||
|
}
|
||||||
|
else if ((c & 0xF0) == 0xE0)
|
||||||
|
{
|
||||||
|
pos += 3;
|
||||||
|
charCount += 1;
|
||||||
|
}
|
||||||
|
else if ((c & 0xF8) == 0xF0)
|
||||||
|
{
|
||||||
|
pos += 4;
|
||||||
|
charCount += 2; // 代理对
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos += 1;
|
||||||
|
charCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // UTF32
|
||||||
|
{
|
||||||
|
if ((c & 0x80) == 0)
|
||||||
|
{
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
else if ((c & 0xE0) == 0xC0)
|
||||||
|
{
|
||||||
|
pos += 2;
|
||||||
|
}
|
||||||
|
else if ((c & 0xF0) == 0xE0)
|
||||||
|
{
|
||||||
|
pos += 3;
|
||||||
|
}
|
||||||
|
else if ((c & 0xF8) == 0xF0)
|
||||||
|
{
|
||||||
|
pos += 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
charCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return charCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== DocumentManager 实现 =====
|
||||||
|
|
||||||
|
void DocumentManager::DidOpenTextDocument(const protocol::DidOpenTextDocumentParams& params)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
|
||||||
|
// 检查文档大小
|
||||||
|
if (config_.max_document_size > 0 &&
|
||||||
|
params.textDocument.text.length() > config_.max_document_size)
|
||||||
|
{
|
||||||
|
spdlog::error("Document {} exceeds maximum size ({} > {})",
|
||||||
|
params.textDocument.uri,
|
||||||
|
params.textDocument.text.length(),
|
||||||
|
config_.max_document_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新文档
|
||||||
|
auto doc = std::make_shared<Document>(params.textDocument);
|
||||||
|
doc->SetEncoding(config_.default_encoding);
|
||||||
|
|
||||||
|
documents_[params.textDocument.uri] = doc;
|
||||||
|
|
||||||
|
spdlog::info("Opened document: {} (version {}, {} bytes, language: {})",
|
||||||
|
params.textDocument.uri,
|
||||||
|
params.textDocument.version,
|
||||||
|
params.textDocument.text.length(),
|
||||||
|
params.textDocument.languageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DocumentManager::DidChangeTextDocument(const protocol::DidChangeTextDocumentParams& params)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
|
||||||
|
auto it = documents_.find(params.textDocument.uri);
|
||||||
|
if (it == documents_.end())
|
||||||
|
{
|
||||||
|
spdlog::error("Attempt to change non-existent document: {}",
|
||||||
|
params.textDocument.uri);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& doc = it->second;
|
||||||
|
|
||||||
|
// 版本检查
|
||||||
|
if (params.textDocument.version)
|
||||||
|
{
|
||||||
|
protocol::integer expectedVersion = params.textDocument.version;
|
||||||
|
if (expectedVersion <= doc->GetVersion())
|
||||||
|
{
|
||||||
|
spdlog::warn("Ignoring stale change for {}: version {} <= current {}",
|
||||||
|
params.textDocument.uri,
|
||||||
|
expectedVersion,
|
||||||
|
doc->GetVersion());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用变更
|
||||||
|
if (params.contentChanges.empty())
|
||||||
|
{
|
||||||
|
spdlog::warn("Empty content changes for document: {}", params.textDocument.uri);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是全文还是增量
|
||||||
|
if (params.contentChanges.size() == 1)
|
||||||
|
{
|
||||||
|
// 全文更新
|
||||||
|
// doc->SetContent(params.textDocument.version(doc->GetVersion() + 1), params.contentChanges[0].text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 增量更新
|
||||||
|
// doc->ApplyContentChanges( params.textDocument.version(doc->GetVersion() + 1), params.contentChanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::debug("Changed document: {} to version {} ({} changes)", params.textDocument.uri, doc->GetVersion(), params.contentChanges.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DocumentManager::DidCloseTextDocument(const protocol::DidCloseTextDocumentParams& params)
|
||||||
|
{
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
|
||||||
|
auto it = documents_.find(params.textDocument.uri);
|
||||||
|
if (it == documents_.end())
|
||||||
|
{
|
||||||
|
spdlog::warn("Attempt to close non-existent document: {}",
|
||||||
|
params.textDocument.uri);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
documents_.erase(it);
|
||||||
|
spdlog::info("Closed document: {}", params.textDocument.uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DocumentManager::DidSaveTextDocument(const protocol::DidSaveTextDocumentParams& params)
|
||||||
|
{
|
||||||
|
std::shared_lock lock(mutex_);
|
||||||
|
|
||||||
|
auto it = documents_.find(params.textDocument.uri);
|
||||||
|
if (it == documents_.end())
|
||||||
|
{
|
||||||
|
spdlog::warn("Attempt to save non-existent document: {}",
|
||||||
|
params.textDocument.uri);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->second->SetDirty(false);
|
||||||
|
|
||||||
|
// 如果保存通知包含文本,可以验证同步状态
|
||||||
|
if (params.text.has_value())
|
||||||
|
{
|
||||||
|
if (params.text.value() != it->second->GetText())
|
||||||
|
{
|
||||||
|
spdlog::error("Document content mismatch on save for: {}", params.textDocument.uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::info("Saved document: {}", params.textDocument.uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Document> DocumentManager::GetDocument(const std::string& uri) const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(mutex_);
|
||||||
|
|
||||||
|
auto it = documents_.find(uri);
|
||||||
|
if (it != documents_.end())
|
||||||
|
{
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> DocumentManager::GetAllUris() const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(mutex_);
|
||||||
|
|
||||||
|
std::vector<std::string> uris;
|
||||||
|
uris.reserve(documents_.size());
|
||||||
|
|
||||||
|
for (const auto& [uri, doc] : documents_)
|
||||||
|
{
|
||||||
|
uris.push_back(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
return uris;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Document>> DocumentManager::GetAllDocuments() const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(mutex_);
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Document>> docs;
|
||||||
|
docs.reserve(documents_.size());
|
||||||
|
|
||||||
|
for (const auto& [uri, doc] : documents_)
|
||||||
|
{
|
||||||
|
docs.push_back(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return docs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Document>> DocumentManager::GetDocumentsByLanguage(
|
||||||
|
const std::string& languageId) const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(mutex_);
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Document>> docs;
|
||||||
|
|
||||||
|
for (const auto& [uri, doc] : documents_)
|
||||||
|
{
|
||||||
|
if (doc->GetLanguageId() == languageId)
|
||||||
|
{
|
||||||
|
docs.push_back(doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return docs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DocumentManager::HasDocument(const std::string& uri) const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(mutex_);
|
||||||
|
return documents_.find(uri) != documents_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t DocumentManager::GetDocumentCount() const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(mutex_);
|
||||||
|
return documents_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> DocumentManager::GetDirtyDocuments() const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(mutex_);
|
||||||
|
|
||||||
|
std::vector<std::string> dirtyUris;
|
||||||
|
|
||||||
|
for (const auto& [uri, doc] : documents_)
|
||||||
|
{
|
||||||
|
if (doc->IsDirty())
|
||||||
|
{
|
||||||
|
dirtyUris.push_back(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirtyUris;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DocumentManager::ResolveUri(const std::string& uri) const
|
||||||
|
{
|
||||||
|
// 如果已经是绝对URI,直接返回
|
||||||
|
if (utils::IsFileUri(uri))
|
||||||
|
{
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试相对于工作区文件夹解析
|
||||||
|
for (const auto& folder : workspace_folders_)
|
||||||
|
{
|
||||||
|
std::string folderPath = utils::UriToPath(folder.uri);
|
||||||
|
std::string resolvedPath = folderPath + "/" + uri;
|
||||||
|
|
||||||
|
// 检查文件是否存在(这里简化处理)
|
||||||
|
return utils::PathToUri(resolvedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 工具函数实现 =====
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
std::string NormalizeUri(const std::string& uri)
|
||||||
|
{
|
||||||
|
std::string normalized = uri;
|
||||||
|
|
||||||
|
// 确保使用正斜杠
|
||||||
|
std::replace(normalized.begin(), normalized.end(), '\\', '/');
|
||||||
|
|
||||||
|
// 移除重复的斜杠
|
||||||
|
auto newEnd = std::unique(normalized.begin(), normalized.end(), [](char a, char b) { return a == '/' && b == '/'; });
|
||||||
|
normalized.erase(newEnd, normalized.end());
|
||||||
|
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string UriToPath(const std::string& uri)
|
||||||
|
{
|
||||||
|
if (uri.substr(0, 7) == "file://")
|
||||||
|
{
|
||||||
|
std::string path = uri.substr(7);
|
||||||
|
|
||||||
|
// Windows路径处理
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (path.length() >= 3 && path[0] == '/' &&
|
||||||
|
std::isalpha(path[1]) && path[2] == ':')
|
||||||
|
{
|
||||||
|
path = path.substr(1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PathToUri(const std::string& path)
|
||||||
|
{
|
||||||
|
std::string uri = "file://";
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Windows路径
|
||||||
|
if (path.length() >= 2 && std::isalpha(path[0]) && path[1] == ':')
|
||||||
|
{
|
||||||
|
uri += "/";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uri += path;
|
||||||
|
return NormalizeUri(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsFileUri(const std::string& uri)
|
||||||
|
{
|
||||||
|
return uri.substr(0, 7) == "file://";
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol::TextEdit CreateReplace(const protocol::Range& range, const std::string& newText)
|
||||||
|
{
|
||||||
|
protocol::TextEdit edit;
|
||||||
|
edit.range = range;
|
||||||
|
edit.newText = newText;
|
||||||
|
return edit;
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol::TextEdit CreateInsert(const protocol::Position& position, const std::string& text)
|
||||||
|
{
|
||||||
|
return CreateReplace(protocol::Range{ position, position }, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol::TextEdit CreateDelete(const protocol::Range& range)
|
||||||
|
{
|
||||||
|
return CreateReplace(range, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPositionInRange(const protocol::Position& position, const protocol::Range& range)
|
||||||
|
{
|
||||||
|
if (position.line < range.start.line || position.line > range.end.line)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position.line == range.start.line && position.character < range.start.character)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position.line == range.end.line && position.character >= range.end.character)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRangeOverlapping(const protocol::Range& a, const protocol::Range& b)
|
||||||
|
{
|
||||||
|
return !(a.end.line < b.start.line ||
|
||||||
|
(a.end.line == b.start.line && a.end.character <= b.start.character) ||
|
||||||
|
b.end.line < a.start.line ||
|
||||||
|
(b.end.line == a.start.line && b.end.character <= a.start.character));
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol::Range ExtendRange(const protocol::Range& range, int32_t lines)
|
||||||
|
{
|
||||||
|
protocol::Range extended = range;
|
||||||
|
extended.start.line = std::max(static_cast<std::int32_t>(0), static_cast<std::int32_t>(extended.start.line - lines));
|
||||||
|
extended.end.line += lines;
|
||||||
|
return extended;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ApplyTextEdits(const std::string& text,
|
||||||
|
const std::vector<protocol::TextEdit>& edits)
|
||||||
|
{
|
||||||
|
if (edits.empty())
|
||||||
|
{
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排序编辑(从后向前,避免偏移问题)
|
||||||
|
std::vector<protocol::TextEdit> sortedEdits = edits;
|
||||||
|
std::sort(sortedEdits.begin(), sortedEdits.end(), [](const protocol::TextEdit& a, const protocol::TextEdit& b) {
|
||||||
|
if (a.range.start.line != b.range.start.line)
|
||||||
|
{
|
||||||
|
return a.range.start.line > b.range.start.line;
|
||||||
|
}
|
||||||
|
return a.range.start.character > b.range.start.character;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建临时文档来应用编辑
|
||||||
|
protocol::TextDocumentItem tempItem;
|
||||||
|
tempItem.uri = "temp://";
|
||||||
|
tempItem.languageId = "";
|
||||||
|
tempItem.version = 0;
|
||||||
|
tempItem.text = text;
|
||||||
|
|
||||||
|
Document tempDoc(tempItem);
|
||||||
|
std::string result = text;
|
||||||
|
|
||||||
|
for (const auto& edit : sortedEdits)
|
||||||
|
{
|
||||||
|
size_t start = tempDoc.PositionToOffset(edit.range.start);
|
||||||
|
size_t end = tempDoc.PositionToOffset(edit.range.end);
|
||||||
|
|
||||||
|
result = result.substr(0, start) + edit.newText + result.substr(end);
|
||||||
|
|
||||||
|
// 更新临时文档
|
||||||
|
tempDoc.SetContent(0, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,238 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
|
#include <chrono>
|
||||||
|
#include <shared_mutex>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include "../protocol/protocol.hpp"
|
||||||
|
|
||||||
|
namespace lsp::services
|
||||||
|
{
|
||||||
|
class Document
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Document(const protocol::TextDocumentItem& item);
|
||||||
|
|
||||||
|
const protocol::DocumentUri& GetUri() const { return item_.uri; }
|
||||||
|
const protocol::string& GetLanguageId() const { return item_.languageId; }
|
||||||
|
protocol::integer GetVersion() const { return item_.version; }
|
||||||
|
const protocol::string& GetText() const { return item_.text; }
|
||||||
|
const protocol::TextDocumentItem& GetItem() const { return item_; }
|
||||||
|
|
||||||
|
protocol::TextDocumentIdentifier GetIdentifier() const
|
||||||
|
{
|
||||||
|
return protocol::TextDocumentIdentifier{item_.uri};
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol::VersionedTextDocumentIdentifier GetVersionedIdentifier() const
|
||||||
|
{
|
||||||
|
protocol::VersionedTextDocumentIdentifier id;
|
||||||
|
id.uri = item_.uri;
|
||||||
|
id.version = item_.version;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 位置和范围操作 =====
|
||||||
|
size_t PositionToOffset(const protocol::Position& position) const;
|
||||||
|
protocol::Position OffsetToPosition(size_t offset) const;
|
||||||
|
std::string GetTextInRange(const protocol::Range& range) const;
|
||||||
|
|
||||||
|
// 内容更新
|
||||||
|
void SetContent(protocol::integer version, const protocol::string& new_text);
|
||||||
|
void ApplyContentChange(protocol::integer version, const std::vector<protocol::TextDocumentContentChangeEvent>& changes);
|
||||||
|
|
||||||
|
// 获取指定位置的字符
|
||||||
|
std::optional<char> GetCharAt(const protocol::Position& position) const;
|
||||||
|
|
||||||
|
// ===== 行操作 =====
|
||||||
|
const std::vector<std::string>& GetLines() const { return lines_; }
|
||||||
|
size_t GetLineCount() const { return lines_.size(); }
|
||||||
|
std::string GetLine(size_t lineNumber) const;
|
||||||
|
std::string GetLineAt(const protocol::Position& position) const;
|
||||||
|
|
||||||
|
// ===== 单词和符号操作 =====
|
||||||
|
std::string GetWordAt(const protocol::Position& position) const;
|
||||||
|
protocol::Range GetWordRangeAt(const protocol::Position& position) const;
|
||||||
|
|
||||||
|
// ===== 实用方法 =====
|
||||||
|
// 创建一个Location
|
||||||
|
protocol::Location CreateLocation(const protocol::Range& range) const
|
||||||
|
{
|
||||||
|
return protocol::Location{item_.uri, range};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个TextDocumentPositionParams
|
||||||
|
protocol::TextDocumentPositionParams CreatePositionParams(const protocol::Position& position) const
|
||||||
|
{
|
||||||
|
protocol::TextDocumentPositionParams params;
|
||||||
|
params.textDocument = GetIdentifier();
|
||||||
|
params.position = position;
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 元数据 =====
|
||||||
|
// 文档是否被修改(相对于上次保存)
|
||||||
|
bool IsDirty() const { return is_dirty_; }
|
||||||
|
void SetDirty(bool dirty) { is_dirty_ = dirty; }
|
||||||
|
|
||||||
|
// 最后修改时间
|
||||||
|
std::chrono::system_clock::time_point GetLastModified() const { return last_modified_time_; }
|
||||||
|
|
||||||
|
void SetEncoding(protocol::PositionEncodingKind encoding) { encoding_ = encoding; }
|
||||||
|
protocol::PositionEncodingKind GetEncoding() const { return encoding_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// 更新内部缓存
|
||||||
|
void UpdateInternalState();
|
||||||
|
void UpdateLines();
|
||||||
|
void UpdateLineOffsets();
|
||||||
|
|
||||||
|
// 辅助方法
|
||||||
|
bool IsWordChar(char c) const;
|
||||||
|
size_t CharacterToByteOffset(const std::string& line, std::int32_t character) const;
|
||||||
|
std::int32_t ByteOffsetToCharacter(const std::string& line, size_t byteOffset) const;
|
||||||
|
|
||||||
|
// 应用单个内容变更
|
||||||
|
void ApplyContentChange(const protocol::TextDocumentContentChangeEvent& change);
|
||||||
|
|
||||||
|
private:
|
||||||
|
protocol::TextDocumentItem item_;
|
||||||
|
|
||||||
|
// 缓存行的信息
|
||||||
|
std::vector<std::string> lines_;
|
||||||
|
std::vector<size_t> line_offsets_;
|
||||||
|
|
||||||
|
bool is_dirty_ = false;
|
||||||
|
std::chrono::system_clock::time_point last_modified_time_;
|
||||||
|
protocol::PositionEncodingKind encoding_ = protocol::PositionEncodingKindLiterals::UTF16;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文档管理器 - 使用protocol类型作为接口
|
||||||
|
*/
|
||||||
|
class DocumentManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DocumentManager() = default;
|
||||||
|
~DocumentManager() = default;
|
||||||
|
|
||||||
|
// 禁止拷贝
|
||||||
|
DocumentManager(const DocumentManager&) = delete;
|
||||||
|
DocumentManager& operator=(const DocumentManager&) = delete;
|
||||||
|
|
||||||
|
// ===== 文档生命周期管理 - 直接使用protocol类型 =====
|
||||||
|
|
||||||
|
// 处理 textDocument/didOpen
|
||||||
|
void DidOpenTextDocument(const protocol::DidOpenTextDocumentParams& params);
|
||||||
|
|
||||||
|
// 处理 textDocument/didChange
|
||||||
|
void DidChangeTextDocument(const protocol::DidChangeTextDocumentParams& params);
|
||||||
|
|
||||||
|
// 处理 textDocument/didClose
|
||||||
|
void DidCloseTextDocument(const protocol::DidCloseTextDocumentParams& params);
|
||||||
|
|
||||||
|
// 处理 textDocument/didSave
|
||||||
|
void DidSaveTextDocument(const protocol::DidSaveTextDocumentParams& params);
|
||||||
|
|
||||||
|
// ===== 文档访问 - 支持多种查询方式 =====
|
||||||
|
|
||||||
|
// 通过URI获取
|
||||||
|
std::shared_ptr<Document> GetDocument(const std::string& uri) const;
|
||||||
|
|
||||||
|
// 通过标识符获取
|
||||||
|
std::shared_ptr<Document> GetDocument(const protocol::TextDocumentIdentifier& identifier) const
|
||||||
|
{
|
||||||
|
return GetDocument(identifier.uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过版本化标识符获取
|
||||||
|
std::shared_ptr<Document> GetDocument(const protocol::VersionedTextDocumentIdentifier& identifier) const
|
||||||
|
{
|
||||||
|
auto doc = GetDocument(identifier.uri);
|
||||||
|
if (doc && identifier.version && doc->GetVersion() != identifier.version)
|
||||||
|
{
|
||||||
|
spdlog::warn("Version mismatch for {}: expected {}, got {}", identifier.uri, identifier.version, doc->GetVersion());
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过TextDocumentPositionParams获取
|
||||||
|
std::shared_ptr<Document> GetDocument(const protocol::TextDocumentPositionParams& params) const
|
||||||
|
{
|
||||||
|
return GetDocument(params.textDocument);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 批量操作 =====
|
||||||
|
std::vector<std::string> GetAllUris() const;
|
||||||
|
std::vector<std::shared_ptr<Document>> GetAllDocuments() const;
|
||||||
|
std::vector<std::shared_ptr<Document>> GetDocumentsByLanguage(const std::string& languageId) const;
|
||||||
|
|
||||||
|
// ===== 查询 =====
|
||||||
|
bool HasDocument(const std::string& uri) const;
|
||||||
|
bool IsDocumentOpen(const protocol::TextDocumentIdentifier& identifier) const
|
||||||
|
{
|
||||||
|
return HasDocument(identifier.uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetDocumentCount() const;
|
||||||
|
|
||||||
|
// ===== 诊断支持 =====
|
||||||
|
// 获取需要诊断的文档(已修改的)
|
||||||
|
std::vector<std::string> GetDirtyDocuments() const;
|
||||||
|
|
||||||
|
// ===== 工作区支持 =====
|
||||||
|
// 设置工作区文件夹(用于相对路径解析)
|
||||||
|
void SetWorkspaceFolders(const std::vector<protocol::WorkspaceFolder>& folders)
|
||||||
|
{
|
||||||
|
workspace_folders_ = folders;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<protocol::WorkspaceFolder>& GetWorkspaceFolders() const
|
||||||
|
{
|
||||||
|
return workspace_folders_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析相对URI
|
||||||
|
std::string ResolveUri(const std::string& uri) const;
|
||||||
|
|
||||||
|
// ===== 配置 =====
|
||||||
|
struct Configuration {
|
||||||
|
size_t max_document_size = 10 * 1024 * 1024; // 10MB
|
||||||
|
bool validate_utf8 = true;
|
||||||
|
protocol::PositionEncodingKind default_encoding = protocol::PositionEncodingKindLiterals::UTF16;
|
||||||
|
};
|
||||||
|
|
||||||
|
void SetConfiguration(const Configuration& config) { config_ = config; }
|
||||||
|
const Configuration& GetConfiguration() const { return config_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::shared_mutex mutex_;
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<Document>> documents_;
|
||||||
|
std::vector<protocol::WorkspaceFolder> workspace_folders_;
|
||||||
|
Configuration config_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== 工具函数 =====
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
// URI处理
|
||||||
|
std::string NormalizeUri(const std::string& uri);
|
||||||
|
std::string UriToPath(const std::string& uri);
|
||||||
|
std::string PathToUri(const std::string& path);
|
||||||
|
bool IsFileUri(const std::string& uri);
|
||||||
|
|
||||||
|
// 创建TextEdit
|
||||||
|
protocol::TextEdit CreateReplace(const protocol::Range& range, const std::string& newText);
|
||||||
|
protocol::TextEdit CreateInsert(const protocol::Position& position, const std::string& text);
|
||||||
|
protocol::TextEdit CreateDelete(const protocol::Range& range);
|
||||||
|
|
||||||
|
// 范围操作
|
||||||
|
bool IsPositionInRange(const protocol::Position& position, const protocol::Range& range);
|
||||||
|
bool IsRangeOverlapping(const protocol::Range& a, const protocol::Range& b);
|
||||||
|
protocol::Range ExtendRange(const protocol::Range& range, int32_t lines);
|
||||||
|
|
||||||
|
// 应用编辑
|
||||||
|
std::string ApplyTextEdits(const std::string& text, const std::vector<protocol::TextEdit>& edits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -103,7 +103,7 @@ namespace lsp::utils
|
||||||
<< "Usage: " << program_name << " [options]\n"
|
<< "Usage: " << program_name << " [options]\n"
|
||||||
<< "\n"
|
<< "\n"
|
||||||
<< "Options:\n"
|
<< "Options:\n"
|
||||||
<< " --help, -h Show this help message and exit\n"
|
<< " --help Show this help message and exit\n"
|
||||||
<< "\n"
|
<< "\n"
|
||||||
<< "Logging options:\n"
|
<< "Logging options:\n"
|
||||||
<< " --log=LEVEL Set log level (trace, debug, info, warn, error, off)\n"
|
<< " --log=LEVEL Set log level (trace, debug, info, warn, error, off)\n"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue