diff --git a/src/commands.cc b/src/commands.cc index dcee0281..f8a3e4dc 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -842,7 +842,8 @@ const CommandDesc add_hook_cmd = { " * window: hook is executed only for the current window\n", ParameterDesc{ { { "group", { true, "set hook group, see remove-hooks" } }, - { "always", { false, "run hook even if hooks are disabled" } }}, + { "always", { false, "run hook even if hooks are disabled" } }, + { "once", { false, "run the hook only once" } } }, ParameterDesc::Flags::None, 4, 4 }, CommandFlags::None, @@ -860,9 +861,9 @@ const CommandDesc add_hook_cmd = { Regex regex{parser[2], RegexCompileFlags::Optimize}; const String& command = parser[3]; auto group = parser.get_switch("group").value_or(StringView{}); - get_scope(parser[0], context).hooks().add_hook(parser[1], group.str(), - parser.get_switch("always") ? - HookFlags::Always : HookFlags::None, + const auto flags = (parser.get_switch("always") ? HookFlags::Always : HookFlags::None) \ + | (parser.get_switch("once") ? HookFlags::Once : HookFlags::None); + get_scope(parser[0], context).hooks().add_hook(parser[1], group.str(), flags, std::move(regex), command); } }; diff --git a/src/hook_manager.cc b/src/hook_manager.cc index 378756c0..8dabb66a 100644 --- a/src/hook_manager.cc +++ b/src/hook_manager.cc @@ -63,7 +63,7 @@ CandidateList HookManager::complete_hook_group(StringView prefix, ByteCount pos_ return res; } -void HookManager::run_hook(StringView hook_name, StringView param, Context& context) const +void HookManager::run_hook(StringView hook_name, StringView param, Context& context) { const bool only_always = context.hooks_disabled(); @@ -99,7 +99,7 @@ void HookManager::run_hook(StringView hook_name, StringView param, Context& cont for (auto& hook : hook_list->value) { MatchResults captures; - if ((not only_always or (hook->flags & HookFlags::Always)) and + if ((not only_always or (hook->flags & HookFlags::Always)) and (hook->group.empty() or disabled_hooks.empty() or not regex_match(hook->group.begin(), hook->group.end(), disabled_hooks)) and regex_match(param.begin(), param.end(), captures, hook->filter)) @@ -123,6 +123,16 @@ void HookManager::run_hook(StringView hook_name, StringView param, Context& cont CommandManager::instance().execute(to_run.hook->commands, context, { {}, std::move(env_vars) }); + + if (to_run.hook->flags & HookFlags::Once) + { + auto it = std::find_if(hook_list->value.begin(), hook_list->value.end(), + [&](const std::unique_ptr& h) + { return h.get() == to_run.hook; }); + + m_hooks_trash.push_back(std::move(*it)); + hook_list->value.erase(it); + } } catch (runtime_error& err) { diff --git a/src/hook_manager.hh b/src/hook_manager.hh index 44fb9107..9e75c086 100644 --- a/src/hook_manager.hh +++ b/src/hook_manager.hh @@ -14,7 +14,8 @@ class Regex; enum class HookFlags { None = 0, - Always = 1 << 0 + Always = 1 << 0, + Once = 1 << 1 }; constexpr bool with_bit_ops(Meta::Type) { return true; } @@ -29,7 +30,7 @@ public: void remove_hooks(StringView group); CandidateList complete_hook_group(StringView prefix, ByteCount pos_in_token); void run_hook(StringView hook_name, StringView param, - Context& context) const; + Context& context); private: HookManager();