patch 9.1.1108: 'smoothscroll' gets stuck with 'listchars' "eol"
Problem: 'smoothscroll' gets stuck with 'listchars' "eol".
Solution: Count size of 'listchars' "eol" in line size when scrolling.
(zeertzjq)
related: neovim/neovim#32405
closes: #16627
Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/change.c b/src/change.c
index 72d01c5..fb33971 100644
--- a/src/change.c
+++ b/src/change.c
@@ -572,8 +572,7 @@
&& (last < wp->w_topline
|| (wp->w_topline >= lnum
&& wp->w_topline < lnume
- && win_linetabsize(wp, wp->w_topline,
- ml_get(wp->w_topline), (colnr_T)MAXCOL)
+ && linetabsize_eol(wp, wp->w_topline)
<= wp->w_skipcol + sms_marker_overlap(wp, -1))))
wp->w_skipcol = 0;
diff --git a/src/charset.c b/src/charset.c
index b4bc812..9c68d4a 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -821,6 +821,7 @@
/*
* Like linetabsize_str(), but for a given window instead of the current one.
+ * Doesn't count the size of 'listchars' "eol".
*/
int
win_linetabsize(win_T *wp, linenr_T lnum, char_u *line, colnr_T len)
@@ -836,8 +837,9 @@
/*
* Return the number of cells line "lnum" of window "wp" will take on the
* screen, taking into account the size of a tab and text properties.
+ * Doesn't count the size of 'listchars' "eol".
*/
- int
+ int
linetabsize(win_T *wp, linenr_T lnum)
{
return win_linetabsize(wp, lnum,
@@ -845,6 +847,16 @@
}
/*
+ * 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_lcs_chars.eol != NUL) ? 1 : 0);
+}
+
+/*
* Like linetabsize(), but excludes 'above'/'after'/'right'/'below' aligned
* virtual text, while keeping inline virtual text.
*/
diff --git a/src/misc2.c b/src/misc2.c
index dc0d83f..a2978e5 100644
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -168,7 +168,7 @@
&& wcol >= (colnr_T)width
&& width > 0)
{
- csize = linetabsize(curwin, pos->lnum);
+ csize = linetabsize_eol(curwin, pos->lnum);
if (csize > 0)
csize--;
diff --git a/src/move.c b/src/move.c
index ed252d9..5d34947 100644
--- a/src/move.c
+++ b/src/move.c
@@ -1621,7 +1621,7 @@
*/
static void cursor_correct_sms(void)
{
- if (!curwin->w_p_sms ||!curwin->w_p_wrap
+ if (!curwin->w_p_sms || !curwin->w_p_wrap
|| curwin->w_cursor.lnum != curwin->w_topline)
return;
@@ -1631,8 +1631,7 @@
int so_cols = so == 0 ? 0 : width1 + (so - 1) * width2;
int space_cols = (curwin->w_height - 1) * width2;
int overlap, top, bot;
- int size = so == 0 ? 0 : win_linetabsize(curwin, curwin->w_topline,
- ml_get(curwin->w_topline), (colnr_T)MAXCOL);
+ int size = so == 0 ? 0 : linetabsize_eol(curwin, curwin->w_topline);
if (curwin->w_topline == 1 && curwin->w_skipcol == 0)
so_cols = 0; // Ignore 'scrolloff' at top of buffer.
@@ -1645,10 +1644,10 @@
if (so_cols >= width1 && so_cols > size)
so_cols -= width1;
- // If there is no marker or we have non-zero scrolloff, just ignore it.
- overlap = (curwin->w_skipcol == 0 || so_cols != 0) ? 0
- : sms_marker_overlap(curwin, -1);
- top = curwin->w_skipcol + overlap + so_cols;
+ overlap = curwin->w_skipcol == 0 ? 0
+ : sms_marker_overlap(curwin, curwin->w_width - width2);
+ // If we have non-zero scrolloff, ignore marker overlap.
+ top = curwin->w_skipcol + (so_cols != 0 ? so_cols : overlap);
bot = curwin->w_skipcol + width1 + (curwin->w_height - 1) * width2
- so_cols;
validate_virtcol();
@@ -1667,12 +1666,28 @@
if (col != curwin->w_virtcol)
{
+ int rc;
+
curwin->w_curswant = col;
- coladvance(curwin->w_curswant);
+ rc = 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);
+ if (rc == FAIL && curwin->w_skipcol > 0
+ && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
+ {
+ validate_virtcol();
+ if (curwin->w_virtcol < curwin->w_skipcol + overlap)
+ {
+ // Cursor still not visible: move it to the next line instead.
+ curwin->w_cursor.lnum++;
+ curwin->w_cursor.col = 0;
+ curwin->w_cursor.coladd = 0;
+ curwin->w_curswant = 0;
+ curwin->w_valid &= ~VALID_VIRTCOL;
+ }
+ }
}
}
@@ -1813,8 +1828,7 @@
#endif
if (do_sms)
{
- int size = win_linetabsize(curwin, curwin->w_topline,
- ml_get(curwin->w_topline), (colnr_T)MAXCOL);
+ int size = linetabsize_eol(curwin, curwin->w_topline);
if (size > width1)
{
curwin->w_skipcol = width1;
@@ -1911,7 +1925,7 @@
colnr_T prev_skipcol = curwin->w_skipcol;
if (do_sms)
- size = linetabsize(curwin, curwin->w_topline);
+ size = linetabsize_eol(curwin, curwin->w_topline);
// diff mode: first consume "topfill"
// 'smoothscroll': increase "w_skipcol" until it goes over the end of
@@ -1966,7 +1980,7 @@
# endif
curwin->w_skipcol = 0;
if (todo > 1 && do_sms)
- size = linetabsize(curwin, curwin->w_topline);
+ size = linetabsize_eol(curwin, curwin->w_topline);
}
}
}
@@ -2043,7 +2057,7 @@
}
validate_virtcol();
- overlap = sms_marker_overlap(curwin, -1);
+ overlap = sms_marker_overlap(curwin, curwin->w_width - width2);
while (curwin->w_skipcol > 0
&& curwin->w_virtcol < curwin->w_skipcol + overlap + scrolloff_cols)
{
@@ -2066,8 +2080,7 @@
// 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/normal.c b/src/normal.c
index 6cf2b9d..1189737 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -5764,7 +5764,7 @@
// 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 - width2);
if (overlap > 0 && i == curwin->w_skipcol)
i += overlap;
}
diff --git a/src/proto/charset.pro b/src/proto/charset.pro
index 54af280..bf1fc10 100644
--- a/src/proto/charset.pro
+++ b/src/proto/charset.pro
@@ -22,6 +22,7 @@
int linetabsize_col(int startcol, char_u *s);
int win_linetabsize(win_T *wp, linenr_T lnum, char_u *line, colnr_T len);
int linetabsize(win_T *wp, linenr_T lnum);
+int linetabsize_eol(win_T *wp, linenr_T lnum);
int linetabsize_no_outer(win_T *wp, linenr_T lnum);
void win_linetabsize_cts(chartabsize_T *cts, colnr_T len);
int vim_isIDc(int c);
diff --git a/src/testdir/test_scroll_opt.vim b/src/testdir/test_scroll_opt.vim
index 9c4e29e..7573db9 100644
--- a/src/testdir/test_scroll_opt.vim
+++ b/src/testdir/test_scroll_opt.vim
@@ -1216,4 +1216,59 @@
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
diff --git a/src/version.c b/src/version.c
index 418fcb7..8854292 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1108,
+/**/
1107,
/**/
1106,