vim-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.

f6196f4244

vim-patch:9.0.0641: missing part of the new option code

Problem:    Missing part of the new option code.
Solution:   Add missing WV_SMS.

bbbda8fd81

Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
Luuk van Baal
2023-04-26 01:55:00 +02:00
parent fba18a3b62
commit be11f80d01
11 changed files with 311 additions and 22 deletions

View File

@ -5652,6 +5652,14 @@ A jump table for the options with a short description can be found at |Q_op|.
option. Also see |ins-expandtab|. When 'expandtab' is not set, the
number of spaces is minimized by using <Tab>s.
*'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

@ -870,6 +870,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

@ -305,6 +305,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

@ -192,6 +192,8 @@ typedef struct {
#define w_p_rlc w_onebuf_opt.wo_rlc // 'rightleftcmd'
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'
int wo_spell;
#define w_p_spell w_onebuf_opt.wo_spell // 'spell'
int wo_cuc;
@ -1163,11 +1165,12 @@ struct window_S {
bool w_botfill; // true when filler lines are actually
// below w_topline (at end of file)
bool w_old_botfill; // w_botfill at last redraw
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
// six fields that are only used when there is a WinScrolled autocommand
linenr_T w_last_topline; ///< last known value for w_topline

View File

@ -607,7 +607,7 @@ static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int num_signs, int si
// Draw the line number (empty space after wrapping).
if (wlv->row == wlv->startrow + wlv->filler_lines) {
get_line_number_str(wp, wlv->lnum, wlv->extra, sizeof(wlv->extra));
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 = '-';
}
@ -754,7 +754,7 @@ static void handle_breakindent(win_T *wp, winlinevars_T *wlv)
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',
@ -804,7 +804,7 @@ static void handle_showbreak_and_filler(win_T *wp, winlinevars_T *wlv)
wlv->c_final = NUL;
wlv->n_extra = (int)strlen(sbr);
wlv->char_attr = win_hl_attr(wp, HLF_AT);
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);
@ -1379,7 +1379,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
// '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;
}
@ -2595,7 +2595,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
if (c == NUL) {
// 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;
}

View File

@ -57,6 +57,28 @@ typedef struct {
# include "move.c.generated.h"
#endif
/// 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.
static void comp_botline(win_T *wp)
@ -79,6 +101,9 @@ static void comp_botline(win_T *wp)
linenr_T last = lnum;
bool folded;
int n = plines_win_full(wp, lnum, &last, &folded, true);
if (lnum == wp->w_topline) {
n = adjust_plines_for_skipcol(wp, n);
}
if (lnum <= wp->w_cursor.lnum && last >= wp->w_cursor.lnum) {
wp->w_cline_row = done;
wp->w_cline_height = n;
@ -565,6 +590,9 @@ static void curs_rows(win_T *wp)
linenr_T last = lnum;
bool folded;
int n = plines_win_full(wp, lnum, &last, &folded, false);
if (lnum == wp->w_topline) {
n = adjust_plines_for_skipcol(wp, n);
}
lnum = last + 1;
if (folded && lnum > wp->w_cursor.lnum) {
break;
@ -907,7 +935,7 @@ void curs_columns(win_T *wp, int may_scroll)
// extra could be either positive or negative
extra = ((int)prev_skipcol - (int)wp->w_skipcol) / width;
win_scroll_lines(wp, 0, extra);
} else {
} else if (!wp->w_p_sms) {
wp->w_skipcol = 0;
}
if (prev_skipcol != wp->w_skipcol) {
@ -1064,6 +1092,13 @@ void f_virtcol2col(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
bool scrolldown(long line_count, int byfold)
{
int done = 0; // total # of physical lines done
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();
}
// Make sure w_topline is at the first of a sequence of folded lines.
(void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
@ -1074,22 +1109,48 @@ bool scrolldown(long line_count, int byfold)
curwin->w_topfill++;
done++;
} else {
if (curwin->w_topline == 1) {
if (curwin->w_topline == 1 && curwin->w_skipcol < width1) {
break;
}
curwin->w_topline--;
curwin->w_topfill = 0;
// A sequence of folded lines only counts for one logical line
linenr_T first;
if (hasFolding(curwin->w_topline, &first, NULL)) {
done++;
if (!byfold) {
line_count -= curwin->w_topline - first - 1;
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;
}
curwin->w_botline -= curwin->w_topline - first;
curwin->w_topline = first;
redraw_later(curwin, UPD_NOT_VALID);
done++;
} else {
done += plines_win_nofill(curwin, curwin->w_topline, true);
curwin->w_topline--;
curwin->w_skipcol = 0;
curwin->w_topfill = 0;
// A sequence of folded lines only counts for one logical line
linenr_T first;
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 {
if (curwin->w_p_wrap && curwin->w_p_sms) {
int size = (int)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(curwin, UPD_NOT_VALID);
}
while (size > width2) {
curwin->w_skipcol += width2;
size -= width2;
}
done++;
} else {
done += plines_win_nofill(curwin, curwin->w_topline, true);
}
}
}
}
curwin->w_botline--; // approximate w_botline
@ -1167,6 +1228,37 @@ bool scrollup(long line_count, int byfold)
// approximate w_botline
curwin->w_botline += lnum - curwin->w_topline;
curwin->w_topline = lnum;
} else if (curwin->w_p_wrap && curwin->w_p_sms) {
int off1 = curwin_col_off();
int off2 = off1 + curwin_col_off2();
int add;
int size = (int)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 (long 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 = (int)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(curwin, UPD_NOT_VALID);
}
} else {
curwin->w_topline += (linenr_T)line_count;
curwin->w_botline += (linenr_T)line_count; // approximate w_botline

View File

@ -2621,6 +2621,19 @@ static const char *did_set_showtabline(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
/// Process the updated 'smoothscroll' option value.
static const char *did_set_smoothscroll(optset_T *args FUNC_ATTR_UNUSED)
{
win_T *win = (win_T *)args->os_win;
if (win->w_p_sms) {
return NULL;
}
win->w_skipcol = 0;
changed_line_abv_curs_win(win);
return NULL;
}
/// Process the new 'foldlevel' option value.
static const char *did_set_foldlevel(optset_T *args FUNC_ATTR_UNUSED)
{
@ -4417,6 +4430,8 @@ static char *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
return (char *)&(win->w_p_rlc);
case PV_SCROLL:
return (char *)&(win->w_p_scr);
case PV_SMS:
return (char *)&(win->w_p_sms);
case PV_WRAP:
return (char *)&(win->w_p_wrap);
case PV_LBR:

View File

@ -949,6 +949,7 @@ enum {
WV_RLC,
WV_SCBIND,
WV_SCROLL,
WV_SMS,
WV_SISO,
WV_SO,
WV_SPELL,

View File

@ -2011,6 +2011,15 @@ return {
pv_name='p_scroll',
defaults={if_true=0}
},
{
full_name='smoothscroll', abbreviation='sms',
short_desc=N_("scroll by screen lines when 'wrap' is set"),
type='bool', scope={'window'},
pv_name='p_sms',
redraw={'current_window'},
defaults={if_true=0},
cb='did_set_smoothscroll'
},
{
full_name='scrollback', abbreviation='scbk',
short_desc=N_("lines to scroll with CTRL-U and CTRL-D"),

View File

@ -0,0 +1,111 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local exec = helpers.exec
local feed = helpers.feed
before_each(clear)
describe('smoothscroll', function()
local screen
before_each(function()
screen = Screen.new(40, 12)
screen:attach()
end)
-- oldtest: Test_smoothscroll_CtrlE_CtrlY()
it('works with <C-E> and <C-E>', function()
exec([[
call setline(1, [ 'line one', 'word '->repeat(20), 'line three', 'long word '->repeat(7), 'line', 'line', 'line', ])
set smoothscroll
:5
]])
local s0 = [[
line one |
word word word word word word word word |
word word word word word word word word |
word word word word |
line three |
long word long word long word long word |
long word long word long word |
^line |
line |
line |
~ |
|
]]
local s1 = [[
word word word word word word word word |
word word word word word word word word |
word word word word |
line three |
long word long word long word long word |
long word long word long word |
^line |
line |
line |
~ |
~ |
|
]]
local s2 = [[
word word word word word word word word |
word word word word |
line three |
long word long word long word long word |
long word long word long word |
^line |
line |
line |
~ |
~ |
~ |
|
]]
local s3 = [[
word word word word |
line three |
long word long word long word long word |
long word long word long word |
^line |
line |
line |
~ |
~ |
~ |
~ |
|
]]
local s4 = [[
line three |
long word long word long word long word |
long word long word long word |
^line |
line |
line |
~ |
~ |
~ |
~ |
~ |
|
]]
feed('<C-E>')
screen:expect(s1)
feed('<C-E>')
screen:expect(s2)
feed('<C-E>')
screen:expect(s3)
feed('<C-E>')
screen:expect(s4)
feed('<C-Y>')
screen:expect(s3)
feed('<C-Y>')
screen:expect(s2)
feed('<C-Y>')
screen:expect(s1)
feed('<C-Y>')
screen:expect(s0)
end)
end)

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
@ -51,4 +54,47 @@ func Test_scolloff_even_line_count()
bwipe!
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