From 0e42c81c7fd429529d89458349c7cdde254d5406 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 12 Oct 2024 08:07:05 +0800 Subject: fix(lua): avoid recursive vim.on_key() callback (#30753) --- runtime/doc/lua.txt | 2 ++ runtime/doc/news.txt | 3 +++ runtime/lua/vim/_editor.lua | 2 ++ src/nvim/lua/executor.c | 9 +++++++++ test/functional/lua/vim_spec.lua | 39 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 55 insertions(+) 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 --- 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('') 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('') + 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('') + screen:expect([[ + ^ | + {1:~ }|*8 + | + ]]) + end) end) describe('vim.wait', function() -- cgit