diff options
-rw-r--r-- | src/nvim/buffer_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/edit.c | 3 | ||||
-rw-r--r-- | src/nvim/move.c | 79 | ||||
-rw-r--r-- | src/nvim/normal.c | 2 | ||||
-rw-r--r-- | test/functional/legacy/scroll_opt_spec.lua | 34 | ||||
-rw-r--r-- | test/old/testdir/test_scroll_opt.vim | 21 |
6 files changed, 130 insertions, 10 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 456acfd798..0f832d3659 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1223,6 +1223,7 @@ struct window_S { int w_valid; pos_T w_valid_cursor; // last known position of w_cursor, used to adjust w_valid colnr_T w_valid_leftcol; // last known w_leftcol + colnr_T w_valid_skipcol; // last known w_skipcol bool w_viewport_invalid; linenr_T w_viewport_last_topline; // topline when the viewport was last updated diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 0fb1102f4f..18434ad69f 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -2449,6 +2449,7 @@ void beginline(int flags) } curwin->w_set_curswant = true; } + adjust_skipcol(); } // oneright oneleft cursor_down cursor_up @@ -2490,6 +2491,7 @@ int oneright(void) curwin->w_cursor.col += l; curwin->w_set_curswant = true; + adjust_skipcol(); return OK; } @@ -2538,6 +2540,7 @@ int oneleft(void) // if the character on the left of the current cursor is a multi-byte // character, move to its first byte mb_adjust_cursor(); + adjust_skipcol(); return OK; } diff --git a/src/nvim/move.c b/src/nvim/move.c index e7f5959dbb..b2cf1ebc03 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -426,7 +426,15 @@ void check_cursor_moved(win_T *wp) |VALID_CHEIGHT|VALID_CROW|VALID_TOPLINE); wp->w_valid_cursor = wp->w_cursor; wp->w_valid_leftcol = wp->w_leftcol; + wp->w_valid_skipcol = wp->w_skipcol; wp->w_viewport_invalid = true; + } else if (wp->w_skipcol != wp->w_valid_skipcol) { + wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL + |VALID_CHEIGHT|VALID_CROW + |VALID_BOTLINE|VALID_BOTLINE_AP); + wp->w_valid_cursor = wp->w_cursor; + wp->w_valid_leftcol = wp->w_leftcol; + wp->w_valid_skipcol = wp->w_skipcol; } else if (wp->w_cursor.col != wp->w_valid_cursor.col || wp->w_leftcol != wp->w_valid_leftcol || wp->w_cursor.coladd != @@ -786,6 +794,12 @@ void curs_columns(win_T *wp, int may_scroll) } else if (wp->w_p_wrap && wp->w_width_inner != 0) { width = textwidth + win_col_off2(wp); + // skip columns that are not visible + if (wp->w_cursor.lnum == wp->w_topline + && wp->w_wcol >= wp->w_skipcol) { + wp->w_wcol -= wp->w_skipcol; + } + // long line wrapping, adjust wp->w_wrow if (wp->w_wcol >= wp->w_width_inner) { // this same formula is used in validate_cursor_col() @@ -929,11 +943,11 @@ void curs_columns(win_T *wp, int may_scroll) endcol -= width; } if (endcol > wp->w_skipcol) { + wp->w_wrow -= (endcol - wp->w_skipcol) / width; wp->w_skipcol = endcol; } } - wp->w_wrow -= wp->w_skipcol / width; if (wp->w_wrow >= wp->w_height_inner) { // small window, make sure cursor is in it extra = wp->w_wrow - wp->w_height_inner + 1; @@ -953,8 +967,10 @@ void curs_columns(win_T *wp, int may_scroll) redraw_for_cursorcolumn(curwin); - // now w_leftcol is valid, avoid check_cursor_moved() thinking otherwise + // now w_leftcol and w_skipcol are valid, avoid check_cursor_moved() + // thinking otherwise wp->w_valid_leftcol = wp->w_leftcol; + wp->w_valid_skipcol = wp->w_skipcol; wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; } @@ -1352,15 +1368,70 @@ bool scrollup(long line_count, int byfold) } curwin->w_curswant = col; coladvance(curwin->w_curswant); + + // validate_virtcol() marked various things as valid, but after + // moving the cursor they need to be recomputed + curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); } } - bool moved = topline != curwin->w_topline - || botline != curwin->w_botline; + bool moved = topline != curwin->w_topline || botline != curwin->w_botline; return moved; } +/// Called after changing the cursor column: make sure that curwin->w_skipcol is +/// valid for 'smoothscroll'. +void adjust_skipcol(void) +{ + if (!curwin->w_p_wrap || !curwin->w_p_sms || curwin->w_cursor.lnum != curwin->w_topline) { + return; + } + + int width1 = curwin->w_width - curwin_col_off(); + int width2 = width1 + curwin_col_off2(); + long so = curwin->w_p_so >= 0 ? curwin->w_p_so : p_so; + long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; + bool scrolled = false; + + validate_virtcol(); + while (curwin->w_skipcol > 0 + && curwin->w_virtcol < curwin->w_skipcol + 3 + scrolloff_cols) { + // scroll a screen line down + if (curwin->w_skipcol >= width1 + width2) { + curwin->w_skipcol -= width2; + } else { + curwin->w_skipcol -= width1; + } + redraw_later(curwin, UPD_NOT_VALID); + scrolled = true; + validate_virtcol(); + } + if (scrolled) { + return; // don't scroll in the other direction now + } + long col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols; + int row = 0; + if (col >= width1) { + col -= width1; + row++; + } + if (col > width2) { + row += (int)col / width2; + col = col % width2; + } + if (row >= curwin->w_height) { + if (curwin->w_skipcol == 0) { + curwin->w_skipcol += width1; + row--; + } + if (row >= curwin->w_height) { + curwin->w_skipcol += (row - curwin->w_height) * width2; + } + redraw_later(curwin, UPD_NOT_VALID); + } +} + /// Don't end up with too many filler lines in the window. /// /// @param down when true scroll down when not enough space diff --git a/src/nvim/normal.c b/src/nvim/normal.c index a51bde967e..d4759aeaba 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2566,6 +2566,8 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) if (atend) { curwin->w_curswant = MAXCOL; // stick in the last column } + adjust_skipcol(); + return retval; } diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index be7d324908..a5660ae4d2 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -428,14 +428,36 @@ describe('smoothscroll', function() | ]]) -- 'scrolloff' set to 2, scrolling down, cursor moves screen line up - feed('<C-E>gjgj<C-Y>') + feed('<C-E>gj<C-Y>') + screen:expect_unchanged() + end) + + -- oldtest: Test_smoothscroll_one_long_line() + it("scrolls correctly when moving the cursor", function() + screen:try_resize(40, 6) + exec([[ + call setline(1, 'with lots of text '->repeat(7)) + set smoothscroll scrolloff=0 + ]]) + local s1 = [[ + ^with lots of text with lots of text with| + lots of text with lots of text with lot| + s of text with lots of text with lots of| + text | + ~ | + | + ]] + screen:expect(s1) + feed('<C-E>') screen:expect([[ - <<<of text with lots of text with lots o| - f text with lots of text with lots of te| - xt with l^ots of text with lots of text w| - ith lots of text with lots of text with | - lots of text with lots of text with lots| + <<<ts of text with lots of text with lot| + ^s of text with lots of text with lots of| + text | + ~ | + ~ | | ]]) + feed('0') + screen:expect(s1) end) end) diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index 3317ec9209..a8e0a1109a 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -265,5 +265,26 @@ func Test_smoothscroll_wrap_long_line() call StopVimInTerminal(buf) endfunc +func Test_smoothscroll_one_long_line() + CheckScreendump + + let lines =<< trim END + vim9script + setline(1, 'with lots of text '->repeat(7)) + set smoothscroll scrolloff=0 + END + call writefile(lines, 'XSmoothOneLong', 'D') + let buf = RunVimInTerminal('-S XSmoothOneLong', #{rows: 6, cols: 40}) + call VerifyScreenDump(buf, 'Test_smooth_one_long_1', {}) + + call term_sendkeys(buf, "\<C-E>") + call VerifyScreenDump(buf, 'Test_smooth_one_long_2', {}) + + call term_sendkeys(buf, "0") + call VerifyScreenDump(buf, 'Test_smooth_one_long_1', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab |