♻️ 重构
This commit is contained in:
@@ -14,10 +14,9 @@ namespace lsp::provider::text_document
|
||||
return "TextDocumentCodeAction";
|
||||
}
|
||||
|
||||
std::string CodeAction::ProvideResponse(const protocol::RequestMessage& request, ExecutionContext& context)
|
||||
std::string CodeAction::ProvideResponse(const protocol::RequestMessage& request, [[maybe_unused]] ExecutionContext& context)
|
||||
{
|
||||
static_cast<void>(context);
|
||||
spdlog::debug("TextDocumentCodeActionProvider: Providing response for method {}", request.method);
|
||||
spdlog::debug("TextDocumentCodeActionProvider: Providing response for method {}", request.method);
|
||||
|
||||
// TODO: Implement the actual request handling logic
|
||||
// 1. Parse request parameters
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,86 +1,14 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../base/interface.hpp"
|
||||
#include "../../service/detail/symbol/types.hpp"
|
||||
#include "../../language/symbol/types.hpp"
|
||||
#include "../../service/symbol.hpp"
|
||||
|
||||
namespace lsp::provider::text_document
|
||||
{
|
||||
struct CompletionContext
|
||||
{
|
||||
protocol::DocumentUri uri;
|
||||
protocol::Position position;
|
||||
std::string prefix;
|
||||
std::string line_content;
|
||||
bool is_unit_context = false; // unit( 上下文
|
||||
bool is_class_context = false; // class( 上下文
|
||||
bool is_new_context = false; // new 上下文
|
||||
bool is_class_method_context = false; // class(xxx)
|
||||
std::string class_name;
|
||||
};
|
||||
|
||||
// 临时设计
|
||||
struct CompletionInfo
|
||||
{
|
||||
protocol::string type;
|
||||
protocol::string class_name;
|
||||
};
|
||||
|
||||
enum class CompletionSource
|
||||
{
|
||||
kEditing, // 当前编辑文档
|
||||
kWorkspace, // 工作区符号
|
||||
kSystem, // 系统库符号
|
||||
kKeyword // 关键字
|
||||
};
|
||||
|
||||
struct SourcedCompletionItem
|
||||
{
|
||||
protocol::CompletionItem item;
|
||||
CompletionSource source;
|
||||
};
|
||||
|
||||
namespace context_analyzer
|
||||
{
|
||||
CompletionContext Analyze(const protocol::CompletionParams& params, const std::optional<std::string>& document_content);
|
||||
|
||||
bool IsUnitContext(const std::string& line);
|
||||
bool IsClassContext(const std::string& line);
|
||||
bool IsNewContext(const std::string& line);
|
||||
bool IsClassMethodContext(const std::string& line, std::string& class_name, std::string& method_prefix);
|
||||
std::string ExtractPrefix(size_t cursor_pos, size_t line_start, const std::string& content);
|
||||
std::string ExtractUnitPrefix(const std::string& line);
|
||||
std::string ExtractClassPrefix(const std::string& line);
|
||||
std::string ExtractNewPrefix(const std::string& line);
|
||||
}
|
||||
|
||||
class Completion : public IRequestProvider
|
||||
{
|
||||
public:
|
||||
std::string GetMethod() const override;
|
||||
std::string GetProviderName() const override;
|
||||
std::string ProvideResponse(const protocol::RequestMessage& request, ExecutionContext& execution_context) override;
|
||||
|
||||
private:
|
||||
protocol::CompletionList BuildCompletionList(const protocol::CompletionParams& params, ExecutionContext& execution_context);
|
||||
|
||||
// 符号收集
|
||||
std::vector<SourcedCompletionItem> CollectKeywords(const std::string& prefix);
|
||||
std::vector<SourcedCompletionItem> CollectClassMethods(const CompletionContext& comp_context, const service::Symbol& symbol_service);
|
||||
std::vector<SourcedCompletionItem> CollectUnitNames(const CompletionContext& comp_context, const service::Symbol& symbol_service);
|
||||
std::vector<SourcedCompletionItem> CollectClassNames(const CompletionContext& comp_context, const service::Symbol& symbol_service, bool is_new_context = false);
|
||||
std::vector<SourcedCompletionItem> CollectEditingFunctions(const CompletionContext& comp_context, const service::Symbol& symbol_service);
|
||||
std::vector<SourcedCompletionItem> CollectWorkspaceFunctions(const CompletionContext& comp_context, const service::Symbol& symbol_service);
|
||||
std::vector<SourcedCompletionItem> CollectSystemFunctions(const CompletionContext& comp_context, const service::Symbol& symbol_service);
|
||||
|
||||
// 过滤和排序
|
||||
std::vector<protocol::CompletionItem> FilterAndSort(const std::vector<SourcedCompletionItem>& items, const std::string& prefix);
|
||||
static int GetMatchScore(const std::string& label, const std::string& prefix);
|
||||
|
||||
// 格式化辅助
|
||||
static protocol::CompletionItemKind ToCompletionItemKind(language::symbol::SymbolKind kind);
|
||||
// static std::string FormatSignature(const service::SymbolSignature& sig);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <cstring>
|
||||
#include "./definition.hpp"
|
||||
#include "../../protocol/transform/facade.hpp"
|
||||
#include "../../service/document.hpp"
|
||||
#include "../../manager/manager_hub.hpp"
|
||||
#include "../../utils/text_coordinates.hpp"
|
||||
#include "../../language/symbol/table.hpp"
|
||||
#include "../../language/semantic/semantic_model.hpp"
|
||||
#include "../../utils/string.hpp"
|
||||
|
||||
namespace lsp::provider::text_document
|
||||
{
|
||||
@@ -25,7 +30,8 @@ namespace lsp::provider::text_document
|
||||
return BuildErrorResponseMessage(request, protocol::ErrorCodes::InvalidParams, "Missing params");
|
||||
}
|
||||
|
||||
protocol::DefinitionParams params = transform::As<protocol::DefinitionParams>(request.params.value());
|
||||
protocol::DefinitionParams params =
|
||||
transform::FromLSPAny.template operator()<protocol::DefinitionParams>(request.params.value());
|
||||
|
||||
auto location = BuildDefinitionResponse(params, context);
|
||||
|
||||
@@ -34,7 +40,7 @@ namespace lsp::provider::text_document
|
||||
|
||||
if (location.has_value())
|
||||
{
|
||||
response.result = transform::LSPAny(*location);
|
||||
response.result = transform::ToLSPAny(*location);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -50,6 +56,141 @@ namespace lsp::provider::text_document
|
||||
std::optional<protocol::Location> Definition::BuildDefinitionResponse(const protocol::DefinitionParams& params, ExecutionContext& context)
|
||||
{
|
||||
spdlog::trace("{}: Processing definition request for URI='{}', Position=({}, {})", GetProviderName(), params.textDocument.uri, params.position.line, params.position.character);
|
||||
|
||||
std::string identifier = GetIdentifierAtPosition(params.textDocument.uri, params.position, context);
|
||||
if (identifier.empty())
|
||||
{
|
||||
spdlog::info("{}: No identifier at position", GetProviderName());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto& hub = context.GetManagerHub();
|
||||
auto content_opt = hub.documents().GetContent(params.textDocument.uri);
|
||||
if (!content_opt)
|
||||
{
|
||||
spdlog::warn("{}: Document content not found for {}", GetProviderName(), params.textDocument.uri);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
language::ast::Location loc{};
|
||||
loc.start_line = loc.end_line = params.position.line;
|
||||
loc.start_column = loc.end_column = params.position.character;
|
||||
loc.start_offset = loc.end_offset = utils::text_coordinates::ToOffset(params.position, *content_opt);
|
||||
|
||||
if (auto local = ResolveInCurrentDocument(params.textDocument.uri, identifier, loc, context))
|
||||
{
|
||||
return local;
|
||||
}
|
||||
|
||||
return ResolveInIndex(identifier, context);
|
||||
}
|
||||
|
||||
std::optional<protocol::Location> Definition::ResolveInCurrentDocument(const protocol::DocumentUri& uri,
|
||||
const std::string& identifier,
|
||||
const language::ast::Location& location,
|
||||
ExecutionContext& context)
|
||||
{
|
||||
auto& hub = context.GetManagerHub();
|
||||
auto* table = hub.symbols().GetSymbolTable(uri);
|
||||
auto* semantic = hub.symbols().GetSemanticModel(uri);
|
||||
|
||||
if (table && semantic)
|
||||
{
|
||||
auto resolved = semantic->name_resolver().ResolveNameAtLocation(identifier, location);
|
||||
if (resolved.IsResolved())
|
||||
{
|
||||
if (const auto* symbol = table->definition(resolved.symbol_id))
|
||||
{
|
||||
return ToLocation(uri, symbol->selection_range());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (table)
|
||||
{
|
||||
auto matches = table->FindSymbolsByName(identifier);
|
||||
for (auto id : matches)
|
||||
{
|
||||
if (const auto* symbol = table->definition(id))
|
||||
{
|
||||
return ToLocation(uri, symbol->selection_range());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<protocol::Location> Definition::ResolveInIndex(const std::string& identifier, ExecutionContext& context)
|
||||
{
|
||||
auto& symbols = context.GetManagerHub().symbols();
|
||||
auto lower = utils::ToLower(identifier);
|
||||
|
||||
auto search_kind = [&](protocol::SymbolKind kind) -> std::optional<protocol::Location> {
|
||||
auto indexed = symbols.QueryIndexedSymbols(kind, std::nullopt);
|
||||
for (const auto& item : indexed)
|
||||
{
|
||||
if (!utils::IEquals(item.name, lower))
|
||||
continue;
|
||||
|
||||
if (const auto* table = symbols.GetSymbolTable(item.uri))
|
||||
{
|
||||
if (const auto* sym = table->definition(item.id))
|
||||
{
|
||||
return ToLocation(item.uri, sym->selection_range());
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
if (auto result = search_kind(protocol::SymbolKind::Function))
|
||||
return result;
|
||||
if (auto result = search_kind(protocol::SymbolKind::Class))
|
||||
return result;
|
||||
if (auto result = search_kind(protocol::SymbolKind::Module))
|
||||
return result;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
protocol::Location Definition::ToLocation(const protocol::DocumentUri& uri, const language::ast::Location& loc)
|
||||
{
|
||||
protocol::Location location;
|
||||
location.uri = uri;
|
||||
location.range.start.line = loc.start_line;
|
||||
location.range.start.character = loc.start_column;
|
||||
location.range.end.line = loc.end_line;
|
||||
location.range.end.character = loc.end_column;
|
||||
return location;
|
||||
}
|
||||
|
||||
std::string Definition::GetIdentifierAtPosition(const protocol::DocumentUri& uri, const protocol::Position& position, ExecutionContext& context)
|
||||
{
|
||||
auto& hub = context.GetManagerHub();
|
||||
auto content = hub.documents().GetContent(uri);
|
||||
auto tree = hub.parser().GetTree(uri);
|
||||
|
||||
if (!content.has_value() || !tree)
|
||||
return "";
|
||||
|
||||
size_t byte_offset = utils::text_coordinates::ToOffset(position, *content);
|
||||
TSNode root = ts_tree_root_node(tree);
|
||||
TSNode node = ts_node_descendant_for_byte_range(root, static_cast<uint32_t>(byte_offset), static_cast<uint32_t>(byte_offset));
|
||||
|
||||
while (!ts_node_is_null(node))
|
||||
{
|
||||
const char* node_type = ts_node_type(node);
|
||||
if (strcmp(node_type, kIdentifier) == 0)
|
||||
{
|
||||
uint32_t start = ts_node_start_byte(node);
|
||||
uint32_t end = ts_node_end_byte(node);
|
||||
if (start < content->size() && end <= content->size())
|
||||
return content->substr(start, end - start);
|
||||
}
|
||||
node = ts_node_parent(node);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "../base/interface.hpp"
|
||||
#include "../../language/ast/types.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <tree_sitter/api.h>
|
||||
@@ -18,24 +19,18 @@ namespace lsp::provider::text_document
|
||||
|
||||
private:
|
||||
std::optional<protocol::Location> BuildDefinitionResponse(const protocol::DefinitionParams& params, ExecutionContext& context);
|
||||
std::optional<protocol::Location> FindLocalDefinition(const protocol::DocumentUri& uri, const protocol::Position& position, ExecutionContext& context);
|
||||
std::optional<protocol::Location> ResolveInCurrentDocument(const protocol::DocumentUri& uri,
|
||||
const std::string& identifier,
|
||||
const language::ast::Location& location,
|
||||
ExecutionContext& context);
|
||||
std::optional<protocol::Location> ResolveInIndex(const std::string& identifier, ExecutionContext& context);
|
||||
std::string GetIdentifierAtPosition(const protocol::DocumentUri& uri, const protocol::Position& position, ExecutionContext& context);
|
||||
|
||||
TSNode FindDefinitionNode(TSNode root, const std::string& identifier, const std::string& content);
|
||||
static protocol::Location ToLocation(const protocol::DocumentUri& uri, const language::ast::Location& loc);
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr const char* kUnit = "unit";
|
||||
constexpr const char* kInterfaceSection = "interface_section";
|
||||
constexpr const char* kImplementationSection = "implementation_section";
|
||||
constexpr const char* kFunctionDefinition = "function_definition_statement";
|
||||
constexpr const char* kFunctionDeclaration = "function_declaration_statement";
|
||||
constexpr const char* kClassDefinition = "class_definition_statement";
|
||||
constexpr const char* kVarStatement = "var_statement";
|
||||
constexpr const char* kConstStatement = "const_statement";
|
||||
constexpr const char* kIdentifier = "identifier";
|
||||
constexpr const char* kMethodWithImplementation = "method_with_implementation";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,10 +14,9 @@ namespace lsp::provider::text_document
|
||||
return "TextDocumentDiagnostic";
|
||||
}
|
||||
|
||||
std::string Diagnostic::ProvideResponse(const protocol::RequestMessage& request, ExecutionContext& context)
|
||||
std::string Diagnostic::ProvideResponse(const protocol::RequestMessage& request, [[maybe_unused]] ExecutionContext& context)
|
||||
{
|
||||
static_cast<void>(context);
|
||||
spdlog::debug("TextDocumentDiagnosticProvider: Providing response for method {}", request.method);
|
||||
spdlog::debug("TextDocumentDiagnosticProvider: Providing response for method {}", request.method);
|
||||
|
||||
// TODO: Implement the actual request handling logic
|
||||
// 1. Parse request parameters
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <spdlog/spdlog.h>
|
||||
#include "./did_change.hpp"
|
||||
#include "../../protocol/transform/facade.hpp"
|
||||
#include "../../service/document.hpp"
|
||||
|
||||
namespace lsp::provider::text_document
|
||||
{
|
||||
@@ -22,8 +21,8 @@ namespace lsp::provider::text_document
|
||||
|
||||
auto params = transform::FromLSPAny.template operator()<protocol::DidChangeTextDocumentParams>(notification.params.value());
|
||||
|
||||
auto document_service = context.GetService<service::Document>();
|
||||
document_service->UpdateDocument(params);
|
||||
auto& documents = context.GetManagerHub().documents();
|
||||
documents.UpdateDocument(params);
|
||||
|
||||
spdlog::info("Document updated: {}", params.textDocument.uri);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <spdlog/spdlog.h>
|
||||
#include "./did_close.hpp"
|
||||
#include "../../protocol/transform/facade.hpp"
|
||||
#include "../../service/document.hpp"
|
||||
|
||||
namespace lsp::provider::text_document
|
||||
{
|
||||
@@ -22,8 +21,8 @@ namespace lsp::provider::text_document
|
||||
|
||||
protocol::DidCloseTextDocumentParams params = transform::FromLSPAny.template operator()<protocol::DidCloseTextDocumentParams>(notification.params.value());
|
||||
|
||||
auto document_service = context.GetService<service::Document>();
|
||||
document_service->CloseDocument(params);
|
||||
auto& documents = context.GetManagerHub().documents();
|
||||
documents.CloseDocument(params);
|
||||
|
||||
spdlog::info("Document closed: {}", params.textDocument.uri);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <spdlog/spdlog.h>
|
||||
#include "./did_open.hpp"
|
||||
#include "../../protocol/transform/facade.hpp"
|
||||
#include "../../service/document.hpp"
|
||||
|
||||
namespace lsp::provider::text_document
|
||||
{
|
||||
@@ -21,8 +20,8 @@ namespace lsp::provider::text_document
|
||||
|
||||
auto params = transform::FromLSPAny.template operator()<protocol::DidOpenTextDocumentParams>(notification.params.value());
|
||||
|
||||
auto document_service = context.GetService<service::Document>();
|
||||
document_service->OpenDocument(params);
|
||||
auto& documents = context.GetManagerHub().documents();
|
||||
documents.OpenDocument(params);
|
||||
|
||||
/*
|
||||
if (auto* symbolService = context.TryGetService<SymbolService>()) {
|
||||
|
||||
@@ -14,10 +14,9 @@ namespace lsp::provider::text_document
|
||||
return "TextDocumentDocumentColor";
|
||||
}
|
||||
|
||||
std::string DocumentColor::ProvideResponse(const protocol::RequestMessage& request, ExecutionContext& context)
|
||||
std::string DocumentColor::ProvideResponse(const protocol::RequestMessage& request, [[maybe_unused]] ExecutionContext& context)
|
||||
{
|
||||
static_cast<void>(context);
|
||||
spdlog::debug("TextDocumentDocumentColorProvider: Providing response for method {}", request.method);
|
||||
spdlog::debug("TextDocumentDocumentColorProvider: Providing response for method {}", request.method);
|
||||
|
||||
// TODO: Implement the actual request handling logic
|
||||
// 1. Parse request parameters
|
||||
|
||||
@@ -14,10 +14,9 @@ namespace lsp::provider::text_document
|
||||
return "TextDocumentDocumentHighlight";
|
||||
}
|
||||
|
||||
std::string DocumentHighlight::ProvideResponse(const protocol::RequestMessage& request, ExecutionContext& context)
|
||||
std::string DocumentHighlight::ProvideResponse(const protocol::RequestMessage& request, [[maybe_unused]] ExecutionContext& context)
|
||||
{
|
||||
static_cast<void>(context);
|
||||
spdlog::debug("TextDocumentDocumentHighlightProvider: Providing response for method {}", request.method);
|
||||
spdlog::debug("TextDocumentDocumentHighlightProvider: Providing response for method {}", request.method);
|
||||
|
||||
// TODO: Implement the actual request handling logic
|
||||
// 1. Parse request parameters
|
||||
|
||||
@@ -4,684 +4,6 @@
|
||||
|
||||
namespace lsp::provider::text_document
|
||||
{
|
||||
bool IsFieldName(TSNode parent, TSNode node, const std::string& field_name)
|
||||
{
|
||||
uint32_t child_count = ts_node_child_count(parent);
|
||||
for (uint32_t i = 0; i < child_count; ++i)
|
||||
{
|
||||
TSNode sibling = ts_node_child(parent, i);
|
||||
if (ts_node_eq(sibling, node))
|
||||
{
|
||||
const char* actual_field = ts_node_field_name_for_child(parent, i);
|
||||
return actual_field && std::string(actual_field) == field_name;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SemanticToken::SemanticToken(protocol::uinteger l, protocol::uinteger c, protocol::uinteger len, SemanticTokenType t, protocol::uinteger mod) :
|
||||
line(l), character(c), length(len), type(t), modifiers(mod)
|
||||
{
|
||||
}
|
||||
|
||||
SemanticTokensCache::SemanticTokensCache(const std::string& id, std::vector<uint32_t> tokens, const std::string& version) :
|
||||
result_id(id), data(std::move(tokens)), document_version(version)
|
||||
{
|
||||
spdlog::debug("SemanticTokensCache: Created cache entry with {} tokens, result_id={}, version={}", data.size() / 5, id, version);
|
||||
}
|
||||
|
||||
SemanticTokensCore::SemanticTokensCore()
|
||||
{
|
||||
spdlog::info("SemanticTokensCore: Initializing semantic tokens core");
|
||||
InitNodeTypeMap();
|
||||
spdlog::info("SemanticTokensCore: Initialization completed with {} node type mappings", node_type_map_.size());
|
||||
}
|
||||
|
||||
SemanticTokensCore& SemanticTokensCore::GetInstance()
|
||||
{
|
||||
static SemanticTokensCore instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void SemanticTokensCore::InitNodeTypeMap()
|
||||
{
|
||||
spdlog::trace("SemanticTokensCore: Initializing node type mappings");
|
||||
// ========== 基本字面量 ==========
|
||||
node_type_map_["number"] = SemanticTokenType::kNumber;
|
||||
node_type_map_["string"] = SemanticTokenType::kString;
|
||||
node_type_map_["escape_sequence"] = SemanticTokenType::kString;
|
||||
node_type_map_["boolean"] = SemanticTokenType::kEnum;
|
||||
node_type_map_["true"] = SemanticTokenType::kEnum;
|
||||
node_type_map_["false"] = SemanticTokenType::kEnum;
|
||||
node_type_map_["nil"] = SemanticTokenType::kEnum;
|
||||
node_type_map_["infinity"] = SemanticTokenType::kNumber;
|
||||
node_type_map_["plus_inf"] = SemanticTokenType::kNumber;
|
||||
node_type_map_["minus_inf"] = SemanticTokenType::kNumber;
|
||||
node_type_map_["ellipsis"] = SemanticTokenType::kOperator;
|
||||
|
||||
// ========== 注释 ==========
|
||||
node_type_map_["line_comment"] = SemanticTokenType::kComment;
|
||||
node_type_map_["block_comment"] = SemanticTokenType::kComment;
|
||||
node_type_map_["nested_comment"] = SemanticTokenType::kComment;
|
||||
node_type_map_["html_comment"] = SemanticTokenType::kComment;
|
||||
|
||||
// ========== 声明关键字 ==========
|
||||
node_type_map_["function"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["var"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["static"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["global"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["const"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["class"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["type"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["property"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["unit"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["interface"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["implementation"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["initialization"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["finalization"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["uses"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["out"] = SemanticTokenType::kKeyword;
|
||||
|
||||
// ========== 控制流关键字 ==========
|
||||
node_type_map_["if"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["else"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["then"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["for"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["while"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["repeat"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["until"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["case"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["of"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["try"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["except"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["end"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["begin"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["do"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["to"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["downto"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["in"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["is"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["like"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["as"] = SemanticTokenType::kKeyword;
|
||||
|
||||
// ========== 语句关键字 ==========
|
||||
node_type_map_["return"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["break"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["continue"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["inherited"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["echo"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["raise"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["new"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["array"] = SemanticTokenType::kKeyword;
|
||||
|
||||
// ========== 访问修饰符 ==========
|
||||
node_type_map_["public"] = SemanticTokenType::kModifier;
|
||||
node_type_map_["private"] = SemanticTokenType::kModifier;
|
||||
node_type_map_["protected"] = SemanticTokenType::kModifier;
|
||||
node_type_map_["virtual"] = SemanticTokenType::kModifier;
|
||||
node_type_map_["override"] = SemanticTokenType::kModifier;
|
||||
node_type_map_["overload"] = SemanticTokenType::kModifier;
|
||||
node_type_map_["operator"] = SemanticTokenType::kModifier;
|
||||
node_type_map_["weakref"] = SemanticTokenType::kModifier;
|
||||
node_type_map_["autoref"] = SemanticTokenType::kModifier;
|
||||
|
||||
// ========== 属性访问器 ==========
|
||||
node_type_map_["read"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["write"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["index"] = SemanticTokenType::kKeyword;
|
||||
|
||||
// ========== SQL关键字 ==========
|
||||
node_type_map_["select"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["sselect"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["vselect"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["mselect"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["update"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["insert"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["delete"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["from"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["where"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["join"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["left"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["right"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["full"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["cross"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["on"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["with"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["group"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["by"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["order"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["having"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["distinct"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["drange"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["into"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["values"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["set"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["insertfields"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["asc"] = SemanticTokenType::kKeyword;
|
||||
node_type_map_["desc"] = SemanticTokenType::kKeyword;
|
||||
|
||||
// ========== 逻辑操作符关键字 ==========
|
||||
node_type_map_["and"] = SemanticTokenType::kOperator;
|
||||
node_type_map_["or"] = SemanticTokenType::kOperator;
|
||||
node_type_map_["not"] = SemanticTokenType::kOperator;
|
||||
node_type_map_["mod"] = SemanticTokenType::kOperator;
|
||||
node_type_map_["div"] = SemanticTokenType::kOperator;
|
||||
node_type_map_["shl"] = SemanticTokenType::kOperator;
|
||||
node_type_map_["shr"] = SemanticTokenType::kOperator;
|
||||
node_type_map_["rol"] = SemanticTokenType::kOperator;
|
||||
node_type_map_["ror"] = SemanticTokenType::kOperator;
|
||||
|
||||
// ========== 集合操作符 ==========
|
||||
node_type_map_["union"] = SemanticTokenType::kOperator;
|
||||
node_type_map_["union2"] = SemanticTokenType::kOperator;
|
||||
node_type_map_["intersect"] = SemanticTokenType::kOperator;
|
||||
node_type_map_["outersect"] = SemanticTokenType::kOperator;
|
||||
node_type_map_["minus"] = SemanticTokenType::kOperator;
|
||||
|
||||
// ========== TSLX模板标签 ==========
|
||||
node_type_map_["tslx"] = SemanticTokenType::kMacro;
|
||||
node_type_map_["tsl"] = SemanticTokenType::kMacro;
|
||||
node_type_map_["html_tag_name"] = SemanticTokenType::kNamespace;
|
||||
|
||||
// ========== 特殊字段 ==========
|
||||
node_type_map_["sql_field"] = SemanticTokenType::kProperty;
|
||||
node_type_map_["_sql_field_simple"] = SemanticTokenType::kProperty;
|
||||
node_type_map_["_sql_field_with_table"] = SemanticTokenType::kProperty;
|
||||
|
||||
// ========== 类型规范 ==========
|
||||
node_type_map_["type_specification"] = SemanticTokenType::kType;
|
||||
|
||||
// 默认标识符为变量
|
||||
node_type_map_["identifier"] = SemanticTokenType::kVariable;
|
||||
}
|
||||
|
||||
std::string SemanticTokensCore::GenerateResultId(const std::string& uri, const std::string& version)
|
||||
{
|
||||
return uri + ":" + version + ":" + std::to_string(std::time(nullptr));
|
||||
}
|
||||
|
||||
std::vector<SemanticToken> SemanticTokensCore::CollectTokensInRange(TSNode root, const std::string& source, const protocol::Range* range)
|
||||
{
|
||||
std::vector<SemanticToken> tokens;
|
||||
TraverseNode(root, source, tokens, range);
|
||||
|
||||
// Sort by position
|
||||
std::sort(tokens.begin(), tokens.end(), [](const SemanticToken& a, const SemanticToken& b) {
|
||||
if (a.line != b.line)
|
||||
return a.line < b.line;
|
||||
return a.character < b.character;
|
||||
});
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
void SemanticTokensCore::TraverseNode(TSNode node, const std::string& source_code, std::vector<SemanticToken>& tokens, const protocol::Range* range)
|
||||
{
|
||||
if (ts_node_is_null(node))
|
||||
{
|
||||
spdlog::trace("SemanticTokensCore: Skipping null node");
|
||||
return;
|
||||
}
|
||||
|
||||
TSPoint start = ts_node_start_point(node);
|
||||
TSPoint end = ts_node_end_point(node);
|
||||
|
||||
if (range && !IsNodeInRange(start, end, *range))
|
||||
return;
|
||||
|
||||
ProcessNode(node, tokens);
|
||||
|
||||
uint32_t child_count = ts_node_child_count(node);
|
||||
for (uint32_t i = 0; i < child_count; ++i)
|
||||
{
|
||||
TSNode child = ts_node_child(node, i);
|
||||
TraverseNode(child, source_code, tokens, range);
|
||||
}
|
||||
spdlog::trace("TraverseNode end...");
|
||||
}
|
||||
|
||||
void SemanticTokensCore::ProcessNode(TSNode node, std::vector<SemanticToken>& tokens)
|
||||
{
|
||||
std::string type_str(ts_node_type(node));
|
||||
spdlog::trace("ProcessNode type_str = {}", type_str);
|
||||
|
||||
auto it = node_type_map_.find(type_str);
|
||||
if (it != node_type_map_.end())
|
||||
{
|
||||
TSPoint start = ts_node_start_point(node);
|
||||
uint32_t start_byte = ts_node_start_byte(node);
|
||||
uint32_t end_byte = ts_node_end_byte(node);
|
||||
uint32_t length = end_byte - start_byte;
|
||||
|
||||
SemanticTokenType token_type = it->second;
|
||||
if (token_type == SemanticTokenType::kVariable)
|
||||
token_type = DetermineIdentifierType(node);
|
||||
|
||||
uint32_t modifiers = DetermineModifiers(node);
|
||||
|
||||
tokens.emplace_back(start.row, start.column, length, token_type, modifiers);
|
||||
|
||||
spdlog::debug("Added semantic token: type = {}, line = {}, char = {}, length = {}", type_str, start.row, start.column, length);
|
||||
}
|
||||
}
|
||||
|
||||
SemanticTokenType SemanticTokensCore::DetermineIdentifierType(TSNode node)
|
||||
{
|
||||
TSNode parent = ts_node_parent(node);
|
||||
if (ts_node_is_null(parent))
|
||||
return SemanticTokenType::kVariable;
|
||||
|
||||
const char* parent_type = ts_node_type(parent);
|
||||
std::string parent_str(parent_type);
|
||||
|
||||
// 函数声明和定义
|
||||
if ((parent_str == "function_declaration_statement" ||
|
||||
parent_str == "function_definition_statement" ||
|
||||
parent_str == "anonymous_function_statement" ||
|
||||
parent_str == "anonymous_function_expression") &&
|
||||
IsFieldName(parent, node, "name"))
|
||||
{
|
||||
return SemanticTokenType::kFunction;
|
||||
}
|
||||
|
||||
// 方法声明和定义
|
||||
if ((parent_str == "method_declaration" ||
|
||||
parent_str == "method_declaration_only" ||
|
||||
parent_str == "method_with_modifier" ||
|
||||
parent_str == "method_with_implementation" ||
|
||||
parent_str == "modifier_method" ||
|
||||
parent_str == "normal_method") &&
|
||||
(IsFieldName(parent, node, "name") || IsFieldName(parent, node, "method_name")))
|
||||
{
|
||||
return SemanticTokenType::kMethod;
|
||||
}
|
||||
|
||||
// 操作符重载
|
||||
if (parent_str == "_qualified_method_name" && IsFieldName(parent, node, "method_name"))
|
||||
{
|
||||
return SemanticTokenType::kMethod;
|
||||
}
|
||||
|
||||
// 函数调用
|
||||
if (parent_str == "call" && IsFieldName(parent, node, "function"))
|
||||
{
|
||||
return SemanticTokenType::kFunction;
|
||||
}
|
||||
|
||||
// ========== 类相关 ==========
|
||||
// 类定义
|
||||
if (parent_str == "class_definition_statement" && IsFieldName(parent, node, "name"))
|
||||
{
|
||||
return SemanticTokenType::kClass;
|
||||
}
|
||||
|
||||
// 类名引用(在方法定义中)
|
||||
if ((parent_str == "_qualified_method_name" ||
|
||||
parent_str == "_operator_method" ||
|
||||
parent_str == "_operator_method_with_modifier") &&
|
||||
IsFieldName(parent, node, "class_name"))
|
||||
{
|
||||
return SemanticTokenType::kClass;
|
||||
}
|
||||
|
||||
// 父类引用
|
||||
if (parent_str == "_parent_list" && IsFieldName(parent, node, "parent"))
|
||||
{
|
||||
return SemanticTokenType::kClass;
|
||||
}
|
||||
|
||||
// ========== 参数相关 ==========
|
||||
if (parent_str == "parameter" && IsFieldName(parent, node, "name"))
|
||||
{
|
||||
return SemanticTokenType::kParameter;
|
||||
}
|
||||
|
||||
// for循环变量
|
||||
if ((parent_str == "for_in_statement" || parent_str == "for_to_statement") &&
|
||||
(IsFieldName(parent, node, "key") || IsFieldName(parent, node, "value") ||
|
||||
IsFieldName(parent, node, "counter")))
|
||||
{
|
||||
return SemanticTokenType::kVariable;
|
||||
}
|
||||
|
||||
// ========== 属性相关 ==========
|
||||
// 属性声明
|
||||
if (parent_str == "property_declaration" && IsFieldName(parent, node, "name"))
|
||||
{
|
||||
return SemanticTokenType::kProperty;
|
||||
}
|
||||
|
||||
// 属性访问
|
||||
if (parent_str == "attribute" && IsFieldName(parent, node, "attribute"))
|
||||
{
|
||||
return SemanticTokenType::kProperty;
|
||||
}
|
||||
|
||||
// 属性访问器中的标识符
|
||||
if ((parent_str == "read_only_accessor" ||
|
||||
parent_str == "write_only_accessor" ||
|
||||
parent_str == "read_write_accessor" ||
|
||||
parent_str == "write_read_accessor"))
|
||||
{
|
||||
return SemanticTokenType::kMethod;
|
||||
}
|
||||
|
||||
// ========== 变量声明 ==========
|
||||
if ((parent_str == "var_declaration" ||
|
||||
parent_str == "static_declaration" ||
|
||||
parent_str == "global_declaration" ||
|
||||
parent_str == "nostatic_declaration") &&
|
||||
IsFieldName(parent, node, "name"))
|
||||
{
|
||||
// 检查是否是成员变量
|
||||
TSNode grandparent = ts_node_parent(parent);
|
||||
if (!ts_node_is_null(grandparent))
|
||||
{
|
||||
std::string grandparent_str(ts_node_type(grandparent));
|
||||
if (grandparent_str == "member_variable" || grandparent_str == "variable_declaration")
|
||||
{
|
||||
TSNode great_grandparent = ts_node_parent(grandparent);
|
||||
if (!ts_node_is_null(great_grandparent))
|
||||
{
|
||||
std::string ggp_str(ts_node_type(great_grandparent));
|
||||
if (ggp_str == "class_member" || ggp_str == "class_body")
|
||||
{
|
||||
return SemanticTokenType::kProperty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return SemanticTokenType::kVariable;
|
||||
}
|
||||
|
||||
uint32_t SemanticTokensCore::DetermineModifiers(TSNode node)
|
||||
{
|
||||
uint32_t modifiers = 0;
|
||||
TSNode parent = ts_node_parent(node);
|
||||
|
||||
if (!ts_node_is_null(parent))
|
||||
{
|
||||
std::string parent_str(ts_node_type(parent));
|
||||
std::string node_str(ts_node_type(node));
|
||||
|
||||
// 声明修饰符
|
||||
if (parent_str.find("declaration") != std::string::npos ||
|
||||
parent_str.find("definition") != std::string::npos)
|
||||
{
|
||||
modifiers |= static_cast<uint32_t>(SemanticTokenModifier::kDeclaration);
|
||||
}
|
||||
|
||||
// 常量修饰符
|
||||
if (parent_str == "const_statement")
|
||||
{
|
||||
modifiers |= static_cast<uint32_t>(SemanticTokenModifier::kReadonly);
|
||||
}
|
||||
|
||||
// 静态修饰符
|
||||
if (parent_str == "static_declaration" || parent_str == "static_statement")
|
||||
{
|
||||
modifiers |= static_cast<uint32_t>(SemanticTokenModifier::kStatic);
|
||||
}
|
||||
|
||||
// 检查方法修饰符
|
||||
TSNode grandparent = ts_node_parent(parent);
|
||||
if (!ts_node_is_null(grandparent))
|
||||
{
|
||||
std::string grandparent_str(ts_node_type(grandparent));
|
||||
|
||||
// 虚方法
|
||||
if (grandparent_str == "method_with_modifier")
|
||||
{
|
||||
// 需要检查modifier字段
|
||||
uint32_t child_count = ts_node_child_count(grandparent);
|
||||
for (uint32_t i = 0; i < child_count; ++i)
|
||||
{
|
||||
TSNode child = ts_node_child(grandparent, i);
|
||||
const char* field = ts_node_field_name_for_child(grandparent, i);
|
||||
if (field && std::string(field) == "modifier")
|
||||
{
|
||||
std::string modifier_str(ts_node_type(child));
|
||||
if (modifier_str == "virtual")
|
||||
{
|
||||
modifiers |= static_cast<uint32_t>(SemanticTokenModifier::kAbstract);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 类成员
|
||||
if (grandparent_str == "class_member" || grandparent_str == "class_body")
|
||||
{
|
||||
// 可以添加默认库修饰符等
|
||||
if (parent_str == "property_declaration")
|
||||
{
|
||||
modifiers |= static_cast<uint32_t>(SemanticTokenModifier::kReadonly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 异步修饰符(如果有异步函数)
|
||||
if (node_str == "async" || parent_str.find("async") != std::string::npos)
|
||||
{
|
||||
modifiers |= static_cast<uint32_t>(SemanticTokenModifier::kAsync);
|
||||
}
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
bool SemanticTokensCore::IsNodeInRange(TSPoint start, TSPoint end, const protocol::Range& range)
|
||||
{
|
||||
if (end.row < range.start.line || start.row > range.end.line)
|
||||
return false;
|
||||
if (start.row == range.start.line && end.column < range.start.character)
|
||||
return false;
|
||||
if (end.row == range.end.line && start.column > range.end.character)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<protocol::uinteger> SemanticTokensCore::EncodeTokens(const std::vector<SemanticToken>& tokens)
|
||||
{
|
||||
std::vector<uint32_t> encoded;
|
||||
encoded.reserve(tokens.size() * 5);
|
||||
protocol::uinteger prev_line = 0;
|
||||
protocol::uinteger prev_char = 0;
|
||||
|
||||
for (const auto& token : tokens)
|
||||
{
|
||||
protocol::uinteger delta_line = token.line - prev_line;
|
||||
protocol::uinteger delta_char = (delta_line == 0) ? token.character - prev_char : token.character;
|
||||
|
||||
encoded.push_back(delta_line);
|
||||
encoded.push_back(delta_char);
|
||||
encoded.push_back(token.length);
|
||||
encoded.push_back(static_cast<protocol::uinteger>(token.type));
|
||||
encoded.push_back(token.modifiers);
|
||||
|
||||
prev_line = token.line;
|
||||
prev_char = token.character;
|
||||
}
|
||||
return encoded;
|
||||
}
|
||||
|
||||
std::vector<protocol::SemanticTokensEdit> SemanticTokensCore::ComputeDelta(const std::vector<protocol::uinteger>& old_data, const std::vector<protocol::uinteger>& new_data)
|
||||
{
|
||||
std::vector<protocol::SemanticTokensEdit> edits;
|
||||
|
||||
// Simple diff algorithm - can be optimized with more sophisticated algorithms
|
||||
size_t old_size = old_data.size();
|
||||
size_t new_size = new_data.size();
|
||||
|
||||
if (old_size == 0 && new_size > 0)
|
||||
{
|
||||
// All new
|
||||
protocol::SemanticTokensEdit edit;
|
||||
edit.start = 0;
|
||||
edit.deleteCount = 0;
|
||||
edit.data = new_data;
|
||||
edits.push_back(edit);
|
||||
}
|
||||
else if (old_size > 0 && new_size == 0)
|
||||
{
|
||||
// All deleted
|
||||
protocol::SemanticTokensEdit edit;
|
||||
edit.start = 0;
|
||||
edit.deleteCount = old_size;
|
||||
edits.push_back(edit);
|
||||
}
|
||||
else if (old_data != new_data)
|
||||
{
|
||||
// Replace all (simple approach - can be optimized)
|
||||
protocol::SemanticTokensEdit edit;
|
||||
edit.start = 0;
|
||||
edit.deleteCount = old_size;
|
||||
edit.data = new_data;
|
||||
edits.push_back(edit);
|
||||
}
|
||||
|
||||
return edits;
|
||||
}
|
||||
|
||||
// Range request
|
||||
std::string SemanticTokensCore::HandleRange(const protocol::RequestMessage& request, ExecutionContext& context)
|
||||
{
|
||||
auto params = transform::FromLSPAny.template operator()<protocol::SemanticTokensRangeParams>(request.params.value());
|
||||
service::Document& doc_service = context.GetService<service::Document>();
|
||||
|
||||
TSTree* tree = doc_service.GetSyntaxTree(params.textDocument.uri);
|
||||
auto content = doc_service.GetContent(params.textDocument.uri);
|
||||
|
||||
if (!tree || !content)
|
||||
return BuildErrorResponseMessage(request, protocol::ErrorCodes::InternalError, "Document not available");
|
||||
|
||||
auto tokens = CollectTokensInRange(ts_tree_root_node(tree), content.value(), ¶ms.range);
|
||||
auto encoded = EncodeTokens(tokens);
|
||||
|
||||
protocol::ResponseMessage response;
|
||||
response.id = request.id;
|
||||
protocol::SemanticTokens result;
|
||||
result.data = encoded;
|
||||
response.result = transform::LSPAny(result);
|
||||
|
||||
spdlog::debug("SemanticTokens Range: Generated {} tokens for range in document: {}", encoded.size() / 5, params.textDocument.uri);
|
||||
|
||||
std::optional<std::string> json = transform::Serialize(response);
|
||||
if (!json.has_value())
|
||||
return BuildErrorResponseMessage(request, protocol::ErrorCodes::InternalError, "Internal error");
|
||||
return json.value();
|
||||
}
|
||||
|
||||
// Full request
|
||||
std::string SemanticTokensCore::HandleFull(const protocol::RequestMessage& request, ExecutionContext& context)
|
||||
{
|
||||
auto params = transform::As<protocol::SemanticTokensParams>(request.params.value());
|
||||
service::Document& doc_service = context.GetService<service::Document>();
|
||||
|
||||
TSTree* tree = doc_service.GetSyntaxTree(params.textDocument.uri);
|
||||
auto content = doc_service.GetContent(params.textDocument.uri);
|
||||
|
||||
if (!tree || !content)
|
||||
return BuildErrorResponseMessage(request, protocol::ErrorCodes::InternalError, "Document not available");
|
||||
|
||||
auto tokens = CollectTokensInRange(ts_tree_root_node(tree), content.value());
|
||||
auto encoded = EncodeTokens(tokens);
|
||||
|
||||
// Generate result ID and cache
|
||||
std::string version = "1"; // Should get actual document version
|
||||
std::string result_id = GenerateResultId(params.textDocument.uri, version);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cache_mutex_);
|
||||
cache_.emplace(params.textDocument.uri, SemanticTokensCache(result_id, encoded, version));
|
||||
}
|
||||
|
||||
protocol::ResponseMessage response;
|
||||
response.id = request.id;
|
||||
protocol::SemanticTokens result;
|
||||
result.resultId = result_id;
|
||||
result.data = encoded;
|
||||
response.result = transform::LSPAny(result);
|
||||
|
||||
spdlog::debug("SemanticTokens Full: Generated {} tokens for document: {}", encoded.size() / 5, params.textDocument.uri);
|
||||
std::optional<std::string> json = transform::Serialize(response);
|
||||
if (!json.has_value())
|
||||
return BuildErrorResponseMessage(request, protocol::ErrorCodes::InternalError, "Internal error");
|
||||
return json.value();
|
||||
}
|
||||
|
||||
// Delta request
|
||||
std::string SemanticTokensCore::HandleDelta(const protocol::RequestMessage& request, ExecutionContext& context)
|
||||
{
|
||||
auto params = transform::As<protocol::SemanticTokensDeltaParams>(request.params.value());
|
||||
service::Document& doc_service = context.GetService<service::Document>();
|
||||
|
||||
// 检查缓存
|
||||
bool need_full_update = false;
|
||||
std::vector<protocol::uinteger> old_data;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cache_mutex_);
|
||||
auto cache_it = cache_.find(params.textDocument.uri);
|
||||
if (cache_it == cache_.end() || cache_it->second.result_id != params.previousResultId)
|
||||
{
|
||||
need_full_update = true; // 标记需要完整更新
|
||||
}
|
||||
else
|
||||
{
|
||||
old_data = cache_it->second.data; // 复制旧数据
|
||||
}
|
||||
} // 锁在这里释放
|
||||
|
||||
// 如果需要完整更新,在锁外调用 HandleFull
|
||||
if (need_full_update)
|
||||
{
|
||||
protocol::RequestMessage full_request = request;
|
||||
full_request.method = "textDocument/semanticTokens/full";
|
||||
protocol::SemanticTokensParams full_params;
|
||||
full_params.textDocument = params.textDocument;
|
||||
full_request.params = transform::LSPObject(full_params);
|
||||
return HandleFull(full_request, context);
|
||||
}
|
||||
|
||||
TSTree* tree = doc_service.GetSyntaxTree(params.textDocument.uri);
|
||||
auto content = doc_service.GetContent(params.textDocument.uri);
|
||||
|
||||
if (!tree || !content)
|
||||
return BuildErrorResponseMessage(request, protocol::ErrorCodes::InternalError, "Document not available");
|
||||
|
||||
// Get new tokens
|
||||
auto tokens = CollectTokensInRange(ts_tree_root_node(tree), content.value());
|
||||
auto new_encoded = EncodeTokens(tokens);
|
||||
|
||||
// Compute delta
|
||||
auto edits = ComputeDelta(old_data, new_encoded);
|
||||
|
||||
// Update cache
|
||||
std::string version = "2"; // Should get actual document version
|
||||
std::string new_result_id = GenerateResultId(params.textDocument.uri, version);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(cache_mutex_);
|
||||
cache_.emplace(params.textDocument.uri, SemanticTokensCache(new_result_id, new_encoded, version));
|
||||
}
|
||||
|
||||
protocol::ResponseMessage response;
|
||||
response.id = request.id;
|
||||
protocol::SemanticTokensDelta result;
|
||||
result.resultId = new_result_id;
|
||||
result.edits = edits;
|
||||
response.result = transform::LSPAny(result);
|
||||
|
||||
spdlog::debug("SemanticTokens Delta: Generated {} edits for document: {}", edits.size(), params.textDocument.uri);
|
||||
std::optional<std::string> json = transform::Serialize(response);
|
||||
if (!json.has_value())
|
||||
return BuildErrorResponseMessage(request, protocol::ErrorCodes::InternalError, "Internal error");
|
||||
return json.value();
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, SemanticTokensCache> SemanticTokensCore::cache_;
|
||||
std::mutex SemanticTokensCore::cache_mutex_;
|
||||
|
||||
std::string SemanticTokensRange::GetMethod() const
|
||||
{
|
||||
return "textDocument/semanticTokens/range";
|
||||
@@ -695,7 +17,6 @@ namespace lsp::provider::text_document
|
||||
std::string SemanticTokensRange::ProvideResponse(const protocol::RequestMessage& request, ExecutionContext& context)
|
||||
{
|
||||
spdlog::debug("TextDocumentSemanticTokensRangeProvider: Providing response for method {}", request.method);
|
||||
return SemanticTokensCore::GetInstance().HandleRange(request, context);
|
||||
}
|
||||
|
||||
std::string SemanticTokensFull::GetMethod() const
|
||||
@@ -711,7 +32,6 @@ namespace lsp::provider::text_document
|
||||
std::string SemanticTokensFull::ProvideResponse(const protocol::RequestMessage& request, ExecutionContext& context)
|
||||
{
|
||||
spdlog::debug("SemanticTokensFullProvider: Providing response for method {}", request.method);
|
||||
return SemanticTokensCore::GetInstance().HandleFull(request, context);
|
||||
}
|
||||
|
||||
std::string SemanticTokensFullDelta::GetMethod() const
|
||||
@@ -727,6 +47,5 @@ namespace lsp::provider::text_document
|
||||
std::string SemanticTokensFullDelta::ProvideResponse(const protocol::RequestMessage& request, ExecutionContext& context)
|
||||
{
|
||||
spdlog::debug("SemanticTokensFullDeltaProvider: Providing response for method {}", request.method);
|
||||
return SemanticTokensCore::GetInstance().HandleDelta(request, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,102 +7,6 @@
|
||||
|
||||
namespace lsp::provider::text_document
|
||||
{
|
||||
enum class SemanticTokenType: protocol::uinteger
|
||||
{
|
||||
kNamespace = 0,
|
||||
kType = 1,
|
||||
kClass = 2,
|
||||
kEnum = 3,
|
||||
kInterface = 4,
|
||||
kStruct = 5,
|
||||
kTypeParameter = 6,
|
||||
kParameter = 7,
|
||||
kVariable = 8,
|
||||
kProperty = 9,
|
||||
kEnumMember = 10,
|
||||
kEvent = 11,
|
||||
kFunction = 12,
|
||||
kMethod = 13,
|
||||
kMacro = 14,
|
||||
kKeyword = 15,
|
||||
kModifier = 16,
|
||||
kComment = 17,
|
||||
kString = 18,
|
||||
kNumber = 19,
|
||||
kRegexp = 20,
|
||||
kOperator = 21,
|
||||
kDecorator = 22
|
||||
};
|
||||
|
||||
enum class SemanticTokenModifier: protocol::uinteger
|
||||
{
|
||||
kDeclaration = 1 << 0,
|
||||
kDefinition = 1 << 1,
|
||||
kReadonly = 1 << 2,
|
||||
kStatic = 1 << 3,
|
||||
kDeprecated = 1 << 4,
|
||||
kAbstract = 1 << 5,
|
||||
kAsync = 1 << 6,
|
||||
kModification = 1 << 7,
|
||||
kDocumentation = 1 << 8,
|
||||
kDefault_library = 1 << 9
|
||||
};
|
||||
|
||||
class SemanticToken
|
||||
{
|
||||
public:
|
||||
SemanticToken(protocol::uinteger l, protocol::uinteger c, protocol::uinteger len, SemanticTokenType t, protocol::uinteger mod);
|
||||
|
||||
protocol::uinteger line;
|
||||
protocol::uinteger character;
|
||||
protocol::uinteger length;
|
||||
SemanticTokenType type;
|
||||
protocol::uinteger modifiers;
|
||||
};
|
||||
|
||||
class SemanticTokensCache
|
||||
{
|
||||
public:
|
||||
SemanticTokensCache(const std::string& id, std::vector<uint32_t> tokens, const std::string& version);
|
||||
|
||||
std::string result_id;
|
||||
std::vector<uint32_t> data;
|
||||
std::string document_version;
|
||||
};
|
||||
|
||||
class SemanticTokensCore
|
||||
{
|
||||
public:
|
||||
SemanticTokensCore(const SemanticTokensCore&) = delete;
|
||||
SemanticTokensCore& operator=(const SemanticTokensCore&) = delete;
|
||||
|
||||
static SemanticTokensCore& GetInstance();
|
||||
|
||||
std::string HandleRange(const protocol::RequestMessage& request, ExecutionContext& context);
|
||||
std::string HandleFull(const protocol::RequestMessage& request, ExecutionContext& context);
|
||||
std::string HandleDelta(const protocol::RequestMessage& request, ExecutionContext& context);
|
||||
|
||||
private:
|
||||
SemanticTokensCore();
|
||||
|
||||
std::vector<SemanticToken> CollectTokensInRange(TSNode root, const std::string& source, const protocol::Range* range = nullptr);
|
||||
void TraverseNode(TSNode node, const std::string& source_code, std::vector<SemanticToken>& tokens, const protocol::Range* range);
|
||||
void ProcessNode(TSNode node, std::vector<SemanticToken>& tokens);
|
||||
|
||||
SemanticTokenType DetermineIdentifierType(TSNode node);
|
||||
uint32_t DetermineModifiers(TSNode node);
|
||||
std::vector<protocol::uinteger> EncodeTokens(const std::vector<SemanticToken>& tokens);
|
||||
std::vector<protocol::SemanticTokensEdit> ComputeDelta(const std::vector<protocol::uinteger>& old_data, const std::vector<protocol::uinteger>& new_data);
|
||||
|
||||
void InitNodeTypeMap();
|
||||
std::string GenerateResultId(const std::string& uri, const std::string& version);
|
||||
bool IsNodeInRange(TSPoint start, TSPoint end, const protocol::Range& range);
|
||||
|
||||
static std::unordered_map<std::string, SemanticTokensCache> cache_;
|
||||
static std::mutex cache_mutex_;
|
||||
std::unordered_map<std::string, SemanticTokenType> node_type_map_;
|
||||
};
|
||||
|
||||
class SemanticTokensRange : public IRequestProvider
|
||||
{
|
||||
public:
|
||||
|
||||
Reference in New Issue
Block a user