diff options
author | Luuk van Baal <luukvbaal@gmail.com> | 2023-04-26 01:55:00 +0200 |
---|---|---|
committer | Luuk van Baal <luukvbaal@gmail.com> | 2023-05-02 13:11:26 +0200 |
commit | be11f80d018797b514ed7d01cde2e4c8f88cc8d2 (patch) | |
tree | e239d828751a6cef04b7906b515e8303891110a5 | |
parent | fba18a3b62310f4535d979a05288101b9af2ef50 (diff) | |
download | rneovim-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.txt | 8 | ||||
-rw-r--r-- | runtime/doc/quickref.txt | 1 | ||||
-rw-r--r-- | runtime/optwin.vim | 3 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 9 | ||||
-rw-r--r-- | src/nvim/drawline.c | 10 | ||||
-rw-r--r-- | src/nvim/move.c | 118 | ||||
-rw-r--r-- | src/nvim/option.c | 15 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/options.lua | 9 | ||||
-rw-r--r-- | test/functional/legacy/scroll_opt_spec.lua | 111 | ||||
-rw-r--r-- | test/old/testdir/test_scroll_opt.vim | 48 |
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 |