map: fail if key is currently executing

If during execution of a mapping, that same mapping is replaced,
there is undefined behavior because we destroy a mapping that we are
still iterating over.

I have been using this mapping inside my kakrc to re-source the kakrc.

	map global user s %{:source "%val{config}/kakrc"<ret>} -docstring 'source "%val{config}/kakrc"'

Now <space>s happens to not trigger undefined behavior because the
mapping stays the same.

However it triggers an assertion added by Commit e49c0fb04 (unmap:
fail if the mapping is currently executing, 2023-05-14), specifically
the destructor of ScopedSetBool that guards mapping execution.

Fix these by banning map of a key that is executing, just like we
did for unmap.

Alternative solution: we could allow mapping (and even unmapping)
keys at any time and keep them alive by moving them into a trash can,
like we do for clients and others.
This commit is contained in:
Johannes Altmanninger 2023-06-24 19:49:02 +02:00
parent 661d1a0905
commit 42be0057a6
5 changed files with 11 additions and 0 deletions

View File

@ -13,6 +13,9 @@ namespace Kakoune
void KeymapManager::map_key(Key key, KeymapMode mode, void KeymapManager::map_key(Key key, KeymapMode mode,
KeyList mapping, String docstring) KeyList mapping, String docstring)
{ {
if (auto it = m_mapping.find(KeyAndMode{key, mode}); it != m_mapping.end())
if (it->value.is_executing)
throw runtime_error("cannot map key that is currently executing");
m_mapping[KeyAndMode{key, mode}] = {std::move(mapping), std::move(docstring)}; m_mapping[KeyAndMode{key, mode}] = {std::move(mapping), std::move(docstring)};
} }

View File

@ -0,0 +1,3 @@
!printf $kak_opt_source_count<ret>l
<space>s
!printf $kak_opt_source_count<ret>

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@
12

View File

@ -0,0 +1,3 @@
try %{ map global user s %exp{:source %%{%val{source}}<ret>} -docstring "re-source my kakrc" }
declare-option int source_count
set-option -add global source_count 1