commit 67832710a5d32a19aaf1b2934c428f02816ea22e
parent 459cffc55f7651a33b638028680255a775dded3d
Author: zeertzjq <zeertzjq@outlook.com>
Date: Fri, 24 Oct 2025 06:01:13 +0800
fix(terminal): wrong row in TermRequest with full scrollback (#36298)
Problem: Wrong row in TermRequest with full scrollback.
Solution: Subtract by the number of lines deleted from scrollback.
Diffstat:
4 files changed, 90 insertions(+), 5 deletions(-)
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
@@ -1066,7 +1066,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
@@ -580,7 +580,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
@@ -158,6 +158,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
@@ -230,6 +231,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
@@ -237,14 +239,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);
@@ -278,7 +281,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)
@@ -1451,6 +1455,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
@@ -555,6 +555,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()