fix(ui)!: decouple ext_messages from message grid #27963

Problem:  ext_messages is implemented to mimic the message grid
          implementation w.r.t. scrolling messages, clearing scrolled
          messages, hit-enter-prompts and replacing a previous message.
          Meanwhile, an ext_messages UI may not be implemented in a way
          where these events are wanted. Moreover, correctness of these
          events even assuming a "scrolled message" implementation
          depends on fragile "currently visible messages" global state,
          which already isn't correct after a previous message was
          supposed to have been overwritten (because that should not only
          happen when `msg_scroll == false`).

Solution: - No longer attempt to keep track of the currently visible
            messages: remove the `msg_ext(_history)_visible` variables.
            UIs may remove messages pre-emptively (timer based), or never
            show messages that don't fit a certain area in the first place.
          - No longer emit the `msg(_history)_clear` events to clear
            "scrolled" messages. This opens up the `msg_clear` event to
            be emitted when messages should actually be cleared (e.g.
            when the screen is cleared). May also be useful to emit before
            the first message in an event loop cycle as a hint to the UI
            that it is a new batch of messages (vim._extui currently
            schedules an event to determine that).
          - Set `replace_last` explicitly at the few callsites that want
            this to be set to true to replace an incomplete status message.
          - Don't store a "keep" message to be re-emitted.
This commit is contained in:
luukvbaal
2025-06-25 17:25:40 +02:00
committed by GitHub
parent 0694ca8822
commit 4369d7d9a7
14 changed files with 115 additions and 369 deletions

View File

@@ -203,6 +203,10 @@ describe('vim.ui_attach', function()
{1:~ }|*4
]],
cmdline = { { abort = false } },
condition = function()
eq('list_cmd', screen.messages[1].kind)
screen.messages = {} -- Ignore the build dependent :version content
end,
})
feed([[:call confirm("Save changes?", "&Yes\n&No\n&Cancel")<CR>]])
screen:expect({
@@ -440,104 +444,57 @@ describe('vim.ui_attach', function()
end)
it('detaches after excessive errors', function()
local screen = Screen.new(86, 10)
local screen = Screen.new(66, 10)
screen:add_extra_attr_ids({ [100] = { bold = true, foreground = Screen.colors.SeaGreen } })
exec_lua([[
vim.ui_attach(vim.api.nvim_create_namespace(''), { ext_messages = true }, function(ev)
if ev:find('msg') then
if ev == 'msg_show' then
vim.api.nvim_buf_set_lines(0, -2, -1, false, { err[1] })
end
end)
]])
local s1 = [[
^ |
{1:~ }|*9
]]
screen:expect(s1)
feed('Q<CR>')
feed('Q')
screen:expect({
grid = s1,
messages = {
{
content = { { "E354: Invalid register name: '^@'", 9, 6 } },
history = true,
kind = 'emsg',
},
{
content = {
{
'Lua callback:\n[string "<nvim>"]:3: attempt to index global \'err\' (a nil value)\nstack traceback:\n\t[string "<nvim>"]:3: in function <[string "<nvim>"]:1>',
9,
6,
},
},
history = true,
kind = 'lua_error',
},
{
content = { { 'Press ENTER or type command to continue', 100, 18 } },
kind = 'return_prompt',
},
},
grid = [[
|
{1:~ }|*5
{3: }|
{9:Error in "msg_show" UI event handler (ns=(UNKNOWN PLUGIN)):} |
{9:fast context failure} |
{100:Press ENTER or type command to continue}^ |
]],
condition = function()
screen.messages = {}
end,
})
feed('<CR>:messages<CR>')
feed('<Esc>')
screen:expect([[
{9:Error in "msg_show" UI event handler (ns=(UNKNOWN PLUGIN)):} |
{9:Lua: [string "<nvim>"]:3: attempt to index global 'err' (a nil value)} |
{9:stack traceback:} |
{9: [string "<nvim>"]:3: in function <[string "<nvim>"]:1>} |
{9:Error in "msg_clear" UI event handler (ns=(UNKNOWN PLUGIN)):} |
{9:Lua: [string "<nvim>"]:3: attempt to index global 'err' (a nil value)} |
{9:stack traceback:} |
{9: [string "<nvim>"]:3: in function <[string "<nvim>"]:1>} |
{9:Excessive errors in vim.ui_attach() callback (ns=(UNKNOWN PLUGIN))} |
{100:Press ENTER or type command to continue}^ |
^ |
{1:~ }|*8
|
]])
feed('<CR>')
-- Also when scheduled
exec_lua([[
vim.ui_attach(vim.api.nvim_create_namespace(''), { ext_messages = true }, function()
vim.schedule(function() vim.api.nvim_buf_set_lines(0, -2, -1, false, { err[1] }) end)
vim.ui_attach(vim.api.nvim_create_namespace(''), { ext_messages = true }, function(ev)
if ev == 'msg_show' then
vim.schedule(function() vim.api.nvim_buf_set_lines(0, -2, -1, false, { err[1] }) end)
end
end)
]])
feed('Q')
screen:expect({
grid = s1,
messages = {
{
content = {
{
'vim.schedule callback: [string "<nvim>"]:2: attempt to index global \'err\' (a nil value)\nstack traceback:\n\t[string "<nvim>"]:2: in function <[string "<nvim>"]:2>',
9,
6,
},
},
history = true,
kind = 'lua_error',
},
{
content = {
{
'vim.schedule callback: [string "<nvim>"]:2: attempt to index global \'err\' (a nil value)\nstack traceback:\n\t[string "<nvim>"]:2: in function <[string "<nvim>"]:2>',
9,
6,
},
},
history = true,
kind = 'lua_error',
},
{
content = { { 'Press ENTER or type command to continue', 100, 18 } },
kind = 'return_prompt',
},
},
grid = [[
|
{1:~ }|*6
{3: }|
{9:Excessive errors in vim.ui_attach() callback (ns=(UNKNOWN PLUGIN))}|
{100:Press ENTER or type command to continue}^ |
]],
condition = function()
screen.messages = {}
end,
})
feed('<Esc>:1messages clear<cr>:messages<CR>')
screen:expect([[
^ |
{1:~ }|*8
{9:Excessive errors in vim.ui_attach() callback (ns=(UNKNOWN PLUGIN))} |
]])
end)
it('sourcing invalid file does not crash #32166', function()