2017-12-05 07:57:41 +00:00
|
|
|
#include "message_handler.h"
|
|
|
|
|
2017-12-06 03:32:33 +00:00
|
|
|
#include "lex_utils.h"
|
2018-01-06 21:40:33 +00:00
|
|
|
#include "project.h"
|
2017-12-06 03:32:33 +00:00
|
|
|
#include "query_utils.h"
|
2017-12-29 16:29:47 +00:00
|
|
|
#include "queue_manager.h"
|
|
|
|
#include "semantic_highlight_symbol_cache.h"
|
2017-12-06 03:32:33 +00:00
|
|
|
|
|
|
|
#include <loguru.hpp>
|
|
|
|
|
2018-01-10 03:04:08 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
struct ScanLineEvent {
|
|
|
|
lsPosition pos;
|
|
|
|
lsPosition end_pos; // Second key when there is a tie for insertion events.
|
|
|
|
int id;
|
|
|
|
Out_CqueryPublishSemanticHighlighting::Symbol* symbol;
|
|
|
|
bool operator<(const ScanLineEvent& other) const {
|
|
|
|
// See the comments below when insertion/deletion events are inserted.
|
|
|
|
return !(pos == other.pos) ? pos < other.pos : other.end_pos < end_pos;
|
|
|
|
}
|
|
|
|
};
|
2018-01-11 02:43:01 +00:00
|
|
|
} // namespace
|
2018-01-10 03:04:08 +00:00
|
|
|
|
2017-12-05 07:57:41 +00:00
|
|
|
MessageHandler::MessageHandler() {
|
|
|
|
// Dynamically allocate |message_handlers|, otherwise there will be static
|
|
|
|
// initialization order races.
|
|
|
|
if (!message_handlers)
|
|
|
|
message_handlers = new std::vector<MessageHandler*>();
|
|
|
|
message_handlers->push_back(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
2017-12-06 03:32:33 +00:00
|
|
|
std::vector<MessageHandler*>* MessageHandler::message_handlers = nullptr;
|
|
|
|
|
|
|
|
bool FindFileOrFail(QueryDatabase* db,
|
2018-01-11 02:43:01 +00:00
|
|
|
const Project* project,
|
2017-12-06 03:32:33 +00:00
|
|
|
optional<lsRequestId> id,
|
|
|
|
const std::string& absolute_path,
|
|
|
|
QueryFile** out_query_file,
|
|
|
|
QueryFileId* out_file_id) {
|
|
|
|
*out_query_file = nullptr;
|
|
|
|
|
2018-01-30 00:55:51 +00:00
|
|
|
auto it = db->usr_to_file.find(NormalizedPath(absolute_path));
|
2017-12-06 03:32:33 +00:00
|
|
|
if (it != db->usr_to_file.end()) {
|
|
|
|
QueryFile& file = db->files[it->second.id];
|
|
|
|
if (file.def) {
|
|
|
|
*out_query_file = &file;
|
|
|
|
if (out_file_id)
|
|
|
|
*out_file_id = QueryFileId(it->second.id);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (out_file_id)
|
|
|
|
*out_file_id = QueryFileId((size_t)-1);
|
|
|
|
|
2017-12-31 03:18:33 +00:00
|
|
|
bool indexing = project->absolute_path_to_entry_index_.find(absolute_path) !=
|
|
|
|
project->absolute_path_to_entry_index_.end();
|
|
|
|
if (indexing)
|
|
|
|
LOG_S(INFO) << "\"" << absolute_path << "\" is being indexed.";
|
|
|
|
else
|
|
|
|
LOG_S(INFO) << "Unable to find file \"" << absolute_path << "\"";
|
2017-12-28 17:03:35 +00:00
|
|
|
/*
|
2017-12-28 16:55:46 +00:00
|
|
|
LOG_S(INFO) << "Files (size=" << db->usr_to_file.size() << "): "
|
|
|
|
<< StringJoinMap(db->usr_to_file,
|
|
|
|
[](const std::pair<Usr, QueryFileId>& entry) {
|
|
|
|
return entry.first;
|
|
|
|
});
|
2017-12-28 17:03:35 +00:00
|
|
|
*/
|
2017-12-06 03:32:33 +00:00
|
|
|
|
|
|
|
if (id) {
|
|
|
|
Out_Error out;
|
|
|
|
out.id = *id;
|
2017-12-31 03:18:33 +00:00
|
|
|
if (indexing) {
|
|
|
|
out.error.code = lsErrorCodes::ServerNotInitialized;
|
|
|
|
out.error.message = absolute_path + " is being indexed.";
|
|
|
|
} else {
|
|
|
|
out.error.code = lsErrorCodes::InternalError;
|
|
|
|
out.error.message = "Unable to find file " + absolute_path;
|
|
|
|
}
|
2017-12-24 00:25:18 +00:00
|
|
|
QueueManager::WriteStdout(IpcId::Unknown, out);
|
2017-12-06 03:32:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EmitInactiveLines(WorkingFile* working_file,
|
|
|
|
const std::vector<Range>& inactive_regions) {
|
|
|
|
Out_CquerySetInactiveRegion out;
|
|
|
|
out.params.uri = lsDocumentUri::FromPath(working_file->filename);
|
|
|
|
for (Range skipped : inactive_regions) {
|
|
|
|
optional<lsRange> ls_skipped = GetLsRange(working_file, skipped);
|
|
|
|
if (ls_skipped)
|
|
|
|
out.params.inactiveRegions.push_back(*ls_skipped);
|
|
|
|
}
|
2017-12-24 00:25:18 +00:00
|
|
|
QueueManager::WriteStdout(IpcId::CqueryPublishInactiveRegions, out);
|
2017-12-06 03:32:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void EmitSemanticHighlighting(QueryDatabase* db,
|
|
|
|
SemanticHighlightSymbolCache* semantic_cache,
|
|
|
|
WorkingFile* working_file,
|
|
|
|
QueryFile* file) {
|
|
|
|
assert(file->def);
|
|
|
|
auto semantic_cache_for_file =
|
|
|
|
semantic_cache->GetCacheForFile(file->def->path);
|
|
|
|
|
|
|
|
// Group symbols together.
|
|
|
|
std::unordered_map<SymbolIdx, Out_CqueryPublishSemanticHighlighting::Symbol>
|
|
|
|
grouped_symbols;
|
|
|
|
for (SymbolRef sym : file->def->all_symbols) {
|
2018-01-31 04:59:31 +00:00
|
|
|
std::string_view detailed_name;
|
2018-01-28 20:34:31 +00:00
|
|
|
SymbolKind parent_kind = SymbolKind::Invalid;
|
2018-01-15 23:08:03 +00:00
|
|
|
ClangSymbolKind kind = ClangSymbolKind::Unknown;
|
2018-01-28 04:25:14 +00:00
|
|
|
StorageClass storage = StorageClass::Invalid;
|
2017-12-06 03:32:33 +00:00
|
|
|
// This switch statement also filters out symbols that are not highlighted.
|
|
|
|
switch (sym.idx.kind) {
|
|
|
|
case SymbolKind::Func: {
|
|
|
|
QueryFunc* func = &db->funcs[sym.idx.idx];
|
|
|
|
if (!func->def)
|
|
|
|
continue; // applies to for loop
|
2018-01-29 17:28:17 +00:00
|
|
|
// Don't highlight overloadable operators or implicit lambda ->
|
|
|
|
// std::function constructor.
|
|
|
|
if (func->def->short_name.compare(0, 8, "operator") == 0 ||
|
|
|
|
func->def->short_name.compare(0, 27,
|
|
|
|
"function<type-parameter-0-0") == 0)
|
2017-12-06 03:32:33 +00:00
|
|
|
continue; // applies to for loop
|
2018-01-15 23:08:03 +00:00
|
|
|
kind = func->def->kind;
|
2017-12-06 03:32:33 +00:00
|
|
|
detailed_name = func->def->short_name;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SymbolKind::Var: {
|
|
|
|
QueryVar* var = &db->vars[sym.idx.idx];
|
|
|
|
if (!var->def)
|
|
|
|
continue; // applies to for loop
|
2018-01-28 20:34:31 +00:00
|
|
|
parent_kind = var->def->parent_kind;
|
2018-01-15 23:08:03 +00:00
|
|
|
kind = var->def->kind;
|
2018-01-26 17:28:29 +00:00
|
|
|
storage = var->def->storage;
|
2018-01-31 04:59:31 +00:00
|
|
|
detailed_name = var->def->ShortName();
|
2017-12-06 03:32:33 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SymbolKind::Type: {
|
|
|
|
QueryType* type = &db->types[sym.idx.idx];
|
|
|
|
if (!type->def)
|
|
|
|
continue; // applies to for loop
|
2018-01-15 23:08:03 +00:00
|
|
|
kind = type->def->kind;
|
2017-12-06 03:32:33 +00:00
|
|
|
detailed_name = type->def->detailed_name;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
continue; // applies to for loop
|
|
|
|
}
|
|
|
|
|
|
|
|
optional<lsRange> loc = GetLsRange(working_file, sym.loc.range);
|
|
|
|
if (loc) {
|
|
|
|
auto it = grouped_symbols.find(sym.idx);
|
|
|
|
if (it != grouped_symbols.end()) {
|
|
|
|
it->second.ranges.push_back(*loc);
|
|
|
|
} else {
|
|
|
|
Out_CqueryPublishSemanticHighlighting::Symbol symbol;
|
2018-01-31 04:59:31 +00:00
|
|
|
symbol.stableId = semantic_cache_for_file->GetStableId(
|
|
|
|
sym.idx.kind, std::string(detailed_name));
|
2018-01-28 20:34:31 +00:00
|
|
|
symbol.parentKind = parent_kind;
|
2018-01-17 01:48:22 +00:00
|
|
|
symbol.kind = kind;
|
2018-01-26 17:28:29 +00:00
|
|
|
symbol.storage = storage;
|
2017-12-06 03:32:33 +00:00
|
|
|
symbol.ranges.push_back(*loc);
|
|
|
|
grouped_symbols[sym.idx] = symbol;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-10 03:04:08 +00:00
|
|
|
// Make ranges non-overlapping using a scan line algorithm.
|
|
|
|
std::vector<ScanLineEvent> events;
|
|
|
|
int id = 0;
|
|
|
|
for (auto& entry : grouped_symbols) {
|
|
|
|
Out_CqueryPublishSemanticHighlighting::Symbol& symbol = entry.second;
|
|
|
|
for (auto& loc : symbol.ranges) {
|
2018-01-11 02:43:01 +00:00
|
|
|
// For ranges sharing the same start point, the one with leftmost end
|
|
|
|
// point comes first.
|
2018-01-10 03:04:08 +00:00
|
|
|
events.push_back({loc.start, loc.end, id, &symbol});
|
|
|
|
// For ranges sharing the same end point, their relative order does not
|
|
|
|
// matter, therefore we arbitrarily assign loc.end to them. We use
|
|
|
|
// negative id to indicate a deletion event.
|
|
|
|
events.push_back({loc.end, loc.end, ~id, &symbol});
|
|
|
|
id++;
|
|
|
|
}
|
|
|
|
symbol.ranges.clear();
|
|
|
|
}
|
|
|
|
std::sort(events.begin(), events.end());
|
|
|
|
|
|
|
|
std::vector<uint8_t> deleted(id, 0);
|
|
|
|
int top = 0;
|
|
|
|
for (size_t i = 0; i < events.size(); i++) {
|
|
|
|
while (top && deleted[events[top - 1].id])
|
|
|
|
top--;
|
|
|
|
// Order [a, b0) after [a, b1) if b0 < b1. The range comes later overrides
|
|
|
|
// the ealier. The order of [a0, b) [a1, b) does not matter.
|
|
|
|
// The order of [a, b) [b, c) does not as long as we do not emit empty
|
|
|
|
// ranges.
|
2018-01-11 02:43:01 +00:00
|
|
|
// Attribute range [events[i-1].pos, events[i].pos) to events[top-1].symbol
|
|
|
|
// .
|
2018-01-10 03:04:08 +00:00
|
|
|
if (top && !(events[i - 1].pos == events[i].pos))
|
2018-01-11 02:43:01 +00:00
|
|
|
events[top - 1].symbol->ranges.emplace_back(events[i - 1].pos,
|
|
|
|
events[i].pos);
|
2018-01-10 03:04:08 +00:00
|
|
|
if (events[i].id >= 0)
|
|
|
|
events[top++] = events[i];
|
|
|
|
else
|
|
|
|
deleted[~events[i].id] = 1;
|
|
|
|
}
|
|
|
|
|
2017-12-06 03:32:33 +00:00
|
|
|
// Publish.
|
|
|
|
Out_CqueryPublishSemanticHighlighting out;
|
|
|
|
out.params.uri = lsDocumentUri::FromPath(working_file->filename);
|
|
|
|
for (auto& entry : grouped_symbols)
|
2018-01-10 03:04:08 +00:00
|
|
|
if (entry.second.ranges.size())
|
|
|
|
out.params.symbols.push_back(entry.second);
|
2017-12-24 00:25:18 +00:00
|
|
|
QueueManager::WriteStdout(IpcId::CqueryPublishSemanticHighlighting, out);
|
2017-12-07 01:00:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ShouldIgnoreFileForIndexing(const std::string& path) {
|
|
|
|
return StartsWith(path, "git:");
|
|
|
|
}
|