From 1d57374c41e6c46c41d537c342ce30ad96cdb85b Mon Sep 17 00:00:00 2001 From: luukvbaal Date: Tue, 27 Jan 2026 15:30:37 +0100 Subject: [PATCH] fix(ui): textlock still causes issues for UI callbacks #37513 Problem: There are still ways to run into textlock errors with vim.ui_attach callbacks trying to display a UI event. Solution: Disregard textlock again during vim.ui_attach() callbacks (also when scheduled). Partially revert 3277dc3b; avoiding to flush while textlock is set is still helpful. --- src/nvim/lua/executor.c | 8 +++++ src/nvim/ui.c | 11 ++++-- test/functional/ui/messages2_spec.lua | 52 ++++++++++++++++++++++----- 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index e047c051f7..78d4515057 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -382,10 +382,18 @@ static void nlua_schedule_event(void **argv) lua_State *const lstate = global_lstate; nlua_pushref(lstate, cb); nlua_unref_global(lstate, cb); + + // Don't impose textlock restrictions upon UI event handlers. + int save_expr_map_lock = expr_map_lock; + int save_textlock = textlock; + expr_map_lock = ns_id > 0 ? 0 : expr_map_lock; + textlock = ns_id > 0 ? 0 : textlock; if (nlua_pcall(lstate, 0, 0)) { nlua_error(lstate, _("vim.schedule callback: %.*s")); ui_remove_cb(ns_id, true); } + expr_map_lock = save_expr_map_lock; + textlock = save_textlock; } /// Schedule Lua callback on main loop's event queue diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 3db55b1b74..cc4e6d27eb 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -550,7 +550,7 @@ void ui_flush(void) win_ui_flush(false); // Avoid flushing callbacks expected to change text during textlock. - if (textlock == 0) { + if (textlock == 0 && expr_map_lock == 0) { cmdline_ui_flush(); msg_ext_ui_flush(); } @@ -761,9 +761,14 @@ void ui_call_event(char *name, Array args) fast = !strequal(not_fast[i], args.items[0].data.string.data); } + // Don't impose textlock restrictions upon UI event handlers. + int save_expr_map_lock = expr_map_lock; + int save_textlock = textlock; + expr_map_lock = 0; + textlock = 0; + bool handled = false; UIEventCallback *event_cb; - map_foreach(&ui_event_cbs, ui_event_ns_id, event_cb, { Error err = ERROR_INIT; uint32_t ns_id = ui_event_ns_id; @@ -778,6 +783,8 @@ void ui_call_event(char *name, Array args) } api_clear_error(&err); }) + expr_map_lock = save_expr_map_lock; + textlock = save_textlock; if (!handled) { UI_CALL(true, event, ui, name, args); diff --git a/test/functional/ui/messages2_spec.lua b/test/functional/ui/messages2_spec.lua index 9e1ae41d07..43c67e8fef 100644 --- a/test/functional/ui/messages2_spec.lua +++ b/test/functional/ui/messages2_spec.lua @@ -429,8 +429,7 @@ describe('messages2', function() exec_lua(function() vim.schedule(function() print('foo') - vim.uv.sleep(100) - print('bar') + vim.fn.getchar() end) end) screen:expect([[ @@ -438,13 +437,6 @@ describe('messages2', function() {1:~ }|*12 foo | ]]) - screen:expect([[ - ^ | - {1:~ }|*10 - {3: }| - foo | - bar | - ]]) end) it('properly formatted carriage return messages', function() @@ -477,4 +469,46 @@ describe('messages2', function() {15:bar}{9:baz} | ]]) end) + + it('can show message during textlock', function() + exec_lua(function() + _G.omnifunc = function() + print('x!') + vim.cmd.sleep('100m') + end + vim.bo.omnifunc = 'v:lua.omnifunc' + end) + feed('i') + screen:expect([[ + ^ | + {1:~ }|*12 + {5:-- ^X mode (^]^D^E^F^I^K^L^N^O^P^Rs^U^V^Y)} | + ]]) + feed('') + screen:expect([[ + ^ | + {1:~ }|*12 + x! | + ]]) + screen:expect([[ + ^ | + {1:~ }|*12 + {5:-- Omni completion (^O^N^P) }{9:Pattern not found} | + ]]) + exec_lua(function() + vim.keymap.set('n', '', function() + print('i hate locks so much!!!!') + vim.cmd.messages() + end, { expr = true }) + end) + feed('') + screen:expect([[ + | + {1:~ }|*8 + {3: }| + x^! | + x! | + i hate locks so much!!!! |*2 + ]]) + end) end)