aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuuk van Baal <luukvbaal@gmail.com>2023-04-26 01:55:00 +0200
committerLuuk van Baal <luukvbaal@gmail.com>2023-05-02 13:11:26 +0200
commitbe11f80d018797b514ed7d01cde2e4c8f88cc8d2 (patch)
treee239d828751a6cef04b7906b515e8303891110a5
parentfba18a3b62310f4535d979a05288101b9af2ef50 (diff)
downloadrneovim-be11f80d018797b514ed7d01cde2e4c8f88cc8d2.tar.gz
rneovim-be11f80d018797b514ed7d01cde2e4c8f88cc8d2.tar.bz2
rneovim-be11f80d018797b514ed7d01cde2e4c8f88cc8d2.zip
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. https://github.com/vim/vim/commit/f6196f424474e2a9c160f2a995fc2691f82b58f9 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. https://github.com/vim/vim/commit/bbbda8fd81f6d720962b67ae885825bad9be4456 Co-authored-by: Bram Moolenaar <Bram@vim.org>
-rw-r--r--runtime/doc/options.txt8
-rw-r--r--runtime/doc/quickref.txt1
-rw-r--r--runtime/optwin.vim3
-rw-r--r--src/nvim/buffer_defs.h9
-rw-r--r--src/nvim/drawline.c10
-rw-r--r--src/nvim/move.c118
-rw-r--r--src/nvim/option.c15
-rw-r--r--src/nvim/option_defs.h1
-rw-r--r--src/nvim/options.lua9
-rw-r--r--test/functional/legacy/scroll_opt_spec.lua111
-rw-r--r--test/old/testdir/test_scroll_opt.vim48
11 files changed, 311 insertions, 22 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index ba73d79cd3..7cb88441f1 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -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
diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt
index 952f0064e6..c166ecd79d 100644
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -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
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index 0d10ac4758..b7b9c61123 100644
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -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"))
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 02226f3cc4..456acfd798 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -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
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 9c6b5c3b8c..1112bf0463 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -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;
}
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 58f8b1c893..a3c4e18f5d 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -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
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 3264d80a2f..7d705b8502 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -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:
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 40e77550aa..944cc583b3 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -949,6 +949,7 @@ enum {
WV_RLC,
WV_SCBIND,
WV_SCROLL,
+ WV_SMS,
WV_SISO,
WV_SO,
WV_SPELL,
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index e028fbb6a6..c4a85969c0 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -2012,6 +2012,15 @@ return {
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"),
type='number', scope={'buffer'},
diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua
new file mode 100644
index 0000000000..0d0e9640a7
--- /dev/null
+++ b/test/functional/legacy/scroll_opt_spec.lua
@@ -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)
diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim
index 64f4ced470..9375d4ad63 100644
--- a/test/old/testdir/test_scroll_opt.vim
+++ b/test/old/testdir/test_scroll_opt.vim
@@ -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