diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 4e12d8fe00..5f8603f713 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -1028,7 +1028,8 @@ TermRequest When a |:terminal| child process emits an OSC, - sequence: the received sequence - cursor: (1,0)-indexed, buffer-relative position of the cursor when the sequence was - received + received (line number may be <= 0 if the + position is no longer in the buffer) This is triggered even when inside an autocommand defined without |autocmd-nested|. diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 5a01260d83..f815503e5d 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -566,7 +566,9 @@ do callback = function(args) if string.match(args.data.sequence, '^\027]133;A') then local lnum = args.data.cursor[1] ---@type integer - vim.api.nvim_buf_set_extmark(args.buf, nvim_terminal_prompt_ns, lnum - 1, 0, {}) + if lnum >= 1 then + vim.api.nvim_buf_set_extmark(args.buf, nvim_terminal_prompt_ns, lnum - 1, 0, {}) + end end end, }) diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index e09bc47fb1..e904658dcb 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -149,6 +149,7 @@ struct terminal { // it actually points to entries that are no longer in sb_buffer (because the // window height has increased) and must be deleted from the terminal buffer int sb_pending; + size_t sb_deleted; // Lines deleted from sb_buffer. char *title; // VTermStringFragment buffer size_t title_len; // number of rows pushed to sb_buffer @@ -221,6 +222,7 @@ static void emit_termrequest(void **argv) StringBuilder *pending_send = argv[3]; int row = (int)(intptr_t)argv[4]; int col = (int)(intptr_t)argv[5]; + size_t sb_deleted = (size_t)(intptr_t)argv[6]; if (term->sb_pending > 0) { // Don't emit the event while there is pending scrollback because we need @@ -228,14 +230,15 @@ static void emit_termrequest(void **argv) // the event onto the pending queue where it will be executed after the // terminal is refreshed and the pending scrollback is cleared. multiqueue_put(term->pending.events, emit_termrequest, term, sequence, (void *)sequence_length, - pending_send, (void *)(intptr_t)row, (void *)(intptr_t)col); + pending_send, (void *)(intptr_t)row, (void *)(intptr_t)col, + (void *)(intptr_t)sb_deleted); return; } set_vim_var_string(VV_TERMREQUEST, sequence, (ptrdiff_t)sequence_length); MAXSIZE_TEMP_ARRAY(cursor, 2); - ADD_C(cursor, INTEGER_OBJ(row)); + ADD_C(cursor, INTEGER_OBJ(row - (int64_t)(term->sb_deleted - sb_deleted))); ADD_C(cursor, INTEGER_OBJ(col)); MAXSIZE_TEMP_DICT(data, 2); @@ -269,7 +272,8 @@ static void schedule_termrequest(Terminal *term) multiqueue_put(main_loop.events, emit_termrequest, term, xmemdup(term->termrequest_buffer.items, term->termrequest_buffer.size), (void *)(intptr_t)term->termrequest_buffer.size, term->pending.send, - (void *)(intptr_t)line, (void *)(intptr_t)term->cursor.col); + (void *)(intptr_t)line, (void *)(intptr_t)term->cursor.col, + (void *)(intptr_t)term->sb_deleted); } static int parse_osc8(const char *str, int *attr) @@ -1372,6 +1376,7 @@ static int term_sb_push(int cols, const VTermScreenCell *cells, void *data) } else { xfree(term->sb_buffer[term->sb_current - 1]); } + term->sb_deleted++; // Make room at the start by shifting to the right. memmove(term->sb_buffer + 1, term->sb_buffer, diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 0f4d0189a4..943e445c25 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -473,6 +473,83 @@ describe(':terminal buffer', function() {5:-- TERMINAL --} | ]]) eq({ 22, 6 }, exec_lua('return _G.cursor')) + + api.nvim_chan_send(term, '\nHello\027]133;D\027\\\nworld!\n') + screen:expect([[ + > |*4 + Hello | + world! | + Hello | + world! | + ^ | + {5:-- TERMINAL --} | + ]]) + eq({ 23, 5 }, exec_lua('return _G.cursor')) + + api.nvim_chan_send(term, 'Hello\027]133;D\027\\\nworld!' .. ('\n'):rep(6)) + screen:expect([[ + world! | + Hello | + world! | + |*5 + ^ | + {5:-- TERMINAL --} | + ]]) + eq({ 25, 5 }, exec_lua('return _G.cursor')) + + api.nvim_set_option_value('scrollback', 10, {}) + eq(19, api.nvim_buf_line_count(0)) + + api.nvim_chan_send(term, 'Hello\nworld!\027]133;D\027\\') + screen:expect([[ + Hello | + world! | + |*5 + Hello | + world!^ | + {5:-- TERMINAL --} | + ]]) + eq({ 19, 6 }, exec_lua('return _G.cursor')) + + api.nvim_chan_send(term, '\nHello\027]133;D\027\\\nworld!\n') + screen:expect([[ + |*4 + Hello | + world! | + Hello | + world! | + ^ | + {5:-- TERMINAL --} | + ]]) + eq({ 17, 5 }, exec_lua('return _G.cursor')) + + api.nvim_chan_send(term, 'Hello\027]133;D\027\\\nworld!' .. ('\n'):rep(6)) + screen:expect([[ + world! | + Hello | + world! | + |*5 + ^ | + {5:-- TERMINAL --} | + ]]) + eq({ 12, 5 }, exec_lua('return _G.cursor')) + + api.nvim_chan_send(term, 'Hello\027]133;D\027\\\nworld!' .. ('\n'):rep(8)) + screen:expect([[ + world! | + |*7 + ^ | + {5:-- TERMINAL --} | + ]]) + eq({ 10, 5 }, exec_lua('return _G.cursor')) + + api.nvim_chan_send(term, 'Hello\027]133;D\027\\\nworld!' .. ('\n'):rep(20)) + screen:expect([[ + |*8 + ^ | + {5:-- TERMINAL --} | + ]]) + eq({ -2, 5 }, exec_lua('return _G.cursor')) end) it('does not cause hang in vim.wait() #32753', function()