commit 7ba0f623d75a3ad0c00c376b20a5f5e6acebebec
parent de7306a250e15785a9d40654f980dd74a4c33e3b
Author: Luuk van Baal <luukvbaal@gmail.com>
Date: Sun, 20 Apr 2025 01:09:24 +0200
feat(ui): avoid setting 'cmdheight' with vim.ui_attach()
Problem: We allow setting 'cmdheight' to 0 with ext_messages enabled
since b72931e7. Enabling ext_messages with vim.ui_attach()
implicitly sets 'cmdheight' to 0 for BWC. When non-zero
'cmdheight' is wanted, this behavior make it unnecessarily
hard to keep track of the user configured value.
Solution: Add set_cmdheight to vim.ui_attach() opts table that can be
set to false to avoid setting 'cmdheight' to 0.
Diffstat:
6 files changed, 44 insertions(+), 27 deletions(-)
diff --git 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<string, any>`)
- • {callback} (`fun()`)
+ • {ns} (`integer`) Namespace ID
+ • {opts} (`table<string, any>`) 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
@@ -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<string, any>
---- @param callback fun()
-function vim.ui_attach(ns, options, callback) end
+--- @param ns integer Namespace ID
+--- @param opts table<string, any> 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
@@ -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
@@ -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
@@ -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
@@ -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