diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index c580c55a5e..2dcf915428 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1031,16 +1031,13 @@ vim.stricmp({a}, {b}) *vim.stricmp()* (`0|1|-1`) if strings are equal, {a} is greater than {b} or {a} is lesser than {b}, respectively. -vim.ui_attach({ns}, {options}, {callback}) *vim.ui_attach()* +vim.ui_attach({ns}, {opts}, {callback}) *vim.ui_attach()* WARNING: This feature is experimental/unstable. Subscribe to |ui-events|, similar to |nvim_ui_attach()| but receive events in a Lua callback. Used to implement screen elements like popupmenu or message handling in Lua. - {options} is a dict with one or more `ext_…` |ui-option|s set to true to - enable events for the respective UI element. - {callback} receives event name plus additional parameters. See |ui-popupmenu| and the sections below for event format for respective events. @@ -1073,16 +1070,21 @@ vim.ui_attach({ns}, {options}, {callback}) *vim.ui_attach()* < Parameters: ~ - • {ns} (`integer`) - • {options} (`table`) - • {callback} (`fun()`) + • {ns} (`integer`) Namespace ID + • {opts} (`table`) Optional parameters. + • {ext_…}? (`boolean`) Any of |ui-ext-options|, if true + enable events for the respective UI element. + • {set_cmdheight}? (`boolean`) If false, avoid setting + 'cmdheight' to 0 when `ext_messages` is enabled. + • {callback} (`fun(event: string, ...)`) Function called for each UI + event vim.ui_detach({ns}) *vim.ui_detach()* Detach a callback previously attached with |vim.ui_attach()| for the given namespace {ns}. Parameters: ~ - • {ns} (`integer`) + • {ns} (`integer`) Namespace ID vim.wait({time}, {callback}, {interval}, {fast_only}) *vim.wait()* Wait for {time} in milliseconds until {callback} returns `true`. diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua index 2d5975b977..ef7ec0bee3 100644 --- a/runtime/lua/vim/_meta/builtin.lua +++ b/runtime/lua/vim/_meta/builtin.lua @@ -226,9 +226,6 @@ function vim.wait(time, callback, interval, fast_only) end --- Subscribe to |ui-events|, similar to |nvim_ui_attach()| but receive events in a Lua callback. --- Used to implement screen elements like popupmenu or message handling in Lua. --- ---- {options} is a dict with one or more `ext_…` |ui-option|s set to true to enable events for ---- the respective UI element. ---- --- {callback} receives event name plus additional parameters. See |ui-popupmenu| --- and the sections below for event format for respective events. --- @@ -263,12 +260,16 @@ function vim.wait(time, callback, interval, fast_only) end --- --- @since 0 --- ---- @param ns integer ---- @param options table ---- @param callback fun() -function vim.ui_attach(ns, options, callback) end +--- @param ns integer Namespace ID +--- @param opts table Optional parameters. +--- - {ext_…}? (`boolean`) Any of |ui-ext-options|, if true +--- enable events for the respective UI element. +--- - {set_cmdheight}? (`boolean`) If false, avoid setting +--- 'cmdheight' to 0 when `ext_messages` is enabled. +--- @param callback fun(event: string, ...) Function called for each UI event +function vim.ui_attach(ns, opts, callback) end --- Detach a callback previously attached with |vim.ui_attach()| for the --- given namespace {ns}. ---- @param ns integer +--- @param ns integer Namespace ID function vim.ui_detach(ns) end diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 5dac24fbc0..8018d15bc8 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -682,7 +682,7 @@ static int nlua_ui_attach(lua_State *lstate) return luaL_error(lstate, "invalid ns_id"); } if (!lua_istable(lstate, 2)) { - return luaL_error(lstate, "ext_widgets must be a table"); + return luaL_error(lstate, "opts must be a table"); } if (!lua_isfunction(lstate, 3)) { return luaL_error(lstate, "callback must be a Lua function"); @@ -699,13 +699,18 @@ static int nlua_ui_attach(lua_State *lstate) const char *s = lua_tolstring(lstate, -2, &len); bool val = lua_toboolean(lstate, -1); - for (size_t i = 0; i < kUIGlobalCount; i++) { - if (strequal(s, ui_ext_names[i])) { - if (val) { - tbl_has_true_val = true; + if (strequal(s, "set_cmdheight")) { + ui_refresh_cmdheight = val; + goto ok; + } else { + for (size_t i = 0; i < kUIGlobalCount; i++) { + if (strequal(s, ui_ext_names[i])) { + if (val) { + tbl_has_true_val = true; + } + ext_widgets[i] = val; + goto ok; } - ext_widgets[i] = val; - goto ok; } } @@ -715,11 +720,12 @@ ok: } if (!tbl_has_true_val) { - return luaL_error(lstate, "ext_widgets table must contain at least one 'true' value"); + return luaL_error(lstate, "opts table must contain at least one 'true' ext_widget"); } LuaRef ui_event_cb = nlua_ref_global(lstate, 3); ui_add_cb(ns_id, ui_event_cb, ext_widgets); + ui_refresh_cmdheight = true; return 0; } diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 9a36588b56..c2983430a0 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -223,9 +223,11 @@ void ui_refresh(void) // Reset 'cmdheight' for all tabpages when ext_messages toggles. if (had_message != ui_ext[kUIMessages]) { - set_option_value(kOptCmdheight, NUMBER_OPTVAL(had_message), 0); - FOR_ALL_TABS(tp) { - tp->tp_ch_used = had_message; + if (ui_refresh_cmdheight) { + set_option_value(kOptCmdheight, NUMBER_OPTVAL(had_message), 0); + FOR_ALL_TABS(tp) { + tp->tp_ch_used = had_message; + } } msg_scroll_flush(); } diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 4aeb8ffda9..c45b367c4d 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -21,3 +21,4 @@ EXTERN Array noargs INIT(= ARRAY_DICT_INIT); // vim.ui_attach() namespace of currently executed callback. EXTERN uint32_t ui_event_ns_id INIT( = 0); EXTERN MultiQueue *resize_events INIT( = NULL); +EXTERN bool ui_refresh_cmdheight INIT( = true); diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua index 096280f563..853bee61c2 100644 --- a/test/functional/lua/ui_event_spec.lua +++ b/test/functional/lua/ui_event_spec.lua @@ -162,6 +162,11 @@ describe('vim.ui_attach', function() eq(0, n.api.nvim_get_option_value('cmdheight', {})) end) + it("can attach ext_messages without changing 'cmdheight'", function() + exec_lua('vim.ui_attach(ns, { ext_messages = true, set_cmdheight = false }, on_event)') + eq(1, n.api.nvim_get_option_value('cmdheight', {})) + end) + it('avoids recursive flushing and invalid memory access with :redraw', function() exec_lua([[ _G.cmdline = 0