fix(terminal): don't poll for output during scrollback refresh (#38365)

Problem:
If buffer update callbacks poll for uv events during terminal scrollback
refresh, new output from PTY process may lead to incorrect scrollback.

Solution:
Don't poll for output to the same terminal as the one being refreshed.
This commit is contained in:
zeertzjq
2026-03-19 18:16:57 +08:00
committed by GitHub
parent 08c64bb036
commit 7d6b6b2d14
5 changed files with 69 additions and 8 deletions

View File

@@ -1153,6 +1153,7 @@ Integer nvim_open_term(Buffer buffer, Dict(open_term) *opts, Error *err)
// displaying the buffer
.width = (uint16_t)MAX(curwin->w_view_width - win_col_off(curwin), 0),
.height = (uint16_t)curwin->w_view_height,
.read_pause_cb = term_read_pause,
.write_cb = term_write,
.resize_cb = term_resize,
.resume_cb = term_resume,
@@ -1185,6 +1186,11 @@ Integer nvim_open_term(Buffer buffer, Dict(open_term) *opts, Error *err)
return (Integer)chan->id;
}
static void term_read_pause(bool pause, void *data)
{
// Not currently needed as sending to channel isn't allowed during buffer updates.
}
static void term_write(const char *buf, size_t size, void *data)
{
Channel *chan = data;

View File

@@ -847,6 +847,7 @@ void channel_terminal_alloc(buf_T *buf, Channel *chan)
.data = chan,
.width = chan->stream.pty.width,
.height = chan->stream.pty.height,
.read_pause_cb = term_read_pause,
.write_cb = term_write,
.resize_cb = term_resize,
.resume_cb = term_resume,
@@ -858,6 +859,19 @@ void channel_terminal_alloc(buf_T *buf, Channel *chan)
chan->term = terminal_alloc(buf, topts);
}
static void term_read_pause(bool pause, void *data)
{
Channel *chan = data;
if (chan->stream.proc.out.s.closed) {
return;
}
if (pause) {
rstream_stop_inner(&chan->stream.proc.out);
} else {
rstream_start_inner(&chan->stream.proc.out);
}
}
static void term_write(const char *buf, size_t size, void *data)
{
Channel *chan = data;

View File

@@ -2530,6 +2530,10 @@ static void adjust_scrollback(Terminal *term, buf_T *buf)
// Refresh the scrollback of an invalidated terminal.
static void refresh_scrollback(Terminal *term, buf_T *buf)
{
// Buffer update callbacks may poll for uv events.
// Avoid polling for output to the same terminal as the one being refreshed.
term->opts.read_pause_cb(true, term->opts.data);
linenr_T deleted = (linenr_T)(term->sb_deleted - term->old_sb_deleted);
deleted = MIN(deleted, buf->b_ml.ml_line_count);
mark_adjust_buf(buf, 1, deleted, MAXLNUM, -deleted, true, kMarkAdjustTerm, kExtmarkUndo);
@@ -2569,6 +2573,8 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
}
adjust_scrollback(term, buf);
term->opts.read_pause_cb(false, term->opts.data);
}
// Refresh the screen (visible part of the buffer when the terminal is

View File

@@ -7,6 +7,7 @@
#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
typedef void (*terminal_read_pause_cb)(bool pause, void *data);
typedef void (*terminal_write_cb)(const char *buffer, size_t size, void *data);
typedef void (*terminal_resize_cb)(uint16_t width, uint16_t height, void *data);
typedef void (*terminal_resume_cb)(void *data);
@@ -15,6 +16,7 @@ typedef void (*terminal_close_cb)(void *data);
typedef struct {
void *data; // PTY process channel
uint16_t width, height;
terminal_read_pause_cb read_pause_cb;
terminal_write_cb write_cb;
terminal_resize_cb resize_cb;
terminal_resume_cb resume_cb;