Region highlighters track line changes in the buffer

This way, region highlighters only need to regex-parse lines that
actually changed. matches on lines that just moved around but whose
content did not change can be updated simply by updating their line
coordinate.
This commit is contained in:
Maxime Coste 2014-01-12 16:28:11 +00:00
parent 4aed32d152
commit faabd24e11

View File

@ -155,7 +155,7 @@ struct BufferSideCache
{ {
Value& cache_val = buffer.values()[m_id]; Value& cache_val = buffer.values()[m_id];
if (not cache_val) if (not cache_val)
cache_val = Value(T{}); cache_val = Value(T{buffer});
return cache_val.as<T>(); return cache_val.as<T>();
} }
private: private:
@ -190,6 +190,7 @@ public:
private: private:
struct MatchesCache struct MatchesCache
{ {
MatchesCache(const Buffer&){}
BufferRange m_range; BufferRange m_range;
size_t m_timestamp = 0; size_t m_timestamp = 0;
std::vector<std::vector<std::pair<BufferCoord, BufferCoord>>> m_matches; std::vector<std::vector<std::pair<BufferCoord, BufferCoord>>> m_matches;
@ -543,47 +544,83 @@ private:
struct Match struct Match
{ {
size_t timestamp; size_t timestamp;
BufferCoord begin; LineCount line;
BufferCoord end; ByteCount begin;
ByteCount end;
}; };
using MatchList = std::vector<Match>; using MatchList = std::vector<Match>;
struct RegionCache struct LineChange
{ {
LineCount pos;
LineCount num;
};
struct Cache : public BufferChangeListener_AutoRegister
{
Cache(const Buffer& buffer)
: BufferChangeListener_AutoRegister(const_cast<Buffer&>(buffer))
{}
void on_insert(const Buffer& buffer, BufferCoord begin, BufferCoord end) override
{
changes.push_back({begin.line, end.line - begin.line});
}
void on_erase(const Buffer& buffer, BufferCoord begin, BufferCoord end) override
{
changes.push_back({begin.line, begin.line - end.line});
}
size_t timestamp = 0; size_t timestamp = 0;
MatchList begin_matches; MatchList begin_matches;
MatchList end_matches; MatchList end_matches;
RegionList regions; RegionList regions;
std::vector<LineChange> changes;
}; };
BufferSideCache<RegionCache> m_cache; BufferSideCache<Cache> m_cache;
const RegionList& update_cache_ifn(const Buffer& buffer) const RegionList& update_cache_ifn(const Buffer& buffer)
{ {
RegionCache& cache = m_cache.get(buffer); Cache& cache = m_cache.get(buffer);
const size_t buf_timestamp = buffer.timestamp(); const size_t buf_timestamp = buffer.timestamp();
if (cache.timestamp == buf_timestamp) if (cache.timestamp == buf_timestamp)
return cache.regions; return cache.regions;
update_matches(buffer, cache.timestamp, cache.begin_matches, m_begin); {
update_matches(buffer, cache.timestamp, cache.end_matches, m_end); std::sort(cache.changes.begin(), cache.changes.end(),
[](const LineChange& lhs, const LineChange& rhs) {
return lhs.pos < rhs.pos;
});
for (size_t i = 1; i < cache.changes.size(); ++i)
cache.changes[i].num += cache.changes[i-1].num;
update_matches(buffer, cache, cache.begin_matches, m_begin);
update_matches(buffer, cache, cache.end_matches, m_end);
cache.changes.clear();
}
cache.regions.clear(); cache.regions.clear();
for (auto beg_it = cache.begin_matches.begin(), end_it = cache.end_matches.begin(); for (auto beg_it = cache.begin_matches.begin(), end_it = cache.end_matches.begin();
beg_it != cache.begin_matches.end(); ) beg_it != cache.begin_matches.end(); )
{ {
auto compare_matches = [&](const Match& lhs, const Match& rhs) { auto compare_matches = [&](const Match& lhs, const Match& rhs) {
if (lhs.line != rhs.line)
return lhs.line < rhs.line;
return lhs.end < rhs.end; return lhs.end < rhs.end;
}; };
end_it = std::upper_bound(end_it, cache.end_matches.end(), end_it = std::upper_bound(end_it, cache.end_matches.end(),
*beg_it, compare_matches); *beg_it, compare_matches);
if (end_it == cache.end_matches.end()) if (end_it == cache.end_matches.end())
{ {
cache.regions.push_back({beg_it->begin, buffer.end_coord()}); cache.regions.push_back({{beg_it->line, beg_it->begin}, buffer.end_coord()});
break; break;
} }
else else
{ {
cache.regions.push_back({beg_it->begin, end_it->end}); cache.regions.push_back({{beg_it->line, beg_it->begin},
{end_it->line, end_it->end}});
beg_it = std::upper_bound(beg_it, cache.begin_matches.end(), beg_it = std::upper_bound(beg_it, cache.begin_matches.end(),
*end_it, compare_matches); *end_it, compare_matches);
} }
@ -592,32 +629,55 @@ private:
return cache.regions; return cache.regions;
} }
void update_matches(const Buffer& buffer, size_t timestamp, MatchList& matches, const Regex& regex) void update_matches(const Buffer& buffer, const Cache& cache, MatchList& matches, const Regex& regex)
{ {
const size_t buf_timestamp = buffer.timestamp(); const size_t buf_timestamp = buffer.timestamp();
// remove out of date matches // remove out of date matches and update line for others
for (auto it = matches.begin(); it != matches.end();) for (auto it = matches.begin(); it != matches.end();)
{ {
if (it->timestamp < buffer.line_timestamp(it->begin.line)) auto change_it = std::lower_bound(cache.changes.begin(), cache.changes.end(), it->line,
[](const LineChange& c, const LineCount& l) {
return c.pos < l; });
if (change_it != cache.changes.begin())
{
it->line += (change_it-1)->num;
if (it->line <= (change_it-1)->pos)
{
it = matches.erase(it);
continue;
}
}
if (it->line >= buffer.line_count() or
it->timestamp < buffer.line_timestamp(it->line))
it = matches.erase(it); it = matches.erase(it);
else ++it; else
{
it->timestamp = buf_timestamp;
kak_assert(buffer.is_valid({it->line, it->begin}));
kak_assert(buffer.is_valid({it->line, it->end}));
++it;
}
} }
// try to find new matches in each updated lines // try to find new matches in each updated lines
for (auto line = 0_line; line < buffer.line_count(); ++line) for (auto line = 0_line; line < buffer.line_count(); ++line)
{ {
if (buffer.line_timestamp(line) > timestamp) if (buffer.line_timestamp(line) > cache.timestamp)
{ {
auto& l = buffer[line]; auto& l = buffer[line];
for (boost::regex_iterator<String::const_iterator> it{l.begin(), l.end(), regex}, end{}; it != end; ++it) for (boost::regex_iterator<String::const_iterator> it{l.begin(), l.end(), regex}, end{}; it != end; ++it)
{ {
BufferCoord b{line, (int)((*it)[0].first - l.begin())}; ByteCount b = (int)((*it)[0].first - l.begin());
BufferCoord e{line, (int)((*it)[0].second - l.begin())}; ByteCount e = (int)((*it)[0].second - l.begin());
matches.push_back({ buf_timestamp, b, e }); matches.push_back({ buf_timestamp, line, b, e });
} }
} }
} }
std::sort(matches.begin(), matches.end(), std::sort(matches.begin(), matches.end(),
[](const Match& lhs, const Match& rhs) { return lhs.begin < rhs.begin; }); [](const Match& lhs, const Match& rhs) {
if (lhs.line != rhs.line)
return lhs.line < rhs.line;
return lhs.begin < rhs.begin;
});
} }
}; };