diff --git a/src/nvim/move.c b/src/nvim/move.c index 767c473eca..3e3e427b16 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1896,9 +1896,19 @@ void scroll_cursor_top(win_T *wp, int min_scroll, int always) } else if (wp->w_topline == wp->w_cursor.lnum) { validate_virtcol(wp); if (wp->w_skipcol >= wp->w_virtcol) { - // TODO(vim): if the line doesn't fit may optimize w_skipcol instead - // of making it zero - reset_skipcol(wp); + // Skip up to the screen line the cursor is in, so that the + // position in the line is kept. + int width1 = wp->w_width - win_col_off(wp); + int width2 = width1 + win_col_off2(wp); + int plines_off = 0; + if (width2 > 0 && wp->w_virtcol >= width1) { + plines_off = (wp->w_virtcol - width1) / width2 + 1; + } + int skipcol = skipcol_from_plines(wp, plines_off); + if (skipcol != wp->w_skipcol) { + wp->w_skipcol = skipcol; + redraw_later(wp, UPD_SOME_VALID); + } } } if (wp->w_topline != old_topline diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index f15c5a0ec4..43a73eb8ce 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -1353,6 +1353,27 @@ func Test_smoothscroll_cursor_back_in_line() bwipe! endfunc +func Test_smoothscroll_squeezed_window() + setlocal smoothscroll + call setline(1, [repeat('x', 3000)] + repeat(['line'], 10)) + exe "norm! gg10\" + redraw + let skipcol = winsaveview().skipcol + call assert_notequal(0, skipcol) + let virtcol = virtcol('.') + + " Squeezing the window to one line and restoring it must not scroll back to + " the start of the line. + new + wincmd _ + close + redraw + call assert_notequal(0, winsaveview().skipcol) + call assert_equal(virtcol, virtcol('.')) + + bwipe! +endfunc + func Test_smoothscroll_long_line_zb() call NewWindow(10, 40) call setline(1, 'abcde '->repeat(150))