commit ba6440c106f99b5d68980784805e8a60a295eafe
parent a33e5fb1bbce06512b35b40b2cb04577a146f2aa
Author: horrifyingHorse <excuseplanation@gmail.com>
Date: Thu, 5 Feb 2026 23:09:57 +0530
fix(terminal): reset `w_leftcol` after resizing terminal
Problem: windows may scroll horizontally upon resize using the old terminal
size, which may be unnecessary and cause the content to be partially out-of-view.
Solution: reset the horizontal scroll after resizing.
Diffstat:
2 files changed, 56 insertions(+), 3 deletions(-)
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
@@ -2210,13 +2210,24 @@ static void refresh_terminal(Terminal *term)
}
linenr_T ml_before = buf->b_ml.ml_line_count;
- refresh_size(term, buf);
+ bool resized = refresh_size(term, buf);
refresh_scrollback(term, buf);
refresh_screen(term, buf);
int ml_added = buf->b_ml.ml_line_count - ml_before;
adjust_topline_cursor(term, buf, ml_added);
+ // Resized window may have scrolled horizontally to keep its cursor in-view using the old terminal
+ // size. Reset the scroll, and let curs_columns correct it if that sends the cursor out-of-view.
+ if (resized) {
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->w_buffer == buf && wp->w_leftcol != 0) {
+ wp->w_leftcol = 0;
+ curs_columns(wp, true);
+ }
+ }
+ }
+
// Copy pending events back to the main event queue
multiqueue_move_events(main_loop.events, term->pending.events);
}
@@ -2287,10 +2298,10 @@ static void refresh_timer_cb(TimeWatcher *watcher, void *data)
unblock_autocmds();
}
-static void refresh_size(Terminal *term, buf_T *buf)
+static bool refresh_size(Terminal *term, buf_T *buf)
{
if (!term->pending.resize || term->closed) {
- return;
+ return false;
}
term->pending.resize = false;
@@ -2299,6 +2310,7 @@ static void refresh_size(Terminal *term, buf_T *buf)
term->invalid_start = 0;
term->invalid_end = height;
term->opts.resize_cb((uint16_t)width, (uint16_t)height, term->opts.data);
+ return true;
}
void on_scrollback_option_changed(Terminal *term)
diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua
@@ -14,6 +14,7 @@ local eq = t.eq
local eval = n.eval
local skip = t.skip
local is_os = t.is_os
+local testprg = n.testprg
describe(':terminal window', function()
before_each(clear)
@@ -33,6 +34,46 @@ describe(':terminal window', function()
eq({ 1, 1, 1 }, eval('[&l:wrap, &wrap, &g:wrap]'))
eq({ 1, 1, 1 }, eval('[&l:list, &list, &g:list]'))
end)
+
+ it('resets horizontal scroll on resize #35331', function()
+ local screen = tt.setup_screen(0, { testprg('shell-test'), 'INTERACT' })
+ command('set statusline=%{win_getid()} splitright')
+ screen:expect([[
+ interact $ ^ |
+ |*5
+ {5:-- TERMINAL --} |
+ ]])
+ feed_data(('A'):rep(30))
+ screen:expect([[
+ interact $ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA^ |
+ |*5
+ {5:-- TERMINAL --} |
+ ]])
+ command('vnew | wincmd p')
+ screen:expect([[
+ interact $ AAAAAAAAAAAAA│ |
+ AAAAAAAAAAAAAAAAA^ │{100:~ }|
+ │{100:~ }|*3
+ {120:1000 }{2:1001 }|
+ {5:-- TERMINAL --} |
+ ]])
+
+ feed([[<C-\><C-N><C-W>o]])
+ screen:expect([[
+ interact $ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
+ ^ |
+ |*5
+ ]])
+ -- Window with less room scrolls anyway to keep its cursor in-view.
+ feed('gg$20<C-W>v')
+ screen:expect([[
+ interact $ AAAAAAAAAAAAAAAAAA│$ AAAAAAAAAAAAAAAAA^A|
+ AAAAAAAAAAAA │AAA |
+ │ |*3
+ {119:1000 }{120:1002 }|
+ |
+ ]])
+ end)
end)
describe(':terminal window', function()