patch 9.0.0640: cannot scroll by screen line if a line wraps

Problem:    Cannot scroll by screen line if a line wraps.
Solution:   Add the 'smoothscroll' option.  Only works for CTRL-E and CTRL-Y
            so far.
This commit is contained in:
Bram Moolenaar
2022-10-02 21:29:55 +01:00
parent ff85d4a107
commit f6196f4244
18 changed files with 312 additions and 29 deletions

View File

@ -7302,6 +7302,14 @@ A jump table for the options with a short description can be found at |Q_op|.
reset.
NOTE: This option is reset when 'compatible' is set.
*'smoothscroll'* *'sms'* *'nosmoothscroll'* *'nosms'*
'smoothscroll' 'sms' boolean (default off)
local to window
Scrolling works with screen lines. When 'wrap' is set and the first
line in the window wraps part of it may not be visible, as if it is
above the window.
NOTE: only partly implemented, works with CTRL-E and CTRL-Y.
*'softtabstop'* *'sts'*
'softtabstop' 'sts' number (default 0)
local to buffer

View File

@ -910,6 +910,7 @@ Short explanation of each option: *option-list*
'smartcase' 'scs' no ignore case when pattern has uppercase
'smartindent' 'si' smart autoindenting for C programs
'smarttab' 'sta' use 'shiftwidth' when inserting <Tab>
'smoothscroll' 'sms' scroll by screen lines when 'wrap' is set
'softtabstop' 'sts' number of spaces that <Tab> uses while editing
'spell' enable spell checking
'spellcapcheck' 'spc' pattern to locate end of a sentence

View File

@ -343,6 +343,9 @@ call <SID>Header(gettext("displaying text"))
call <SID>AddOption("scroll", gettext("number of lines to scroll for CTRL-U and CTRL-D"))
call append("$", "\t" .. s:local_to_window)
call <SID>OptionL("scr")
call <SID>AddOption("smoothscroll", gettext("scroll by screen line"))
call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("sms")
call <SID>AddOption("scrolloff", gettext("number of screen lines to show around the cursor"))
call append("$", " \tset so=" . &so)
call <SID>AddOption("wrap", gettext("long lines wrap"))

View File

@ -387,7 +387,7 @@ handle_lnum_col(
}
sprintf((char *)wlv->extra, fmt, number_width(wp), num);
if (wp->w_skipcol > 0)
if (wp->w_skipcol > 0 && wlv->startrow == 0)
for (wlv->p_extra = wlv->extra; *wlv->p_extra == ' ';
++wlv->p_extra)
*wlv->p_extra = '-';
@ -492,7 +492,8 @@ handle_breakindent(win_T *wp, winlinevars_T *wlv)
if (wlv->n_extra < 0)
wlv->n_extra = 0;
}
if (wp->w_skipcol > 0 && wp->w_p_wrap && wp->w_briopt_sbr)
if (wp->w_skipcol > 0 && wlv->startrow == 0
&& wp->w_p_wrap && wp->w_briopt_sbr)
wlv->need_showbreak = FALSE;
// Correct end of highlighted area for 'breakindent',
// required when 'linebreak' is also set.
@ -540,7 +541,7 @@ handle_showbreak_and_filler(win_T *wp, winlinevars_T *wlv)
wlv->c_extra = NUL;
wlv->c_final = NUL;
wlv->n_extra = (int)STRLEN(sbr);
if (wp->w_skipcol == 0 || !wp->w_p_wrap)
if ((wp->w_skipcol == 0 && wlv->startrow == 0) || !wp->w_p_wrap)
wlv->need_showbreak = FALSE;
wlv->vcol_sbr = wlv->vcol + MB_CHARLEN(sbr);
// Correct end of highlighted area for 'showbreak',
@ -750,7 +751,7 @@ draw_screen_line(win_T *wp, winlinevars_T *wlv)
// Highlight 'cursorcolumn' & 'colorcolumn' past end of the line.
if (wp->w_p_wrap)
v = wp->w_skipcol;
v = wlv->startrow == 0 ? wp->w_skipcol : 0;
else
v = wp->w_leftcol;
@ -1411,7 +1412,7 @@ win_line(
// 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the
// first character to be displayed.
if (wp->w_p_wrap)
v = wp->w_skipcol;
v = startrow == 0 ? wp->w_skipcol : 0;
else
v = wp->w_leftcol;
if (v > 0 && !number_only)
@ -3219,9 +3220,8 @@ win_line(
// special character (via 'listchars' option "precedes:<char>".
if (lcs_prec_todo != NUL
&& wp->w_p_list
&& (wp->w_p_wrap ?
(wp->w_skipcol > 0 && wlv.row == 0) :
wp->w_leftcol > 0)
&& (wp->w_p_wrap ? (wp->w_skipcol > 0 && wlv.row == 0)
: wp->w_leftcol > 0)
#ifdef FEAT_DIFF
&& wlv.filler_todo <= 0
#endif

View File

@ -35,6 +35,32 @@ typedef struct
static void topline_back(lineoff_T *lp);
static void botline_forw(lineoff_T *lp);
/*
* Reduce "n" for the screen lines skipped with "wp->w_skipcol".
*/
static int
adjust_plines_for_skipcol(win_T *wp, int n)
{
if (wp->w_skipcol == 0)
return n;
int off = 0;
int width = wp->w_width - win_col_off(wp);
if (wp->w_skipcol >= width)
{
++off;
int skip = wp->w_skipcol - width;
width -= win_col_off2(wp);
while (skip >= width)
{
++off;
skip -= width;
}
}
wp->w_valid &= ~VALID_WROW;
return n - off;
}
/*
* Compute wp->w_botline for the current wp->w_topline. Can be called after
* wp->w_topline changed.
@ -78,12 +104,16 @@ comp_botline(win_T *wp)
}
else
#endif
{
#ifdef FEAT_DIFF
if (lnum == wp->w_topline)
n = plines_win_nofill(wp, lnum, TRUE) + wp->w_topfill;
else
#endif
n = plines_win(wp, lnum, TRUE);
if (lnum == wp->w_topline)
n = adjust_plines_for_skipcol(wp, n);
}
if (
#ifdef FEAT_FOLDING
lnum <= wp->w_cursor.lnum && last >= wp->w_cursor.lnum
@ -778,13 +808,18 @@ curs_rows(win_T *wp)
}
else
#endif
{
int n;
#ifdef FEAT_DIFF
if (lnum == wp->w_topline)
wp->w_cline_row += plines_win_nofill(wp, lnum++, TRUE)
+ wp->w_topfill;
n = plines_win_nofill(wp, lnum, TRUE) + wp->w_topfill;
else
#endif
wp->w_cline_row += plines_win(wp, lnum++, TRUE);
n = plines_win(wp, lnum, TRUE);
if (lnum++ == wp->w_topline)
n = adjust_plines_for_skipcol(wp, n);
wp->w_cline_row += n;
}
}
}
@ -1237,7 +1272,7 @@ curs_columns(
else if (extra < 0)
win_del_lines(curwin, 0, -extra, FALSE, FALSE, 0);
}
else
else if (!curwin->w_p_sms)
curwin->w_skipcol = 0;
if (prev_skipcol != curwin->w_skipcol)
redraw_later(UPD_NOT_VALID);
@ -1422,6 +1457,14 @@ scrolldown(
long done = 0; // total # of physical lines done
int wrow;
int moved = FALSE;
int width1 = 0;
int width2 = 0;
if (curwin->w_p_wrap && curwin->w_p_sms)
{
width1 = curwin->w_width - curwin_col_off();
width2 = width1 - curwin_col_off2();
}
#ifdef FEAT_FOLDING
linenr_T first;
@ -1442,25 +1485,57 @@ scrolldown(
else
#endif
{
if (curwin->w_topline == 1)
if (curwin->w_topline == 1 && curwin->w_skipcol < width1)
break;
--curwin->w_topline;
#ifdef FEAT_DIFF
curwin->w_topfill = 0;
#endif
#ifdef FEAT_FOLDING
// A sequence of folded lines only counts for one logical line
if (hasFolding(curwin->w_topline, &first, NULL))
if (curwin->w_p_wrap && curwin->w_p_sms
&& curwin->w_skipcol >= width1)
{
if (curwin->w_skipcol >= width1 + width2)
curwin->w_skipcol -= width2;
else
curwin->w_skipcol -= width1;
redraw_later(UPD_NOT_VALID);
++done;
if (!byfold)
line_count -= curwin->w_topline - first - 1;
curwin->w_botline -= curwin->w_topline - first;
curwin->w_topline = first;
}
else
{
--curwin->w_topline;
curwin->w_skipcol = 0;
#ifdef FEAT_DIFF
curwin->w_topfill = 0;
#endif
done += PLINES_NOFILL(curwin->w_topline);
#ifdef FEAT_FOLDING
// A sequence of folded lines only counts for one logical line
if (hasFolding(curwin->w_topline, &first, NULL))
{
++done;
if (!byfold)
line_count -= curwin->w_topline - first - 1;
curwin->w_botline -= curwin->w_topline - first;
curwin->w_topline = first;
}
else
#endif
if (curwin->w_p_wrap && curwin->w_p_sms)
{
int size = win_linetabsize(curwin, curwin->w_topline,
ml_get(curwin->w_topline), (colnr_T)MAXCOL);
if (size > width1)
{
curwin->w_skipcol = width1;
size -= width1;
redraw_later(UPD_NOT_VALID);
}
while (size > width2)
{
curwin->w_skipcol += width2;
size -= width2;
}
++done;
}
else
done += PLINES_NOFILL(curwin->w_topline);
}
}
--curwin->w_botline; // approximate w_botline
invalidate_botline();
@ -1565,6 +1640,41 @@ scrollup(
}
else
#endif
if (curwin->w_p_wrap && curwin->w_p_sms)
{
int off1 = curwin_col_off();
int off2 = off1 + curwin_col_off2();
int add;
int size = win_linetabsize(curwin, curwin->w_topline,
ml_get(curwin->w_topline), (colnr_T)MAXCOL);
linenr_T prev_topline = curwin->w_topline;
// 'smoothscroll': increase "w_skipcol" until it goes over the end of
// the line, then advance to the next line.
for (int todo = line_count; todo > 0; --todo)
{
add = curwin->w_width - (curwin->w_skipcol > 0 ? off2 : off1);
curwin->w_skipcol += add;
if (curwin->w_skipcol >= size)
{
if (curwin->w_topline == curbuf->b_ml.ml_line_count)
{
curwin->w_skipcol -= add;
break;
}
++curwin->w_topline;
++curwin->w_botline; // approximate w_botline
curwin->w_skipcol = 0;
if (todo > 1)
size = win_linetabsize(curwin, curwin->w_topline,
ml_get(curwin->w_topline), (colnr_T)MAXCOL);
}
}
if (curwin->w_topline == prev_topline)
// need to redraw even though w_topline didn't change
redraw_later(UPD_NOT_VALID);
}
else
{
curwin->w_topline += line_count;
curwin->w_botline += line_count; // approximate w_botline

View File

@ -2964,6 +2964,15 @@ set_bool_option(
}
#endif
else if ((int *)varp == &curwin->w_p_sms)
{
if (!curwin->w_p_sms)
{
curwin->w_skipcol = 0;
changed_line_abv_curs();
}
}
// when 'textmode' is set or reset also change 'fileformat'
else if ((int *)varp == &curbuf->b_p_tx)
{
@ -5436,6 +5445,7 @@ get_varp(struct vimoption *p)
case PV_RLC: return (char_u *)&(curwin->w_p_rlc);
#endif
case PV_SCROLL: return (char_u *)&(curwin->w_p_scr);
case PV_SMS: return (char_u *)&(curwin->w_p_sms);
case PV_WRAP: return (char_u *)&(curwin->w_p_wrap);
#ifdef FEAT_LINEBREAK
case PV_LBR: return (char_u *)&(curwin->w_p_lbr);

View File

@ -194,6 +194,7 @@
#endif
#define PV_SCBIND OPT_WIN(WV_SCBIND)
#define PV_SCROLL OPT_WIN(WV_SCROLL)
#define PV_SMS OPT_WIN(WV_SMS)
#define PV_SISO OPT_BOTH(OPT_WIN(WV_SISO))
#define PV_SO OPT_BOTH(OPT_WIN(WV_SO))
#ifdef FEAT_SPELL
@ -2282,6 +2283,9 @@ static struct vimoption options[] =
{"smarttab", "sta", P_BOOL|P_VI_DEF|P_VIM,
(char_u *)&p_sta, PV_NONE,
{(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
{"smoothscroll", "sms", P_BOOL|P_VI_DEF|P_RWIN,
(char_u *)VAR_WIN, PV_SMS,
{(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
{"softtabstop", "sts", P_NUM|P_VI_DEF|P_VIM,
(char_u *)&p_sts, PV_STS,
{(char_u *)0L, (char_u *)0L} SCTX_INIT},

View File

@ -262,6 +262,8 @@ typedef struct
#endif
long wo_scr;
#define w_p_scr w_onebuf_opt.wo_scr // 'scroll'
int wo_sms;
#define w_p_sms w_onebuf_opt.wo_sms // 'smoothscroll'
#ifdef FEAT_SPELL
int wo_spell;
# define w_p_spell w_onebuf_opt.wo_spell // 'spell'
@ -3592,11 +3594,12 @@ struct window_S
// below w_topline (at end of file)
int w_old_botfill; // w_botfill at last redraw
#endif
colnr_T w_leftcol; // window column number of the left most
colnr_T w_leftcol; // screen column number of the left most
// character in the window; used when
// 'wrap' is off
colnr_T w_skipcol; // starting column when a single line
// doesn't fit in the window
colnr_T w_skipcol; // starting screen column for the first
// line in the window; used when 'wrap' is
// on
int w_empty_rows; // number of ~ rows in window
#ifdef FEAT_DIFF

View File

@ -0,0 +1,12 @@
|w+0&#ffffff0|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d|
|w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d|
|w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| @20
|l|i|n|e| |t|h|r|e@1| @29
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d|
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| @10
>l|i|n|e| @35
|l|i|n|e| @35
|l|i|n|e| @35
|~+0#4040ff13&| @38
|~| @38
| +0#0000000&@21|5|,|1| @10|B|o|t|

View File

@ -0,0 +1,12 @@
|w+0&#ffffff0|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d|
|w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| @20
|l|i|n|e| |t|h|r|e@1| @29
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d|
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| @10
>l|i|n|e| @35
|l|i|n|e| @35
|l|i|n|e| @35
|~+0#4040ff13&| @38
|~| @38
|~| @38
| +0#0000000&@21|5|,|1| @10|B|o|t|

View File

@ -0,0 +1,12 @@
|w+0&#ffffff0|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| @20
|l|i|n|e| |t|h|r|e@1| @29
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d|
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| @10
>l|i|n|e| @35
|l|i|n|e| @35
|l|i|n|e| @35
|~+0#4040ff13&| @38
|~| @38
|~| @38
|~| @38
| +0#0000000&@21|5|,|1| @10|B|o|t|

View File

@ -0,0 +1,12 @@
|l+0&#ffffff0|i|n|e| |t|h|r|e@1| @29
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d|
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| @10
|l|i|n|e| @35
|l|i|n|e| @35
>l|i|n|e| @35
|~+0#4040ff13&| @38
|~| @38
|~| @38
|~| @38
|~| @38
| +0#0000000&@21|7|,|1| @10|B|o|t|

View File

@ -0,0 +1,12 @@
|w+0&#ffffff0|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| @20
|l|i|n|e| |t|h|r|e@1| @29
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d|
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| @10
|l|i|n|e| @35
|l|i|n|e| @35
>l|i|n|e| @35
|~+0#4040ff13&| @38
|~| @38
|~| @38
|~| @38
| +0#0000000&@21|7|,|1| @10|B|o|t|

View File

@ -0,0 +1,12 @@
|w+0&#ffffff0|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d|
|w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| @20
|l|i|n|e| |t|h|r|e@1| @29
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d|
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| @10
|l|i|n|e| @35
|l|i|n|e| @35
>l|i|n|e| @35
|~+0#4040ff13&| @38
|~| @38
|~| @38
| +0#0000000&@21|7|,|1| @10|B|o|t|

View File

@ -0,0 +1,12 @@
|w+0&#ffffff0|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d|
|w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d|
|w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| @20
|l|i|n|e| |t|h|r|e@1| @29
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d|
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| @10
|l|i|n|e| @35
|l|i|n|e| @35
>l|i|n|e| @35
|~+0#4040ff13&| @38
|~| @38
| +0#0000000&@21|7|,|1| @10|B|o|t|

View File

@ -0,0 +1,12 @@
|l+0&#ffffff0|i|n|e| |o|n|e| @31
|w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d|
|w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d|
|w|o|r|d| |w|o|r|d| |w|o|r|d| |w|o|r|d| @20
|l|i|n|e| |t|h|r|e@1| @29
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d|
|l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| |l|o|n|g| |w|o|r|d| @10
|l|i|n|e| @35
|l|i|n|e| @35
>l|i|n|e| @35
|~+0#4040ff13&| @38
| +0#0000000&@21|7|,|1| @10|A|l@1|

View File

@ -1,4 +1,7 @@
" Test for reset 'scroll'
" Test for reset 'scroll' and 'smoothscroll'
source check.vim
source screendump.vim
func Test_reset_scroll()
let scr = &l:scroll
@ -34,4 +37,47 @@ func Test_reset_scroll()
quit!
endfunc
func Test_smoothscroll_CtrlE_CtrlY()
CheckScreendump
let lines =<< trim END
vim9script
setline(1, [
'line one',
'word '->repeat(20),
'line three',
'long word '->repeat(7),
'line',
'line',
'line',
])
set smoothscroll
:5
END
call writefile(lines, 'XSmoothScroll', 'D')
let buf = RunVimInTerminal('-S XSmoothScroll', #{rows: 12, cols: 40})
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smoothscroll_1', {})
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smoothscroll_2', {})
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smoothscroll_3', {})
call term_sendkeys(buf, "\<C-E>")
call VerifyScreenDump(buf, 'Test_smoothscroll_4', {})
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smoothscroll_5', {})
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smoothscroll_6', {})
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smoothscroll_7', {})
call term_sendkeys(buf, "\<C-Y>")
call VerifyScreenDump(buf, 'Test_smoothscroll_8', {})
call StopVimInTerminal(buf)
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -699,6 +699,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
640,
/**/
639,
/**/