Add support for recursion in region highlighter

An optional parameter defines the recursion regex, that match
potential closers.

For example, %sh{ ... } blocks support nested { }, so defining \{
as the recurse regex make the region highlighter correctly match
%sh{ ... { ... } ... } constructs
This commit is contained in:
Maxime Coste 2014-06-11 00:23:44 +01:00
parent 37d66b1e0b
commit 0a76fe3040
2 changed files with 63 additions and 27 deletions

View File

@ -17,7 +17,7 @@ addhl -def-group kakrc/single_string/content fill string
addhl -def-group kakrc regex (^|\h)\#[^\n]*\n 0:comment addhl -def-group kakrc regex (^|\h)\#[^\n]*\n 0:comment
addhl -def-group kakrc region shell '%sh\{' '\}' addhl -def-group kakrc region shell '%sh\{' '\}' '\{'
addhl -def-group kakrc/shell/content ref sh addhl -def-group kakrc/shell/content ref sh
hook global WinSetOption filetype=kak %{ addhl ref kakrc } hook global WinSetOption filetype=kak %{ addhl ref kakrc }

View File

@ -670,10 +670,14 @@ HighlighterAndId reference_factory(HighlighterParameters params)
struct RegionHighlighter struct RegionHighlighter
{ {
public: public:
RegionHighlighter(Regex begin, Regex end) RegionHighlighter(Regex begin, Regex end, Regex recurse = Regex{})
: m_begin(std::move(begin)), : m_begin(std::move(begin)),
m_end(std::move(end)) 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, void operator()(HierachicalHighlighter::GroupMap groups, const Context& context,
HighlightFlags flags, DisplayBuffer& display_buffer) HighlightFlags flags, DisplayBuffer& display_buffer)
@ -705,6 +709,7 @@ public:
private: private:
Regex m_begin; Regex m_begin;
Regex m_end; Regex m_end;
Regex m_recurse;
struct Region struct Region
{ {
@ -719,26 +724,18 @@ private:
LineCount line; LineCount line;
ByteCount begin; ByteCount begin;
ByteCount end; ByteCount end;
ByteCoord begin_coord() const { return { line, begin }; }
ByteCoord end_coord() const { return { line, end }; }
}; };
using MatchList = std::vector<Match>; using MatchList = std::vector<Match>;
static bool compare_matches_end(const Match& lhs, const Match& rhs)
{
return (lhs.line != rhs.line) ? lhs.line < rhs.line
: lhs.end < rhs.end;
}
static bool compare_matches_begin(const Match& lhs, const Match& rhs)
{
return (lhs.line != rhs.line) ? lhs.line < rhs.line
: lhs.begin < rhs.begin;
}
struct Cache struct Cache
{ {
size_t timestamp = 0; size_t timestamp = 0;
MatchList begin_matches; MatchList begin_matches;
MatchList end_matches; MatchList end_matches;
MatchList recurse_matches;
RegionList regions; RegionList regions;
}; };
BufferSideCache<Cache> m_cache; BufferSideCache<Cache> m_cache;
@ -754,30 +751,62 @@ private:
{ {
find_matches(buffer, cache.begin_matches, m_begin); find_matches(buffer, cache.begin_matches, m_begin);
find_matches(buffer, cache.end_matches, m_end); find_matches(buffer, cache.end_matches, m_end);
if (not m_recurse.empty())
find_matches(buffer, cache.recurse_matches, m_recurse);
} }
else else
{ {
auto modifs = compute_line_modifications(buffer, cache.timestamp); auto modifs = compute_line_modifications(buffer, cache.timestamp);
update_matches(buffer, modifs, cache.begin_matches, m_begin); update_matches(buffer, modifs, cache.begin_matches, m_begin);
update_matches(buffer, modifs, cache.end_matches, m_end); 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(); cache.regions.clear();
for (auto beg_it = cache.begin_matches.begin(), end_it = cache.end_matches.begin(); 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(); ) beg_it != cache.begin_matches.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_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()) if (end_it == cache.end_matches.end())
{ {
cache.regions.push_back({ {beg_it->line, beg_it->begin}, cache.regions.push_back({ {beg_it->line, beg_it->begin},
buffer.end_coord() }); buffer.end_coord() });
break; break;
} }
cache.regions.push_back({ {beg_it->line, beg_it->begin},
{end_it->line, end_it->end} }); while (rec_it != cache.recurse_matches.end() and
beg_it = std::upper_bound(beg_it, cache.begin_matches.end(), rec_it->end_coord() < end_it->begin_coord())
*end_it, compare_matches_end); {
++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; cache.timestamp = buf_timestamp;
return cache.regions; return cache.regions;
@ -821,9 +850,9 @@ private:
if (not erase) if (not erase)
{ {
it->timestamp = buf_timestamp; it->timestamp = buf_timestamp;
kak_assert(buffer.is_valid({it->line, it->begin}) or kak_assert(buffer.is_valid(it->begin_coord()) or
buffer[it->line].length() == it->begin); buffer[it->line].length() == it->begin);
kak_assert(buffer.is_valid({it->line, it->end}) or kak_assert(buffer.is_valid(it->end_coord()) or
buffer[it->line].length() == it->end); buffer[it->line].length() == it->end);
if (ins_pos != it) if (ins_pos != it)
@ -851,7 +880,9 @@ private:
} }
} }
std::inplace_merge(matches.begin(), matches.begin() + pivot, matches.end(), std::inplace_merge(matches.begin(), matches.begin() + pivot, matches.end(),
compare_matches_begin); [](const Match& lhs, const Match& rhs) {
return lhs.begin_coord() < rhs.begin_coord();
});
} }
}; };
@ -859,14 +890,19 @@ HighlighterAndId region_factory(HighlighterParameters params)
{ {
try try
{ {
if (params.size() != 3) if (params.size() != 3 && params.size() != 4)
throw runtime_error("wrong parameter count"); throw runtime_error("wrong parameter count");
Regex begin{params[1], Regex::nosubs | Regex::optimize }; Regex begin{params[1], Regex::nosubs | Regex::optimize };
Regex end{params[2], Regex::nosubs | Regex::optimize }; Regex end{params[2], Regex::nosubs | Regex::optimize };
Regex recurse;
if (params.size() == 4)
recurse = Regex{params[3], Regex::nosubs | Regex::optimize };
return {params[0], return {params[0],
HierachicalHighlighter(RegionHighlighter(std::move(begin), HierachicalHighlighter(RegionHighlighter(std::move(begin),
std::move(end)), std::move(end),
std::move(recurse)),
{ { "content", HighlighterGroup{} } })}; { { "content", HighlighterGroup{} } })};
} }
catch (boost::regex_error& err) catch (boost::regex_error& err)