Make semantic highlighting symbol ranges non-overlapping using a scan line algorithm

This commit is contained in:
Fangrui Song 2018-01-09 19:04:08 -08:00
parent 0bcf72d0a0
commit 60e384a859
3 changed files with 60 additions and 1 deletions

View File

@ -280,6 +280,10 @@ bool lsPosition::operator==(const lsPosition& other) const {
return line == other.line && character == other.character;
}
bool lsPosition::operator<(const lsPosition& other) const {
return line != other.line ? line < other.line : character < other.character;
}
std::string lsPosition::ToString() const {
return std::to_string(line) + ":" + std::to_string(character);
}

View File

@ -148,6 +148,7 @@ struct lsPosition {
lsPosition(int line, int character);
bool operator==(const lsPosition& other) const;
bool operator<(const lsPosition& other) const;
std::string ToString() const;

View File

@ -8,6 +8,21 @@
#include <loguru.hpp>
#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;
}
};
}
MessageHandler::MessageHandler() {
// Dynamically allocate |message_handlers|, otherwise there will be static
// initialization order races.
@ -172,11 +187,50 @@ void EmitSemanticHighlighting(QueryDatabase* db,
}
}
// 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) {
// For ranges sharing the same start point, the one with leftmost end point
// comes first.
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++) {
// |start| is used as liveness tag here.
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.
// Attribute range [events[i-1].pos, events[i].pos) to events[top-1].symbol .
if (top && !(events[i - 1].pos == events[i].pos))
events[top - 1].symbol->ranges.emplace_back(events[i - 1].pos, events[i].pos);
if (events[i].id >= 0)
events[top++] = events[i];
else
deleted[~events[i].id] = 1;
}
// Publish.
Out_CqueryPublishSemanticHighlighting out;
out.params.uri = lsDocumentUri::FromPath(working_file->filename);
for (auto& entry : grouped_symbols)
out.params.symbols.push_back(entry.second);
if (entry.second.ranges.size())
out.params.symbols.push_back(entry.second);
QueueManager::WriteStdout(IpcId::CqueryPublishSemanticHighlighting, out);
}