mirror of
https://github.com/neovim/neovim.git
synced 2026-04-01 05:12:02 +00:00
fix(terminal): wrong scrollback with BufFile* autocommand (#37601)
Problem: Wrong terminal scrollback if BufFile* autocommand drains PTY
output but doesn't process the pending refresh.
Solution: Refresh scrollback before refreshing screen in terminal_open()
if scrollback has been allocated.
This commit is contained in:
@@ -563,6 +563,11 @@ void terminal_open(Terminal **termpp, buf_T *buf)
|
|||||||
aco_save_T aco;
|
aco_save_T aco;
|
||||||
aucmd_prepbuf(&aco, buf);
|
aucmd_prepbuf(&aco, buf);
|
||||||
|
|
||||||
|
if (term->sb_buffer != NULL) {
|
||||||
|
// If scrollback has been allocated by autocommands between terminal_alloc()
|
||||||
|
// and terminal_open(), it also needs to be refreshed.
|
||||||
|
refresh_scrollback(term, buf);
|
||||||
|
}
|
||||||
refresh_screen(term, buf);
|
refresh_screen(term, buf);
|
||||||
set_option_value(kOptBuftype, STATIC_CSTR_AS_OPTVAL("terminal"), OPT_LOCAL);
|
set_option_value(kOptBuftype, STATIC_CSTR_AS_OPTVAL("terminal"), OPT_LOCAL);
|
||||||
|
|
||||||
|
|||||||
@@ -773,40 +773,56 @@ describe(':terminal buffer', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
--- @param subcmd 'REP'|'REPFAST'
|
--- @param subcmd 'REP'|'REPFAST'
|
||||||
local function check_term_rep_20000(subcmd)
|
local function check_term_rep(subcmd, count)
|
||||||
local screen = Screen.new(50, 7)
|
local screen = Screen.new(50, 7)
|
||||||
api.nvim_set_option_value('scrollback', 30000, {})
|
|
||||||
api.nvim_create_autocmd('TermClose', { command = 'let g:did_termclose = 1' })
|
api.nvim_create_autocmd('TermClose', { command = 'let g:did_termclose = 1' })
|
||||||
fn.jobstart({ testprg('shell-test'), subcmd, '20000', 'TEST' }, { term = true })
|
fn.jobstart({ testprg('shell-test'), subcmd, count, 'TEST' }, { term = true })
|
||||||
retry(nil, nil, function()
|
retry(nil, nil, function()
|
||||||
eq(1, api.nvim_get_var('did_termclose'))
|
eq(1, api.nvim_get_var('did_termclose'))
|
||||||
end)
|
end)
|
||||||
feed('i')
|
feed('i')
|
||||||
screen:expect([[
|
screen:expect(([[
|
||||||
19996: TEST |
|
%d: TEST{MATCH: +}|
|
||||||
19997: TEST |
|
%d: TEST{MATCH: +}|
|
||||||
19998: TEST |
|
%d: TEST{MATCH: +}|
|
||||||
19999: TEST |
|
%d: TEST{MATCH: +}|
|
||||||
|
|
|
|
||||||
[Process exited 0]^ |
|
[Process exited 0]^ |
|
||||||
{5:-- TERMINAL --} |
|
{5:-- TERMINAL --} |
|
||||||
]])
|
]]):format(count - 4, count - 3, count - 2, count - 1))
|
||||||
local lines = api.nvim_buf_get_lines(0, 0, -1, true)
|
local lines = api.nvim_buf_get_lines(0, 0, -1, true)
|
||||||
for i = 0, 19999 do
|
for i = 1, count do
|
||||||
eq(('%d: TEST'):format(i), lines[i + 1])
|
eq(('%d: TEST'):format(i - 1), lines[i])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it('does not drop data when job exits immediately after output #3030', function()
|
it('does not drop data when job exits immediately after output #3030', function()
|
||||||
check_term_rep_20000('REPFAST')
|
api.nvim_set_option_value('scrollback', 30000, {})
|
||||||
|
check_term_rep('REPFAST', 20000)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('does not drop data when autocommands poll for events #37559', function()
|
it('does not drop data when autocommands poll for events #37559', function()
|
||||||
|
api.nvim_set_option_value('scrollback', 30000, {})
|
||||||
api.nvim_create_autocmd('BufFilePre', { command = 'sleep 50m', nested = true })
|
api.nvim_create_autocmd('BufFilePre', { command = 'sleep 50m', nested = true })
|
||||||
api.nvim_create_autocmd('BufFilePost', { command = 'sleep 50m', nested = true })
|
api.nvim_create_autocmd('BufFilePost', { command = 'sleep 50m', nested = true })
|
||||||
api.nvim_create_autocmd('TermOpen', { command = 'sleep 50m', nested = true })
|
api.nvim_create_autocmd('TermOpen', { command = 'sleep 50m', nested = true })
|
||||||
-- REP pauses 1 ms every 100 lines, so each autocommand processes some output.
|
-- REP pauses 1 ms every 100 lines, so each autocommand processes some output.
|
||||||
check_term_rep_20000('REP')
|
check_term_rep('REP', 20000)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('scrollback is correct if all output is drained by', function()
|
||||||
|
for _, event in ipairs({ 'BufFilePre', 'BufFilePost', 'TermOpen' }) do
|
||||||
|
describe(('%s autocommand that lasts for'):format(event), function()
|
||||||
|
for _, delay in ipairs({ 5, 15, 25 }) do
|
||||||
|
-- Terminal refresh delay is 10 ms.
|
||||||
|
it(('%.1f * terminal refresh delay'):format(delay / 10), function()
|
||||||
|
local cmd = ('sleep %dm'):format(delay)
|
||||||
|
api.nvim_create_autocmd(event, { command = cmd, nested = true })
|
||||||
|
check_term_rep('REPFAST', 200)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('handles unprintable chars', function()
|
it('handles unprintable chars', function()
|
||||||
|
|||||||
@@ -160,14 +160,21 @@ local function test_autocmd_no_crash(event, extra_tests)
|
|||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
local input_prompt_screen = [[
|
||||||
|
|
|
||||||
|
{1:~ }|*2
|
||||||
|
^ |
|
||||||
|
]]
|
||||||
|
local oldbuf_screen = [[
|
||||||
|
^OLDBUF |
|
||||||
|
{1:~ }|*2
|
||||||
|
|
|
||||||
|
]]
|
||||||
|
|
||||||
it('processes job exit event when using jobstart(…,{term=true})', function()
|
it('processes job exit event when using jobstart(…,{term=true})', function()
|
||||||
api.nvim_create_autocmd(event, { command = "call input('')" })
|
api.nvim_create_autocmd(event, { command = "call input('')" })
|
||||||
async_meths.nvim_call_function('jobstart', { term_args, { term = true } })
|
async_meths.nvim_call_function('jobstart', { term_args, { term = true } })
|
||||||
env.screen:expect([[
|
env.screen:expect(input_prompt_screen)
|
||||||
|
|
|
||||||
{1:~ }|*2
|
|
||||||
^ |
|
|
||||||
]])
|
|
||||||
vim.uv.sleep(20)
|
vim.uv.sleep(20)
|
||||||
feed('<CR>')
|
feed('<CR>')
|
||||||
env.screen:expect([[
|
env.screen:expect([[
|
||||||
@@ -184,29 +191,40 @@ local function test_autocmd_no_crash(event, extra_tests)
|
|||||||
{5:-- TERMINAL --} |
|
{5:-- TERMINAL --} |
|
||||||
]])
|
]])
|
||||||
feed('<CR>')
|
feed('<CR>')
|
||||||
env.screen:expect([[
|
env.screen:expect(oldbuf_screen)
|
||||||
^OLDBUF |
|
|
||||||
{1:~ }|*2
|
|
||||||
|
|
|
||||||
]])
|
|
||||||
assert_alive()
|
assert_alive()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('wipes buffer and processes events when using jobstart(…,{term=true})', function()
|
it('wipes buffer and processes events when using jobstart(…,{term=true})', function()
|
||||||
api.nvim_create_autocmd(event, { command = "call Wipe() | call input('')" })
|
api.nvim_create_autocmd(event, { command = "call Wipe() | call input('')" })
|
||||||
async_meths.nvim_call_function('jobstart', { term_args, { term = true } })
|
async_meths.nvim_call_function('jobstart', { term_args, { term = true } })
|
||||||
env.screen:expect([[
|
env.screen:expect(input_prompt_screen)
|
||||||
|
|
|
||||||
{1:~ }|*2
|
|
||||||
^ |
|
|
||||||
]])
|
|
||||||
vim.uv.sleep(20)
|
vim.uv.sleep(20)
|
||||||
feed('<CR>')
|
feed('<CR>')
|
||||||
env.screen:expect([[
|
env.screen:expect(oldbuf_screen)
|
||||||
^OLDBUF |
|
assert_alive()
|
||||||
{1:~ }|*2
|
eq('Xoldbuf', eval('bufname()'))
|
||||||
|
|
eq(0, eval([[exists('b:term_title')]]))
|
||||||
]])
|
end)
|
||||||
|
|
||||||
|
it('processes :bwipe from TermClose when using jobstart(…,{term=true})', function()
|
||||||
|
local term_buf = api.nvim_get_current_buf()
|
||||||
|
api.nvim_create_autocmd('TermClose', { command = ('bwipe! %d'):format(term_buf) })
|
||||||
|
api.nvim_create_autocmd(event, { command = "call input('')", nested = true })
|
||||||
|
async_meths.nvim_call_function('jobstart', { term_args, { term = true } })
|
||||||
|
env.screen:expect(input_prompt_screen)
|
||||||
|
vim.uv.sleep(20)
|
||||||
|
feed('<CR>')
|
||||||
|
env.screen:expect(oldbuf_screen)
|
||||||
|
assert_alive()
|
||||||
|
eq('Xoldbuf', eval('bufname()'))
|
||||||
|
eq(0, eval([[exists('b:term_title')]]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('only wipes buffer when using jobstart(…,{term=true})', function()
|
||||||
|
api.nvim_create_autocmd(event, { command = 'call Wipe()' })
|
||||||
|
async_meths.nvim_call_function('jobstart', { term_args, { term = true } })
|
||||||
|
env.screen:expect(oldbuf_screen)
|
||||||
assert_alive()
|
assert_alive()
|
||||||
eq('Xoldbuf', eval('bufname()'))
|
eq('Xoldbuf', eval('bufname()'))
|
||||||
eq(0, eval([[exists('b:term_title')]]))
|
eq(0, eval([[exists('b:term_title')]]))
|
||||||
|
|||||||
Reference in New Issue
Block a user