tsl-devkit/lsp-server/src/provider/workspace/symbol.cpp

231 lines
6.9 KiB
C++

#include "./symbol.hpp"
#include <spdlog/spdlog.h>
#include <algorithm>
#include <cctype>
#include "../../protocol/transform/facade.hpp"
#include "../../service/document.hpp"
#include "../../service/symbol.hpp"
namespace lsp::provider::workspace {
std::string Symbol::GetMethod() const { return "workspace/symbol"; }
std::string Symbol::GetProviderName() const { return "WorkSpaceSymbol"; }
std::string Symbol::ProvideResponse(const protocol::RequestMessage& request,
ExecutionContext& context) {
spdlog::debug("WorkspaceSymbolProvider: Providing response for method {}",
request.method);
if (!request.params.has_value()) {
spdlog::warn("{}: Missing params in request", GetProviderName());
return BuildErrorResponseMessage(
request, protocol::ErrorCodes::InvalidParams, "Missing params");
}
protocol::WorkspaceSymbolParams params =
transform::FromLSPAny.template
operator()<protocol::WorkspaceSymbolParams>(request.params.value());
auto symbols = BuildSymbolResponse(params, context);
protocol::ResponseMessage response;
response.id = request.id;
response.result = transform::ToLSPAny(symbols);
std::optional<std::string> json = transform::Serialize(response);
if (!json.has_value())
return BuildErrorResponseMessage(request,
protocol::ErrorCodes::InternalError,
"Failed to serialize response");
return json.value();
}
std::vector<protocol::SymbolInformation> Symbol::BuildSymbolResponse(
const protocol::WorkspaceSymbolParams& params, ExecutionContext& context) {
spdlog::trace("{}: Searching for symbols matching '{}'", GetProviderName(),
params.query);
auto symbols = SearchSymbols(params.query, context);
// 按匹配分数排序
std::sort(symbols.begin(), symbols.end(),
[&params](const protocol::SymbolInformation& a,
const protocol::SymbolInformation& b) {
// 可以实现更复杂的排序逻辑
return a.name < b.name;
});
// 限制返回数量(避免返回太多结果)
const size_t max_results = 100;
if (symbols.size() > max_results) {
symbols.resize(max_results);
spdlog::debug("{}: Limited results to {} symbols", GetProviderName(),
max_results);
}
spdlog::info("{}: Found {} symbols matching '{}'", GetProviderName(),
symbols.size(), params.query);
return symbols;
}
std::vector<protocol::SymbolInformation> Symbol::SearchSymbols(
const std::string& query, ExecutionContext& context) {
std::vector<protocol::SymbolInformation> results;
// 从容器获取服务
auto document_service = context.GetService<service::Document>();
auto symbol_service = context.GetService<service::Symbol>();
// 获取所有打开的文档
auto document_uris = document_service->GetAllDocumentUris();
for (const auto& uri : document_uris) {
// 获取文档符号
auto doc_symbols = symbol_service->GetDocumentSymbols(uri);
// 递归转换并过滤符号
for (const auto& symbol : doc_symbols) {
ConvertToSymbolInformation(symbol, uri, query, results);
}
}
return results;
}
void Symbol::ConvertToSymbolInformation(
const protocol::DocumentSymbol& doc_symbol,
const protocol::DocumentUri& uri, const std::string& query,
std::vector<protocol::SymbolInformation>& results,
const std::string& container_name) {
// 检查是否匹配查询
if (MatchesQuery(doc_symbol.name, query)) {
protocol::SymbolInformation info;
info.name = doc_symbol.name;
info.kind = doc_symbol.kind;
info.location.uri = uri;
info.location.range = doc_symbol.range;
// 设置容器名称
if (!container_name.empty()) {
info.containerName = container_name;
}
results.push_back(info);
}
// 递归处理子符号
std::string new_container = container_name.empty()
? doc_symbol.name
: container_name + "." + doc_symbol.name;
for (const auto& child : doc_symbol.children.value()) {
ConvertToSymbolInformation(child, uri, query, results, new_container);
}
}
bool Symbol::MatchesQuery(const std::string& symbol_name,
const std::string& query) {
// 空查询匹配所有
if (query.empty()) return true;
// 转换为小写进行不区分大小写的匹配
std::string lower_symbol = symbol_name;
std::string lower_query = query;
std::transform(lower_symbol.begin(), lower_symbol.end(), lower_symbol.begin(),
::tolower);
std::transform(lower_query.begin(), lower_query.end(), lower_query.begin(),
::tolower);
// 1. 精确匹配
if (lower_symbol == lower_query) return true;
// 2. 前缀匹配
if (lower_symbol.find(lower_query) == 0) return true;
// 3. 包含匹配
if (lower_symbol.find(lower_query) != std::string::npos) return true;
// 4. 模糊匹配(驼峰匹配)
if (FuzzyMatch(query, symbol_name)) return true;
return false;
}
bool Symbol::FuzzyMatch(const std::string& pattern, const std::string& text) {
// 实现驼峰匹配
// 例如 "gS" 匹配 "getString"
size_t pattern_idx = 0;
size_t text_idx = 0;
while (pattern_idx < pattern.length() && text_idx < text.length()) {
char p = pattern[pattern_idx];
// 查找下一个匹配字符
bool found = false;
while (text_idx < text.length()) {
char t = text[text_idx];
// 不区分大小写匹配
if (std::tolower(p) == std::tolower(t)) {
found = true;
text_idx++;
break;
}
// 如果模式字符是大写,只在大写字母位置匹配
if (std::isupper(p) && !std::isupper(t)) {
text_idx++;
continue;
}
text_idx++;
}
if (!found) return false;
pattern_idx++;
}
return pattern_idx == pattern.length();
}
int Symbol::CalculateScore(const std::string& symbol_name,
const std::string& query) {
int score = 0;
// 精确匹配得分最高
if (symbol_name == query) return 1000;
// 前缀匹配得分次高
if (symbol_name.find(query) == 0) return 900;
// 不区分大小写的前缀匹配
std::string lower_symbol = symbol_name;
std::string lower_query = query;
std::transform(lower_symbol.begin(), lower_symbol.end(), lower_symbol.begin(),
::tolower);
std::transform(lower_query.begin(), lower_query.end(), lower_query.begin(),
::tolower);
if (lower_symbol.find(lower_query) == 0) return 800;
// 包含匹配
size_t pos = lower_symbol.find(lower_query);
if (pos != std::string::npos) {
// 越靠前得分越高
score = 700 - static_cast<int>(pos * 10);
}
// 模糊匹配得分最低
if (FuzzyMatch(query, symbol_name)) {
score = std::max(score, 500);
}
return score;
}
} // namespace lsp::provider::workspace