diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 9fd366333d..d0e1c890e1 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -49,7 +49,7 @@ #include "api/buffer.c.generated.h" /// Ensures that a buffer is loaded. -static buf_T *api_buf_ensure_loaded(Buffer buffer, Error *err) +buf_T *api_buf_ensure_loaded(Buffer buffer, Error *err) { buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index c753463699..29239f4481 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1106,7 +1106,7 @@ Integer nvim_open_term(Buffer buffer, Dict(open_term) *opts, Error *err) FUNC_API_SINCE(7) FUNC_API_TEXTLOCK_ALLOW_CMDWIN { - buf_T *buf = find_buffer_by_handle(buffer, err); + buf_T *buf = api_buf_ensure_loaded(buffer, err); if (!buf) { return 0; } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index bdaf4d163d..4c333e593d 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3611,6 +3611,15 @@ void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const int pid = chan->stream.pty.proc.pid; buf_T *const buf = curbuf; + // If the buffer isn't loaded, open a memfile here to avoid spurious autocommands + // from open_buffer() when updating the terminal buffer later. + if (buf->b_ml.ml_mfp == NULL && ml_open(buf) == FAIL) { + // Internal error in ml_open(): stop the job. + proc_stop(&chan->stream.proc); + channel_decref(chan); + return; + } + channel_incref(chan); channel_terminal_alloc(buf, chan); diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 3b6a14b3a4..490de454c6 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -936,6 +936,61 @@ describe('startup', function() | ]]) end) + + describe('opening a terminal before buffers are loaded #30765', function() + local lines = {} --- @type string[] + for i = 1, 50 do + lines[#lines + 1] = ('line%d'):format(i) + end + + setup(function() + write_file('Xsomefile', table.concat(lines, '\n') .. '\n') + end) + + teardown(function() + os.remove('Xsomefile') + end) + + it('sends buffer content to terminal with nvim_open_term()', function() + clear({ + args_rm = { '--headless' }, + args = { + 'Xsomefile', + '--cmd', + 'let g:chan = nvim_open_term(0, {}) | startinsert', + '--cmd', + 'call chansend(g:chan, "new_line1\nnew_line2\nnew_line3")', + }, + }) + local screen = Screen.new(50, 7) + screen:expect([[ + line48 | + line49 | + line50 | + new_line1 | + new_line2 | + new_line3^ | + {5:-- TERMINAL --} | + ]]) + eq(lines, api.nvim_buf_get_lines(0, 0, #lines, true)) + end) + + it('does not error with jobstart(…,{term=true})', function() + clear({ + args_rm = { '--headless' }, + args = { + 'Xsomefile', + '--cmd', + ('lua vim.fn.jobstart({%q}, {term = true})'):format(n.testprg('tty-test')), + }, + }) + local screen = Screen.new(50, 7) + screen:expect([[ + ^tty ready | + |*6 + ]]) + end) + end) end) describe('startup', function()