diff options
author | zeertzjq <zeertzjq@outlook.com> | 2025-02-14 07:21:45 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-02-13 23:21:45 +0000 |
commit | 9f85dace94d2682f076ede824d3516cdf779ff7b (patch) | |
tree | c68afb77088206a0229731aa1feae600d80c1774 | |
parent | 93480f7fbaa5b4e5418d95dd35005daa6142dbb9 (diff) | |
download | rneovim-9f85dace94d2682f076ede824d3516cdf779ff7b.tar.gz rneovim-9f85dace94d2682f076ede824d3516cdf779ff7b.tar.bz2 rneovim-9f85dace94d2682f076ede824d3516cdf779ff7b.zip |
vim-patch:9.1.1108: 'smoothscroll' gets stuck with 'listchars' "eol" (#32434)
Problem: 'smoothscroll' gets stuck with 'listchars' "eol".
Solution: Count size of 'listchars' "eol" in line size when scrolling.
(zeertzjq)
related: neovim/neovim#32405
closes: vim/vim#16627
https://github.com/vim/vim/commit/2c47ab8fcd7188fa87053c757ea86b0d846c06c1
-rw-r--r-- | src/nvim/change.c | 4 | ||||
-rw-r--r-- | src/nvim/cursor.c | 2 | ||||
-rw-r--r-- | src/nvim/move.c | 37 | ||||
-rw-r--r-- | src/nvim/normal.c | 2 | ||||
-rw-r--r-- | src/nvim/plines.c | 8 | ||||
-rw-r--r-- | src/nvim/plines.h | 1 | ||||
-rw-r--r-- | test/old/testdir/test_scroll_opt.vim | 55 |
7 files changed, 91 insertions, 18 deletions
diff --git a/src/nvim/change.c b/src/nvim/change.c index ecd6012679..cb5e2b0f65 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -341,8 +341,8 @@ static void changed_common(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnum && (last < wp->w_topline || (wp->w_topline >= lnum && wp->w_topline < lnume - && win_linetabsize(wp, wp->w_topline, ml_get_buf(buf, wp->w_topline), MAXCOL) - <= (wp->w_skipcol + sms_marker_overlap(wp, -1))))) { + && (linetabsize_eol(wp, wp->w_topline) + <= wp->w_skipcol + sms_marker_overlap(wp, -1))))) { wp->w_skipcol = 0; } diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 580ed856e4..98cea7f283 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -130,7 +130,7 @@ static int coladvance2(win_T *wp, pos_T *pos, bool addspaces, bool finetune, col && wp->w_width_inner != 0 && wcol >= (colnr_T)width && width > 0) { - csize = linetabsize(wp, pos->lnum); + csize = linetabsize_eol(wp, pos->lnum); if (csize > 0) { csize--; } diff --git a/src/nvim/move.c b/src/nvim/move.c index afd569ba7d..424f2a6f3b 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1191,9 +1191,7 @@ static void cursor_correct_sms(win_T *wp) int width2 = width1 + win_col_off2(wp); int so_cols = so == 0 ? 0 : width1 + (so - 1) * width2; int space_cols = (wp->w_height_inner - 1) * width2; - int size = so == 0 ? 0 : win_linetabsize(wp, wp->w_topline, - ml_get_buf(wp->w_buffer, wp->w_topline), - (colnr_T)MAXCOL); + int size = so == 0 ? 0 : linetabsize_eol(wp, wp->w_topline); if (wp->w_topline == 1 && wp->w_skipcol == 0) { so_cols = 0; // Ignore 'scrolloff' at top of buffer. @@ -1209,9 +1207,10 @@ static void cursor_correct_sms(win_T *wp) so_cols -= width1; } - // If there is no marker or we have non-zero scrolloff, just ignore it. - int overlap = (wp->w_skipcol == 0 || so_cols != 0) ? 0 : sms_marker_overlap(wp, -1); - int top = wp->w_skipcol + overlap + so_cols; + int overlap = wp->w_skipcol == 0 + ? 0 : sms_marker_overlap(wp, wp->w_width_inner - width2); + // If we have non-zero scrolloff, ignore marker overlap. + int top = wp->w_skipcol + (so_cols != 0 ? so_cols : overlap); int bot = wp->w_skipcol + width1 + (wp->w_height_inner - 1) * width2 - so_cols; validate_virtcol(wp); @@ -1232,10 +1231,22 @@ static void cursor_correct_sms(win_T *wp) if (col != wp->w_virtcol) { wp->w_curswant = col; - coladvance(wp, wp->w_curswant); + int rc = coladvance(wp, wp->w_curswant); // validate_virtcol() marked various things as valid, but after // moving the cursor they need to be recomputed wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); + if (rc == FAIL && wp->w_skipcol > 0 + && wp->w_cursor.lnum < wp->w_buffer->b_ml.ml_line_count) { + validate_virtcol(wp); + if (wp->w_virtcol < wp->w_skipcol + overlap) { + // Cursor still not visible: move it to the next line instead. + wp->w_cursor.lnum++; + wp->w_cursor.col = 0; + wp->w_cursor.coladd = 0; + wp->w_curswant = 0; + wp->w_valid &= ~VALID_VIRTCOL; + } + } } } @@ -1348,8 +1359,7 @@ bool scrolldown(win_T *wp, linenr_T line_count, int byfold) wp->w_topline = first; } else { if (do_sms) { - int size = win_linetabsize(wp, wp->w_topline, - ml_get_buf(wp->w_buffer, wp->w_topline), MAXCOL); + int size = linetabsize_eol(wp, wp->w_topline); if (size > width1) { wp->w_skipcol = width1; size -= width1; @@ -1430,7 +1440,7 @@ bool scrollup(win_T *wp, linenr_T line_count, bool byfold) const colnr_T prev_skipcol = wp->w_skipcol; if (do_sms) { - size = linetabsize(wp, wp->w_topline); + size = linetabsize_eol(wp, wp->w_topline); } // diff mode: first consume "topfill" @@ -1473,7 +1483,7 @@ bool scrollup(win_T *wp, linenr_T line_count, bool byfold) wp->w_topfill = win_get_fill(wp, lnum); wp->w_skipcol = 0; if (todo > 1 && do_sms) { - size = linetabsize(wp, wp->w_topline); + size = linetabsize_eol(wp, wp->w_topline); } } } @@ -1540,7 +1550,7 @@ void adjust_skipcol(void) } validate_virtcol(curwin); - int overlap = sms_marker_overlap(curwin, -1); + int overlap = sms_marker_overlap(curwin, curwin->w_width_inner - width2); while (curwin->w_skipcol > 0 && curwin->w_virtcol < curwin->w_skipcol + overlap + scrolloff_cols) { // scroll a screen line down @@ -1561,8 +1571,7 @@ void adjust_skipcol(void) // Avoid adjusting for 'scrolloff' beyond the text line height. if (scrolloff_cols > 0) { - int size = win_linetabsize(curwin, curwin->w_topline, - ml_get(curwin->w_topline), (colnr_T)MAXCOL); + int size = linetabsize_eol(curwin, curwin->w_topline); size = width1 + width2 * ((size - width1 + width2 - 1) / width2); while (col > size) { col -= width2; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 7d0080622d..0cd60c7dc7 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -5225,7 +5225,7 @@ void nv_g_home_m_cmd(cmdarg_T *cap) // When ending up below 'smoothscroll' marker, move just beyond it so // that skipcol is not adjusted later. if (curwin->w_skipcol > 0 && curwin->w_cursor.lnum == curwin->w_topline) { - int overlap = sms_marker_overlap(curwin, -1); + int overlap = sms_marker_overlap(curwin, curwin->w_width_inner - width2); if (overlap > 0 && i == curwin->w_skipcol) { i += overlap; } diff --git a/src/nvim/plines.c b/src/nvim/plines.c index e084d107a0..4d1cf80e77 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -75,11 +75,19 @@ int linetabsize_col(int startvcol, char *s) /// Return the number of cells line "lnum" of window "wp" will take on the /// screen, taking into account the size of a tab and inline virtual text. +/// Doesn't count the size of 'listchars' "eol". int linetabsize(win_T *wp, linenr_T lnum) { return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum), MAXCOL); } +/// Like linetabsize(), but counts the size of 'listchars' "eol". +int linetabsize_eol(win_T *wp, linenr_T lnum) +{ + return linetabsize(wp, lnum) + + ((wp->w_p_list && wp->w_p_lcs_chars.eol != NUL) ? 1 : 0); +} + static const uint32_t inline_filter[4] = {[kMTMetaInline] = kMTFilterSelect }; /// Prepare the structure passed to charsize functions. diff --git a/src/nvim/plines.h b/src/nvim/plines.h index 50310b8ce1..5e7f485284 100644 --- a/src/nvim/plines.h +++ b/src/nvim/plines.h @@ -73,6 +73,7 @@ static inline int linetabsize_str(char *s) } /// Like linetabsize_str(), but for a given window instead of the current one. +/// Doesn't count the size of 'listchars' "eol". /// /// @param wp /// @param line diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index 55f9a32718..14092757a1 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -1219,4 +1219,59 @@ func Test_smooth_long_scrolloff() call StopVimInTerminal(buf) endfunc +func Test_smoothscroll_listchars_eol() + call NewWindow(10, 40) + setlocal list listchars=eol:$ scrolloff=0 smoothscroll + call setline(1, repeat('-', 40)) + call append(1, repeat(['foobar'], 10)) + + normal! G + call assert_equal(2, line('w0')) + call assert_equal(0, winsaveview().skipcol) + + exe "normal! \<C-Y>" + call assert_equal(1, line('w0')) + call assert_equal(40, winsaveview().skipcol) + + exe "normal! \<C-Y>" + call assert_equal(1, line('w0')) + call assert_equal(0, winsaveview().skipcol) + + exe "normal! \<C-Y>" + call assert_equal(1, line('w0')) + call assert_equal(0, winsaveview().skipcol) + + exe "normal! \<C-E>" + call assert_equal(1, line('w0')) + call assert_equal(40, winsaveview().skipcol) + + exe "normal! \<C-E>" + call assert_equal(2, line('w0')) + call assert_equal(0, winsaveview().skipcol) + + for ve in ['', 'all', 'onemore'] + let &virtualedit = ve + normal! gg + call assert_equal(1, line('w0')) + call assert_equal(0, winsaveview().skipcol) + + exe "normal! \<C-E>" + redraw " redrawing should not cause another scroll + call assert_equal(1, line('w0')) + call assert_equal(40, winsaveview().skipcol) + + exe "normal! \<C-E>" + redraw + call assert_equal(2, line('w0')) + call assert_equal(0, winsaveview().skipcol) + + if ve != 'all' + call assert_equal([0, 2, 1, 0], getpos('.')) + endif + endfor + + set virtualedit& + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab |