231 lines
6.9 KiB
C++
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(),
|
|
[¶ms](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
|