Refactor region highlighter, extract code to helper classes
This commit is contained in:
parent
0a76fe3040
commit
0faf7ff5e6
|
@ -667,56 +667,8 @@ HighlighterAndId reference_factory(HighlighterParameters params)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RegionHighlighter
|
namespace RegionHighlight
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
RegionHighlighter(Regex begin, Regex end, Regex recurse = Regex{})
|
|
||||||
: m_begin(std::move(begin)),
|
|
||||||
m_end(std::move(end)),
|
|
||||||
m_recurse(std::move(recurse))
|
|
||||||
{
|
|
||||||
if (m_begin.empty() or m_end.empty())
|
|
||||||
throw runtime_error("invalid regex for region highlighter");
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(HierachicalHighlighter::GroupMap groups, const Context& context,
|
|
||||||
HighlightFlags flags, DisplayBuffer& display_buffer)
|
|
||||||
{
|
|
||||||
if (flags != HighlightFlags::Highlight)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto it = groups.find("content");
|
|
||||||
if (it == groups.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto range = display_buffer.range();
|
|
||||||
const auto& buffer = context.buffer();
|
|
||||||
auto& regions = update_cache_ifn(buffer);
|
|
||||||
auto begin = std::lower_bound(regions.begin(), regions.end(), range.first,
|
|
||||||
[](const Region& r, const ByteCoord& c) { return r.end < c; });
|
|
||||||
auto end = std::lower_bound(begin, regions.end(), range.second,
|
|
||||||
[](const Region& r, const ByteCoord& c) { return r.begin < c; });
|
|
||||||
auto correct = [&](const ByteCoord& c) -> ByteCoord {
|
|
||||||
if (buffer[c.line].length() == c.column)
|
|
||||||
return {c.line+1, 0};
|
|
||||||
return c;
|
|
||||||
};
|
|
||||||
for (; begin != end; ++begin)
|
|
||||||
apply_highlighter(context, flags, display_buffer,
|
|
||||||
correct(begin->begin), correct(begin->end),
|
|
||||||
it->second);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
Regex m_begin;
|
|
||||||
Regex m_end;
|
|
||||||
Regex m_recurse;
|
|
||||||
|
|
||||||
struct Region
|
|
||||||
{
|
|
||||||
ByteCoord begin;
|
|
||||||
ByteCoord end;
|
|
||||||
};
|
|
||||||
using RegionList = std::vector<Region>;
|
|
||||||
|
|
||||||
struct Match
|
struct Match
|
||||||
{
|
{
|
||||||
|
@ -730,88 +682,6 @@ private:
|
||||||
};
|
};
|
||||||
using MatchList = std::vector<Match>;
|
using MatchList = std::vector<Match>;
|
||||||
|
|
||||||
struct Cache
|
|
||||||
{
|
|
||||||
size_t timestamp = 0;
|
|
||||||
MatchList begin_matches;
|
|
||||||
MatchList end_matches;
|
|
||||||
MatchList recurse_matches;
|
|
||||||
RegionList regions;
|
|
||||||
};
|
|
||||||
BufferSideCache<Cache> m_cache;
|
|
||||||
|
|
||||||
const RegionList& update_cache_ifn(const Buffer& buffer)
|
|
||||||
{
|
|
||||||
Cache& cache = m_cache.get(buffer);
|
|
||||||
const size_t buf_timestamp = buffer.timestamp();
|
|
||||||
if (cache.timestamp == buf_timestamp)
|
|
||||||
return cache.regions;
|
|
||||||
|
|
||||||
if (cache.timestamp == 0)
|
|
||||||
{
|
|
||||||
find_matches(buffer, cache.begin_matches, m_begin);
|
|
||||||
find_matches(buffer, cache.end_matches, m_end);
|
|
||||||
if (not m_recurse.empty())
|
|
||||||
find_matches(buffer, cache.recurse_matches, m_recurse);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto modifs = compute_line_modifications(buffer, cache.timestamp);
|
|
||||||
update_matches(buffer, modifs, cache.begin_matches, m_begin);
|
|
||||||
update_matches(buffer, modifs, cache.end_matches, m_end);
|
|
||||||
if (not m_recurse.empty())
|
|
||||||
update_matches(buffer, modifs, cache.recurse_matches, m_recurse);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto compare_matches_end = [](const Match& lhs, const Match& rhs) {
|
|
||||||
return lhs.end_coord() < rhs.end_coord();
|
|
||||||
};
|
|
||||||
|
|
||||||
cache.regions.clear();
|
|
||||||
int recurse_level = 0;
|
|
||||||
auto ref_it = cache.begin_matches.begin();
|
|
||||||
for (auto beg_it = cache.begin_matches.begin(),
|
|
||||||
end_it = cache.end_matches.begin(),
|
|
||||||
rec_it = cache.recurse_matches.begin();
|
|
||||||
beg_it != cache.begin_matches.end(); )
|
|
||||||
{
|
|
||||||
end_it = std::upper_bound(end_it, cache.end_matches.end(),
|
|
||||||
*ref_it, compare_matches_end);
|
|
||||||
rec_it = std::upper_bound(rec_it, cache.recurse_matches.end(),
|
|
||||||
*ref_it, compare_matches_end);
|
|
||||||
|
|
||||||
if (end_it == cache.end_matches.end())
|
|
||||||
{
|
|
||||||
cache.regions.push_back({ {beg_it->line, beg_it->begin},
|
|
||||||
buffer.end_coord() });
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (rec_it != cache.recurse_matches.end() and
|
|
||||||
rec_it->end_coord() < end_it->begin_coord())
|
|
||||||
{
|
|
||||||
++recurse_level;
|
|
||||||
++rec_it;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recurse_level == 0)
|
|
||||||
{
|
|
||||||
cache.regions.push_back({ beg_it->begin_coord(),
|
|
||||||
end_it->end_coord() });
|
|
||||||
beg_it = std::upper_bound(beg_it, cache.begin_matches.end(),
|
|
||||||
*end_it, compare_matches_end);
|
|
||||||
ref_it = beg_it;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
--recurse_level;
|
|
||||||
ref_it = end_it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cache.timestamp = buf_timestamp;
|
|
||||||
return cache.regions;
|
|
||||||
}
|
|
||||||
|
|
||||||
void find_matches(const Buffer& buffer, MatchList& matches, const Regex& regex)
|
void find_matches(const Buffer& buffer, MatchList& matches, const Regex& regex)
|
||||||
{
|
{
|
||||||
const size_t buf_timestamp = buffer.timestamp();
|
const size_t buf_timestamp = buffer.timestamp();
|
||||||
|
@ -827,7 +697,8 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_matches(const Buffer& buffer, memoryview<LineModification> modifs, MatchList& matches, const Regex& regex)
|
void update_matches(const Buffer& buffer, memoryview<LineModification> modifs,
|
||||||
|
MatchList& matches, const Regex& regex)
|
||||||
{
|
{
|
||||||
const size_t buf_timestamp = buffer.timestamp();
|
const size_t buf_timestamp = buffer.timestamp();
|
||||||
// remove out of date matches and update line for others
|
// remove out of date matches and update line for others
|
||||||
|
@ -884,6 +755,175 @@ private:
|
||||||
return lhs.begin_coord() < rhs.begin_coord();
|
return lhs.begin_coord() < rhs.begin_coord();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RegionMatches
|
||||||
|
{
|
||||||
|
MatchList begin_matches;
|
||||||
|
MatchList end_matches;
|
||||||
|
MatchList recurse_matches;
|
||||||
|
|
||||||
|
static bool compare_to_end(ByteCoord lhs, const Match& rhs)
|
||||||
|
{
|
||||||
|
return lhs < rhs.end_coord();
|
||||||
|
}
|
||||||
|
|
||||||
|
MatchList::const_iterator find_next_begin(ByteCoord pos) const
|
||||||
|
{
|
||||||
|
return std::upper_bound(begin_matches.begin(), begin_matches.end(),
|
||||||
|
pos, compare_to_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
MatchList::const_iterator find_matching_end(MatchList::const_iterator beg_it) const
|
||||||
|
{
|
||||||
|
auto end_it = end_matches.begin();
|
||||||
|
auto rec_it = recurse_matches.begin();
|
||||||
|
auto ref_pos = beg_it->end_coord();
|
||||||
|
int recurse_level = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
end_it = std::upper_bound(end_it, end_matches.end(),
|
||||||
|
ref_pos, compare_to_end);
|
||||||
|
rec_it = std::upper_bound(rec_it, recurse_matches.end(),
|
||||||
|
ref_pos, compare_to_end);
|
||||||
|
|
||||||
|
if (end_it == end_matches.end())
|
||||||
|
return end_it;
|
||||||
|
|
||||||
|
while (rec_it != recurse_matches.end() and
|
||||||
|
rec_it->end_coord() < end_it->begin_coord())
|
||||||
|
{
|
||||||
|
++recurse_level;
|
||||||
|
++rec_it;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recurse_level == 0)
|
||||||
|
return end_it;
|
||||||
|
|
||||||
|
--recurse_level;
|
||||||
|
ref_pos = end_it->end_coord();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RegionDesc
|
||||||
|
{
|
||||||
|
Regex m_begin;
|
||||||
|
Regex m_end;
|
||||||
|
Regex m_recurse;
|
||||||
|
|
||||||
|
RegionMatches find_matches(const Buffer& buffer) const
|
||||||
|
{
|
||||||
|
RegionMatches res;
|
||||||
|
RegionHighlight::find_matches(buffer, res.begin_matches, m_begin);
|
||||||
|
RegionHighlight::find_matches(buffer, res.end_matches, m_end);
|
||||||
|
if (not m_recurse.empty())
|
||||||
|
RegionHighlight::find_matches(buffer, res.recurse_matches, m_recurse);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_matches(const Buffer& buffer,
|
||||||
|
memoryview<LineModification> modifs,
|
||||||
|
RegionMatches& matches) const
|
||||||
|
{
|
||||||
|
RegionHighlight::update_matches(buffer, modifs, matches.begin_matches, m_begin);
|
||||||
|
RegionHighlight::update_matches(buffer, modifs, matches.end_matches, m_end);
|
||||||
|
if (not m_recurse.empty())
|
||||||
|
RegionHighlight::update_matches(buffer, modifs, matches.recurse_matches, m_recurse);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RegionHighlighter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RegionHighlighter(Regex begin, Regex end, Regex recurse = Regex{})
|
||||||
|
: m_region{ std::move(begin), std::move(end), std::move(recurse) }
|
||||||
|
{
|
||||||
|
if (m_region.m_begin.empty() or m_region.m_end.empty())
|
||||||
|
throw runtime_error("invalid regex for region highlighter");
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(HierachicalHighlighter::GroupMap groups, const Context& context,
|
||||||
|
HighlightFlags flags, DisplayBuffer& display_buffer)
|
||||||
|
{
|
||||||
|
if (flags != HighlightFlags::Highlight)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto it = groups.find("content");
|
||||||
|
if (it == groups.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto range = display_buffer.range();
|
||||||
|
const auto& buffer = context.buffer();
|
||||||
|
auto& regions = update_cache_ifn(buffer);
|
||||||
|
auto begin = std::lower_bound(regions.begin(), regions.end(), range.first,
|
||||||
|
[](const Region& r, ByteCoord c) { return r.end < c; });
|
||||||
|
auto end = std::lower_bound(begin, regions.end(), range.second,
|
||||||
|
[](const Region& r, ByteCoord c) { return r.begin < c; });
|
||||||
|
auto correct = [&](ByteCoord c) -> ByteCoord {
|
||||||
|
if (buffer[c.line].length() == c.column)
|
||||||
|
return {c.line+1, 0};
|
||||||
|
return c;
|
||||||
|
};
|
||||||
|
for (; begin != end; ++begin)
|
||||||
|
apply_highlighter(context, flags, display_buffer,
|
||||||
|
correct(begin->begin), correct(begin->end),
|
||||||
|
it->second);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
RegionDesc m_region;
|
||||||
|
|
||||||
|
struct Region
|
||||||
|
{
|
||||||
|
ByteCoord begin;
|
||||||
|
ByteCoord end;
|
||||||
|
};
|
||||||
|
using RegionList = std::vector<Region>;
|
||||||
|
|
||||||
|
struct Cache
|
||||||
|
{
|
||||||
|
size_t timestamp = 0;
|
||||||
|
RegionMatches matches;
|
||||||
|
RegionList regions;
|
||||||
|
};
|
||||||
|
BufferSideCache<Cache> m_cache;
|
||||||
|
|
||||||
|
const RegionList& update_cache_ifn(const Buffer& buffer)
|
||||||
|
{
|
||||||
|
Cache& cache = m_cache.get(buffer);
|
||||||
|
const size_t buf_timestamp = buffer.timestamp();
|
||||||
|
if (cache.timestamp == buf_timestamp)
|
||||||
|
return cache.regions;
|
||||||
|
|
||||||
|
if (cache.timestamp == 0)
|
||||||
|
cache.matches = m_region.find_matches(buffer);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto modifs = compute_line_modifications(buffer, cache.timestamp);
|
||||||
|
m_region.update_matches(buffer, modifs, cache.matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.regions.clear();
|
||||||
|
for (auto beg_it = cache.matches.begin_matches.cbegin();
|
||||||
|
beg_it != cache.matches.begin_matches.end(); )
|
||||||
|
{
|
||||||
|
auto end_it = cache.matches.find_matching_end(beg_it);
|
||||||
|
|
||||||
|
if (end_it == cache.matches.end_matches.end())
|
||||||
|
{
|
||||||
|
cache.regions.push_back({ {beg_it->line, beg_it->begin},
|
||||||
|
buffer.end_coord() });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cache.regions.push_back({ beg_it->begin_coord(),
|
||||||
|
end_it->end_coord() });
|
||||||
|
beg_it = cache.matches.find_next_begin(end_it->end_coord());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cache.timestamp = buf_timestamp;
|
||||||
|
return cache.regions;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
HighlighterAndId region_factory(HighlighterParameters params)
|
HighlighterAndId region_factory(HighlighterParameters params)
|
||||||
|
@ -911,6 +951,8 @@ HighlighterAndId region_factory(HighlighterParameters params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void register_highlighters()
|
void register_highlighters()
|
||||||
{
|
{
|
||||||
HighlighterRegistry& registry = HighlighterRegistry::instance();
|
HighlighterRegistry& registry = HighlighterRegistry::instance();
|
||||||
|
@ -925,7 +967,7 @@ void register_highlighters()
|
||||||
registry.register_func("group", highlighter_group_factory);
|
registry.register_func("group", highlighter_group_factory);
|
||||||
registry.register_func("flag_lines", flag_lines_factory);
|
registry.register_func("flag_lines", flag_lines_factory);
|
||||||
registry.register_func("ref", reference_factory);
|
registry.register_func("ref", reference_factory);
|
||||||
registry.register_func("region", region_factory);
|
registry.register_func("region", RegionHighlight::region_factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user