fix(cmdline): prevent cmdline_show events after exiting cmdline #32033

Problem:  If a (vim.ui_attach) cmdline_hide callback triggers a redraw,
          it may cause cmdline_show events for an already exited cmdline.
Solution: Avoid emitting cmdline_show event when ccline.cmdbuff is
          already NULL. Unset ccline.cmdbuff before emitting cmdline_hide.
This commit is contained in:
luukvbaal 2025-01-15 15:55:21 +01:00 committed by GitHub
parent 5cc93ef472
commit bbf36ef8ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 62 additions and 9 deletions

View File

@ -951,6 +951,8 @@ theend:
kv_destroy(ccline.last_colors.colors);
char *p = ccline.cmdbuff;
// Prevent show events triggered by a (vim.ui_attach) hide callback.
ccline.cmdbuff = NULL;
if (ui_has(kUICmdline)) {
ui_call_cmdline_hide(ccline.level, s->gotesc);
@ -965,8 +967,6 @@ theend:
if (did_save_ccline) {
restore_cmdline(&save_ccline);
} else {
ccline.cmdbuff = NULL;
}
return (uint8_t *)p;
@ -3415,6 +3415,10 @@ static void draw_cmdline(int start, int len)
static void ui_ext_cmdline_show(CmdlineInfo *line)
{
if (line->cmdbuff == NULL) {
return;
}
Arena arena = ARENA_EMPTY;
Array content;
if (cmdline_star) {

View File

@ -168,18 +168,67 @@ describe('vim.ui_attach', function()
vim.ui_attach(ns, { ext_messages = true }, function(ev)
if ev == 'msg_show' then
vim.schedule(function() vim.cmd.redraw() end)
else
vim.cmd.redraw()
end
elseif ev:find('cmdline') then
_G.cmdline = _G.cmdline + (ev == 'cmdline_show' and 1 or 0)
vim.api.nvim_buf_set_lines(0, 0, -1, false, { tostring(_G.cmdline) })
vim.cmd('redraw')
end
end
)]])
screen:expect([[
^ |
{1:~ }|*4
]])
feed(':')
n.assert_alive()
eq(2, exec_lua('return _G.cmdline'))
n.assert_alive()
screen:expect({
grid = [[
^1 |
{1:~ }|*4
]],
cmdline = { {
content = { { '' } },
firstc = ':',
pos = 0,
} },
})
feed('version<CR><CR>v<Esc>')
n.assert_alive()
screen:expect({
grid = [[
^2 |
{1:~ }|*4
]],
cmdline = { { abort = false } },
})
feed([[:call confirm("Save changes?", "&Yes\n&No\n&Cancel")<CR>]])
screen:expect({
grid = [[
^5 |
{1:~ }|*4
]],
cmdline = {
{
content = { { '' } },
hl_id = 10,
pos = 0,
prompt = '[Y]es, (N)o, (C)ancel: ',
},
},
messages = {
{
content = { { '\nSave changes?\n', 6, 10 } },
history = false,
kind = 'confirm',
},
},
})
feed('n')
screen:expect({
grid = [[
^5 |
{1:~ }|*4
]],
cmdline = { { abort = false } },
})
end)
it("preserved 'incsearch/command' screen state after :redraw from ext_cmdline", function()