Refactor highlighters, use an interface with virtual methods

This commit is contained in:
Maxime Coste 2014-10-22 00:20:09 +01:00
parent fc4142178f
commit b2e90fe21e
8 changed files with 307 additions and 375 deletions

View File

@ -370,19 +370,19 @@ const CommandDesc namebuf_cmd = {
} }
}; };
Completions complete_highlighter_group(const Context& context, Completions complete_highlighter(const Context& context,
StringView arg, ByteCount pos_in_token) StringView arg, ByteCount pos_in_token, bool only_group)
{ {
const bool shared = not arg.empty() and arg[0] == '/'; const bool shared = not arg.empty() and arg[0] == '/';
if (shared) if (shared)
{ {
auto& group = DefinedHighlighters::instance(); auto& group = DefinedHighlighters::instance();
return offset_pos(group.complete_group_id(arg.substr(1_byte), pos_in_token-1), 1); return offset_pos(group.complete_child(arg.substr(1_byte), pos_in_token-1, only_group), 1);
} }
else else
{ {
auto& group = context.window().highlighters(); auto& group = context.window().highlighters();
return group.complete_group_id(arg, pos_in_token); return group.complete_child(arg, pos_in_token, only_group);
} }
} }
@ -394,10 +394,10 @@ Completions rm_highlighter_completer(
if (token_to_complete == 0 and not arg.empty() and arg.front() == '/') if (token_to_complete == 0 and not arg.empty() and arg.front() == '/')
{ {
auto& group = DefinedHighlighters::instance(); auto& group = DefinedHighlighters::instance();
return offset_pos(group.complete_id(arg.substr(1_byte), pos_in_token-1), 1); return offset_pos(group.complete_child(arg.substr(1_byte), pos_in_token-1, false), 1);
} }
else if (token_to_complete == 0) else if (token_to_complete == 0)
return context.window().highlighters().complete_id(arg, pos_in_token); return context.window().highlighters().complete_child(arg, pos_in_token, false);
return {}; return {};
} }
@ -407,32 +407,32 @@ Completions add_highlighter_completer(
{ {
StringView arg = params[token_to_complete]; StringView arg = params[token_to_complete];
if (token_to_complete == 1 and params[0] == "-group") if (token_to_complete == 1 and params[0] == "-group")
return complete_highlighter_group(context, params[1], pos_in_token); return complete_highlighter(context, params[1], pos_in_token, true);
else if (token_to_complete == 0 or (token_to_complete == 2 and params[0] == "-group")) else if (token_to_complete == 0 or (token_to_complete == 2 and params[0] == "-group"))
return { 0_byte, arg.length(), HighlighterRegistry::instance().complete_name(arg, pos_in_token) }; return { 0_byte, arg.length(), HighlighterRegistry::instance().complete_name(arg, pos_in_token) };
return Completions{}; return Completions{};
} }
HighlighterGroup& get_highlighter_group(const Context& context, StringView path) Highlighter& get_highlighter(const Context& context, StringView path)
{ {
if (path.empty()) if (path.empty())
throw runtime_error("group path should not be empty"); throw runtime_error("group path should not be empty");
HighlighterGroup* group = nullptr; Highlighter* root = nullptr;
if (path[0] == '/') if (path[0] == '/')
{ {
group = &DefinedHighlighters::instance(); root = &DefinedHighlighters::instance();
path = path.substr(1_byte); path = path.substr(1_byte);
} }
else else
group = &context.window().highlighters(); root = &context.window().highlighters();
if (path.back() == '/') if (path.back() == '/')
path = path.substr(0_byte, path.length() - 1); path = path.substr(0_byte, path.length() - 1);
if (not path.empty()) if (not path.empty())
return group->get_group(path); return root->get_child(path);
return *group; return *root;
} }
const CommandDesc add_highlighter_cmd = { const CommandDesc add_highlighter_cmd = {
@ -456,9 +456,9 @@ const CommandDesc add_highlighter_cmd = {
highlighter_params.push_back(*begin); highlighter_params.push_back(*begin);
auto& group = (parser.has_option("group")) ? auto& group = (parser.has_option("group")) ?
get_highlighter_group(context, parser.option_value("group")) get_highlighter(context, parser.option_value("group"))
: context.window().highlighters(); : context.window().highlighters();
group.append(registry[name](highlighter_params)); group.add_child(registry[name](highlighter_params));
} }
}; };
@ -477,10 +477,10 @@ const CommandDesc rm_highlighter_cmd = {
StringView path = parser[0]; StringView path = parser[0];
auto sep_it = find(reversed(path), '/'); auto sep_it = find(reversed(path), '/');
auto& group = sep_it != path.rend() ? auto& group = sep_it != path.rend() ?
get_highlighter_group(context, {path.begin(), sep_it.base()-1}) get_highlighter(context, {path.begin(), sep_it.base()-1})
: context.window().highlighters(); : context.window().highlighters();
group.remove({sep_it.base(), path.end()}); group.remove_child({sep_it.base(), path.end()});
} }
}; };

View File

@ -25,8 +25,39 @@ enum class HighlightFlags
// color, adding information text (line numbering for example) or replacing // color, adding information text (line numbering for example) or replacing
// buffer content (folding for example) // buffer content (folding for example)
using HighlighterFunc = std::function<void (const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer)>; struct Highlighter;
using HighlighterAndId = std::pair<String, HighlighterFunc>;
using HighlighterAndId = std::pair<String, std::unique_ptr<Highlighter>>;
struct Highlighter
{
virtual void highlight(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer) = 0;
virtual bool has_children() const { return false; }
virtual Highlighter& get_child(StringView path) { throw runtime_error("this highlighter do not hold children"); }
virtual void add_child(HighlighterAndId&& hl) { throw runtime_error("this highlighter do not hold children"); }
virtual void remove_child(StringView id) { throw runtime_error("this highlighter do not hold children"); }
virtual Completions complete_child(StringView path, ByteCount cursor_pos, bool group) const { throw runtime_error("this highlighter do not hold children"); }
};
template<typename Func>
struct SimpleHighlighter : public Highlighter
{
SimpleHighlighter(Func func) : m_func(std::move(func)) {}
virtual void highlight(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer) override
{
m_func(context, flags, display_buffer);
}
private:
Func m_func;
};
template<typename T>
std::unique_ptr<SimpleHighlighter<T>> make_simple_highlighter(T func)
{
return make_unique<SimpleHighlighter<T>>(std::move(func));
}
using HighlighterParameters = memoryview<String>; using HighlighterParameters = memoryview<String>;
using HighlighterFactory = std::function<HighlighterAndId (HighlighterParameters params)>; using HighlighterFactory = std::function<HighlighterAndId (HighlighterParameters params)>;

View File

@ -3,164 +3,54 @@
namespace Kakoune namespace Kakoune
{ {
static constexpr Codepoint path_separator = '/'; void HighlighterGroup::highlight(const Context& context, HighlightFlags flags,
DisplayBuffer& display_buffer)
void HighlighterGroup::operator()(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer) const
{ {
for (auto& hl : m_highlighters) for (auto& hl : m_highlighters)
hl.second(context, flags, display_buffer); hl.second->highlight(context, flags, display_buffer);
} }
void HighlighterGroup::append(HighlighterAndId&& hl) void HighlighterGroup::add_child(HighlighterAndId&& hl)
{ {
if (m_highlighters.contains(hl.first)) if (m_highlighters.contains(hl.first))
throw runtime_error("duplicate id: " + hl.first); throw runtime_error("duplicate id: " + hl.first);
m_highlighters.append(std::move(hl)); m_highlighters.append(std::move(hl));
} }
void HighlighterGroup::remove(StringView id)
void HighlighterGroup::remove_child(StringView id)
{ {
m_highlighters.remove(id); m_highlighters.remove(id);
} }
HighlighterGroup& HighlighterGroup::get_group(StringView path) Highlighter& HighlighterGroup::get_child(StringView path)
{ {
auto sep_it = find(path, path_separator); auto sep_it = find(path, '/');
StringView id(path.begin(), sep_it); StringView id(path.begin(), sep_it);
auto it = m_highlighters.find(id); auto it = m_highlighters.find(id);
if (it == m_highlighters.end()) if (it == m_highlighters.end())
throw group_not_found("no such id: "_str + id); throw child_not_found("no such id: "_str + id);
if (auto* group = it->second.target<HighlighterGroup>()) if (sep_it == path.end())
{ return *it->second;
else
return it->second->get_child({sep_it+1, path.end()});
}
Completions HighlighterGroup::complete_child(StringView path, ByteCount cursor_pos, bool group) const
{
auto sep_it = find(path, '/');
if (sep_it != path.end()) if (sep_it != path.end())
return group->get_group({sep_it+1, path.end()});
return *group;
}
else if (auto* hier_group = it->second.target<HierachicalHighlighter>())
{ {
if (sep_it == path.end()) ByteCount offset = sep_it+1 - path.begin();
throw group_not_found("not a leaf group: "_str + id); Highlighter& hl = const_cast<HighlighterGroup*>(this)->get_child({path.begin(), sep_it});
return hier_group->get_group({sep_it+1, path.end()}); return offset_pos(hl.complete_child(path.substr(offset), cursor_pos - offset, group), offset);
} }
else
throw group_not_found("not a group: "_str + id);
}
HighlighterFunc HighlighterGroup::get_highlighter(StringView path) const auto condition = [=](const HighlighterMap::value_type& hl)
{
auto sep_it = find(path, path_separator);
StringView id(path.begin(), sep_it);
auto it = m_highlighters.find(id);
if (it == m_highlighters.end())
throw group_not_found("no such id: "_str + id);
if (sep_it == path.end())
return HighlighterFunc{std::ref(it->second)};
else if (auto* group = it->second.target<HighlighterGroup>())
return group->get_highlighter({sep_it+1, path.end()});
else if (auto* hier_group = it->second.target<HierachicalHighlighter>())
return hier_group->get_highlighter({sep_it+1, path.end()});
else
throw group_not_found("not a group: "_str + id);
}
template<Completions (HighlighterGroup::*hg_complete)(StringView path, ByteCount cursor_pos) const,
Completions (HierachicalHighlighter::*hh_complete)(StringView path, ByteCount cursor_pos) const,
typename Condition>
Completions complete_impl(const id_map<HighlighterFunc>& highlighters, Condition condition,
StringView path, ByteCount cursor_pos)
{
auto sep_it = find(path, path_separator);
StringView id(path.begin(), sep_it);
if (sep_it == path.end())
return { 0_byte, path.length(), highlighters.complete_id_if(path, cursor_pos, condition) };
auto it = highlighters.find(id);
if (it != highlighters.end())
{ {
const ByteCount offset = (int)(sep_it + 1 - path.begin()); return not group || hl.second->has_children();
cursor_pos -= offset; };
if (auto* group = it->second.target<HighlighterGroup>()) return { 0, 0, m_highlighters.complete_id_if(path, cursor_pos, condition) };
return offset_pos((group->*hg_complete)({sep_it+1, path.end()}, cursor_pos), offset);
if (auto* hier_group = it->second.target<HierachicalHighlighter>())
return offset_pos((hier_group->*hh_complete)({sep_it+1, path.end()}, cursor_pos), offset);
}
return {};
}
Completions HighlighterGroup::complete_id(StringView path, ByteCount cursor_pos) const
{
return complete_impl<
&HighlighterGroup::complete_id,
&HierachicalHighlighter::complete_id
>(m_highlighters, [](const HighlighterAndId&) { return true; }, path, cursor_pos);
}
Completions HighlighterGroup::complete_group_id(StringView path, ByteCount cursor_pos) const
{
return complete_impl<
&HighlighterGroup::complete_group_id,
&HierachicalHighlighter::complete_group_id
>(m_highlighters, [](const HighlighterAndId& func) {
return func.second.target<HighlighterGroup>() or
func.second.target<HierachicalHighlighter>();
}, path, cursor_pos);
}
HighlighterGroup& HierachicalHighlighter::get_group(StringView path)
{
auto sep_it = find(path, path_separator);
StringView id(path.begin(), sep_it);
auto it = m_groups.find(id);
if (it == m_groups.end())
throw group_not_found("no such id: "_str + id);
if (sep_it != path.end())
return it->second.get_group(StringView(sep_it+1, path.end()));
else
return it->second;
}
HighlighterFunc HierachicalHighlighter::get_highlighter(StringView path) const
{
auto sep_it = find(path, path_separator);
StringView id(path.begin(), sep_it);
auto it = m_groups.find(id);
if (it == m_groups.end())
throw group_not_found("no such id: "_str + id);
if (sep_it != path.end())
return it->second.get_highlighter(StringView(sep_it+1, path.end()));
else
return HighlighterFunc(std::ref(it->second));
}
template<Completions (HighlighterGroup::*complete)(StringView path, ByteCount cursor_pos) const>
Completions complete_impl(const HierachicalHighlighter::GroupMap& groups,
StringView path, ByteCount cursor_pos)
{
auto sep_it = find(path, path_separator);
StringView id(path.begin(), sep_it);
auto it = groups.find(id);
if (sep_it == path.end())
return { 0_byte, id.length(), groups.complete_id(id, cursor_pos) };
if (it != groups.end())
{
const ByteCount offset = (int)(sep_it + 1- path.begin());
return offset_pos(
(it->second.*complete)({sep_it+1, path.end()},
cursor_pos - offset), offset);
}
return {};
}
Completions HierachicalHighlighter::complete_id(StringView path, ByteCount cursor_pos) const
{
return complete_impl<&HighlighterGroup::complete_id>(m_groups, path, cursor_pos);
}
Completions HierachicalHighlighter::complete_group_id(StringView path, ByteCount cursor_pos) const
{
return complete_impl<&HighlighterGroup::complete_group_id>(m_groups, path, cursor_pos);
} }
} }

View File

@ -9,59 +9,27 @@
namespace Kakoune namespace Kakoune
{ {
struct group_not_found : public runtime_error struct child_not_found : public runtime_error
{ {
using runtime_error::runtime_error; using runtime_error::runtime_error;
}; };
class HighlighterGroup class HighlighterGroup : public Highlighter
{ {
public: public:
void operator()(const Context& context, void highlight(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer) override;
HighlightFlags flags,
DisplayBuffer& display_buffer) const;
void append(HighlighterAndId&& hl); bool has_children() const { return true; }
void remove(StringView id); void add_child(HighlighterAndId&& hl) override;
void remove_child(StringView id) override;
HighlighterGroup& get_group(StringView path); Highlighter& get_child(StringView path) override;
HighlighterFunc get_highlighter(StringView path) const;
Completions complete_id(StringView path, ByteCount cursor_pos) const; Completions complete_child(StringView path, ByteCount cursor_pos, bool group) const override;
Completions complete_group_id(StringView path, ByteCount cursor_pos) const;
private: private:
id_map<HighlighterFunc> m_highlighters; using HighlighterMap = id_map<std::unique_ptr<Highlighter>>;
}; HighlighterMap m_highlighters;
class HierachicalHighlighter
{
public:
using GroupMap = id_map<HighlighterGroup>;
using Callback = std::function<void (GroupMap& groups,
const Context& context,
HighlightFlags flags,
DisplayBuffer& display_buffer)>;
HierachicalHighlighter(Callback callback, GroupMap groups)
: m_callback(std::move(callback)), m_groups(std::move(groups)) {}
void operator()(const Context& context,
HighlightFlags flags,
DisplayBuffer& display_buffer)
{
m_callback(m_groups, context, flags, display_buffer);
}
HighlighterGroup& get_group(StringView path);
HighlighterFunc get_highlighter(StringView path) const;
Completions complete_id(StringView path, ByteCount cursor_pos) const;
Completions complete_group_id(StringView path, ByteCount cursor_pos) const;
protected:
Callback m_callback;
GroupMap m_groups;
}; };
struct DefinedHighlighters : public HighlighterGroup, struct DefinedHighlighters : public HighlighterGroup,

View File

@ -63,12 +63,11 @@ void highlight_range(DisplayBuffer& display_buffer,
} }
} }
template<typename T>
void apply_highlighter(const Context& context, void apply_highlighter(const Context& context,
HighlightFlags flags, HighlightFlags flags,
DisplayBuffer& display_buffer, DisplayBuffer& display_buffer,
ByteCoord begin, ByteCoord end, ByteCoord begin, ByteCoord end,
T&& highlighter) Highlighter& highlighter)
{ {
using LineIterator = DisplayBuffer::LineList::iterator; using LineIterator = DisplayBuffer::LineList::iterator;
LineIterator first_line; LineIterator first_line;
@ -135,7 +134,7 @@ void apply_highlighter(const Context& context,
} }
region_display.compute_range(); region_display.compute_range();
highlighter(context, flags, region_display); highlighter.highlight(context, flags, region_display);
for (size_t i = 0; i < region_lines.size(); ++i) for (size_t i = 0; i < region_lines.size(); ++i)
{ {
@ -161,7 +160,7 @@ auto apply_face = [](const Face& face)
using FacesSpec = std::vector<String>; using FacesSpec = std::vector<String>;
HighlighterAndId fill_factory(HighlighterParameters params) static HighlighterAndId create_fill_highlighter(HighlighterParameters params)
{ {
if (params.size() != 1) if (params.size() != 1)
throw runtime_error("wrong parameter count"); throw runtime_error("wrong parameter count");
@ -169,14 +168,14 @@ HighlighterAndId fill_factory(HighlighterParameters params)
const String& facespec = params[0]; const String& facespec = params[0];
get_face(facespec); // validate param get_face(facespec); // validate param
auto fill = [facespec](const Context& context, HighlightFlags flags, auto func = [=](const Context& context, HighlightFlags flags,
DisplayBuffer& display_buffer) DisplayBuffer& display_buffer)
{ {
auto range = display_buffer.range(); auto range = display_buffer.range();
highlight_range(display_buffer, range.first, range.second, true, highlight_range(display_buffer, range.first, range.second, true,
apply_face(get_face(facespec))); apply_face(get_face(facespec)));
}; };
return HighlighterAndId("fill_" + params[0], fill); return {"fill_" + facespec, make_simple_highlighter(std::move(func))};
} }
template<typename T> template<typename T>
@ -195,7 +194,7 @@ private:
ValueId m_id; ValueId m_id;
}; };
class RegexHighlighter class RegexHighlighter : public Highlighter
{ {
public: public:
RegexHighlighter(Regex regex, FacesSpec faces) RegexHighlighter(Regex regex, FacesSpec faces)
@ -203,7 +202,7 @@ public:
{ {
} }
void operator()(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer) void highlight(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer) override
{ {
if (flags != HighlightFlags::Highlight) if (flags != HighlightFlags::Highlight)
return; return;
@ -225,53 +224,15 @@ public:
} }
} }
private: void reset(Regex regex, FacesSpec faces)
struct Cache
{ {
std::pair<LineCount, LineCount> m_range; m_regex = std::move(regex);
size_t m_timestamp = 0; m_faces = std::move(faces);
std::vector<std::vector<std::pair<ByteCoord, ByteCoord>>> m_matches; m_force_update = true;
};
BufferSideCache<Cache> m_cache;
Regex m_regex;
FacesSpec m_faces;
Cache& update_cache_ifn(const Buffer& buffer, const BufferRange& range)
{
Cache& cache = m_cache.get(buffer);
LineCount first_line = range.first.line;
LineCount last_line = std::min(buffer.line_count()-1, range.second.line);
if (buffer.timestamp() == cache.m_timestamp and
first_line >= cache.m_range.first and
last_line <= cache.m_range.second)
return cache;
cache.m_range.first = std::max(0_line, first_line - 10);
cache.m_range.second = std::min(buffer.line_count()-1, last_line+10);
cache.m_timestamp = buffer.timestamp();
cache.m_matches.clear();
using RegexIt = RegexIterator<BufferIterator>;
RegexIt re_it{buffer.iterator_at(cache.m_range.first),
buffer.iterator_at(cache.m_range.second+1), m_regex};
RegexIt re_end;
for (; re_it != re_end; ++re_it)
{
cache.m_matches.emplace_back();
auto& match = cache.m_matches.back();
for (auto& sub : *re_it)
match.emplace_back(sub.first.coord(), sub.second.coord());
} }
return cache;
}
};
HighlighterAndId highlight_regex_factory(HighlighterParameters params) static HighlighterAndId create(HighlighterParameters params)
{ {
if (params.size() < 2) if (params.size() < 2)
throw runtime_error("wrong parameter count"); throw runtime_error("wrong parameter count");
@ -296,17 +257,67 @@ HighlighterAndId highlight_regex_factory(HighlighterParameters params)
Regex ex{params[0].begin(), params[0].end(), Regex::optimize}; Regex ex{params[0].begin(), params[0].end(), Regex::optimize};
return HighlighterAndId(id, RegexHighlighter(std::move(ex), return {id, make_unique<RegexHighlighter>(std::move(ex),
std::move(faces))); std::move(faces))};
} }
catch (RegexError& err) catch (RegexError& err)
{ {
throw runtime_error(String("regex error: ") + err.what()); throw runtime_error(String("regex error: ") + err.what());
} }
} }
private:
struct Cache
{
std::pair<LineCount, LineCount> m_range;
size_t m_timestamp = 0;
std::vector<std::vector<std::pair<ByteCoord, ByteCoord>>> m_matches;
};
BufferSideCache<Cache> m_cache;
Regex m_regex;
FacesSpec m_faces;
bool m_force_update = false;
Cache& update_cache_ifn(const Buffer& buffer, const BufferRange& range)
{
Cache& cache = m_cache.get(buffer);
LineCount first_line = range.first.line;
LineCount last_line = std::min(buffer.line_count()-1, range.second.line);
if (not m_force_update and
buffer.timestamp() == cache.m_timestamp and
first_line >= cache.m_range.first and
last_line <= cache.m_range.second)
return cache;
m_force_update = false;
cache.m_range.first = std::max(0_line, first_line - 10);
cache.m_range.second = std::min(buffer.line_count()-1, last_line+10);
cache.m_timestamp = buffer.timestamp();
cache.m_matches.clear();
using RegexIt = RegexIterator<BufferIterator>;
RegexIt re_it{buffer.iterator_at(cache.m_range.first),
buffer.iterator_at(cache.m_range.second+1), m_regex};
RegexIt re_end;
for (; re_it != re_end; ++re_it)
{
cache.m_matches.emplace_back();
auto& match = cache.m_matches.back();
for (auto& sub : *re_it)
match.emplace_back(sub.first.coord(), sub.second.coord());
}
return cache;
}
};
template<typename RegexGetter, typename FaceGetter> template<typename RegexGetter, typename FaceGetter>
class DynamicRegexHighlighter class DynamicRegexHighlighter : public Highlighter
{ {
public: public:
DynamicRegexHighlighter(RegexGetter regex_getter, FaceGetter face_getter) DynamicRegexHighlighter(RegexGetter regex_getter, FaceGetter face_getter)
@ -314,7 +325,7 @@ public:
m_face_getter(std::move(face_getter)), m_face_getter(std::move(face_getter)),
m_highlighter(Regex(), FacesSpec{}) {} m_highlighter(Regex(), FacesSpec{}) {}
void operator()(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer) void highlight(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer)
{ {
if (flags != HighlightFlags::Highlight) if (flags != HighlightFlags::Highlight)
return; return;
@ -326,10 +337,10 @@ public:
m_last_regex = regex; m_last_regex = regex;
m_last_face = face; m_last_face = face;
if (not m_last_regex.empty()) if (not m_last_regex.empty())
m_highlighter= RegexHighlighter{m_last_regex, face}; m_highlighter.reset(m_last_regex, m_last_face);
} }
if (not m_last_regex.empty() and not m_last_face.empty()) if (not m_last_regex.empty() and not m_last_face.empty())
m_highlighter(context, flags, display_buffer); m_highlighter.highlight(context, flags, display_buffer);
} }
private: private:
@ -343,15 +354,15 @@ private:
}; };
template<typename RegexGetter, typename FaceGetter> template<typename RegexGetter, typename FaceGetter>
DynamicRegexHighlighter<RegexGetter, FaceGetter> std::unique_ptr<DynamicRegexHighlighter<RegexGetter, FaceGetter>>
make_dynamic_regex_highlighter(RegexGetter regex_getter, FaceGetter face_getter) make_dynamic_regex_highlighter(RegexGetter regex_getter, FaceGetter face_getter)
{ {
return DynamicRegexHighlighter<RegexGetter, FaceGetter>( return make_unique<DynamicRegexHighlighter<RegexGetter, FaceGetter>>(
std::move(regex_getter), std::move(face_getter)); std::move(regex_getter), std::move(face_getter));
} }
HighlighterAndId highlight_search_factory(HighlighterParameters params) HighlighterAndId create_search_highlighter(HighlighterParameters params)
{ {
if (params.size() != 0) if (params.size() != 0)
throw runtime_error("wrong parameter count"); throw runtime_error("wrong parameter count");
@ -372,7 +383,7 @@ HighlighterAndId highlight_search_factory(HighlighterParameters params)
return {"hlsearch", make_dynamic_regex_highlighter(get_regex, get_face)}; return {"hlsearch", make_dynamic_regex_highlighter(get_regex, get_face)};
} }
HighlighterAndId highlight_regex_option_factory(HighlighterParameters params) HighlighterAndId create_regex_option_highlighter(HighlighterParameters params)
{ {
if (params.size() != 2) if (params.size() != 2)
throw runtime_error("wrong parameter count"); throw runtime_error("wrong parameter count");
@ -392,19 +403,18 @@ HighlighterAndId highlight_regex_option_factory(HighlighterParameters params)
return {"hloption_" + option_name, make_dynamic_regex_highlighter(get_regex, get_face)}; return {"hloption_" + option_name, make_dynamic_regex_highlighter(get_regex, get_face)};
} }
HighlighterAndId highlight_line_option_factory(HighlighterParameters params) HighlighterAndId create_line_option_highlighter(HighlighterParameters params)
{ {
if (params.size() != 2) if (params.size() != 2)
throw runtime_error("wrong parameter count"); throw runtime_error("wrong parameter count");
String facespec = params[1]; String facespec = params[1];
get_face(facespec); // validate facespec
String option_name = params[0]; String option_name = params[0];
// verify option type now
GlobalOptions::instance()[option_name].get<int>();
auto highlighter = [=](const Context& context, HighlightFlags flags, get_face(facespec); // validate facespec
GlobalOptions::instance()[option_name].get<int>(); // verify option type now
auto func = [=](const Context& context, HighlightFlags flags,
DisplayBuffer& display_buffer) DisplayBuffer& display_buffer)
{ {
int line = context.options()[option_name].get<int>(); int line = context.options()[option_name].get<int>();
@ -412,7 +422,7 @@ HighlighterAndId highlight_line_option_factory(HighlighterParameters params)
apply_face(get_face(facespec))); apply_face(get_face(facespec)));
}; };
return {"hlline_" + option_name, std::move(highlighter)}; return {"hlline_" + params[0], make_simple_highlighter(std::move(func))};
} }
void expand_tabulations(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer) void expand_tabulations(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer)
@ -626,7 +636,7 @@ void expand_unprintable(const Context& context, HighlightFlags flags, DisplayBuf
} }
} }
HighlighterAndId flag_lines_factory(HighlighterParameters params) HighlighterAndId create_flag_lines_highlighter(HighlighterParameters params)
{ {
if (params.size() != 2) if (params.size() != 2)
throw runtime_error("wrong parameter count"); throw runtime_error("wrong parameter count");
@ -637,8 +647,8 @@ HighlighterAndId flag_lines_factory(HighlighterParameters params)
// throw if wrong option type // throw if wrong option type
GlobalOptions::instance()[option_name].get<std::vector<LineAndFlag>>(); GlobalOptions::instance()[option_name].get<std::vector<LineAndFlag>>();
return {"hlflags_" + params[1], auto func = [=](const Context& context, HighlightFlags flags,
[=](const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer) DisplayBuffer& display_buffer)
{ {
auto& lines_opt = context.options()[option_name]; auto& lines_opt = context.options()[option_name];
auto& lines = lines_opt.get<std::vector<LineAndFlag>>(); auto& lines = lines_opt.get<std::vector<LineAndFlag>>();
@ -659,32 +669,20 @@ HighlighterAndId flag_lines_factory(HighlighterParameters params)
atom.face = { it != lines.end() ? std::get<1>(*it) : Colors::Default , bg }; atom.face = { it != lines.end() ? std::get<1>(*it) : Colors::Default , bg };
line.insert(line.begin(), std::move(atom)); line.insert(line.begin(), std::move(atom));
} }
}}; };
return {"hlflags_" + params[1], make_simple_highlighter(func) };
} }
template<void (*highlighter_func)(const Context&, HighlightFlags, DisplayBuffer&)> HighlighterAndId create_highlighter_group(HighlighterParameters params)
class SimpleHighlighterFactory
{
public:
SimpleHighlighterFactory(const String& id) : m_id(id) {}
HighlighterAndId operator()(HighlighterParameters params) const
{
return HighlighterAndId(m_id, HighlighterFunc(highlighter_func));
}
private:
String m_id;
};
HighlighterAndId highlighter_group_factory(HighlighterParameters params)
{ {
if (params.size() != 1) if (params.size() != 1)
throw runtime_error("wrong parameter count"); throw runtime_error("wrong parameter count");
return HighlighterAndId(params[0], HighlighterGroup()); return HighlighterAndId(params[0], make_unique<HighlighterGroup>());
} }
HighlighterAndId reference_factory(HighlighterParameters params) HighlighterAndId create_reference_highlighter(HighlighterParameters params)
{ {
if (params.size() != 1) if (params.size() != 1)
throw runtime_error("wrong parameter count"); throw runtime_error("wrong parameter count");
@ -694,17 +692,18 @@ HighlighterAndId reference_factory(HighlighterParameters params)
// throw if not found // throw if not found
//DefinedHighlighters::instance().get_group(name, '/'); //DefinedHighlighters::instance().get_group(name, '/');
return {name, auto func = [=](const Context& context, HighlightFlags flags,
[name](const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer) DisplayBuffer& display_buffer)
{ {
try try
{ {
DefinedHighlighters::instance().get_highlighter(name)(context, flags, display_buffer); DefinedHighlighters::instance().get_child(name).highlight(context, flags, display_buffer);
} }
catch (group_not_found&) catch (child_not_found&)
{ {}
} };
}};
return {name, make_simple_highlighter(func)};
} }
struct RegexMatch struct RegexMatch
@ -868,7 +867,7 @@ struct RegionDesc
} }
}; };
struct RegionsHighlighter struct RegionsHighlighter : public Highlighter
{ {
public: public:
using NamedRegionDescList = std::vector<std::pair<String, RegionDesc>>; using NamedRegionDescList = std::vector<std::pair<String, RegionDesc>>;
@ -880,12 +879,16 @@ public:
throw runtime_error("at least one region must be defined"); throw runtime_error("at least one region must be defined");
for (auto& region : m_regions) for (auto& region : m_regions)
{
m_groups.append({region.first, HighlighterGroup{}});
if (region.second.m_begin.empty() or region.second.m_end.empty()) if (region.second.m_begin.empty() or region.second.m_end.empty())
throw runtime_error("invalid regex for region highlighter"); throw runtime_error("invalid regex for region highlighter");
} }
if (not m_default_group.empty())
m_groups.append({m_default_group, HighlighterGroup{}});
}
void operator()(HierachicalHighlighter::GroupMap groups, const Context& context, void highlight(const Context& context, HighlightFlags flags, DisplayBuffer& display_buffer)
HighlightFlags flags, DisplayBuffer& display_buffer)
{ {
if (flags != HighlightFlags::Highlight) if (flags != HighlightFlags::Highlight)
return; return;
@ -904,8 +907,8 @@ public:
return c; return c;
}; };
auto default_group_it = groups.find(m_default_group); auto default_group_it = m_groups.find(m_default_group);
const bool apply_default = default_group_it != groups.end(); const bool apply_default = default_group_it != m_groups.end();
auto last_begin = range.first; auto last_begin = range.first;
for (; begin != end; ++begin) for (; begin != end; ++begin)
@ -915,8 +918,8 @@ public:
correct(last_begin), correct(begin->begin), correct(last_begin), correct(begin->begin),
default_group_it->second); default_group_it->second);
auto it = groups.find(begin->group); auto it = m_groups.find(begin->group);
if (it == groups.end()) if (it == m_groups.end())
continue; continue;
apply_highlighter(context, flags, display_buffer, apply_highlighter(context, flags, display_buffer,
correct(begin->begin), correct(begin->end), correct(begin->begin), correct(begin->end),
@ -929,9 +932,79 @@ public:
default_group_it->second); default_group_it->second);
} }
bool has_children() const override { return true; }
Highlighter& get_child(StringView path) override
{
auto sep_it = find(path, '/');
StringView id(path.begin(), sep_it);
auto it = m_groups.find(id);
if (it == m_groups.end())
throw child_not_found("no such id: "_str + id);
if (sep_it == path.end())
return it->second;
else
return it->second.get_child({sep_it+1, path.end()});
}
Completions complete_child(StringView path, ByteCount cursor_pos, bool group) const override
{
auto sep_it = find(path, '/');
if (sep_it != path.end())
{
ByteCount offset = sep_it+1 - path.begin();
Highlighter& hl = const_cast<RegionsHighlighter*>(this)->get_child({path.begin(), sep_it});
return offset_pos(hl.complete_child(path.substr(offset), cursor_pos - offset, group), offset);
}
return { 0, 0, m_groups.complete_id(path, cursor_pos) };
}
static HighlighterAndId create(HighlighterParameters params)
{
try
{
static const ParameterDesc param_desc{
SwitchMap{ { "default", { true, "" } } },
ParameterDesc::Flags::SwitchesOnlyAtStart,
5};
ParametersParser parser{params, param_desc};
if ((parser.positional_count() % 4) != 1)
throw runtime_error("wrong parameter count, expect <id> (<group name> <begin> <end> <recurse>)+");
RegionsHighlighter::NamedRegionDescList regions;
for (size_t i = 1; i < parser.positional_count(); i += 4)
{
if (parser[i].empty() or parser[i+1].empty() or parser[i+2].empty())
throw runtime_error("group id, begin and end must not be empty");
Regex begin{parser[i+1], Regex::nosubs | Regex::optimize };
Regex end{parser[i+2], Regex::nosubs | Regex::optimize };
Regex recurse;
if (not parser[i+3].empty())
recurse = Regex{parser[i+3], Regex::nosubs | Regex::optimize };
regions.push_back({ parser[i], {std::move(begin), std::move(end), std::move(recurse)} });
}
String default_group;
if (parser.has_option("default"))
default_group = parser.option_value("default");
return {parser[0], make_unique<RegionsHighlighter>(std::move(regions),
std::move(default_group))};
}
catch (RegexError& err)
{
throw runtime_error(String("regex error: ") + err.what());
}
}
private: private:
const NamedRegionDescList m_regions; const NamedRegionDescList m_regions;
const String m_default_group; const String m_default_group;
id_map<HighlighterGroup> m_groups;
struct Region struct Region
{ {
@ -1029,68 +1102,31 @@ private:
} }
}; };
HighlighterAndId regions_factory(HighlighterParameters params) template<typename Func>
HighlighterFactory simple_factory(const String id, Func func)
{ {
try return [=](HighlighterParameters params)
{ {
static const ParameterDesc param_desc{ return HighlighterAndId(id, make_simple_highlighter(func));
SwitchMap{ { "default", { true, "" } } }, };
ParameterDesc::Flags::SwitchesOnlyAtStart,
5};
ParametersParser parser{params, param_desc};
if ((parser.positional_count() % 4) != 1)
throw runtime_error("wrong parameter count, expect <id> (<group name> <begin> <end> <recurse>)+");
RegionsHighlighter::NamedRegionDescList regions;
id_map<HighlighterGroup> groups;
for (size_t i = 1; i < parser.positional_count(); i += 4)
{
if (parser[i].empty() or parser[i+1].empty() or parser[i+2].empty())
throw runtime_error("group id, begin and end must not be empty");
Regex begin{parser[i+1], Regex::nosubs | Regex::optimize };
Regex end{parser[i+2], Regex::nosubs | Regex::optimize };
Regex recurse;
if (not parser[i+3].empty())
recurse = Regex{parser[i+3], Regex::nosubs | Regex::optimize };
regions.push_back({ parser[i], {std::move(begin), std::move(end), std::move(recurse)} });
groups.append({ parser[i], HighlighterGroup{} });
}
String default_group;
if (parser.has_option("default"))
{
default_group = parser.option_value("default");
groups.append({ default_group, HighlighterGroup{} });
}
return {parser[0],
HierachicalHighlighter(
RegionsHighlighter(std::move(regions), std::move(default_group)), std::move(groups))};
}
catch (RegexError& err)
{
throw runtime_error(String("regex error: ") + err.what());
}
} }
void register_highlighters() void register_highlighters()
{ {
HighlighterRegistry& registry = HighlighterRegistry::instance(); HighlighterRegistry& registry = HighlighterRegistry::instance();
registry.register_func("number_lines", SimpleHighlighterFactory<show_line_numbers>("number_lines")); registry.register_func("number_lines", simple_factory("number_lines", show_line_numbers));
registry.register_func("show_matching", SimpleHighlighterFactory<show_matching_char>("show_matching")); registry.register_func("show_matching", simple_factory("show_matching", show_matching_char));
registry.register_func("show_whitespaces", SimpleHighlighterFactory<show_whitespaces>("show_whitespaces")); registry.register_func("show_whitespaces", simple_factory("show_whitespaces", show_whitespaces));
registry.register_func("fill", fill_factory); registry.register_func("fill", create_fill_highlighter);
registry.register_func("regex", highlight_regex_factory); registry.register_func("regex", RegexHighlighter::create);
registry.register_func("regex_option", highlight_regex_option_factory); registry.register_func("regex_option", create_regex_option_highlighter);
registry.register_func("search", highlight_search_factory); registry.register_func("search", create_search_highlighter);
registry.register_func("group", highlighter_group_factory); registry.register_func("group", create_highlighter_group);
registry.register_func("flag_lines", flag_lines_factory); registry.register_func("flag_lines", create_flag_lines_highlighter);
registry.register_func("line_option", highlight_line_option_factory); registry.register_func("line_option", create_line_option_highlighter);
registry.register_func("ref", reference_factory); registry.register_func("ref", create_reference_highlighter);
registry.register_func("regions", regions_factory); registry.register_func("regions", RegionsHighlighter::create);
} }
} }

View File

@ -11,6 +11,13 @@
namespace Kakoune namespace Kakoune
{ {
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
// *** Singleton *** // *** Singleton ***
// //
// Singleton helper class, every singleton type T should inherit // Singleton helper class, every singleton type T should inherit

View File

@ -28,9 +28,9 @@ Window::Window(Buffer& buffer)
m_hooks.run_hook("WinCreate", buffer.name(), hook_handler.context()); m_hooks.run_hook("WinCreate", buffer.name(), hook_handler.context());
m_options.register_watcher(*this); m_options.register_watcher(*this);
m_builtin_highlighters.append({"tabulations", expand_tabulations}); m_builtin_highlighters.add_child({"tabulations"_str, make_simple_highlighter(expand_tabulations)});
m_builtin_highlighters.append({"unprintable", expand_unprintable}); m_builtin_highlighters.add_child({"unprintable"_str, make_simple_highlighter(expand_unprintable)});
m_builtin_highlighters.append({"selections", highlight_selections}); m_builtin_highlighters.add_child({"selections"_str, make_simple_highlighter(highlight_selections)});
for (auto& option : m_options.flatten_options()) for (auto& option : m_options.flatten_options())
on_option_changed(*option); on_option_changed(*option);
@ -82,8 +82,8 @@ void Window::update_display_buffer(const Context& context)
} }
m_display_buffer.compute_range(); m_display_buffer.compute_range();
m_highlighters(context, HighlightFlags::Highlight, m_display_buffer); m_highlighters.highlight(context, HighlightFlags::Highlight, m_display_buffer);
m_builtin_highlighters(context, HighlightFlags::Highlight, m_display_buffer); m_builtin_highlighters.highlight(context, HighlightFlags::Highlight, m_display_buffer);
// cut the start of the line before m_position.column // cut the start of the line before m_position.column
for (auto& line : lines) for (auto& line : lines)
@ -182,8 +182,8 @@ void Window::scroll_to_keep_selection_visible_ifn(const Context& context)
lines.emplace_back(AtomList{ {buffer(), cursor.line, cursor.line+1} }); lines.emplace_back(AtomList{ {buffer(), cursor.line, cursor.line+1} });
display_buffer.compute_range(); display_buffer.compute_range();
m_highlighters(context, HighlightFlags::MoveOnly, display_buffer); m_highlighters.highlight(context, HighlightFlags::MoveOnly, display_buffer);
m_builtin_highlighters(context, HighlightFlags::MoveOnly, display_buffer); m_builtin_highlighters.highlight(context, HighlightFlags::MoveOnly, display_buffer);
// now we can compute where the cursor is in display columns // now we can compute where the cursor is in display columns
// (this is only valid if highlighting one line and multiple lines put // (this is only valid if highlighting one line and multiple lines put
@ -266,8 +266,8 @@ ByteCoordAndTarget Window::offset_coord(ByteCoordAndTarget coord, LineCount offs
InputHandler hook_handler{{ *m_buffer, Selection{} } }; InputHandler hook_handler{{ *m_buffer, Selection{} } };
hook_handler.context().set_window(*this); hook_handler.context().set_window(*this);
m_highlighters(hook_handler.context(), HighlightFlags::MoveOnly, display_buffer); m_highlighters.highlight(hook_handler.context(), HighlightFlags::MoveOnly, display_buffer);
m_builtin_highlighters(hook_handler.context(), HighlightFlags::MoveOnly, display_buffer); m_builtin_highlighters.highlight(hook_handler.context(), HighlightFlags::MoveOnly, display_buffer);
CharCount column = coord.target == -1 ? find_display_column(lines[0], buffer(), coord) : coord.target; CharCount column = coord.target == -1 ? find_display_column(lines[0], buffer(), coord) : coord.target;
return { find_buffer_coord(lines[1], buffer(), column), column }; return { find_buffer_coord(lines[1], buffer(), column), column };

View File

@ -36,7 +36,7 @@ public:
CharCoord display_position(ByteCoord coord); CharCoord display_position(ByteCoord coord);
HighlighterGroup& highlighters() { return m_highlighters; } Highlighter& highlighters() { return m_highlighters; }
OptionManager& options() { return m_options; } OptionManager& options() { return m_options; }
const OptionManager& options() const { return m_options; } const OptionManager& options() const { return m_options; }