lsp-server first commit

This commit is contained in:
csh
2025-06-23 20:29:48 +08:00
parent cbcff7d1b4
commit 4f5bf9d781
43 changed files with 1851 additions and 11 deletions
+63
View File
@@ -0,0 +1,63 @@
#include "./dispacther.hpp"
namespace lsp {
void RequestDispatcher::RegisterProvider(const std::string& method, RequestProvider handler)
{
providers_[method] = std::move(handler);
}
nlohmann::json RequestDispatcher::Dispatch(const LspRequest& request)
{
auto it = providers_.find(request.method);
if (it != providers_.end()) {
try
{
return it->second(request);
} catch (const std::exception& e)
{
return HandleException(request, e.what());
}
}
return HandleUnknownMethod(request);
}
bool RequestDispatcher::SupportsMethod(const std::string& method) const
{
return providers_.find(method) != providers_.end();
}
std::vector<std::string> RequestDispatcher::GetSupportedMethods() const
{
std::vector<std::string> methods;
for (const auto& pair : providers_) {
methods.push_back(pair.first);
}
return methods;
}
nlohmann::json RequestDispatcher::HandleUnknownMethod(const LspRequest& request)
{
nlohmann::json resp;
resp["jsonrpc"] = "2.0";
resp["id"] = request.id;
resp["error"] = {
{"code", -32601},
{"message", "Method not found: " + request.method}
};
return resp;
}
nlohmann::json RequestDispatcher::HandleException(const LspRequest& request, const std::string& error_message)
{
nlohmann::json resp;
resp["jsonrpc"] = "2.0";
resp["id"] = request.id;
resp["error"] = {
{"code", -32603}, // Internal error
{"message", "Internal error: " + error_message}
};
return resp;
}
}
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include "./lsp_types.hpp"
namespace lsp
{
// 请求处理函数类型
using RequestProvider = std::function<nlohmann::json(const LspRequest&)>;
class RequestDispatcher
{
public:
RequestDispatcher() = default;
void RegisterProvider(const std::string& method, RequestProvider provider); // 可选:重命名
nlohmann::json Dispatch(const LspRequest& request);
bool SupportsMethod(const std::string& method) const;
std::vector<std::string> GetSupportedMethods() const;
private:
nlohmann::json HandleUnknownMethod(const LspRequest& request);
nlohmann::json HandleException(const LspRequest& request, const std::string& error_message);
private:
std::unordered_map<std::string, RequestProvider> providers_;
};
}
+60
View File
@@ -0,0 +1,60 @@
#include "./logger.hpp"
namespace lsp::log
{
Logger::~Logger()
{
if (file_stream_.is_open())
file_stream_.close();
}
Logger& Logger::Instance()
{
static Logger logger;
return logger;
}
void Logger::SetLevel(LogLevel level)
{
std::lock_guard<std::mutex> lock(mutex_);
level_ = level;
}
void Logger::SetLogFile(const std::string& filename)
{
std::lock_guard<std::mutex> lock(mutex_);
if (file_stream_.is_open())
file_stream_.close();
file_stream_.open(filename, std::ios::app);
use_file_ = file_stream_.is_open();
}
void Logger::EnableStderr(bool enable)
{
std::lock_guard<std::mutex> lock(mutex_);
use_stderr_ = enable;
}
const char* Logger::LevelToString(LogLevel level)
{
switch (level)
{
case LogLevel::kOff:
return "OFF";
case LogLevel::kError:
return "ERROR";
case LogLevel::kWarn:
return "WARN";
case LogLevel::kInfo:
return "INFO";
case LogLevel::kDebug:
return "DEBUG";
case LogLevel::kVerbose:
return "VERBOSE";
default:
return "UNKNOWN";
}
}
}
+93
View File
@@ -0,0 +1,93 @@
#pragma once
#include <iostream>
#include <fstream>
#include <sstream>
#include <mutex>
namespace lsp::log
{
enum class LogLevel
{
kOff = 0,
kError = 1,
kWarn = 2,
kInfo = 3,
kDebug = 4,
kVerbose = 5
};
class Logger
{
public:
static Logger& Instance();
void SetLevel(LogLevel level);
void SetLogFile(const std::string& filename);
void EnableStderr(bool enable);
template<typename... Args>
void log(LogLevel level, Args&&... args) {
if (level > level_)
return;
std::lock_guard<std::mutex> lock(mutex_);
std::ostringstream oss;
oss << "[TSL-LSP:" << LevelToString(level) << "] ";
(oss << ... << args);
oss << std::endl;
std::string message = oss.str();
if (use_stderr_)
{
std::cerr << message;
}
if (use_file_ && file_stream_.is_open())
{
file_stream_ << message;
file_stream_.flush();
}
}
private:
Logger() = default;
~Logger();
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
const char* LevelToString(LogLevel level);
private:
LogLevel level_;
bool use_file_ = false;
bool use_stderr_ = false;
std::ofstream file_stream_;
std::mutex mutex_;
};
template<typename... Args>
void Error(Args&&... args) {
Logger::Instance().log(LogLevel::kError, std::forward<Args>(args)...);
}
template<typename... Args>
void Warn(Args&&... args) {
Logger::Instance().log(LogLevel::kWarn, std::forward<Args>(args)...);
}
template<typename... Args>
void Info(Args&&... args) {
Logger::Instance().log(LogLevel::kInfo, std::forward<Args>(args)...);
}
template<typename... Args>
void Debug(Args&&... args) {
Logger::Instance().log(LogLevel::kDebug, std::forward<Args>(args)...);
}
template<typename... Args>
void Verbose(Args&&... args) {
Logger::Instance().log(LogLevel::kVerbose, std::forward<Args>(args)...);
}
}
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include <string>
#include <optional>
#include <nlohmann/json.hpp>
namespace lsp
{
// LSP请求结构
struct LspRequest
{
std::string jsonrpc;
std::string method;
nlohmann::json params;
nlohmann::json id;
LspRequest(const nlohmann::json& json) :
jsonrpc(json.value("jsonrpc", "2.0")),
method(json.value("method", "")),
params(json.value("params", nlohmann::json::object())),
id(json.value("id", nlohmann::json::array())) {}
};
// 补全项类型枚举
enum class CompletionItemKind
{
kText = 1,
kMethod = 2,
kFunction = 3,
kConstructor = 4,
kField = 5,
kVariable = 6,
kClass = 7,
kInterface = 8,
kModule = 9,
kProperty = 10,
kUnit = 11,
kValue = 12,
kEnum = 13,
kKeyword = 14,
kSnippet = 15,
kColor = 16,
kFile = 17,
kReference = 18,
kFolder = 19,
kEnumMember = 20,
kConstant = 21,
kStruct = 22,
kEvent = 23,
kOperator = 24,
kTypeParameter = 25
};
// 补全项目
struct CompletionItem
{
std::string label;
CompletionItemKind kind;
std::optional<std::string> detail;
std::optional<std::string> documentation;
std::optional<std::string> insert_text;
};
}
+126
View File
@@ -0,0 +1,126 @@
#include <exception>
#include <iostream>
#include "../provider/base/provider_registry.hpp"
#include "./server.hpp"
#include "./logger.hpp"
namespace lsp
{
LspServer::LspServer()
{
providers::RegisterAllProviders(dispatcher_);
log::Debug("LSP server initialized with providers.");
}
void LspServer::Run()
{
log::Info("LSP server starting main loop...");
while (true)
{
try
{
std::optional<std::string> message = ReadMessage();
if (!message) continue;
nlohmann::json response = HandleRequest(*message);
if (!response.empty())
SendResponse(response);
}
catch(const std::exception& e)
{
log::Error("Error processing message: ", e.what());
}
}
log::Info("LSP server main loop ended");
}
std::optional<std::string> LspServer::ReadMessage()
{
std::string line;
size_t content_length = 0;
// 读取 LSP Header
while (std::getline(std::cin, line))
{
log::Verbose("Received header line: ", line);
// 去掉尾部 \r
if (!line.empty() && line.back() == '\r')
line.pop_back();
if (line.empty())
break;
// 查找 Content-Length
if (line.find("Content-Length:") == 0)
{
std::string length_str = line.substr(15); // 跳过 "Content-Length:"
size_t start = length_str.find_first_not_of(" ");
if (start != std::string::npos)
{
length_str = length_str.substr(start);
try
{
content_length = std::stoul(length_str);
log::Verbose("Content-Length: ", content_length);
}
catch (const std::exception& e)
{
log::Error("Failed to parse Content-Length: ", e.what());
return std::nullopt;
}
}
}
}
if (content_length == 0)
{
log::Warn("No Content-Length found in header");
return std::nullopt;
}
// 读取内容体
std::string body(content_length, '\0');
std::cin.read(&body[0], content_length);
log::Verbose("Message body: ", body);
if (std::cin.gcount() != static_cast<std::streamsize>(content_length))
{
log::Error("Read incomplete message body, expected: ", content_length, ", got: ", std::cin.gcount());
return std::nullopt;
}
return body;
}
nlohmann::json LspServer::HandleRequest(const std::string& raw_request)
{
try
{
nlohmann::json json = nlohmann::json::parse(raw_request);
LspRequest request(json);
log::Debug("Processing method: ", request.method);
nlohmann::json response = dispatcher_.Dispatch(request);
return response;
}
catch (const std::exception& e)
{
log::Error("Failed to handle request: ", e.what());
return nlohmann::json();
}
}
void LspServer::SendResponse(const nlohmann::json& response)
{
std::string response_str = response.dump();
size_t byte_length = response_str.length();
// 调试:显示实际发送的原始内容
log::Debug("Response length: ", byte_length);
log::Debug("Raw response content: [", response_str, "]");
std::cout << "Content-Length: " << byte_length << "\r\n\r\n";
std::cout << response_str;
std::cout.flush();
log::Verbose("Response sent successfully");
}
}
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <optional>
#include <string>
#include "./dispacther.hpp"
namespace lsp
{
class LspServer
{
public:
LspServer();
~LspServer() = default;
void Run();
private:
// 读取LSP消息
std::optional<std::string> ReadMessage();
// 处理LSP请求
nlohmann::json HandleRequest(const std::string& raw_request);
// 发送LSP响应
void SendResponse(const nlohmann::json& response);
private:
RequestDispatcher dispatcher_;
};
}