Compare commits

..

No commits in common. "56b0f5473a53942ba67456e4b252d6e68d61359a" and "b4b916fd9ac1418feb1228058b1a391bab76f2c0" have entirely different histories.

21 changed files with 281 additions and 154 deletions

View File

@ -1,8 +1,6 @@
# TSL Devkit # TSL Devkit
该插件提供 `tsl` 语言的语法高亮以及`LSP`代码补全 该插件提供 tsl 语言的语法高亮
支持`LSP`需要安装`nodejs`
## VSCode ## VSCode

View File

@ -5,14 +5,13 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(nlohmann_json REQUIRED) find_package(nlohmann_json REQUIRED)
find_package(spdlog REQUIRED)
find_package(fmt REQUIRED)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
set(SOURCES set(SOURCES
src/main.cpp src/main.cpp
src/language/tsl_keywords.cpp src/language/tsl_keywords.cpp
src/lsp/dispacther.cpp src/lsp/dispacther.cpp
src/lsp/server.cpp src/lsp/server.cpp
src/lsp/logger.cpp
src/provider/base/provider_registry.cpp src/provider/base/provider_registry.cpp
src/provider/initialize/initialize_provider.cpp src/provider/initialize/initialize_provider.cpp
src/provider/initialized/initialized_provider.cpp src/provider/initialized/initialized_provider.cpp
@ -25,7 +24,5 @@ set(SOURCES
add_executable(${PROJECT_NAME} ${SOURCES}) add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} target_link_libraries(${PROJECT_NAME}
nlohmann_json::nlohmann_json nlohmann_json::nlohmann_json
spdlog::spdlog
fmt::fmt
) )

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";
}
}
}

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)...);
}
}

View File

@ -1,24 +1,24 @@
#include <exception> #include <exception>
#include <iostream> #include <iostream>
#include <spdlog/spdlog.h>
#ifdef _WIN32 #ifdef _WIN32
#include <io.h> #include <io.h>
#include <fcntl.h> #include <fcntl.h>
#endif #endif
#include "../provider/base/provider_registry.hpp" #include "../provider/base/provider_registry.hpp"
#include "./server.hpp" #include "./server.hpp"
#include "./logger.hpp"
namespace lsp namespace lsp
{ {
LspServer::LspServer() LspServer::LspServer()
{ {
providers::RegisterAllProviders(dispatcher_); providers::RegisterAllProviders(dispatcher_);
spdlog::debug("LSP server initialized with providers."); log::Debug("LSP server initialized with providers.");
} }
void LspServer::Run() void LspServer::Run()
{ {
spdlog::info("LSP server starting main loop..."); log::Info("LSP server starting main loop...");
while (true) while (true)
{ {
try try
@ -31,10 +31,10 @@ namespace lsp
} }
catch(const std::exception& e) catch(const std::exception& e)
{ {
spdlog::error("Error processing message: {}", e.what()); log::Error("Error processing message: ", e.what());
} }
} }
spdlog::info("LSP server main loop ended"); log::Info("LSP server main loop ended");
} }
std::optional<std::string> LspServer::ReadMessage() std::optional<std::string> LspServer::ReadMessage()
@ -45,7 +45,7 @@ namespace lsp
// 读取 LSP Header // 读取 LSP Header
while (std::getline(std::cin, line)) while (std::getline(std::cin, line))
{ {
spdlog::trace("Received header line: {}", line); log::Verbose("Received header line: ", line);
// 去掉尾部 \r // 去掉尾部 \r
if (!line.empty() && line.back() == '\r') if (!line.empty() && line.back() == '\r')
@ -63,11 +63,11 @@ namespace lsp
try try
{ {
content_length = std::stoul(length_str); content_length = std::stoul(length_str);
spdlog::trace("Content-Length: {}", content_length); log::Verbose("Content-Length: ", content_length);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
spdlog::error("Failed to parse Content-Length: {}", e.what()); log::Error("Failed to parse Content-Length: ", e.what());
return std::nullopt; return std::nullopt;
} }
} }
@ -76,7 +76,7 @@ namespace lsp
if (content_length == 0) if (content_length == 0)
{ {
spdlog::warn("No Content-Length found in header"); log::Warn("No Content-Length found in header");
return std::nullopt; return std::nullopt;
} }
@ -84,11 +84,11 @@ namespace lsp
std::string body(content_length, '\0'); std::string body(content_length, '\0');
std::cin.read(&body[0], content_length); std::cin.read(&body[0], content_length);
spdlog::trace("Message body: {}", body); log::Verbose("Message body: ", body);
if (std::cin.gcount() != static_cast<std::streamsize>(content_length)) if (std::cin.gcount() != static_cast<std::streamsize>(content_length))
{ {
spdlog::error("Read incomplete message body, expected: {}, got: {}", content_length, std::cin.gcount()); log::Error("Read incomplete message body, expected: ", content_length, ", got: ", std::cin.gcount());
return std::nullopt; return std::nullopt;
} }
return body; return body;
@ -100,14 +100,14 @@ namespace lsp
{ {
nlohmann::json json = nlohmann::json::parse(raw_request); nlohmann::json json = nlohmann::json::parse(raw_request);
LspRequest request(json); LspRequest request(json);
spdlog::trace("Processing method: {}", request.method); log::Debug("Processing method: ", request.method);
nlohmann::json response = dispatcher_.Dispatch(request); nlohmann::json response = dispatcher_.Dispatch(request);
return response; return response;
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
spdlog::error("Failed to handle request: {}", e.what()); log::Error("Failed to handle request: ", e.what());
return nlohmann::json(); return nlohmann::json();
} }
} }
@ -118,27 +118,27 @@ namespace lsp
size_t byte_length = response_str.length(); size_t byte_length = response_str.length();
// 调试:显示实际发送的原始内容 // 调试:显示实际发送的原始内容
spdlog::debug("Response length: {}", byte_length); log::Debug("Response length: ", byte_length);
spdlog::debug("Raw response content: [{}]", response_str); log::Debug("Raw response content: [", response_str, "]");
// 在程序启动时设置stdout为二进制模式只需设置一次 // 在程序启动时设置stdout为二进制模式只需设置一次
static bool binary_mode_set = false; static bool binary_mode_set = false;
if (!binary_mode_set) if (!binary_mode_set) {
{
#ifdef _WIN32 #ifdef _WIN32
_setmode(_fileno(stdout), _O_BINARY); _setmode(_fileno(stdout), _O_BINARY);
#endif #endif
binary_mode_set = true; binary_mode_set = true;
} }
// 构建完整消息
std::string header = "Content-Length: " + std::to_string(byte_length) + "\r\n\r\n";
// 使用 write 系统调用,绕过 C++ 流的缓冲和转换
std::cout.write(header.c_str(), header.length());
std::cout.write(response_str.c_str(), response_str.length());
std::cout.flush();
log::Verbose("Response sent successfully2");
// 构建完整消息
std::string header = "Content-Length: " + std::to_string(byte_length) + "\r\n\r\n";
// 使用 write 系统调用,绕过 C++ 流的缓冲和转换
std::cout.write(header.c_str(), header.length());
std::cout.write(response_str.c_str(), response_str.length());
std::cout.flush();
spdlog::trace("Response sent successfully");
} }
} }

View File

@ -1,65 +1,42 @@
#include <iostream> #include <iostream>
#include <exception> #include <exception>
#include <memory>
#include <vector>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include "./lsp/server.hpp" #include "./lsp/server.hpp"
#include "./lsp/logger.hpp"
void ParseArgs(int argc, char* argv[]) void ParseArgs(int argc, char* argv[])
{ {
std::vector<spdlog::sink_ptr> sinks;
bool use_stderr = false;
std::string log_file;
spdlog::level::level_enum log_level = spdlog::level::info;
for (int i = 1; i < argc; ++i) for (int i = 1; i < argc; ++i)
{ {
std::string arg = argv[i]; std::string arg = argv[i];
if (arg == "--log=trace")
log_level = spdlog::level::trace; if (arg == "--log=verbose")
else if (arg == "--log=debug") {
log_level = spdlog::level::debug; lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kVerbose);
else if (arg == "--log=info") } else if (arg == "--log=debug")
log_level = spdlog::level::info; {
else if (arg == "--log=warn") lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kDebug);
log_level = spdlog::level::warn; } else if (arg == "--log=info")
else if (arg == "--log=error") {
log_level = spdlog::level::err; lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kInfo);
else if (arg == "--log=off") } else if (arg == "--log=warn")
log_level = spdlog::level::off; {
else if (arg.find("--log-file=") == 0) lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kWarn);
log_file = arg.substr(11); // 跳过 "--log-file=" } else if (arg == "--log=error")
{
lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kError);
} else if (arg == "--log=off")
{
lsp::log::Logger::Instance().SetLevel(lsp::log::LogLevel::kOff);
} else if (arg.find("--log-file=") == 0)
{
std::string filename = arg.substr(11); // 跳过 "--log-file="
lsp::log::Logger::Instance().SetLogFile(filename);
}
else if (arg == "--log-stderr") else if (arg == "--log-stderr")
use_stderr = true;
}
sinks.clear();
if (use_stderr)
sinks.push_back(std::make_shared<spdlog::sinks::stderr_sink_mt>());
// 如果指定了日志文件,添加文件 sink
if (!log_file.empty())
{
try
{ {
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(log_file, true)); lsp::log::Logger::Instance().EnableStderr(true);
}
catch (const std::exception& e)
{
std::cerr << "[TSL-LSP] Failed to create log file: " << e.what() << std::endl;
} }
} }
std::shared_ptr<spdlog::logger> logger = std::make_shared<spdlog::logger>("tsl_lsp", sinks.begin(), sinks.end());
logger->set_level(log_level);
logger->set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] %v");
// 设置为默认日志器
spdlog::set_default_logger(logger);
spdlog::flush_on(spdlog::level::warn);
} }
int main(int argc, char* argv[]) int main(int argc, char* argv[])
@ -67,24 +44,23 @@ int main(int argc, char* argv[])
ParseArgs(argc, argv); ParseArgs(argc, argv);
try try
{ {
spdlog::info("TSL-LSP server starting..."); lsp::log::Info("TSL-LSP server starting...");
lsp::LspServer server; lsp::LspServer server;
server.Run(); server.Run();
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
std::cerr << "[TSL-LSP] Server fatal error: " << e.what() << std::endl; std::cerr << "[TSL-LSP] Server fatal error: " << e.what() << std::endl;
spdlog::error("Server fatal error: ", e.what()); lsp::log::Error("Server fatal error: ", e.what());
return 1; return 1;
} }
catch (...) catch (...)
{ {
std::cerr << "[TSL-LSP] Server unknown fatal error" << std::endl; std::cerr << "[TSL-LSP] Server unknown fatal error" << std::endl;
spdlog::error("Server unknown fatal error"); lsp::log::Error("Server unknown fatal error");
return 1; return 1;
} }
spdlog::info("TSL-LSP server stopped normally"); lsp::log::Info("TSL-LSP server stopped normally");
spdlog::shutdown();
return 0; return 0;
} }

View File

@ -1,4 +1,3 @@
#include <spdlog/spdlog.h>
#include "./provider_registry.hpp" #include "./provider_registry.hpp"
#include "../initialize/initialize_provider.hpp" #include "../initialize/initialize_provider.hpp"
#include "../initialized/initialized_provider.hpp" #include "../initialized/initialized_provider.hpp"
@ -6,13 +5,14 @@
#include "../text_document/did_change_provider.hpp" #include "../text_document/did_change_provider.hpp"
#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 "../../lsp/logger.hpp"
namespace lsp::providers namespace lsp::providers
{ {
void RegisterAllProviders(RequestDispatcher& dispatcher) void RegisterAllProviders(RequestDispatcher& dispatcher)
{ {
spdlog::info("Registering LSP providers..."); log::Info("Registering LSP providers...");
RegisterProvider<initialize::InitializeProvider>(dispatcher); RegisterProvider<initialize::InitializeProvider>(dispatcher);
RegisterProvider<initialized::InitializedProvider>(dispatcher); RegisterProvider<initialized::InitializedProvider>(dispatcher);
@ -21,7 +21,7 @@ namespace lsp::providers
RegisterProvider<text_document::CompletionProvider>(dispatcher); RegisterProvider<text_document::CompletionProvider>(dispatcher);
RegisterProvider<trace::SetTraceProvider>(dispatcher); RegisterProvider<trace::SetTraceProvider>(dispatcher);
spdlog::info("Successfully registered {} LSP providers", dispatcher.GetSupportedMethods().size()); log::Info("Successfully registered ", dispatcher.GetSupportedMethods().size(), " LSP providers");
} }
} }

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <spdlog/spdlog.h>
#include "../../lsp/dispacther.hpp" #include "../../lsp/dispacther.hpp"
#include "./provider_interface.hpp" #include "./provider_interface.hpp"
#include "../../lsp/logger.hpp"
namespace lsp::providers namespace lsp::providers
{ {
@ -15,7 +15,7 @@ namespace lsp::providers
auto provider = std::make_shared<ProviderClass>(); auto provider = std::make_shared<ProviderClass>();
spdlog::info("Registering {} for method: {}", provider->GetProviderName(), provider->GetMethod()); log::Info("Registering ", provider->GetProviderName(), " for method: ", provider->GetMethod());
dispatcher.RegisterProvider( dispatcher.RegisterProvider(
provider->GetMethod(), provider->GetMethod(),

View File

@ -1,16 +1,16 @@
#include <spdlog/spdlog.h>
#include "./initialize_provider.hpp" #include "./initialize_provider.hpp"
#include "../../lsp/logger.hpp"
namespace lsp::providers::initialize namespace lsp::providers::initialize
{ {
nlohmann::json InitializeProvider::ProvideResponse(const LspRequest& request) nlohmann::json InitializeProvider::ProvideResponse(const LspRequest& request)
{ {
spdlog::debug("InitializeProvider: Providing response for method {}", request.method);
nlohmann::json response; nlohmann::json response;
response["jsonrpc"] = "2.0"; response["jsonrpc"] = "2.0";
response["id"] = request.id; response["id"] = request.id;
response["result"] = BuildInitializeResult(); response["result"] = BuildInitializeResult();
log::Debug("InitializeProvider: Providing response for method '", request.method, "' with id ", request.id);
return response; return response;
} }

View File

@ -1,12 +1,12 @@
#include <spdlog/spdlog.h>
#include "./initialized_provider.hpp" #include "./initialized_provider.hpp"
#include "../../lsp/logger.hpp"
namespace lsp::providers::initialized namespace lsp::providers::initialized
{ {
nlohmann::json InitializedProvider::ProvideResponse(const LspRequest& request) nlohmann::json InitializedProvider::ProvideResponse(const LspRequest& request)
{ {
spdlog::debug("InitializeProvider: Providing response for method {}", request.method); log::Debug("InitializedProvider: Providing response for method '", request.method, "' with id ", request.id);
return nlohmann::json(); return nlohmann::json();
} }

View File

@ -1,11 +1,11 @@
#include <spdlog/spdlog.h>
#include "./completion_provider.hpp" #include "./completion_provider.hpp"
#include "../../lsp/logger.hpp"
namespace lsp::providers::text_document namespace lsp::providers::text_document
{ {
nlohmann::json CompletionProvider::ProvideResponse(const LspRequest& request) nlohmann::json CompletionProvider::ProvideResponse(const LspRequest& request)
{ {
spdlog::debug("CompletionProvider: Providing response for method {}", request.method); log::Debug("CompletionProvider: Providing response for method '", request.method, "' with id ", request.id);
try try
{ {
nlohmann::json response = BuildCompletionResponse(request); nlohmann::json response = BuildCompletionResponse(request);
@ -13,7 +13,7 @@ namespace lsp::providers::text_document
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
spdlog::error("{}: Error - ", GetProviderName(), e.what()); log::Error(GetProviderName(), ": Error - ", e.what());
nlohmann::json errorResponse = CreateErrorResponse(request.id, -32603, e.what()); nlohmann::json errorResponse = CreateErrorResponse(request.id, -32603, e.what());
return errorResponse; return errorResponse;
} }
@ -38,7 +38,7 @@ namespace lsp::providers::text_document
// 验证必要参数 // 验证必要参数
if (!request.params.contains("textDocument") || !request.params.contains("position")) if (!request.params.contains("textDocument") || !request.params.contains("position"))
{ {
spdlog::warn("{}: Missing required parameters in request", GetProviderName()); log::Warn(GetProviderName(), ": Missing required parameters in request");
// 返回空补全列表而非错误 // 返回空补全列表而非错误
response["result"] = BuildCompletionResult({}); response["result"] = BuildCompletionResult({});
return response; return response;
@ -49,7 +49,8 @@ namespace lsp::providers::text_document
nlohmann::json position = ExtractPosition(request.params); nlohmann::json position = ExtractPosition(request.params);
std::string prefix = ExtractPrefix(request.params); std::string prefix = ExtractPrefix(request.params);
spdlog::trace("{}: Processing completion request for URI='{}', Position={}, prefix='{}'", GetProviderName(), uri, position.dump(), prefix); log::Verbose(
GetProviderName(), ": Processing completion request for URI='", uri, "', Position=", position.dump(), ", Prefix='", prefix, "'");
// 收集所有补全项 // 收集所有补全项
std::vector<CompletionItem> allItems; std::vector<CompletionItem> allItems;
@ -65,7 +66,7 @@ namespace lsp::providers::text_document
// 构建响应 // 构建响应
response["result"] = BuildCompletionResult(allItems); response["result"] = BuildCompletionResult(allItems);
spdlog::info("{}: Provided {}", GetProviderName(), allItems.size()); log::Info(GetProviderName(), ": Provided ", allItems.size(), " completion items");
return response; return response;
} }
@ -87,10 +88,10 @@ namespace lsp::providers::text_document
if (params.contains("textDocument") && params["textDocument"].contains("uri")) if (params.contains("textDocument") && params["textDocument"].contains("uri"))
{ {
std::string uri = params["textDocument"]["uri"].get<std::string>(); std::string uri = params["textDocument"]["uri"].get<std::string>();
spdlog::trace("ExtractDocumentUri: Found URI: ", uri); log::Verbose("ExtractDocumentUri: Found URI: ", uri);
return uri; return uri;
} }
spdlog::warn("ExtractDocumentUri: No URI found in parameters"); log::Warn("ExtractDocumentUri: No URI found in parameters");
return ""; return "";
} }
@ -99,14 +100,14 @@ namespace lsp::providers::text_document
if (params.contains("position")) if (params.contains("position"))
{ {
nlohmann::json pos = params["position"]; nlohmann::json pos = params["position"];
spdlog::trace("ExtractPosition: Found position: ", pos.dump()); log::Verbose("ExtractPosition: Found position: ", pos.dump());
return pos; return pos;
} }
// 返回默认位置 // 返回默认位置
nlohmann::json defaultPos; nlohmann::json defaultPos;
defaultPos["line"] = 0; defaultPos["line"] = 0;
defaultPos["character"] = 0; defaultPos["character"] = 0;
spdlog::warn("ExtractPosition: No position found in parameters, using default (0, 0)"); log::Warn("ExtractPosition: No position found in parameters, using default (0, 0)");
return defaultPos; return defaultPos;
} }
@ -116,7 +117,7 @@ namespace lsp::providers::text_document
if (params.contains("prefix")) if (params.contains("prefix"))
{ {
std::string prefix = params["prefix"].get<std::string>(); std::string prefix = params["prefix"].get<std::string>();
spdlog::trace("ExtractPrefix: Found prefix form params: '", prefix, "'"); log::Verbose("ExtractPrefix: Found prefix form params: '", prefix, "'");
return prefix; return prefix;
} }
@ -124,13 +125,13 @@ namespace lsp::providers::text_document
if (params.contains("context") && params["context"].contains("prefix")) if (params.contains("context") && params["context"].contains("prefix"))
{ {
std::string prefix = params["context"]["prefix"].get<std::string>(); std::string prefix = params["context"]["prefix"].get<std::string>();
spdlog::trace("ExtractPrefix: Found prefix form params: '", prefix, "'"); log::Verbose("ExtractPrefix: Found prefix form params: '", prefix, "'");
return prefix; return prefix;
} }
// TODO: 理想情况下,应该从文档内容和位置计算前缀 // TODO: 理想情况下,应该从文档内容和位置计算前缀
// 这需要维护文档内容的状态 // 这需要维护文档内容的状态
spdlog::trace("ExtractPrefix: No prefix found, returning empty string"); log::Verbose("ExtractPrefix: No prefix found, returning empty string");
return ""; return "";
} }
@ -152,7 +153,7 @@ namespace lsp::providers::text_document
items.push_back(item); items.push_back(item);
} }
spdlog::debug("ProvideKeywordCompletions: Found ", items.size(), " keyword completions"); log::Debug("ProvideKeywordCompletions: Found ", items.size(), " keyword completions");
return items; return items;
} }
@ -161,7 +162,7 @@ namespace lsp::providers::text_document
const nlohmann::json& position, const nlohmann::json& position,
const std::string& prefix) const std::string& prefix)
{ {
spdlog::debug("ProvideContextualCompletions: Processing contextual completions for URI: ", uri); log::Debug("ProvideContextualCompletions: Processing contextual completions for URI: ", uri);
std::vector<CompletionItem> items; std::vector<CompletionItem> items;
// TODO: 基于上下文提供补全 // TODO: 基于上下文提供补全
@ -170,7 +171,7 @@ namespace lsp::providers::text_document
// - 函数名补全 // - 函数名补全
// - 类型补全 // - 类型补全
// - 属性补全等 // - 属性补全等
spdlog::debug("ProvideContextualCompletions: Found ", items.size(), " contextual completions"); log::Debug("ProvideContextualCompletions: Found ", items.size(), " contextual completions");
return items; return items;
} }
@ -204,7 +205,7 @@ namespace lsp::providers::text_document
error["message"] = GetProviderName() + ": " + message; error["message"] = GetProviderName() + ": " + message;
response["error"] = error; response["error"] = error;
spdlog::error("CreateErrorResponse: Created error response with code {} and message {}", code, message); log::Error("CreateErrorResponse: Created error response with code ", code, " and message: ", message);
return response; return response;
} }

View File

@ -1,13 +1,13 @@
#include <spdlog/spdlog.h>
#include "./did_change_provider.hpp" #include "./did_change_provider.hpp"
#include "./did_open_provider.hpp" #include "./did_open_provider.hpp"
#include "../../lsp/logger.hpp"
namespace lsp::providers::text_document namespace lsp::providers::text_document
{ {
nlohmann::json DidChangeProvider::ProvideResponse(const LspRequest& request) nlohmann::json DidChangeProvider::ProvideResponse(const LspRequest& request)
{ {
spdlog::debug("DidChangeProvider: Providing response for method {}", request.method); log::Debug("DidChangeProvider: Providing response for method '", request.method, "' with id ", request.id);
try try
{ {
auto params = request.params; auto params = request.params;

View File

@ -1,5 +1,5 @@
#include <spdlog/spdlog.h>
#include "./did_open_provider.hpp" #include "./did_open_provider.hpp"
#include "../../lsp/logger.hpp"
namespace lsp::providers::text_document namespace lsp::providers::text_document
{ {
@ -7,7 +7,7 @@ namespace lsp::providers::text_document
nlohmann::json DidOpenProvider::ProvideResponse(const LspRequest& request) nlohmann::json DidOpenProvider::ProvideResponse(const LspRequest& request)
{ {
spdlog::debug("DidOpenProvider: Providing response for method {}", request.method); log::Debug("DidOpenProvider: Providing response for method '", request.method, "' with id ", request.id);
try try
{ {
auto params = request.params; auto params = request.params;

View File

@ -2,11 +2,9 @@
// 字符串,数字,注释 // 字符串,数字,注释
// 注释 TODO // 注释 TODO
// abc();
a := "string"; // 字符串 a := "string"; // 字符串
b := 123; // 数字 b := 123; // 数字
c := 1.23; // 浮点数 c := 1.23; // 浮点数
a := "字符串函数func()";
// 类相关 // 类相关
@ -49,14 +47,13 @@ function f(a: real = 1; b: string = "123"; c: number = abc.CONST1): def; // 带
f(); f();
f(a, b); f(a, b);
f(a: 1; b: 's'); f(a: 1; b: 's');
f(f(2, "111"));
// 类变量声明,三元表达式(不应该有冲突) // 类变量声明,三元表达式(不应该有冲突)
a: string; a: string;
[weakref]b: MyClass; [weakref]b: MyClass;
a ? b : c; a ? b : c;
a ? b : c ? d : e; a ? b : c ? d : e;
return ifnil(rpr_.Caps) ? false : rpr_.Caps.IsApplied;
// select // select
Select ["test"] from table end; Select ["test"] from table end;
@ -65,5 +62,5 @@ select distinct ["test"] from table where ['a'] = 1 end;
// array // array
a := array(1, 2); a := array(1, 2);
b := array((1, 2), (3, 4)); b := array((1, 2), (3, 4))

View File

@ -13,7 +13,7 @@ syn case ignore
# Keywords # Keywords
# 程序结构声明 # 程序结构声明
syn keyword tslProgramStructure program function procedure nextgroup=tslFuncName skipwhite syn keyword tslProgramStructure program function procedure nextgroup=tslFuncName skipwhite
syn keyword tslModuleStructure unit uses implementation interface initialization finalization syn keyword tslModuleStructure unit uses implementation interface initalization finalization
# 数据类型 # 数据类型
syn keyword tslPrimitiveType string integer boolean int64 real array syn keyword tslPrimitiveType string integer boolean int64 real array
@ -87,9 +87,9 @@ syn match tslReturnType ':\s*[^;]*' contained contains=tslColon
syn match tslParamSep '[;,]' contained syn match tslParamSep '[;,]' contained
syn match tslColon ':' contained syn match tslColon ':' contained
syn match tslVarDecl '\(?\s\)\@<!\h\w\s:=\@!\s[^;?];' contains=tslVarName,tslVarType syn match tslVarDecl '\(?\s*\)\@<!\h\w*\s*:=\@!\s*[^;?]*;' contains=tslVarName,tslVarType
syn match tslVarName '\(?\s\)\@<!\h\w\ze\s:=\@!' contained syn match tslVarName '\(?\s*\)\@<!\h\w*\ze\s*:=\@!' contained
syn match tslVarType ':=\@!\s[^;?]' contained contains=tslColon syn match tslVarType ':=\@!\s*[^;?]*' contained contains=tslColon
# 匹配完整的属性链 # 匹配完整的属性链
syn match tslPropertyChain '\h\w*\%(\.\h\w*\)\+' contains=tslObjectName,tslPropertyDot,tslPropertyName syn match tslPropertyChain '\h\w*\%(\.\h\w*\)\+' contains=tslObjectName,tslPropertyDot,tslPropertyName
@ -99,30 +99,31 @@ syn match tslPropertyName '\.\@<=\h\w*\%(\ze\.\|\ze\s*[^(]\|\ze\s*$\)' contain
# Function calls # Function calls
syn match tslFuncCallName '\%(\<\%(function\|procedure\)\s\+\%(\h\w*\.\)\?\)\@<!\h\w*\ze\s*(' syn match tslFuncCallName '\%(\<\%(function\|procedure\)\s\+\%(\h\w*\.\)\?\)\@<!\h\w*\ze\s*('
\ containedin=ALLBUT,tslFuncParams,tslPropertyChain,tslFunction,tslFuncName,tslFuncNamePart,tslComment,tslString,tslRawString \ containedin=ALLBUT,tslFuncParams,tslPropertyChain,tslFunction,tslFuncName,tslFuncNamePart
syn region tslFuncCall syn region tslFuncCall
\ start='\h\w*\s*(\zs' \ start='\%(\<\h\w*\s*(\)'
\ end='\ze)' \ end=')'
\ contains=tslFuncCallName,tslFuncCall,tslNamedParam,tslPositionalParam,tslString,tslRawString,tslNumber,tslIdentifier,tslOperator,tslParamSep \ contains=tslCallParams,tslCallSep,tslCallValue,tslCallParam,tslString,tslNumber,tslIdentifier,tslOperator
\ transparent \ transparent
syn match tslNamedParam '\h\w*\s*:\s*[^;,)]*' contained syn region tslCallParams
\ contains=tslParamName,tslParamColon,tslParamValue \ start='\%(\<\h\w*\s*(\)\zs'
\ end='\ze)'
syn match tslParamName '\h\w*\ze\s*:' contained \ contained
syn match tslParamColon ':' contained \ contains=tslCallParam,tslCallValue,tslCallSep,tslString,tslNumber,tslIdentifier,tslOperator,tslFuncCall
syn match tslParamValue ':\s*\zs[^;,)]*' contained
\ contains=tslString,tslNumber,tslIdentifier,tslOperator,tslFuncCall,tslFuncCallName
syn match tslPositionalParam '[^;,():]*' contained
\ contains=tslString,tslNumber,tslIdentifier,tslOperator,tslFuncCall,tslFuncCallName
syn match tslParamSep '[;,]' contained
syn match tslCallParam '\h\w*\ze\s*:' contained
syn match tslCallValue ':\s*\zs[^;)]*' contained
syn match tslCallValue
\ ':\s*\zs\\%([^;,)]\+\|.\{-}\%([;,)]\|$\))'
\ contained
\ contains=tslString,tslNumber,tslIdentifier,tslOperator,tslFunctionCall
syn match tslCallSep '[;,]' contained
# Operators and delimiters # Operators and delimiters
syn match tslOperator '[+\-*/<>=!&|^~%]' syn match tslOperator '[+\-*/<>=!&|^~%]'
syn match tslOperator ':='
syn match tslDelimiter '[()[\]{},;:.@?]' syn match tslDelimiter '[()[\]{},;:.@?]'
# Comments # Comments

View File

@ -2,10 +2,6 @@
Notable changes to the `TSL` extension will be documented in this file. Notable changes to the `TSL` extension will be documented in this file.
## [2.1.0]: 2025-06-25
- 更换`LSP`日志模块,启动参数`verbose -> trace`
## [2.0.0]: 2025-06-22 ## [2.0.0]: 2025-06-22
- 初步支持`LSP`--关键字补全 - 初步支持`LSP`--关键字补全

Binary file not shown.

Binary file not shown.

View File

@ -1,12 +1,12 @@
{ {
"name": "tsl-devkit", "name": "tsl-devkit",
"version": "2.1.0", "version": "1.4.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "tsl-devkit", "name": "tsl-devkit",
"version": "2.1.0", "version": "1.4.0",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"vscode-languageclient": "^9.0.1" "vscode-languageclient": "^9.0.1"
@ -15,6 +15,7 @@
"@types/node": "^24.0.3", "@types/node": "^24.0.3",
"@types/vscode": "^1.101.0", "@types/vscode": "^1.101.0",
"@vscode/vsce": "^3.5.0", "@vscode/vsce": "^3.5.0",
"cp": "^0.2.0",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"typescript": "^5.8.3" "typescript": "^5.8.3"
}, },
@ -1297,6 +1298,13 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/cp": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/cp/-/cp-0.2.0.tgz",
"integrity": "sha512-4ftCvShHjIZG/zzomHyunNpBof3sOFTTmU6s6q9DdqAL/ANqrKV3pr6Z6kVfBI4hjn59DFLImrBqn7GuuMqSZA==",
"dev": true,
"license": "MIT"
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.6", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",

View File

@ -1,7 +1,7 @@
{ {
"name": "tsl-devkit", "name": "tsl-devkit",
"displayName": "TSL", "displayName": "TSL",
"version": "2.1.0", "version": "2.0.0",
"description": "TSL syntax highlighter support for VSCode.", "description": "TSL syntax highlighter support for VSCode.",
"publisher": "csh", "publisher": "csh",
"homepage": "https://git.mytsl.cn/csh/tsl-devkit", "homepage": "https://git.mytsl.cn/csh/tsl-devkit",

Binary file not shown.