ccls/src/messages/workspace_symbol.cc

160 lines
5.4 KiB
C++
Raw Normal View History

#include "fuzzy_match.h"
2017-12-06 03:32:33 +00:00
#include "lex_utils.h"
#include "message_handler.h"
#include "query_utils.h"
2017-12-29 16:29:47 +00:00
#include "queue_manager.h"
2017-12-06 03:32:33 +00:00
#include <loguru.hpp>
2017-12-29 16:29:47 +00:00
#include <ctype.h>
#include <limits.h>
#include <algorithm>
#include <functional>
namespace {
MethodType kMethodType = "workspace/symbol";
// Lookup |symbol| in |db| and insert the value into |result|.
bool InsertSymbolIntoResult(QueryDatabase* db,
WorkingFiles* working_files,
SymbolIdx symbol,
std::vector<lsSymbolInformation>* result) {
2018-03-31 03:16:33 +00:00
std::optional<lsSymbolInformation> info =
GetSymbolInfo(db, working_files, symbol, true);
if (!info)
return false;
Use loc;
if (Maybe<Use> location = GetDefinitionExtent(db, symbol))
2018-02-09 05:11:35 +00:00
loc = *location;
else {
auto decls = GetNonDefDeclarations(db, symbol);
if (decls.empty())
return false;
2018-02-09 05:11:35 +00:00
loc = decls[0];
}
2018-03-31 03:16:33 +00:00
std::optional<lsLocation> ls_location = GetLsLocation(db, working_files, loc);
if (!ls_location)
return false;
info->location = *ls_location;
result->push_back(*info);
return true;
}
2018-03-22 05:01:21 +00:00
struct In_WorkspaceSymbol : public RequestInMessage {
MethodType GetMethodType() const override { return kMethodType; }
struct Params {
std::string query;
};
Params params;
2017-12-06 04:39:44 +00:00
};
MAKE_REFLECT_STRUCT(In_WorkspaceSymbol::Params, query);
MAKE_REFLECT_STRUCT(In_WorkspaceSymbol, id, params);
REGISTER_IN_MESSAGE(In_WorkspaceSymbol);
2017-12-06 04:39:44 +00:00
struct Out_WorkspaceSymbol : public lsOutMessage<Out_WorkspaceSymbol> {
lsRequestId id;
2017-12-12 05:20:29 +00:00
std::vector<lsSymbolInformation> result;
2017-12-06 04:39:44 +00:00
};
MAKE_REFLECT_STRUCT(Out_WorkspaceSymbol, jsonrpc, id, result);
///// Fuzzy matching
struct Handler_WorkspaceSymbol : BaseMessageHandler<In_WorkspaceSymbol> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_WorkspaceSymbol* request) override {
2017-12-06 03:32:33 +00:00
Out_WorkspaceSymbol out;
out.id = request->id;
LOG_S(INFO) << "[querydb] Considering " << db->symbols.size()
2017-12-06 03:32:33 +00:00
<< " candidates for query " << request->params.query;
std::string query = request->params.query;
std::unordered_set<std::string> inserted_results;
// db->detailed_names indices of each lsSymbolInformation in out.result
std::vector<int> result_indices;
std::vector<lsSymbolInformation> unsorted_results;
2018-04-04 06:05:41 +00:00
inserted_results.reserve(g_config->workspaceSymbol.maxNum);
result_indices.reserve(g_config->workspaceSymbol.maxNum);
2017-12-06 03:32:33 +00:00
// We use detailed_names without parameters for matching.
// Find exact substring matches.
for (int i = 0; i < db->symbols.size(); ++i) {
std::string_view detailed_name = db->GetSymbolName(i, true);
if (detailed_name.find(query) != std::string::npos) {
2017-12-06 03:32:33 +00:00
// Do not show the same entry twice.
if (!inserted_results.insert(std::string(detailed_name)).second)
2017-12-06 03:32:33 +00:00
continue;
2017-12-27 15:53:35 +00:00
if (InsertSymbolIntoResult(db, working_files, db->symbols[i],
&unsorted_results)) {
result_indices.push_back(i);
2018-04-04 06:05:41 +00:00
if (unsorted_results.size() >= g_config->workspaceSymbol.maxNum)
break;
}
2017-12-06 03:32:33 +00:00
}
}
// Find subsequence matches.
2018-04-04 06:05:41 +00:00
if (unsorted_results.size() < g_config->workspaceSymbol.maxNum) {
std::string query_without_space;
query_without_space.reserve(query.size());
2017-12-27 15:53:35 +00:00
for (char c : query)
if (!isspace(c))
query_without_space += c;
2018-01-31 05:55:53 +00:00
for (int i = 0; i < (int)db->symbols.size(); ++i) {
std::string_view detailed_name = db->GetSymbolName(i, true);
2018-03-20 02:51:42 +00:00
if (CaseFoldingSubsequenceMatch(query_without_space, detailed_name)
.first) {
2017-12-06 03:32:33 +00:00
// Do not show the same entry twice.
if (!inserted_results.insert(std::string(detailed_name)).second)
2017-12-06 03:32:33 +00:00
continue;
2017-12-27 15:53:35 +00:00
if (InsertSymbolIntoResult(db, working_files, db->symbols[i],
&unsorted_results)) {
result_indices.push_back(i);
2018-04-04 06:05:41 +00:00
if (unsorted_results.size() >= g_config->workspaceSymbol.maxNum)
break;
}
2017-12-06 03:32:33 +00:00
}
}
}
2018-04-04 06:05:41 +00:00
if (g_config->workspaceSymbol.sort && query.size() <= FuzzyMatcher::kMaxPat) {
// Sort results with a fuzzy matching algorithm.
int longest = 0;
for (int i : result_indices)
longest = std::max(longest, int(db->GetSymbolName(i, true).size()));
2018-03-16 07:19:49 +00:00
FuzzyMatcher fuzzy(query);
std::vector<std::pair<int, int>> permutation(result_indices.size());
for (int i = 0; i < int(result_indices.size()); i++) {
permutation[i] = {
fuzzy.Match(db->GetSymbolName(result_indices[i], true)), i};
}
std::sort(permutation.begin(), permutation.end(),
std::greater<std::pair<int, int>>());
out.result.reserve(result_indices.size());
2018-03-18 19:17:40 +00:00
// Discard awful candidates.
for (int i = 0; i < int(result_indices.size()) &&
permutation[i].first > FuzzyMatcher::kMinScore;
i++)
2018-01-11 02:43:01 +00:00
out.result.push_back(
std::move(unsorted_results[permutation[i].second]));
} else {
out.result.reserve(unsorted_results.size());
for (const auto& entry : unsorted_results)
out.result.push_back(std::move(entry));
}
2017-12-06 03:32:33 +00:00
LOG_S(INFO) << "[querydb] Found " << out.result.size()
<< " results for query " << query;
QueueManager::WriteStdout(kMethodType, out);
2017-12-06 03:32:33 +00:00
}
};
REGISTER_MESSAGE_HANDLER(Handler_WorkspaceSymbol);
} // namespace