fix(terminal): :edit should respect 'bufhidden' with exited job (#37301)

This commit is contained in:
zeertzjq
2026-01-10 08:25:49 +08:00
committed by GitHub
parent d1f7672bc9
commit 295fb3fdb2
2 changed files with 92 additions and 55 deletions

View File

@@ -2601,7 +2601,10 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// oldwin->w_buffer to NULL. // oldwin->w_buffer to NULL.
u_sync(false); u_sync(false);
const bool did_decrement const bool did_decrement
= close_buffer(oldwin, curbuf, (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, = close_buffer(oldwin, curbuf,
(flags & ECMD_HIDE)
|| (curbuf->terminal && terminal_running(curbuf->terminal))
? 0 : DOBUF_UNLOAD,
false, false); false, false);
// Autocommands may have closed the window. // Autocommands may have closed the window.

View File

@@ -64,26 +64,6 @@ describe(':terminal buffer', function()
eq({ 0, 'line' }, eval('[&l:cursorline, &l:cursorlineopt]')) eq({ 0, 'line' }, eval('[&l:cursorline, &l:cursorlineopt]'))
end) end)
describe('when a new file is edited', function()
before_each(function()
feed('<c-\\><c-n>:set bufhidden=wipe<cr>:enew<cr>')
screen:expect([[
^ |
{100:~ }|*5
:enew |
]])
end)
it('will hide the buffer, ignoring the bufhidden option', function()
feed(':bnext:l<esc>')
screen:expect([[
^ |
{100:~ }|*5
|
]])
end)
end)
describe('swap and undo', function() describe('swap and undo', function()
before_each(function() before_each(function()
feed('<c-\\><c-n>') feed('<c-\\><c-n>')
@@ -880,9 +860,30 @@ describe(':terminal buffer', function()
eq(false, api.nvim_buf_is_valid(term_buf)) eq(false, api.nvim_buf_is_valid(term_buf))
end) end)
local enew_screen = [[
^ |
{1:~ }|*5
|
]]
local function test_enew_in_buf_with_running_term(env)
describe('editing a new file', function()
it('hides terminal buffer ignoring bufhidden=wipe', function()
local old_snapshot = env.screen:get_snapshot()
command('setlocal bufhidden=wipe')
command('enew')
neq(env.buf, api.nvim_get_current_buf())
env.screen:expect(enew_screen)
feed('<C-^>')
eq(env.buf, api.nvim_get_current_buf())
env.screen:expect(old_snapshot)
end)
end)
end
local function test_open_term_in_buf_with_running_term(env) local function test_open_term_in_buf_with_running_term(env)
describe('does not allow', function() describe('does not allow opening another terminal', function()
it('opening another terminal with jobstart() in same buffer', function() it('with jobstart() in same buffer', function()
eq( eq(
('Vim:Terminal already connected to buffer %d'):format(env.buf), ('Vim:Terminal already connected to buffer %d'):format(env.buf),
pcall_err(fn.jobstart, { testprg('tty-test') }, { term = true }) pcall_err(fn.jobstart, { testprg('tty-test') }, { term = true })
@@ -890,7 +891,7 @@ describe(':terminal buffer', function()
env.screen:expect_unchanged() env.screen:expect_unchanged()
end) end)
it('opening another terminal with nvim_open_term() in same buffer', function() it('with nvim_open_term() in same buffer', function()
eq( eq(
('Terminal already connected to buffer %d'):format(env.buf), ('Terminal already connected to buffer %d'):format(env.buf),
pcall_err(api.nvim_open_term, env.buf, {}) pcall_err(api.nvim_open_term, env.buf, {})
@@ -904,16 +905,17 @@ describe(':terminal buffer', function()
local env = {} local env = {}
before_each(function() before_each(function()
env.screen = Screen.new(60, 6) env.screen = Screen.new(50, 7)
fn.jobstart({ testprg('tty-test') }, { term = true }) fn.jobstart({ testprg('tty-test') }, { term = true })
env.screen:expect([[ env.screen:expect([[
^tty ready | ^tty ready |
|*5 |*6
]]) ]])
env.buf = api.nvim_get_current_buf() env.buf = api.nvim_get_current_buf()
api.nvim_set_option_value('modified', false, { buf = env.buf }) api.nvim_set_option_value('modified', false, { buf = env.buf })
end) end)
test_enew_in_buf_with_running_term(env)
test_open_term_in_buf_with_running_term(env) test_open_term_in_buf_with_running_term(env)
end) end)
@@ -921,28 +923,58 @@ describe(':terminal buffer', function()
local env = {} local env = {}
before_each(function() before_each(function()
env.screen = Screen.new(60, 6) env.screen = Screen.new(50, 7)
local chan = api.nvim_open_term(0, {}) local chan = api.nvim_open_term(0, {})
api.nvim_chan_send(chan, 'TEST') api.nvim_chan_send(chan, 'TEST')
env.screen:expect([[ env.screen:expect([[
^TEST | ^TEST |
|*5 |*6
]]) ]])
env.buf = api.nvim_get_current_buf() env.buf = api.nvim_get_current_buf()
api.nvim_set_option_value('modified', false, { buf = env.buf }) api.nvim_set_option_value('modified', false, { buf = env.buf })
end) end)
test_enew_in_buf_with_running_term(env)
test_open_term_in_buf_with_running_term(env) test_open_term_in_buf_with_running_term(env)
end) end)
local function test_open_term_in_buf_with_closed_term(env) local function test_enew_in_buf_with_finished_term(env)
describe('does not leak memory when', function() describe('editing a new file', function()
describe('opening another terminal with jobstart() in same buffer', function() it('hides terminal buffer with bufhidden=hide', function()
local old_snapshot = env.screen:get_snapshot()
command('setlocal bufhidden=hide')
command('enew')
neq(env.buf, api.nvim_get_current_buf())
env.screen:expect(enew_screen)
feed('<C-^>')
eq(env.buf, api.nvim_get_current_buf())
env.screen:expect(old_snapshot)
end)
it('wipes terminal buffer with bufhidden=wipe', function()
command('setlocal bufhidden=wipe')
command('enew')
neq(env.buf, api.nvim_get_current_buf())
eq(false, api.nvim_buf_is_valid(env.buf))
env.screen:expect(enew_screen)
feed('<C-^>')
env.screen:expect([[
^ |
{1:~ }|*5
{9:E23: No alternate file} |
]])
end)
end)
end
local function test_open_term_in_buf_with_finished_term(env)
describe('does not leak memory when opening another terminal', function()
describe('with jobstart() in same buffer', function()
it('in Normal mode', function() it('in Normal mode', function()
fn.jobstart({ testprg('tty-test') }, { term = true }) fn.jobstart({ testprg('tty-test') }, { term = true })
env.screen:expect([[ env.screen:expect([[
^tty ready | ^tty ready |
|*5 |*6
]]) ]])
end) end)
@@ -951,21 +983,21 @@ describe(':terminal buffer', function()
eq({ blocking = false, mode = 't' }, api.nvim_get_mode()) eq({ blocking = false, mode = 't' }, api.nvim_get_mode())
fn.jobstart({ testprg('tty-test') }, { term = true }) fn.jobstart({ testprg('tty-test') }, { term = true })
env.screen:expect([[ env.screen:expect([[
tty ready | tty ready |
^ | ^ |
|*3 |*4
{5:-- TERMINAL --} | {5:-- TERMINAL --} |
]]) ]])
end) end)
end) end)
describe('opening another terminal with nvim_open_term() in same buffer', function() describe('with nvim_open_term() in same buffer', function()
it('in Normal mode', function() it('in Normal mode', function()
local chan = api.nvim_open_term(env.buf, {}) local chan = api.nvim_open_term(env.buf, {})
api.nvim_chan_send(chan, 'OTHER') api.nvim_chan_send(chan, 'OTHER')
env.screen:expect([[ env.screen:expect([[
^OTHER | ^OTHER |
|*5 |*6
]]) ]])
end) end)
@@ -975,9 +1007,9 @@ describe(':terminal buffer', function()
local chan = api.nvim_open_term(env.buf, {}) local chan = api.nvim_open_term(env.buf, {})
api.nvim_chan_send(chan, 'OTHER') api.nvim_chan_send(chan, 'OTHER')
env.screen:expect([[ env.screen:expect([[
OTHER^ | OTHER^ |
|*4 |*5
{5:-- TERMINAL --} | {5:-- TERMINAL --} |
]]) ]])
end) end)
end) end)
@@ -988,38 +1020,40 @@ describe(':terminal buffer', function()
local env = {} local env = {}
before_each(function() before_each(function()
env.screen = Screen.new(60, 6) env.screen = Screen.new(50, 7)
fn.jobstart({ testprg('shell-test') }, { term = true }) fn.jobstart({ testprg('shell-test') }, { term = true })
env.screen:expect([[ env.screen:expect([[
^ready $ | ^ready $ |
[Process exited 0] | [Process exited 0] |
|*4 |*5
]]) ]])
env.buf = api.nvim_get_current_buf() env.buf = api.nvim_get_current_buf()
api.nvim_set_option_value('modified', false, { buf = env.buf }) api.nvim_set_option_value('modified', false, { buf = env.buf })
end) end)
test_open_term_in_buf_with_closed_term(env) test_enew_in_buf_with_finished_term(env)
test_open_term_in_buf_with_finished_term(env)
end) end)
describe('with closed nvim_open_term() channel', function() describe('with closed nvim_open_term() channel', function()
local env = {} local env = {}
before_each(function() before_each(function()
env.screen = Screen.new(60, 6) env.screen = Screen.new(50, 7)
local chan = api.nvim_open_term(0, {}) local chan = api.nvim_open_term(0, {})
api.nvim_chan_send(chan, 'TEST') api.nvim_chan_send(chan, 'TEST')
fn.chanclose(chan) fn.chanclose(chan)
env.screen:expect([[ env.screen:expect([[
^TEST | ^TEST |
[Terminal closed] | [Terminal closed] |
|*4 |*5
]]) ]])
env.buf = api.nvim_get_current_buf() env.buf = api.nvim_get_current_buf()
api.nvim_set_option_value('modified', false, { buf = env.buf }) api.nvim_set_option_value('modified', false, { buf = env.buf })
end) end)
test_open_term_in_buf_with_closed_term(env) test_enew_in_buf_with_finished_term(env)
test_open_term_in_buf_with_finished_term(env)
end) end)
it('with nvim_open_term() channel and only 1 line is not reused by :enew', function() it('with nvim_open_term() channel and only 1 line is not reused by :enew', function()