aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2024-10-12 08:07:05 +0800
committerGitHub <noreply@github.com>2024-10-12 08:07:05 +0800
commit0e42c81c7fd429529d89458349c7cdde254d5406 (patch)
tree4504e1e7382f27d80aaf5914d61c3bdf729ee456
parentc49030b75ad8b8a9f8e7f023b0ee5f9c8c40afdd (diff)
downloadrneovim-0e42c81c7fd429529d89458349c7cdde254d5406.tar.gz
rneovim-0e42c81c7fd429529d89458349c7cdde254d5406.tar.bz2
rneovim-0e42c81c7fd429529d89458349c7cdde254d5406.zip
fix(lua): avoid recursive vim.on_key() callback (#30753)
-rw-r--r--runtime/doc/lua.txt2
-rw-r--r--runtime/doc/news.txt3
-rw-r--r--runtime/lua/vim/_editor.lua2
-rw-r--r--src/nvim/lua/executor.c9
-rw-r--r--test/functional/lua/vim_spec.lua39
5 files changed, 55 insertions, 0 deletions
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 3e8d8bb62c..4acf8dc0af 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -1678,6 +1678,8 @@ vim.on_key({fn}, {ns_id}) *vim.on_key()*
Note: ~
• {fn} will be removed on error.
+ • {fn} won't be invoked recursively, i.e. if {fn} itself consumes input,
+ it won't be invoked for those keys.
• {fn} will not be cleared by |nvim_buf_clear_namespace()|
Parameters: ~
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 1b311330b0..6af136bb5a 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -262,6 +262,9 @@ These existing features changed their behavior.
more emoji characters than before, including those encoded with multiple
emoji codepoints combined with ZWJ (zero width joiner) codepoints.
+• |vim.on_key()| callbacks won't be invoked recursively when a callback itself
+ consumes input.
+
==============================================================================
REMOVED FEATURES *news-removed*
diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua
index 2e829578a7..17a87dd9fd 100644
--- a/runtime/lua/vim/_editor.lua
+++ b/runtime/lua/vim/_editor.lua
@@ -658,6 +658,8 @@ local on_key_cbs = {} --- @type table<integer,function>
--- and cannot be toggled dynamically.
---
---@note {fn} will be removed on error.
+---@note {fn} won't be invoked recursively, i.e. if {fn} itself consumes input,
+--- it won't be invoked for those keys.
---@note {fn} will not be cleared by |nvim_buf_clear_namespace()|
---
---@param fn fun(key: string, typed: string)?
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index d4940f3add..9392765f40 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -2065,6 +2065,13 @@ char *nlua_register_table_as_callable(const typval_T *const arg)
void nlua_execute_on_key(int c, char *typed_buf)
{
+ static bool recursive = false;
+
+ if (recursive) {
+ return;
+ }
+ recursive = true;
+
char buf[MB_MAXBYTES * 3 + 4];
size_t buf_len = special_to_buf(c, mod_mask, false, buf);
vim_unescape_ks(typed_buf);
@@ -2103,6 +2110,8 @@ void nlua_execute_on_key(int c, char *typed_buf)
// [ ]
assert(top == lua_gettop(lstate));
#endif
+
+ recursive = false;
}
// Sets the editor "script context" during Lua execution. Used by :verbose.
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 3c65ec664e..793cf7cfd7 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -3223,6 +3223,45 @@ describe('lua stdlib', function()
feed('<C-C>')
eq('/', exec_lua([[return _G.ctrl_c_cmdtype]]))
end)
+
+ it('callback is not invoked recursively #30752', function()
+ local screen = Screen.new(60, 10)
+ screen:attach()
+ exec_lua([[
+ vim.on_key(function(key, typed)
+ vim.api.nvim_echo({
+ { 'key_cb\n' },
+ { ("KEYCB: key '%s', typed '%s'\n"):format(key, typed) },
+ }, false, {})
+ end)
+ ]])
+ feed('^')
+ screen:expect([[
+ |
+ {1:~ }|*5
+ {3: }|
+ key_cb |
+ KEYCB: key '^', typed '^' |
+ {6:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<C-C>')
+ screen:expect([[
+ |
+ {1:~ }|*3
+ {3: }|
+ key_cb |
+ KEYCB: key '^', typed '^' |
+ key_cb |
+ KEYCB: key '{18:^C}', typed '{18:^C}' |
+ {6:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<C-C>')
+ screen:expect([[
+ ^ |
+ {1:~ }|*8
+ |
+ ]])
+ end)
end)
describe('vim.wait', function()