src: Allow hooks to be run only once

This commit implements the -once flag on the `:hook` command, which
automatically removes a hook after it was run, to avoid having to
declare a group and remove it in the hook implementation.

Closes #2277
This commit is contained in:
Frank LENORMAND 2018-08-02 12:52:48 +03:00
parent ae75032936
commit e84dcf72c0
3 changed files with 20 additions and 8 deletions

View File

@ -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);
}
};

View File

@ -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<const char*> 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<Hook>& h)
{ return h.get() == to_run.hook; });
m_hooks_trash.push_back(std::move(*it));
hook_list->value.erase(it);
}
}
catch (runtime_error& err)
{

View File

@ -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<HookFlags>) { 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();