ccls/src/messages/workspace_symbol.cc

158 lines
5.3 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 {
// Lookup |symbol| in |db| and insert the value into |result|.
bool InsertSymbolIntoResult(QueryDatabase* db,
WorkingFiles* working_files,
SymbolIdx symbol,
std::vector<lsSymbolInformation>* result) {
2017-12-23 16:01:43 +00:00
optional<lsSymbolInformation> info =
GetSymbolInfo(db, working_files, symbol, false /*use_short_name*/);
if (!info)
return false;
Maybe<Use> location = GetDefinitionExtent(db, symbol);
Use loc;
2018-02-09 05:11:35 +00:00
if (location)
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-02-09 05:11:35 +00:00
optional<lsLocation> ls_location = GetLsLocation(db, working_files, loc);
if (!ls_location)
return false;
info->location = *ls_location;
result->push_back(*info);
return true;
}
struct Ipc_WorkspaceSymbol : public RequestMessage<Ipc_WorkspaceSymbol> {
2017-12-06 04:39:44 +00:00
const static IpcId kIpcId = IpcId::WorkspaceSymbol;
struct Params {
std::string query;
};
Params params;
2017-12-06 04:39:44 +00:00
};
MAKE_REFLECT_STRUCT(Ipc_WorkspaceSymbol::Params, query);
2017-12-06 04:39:44 +00:00
MAKE_REFLECT_STRUCT(Ipc_WorkspaceSymbol, id, params);
REGISTER_IPC_MESSAGE(Ipc_WorkspaceSymbol);
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
2017-12-06 03:32:33 +00:00
struct WorkspaceSymbolHandler : BaseMessageHandler<Ipc_WorkspaceSymbol> {
void Run(Ipc_WorkspaceSymbol* request) override {
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;
inserted_results.reserve(config->workspaceSymbol.maxNum);
result_indices.reserve(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->GetSymbolDetailedName(i);
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);
if (unsorted_results.size() >= config->workspaceSymbol.maxNum)
break;
}
2017-12-06 03:32:33 +00:00
}
}
// Find subsequence matches.
if (unsorted_results.size() < 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->GetSymbolDetailedName(i);
2018-02-22 23:49:16 +00:00
if (SubsequenceMatchIgnoreCase(query_without_space, detailed_name)) {
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);
if (unsorted_results.size() >= config->workspaceSymbol.maxNum)
break;
}
2017-12-06 03:32:33 +00:00
}
}
}
2018-03-16 07:19:49 +00:00
if (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->GetSymbolDetailedName(i).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] = {
2018-03-16 07:19:49 +00:00
fuzzy.Match(db->GetSymbolDetailedName(result_indices[i])), 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;
2017-12-24 00:25:18 +00:00
QueueManager::WriteStdout(IpcId::WorkspaceSymbol, out);
2017-12-06 03:32:33 +00:00
}
};
REGISTER_MESSAGE_HANDLER(WorkspaceSymbolHandler);
} // namespace