#ifndef function_group_hh_INCLUDED #define function_group_hh_INCLUDED #include "exception.hh" #include "id_map.hh" #include "string.hh" namespace Kakoune { struct group_not_found : public runtime_error { using runtime_error::runtime_error; }; template class FunctionGroup { public: using Function = std::function; using FunctionAndId = std::pair>; void operator()(Args... args) { for (auto& func : m_functions) func.second(std::forward(args)...); } void append(FunctionAndId&& function) { if (m_functions.contains(function.first)) throw runtime_error("duplicate id: " + function.first); m_functions.append(std::forward(function)); } void remove(StringView id) { m_functions.remove(id); } FunctionGroup& get_group(StringView path, Codepoint path_separator = 0) { auto sep_it = std::find(path.begin(), path.end(), path_separator); StringView id(path.begin(), sep_it); auto it = m_functions.find(id); if (it == m_functions.end()) throw group_not_found("no such id: "_str + id); FunctionGroup* group = it->second.template target(); if (not group) throw group_not_found("not a group: "_str + id); if (sep_it != path.end()) return group->get_group(StringView(sep_it+1, path.end()), path_separator); else return *group; } CandidateList complete_id(StringView prefix, ByteCount cursor_pos) const { return m_functions.complete_id(prefix, cursor_pos); } CandidateList complete_group_id(StringView prefix, ByteCount cursor_pos) const { return m_functions.complete_id_if( prefix, cursor_pos, [](const FunctionAndId& func) { return func.second.template target() != nullptr; }); } private: id_map m_functions; }; } #endif // function_group_hh_INCLUDED