From be11f80d018797b514ed7d01cde2e4c8f88cc8d2 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 01:55:00 +0200 Subject: [PATCH 01/45] 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 --- runtime/doc/options.txt | 8 ++ runtime/doc/quickref.txt | 1 + runtime/optwin.vim | 3 + src/nvim/buffer_defs.h | 9 +- src/nvim/drawline.c | 10 +- src/nvim/move.c | 118 ++++++++++++++++++--- src/nvim/option.c | 15 +++ src/nvim/option_defs.h | 1 + src/nvim/options.lua | 9 ++ test/functional/legacy/scroll_opt_spec.lua | 111 +++++++++++++++++++ test/old/testdir/test_scroll_opt.vim | 48 ++++++++- 11 files changed, 311 insertions(+), 22 deletions(-) create mode 100644 test/functional/legacy/scroll_opt_spec.lua 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 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 +'smoothscroll' 'sms' scroll by screen lines when 'wrap' is set 'softtabstop' 'sts' number of spaces that 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 Header(gettext("displaying text")) call AddOption("scroll", gettext("number of lines to scroll for CTRL-U and CTRL-D")) call append("$", "\t" .. s:local_to_window) call OptionL("scr") +call AddOption("smoothscroll", gettext("scroll by screen line")) +call append("$", "\t" .. s:local_to_window) +call BinOptionL("sms") call AddOption("scrolloff", gettext("number of screen lines to show around the cursor")) call append("$", " \tset so=" . &so) call 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 @@ -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"), 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 and ', 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('') + screen:expect(s1) + feed('') + screen:expect(s2) + feed('') + screen:expect(s3) + feed('') + screen:expect(s4) + feed('') + screen:expect(s3) + feed('') + screen:expect(s2) + feed('') + screen:expect(s1) + feed('') + 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, "\") + call VerifyScreenDump(buf, 'Test_smoothscroll_1', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smoothscroll_2', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smoothscroll_3', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smoothscroll_4', {}) + + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smoothscroll_5', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smoothscroll_6', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smoothscroll_7', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smoothscroll_8', {}) + + call StopVimInTerminal(buf) +endfunc + + + " vim: shiftwidth=2 sts=2 expandtab From 7cc3062e9c13ea13986654119278997d8971baec Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 03:45:10 +0200 Subject: [PATCH 02/45] vim-patch:9.0.0642: breakindent test fails Problem: Breakindent test fails. Solution: Correct logic for resetting need_showbreak. https://github.com/vim/vim/commit/693729ae58bd30fc1a4c08042ebe9923b45f5763 Co-authored-by: Bram Moolenaar --- src/nvim/drawline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 1112bf0463..45cfbb6d47 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -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 && wlv->startrow == 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); From e51e63c9bae88bb2d4da669c667e8dbee3ec3562 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 03:48:02 +0200 Subject: [PATCH 03/45] vim-patch:9.0.0643: smoothscroll test fails Problem: Smoothscroll test fails. Solution: Check if skipcol changed. https://github.com/vim/vim/commit/b34c4b7863af8718ad726173585dd38a7c292f0f Co-authored-by: Bram Moolenaar --- src/nvim/normal.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 88741e1527..a51bde967e 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2633,6 +2633,7 @@ static void nv_scroll_line(cmdarg_T *cap) void scroll_redraw(int up, long count) { linenr_T prev_topline = curwin->w_topline; + int prev_skipcol = curwin->w_skipcol; int prev_topfill = curwin->w_topfill; linenr_T prev_lnum = curwin->w_cursor.lnum; @@ -2651,6 +2652,7 @@ void scroll_redraw(int up, long count) // we get stuck at one position. Don't move the cursor up if the // first line of the buffer is already on the screen while (curwin->w_topline == prev_topline + && curwin->w_skipcol == prev_skipcol && curwin->w_topfill == prev_topfill) { if (up) { if (curwin->w_cursor.lnum > prev_lnum From 9128fc79f0630e0d1e5bedf9bd32417ecd42c4f5 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 03:57:23 +0200 Subject: [PATCH 04/45] vim-patch:9.0.0644: 'smoothscroll' is not copied to a new window on :split Problem: 'smoothscroll' is not copied to a new window on :split. Solution: Copy the option value. Add a test. https://github.com/vim/vim/commit/b1fd26d208aadc96d3e8b9215f761150f40a9f91 Co-authored-by: Bram Moolenaar --- src/nvim/option.c | 1 + test/old/testdir/test_options.vim | 104 +++++++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/nvim/option.c b/src/nvim/option.c index 7d705b8502..a977fc4f86 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4663,6 +4663,7 @@ void copy_winopt(winopt_T *from, winopt_T *to) to->wo_briopt = copy_option_val(from->wo_briopt); to->wo_scb = from->wo_scb; to->wo_scb_save = from->wo_scb_save; + to->wo_sms = from->wo_sms; to->wo_crb = from->wo_crb; to->wo_crb_save = from->wo_crb_save; to->wo_spell = from->wo_spell; diff --git a/test/old/testdir/test_options.vim b/test/old/testdir/test_options.vim index f101f550d1..8fc86a99e3 100644 --- a/test/old/testdir/test_options.vim +++ b/test/old/testdir/test_options.vim @@ -721,7 +721,7 @@ func Test_backupskip() let &backupskip = backupskip endfunc -func Test_copy_winopt() +func Test_buf_copy_winopt() set hidden " Test copy option from current buffer in window @@ -775,6 +775,108 @@ func Test_copy_winopt() set hidden& endfunc +func Test_split_copy_options() + let values = [ + \['cursorbind', 1, 0], + \['fillchars', '"vert:-"', '"' .. &fillchars .. '"'], + \['list', 1, 0], + \['listchars', '"space:-"', '"' .. &listchars .. '"'], + \['number', 1, 0], + \['relativenumber', 1, 0], + \['scrollbind', 1, 0], + \['smoothscroll', 1, 0], + \['virtualedit', '"block"', '"' .. &virtualedit .. '"'], + "\ ['wincolor', '"Search"', '"' .. &wincolor .. '"'], + \['wrap', 0, 1], + \] + if has('linebreak') + let values += [ + \['breakindent', 1, 0], + \['breakindentopt', '"min:5"', '"' .. &breakindentopt .. '"'], + \['linebreak', 1, 0], + \['numberwidth', 7, 4], + \['showbreak', '"++"', '"' .. &showbreak .. '"'], + \] + endif + if has('rightleft') + let values += [ + \['rightleft', 1, 0], + \['rightleftcmd', '"search"', '"' .. &rightleftcmd .. '"'], + \] + endif + if has('statusline') + let values += [ + \['statusline', '"---%f---"', '"' .. &statusline .. '"'], + \] + endif + if has('spell') + let values += [ + \['spell', 1, 0], + \] + endif + if has('syntax') + let values += [ + \['cursorcolumn', 1, 0], + \['cursorline', 1, 0], + \['cursorlineopt', '"screenline"', '"' .. &cursorlineopt .. '"'], + \['colorcolumn', '"+1"', '"' .. &colorcolumn .. '"'], + \] + endif + if has('diff') + let values += [ + \['diff', 1, 0], + \] + endif + if has('conceal') + let values += [ + \['concealcursor', '"nv"', '"' .. &concealcursor .. '"'], + \['conceallevel', '3', &conceallevel], + \] + endif + if has('terminal') + let values += [ + \['termwinkey', '""', '"' .. &termwinkey .. '"'], + \['termwinsize', '"10x20"', '"' .. &termwinsize .. '"'], + \] + endif + if has('folding') + let values += [ + \['foldcolumn', '"5"', &foldcolumn], + \['foldenable', 0, 1], + \['foldexpr', '"2 + 3"', '"' .. &foldexpr .. '"'], + \['foldignore', '"+="', '"' .. &foldignore .. '"'], + \['foldlevel', 4, &foldlevel], + \['foldmarker', '">>,<<"', '"' .. &foldmarker .. '"'], + \['foldmethod', '"marker"', '"' .. &foldmethod .. '"'], + \['foldminlines', 3, &foldminlines], + \['foldnestmax', 17, &foldnestmax], + \['foldtext', '"closed"', '"' .. &foldtext .. '"'], + \] + endif + if has('signs') + let values += [ + \['signcolumn', '"number"', '"' .. &signcolumn .. '"'], + \] + endif + + " set options to non-default value + for item in values + exe $"let &{item[0]} = {item[1]}" + endfor + + " check values are set in new window + split + for item in values + exe $'call assert_equal({item[1]}, &{item[0]}, "{item[0]}")' + endfor + + " restore + close + for item in values + exe $"let &{item[0]} = {item[1]}" + endfor +endfunc + func Test_shortmess_F() new call assert_match('\[No Name\]', execute('file')) From 69af5e8782e601fe9c1e39adf49ce16728719a73 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 04:00:38 +0200 Subject: [PATCH 05/45] vim-patch:9.0.0645: CTRL-Y does not stop at line 1 Problem: CTRL-Y does not stop at line 1. (John Marriott) Solution: Stop at line 1 when 'smoothscroll' is not set. (closes vim/vim#11261) https://github.com/vim/vim/commit/8df9748edb2ac8bd025e34e06194ac210667c97a Co-authored-by: Bram Moolenaar --- src/nvim/move.c | 5 ++++- test/functional/legacy/scroll_opt_spec.lua | 13 +++++++++++++ test/old/testdir/test_scroll_opt.vim | 13 +++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index a3c4e18f5d..75f5c6438b 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1109,10 +1109,12 @@ bool scrolldown(long line_count, int byfold) curwin->w_topfill++; done++; } else { - if (curwin->w_topline == 1 && curwin->w_skipcol < width1) { + // break when at the very top + if (curwin->w_topline == 1 && (!curwin->w_p_sms || curwin->w_skipcol < width1)) { break; } if (curwin->w_p_wrap && curwin->w_p_sms && curwin->w_skipcol >= width1) { + // scroll a screen line down if (curwin->w_skipcol >= width1 + width2) { curwin->w_skipcol -= width2; } else { @@ -1121,6 +1123,7 @@ bool scrolldown(long line_count, int byfold) redraw_later(curwin, UPD_NOT_VALID); done++; } else { + // scroll a text line down curwin->w_topline--; curwin->w_skipcol = 0; curwin->w_topfill = 0; diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 0d0e9640a7..14dbe0caf9 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -14,6 +14,19 @@ describe('smoothscroll', function() screen:attach() end) + -- oldtest: Test_CtrlE_CtrlY_stop_at_end() + it('disabled does not break and stop at end', function() + exec([[ + enew + call setline(1, ['one', 'two']) + set number + ]]) + feed('') + screen:expect({any = " 1 ^one"}) + feed('') + screen:expect({any = " 2 ^two"}) + end) + -- oldtest: Test_smoothscroll_CtrlE_CtrlY() it('works with and ', function() exec([[ diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index 9375d4ad63..ed3ffd83e7 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -54,6 +54,19 @@ func Test_scolloff_even_line_count() bwipe! endfunc +func Test_CtrlE_CtrlY_stop_at_end() + enew + call setline(1, ['one', 'two']) + set number + exe "normal \" + call assert_equal([" 1 one "], ScreenLines(1, 10)) + exe "normal \\\" + call assert_equal([" 2 two "], ScreenLines(1, 10)) + + bwipe! + set nonumber +endfunc + func Test_smoothscroll_CtrlE_CtrlY() CheckScreendump From d6050e9bda7f8b080c577100ae94e017dc146c88 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 04:32:50 +0200 Subject: [PATCH 06/45] vim-patch:9.0.0646: with 'smoothscroll' CTRL-E is wrong when 'foldmethod' set Problem: with 'smoothscroll' set CTRL-E does not work properly when 'foldmethod' is set to "indent". (Yee Cheng Chin) Solution: Merge the code for scroling with folds and 'smoothscroll'. (closes vim/vim#11262) https://github.com/vim/vim/commit/6b2d4ff7148e0b416ba745d20d061e6f7bb53ee7 Co-authored-by: Bram Moolenaar --- src/nvim/move.c | 91 ++++++++++++---------- test/functional/legacy/scroll_opt_spec.lua | 88 ++++++++++++++++----- test/old/testdir/test_scroll_opt.vim | 15 ++++ 3 files changed, 136 insertions(+), 58 deletions(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index 75f5c6438b..3b882a5d02 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -707,8 +707,8 @@ int curwin_col_off(void) } // Return the difference in column offset for the second screen line of a -// wrapped line. It's 8 if 'number' or 'relativenumber' is on and 'n' is in -// 'cpoptions'. +// wrapped line. It's positive if 'number' or 'relativenumber' is on and 'n' +// is in 'cpoptions'. int win_col_off2(win_T *wp) { if ((wp->w_p_nu || wp->w_p_rnu) && vim_strchr(p_cpo, CPO_NUMCOL) != NULL) { @@ -1097,7 +1097,7 @@ bool scrolldown(long line_count, int byfold) if (curwin->w_p_wrap && curwin->w_p_sms) { width1 = curwin->w_width - curwin_col_off(); - width2 = width1 - curwin_col_off2(); + width2 = width1 + curwin_col_off2(); } // Make sure w_topline is at the first of a sequence of folded lines. @@ -1209,55 +1209,66 @@ bool scrollup(long line_count, int byfold) { linenr_T topline = curwin->w_topline; linenr_T botline = curwin->w_botline; + int do_smoothscroll = curwin->w_p_wrap && curwin->w_p_sms; - if ((byfold && hasAnyFolding(curwin)) - || win_may_fill(curwin)) { - // count each sequence of folded lines as one logical line - linenr_T lnum = curwin->w_topline; - while (line_count--) { + if (do_smoothscroll || (byfold && hasAnyFolding(curwin)) || win_may_fill(curwin)) { + int width1 = curwin->w_width - curwin_col_off(); + int width2 = width1 + curwin_col_off2(); + int size = 0; + linenr_T prev_topline = curwin->w_topline; + + if (do_smoothscroll) { + size = (int)win_linetabsize(curwin, curwin->w_topline, + ml_get(curwin->w_topline), (colnr_T)MAXCOL); + } + + // diff mode: first consume "topfill" + // 'smoothscroll': increase "w_skipcol" until it goes over the end of + // the line, then advance to the next line. + // folding: count each sequence of folded lines as one logical line. + for (long todo = line_count; todo > 0; todo--) { if (curwin->w_topfill > 0) { curwin->w_topfill--; } else { + linenr_T lnum = curwin->w_topline; if (byfold) { + // for a closed fold: go to the last line in the fold (void)hasFolding(lnum, NULL, &lnum); } - if (lnum >= curbuf->b_ml.ml_line_count) { - break; + if (lnum == curwin->w_topline && curwin->w_p_wrap && curwin->w_p_sms) { + // 'smoothscroll': increase "w_skipcol" until it goes over + // the end of the line, then advance to the next line. + int add = curwin->w_skipcol > 0 ? width2 : width1; + curwin->w_skipcol += add; + if (curwin->w_skipcol >= size) { + if (lnum == curbuf->b_ml.ml_line_count) { + // at the last screen line, can't scroll further + curwin->w_skipcol -= add; + break; + } + lnum++; + } + } else { + if (lnum >= curbuf->b_ml.ml_line_count) { + break; + } + lnum++; } - lnum++; - curwin->w_topfill = win_get_fill(curwin, lnum); - } - } - // 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 (lnum > curwin->w_topline) { + // approximate w_botline + curwin->w_botline += lnum - curwin->w_topline; + curwin->w_topline = lnum; + curwin->w_topfill = win_get_fill(curwin, lnum); + curwin->w_skipcol = 0; + if (todo > 1 && do_smoothscroll) { + 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); diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 14dbe0caf9..20976089e2 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -31,23 +31,9 @@ describe('smoothscroll', function() it('works with and ', function() exec([[ call setline(1, [ 'line one', 'word '->repeat(20), 'line three', 'long word '->repeat(7), 'line', 'line', 'line', ]) - set smoothscroll + set smoothscroll scrolloff=5 :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 | @@ -94,13 +80,69 @@ describe('smoothscroll', function() line three | long word long word long word long word | long word long word long word | + line | + line | ^line | + ~ | + ~ | + ~ | + ~ | + ~ | + | + ]] + local s5 = [[ + word word word word | + line three | + long word long word long word long word | + long word long word long word | line | line | + ^line | ~ | ~ | ~ | ~ | + | + ]] + local s6 = [[ + 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 s7 = [[ + 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 s8 = [[ + 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 | ~ | | ]] @@ -113,12 +155,22 @@ describe('smoothscroll', function() feed('') screen:expect(s4) feed('') - screen:expect(s3) + screen:expect(s5) feed('') - screen:expect(s2) + screen:expect(s6) feed('') + screen:expect(s7) + feed('') + screen:expect(s8) + exec('set foldmethod=indent') + -- move the cursor so we can reuse the same dumps + feed('5G') screen:expect(s1) + feed('') + screen:expect(s2) + feed('7G') + screen:expect(s7) feed('') - screen:expect(s0) + screen:expect(s8) end) end) diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index ed3ffd83e7..c1f6c66642 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -105,6 +105,21 @@ func Test_smoothscroll_CtrlE_CtrlY() call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smoothscroll_8', {}) + if has('folding') + call term_sendkeys(buf, ":set foldmethod=indent\") + " move the cursor so we can reuse the same dumps + call term_sendkeys(buf, "5G") + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smoothscroll_1', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smoothscroll_2', {}) + call term_sendkeys(buf, "7G") + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smoothscroll_7', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smoothscroll_8', {}) + endif + call StopVimInTerminal(buf) endfunc From a43b28a34c568eb3e280e75a81424f80f0ed980b Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 13:11:33 +0200 Subject: [PATCH 07/45] vim-patch:9.0.0649: no indication the first line is broken for 'smoothscroll' Problem: No indication when the first line is broken for 'smoothscroll'. Solution: Show "<<<" in the first line. https://github.com/vim/vim/commit/406b5d89e18742ac6e6256ffc72fb70a27f0148b Co-authored-by: Bram Moolenaar --- src/nvim/grid.c | 9 +++++++++ test/functional/legacy/scroll_opt_spec.lua | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 7745daf69a..2c548e11f1 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -503,6 +503,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle int col = 0; bool redraw_next; // redraw_this for next character bool clear_next = false; + bool topline = row == 0; int char_cells; // 1: normal char // 2: occupies two display cells int start_dirty = -1, end_dirty = 0; @@ -529,6 +530,14 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle max_off_from = linebuf_size; max_off_to = grid->line_offset[row] + (size_t)grid->cols; + if (topline && wp->w_skipcol > 0) { + // Take care of putting "<<<" on the first line for 'smoothscroll'. + for (int i = 0; i < 3; i++) { + schar_from_ascii(linebuf_char[i], '<'); + linebuf_attr[i] = HL_ATTR(HLF_AT); + } + } + if (rlflag) { // Clear rest first, because it's left of the text. if (clear_width > 0) { diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 20976089e2..ddbeca1a30 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -49,7 +49,7 @@ describe('smoothscroll', function() | ]] local s2 = [[ - word word word word word word word word | + << Date: Thu, 27 Apr 2023 04:52:52 +0200 Subject: [PATCH 08/45] vim-patch:9.0.0650: some tests are failing Problem: Some tests are failing. Solution: Adjust for "<<<" showing up. https://github.com/vim/vim/commit/0466d398a550623126fba3a2a08208a798b28bda Co-authored-by: Bram Moolenaar --- test/old/testdir/test_breakindent.vim | 20 ++++++++++---------- test/old/testdir/test_display.vim | 2 +- test/old/testdir/test_listlbr.vim | 2 +- test/old/testdir/test_listlbr_utf8.vim | 4 ++-- test/old/testdir/test_number.vim | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/test/old/testdir/test_breakindent.vim b/test/old/testdir/test_breakindent.vim index 0d1753182e..f271e6f2ea 100644 --- a/test/old/testdir/test_breakindent.vim +++ b/test/old/testdir/test_breakindent.vim @@ -87,7 +87,7 @@ func Test_breakindent02_vartabs() endif " simple breakindent test with showbreak set call s:test_windows('setl briopt=min:0 sbr=>> vts=4') - let lines = s:screen_lines(line('.'),8) + let lines = s:screen_lines(line('.'), 8) let expect = [ \ " abcd", \ " >>qr", @@ -100,7 +100,7 @@ endfunc func Test_breakindent03() " simple breakindent test with showbreak set and briopt including sbr call s:test_windows('setl briopt=sbr,min:0 sbr=++') - let lines = s:screen_lines(line('.'),8) + let lines = s:screen_lines(line('.'), 8) let expect=[ \ " abcd", \ "++ qrst", @@ -117,7 +117,7 @@ func Test_breakindent03_vartabs() return endif call s:test_windows('setl briopt=sbr,min:0 sbr=++ vts=4') - let lines = s:screen_lines(line('.'),8) + let lines = s:screen_lines(line('.'), 8) let expect = [ \ " abcd", \ "++ qrst", @@ -132,7 +132,7 @@ func Test_breakindent04() " breakindent set with min width 18 set sbr=<<< call s:test_windows('setl sbr=NONE briopt=min:18') - let lines = s:screen_lines(line('.'),8) + let lines = s:screen_lines(line('.'), 8) let expect = [ \ " abcd", \ " qrstuv", @@ -150,7 +150,7 @@ func Test_breakindent04_vartabs() return endif call s:test_windows('setl sbr= briopt=min:18 vts=4') - let lines = s:screen_lines(line('.'),8) + let lines = s:screen_lines(line('.'), 8) let expect = [ \ " abcd", \ " qrstuv", @@ -583,7 +583,7 @@ func Test_breakindent16() redraw! let lines = s:screen_lines(1,10) let expect = [ - \ " 789012", + \ "<<< 789012", \ " 345678", \ " 901234", \ ] @@ -611,7 +611,7 @@ func Test_breakindent16_vartabs() redraw! let lines = s:screen_lines(1,10) let expect = [ - \ " 789012", + \ "<<< 789012", \ " 345678", \ " 901234", \ ] @@ -700,7 +700,7 @@ func Test_breakindent19_sbr_nextpage() norm! 5gj let lines = s:screen_lines(1, 20) let expect = [ - \ ">aaaaaaaaaaaaaaaaaaa", + \ "<<aaaaaaaaaaaaaaaaaaa", \ ">aaaaaaaaaaaaaaaaaaa", \ ] @@ -727,7 +727,7 @@ func Test_breakindent20_cpo_n_nextpage() redraw! let lines = s:screen_lines(1, 20) let expect = [ - \ "--1 aaaaaaaaaaaaaaaa", + \ "<<< aaaaaaaaaaaaaaaa", \ " aaaaaaaaaaaaaaaa", \ " aaaaaaaaaaaaaaaa", \ ] @@ -746,7 +746,7 @@ func Test_breakindent20_cpo_n_nextpage() norm! 5gj let lines = s:screen_lines(1, 20) let expect = [ - \ "--1 aaaaaaaaaaaaaa", + \ "<<< aaaaaaaaaaaaaa", \ " aaaaaaaaaaaaaa", \ " aaaaaaaaaaaaaa", \ ] diff --git a/test/old/testdir/test_display.vim b/test/old/testdir/test_display.vim index b642f39c9f..aae1030aac 100644 --- a/test/old/testdir/test_display.vim +++ b/test/old/testdir/test_display.vim @@ -146,7 +146,7 @@ func Test_display_listchars_precedes() let lines = ScreenLines([1,10], winwidth(0)+1) let expect = [ - \ "eAx\" let lines = s:screen_lines([1, 10], winwidth(0)) let expect = [ -\ "foobar foobar ", +\ "<<eAx\" let lines = s:screen_lines([1, 10], winwidth(0)) let expect = [ -\ "+foobar foobar ", +\ "<<', +\ '<<', \ '中aaaaaaaaaaaaaaaaa>', \ '中aaaaaaaaaaaaaaaaa>', \ '中aaaaaaaaaaaaaaaaa>', diff --git a/test/old/testdir/test_number.vim b/test/old/testdir/test_number.vim index 521b0cf706..cf777fd918 100644 --- a/test/old/testdir/test_number.vim +++ b/test/old/testdir/test_number.vim @@ -138,7 +138,7 @@ func Test_number_with_linewrap1() call s:validate_cursor() let lines = s:screen_lines(1, 3) let expect = [ -\ "--1 aaaa", +\ "<<< aaaa", \ " aaaa", \ " aaaa", \ ] From f3b44cf23d9c240471826e4f35555ee85ded1e5b Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 04:54:50 +0200 Subject: [PATCH 09/45] vim-patch:9.0.0652: 'smoothscroll' not tested with 'number' and "n" in 'cpo' Problem: 'smoothscroll' not tested with 'number' and "n" in 'cpo'. Solution: Add tests, fix uncovered problem. https://github.com/vim/vim/commit/b6aab8f44beb8c5d99393abdc2c9faab085c72aa Co-authored-by: Bram Moolenaar --- src/nvim/drawline.c | 3 +- src/nvim/drawscreen.c | 20 +++++ test/functional/legacy/scroll_opt_spec.lua | 99 ++++++++++++++++++++++ test/functional/ui/highlight_spec.lua | 2 +- test/functional/ui/popupmenu_spec.lua | 2 +- test/old/testdir/test_scroll_opt.vim | 35 ++++++++ 6 files changed, 158 insertions(+), 3 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 45cfbb6d47..35038f4717 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -596,7 +596,8 @@ static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int num_signs, int si int sign_num_attr, int sign_cul_attr) { if ((wp->w_p_nu || wp->w_p_rnu) - && (wlv->row == wlv->startrow + wlv->filler_lines + && ((wlv->row == wlv->startrow + wlv->filler_lines + && (wp->w_skipcol == 0 || wlv->row > wp->w_winrow)) || vim_strchr(p_cpo, CPO_NUMCOL) == NULL)) { // If 'signcolumn' is set to 'number' and a sign is present // in "lnum", then display the sign instead of the line diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index b5e516005b..36ff53aacb 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -1447,6 +1447,26 @@ static void win_update(win_T *wp, DecorProviders *providers) init_search_hl(wp, &screen_search_hl); + // Make sure skipcol is valid, it depends on various options and the window + // width. + if (wp->w_skipcol > 0) { + int w = 0; + int width1 = wp->w_width - win_col_off(wp); + int width2 = width1 + win_col_off2(wp); + int add = width1; + + while (w < wp->w_skipcol) { + if (w > 0) { + add = width2; + } + w += add; + } + if (w != wp->w_skipcol) { + // always round down, the higher value may not be valid + wp->w_skipcol = w - add; + } + } + // Force redraw when width of 'number' or 'relativenumber' column // changes. int nrwidth = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0; diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index ddbeca1a30..8d2eade1a8 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -173,4 +173,103 @@ describe('smoothscroll', function() feed('') screen:expect(s8) end) + + -- oldtest: Test_smoothscroll_number() + it("works 'number' and 'cpo'+=n", function() + exec([[ + call setline(1, [ 'one ' .. 'word '->repeat(20), 'two ' .. 'long word '->repeat(7), 'line', 'line', 'line', ]) + set smoothscroll scrolloff=5 + set number cpo+=n + :3 + ]]) + screen:expect([[ + 1 one word word word word word word wo| + rd word word word word word word word wo| + rd word word word word word | + 2 two long word long word long word lo| + ng word long word long word long word | + 3 ^line | + 4 line | + 5 line | + ~ | + ~ | + ~ | + | + ]]) + feed('') + screen:expect([[ + <<') + screen:expect([[ + <<') + screen:expect([[ + <<< rd word word word word word word wor| + d word word word word word word | + 2 two long word long word long word lo| + ng word long word long word long wor| + d | + 3 ^line | + 4 line | + 5 line | + ~ | + ~ | + ~ | + | + ]]) + feed('') + screen:expect([[ + 1 one word word word word word word wo| + rd word word word word word word wor| + d word word word word word word | + 2 two long word long word long word lo| + ng word long word long word long wor| + d | + 3 ^line | + 4 line | + 5 line | + ~ | + ~ | + | + ]]) + end) end) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 89b503141b..01895003ae 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -704,7 +704,7 @@ describe("'listchars' highlight", function() feed_command('set listchars=eol:¬,precedes:< list') feed('90ia') screen:expect([[ - {0:<}aaaaaaaaaaaaaaaaaaa| + {0:<<<}aaaaaaaaaaaaaaaaa| aaaaaaaaaaaaaaaaaaaa| aaaaaaaaaaaaaaaaaaaa| aaaaaaaaa^a{0:¬} | diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 0b71e12b6f..6c26c8ea39 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -2396,7 +2396,7 @@ describe('builtin popupmenu', function() -- can't draw the pum, but check we don't crash screen:try_resize(12,2) screen:expect([[ - text^ | + {1:<<<}t^ | {2:-- INSERT -} | ]]) diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index c1f6c66642..f574286b09 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -123,6 +123,41 @@ func Test_smoothscroll_CtrlE_CtrlY() call StopVimInTerminal(buf) endfunc +func Test_smoothscroll_number() + CheckScreendump + + let lines =<< trim END + vim9script + setline(1, [ + 'one ' .. 'word '->repeat(20), + 'two ' .. 'long word '->repeat(7), + 'line', + 'line', + 'line', + ]) + set smoothscroll + set number cpo+=n + :3 + END + call writefile(lines, 'XSmoothNumber', 'D') + let buf = RunVimInTerminal('-S XSmoothNumber', #{rows: 12, cols: 40}) + + call VerifyScreenDump(buf, 'Test_smooth_number_1', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smooth_number_2', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smooth_number_3', {}) + + call term_sendkeys(buf, ":set cpo-=n\") + call VerifyScreenDump(buf, 'Test_smooth_number_4', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smooth_number_5', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smooth_number_6', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab From 181097e4434bbbd7482fd238a67c1e65f002e3ff Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 16:46:51 +0200 Subject: [PATCH 10/45] vim-patch:9.0.0654: breakindent test fails Problem: Breakindent test fails. Solution: Temporarily accept wrong result. https://github.com/vim/vim/commit/ec32c781a282398e3da27f4aec4b03fcd20f8b0d Co-authored-by: Bram Moolenaar --- test/old/testdir/test_breakindent.vim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/old/testdir/test_breakindent.vim b/test/old/testdir/test_breakindent.vim index f271e6f2ea..de25f9b787 100644 --- a/test/old/testdir/test_breakindent.vim +++ b/test/old/testdir/test_breakindent.vim @@ -726,8 +726,9 @@ func Test_breakindent20_cpo_n_nextpage() norm! 5gj redraw! let lines = s:screen_lines(1, 20) + " FIXME: this is not the right result let expect = [ - \ "<<< aaaaaaaaaaaaaaaa", + \ "<< Date: Wed, 26 Apr 2023 16:54:23 +0200 Subject: [PATCH 11/45] vim-patch:9.0.0671: negative topline using CTRL-Y with 'smoothscroll' and 'diff' Problem: Negative topline using CTRL-Y with 'smoothscroll' and 'diff'. (Ernie Rael) Solution: Only use 'smoothscroll' when 'wrap' is set. https://github.com/vim/vim/commit/1a58e1d97cfc72e501cbf63ad75f46f1bb4c8da2 Co-authored-by: Bram Moolenaar --- src/nvim/move.c | 21 ++++++++-------- test/functional/legacy/scroll_opt_spec.lua | 29 ++++++++++++++++++++++ test/old/testdir/test_scroll_opt.vim | 26 +++++++++++++++++++ 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index 3b882a5d02..5e85768865 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1094,8 +1094,9 @@ bool scrolldown(long line_count, int byfold) int done = 0; // total # of physical lines done int width1 = 0; int width2 = 0; + bool do_sms = curwin->w_p_wrap && curwin->w_p_sms; - if (curwin->w_p_wrap && curwin->w_p_sms) { + if (do_sms) { width1 = curwin->w_width - curwin_col_off(); width2 = width1 + curwin_col_off2(); } @@ -1103,17 +1104,17 @@ bool scrolldown(long line_count, int byfold) // Make sure w_topline is at the first of a sequence of folded lines. (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); validate_cursor(); // w_wrow needs to be valid - while (line_count-- > 0) { + for (long todo = line_count; todo > 0; todo--) { if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline) && curwin->w_topfill < curwin->w_height_inner - 1) { curwin->w_topfill++; done++; } else { // break when at the very top - if (curwin->w_topline == 1 && (!curwin->w_p_sms || curwin->w_skipcol < width1)) { + if (curwin->w_topline == 1 && (!do_sms || curwin->w_skipcol < width1)) { break; } - if (curwin->w_p_wrap && curwin->w_p_sms && curwin->w_skipcol >= width1) { + if (do_sms && curwin->w_skipcol >= width1) { // scroll a screen line down if (curwin->w_skipcol >= width1 + width2) { curwin->w_skipcol -= width2; @@ -1132,12 +1133,12 @@ bool scrolldown(long line_count, int byfold) if (hasFolding(curwin->w_topline, &first, NULL)) { done++; if (!byfold) { - line_count -= curwin->w_topline - first - 1; + todo -= 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) { + if (do_sms) { int size = (int)win_linetabsize(curwin, curwin->w_topline, ml_get(curwin->w_topline), (colnr_T)MAXCOL); if (size > width1) { @@ -1209,15 +1210,15 @@ bool scrollup(long line_count, int byfold) { linenr_T topline = curwin->w_topline; linenr_T botline = curwin->w_botline; - int do_smoothscroll = curwin->w_p_wrap && curwin->w_p_sms; + int do_sms = curwin->w_p_wrap && curwin->w_p_sms; - if (do_smoothscroll || (byfold && hasAnyFolding(curwin)) || win_may_fill(curwin)) { + if (do_sms || (byfold && hasAnyFolding(curwin)) || win_may_fill(curwin)) { int width1 = curwin->w_width - curwin_col_off(); int width2 = width1 + curwin_col_off2(); int size = 0; linenr_T prev_topline = curwin->w_topline; - if (do_smoothscroll) { + if (do_sms) { size = (int)win_linetabsize(curwin, curwin->w_topline, ml_get(curwin->w_topline), (colnr_T)MAXCOL); } @@ -1261,7 +1262,7 @@ bool scrollup(long line_count, int byfold) curwin->w_topline = lnum; curwin->w_topfill = win_get_fill(curwin, lnum); curwin->w_skipcol = 0; - if (todo > 1 && do_smoothscroll) { + if (todo > 1 && do_sms) { size = (int)win_linetabsize(curwin, curwin->w_topline, ml_get(curwin->w_topline), (colnr_T)MAXCOL); } diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 8d2eade1a8..90417d93f3 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -272,4 +272,33 @@ describe('smoothscroll', function() | ]]) end) + + -- oldtest: Test_smoothscroll_diff_mode() + it("works with diff mode", function() + screen:try_resize(40, 8) + exec([[ + let text = 'just some text here' + call setline(1, text) + set smoothscroll + diffthis + new + call setline(1, text) + set smoothscroll + diffthis + ]]) + screen:expect([[ + - ^just some text here | + ~ | + ~ | + [No Name] [+] | + - just some text here | + ~ | + [No Name] [+] | + | + ]]) + feed('') + screen:expect_unchanged() + feed('') + screen:expect_unchanged() + end) end) diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index f574286b09..53768de6fa 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -158,6 +158,32 @@ func Test_smoothscroll_number() call StopVimInTerminal(buf) endfunc +func Test_smoothscroll_diff_mode() + CheckScreendump + + let lines =<< trim END + vim9script + var text = 'just some text here' + setline(1, text) + set smoothscroll + diffthis + new + setline(1, text) + set smoothscroll + diffthis + END + call writefile(lines, 'XSmoothDiff', 'D') + let buf = RunVimInTerminal('-S XSmoothDiff', #{rows: 8}) + + call VerifyScreenDump(buf, 'Test_smooth_diff_1', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smooth_diff_1', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smooth_diff_1', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab From 3a1973debceca29e65c4f7c83d025cb3314ebaf2 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 17:08:35 +0200 Subject: [PATCH 12/45] vim-patch:9.0.0672: line partly shows with 'smoothscroll' and 'scrolloff' zero Problem: Cursor line only partly shows with 'smoothscroll' and 'scrolloff' zero. Solution: Do not use 'smoothscroll' when adjusting the bottom of the window. (closes vim/vim#11269) https://github.com/vim/vim/commit/9bab7a024393200bb2c03b3abddfda86436990a7 Co-authored-by: Bram Moolenaar --- src/nvim/move.c | 11 +++++++++ test/functional/legacy/scroll_opt_spec.lua | 26 ++++++++++++++++++++++ test/old/testdir/test_scroll_opt.vim | 25 +++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/src/nvim/move.c b/src/nvim/move.c index 5e85768865..0a91b12255 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1627,6 +1627,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) { int used; int scrolled = 0; + int min_scrolled = 1; int extra = 0; lineoff_T loff; lineoff_T boff; @@ -1676,6 +1677,12 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) if (cln == curwin->w_botline) { scrolled -= curwin->w_empty_rows; } + min_scrolled = scrolled; + if (cln > curwin->w_botline && curwin->w_p_sms && curwin->w_p_wrap) { + for (linenr_T lnum = curwin->w_botline + 1; lnum <= cln; lnum++) { + min_scrolled += plines_win_nofill(curwin, lnum, true); + } + } } // Stop counting lines to scroll when @@ -1777,6 +1784,10 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) if (line_count >= curwin->w_height_inner && line_count > min_scroll) { scroll_cursor_halfway(false, true); } else { + // With 'smoothscroll' scroll at least the height of the cursor line. + if (curwin->w_p_wrap && curwin->w_p_sms && line_count < min_scrolled) { + line_count = min_scrolled; + } scrollup(line_count, true); } diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 90417d93f3..c63401cb02 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -301,4 +301,30 @@ describe('smoothscroll', function() feed('') screen:expect_unchanged() end) + + -- oldtest: Test_smoothscroll_wrap_scrolloff_zero() + it("works with zero 'scrolloff'", function() + screen:try_resize(40, 8) + exec([[ + call setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(7)) + set smoothscroll scrolloff=0 + :3 + ]]) + screen:expect([[ + <<j') + screen:expect_unchanged() + feed('G') + screen:expect_unchanged() + end) end) diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index 53768de6fa..799db19298 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -184,6 +184,31 @@ func Test_smoothscroll_diff_mode() call StopVimInTerminal(buf) endfunc +func Test_smoothscroll_wrap_scrolloff_zero() + CheckScreendump + + let lines =<< trim END + vim9script + setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(7)) + set smoothscroll scrolloff=0 + :3 + END + call writefile(lines, 'XSmoothWrap', 'D') + let buf = RunVimInTerminal('-S XSmoothWrap', #{rows: 8, cols: 40}) + + call VerifyScreenDump(buf, 'Test_smooth_wrap_1', {}) + + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_smooth_wrap_2', {}) + + call term_sendkeys(buf, "\j") + call VerifyScreenDump(buf, 'Test_smooth_wrap_3', {}) + + call term_sendkeys(buf, "G") + call VerifyScreenDump(buf, 'Test_smooth_wrap_4', {}) + + call StopVimInTerminal(buf) +endfunc " vim: shiftwidth=2 sts=2 expandtab From d95697d6d4533e84bbb9d262b355ee9f71bd7452 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 17:23:42 +0200 Subject: [PATCH 13/45] vim-patch:9.0.0673: first line wong with 'smoothscroll' and 'scrolloff' zero Problem: First line not scrolled properly with 'smoothscroll' and 'scrolloff' zero and using "k". Solution: Make sure the cursor position is visible. https://github.com/vim/vim/commit/46b54747c5d252c584571a321231bad9330018ec Co-authored-by: Bram Moolenaar --- src/nvim/move.c | 18 +++++++++++++++++- test/functional/legacy/scroll_opt_spec.lua | 15 ++++++++++++++- test/old/testdir/test_scroll_opt.vim | 5 +++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index 0a91b12255..7c63aa9d7f 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -203,7 +203,7 @@ void update_topline(win_T *wp) bool check_topline = false; // If the cursor is above or near the top of the window, scroll the window // to show the line the cursor is in, with 'scrolloff' context. - if (wp->w_topline > 1) { + if (wp->w_topline > 1 || wp->w_skipcol > 0) { // If the cursor is above topline, scrolling is always needed. // If the cursor is far below topline and there is no folding, // scrolling down is never needed. @@ -211,6 +211,15 @@ void update_topline(win_T *wp) check_topline = true; } else if (check_top_offset()) { check_topline = true; + } else if (wp->w_skipcol > 0 && wp->w_cursor.lnum == wp->w_topline) { + colnr_T vcol; + + // check the cursor position is visible. Add 3 for the ">>>" + // displayed in the top-left. + getvvcol(wp, &wp->w_cursor, &vcol, NULL, NULL); + if (wp->w_skipcol + 3 >= vcol) { + check_topline = true; + } } } // Check if there are more filler lines than allowed. @@ -1499,6 +1508,7 @@ void scroll_cursor_top(int min_scroll, int always) linenr_T top; // just above displayed lines linenr_T bot; // just below displayed lines linenr_T old_topline = curwin->w_topline; + int old_skipcol = curwin->w_skipcol; linenr_T old_topfill = curwin->w_topfill; linenr_T new_topline; int off = (int)get_scrolloff_value(curwin); @@ -1588,7 +1598,13 @@ void scroll_cursor_top(int min_scroll, int always) } } check_topfill(curwin, false); + // TODO(vim): if the line doesn't fit may optimize w_skipcol + if (curwin->w_topline == curwin->w_cursor.lnum) { + curwin->w_skipcol = 0; + redraw_later(curwin, UPD_NOT_VALID); + } if (curwin->w_topline != old_topline + || curwin->w_skipcol != old_skipcol || curwin->w_topfill != old_topfill) { curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index c63401cb02..fb4990ff00 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -307,7 +307,7 @@ describe('smoothscroll', function() screen:try_resize(40, 8) exec([[ call setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(7)) - set smoothscroll scrolloff=0 + set smoothscroll scrolloff=0 display= :3 ]]) screen:expect([[ @@ -322,9 +322,22 @@ describe('smoothscroll', function() ]]) feed('j') screen:expect_unchanged() + -- moving cursor down - whole bottom line shows feed('j') screen:expect_unchanged() feed('G') screen:expect_unchanged() + -- moving cursor up - whole top line shows + feed('2k') + screen:expect([[ + ^Line with some text with some text with | + some text with some text with some text | + with some text with some text | + Line with some text with some text with | + some text with some text with some text | + with some text with some text | + @ | + | + ]]) end) end) diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index 799db19298..77e656cf9f 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -198,6 +198,7 @@ func Test_smoothscroll_wrap_scrolloff_zero() call VerifyScreenDump(buf, 'Test_smooth_wrap_1', {}) + " moving cursor down - whole bottom line shows call term_sendkeys(buf, "j") call VerifyScreenDump(buf, 'Test_smooth_wrap_2', {}) @@ -207,6 +208,10 @@ func Test_smoothscroll_wrap_scrolloff_zero() call term_sendkeys(buf, "G") call VerifyScreenDump(buf, 'Test_smooth_wrap_4', {}) + " moving cursor up - whole top line shows + call term_sendkeys(buf, "2k") + call VerifyScreenDump(buf, 'Test_smooth_wrap_5', {}) + call StopVimInTerminal(buf) endfunc From 7dc2c087e7c2071166772dc6b167c802612112fe Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 18:08:55 +0200 Subject: [PATCH 14/45] vim-patch:9.0.0677: breakindent test accepts wrong result Problem: Breakindent test accepts wrong result. Solution: Fix the number column and adjust the expected text. https://github.com/vim/vim/commit/06618f94f1c9ed73a84ad5d6a8e1933b0b8da846 Co-authored-by: Bram Moolenaar --- src/nvim/drawline.c | 6 +++--- test/old/testdir/test_breakindent.vim | 28 +++++++++++++-------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 35038f4717..c9cd08fedd 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -596,8 +596,7 @@ static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int num_signs, int si int sign_num_attr, int sign_cul_attr) { if ((wp->w_p_nu || wp->w_p_rnu) - && ((wlv->row == wlv->startrow + wlv->filler_lines - && (wp->w_skipcol == 0 || wlv->row > wp->w_winrow)) + && (wlv->row == wlv->startrow + wlv->filler_lines || vim_strchr(p_cpo, CPO_NUMCOL) == NULL)) { // If 'signcolumn' is set to 'number' and a sign is present // in "lnum", then display the sign instead of the line @@ -606,7 +605,8 @@ static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int num_signs, int si get_sign_display_info(true, wp, wlv, sign_idx, sign_cul_attr); } else { // Draw the line number (empty space after wrapping). - if (wlv->row == wlv->startrow + wlv->filler_lines) { + if (wlv->row == wlv->startrow + wlv->filler_lines + && (wp->w_skipcol == 0 || wlv->row > wp->w_winrow)) { get_line_number_str(wp, wlv->lnum, wlv->extra, sizeof(wlv->extra)); if (wp->w_skipcol > 0 && wlv->startrow == 0) { for (wlv->p_extra = wlv->extra; *wlv->p_extra == ' '; wlv->p_extra++) { diff --git a/test/old/testdir/test_breakindent.vim b/test/old/testdir/test_breakindent.vim index de25f9b787..9cc398f64a 100644 --- a/test/old/testdir/test_breakindent.vim +++ b/test/old/testdir/test_breakindent.vim @@ -711,14 +711,14 @@ endfunc func Test_breakindent20_cpo_n_nextpage() let s:input = "" call s:test_windows('setl breakindent briopt=min:14 cpo+=n number') - call setline(1, repeat('a', 200)) + call setline(1, repeat('abcdefghijklmnopqrst', 10)) norm! 1gg redraw! let lines = s:screen_lines(1, 20) let expect = [ - \ " 1 aaaaaaaaaaaaaaaa", - \ " aaaaaaaaaaaaaaaa", - \ " aaaaaaaaaaaaaaaa", + \ " 1 abcdefghijklmnop", + \ " qrstabcdefghijkl", + \ " mnopqrstabcdefgh", \ ] call s:compare_lines(expect, lines) " Scroll down one screen line @@ -726,11 +726,10 @@ func Test_breakindent20_cpo_n_nextpage() norm! 5gj redraw! let lines = s:screen_lines(1, 20) - " FIXME: this is not the right result let expect = [ - \ "<< Date: Wed, 26 Apr 2023 19:35:16 +0200 Subject: [PATCH 15/45] vim-patch:9.0.0679: tests failing with 'smoothscroll', 'number' and "n" in 'cpo' Problem: Tests failing with 'smoothscroll', 'number' and "n" in 'cpo'. Solution: Do not count number column in topline if columns are skipped. https://github.com/vim/vim/commit/35b251d2c2c9d415887d334473669ea886117356 Co-authored-by: Bram Moolenaar --- src/nvim/drawline.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index c9cd08fedd..1e8de1bab0 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -595,9 +595,11 @@ static int get_line_number_attr(win_T *wp, winlinevars_T *wlv) static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int num_signs, int sign_idx, int sign_num_attr, int sign_cul_attr) { + bool has_cpo_n = vim_strchr(p_cpo, CPO_NUMCOL) != NULL; + if ((wp->w_p_nu || wp->w_p_rnu) - && (wlv->row == wlv->startrow + wlv->filler_lines - || vim_strchr(p_cpo, CPO_NUMCOL) == NULL)) { + && (wlv->row == wlv->startrow + wlv->filler_lines || !has_cpo_n) + && !(has_cpo_n && wp->w_skipcol > 0 && wlv->lnum == wp->w_topline)) { // If 'signcolumn' is set to 'number' and a sign is present // in "lnum", then display the sign instead of the line // number. From 34a4f3729ce4a047ae945238276c8e2dabc38048 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 19:40:22 +0200 Subject: [PATCH 16/45] vim-patch:9.0.0680: tests failing with 'breakindent', 'number' and "n" in 'cpo' Problem: Tests failing with 'breakindent', 'number' and "n" in 'cpo'. Solution: Do count the number column in topline if 'breakindent' is set. https://github.com/vim/vim/commit/3725116f6ec3b5c01e456b151a60c0690e04f76c Co-authored-by: Bram Moolenaar --- src/nvim/drawline.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 1e8de1bab0..0d701f07bb 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -599,10 +599,11 @@ static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int num_signs, int si if ((wp->w_p_nu || wp->w_p_rnu) && (wlv->row == wlv->startrow + wlv->filler_lines || !has_cpo_n) - && !(has_cpo_n && wp->w_skipcol > 0 && wlv->lnum == wp->w_topline)) { - // If 'signcolumn' is set to 'number' and a sign is present - // in "lnum", then display the sign instead of the line - // number. + // there is no line number in a wrapped line when "n" is in + // 'cpoptions', but 'breakindent' assumes it anyway. + && !((has_cpo_n && !wp->w_p_bri) && wp->w_skipcol > 0 && wlv->lnum == wp->w_topline)) { + // If 'signcolumn' is set to 'number' and a sign is present in "lnum", + // then display the sign instead of the line number. if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' && num_signs > 0) { get_sign_display_info(true, wp, wlv, sign_idx, sign_cul_attr); } else { From 2918720addd3837abcf06f457e8e2cf674ece5e7 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 20:30:39 +0200 Subject: [PATCH 17/45] vim-patch:9.0.0681: "<<<" shows for 'smoothscroll' even when 'showbreak is set Problem: "<<<" shows for 'smoothscroll' even when 'showbreak is set. Solution: When 'showbreak' is set do not display "<<<". https://github.com/vim/vim/commit/0937b9fb244949b7ce9bfcf8398d7495b9b6aa85 Co-authored-by: Bram Moolenaar --- src/nvim/grid.c | 7 ++++--- test/old/testdir/test_breakindent.vim | 2 +- test/old/testdir/test_listlbr_utf8.vim | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 2c548e11f1..0ceaeaa8b2 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -22,7 +22,7 @@ #include "nvim/highlight.h" #include "nvim/log.h" #include "nvim/message.h" -#include "nvim/option_defs.h" +#include "nvim/option.h" #include "nvim/types.h" #include "nvim/ui.h" #include "nvim/vim.h" @@ -530,8 +530,9 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle max_off_from = linebuf_size; max_off_to = grid->line_offset[row] + (size_t)grid->cols; - if (topline && wp->w_skipcol > 0) { - // Take care of putting "<<<" on the first line for 'smoothscroll'. + if (topline && wp->w_skipcol > 0 && *get_showbreak_value(wp) == NUL) { + // Take care of putting "<<<" on the first line for 'smoothscroll' + // when 'showbreak' is not set. for (int i = 0; i < 3; i++) { schar_from_ascii(linebuf_char[i], '<'); linebuf_attr[i] = HL_ATTR(HLF_AT); diff --git a/test/old/testdir/test_breakindent.vim b/test/old/testdir/test_breakindent.vim index 9cc398f64a..3cbc1f4900 100644 --- a/test/old/testdir/test_breakindent.vim +++ b/test/old/testdir/test_breakindent.vim @@ -700,7 +700,7 @@ func Test_breakindent19_sbr_nextpage() norm! 5gj let lines = s:screen_lines(1, 20) let expect = [ - \ "<<aaaaaaaaaaaaaaaaaaa", \ ">aaaaaaaaaaaaaaaaaaa", \ ">aaaaaaaaaaaaaaaaaaa", \ ] diff --git a/test/old/testdir/test_listlbr_utf8.vim b/test/old/testdir/test_listlbr_utf8.vim index 47683696d1..15b248964f 100644 --- a/test/old/testdir/test_listlbr_utf8.vim +++ b/test/old/testdir/test_listlbr_utf8.vim @@ -167,7 +167,7 @@ func Test_visual_block_and_selection_exclusive() exe "norm! $3B\eAx\" let lines = s:screen_lines([1, 10], winwidth(0)) let expect = [ -\ "<< Date: Wed, 26 Apr 2023 21:56:31 +0200 Subject: [PATCH 18/45] vim-patch:9.0.0701: with 'smoothscroll' cursor position not adjusted in long line Problem: With 'smoothscroll' the cursor position s not adjusted in a long line. Solution: Move the cursor further up or down in the line. https://github.com/vim/vim/commit/8cf3459878198c5bb4a96f3c63214b2beccce341 Co-authored-by: Bram Moolenaar --- src/nvim/move.c | 38 ++++++++++++++- test/functional/legacy/scroll_opt_spec.lua | 56 ++++++++++++++++++++++ test/old/testdir/test_scroll_opt.vim | 28 +++++++++++ 3 files changed, 121 insertions(+), 1 deletion(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index 7c63aa9d7f..144384c294 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1208,6 +1208,24 @@ bool scrolldown(long line_count, int byfold) foldAdjustCursor(); coladvance(curwin->w_curswant); } + + if (curwin->w_cursor.lnum == curwin->w_topline && do_sms) { + // make sure the cursor is in the visible text + validate_virtcol(); + int col = curwin->w_virtcol - curwin->w_skipcol; + int row = 0; + if (col >= width1) { + col -= width1; + ++row; + } + if (col > width2) { + row += col / width2; + col = col % width2; + } + if (row >= curwin->w_height) { + coladvance(curwin->w_virtcol - (row - curwin->w_height + 1) * width2); + } + } return moved; } @@ -1245,7 +1263,7 @@ bool scrollup(long line_count, int byfold) // for a closed fold: go to the last line in the fold (void)hasFolding(lnum, NULL, &lnum); } - if (lnum == curwin->w_topline && curwin->w_p_wrap && curwin->w_p_sms) { + if (lnum == curwin->w_topline && do_sms) { // 'smoothscroll': increase "w_skipcol" until it goes over // the end of the line, then advance to the next line. int add = curwin->w_skipcol > 0 ? width2 : width1; @@ -1310,6 +1328,24 @@ bool scrollup(long line_count, int byfold) coladvance(curwin->w_curswant); } + if (curwin->w_cursor.lnum == curwin->w_topline && do_sms && curwin->w_skipcol > 0) { + // make sure the cursor is in a visible part of the line + validate_virtcol(); + if (curwin->w_virtcol < curwin->w_skipcol + 3) { + int width1 = curwin->w_width - curwin_col_off(); + int width2 = width1 + curwin_col_off2(); + colnr_T col = curwin->w_virtcol; + + if (col < width1) { + col += width1; + } + while (col < curwin->w_skipcol + 3) { + col += width2; + } + coladvance(col); + } + } + bool moved = topline != curwin->w_topline || botline != curwin->w_botline; diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index fb4990ff00..5c7c9cd55a 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -340,4 +340,60 @@ describe('smoothscroll', function() | ]]) end) + + -- oldtest: Test_smoothscroll_wrap_long_line() + it("adjusts the cursor position in a long line", function() + screen:try_resize(40, 6) + exec([[ + call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(30))]) + set smoothscroll scrolloff=0 + normal 3G10|zt + ]]) + -- scrolling up, cursor moves screen line down + screen:expect([[ + Line with^ lots of text with lots of text| + with lots of text with lots of text wit| + h lots of text with lots of text with lo| + ts of text with lots of text with lots o| + f text with lots of text with lots of te| + | + ]]) + feed('') + screen:expect([[ + <<') + screen:expect([[ + <<< lots ^of text with lots of text with | + lots of text with lots of text with lots| + of text with lots of text with lots of | + text with lots of text with lots of text| + with lots of text with lots of text wit| + | + ]]) + -- scrolling down, cursor moves screen line up + feed('5') + screen:expect([[ + <<') + screen:expect([[ + Line with lots of text with lots of text| + with lots of text with lots of text wit| + h lots of text with lots of text with lo| + ts of text with lots of text with lots o| + f text wi^th lots of text with lots of te| + | + ]]) + end) end) diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index 77e656cf9f..0041fbaa25 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -215,5 +215,33 @@ func Test_smoothscroll_wrap_scrolloff_zero() call StopVimInTerminal(buf) endfunc +func Test_smoothscroll_wrap_long_line() + CheckScreendump + + let lines =<< trim END + vim9script + setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(30))]) + set smoothscroll scrolloff=0 + normal 3G10|zt + END + call writefile(lines, 'XSmoothWrap', 'D') + let buf = RunVimInTerminal('-S XSmoothWrap', #{rows: 6, cols: 40}) + call VerifyScreenDump(buf, 'Test_smooth_long_1', {}) + + " scrolling up, cursor moves screen line down + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smooth_long_2', {}) + call term_sendkeys(buf, "5\") + call VerifyScreenDump(buf, 'Test_smooth_long_3', {}) + + " scrolling down, cursor moves screen line up + call term_sendkeys(buf, "5\") + call VerifyScreenDump(buf, 'Test_smooth_long_4', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smooth_long_5', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab From 36c98b47a3526dc61d149f951f8b8e3a677c26eb Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 22:15:25 +0200 Subject: [PATCH 19/45] vim-patch:9.0.0707: with 'smoothscroll' cursor position not adjusted in long line Problem: With 'smoothscroll' and 'scrolloff' non-zero the cursor position is not properly adjusted in a long line. Solution: Move the cursor further up or down in the line. https://github.com/vim/vim/commit/118c235112854f34182d968613d7fe98be3b290b Co-authored-by: Bram Moolenaar --- src/nvim/move.c | 29 +++++++++------ test/functional/legacy/scroll_opt_spec.lua | 42 ++++++++++++++++++++++ test/old/testdir/test_scroll_opt.vim | 22 ++++++++++++ 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index 144384c294..e7f5959dbb 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1210,20 +1210,24 @@ bool scrolldown(long line_count, int byfold) } if (curwin->w_cursor.lnum == curwin->w_topline && do_sms) { + long so = curwin->w_p_so >= 0 ? curwin->w_p_so : p_so; + long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; + // make sure the cursor is in the visible text validate_virtcol(); - int col = curwin->w_virtcol - curwin->w_skipcol; + long col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols; int row = 0; if (col >= width1) { col -= width1; - ++row; + row++; } if (col > width2) { - row += col / width2; + row += (int)col / width2; col = col % width2; } if (row >= curwin->w_height) { - coladvance(curwin->w_virtcol - (row - curwin->w_height + 1) * width2); + curwin->w_curswant = curwin->w_virtcol - (row - curwin->w_height + 1) * width2; + coladvance(curwin->w_curswant); } } return moved; @@ -1329,20 +1333,25 @@ bool scrollup(long line_count, int byfold) } if (curwin->w_cursor.lnum == curwin->w_topline && do_sms && curwin->w_skipcol > 0) { - // make sure the cursor is in a visible part of the line + int width1 = curwin->w_width - curwin_col_off(); + int width2 = width1 + curwin_col_off2(); + long so = curwin->w_p_so >= 0 ? curwin->w_p_so : p_so; + long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; + + // Make sure the cursor is in a visible part of the line, taking + // 'scrolloff' into account, but using screen lines. validate_virtcol(); - if (curwin->w_virtcol < curwin->w_skipcol + 3) { - int width1 = curwin->w_width - curwin_col_off(); - int width2 = width1 + curwin_col_off2(); + if (curwin->w_virtcol < curwin->w_skipcol + 3 + scrolloff_cols) { colnr_T col = curwin->w_virtcol; if (col < width1) { col += width1; } - while (col < curwin->w_skipcol + 3) { + while (col < curwin->w_skipcol + 3 + scrolloff_cols) { col += width2; } - coladvance(col); + curwin->w_curswant = col; + coladvance(curwin->w_curswant); } } diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 5c7c9cd55a..be7d324908 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -395,5 +395,47 @@ describe('smoothscroll', function() f text wi^th lots of text with lots of te| | ]]) + -- 'scrolloff' set to 1, scrolling up, cursor moves screen line down + exec('set scrolloff=1') + feed('10|') + screen:expect([[ + <<gjgj') + screen:expect([[ + <<') + screen:expect([[ + <<gjgj') + screen:expect([[ + <<") call VerifyScreenDump(buf, 'Test_smooth_long_5', {}) + " 'scrolloff' set to 1, scrolling up, cursor moves screen line down + call term_sendkeys(buf, ":set scrolloff=1\") + call term_sendkeys(buf, "10|\") + call VerifyScreenDump(buf, 'Test_smooth_long_6', {}) + + " 'scrolloff' set to 1, scrolling down, cursor moves screen line up + call term_sendkeys(buf, "\") + call term_sendkeys(buf, "gjgj") + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smooth_long_7', {}) + + " 'scrolloff' set to 2, scrolling up, cursor moves screen line down + call term_sendkeys(buf, ":set scrolloff=2\") + call term_sendkeys(buf, "10|\") + call VerifyScreenDump(buf, 'Test_smooth_long_8', {}) + + " 'scrolloff' set to 2, scrolling down, cursor moves screen line up + call term_sendkeys(buf, "\") + call term_sendkeys(buf, "gj") + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smooth_long_9', {}) + call StopVimInTerminal(buf) endfunc From bf12a85a69f03bb5321fe0a32e1a9ac81f31750c Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 22:32:38 +0200 Subject: [PATCH 20/45] vim-patch:9.0.0734: cursor position invalid when scrolling with 'smoothscroll' Problem: Cursor position invalid when scrolling with 'smoothscroll' set. (Ernie Rael) Solution: Add w_valid_skipcol and clear flags when it changes. Adjust w_skipcol after moving the cursor. https://github.com/vim/vim/commit/2fbabd238a94022c99506e920186a5b6cdf15426 Co-authored-by: Bram Moolenaar --- src/nvim/buffer_defs.h | 1 + src/nvim/edit.c | 3 + src/nvim/move.c | 79 ++++++++++++++++++++-- src/nvim/normal.c | 2 + test/functional/legacy/scroll_opt_spec.lua | 34 ++++++++-- test/old/testdir/test_scroll_opt.vim | 21 ++++++ 6 files changed, 130 insertions(+), 10 deletions(-) diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 456acfd798..0f832d3659 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1223,6 +1223,7 @@ struct window_S { int w_valid; pos_T w_valid_cursor; // last known position of w_cursor, used to adjust w_valid colnr_T w_valid_leftcol; // last known w_leftcol + colnr_T w_valid_skipcol; // last known w_skipcol bool w_viewport_invalid; linenr_T w_viewport_last_topline; // topline when the viewport was last updated diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 0fb1102f4f..18434ad69f 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -2449,6 +2449,7 @@ void beginline(int flags) } curwin->w_set_curswant = true; } + adjust_skipcol(); } // oneright oneleft cursor_down cursor_up @@ -2490,6 +2491,7 @@ int oneright(void) curwin->w_cursor.col += l; curwin->w_set_curswant = true; + adjust_skipcol(); return OK; } @@ -2538,6 +2540,7 @@ int oneleft(void) // if the character on the left of the current cursor is a multi-byte // character, move to its first byte mb_adjust_cursor(); + adjust_skipcol(); return OK; } diff --git a/src/nvim/move.c b/src/nvim/move.c index e7f5959dbb..b2cf1ebc03 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -426,7 +426,15 @@ void check_cursor_moved(win_T *wp) |VALID_CHEIGHT|VALID_CROW|VALID_TOPLINE); wp->w_valid_cursor = wp->w_cursor; wp->w_valid_leftcol = wp->w_leftcol; + wp->w_valid_skipcol = wp->w_skipcol; wp->w_viewport_invalid = true; + } else if (wp->w_skipcol != wp->w_valid_skipcol) { + wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL + |VALID_CHEIGHT|VALID_CROW + |VALID_BOTLINE|VALID_BOTLINE_AP); + wp->w_valid_cursor = wp->w_cursor; + wp->w_valid_leftcol = wp->w_leftcol; + wp->w_valid_skipcol = wp->w_skipcol; } else if (wp->w_cursor.col != wp->w_valid_cursor.col || wp->w_leftcol != wp->w_valid_leftcol || wp->w_cursor.coladd != @@ -786,6 +794,12 @@ void curs_columns(win_T *wp, int may_scroll) } else if (wp->w_p_wrap && wp->w_width_inner != 0) { width = textwidth + win_col_off2(wp); + // skip columns that are not visible + if (wp->w_cursor.lnum == wp->w_topline + && wp->w_wcol >= wp->w_skipcol) { + wp->w_wcol -= wp->w_skipcol; + } + // long line wrapping, adjust wp->w_wrow if (wp->w_wcol >= wp->w_width_inner) { // this same formula is used in validate_cursor_col() @@ -929,11 +943,11 @@ void curs_columns(win_T *wp, int may_scroll) endcol -= width; } if (endcol > wp->w_skipcol) { + wp->w_wrow -= (endcol - wp->w_skipcol) / width; wp->w_skipcol = endcol; } } - wp->w_wrow -= wp->w_skipcol / width; if (wp->w_wrow >= wp->w_height_inner) { // small window, make sure cursor is in it extra = wp->w_wrow - wp->w_height_inner + 1; @@ -953,8 +967,10 @@ void curs_columns(win_T *wp, int may_scroll) redraw_for_cursorcolumn(curwin); - // now w_leftcol is valid, avoid check_cursor_moved() thinking otherwise + // now w_leftcol and w_skipcol are valid, avoid check_cursor_moved() + // thinking otherwise wp->w_valid_leftcol = wp->w_leftcol; + wp->w_valid_skipcol = wp->w_skipcol; wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; } @@ -1352,15 +1368,70 @@ bool scrollup(long line_count, int byfold) } curwin->w_curswant = col; 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); } } - bool moved = topline != curwin->w_topline - || botline != curwin->w_botline; + bool moved = topline != curwin->w_topline || botline != curwin->w_botline; return moved; } +/// Called after changing the cursor column: make sure that curwin->w_skipcol is +/// valid for 'smoothscroll'. +void adjust_skipcol(void) +{ + if (!curwin->w_p_wrap || !curwin->w_p_sms || curwin->w_cursor.lnum != curwin->w_topline) { + return; + } + + int width1 = curwin->w_width - curwin_col_off(); + int width2 = width1 + curwin_col_off2(); + long so = curwin->w_p_so >= 0 ? curwin->w_p_so : p_so; + long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; + bool scrolled = false; + + validate_virtcol(); + while (curwin->w_skipcol > 0 + && curwin->w_virtcol < curwin->w_skipcol + 3 + scrolloff_cols) { + // scroll a screen line down + if (curwin->w_skipcol >= width1 + width2) { + curwin->w_skipcol -= width2; + } else { + curwin->w_skipcol -= width1; + } + redraw_later(curwin, UPD_NOT_VALID); + scrolled = true; + validate_virtcol(); + } + if (scrolled) { + return; // don't scroll in the other direction now + } + long col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols; + int row = 0; + if (col >= width1) { + col -= width1; + row++; + } + if (col > width2) { + row += (int)col / width2; + col = col % width2; + } + if (row >= curwin->w_height) { + if (curwin->w_skipcol == 0) { + curwin->w_skipcol += width1; + row--; + } + if (row >= curwin->w_height) { + curwin->w_skipcol += (row - curwin->w_height) * width2; + } + redraw_later(curwin, UPD_NOT_VALID); + } +} + /// Don't end up with too many filler lines in the window. /// /// @param down when true scroll down when not enough space diff --git a/src/nvim/normal.c b/src/nvim/normal.c index a51bde967e..d4759aeaba 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2566,6 +2566,8 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) if (atend) { curwin->w_curswant = MAXCOL; // stick in the last column } + adjust_skipcol(); + return retval; } diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index be7d324908..a5660ae4d2 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -428,14 +428,36 @@ describe('smoothscroll', function() | ]]) -- 'scrolloff' set to 2, scrolling down, cursor moves screen line up - feed('gjgj') + feed('gj') + screen:expect_unchanged() + end) + + -- oldtest: Test_smoothscroll_one_long_line() + it("scrolls correctly when moving the cursor", function() + screen:try_resize(40, 6) + exec([[ + call setline(1, 'with lots of text '->repeat(7)) + set smoothscroll scrolloff=0 + ]]) + local s1 = [[ + ^with lots of text with lots of text with| + lots of text with lots of text with lot| + s of text with lots of text with lots of| + text | + ~ | + | + ]] + screen:expect(s1) + feed('') screen:expect([[ - <<repeat(7)) + set smoothscroll scrolloff=0 + END + call writefile(lines, 'XSmoothOneLong', 'D') + let buf = RunVimInTerminal('-S XSmoothOneLong', #{rows: 6, cols: 40}) + call VerifyScreenDump(buf, 'Test_smooth_one_long_1', {}) + + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smooth_one_long_2', {}) + + call term_sendkeys(buf, "0") + call VerifyScreenDump(buf, 'Test_smooth_one_long_1', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab From 9189c2d16260824db3f1a9633c17245c474baa9a Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 23:12:28 +0200 Subject: [PATCH 21/45] vim-patch:9.0.0735: breakindent and scrolloff tests fail Problem: Breakindent and scrolloff tests fail. Solution: Temporarily skip the assertions. https://github.com/vim/vim/commit/e42033e735febb163fdc57aadbc11787ca611ba9 Co-authored-by: Bram Moolenaar --- test/old/testdir/test_breakindent.vim | 3 ++- test/old/testdir/test_normal.vim | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/test/old/testdir/test_breakindent.vim b/test/old/testdir/test_breakindent.vim index 3cbc1f4900..76634a5ae5 100644 --- a/test/old/testdir/test_breakindent.vim +++ b/test/old/testdir/test_breakindent.vim @@ -731,7 +731,8 @@ func Test_breakindent20_cpo_n_nextpage() \ " mnopqrstabcdefgh", \ " ijklmnopqrstabcd", \ ] - call s:compare_lines(expect, lines) + " FIXME: this currently fails + " call s:compare_lines(expect, lines) setl briopt+=shift:2 norm! 1gg diff --git a/test/old/testdir/test_normal.vim b/test/old/testdir/test_normal.vim index fe8611d527..295c1b3d6a 100644 --- a/test/old/testdir/test_normal.vim +++ b/test/old/testdir/test_normal.vim @@ -3742,9 +3742,11 @@ func Test_normal_scroloff() call setline(1, repeat('a', 1000)) set scrolloff=10 normal gg10gj - call assert_equal(8, winline()) +" FIXME: currently get 10 +" call assert_equal(8, winline()) normal 10gj - call assert_equal(10, winline()) +" FIXME: currently get 9 +" call assert_equal(10, winline()) normal 10gk call assert_equal(3, winline()) set scrolloff& From e8dfff5f288f5fcf4a11d62e973080600f05c980 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 23:23:50 +0200 Subject: [PATCH 22/45] vim-patch:9.0.0745: wrong cursor position when using "gj" and "gk" in a long line Problem: Wrong cursor position when using "gj" and "gk" in a long line. Solution: Adjust computations for the cursor position and skipcol. Re-enable tests that pass now, disable failing breakindent test. https://github.com/vim/vim/commit/4b6172e108fe06be47c09f8690dc54608be3ee80 Co-authored-by: Bram Moolenaar --- src/nvim/move.c | 12 ++++++-- test/old/testdir/test_breakindent.vim | 3 +- test/old/testdir/test_normal.vim | 42 ++++++++++++++++++++++----- 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index b2cf1ebc03..0cebcbdfdf 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -752,6 +752,7 @@ void curs_columns(win_T *wp, int may_scroll) colnr_T prev_skipcol; long so = get_scrolloff_value(wp); long siso = get_sidescrolloff_value(wp); + bool did_sub_skipcol = false; // First make sure that w_topline is valid (after moving the cursor). update_topline(wp); @@ -798,6 +799,7 @@ void curs_columns(win_T *wp, int may_scroll) if (wp->w_cursor.lnum == wp->w_topline && wp->w_wcol >= wp->w_skipcol) { wp->w_wcol -= wp->w_skipcol; + did_sub_skipcol = true; } // long line wrapping, adjust wp->w_wrow @@ -912,7 +914,7 @@ void curs_columns(win_T *wp, int may_scroll) extra += 2; } - if (extra == 3 || plines <= so * 2) { + if (extra == 3 || wp->w_height <= so * 2) { // not enough room for 'scrolloff', put cursor in the middle n = wp->w_virtcol / width; if (n > wp->w_height_inner / 2) { @@ -943,11 +945,17 @@ void curs_columns(win_T *wp, int may_scroll) endcol -= width; } if (endcol > wp->w_skipcol) { - wp->w_wrow -= (endcol - wp->w_skipcol) / width; wp->w_skipcol = endcol; } } + // adjust w_wrow for the changed w_skipcol + if (did_sub_skipcol) { + wp->w_wrow -= (wp->w_skipcol - prev_skipcol) / width; + } else { + wp->w_wrow -= wp->w_skipcol / width; + } + if (wp->w_wrow >= wp->w_height_inner) { // small window, make sure cursor is in it extra = wp->w_wrow - wp->w_height_inner + 1; diff --git a/test/old/testdir/test_breakindent.vim b/test/old/testdir/test_breakindent.vim index 76634a5ae5..2317cb8568 100644 --- a/test/old/testdir/test_breakindent.vim +++ b/test/old/testdir/test_breakindent.vim @@ -751,7 +751,8 @@ func Test_breakindent20_cpo_n_nextpage() \ " klmnopqrstabcd", \ " efghijklmnopqr", \ ] - call s:compare_lines(expect, lines) + " FIXME: this currently fails + " call s:compare_lines(expect, lines) call s:close_windows('set breakindent& briopt& cpo& number&') endfunc diff --git a/test/old/testdir/test_normal.vim b/test/old/testdir/test_normal.vim index 295c1b3d6a..600e58848b 100644 --- a/test/old/testdir/test_normal.vim +++ b/test/old/testdir/test_normal.vim @@ -3738,17 +3738,45 @@ endfunc " Test for 'scrolloff' with a long line that doesn't fit in the screen func Test_normal_scroloff() 10new - 80vnew - call setline(1, repeat('a', 1000)) + 60vnew + call setline(1, ' 1 ' .. repeat('a', 57) + \ .. ' 2 ' .. repeat('b', 57) + \ .. ' 3 ' .. repeat('c', 57) + \ .. ' 4 ' .. repeat('d', 57) + \ .. ' 5 ' .. repeat('e', 57) + \ .. ' 6 ' .. repeat('f', 57) + \ .. ' 7 ' .. repeat('g', 57) + \ .. ' 8 ' .. repeat('h', 57) + \ .. ' 9 ' .. repeat('i', 57) + \ .. '10 ' .. repeat('j', 57) + \ .. '11 ' .. repeat('k', 57) + \ .. '12 ' .. repeat('l', 57) + \ .. '13 ' .. repeat('m', 57) + \ .. '14 ' .. repeat('n', 57) + \ .. '15 ' .. repeat('o', 57) + \ .. '16 ' .. repeat('p', 57) + \ .. '17 ' .. repeat('q', 57) + \ .. '18 ' .. repeat('r', 57) + \ .. '19 ' .. repeat('s', 57) + \ .. '20 ' .. repeat('t', 57) + \ .. '21 ' .. repeat('u', 57) + \ .. '22 ' .. repeat('v', 57) + \ .. '23 ' .. repeat('w', 57) + \ .. '24 ' .. repeat('x', 57) + \ .. '25 ' .. repeat('y', 57) + \ .. '26 ' .. repeat('z', 57) + \ ) set scrolloff=10 normal gg10gj -" FIXME: currently get 10 -" call assert_equal(8, winline()) + call assert_equal(6, winline()) normal 10gj -" FIXME: currently get 9 -" call assert_equal(10, winline()) + call assert_equal(6, winline()) normal 10gk - call assert_equal(3, winline()) + call assert_equal(6, winline()) + normal 0 + call assert_equal(1, winline()) + normal $ + call assert_equal(10, winline()) set scrolloff& close! endfunc From 223c7173eea2a4abec75611bd70051ea0b4c5d20 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 23:36:59 +0200 Subject: [PATCH 23/45] vim-patch:9.0.0746: breakindent test cases are commented out Problem: Breakindent test cases are commented out. Solution: Adjust expected result to slightly different behavior. Correct computations for cursor position. https://github.com/vim/vim/commit/856c5d2bc7c3864f8b63a0ab3e376d5c5e51f1d5 Co-authored-by: Bram Moolenaar --- src/nvim/buffer_defs.h | 2 +- src/nvim/move.c | 76 ++++++++++++++------------- test/old/testdir/test_breakindent.vim | 10 ++-- 3 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 0f832d3659..ce8ee21882 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1170,7 +1170,7 @@ struct window_S { // 'wrap' is off colnr_T w_skipcol; // starting screen column for the first // line in the window; used when 'wrap' is - // on + // on; does not include win_col_off() // 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/move.c b/src/nvim/move.c index 0cebcbdfdf..a5383202a3 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -739,20 +739,14 @@ int curwin_col_off2(void) return win_col_off2(curwin); } -// Compute curwin->w_wcol and curwin->w_virtcol. -// Also updates curwin->w_wrow and curwin->w_cline_row. -// Also updates curwin->w_leftcol. +// Compute wp->w_wcol and wp->w_virtcol. +// Also updates wp->w_wrow and wp->w_cline_row. +// Also updates wp->w_leftcol. // @param may_scroll when true, may scroll horizontally void curs_columns(win_T *wp, int may_scroll) { - int n; - int width = 0; colnr_T startcol; colnr_T endcol; - colnr_T prev_skipcol; - long so = get_scrolloff_value(wp); - long siso = get_sidescrolloff_value(wp); - bool did_sub_skipcol = false; // First make sure that w_topline is valid (after moving the cursor). update_topline(wp); @@ -782,8 +776,11 @@ void curs_columns(win_T *wp, int may_scroll) // Now compute w_wrow, counting screen lines from w_cline_row. wp->w_wrow = wp->w_cline_row; - int textwidth = wp->w_width_inner - extra; - if (textwidth <= 0) { + int n; + int width1 = wp->w_width - extra; // text width for first screen line + int width2 = 0; // text width for second and later screen line + bool did_sub_skipcol = false; + if (width1 <= 0) { // No room for text, put cursor in last char of window. // If not wrapping, the last non-empty line. wp->w_wcol = wp->w_width_inner - 1; @@ -793,20 +790,27 @@ void curs_columns(win_T *wp, int may_scroll) wp->w_wrow = wp->w_height_inner - 1 - wp->w_empty_rows; } } else if (wp->w_p_wrap && wp->w_width_inner != 0) { - width = textwidth + win_col_off2(wp); + width2 = width1 + win_col_off2(wp); // skip columns that are not visible if (wp->w_cursor.lnum == wp->w_topline + && wp->w_skipcol > 0 && wp->w_wcol >= wp->w_skipcol) { - wp->w_wcol -= wp->w_skipcol; + // w_skipcol excludes win_col_off(). Include it here, since w_wcol + // counts actual screen columns. + if (wp->w_skipcol <= width1) { + wp->w_wcol -= wp->w_width; + } else { + wp->w_wcol -= wp->w_width * (((wp->w_skipcol - width1) / width2) + 1); + } did_sub_skipcol = true; } // long line wrapping, adjust wp->w_wrow if (wp->w_wcol >= wp->w_width_inner) { // this same formula is used in validate_cursor_col() - n = (wp->w_wcol - wp->w_width_inner) / width + 1; - wp->w_wcol -= n * width; + n = (wp->w_wcol - wp->w_width_inner) / width2 + 1; + wp->w_wcol -= n * width2; wp->w_wrow += n; // When cursor wraps to first char of next line in Insert @@ -828,6 +832,7 @@ void curs_columns(win_T *wp, int may_scroll) // If Cursor is right of the screen, scroll leftwards // If we get closer to the edge than 'sidescrolloff', scroll a little // extra + long siso = get_sidescrolloff_value(wp); assert(siso <= INT_MAX); int off_left = startcol - wp->w_leftcol - (int)siso; int off_right = @@ -838,8 +843,8 @@ void curs_columns(win_T *wp, int may_scroll) // When far off or not enough room on either side, put cursor in // middle of window. int new_leftcol; - if (p_ss == 0 || diff >= textwidth / 2 || off_right >= off_left) { - new_leftcol = wp->w_wcol - extra - textwidth / 2; + if (p_ss == 0 || diff >= width1 / 2 || off_right >= off_left) { + new_leftcol = curwin->w_wcol - extra - width1 / 2; } else { if (diff < p_ss) { assert(p_ss <= INT_MAX); @@ -876,9 +881,9 @@ void curs_columns(win_T *wp, int may_scroll) wp->w_wrow += win_get_fill(wp, wp->w_cursor.lnum); } - prev_skipcol = wp->w_skipcol; - int plines = 0; + long so = get_scrolloff_value(wp); + colnr_T prev_skipcol = wp->w_skipcol; if ((wp->w_wrow >= wp->w_height_inner || ((prev_skipcol > 0 || wp->w_wrow + so >= wp->w_height_inner) @@ -886,7 +891,7 @@ void curs_columns(win_T *wp, int may_scroll) >= wp->w_height_inner)) && wp->w_height_inner != 0 && wp->w_cursor.lnum == wp->w_topline - && width > 0 + && width2 > 0 && wp->w_width_inner != 0) { // Cursor past end of screen. Happens with a single line that does // not fit on screen. Find a skipcol to show the text around the @@ -895,7 +900,7 @@ void curs_columns(win_T *wp, int may_scroll) // 2: Less than "p_so" lines below // 3: both of them extra = 0; - if (wp->w_skipcol + so * width > wp->w_virtcol) { + if (wp->w_skipcol + so * width2 > wp->w_virtcol) { extra = 1; } // Compute last display line of the buffer line that we want at the @@ -910,13 +915,13 @@ void curs_columns(win_T *wp, int may_scroll) } else { n = plines; } - if ((colnr_T)n >= wp->w_height_inner + wp->w_skipcol / width - so) { + if ((colnr_T)n >= wp->w_height_inner + wp->w_skipcol / width2 - so) { extra += 2; } if (extra == 3 || wp->w_height <= so * 2) { // not enough room for 'scrolloff', put cursor in the middle - n = wp->w_virtcol / width; + n = wp->w_virtcol / width2; if (n > wp->w_height_inner / 2) { n -= wp->w_height_inner / 2; } else { @@ -926,23 +931,22 @@ void curs_columns(win_T *wp, int may_scroll) if (n > plines - wp->w_height_inner + 1) { n = plines - wp->w_height_inner + 1; } - wp->w_skipcol = n * width; + wp->w_skipcol = n * width2; } else if (extra == 1) { // less than 'scrolloff' lines above, decrease skipcol assert(so <= INT_MAX); - extra = (wp->w_skipcol + (int)so * width - wp->w_virtcol - + width - 1) / width; + extra = (wp->w_skipcol + (int)so * width2 - wp->w_virtcol + width2 - 1) / width2; if (extra > 0) { - if ((colnr_T)(extra * width) > wp->w_skipcol) { - extra = wp->w_skipcol / width; + if ((colnr_T)(extra * width2) > wp->w_skipcol) { + extra = wp->w_skipcol / width2; } - wp->w_skipcol -= extra * width; + wp->w_skipcol -= extra * width2; } } else if (extra == 2) { // less than 'scrolloff' lines below, increase skipcol - endcol = (n - wp->w_height_inner + 1) * width; + endcol = (n - wp->w_height_inner + 1) * width2; while (endcol > wp->w_virtcol) { - endcol -= width; + endcol -= width2; } if (endcol > wp->w_skipcol) { wp->w_skipcol = endcol; @@ -951,20 +955,20 @@ void curs_columns(win_T *wp, int may_scroll) // adjust w_wrow for the changed w_skipcol if (did_sub_skipcol) { - wp->w_wrow -= (wp->w_skipcol - prev_skipcol) / width; + wp->w_wrow -= (wp->w_skipcol - prev_skipcol) / width2; } else { - wp->w_wrow -= wp->w_skipcol / width; + wp->w_wrow -= wp->w_skipcol / width2; } if (wp->w_wrow >= wp->w_height_inner) { // small window, make sure cursor is in it extra = wp->w_wrow - wp->w_height_inner + 1; - wp->w_skipcol += extra * width; + wp->w_skipcol += extra * width2; wp->w_wrow -= extra; } // extra could be either positive or negative - extra = ((int)prev_skipcol - (int)wp->w_skipcol) / width; + extra = (prev_skipcol - wp->w_skipcol) / width2; win_scroll_lines(wp, 0, extra); } else if (!wp->w_p_sms) { wp->w_skipcol = 0; @@ -973,7 +977,7 @@ void curs_columns(win_T *wp, int may_scroll) redraw_later(wp, UPD_NOT_VALID); } - redraw_for_cursorcolumn(curwin); + redraw_for_cursorcolumn(wp); // now w_leftcol and w_skipcol are valid, avoid check_cursor_moved() // thinking otherwise diff --git a/test/old/testdir/test_breakindent.vim b/test/old/testdir/test_breakindent.vim index 2317cb8568..f6c0e32adf 100644 --- a/test/old/testdir/test_breakindent.vim +++ b/test/old/testdir/test_breakindent.vim @@ -723,7 +723,7 @@ func Test_breakindent20_cpo_n_nextpage() call s:compare_lines(expect, lines) " Scroll down one screen line setl scrolloff=5 - norm! 5gj + norm! 6gj redraw! let lines = s:screen_lines(1, 20) let expect = [ @@ -731,8 +731,7 @@ func Test_breakindent20_cpo_n_nextpage() \ " mnopqrstabcdefgh", \ " ijklmnopqrstabcd", \ ] - " FIXME: this currently fails - " call s:compare_lines(expect, lines) + call s:compare_lines(expect, lines) setl briopt+=shift:2 norm! 1gg @@ -744,15 +743,14 @@ func Test_breakindent20_cpo_n_nextpage() \ ] call s:compare_lines(expect, lines) " Scroll down one screen line - norm! 5gj + norm! 6gj let lines = s:screen_lines(1, 20) let expect = [ \ "<<< qrstabcdefghij", \ " klmnopqrstabcd", \ " efghijklmnopqr", \ ] - " FIXME: this currently fails - " call s:compare_lines(expect, lines) + call s:compare_lines(expect, lines) call s:close_windows('set breakindent& briopt& cpo& number&') endfunc From c426f7a6228cb82af0f75ac4f2421543408ff091 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 00:03:46 +0200 Subject: [PATCH 24/45] vim-patch:9.0.0751: 'scrolloff' does not work well with 'smoothscroll' Problem: 'scrolloff' does not work well with 'smoothscroll'. Solution: Make positioning the cursor a bit better. Rename functions. https://github.com/vim/vim/commit/c9121f798f49fa71e814912cb186d89c164090c3 Co-authored-by: Bram Moolenaar --- src/nvim/cursor.c | 4 ++-- src/nvim/edit.c | 4 ++-- src/nvim/ex_cmds.c | 6 ++---- src/nvim/move.c | 40 +++++++++++++++++++++++++++++++--------- src/nvim/normal.c | 8 ++++---- src/nvim/ops.c | 2 +- src/nvim/plines.c | 13 ++++++++++--- 7 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 6d90b32545..cdadc89e31 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -115,7 +115,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a col = wcol; if ((addspaces || finetune) && !VIsual_active) { - curwin->w_curswant = linetabsize(line) + one_more; + curwin->w_curswant = linetabsize_str(line) + one_more; if (curwin->w_curswant > 0) { curwin->w_curswant--; } @@ -129,7 +129,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a && curwin->w_width_inner != 0 && wcol >= (colnr_T)width && width > 0) { - csize = linetabsize(line); + csize = linetabsize_str(line); if (csize > 0) { csize--; } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 18434ad69f..2078fc4251 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -193,7 +193,7 @@ static void insert_enter(InsertState *s) } } - Insstart_textlen = (colnr_T)linetabsize(get_cursor_line_ptr()); + Insstart_textlen = linetabsize_str(get_cursor_line_ptr()); Insstart_blank_vcol = MAXCOL; if (!did_ai) { @@ -2251,7 +2251,7 @@ int stop_arrow(void) // right, except when nothing was inserted yet. update_Insstart_orig = false; } - Insstart_textlen = (colnr_T)linetabsize(get_cursor_line_ptr()); + Insstart_textlen = linetabsize_str(get_cursor_line_ptr()); if (u_save_cursor() == OK) { arrow_used = false; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index f276e8ae24..2c31f742c3 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -346,10 +346,8 @@ static int linelen(int *has_tab) last > first && ascii_iswhite(last[-1]); last--) {} char save = *last; *last = NUL; - // Get line length. - len = linetabsize(line); - // Check for embedded TAB. - if (has_tab != NULL) { + len = linetabsize_str(line); // Get line length. + if (has_tab != NULL) { // Check for embedded TAB. *has_tab = vim_strchr(first, TAB) != NULL; } *last = save; diff --git a/src/nvim/move.c b/src/nvim/move.c index a5383202a3..c080cf6691 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1238,7 +1238,7 @@ bool scrolldown(long line_count, int byfold) } if (curwin->w_cursor.lnum == curwin->w_topline && do_sms) { - long so = curwin->w_p_so >= 0 ? curwin->w_p_so : p_so; + long so = get_scrolloff_value(curwin); long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; // make sure the cursor is in the visible text @@ -1274,12 +1274,11 @@ bool scrollup(long line_count, int byfold) if (do_sms || (byfold && hasAnyFolding(curwin)) || win_may_fill(curwin)) { int width1 = curwin->w_width - curwin_col_off(); int width2 = width1 + curwin_col_off2(); - int size = 0; + unsigned size = 0; linenr_T prev_topline = curwin->w_topline; if (do_sms) { - size = (int)win_linetabsize(curwin, curwin->w_topline, - ml_get(curwin->w_topline), (colnr_T)MAXCOL); + size = linetabsize(curwin, curwin->w_topline); } // diff mode: first consume "topfill" @@ -1300,7 +1299,7 @@ bool scrollup(long line_count, int byfold) // the end of the line, then advance to the next line. int add = curwin->w_skipcol > 0 ? width2 : width1; curwin->w_skipcol += add; - if (curwin->w_skipcol >= size) { + if ((unsigned)curwin->w_skipcol >= size) { if (lnum == curbuf->b_ml.ml_line_count) { // at the last screen line, can't scroll further curwin->w_skipcol -= add; @@ -1322,8 +1321,7 @@ bool scrollup(long line_count, int byfold) curwin->w_topfill = win_get_fill(curwin, lnum); curwin->w_skipcol = 0; if (todo > 1 && do_sms) { - size = (int)win_linetabsize(curwin, curwin->w_topline, - ml_get(curwin->w_topline), (colnr_T)MAXCOL); + size = linetabsize(curwin, curwin->w_topline); } } } @@ -1363,11 +1361,16 @@ bool scrollup(long line_count, int byfold) if (curwin->w_cursor.lnum == curwin->w_topline && do_sms && curwin->w_skipcol > 0) { int width1 = curwin->w_width - curwin_col_off(); int width2 = width1 + curwin_col_off2(); - long so = curwin->w_p_so >= 0 ? curwin->w_p_so : p_so; + long so = get_scrolloff_value(curwin); long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; + int space_cols = (curwin->w_height - 1) * width2; // Make sure the cursor is in a visible part of the line, taking // 'scrolloff' into account, but using screen lines. + // If there are not enough screen lines put the cursor in the middle. + if (scrolloff_cols > space_cols / 2) { + scrolloff_cols = space_cols / 2; + } validate_virtcol(); if (curwin->w_virtcol < curwin->w_skipcol + 3 + scrolloff_cols) { colnr_T col = curwin->w_virtcol; @@ -1402,11 +1405,20 @@ void adjust_skipcol(void) int width1 = curwin->w_width - curwin_col_off(); int width2 = width1 + curwin_col_off2(); - long so = curwin->w_p_so >= 0 ? curwin->w_p_so : p_so; + long so = get_scrolloff_value(curwin); long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; bool scrolled = false; validate_virtcol(); + if (curwin->w_cline_height == curwin->w_height) { + // the line just fits in the window, don't scroll + if (curwin->w_skipcol != 0) { + curwin->w_skipcol = 0; + redraw_later(curwin, UPD_NOT_VALID); + } + return; + } + while (curwin->w_skipcol > 0 && curwin->w_virtcol < curwin->w_skipcol + 3 + scrolloff_cols) { // scroll a screen line down @@ -2071,6 +2083,16 @@ void cursor_correct(void) return; } + if (curwin->w_p_sms && !curwin->w_p_wrap) { + // 'smoothscroll is active + if (curwin->w_cline_height == curwin->w_height) { + // The cursor line just fits in the window, don't scroll. + curwin->w_skipcol = 0; + return; + } + // TODO(vim): If the cursor line doesn't fit in the window then only adjust w_skipcol. + } + // Narrow down the area where the cursor can be put by taking lines from // the top and the bottom until: // - the desired context lines are found diff --git a/src/nvim/normal.c b/src/nvim/normal.c index d4759aeaba..9792f5950e 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2432,7 +2432,7 @@ bool find_decl(char *ptr, size_t len, bool locally, bool thisblock, int flags_ar /// @return true if able to move cursor, false otherwise. static bool nv_screengo(oparg_T *oap, int dir, long dist) { - int linelen = linetabsize(get_cursor_line_ptr()); + int linelen = linetabsize_str(get_cursor_line_ptr()); bool retval = true; bool atend = false; int col_off1; // margin offset for first screen line @@ -2494,7 +2494,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) retval = false; break; } - linelen = linetabsize(get_cursor_line_ptr()); + linelen = linetabsize_str(get_cursor_line_ptr()); if (linelen > width1) { int w = (((linelen - width1 - 1) / width2) + 1) * width2; assert(curwin->w_curswant <= INT_MAX - w); @@ -2525,7 +2525,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) if (curwin->w_curswant >= width1) { curwin->w_curswant -= width2; } - linelen = linetabsize(get_cursor_line_ptr()); + linelen = linetabsize_str(get_cursor_line_ptr()); } } } @@ -5497,7 +5497,7 @@ static void nv_g_cmd(cmdarg_T *cap) case 'M': oap->motion_type = kMTCharWise; oap->inclusive = false; - i = linetabsize(get_cursor_line_ptr()); + i = linetabsize_str(get_cursor_line_ptr()); if (cap->count0 > 0 && cap->count0 <= 100) { coladvance((colnr_T)(i * cap->count0 / 100)); } else { diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 89fe9b464d..d8380303a3 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -5503,7 +5503,7 @@ void cursor_pos_info(dict_T *dict) validate_virtcol(); col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1); - col_print(buf2, sizeof(buf2), (int)strlen(p), linetabsize(p)); + col_print(buf2, sizeof(buf2), (int)strlen(p), linetabsize_str(p)); if (char_count_cursor == byte_count_cursor && char_count == byte_count) { diff --git a/src/nvim/plines.c b/src/nvim/plines.c index b2a4ac710d..3c8ee7d66d 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -243,12 +243,12 @@ int win_chartabsize(win_T *wp, char *p, colnr_T col) /// @param s /// /// @return Number of characters the string will take on the screen. -int linetabsize(char *s) +int linetabsize_str(char *s) { return linetabsize_col(0, s); } -/// Like linetabsize(), but "s" starts at column "startcol". +/// Like linetabsize_str(), but "s" starts at column "startcol". /// /// @param startcol /// @param s @@ -265,7 +265,7 @@ int linetabsize_col(int startcol, char *s) return cts.cts_vcol; } -/// Like linetabsize(), but for a given window instead of the current one. +/// Like linetabsize_str(), but for a given window instead of the current one. /// /// @param wp /// @param line @@ -284,6 +284,13 @@ unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) return (unsigned)cts.cts_vcol; } +/// 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. +unsigned linetabsize(win_T *wp, linenr_T lnum) +{ + return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, false), (colnr_T)MAXCOL); +} + /// Prepare the structure passed to chartabsize functions. /// /// "line" is the start of the line, "ptr" is the first relevant character. From 6146400605af93ac48dae4393569c44e8a2e39d2 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 00:57:48 +0200 Subject: [PATCH 25/45] vim-patch:9.0.0757: line number not visisble with 'smoothscroll', 'nu' and 'rnu' Problem: Line number not visisble with 'smoothscroll', 'nu' and 'rnu'. Solution: Put the ">>>" after the line number instead of on top. https://github.com/vim/vim/commit/eb4de629315f2682d8b314462d02422ec98d751a Co-authored-by: Bram Moolenaar --- src/nvim/drawline.c | 2 +- src/nvim/grid.c | 18 +++++++++++++++--- test/functional/legacy/scroll_opt_spec.lua | 21 +++++++++++++++++++++ test/old/testdir/test_scroll_opt.vim | 14 ++++++++++++++ 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 0d701f07bb..ef9912e503 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -609,7 +609,7 @@ static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int num_signs, int si } else { // Draw the line number (empty space after wrapping). if (wlv->row == wlv->startrow + wlv->filler_lines - && (wp->w_skipcol == 0 || wlv->row > wp->w_winrow)) { + && (wp->w_skipcol == 0 || wlv->row > wp->w_winrow || (wp->w_p_nu && wp->w_p_rnu))) { get_line_number_str(wp, wlv->lnum, wlv->extra, sizeof(wlv->extra)); if (wp->w_skipcol > 0 && wlv->startrow == 0) { for (wlv->p_extra = wlv->extra; *wlv->p_extra == ' '; wlv->p_extra++) { diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 0ceaeaa8b2..8431c078b9 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -533,9 +533,21 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle if (topline && wp->w_skipcol > 0 && *get_showbreak_value(wp) == NUL) { // Take care of putting "<<<" on the first line for 'smoothscroll' // when 'showbreak' is not set. - for (int i = 0; i < 3; i++) { - schar_from_ascii(linebuf_char[i], '<'); - linebuf_attr[i] = HL_ATTR(HLF_AT); + int off = 0; + int skip = 0; + if (wp->w_p_nu && wp->w_p_rnu) { + // do not overwrite the line number, change "123 text" to + // "123>>>xt". + while (skip < wp->w_width && ascii_isdigit(*linebuf_char[off])) { + off++; + skip++; + } + } + + for (int i = 0; i < 3 && i + skip < wp->w_width; i++) { + schar_from_ascii(linebuf_char[off], '<'); + linebuf_attr[off] = HL_ATTR(HLF_AT); + off++; } } diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index a5660ae4d2..5e54470bd4 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -181,6 +181,12 @@ describe('smoothscroll', function() set smoothscroll scrolloff=5 set number cpo+=n :3 + func g:DoRel() + set number relativenumber scrolloff=0 + :%del + call setline(1, [ 'one', 'very long text '->repeat(12), 'three', ]) + exe "normal 2Gzt\" + endfunc ]]) screen:expect([[ 1 one word word word word word word wo| @@ -271,6 +277,21 @@ describe('smoothscroll', function() ~ | | ]]) + exec('call DoRel()') + screen:expect([[ + 2<<repeat(12), + 'three', + ]) + exe "normal 2Gzt\" + enddef END call writefile(lines, 'XSmoothNumber', 'D') let buf = RunVimInTerminal('-S XSmoothNumber', #{rows: 12, cols: 40}) @@ -155,6 +166,9 @@ func Test_smoothscroll_number() call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_number_6', {}) + call term_sendkeys(buf, ":call DoRel()\") + call VerifyScreenDump(buf, 'Test_smooth_number_7', {}) + call StopVimInTerminal(buf) endfunc From 72c525d5675bc017fa52bcc74a394db7d01e6359 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 02:54:51 +0200 Subject: [PATCH 26/45] vim-patch:9.0.0758: "precedes" from 'listchars' overwritten by <<< Problem: "precedes" from 'listchars' overwritten by <<< for 'smoothscroll'. Solution: Keep the "precedes" character. https://github.com/vim/vim/commit/13cdde39520220bb856cba16626327c706752b51 Co-authored-by: Bram Moolenaar --- src/nvim/grid.c | 9 ++++-- test/functional/legacy/scroll_opt_spec.lua | 32 ++++++++++++++++++++++ test/old/testdir/test_scroll_opt.vim | 25 +++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 8431c078b9..9e830413bd 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -530,9 +530,12 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle max_off_from = linebuf_size; max_off_to = grid->line_offset[row] + (size_t)grid->cols; - if (topline && wp->w_skipcol > 0 && *get_showbreak_value(wp) == NUL) { - // Take care of putting "<<<" on the first line for 'smoothscroll' - // when 'showbreak' is not set. + // Take care of putting "<<<" on the first line for 'smoothscroll'. + if (topline && wp->w_skipcol > 0 + // do not overwrite the 'showbreak' text with "<<<" + && *get_showbreak_value(wp) == NUL + // do not overwrite the 'listchars' "precedes" text with "<<<" + && !(wp->w_p_list && wp->w_p_lcs_chars.prec != 0)) { int off = 0; int skip = 0; if (wp->w_p_nu && wp->w_p_rnu) { diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 5e54470bd4..b5a571d8b5 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -294,6 +294,38 @@ describe('smoothscroll', function() ]]) end) + -- oldtest: Test_smoothscroll_list() + it("works with list mode", function() + screen:try_resize(40, 8) + exec([[ + set smoothscroll scrolloff=0 + set list + call setline(1, [ 'one', 'very long text '->repeat(12), 'three', ]) + exe "normal 2Gzt\" + ]]) + screen:expect([[ + <<repeat(12), + 'three', + ]) + exe "normal 2Gzt\" + END + call writefile(lines, 'XSmoothList', 'D') + let buf = RunVimInTerminal('-S XSmoothList', #{rows: 8, cols: 40}) + + call VerifyScreenDump(buf, 'Test_smooth_list_1', {}) + + call term_sendkeys(buf, ":set listchars+=precedes:#\") + call VerifyScreenDump(buf, 'Test_smooth_list_2', {}) + + call StopVimInTerminal(buf) +endfunc + func Test_smoothscroll_diff_mode() CheckScreendump From 0588329c8569553e3232e72889a28496a30aa54b Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 03:15:46 +0200 Subject: [PATCH 27/45] vim-patch:9.0.0760: display test for 'listchars' "precedes" fails Problem: Display test for 'listchars' "precedes" fails. Solution: Correct the expected result. https://github.com/vim/vim/commit/297164cb7972beff35e375ccac4fbad8196ccbd7 Co-authored-by: Bram Moolenaar --- test/functional/ui/highlight_spec.lua | 2 +- test/old/testdir/test_display.vim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 01895003ae..89b503141b 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -704,7 +704,7 @@ describe("'listchars' highlight", function() feed_command('set listchars=eol:¬,precedes:< list') feed('90ia') screen:expect([[ - {0:<<<}aaaaaaaaaaaaaaaaa| + {0:<}aaaaaaaaaaaaaaaaaaa| aaaaaaaaaaaaaaaaaaaa| aaaaaaaaaaaaaaaaaaaa| aaaaaaaaa^a{0:¬} | diff --git a/test/old/testdir/test_display.vim b/test/old/testdir/test_display.vim index aae1030aac..b642f39c9f 100644 --- a/test/old/testdir/test_display.vim +++ b/test/old/testdir/test_display.vim @@ -146,7 +146,7 @@ func Test_display_listchars_precedes() let lines = ScreenLines([1,10], winwidth(0)+1) let expect = [ - \ "<< Date: Thu, 27 Apr 2023 03:36:31 +0200 Subject: [PATCH 28/45] vim-patch:9.0.0807: with 'smoothscroll' typing "0" may not go to the first column Problem: With 'smoothscroll' typing "0" may not go to the first column. Solution: Recompute w_cline_height when needed. Do not scroll up when it would move the cursor. https://github.com/vim/vim/commit/d5337efece7c68e9b4ce864532ea49b02453b674 Co-authored-by: Bram Moolenaar --- src/nvim/move.c | 58 ++++++++++++++++------ src/nvim/plines.c | 9 ++-- test/functional/legacy/scroll_opt_spec.lua | 9 +++- 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index c080cf6691..f245890e40 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -79,6 +79,21 @@ static int adjust_plines_for_skipcol(win_T *wp, int n) return n - off; } +/// Return how many lines "lnum" will take on the screen, taking into account +/// whether it is the first line, whether w_skipcol is non-zero and limiting to +/// the window height. +static int plines_correct_topline(win_T *wp, linenr_T lnum, linenr_T *nextp, bool *foldedp) +{ + int n = plines_win_full(wp, lnum, nextp, foldedp, true, false); + if (lnum == wp->w_topline) { + n = adjust_plines_for_skipcol(wp, n); + } + if (n > wp->w_height) { + return wp->w_height; + } + return n; +} + // 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) @@ -100,10 +115,7 @@ static void comp_botline(win_T *wp) for (; lnum <= wp->w_buffer->b_ml.ml_line_count; lnum++) { 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); - } + int n = plines_correct_topline(wp, lnum, &last, &folded); if (lnum <= wp->w_cursor.lnum && last >= wp->w_cursor.lnum) { wp->w_cline_row = done; wp->w_cline_height = n; @@ -606,10 +618,7 @@ static void curs_rows(win_T *wp) } else { 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); - } + int n = plines_correct_topline(wp, lnum, &last, &folded); lnum = last + 1; if (folded && lnum > wp->w_cursor.lnum) { break; @@ -626,7 +635,7 @@ static void curs_rows(win_T *wp) && (!wp->w_lines[i].wl_valid || wp->w_lines[i].wl_lnum != wp->w_cursor.lnum))) { wp->w_cline_height = plines_win_full(wp, wp->w_cursor.lnum, NULL, - &wp->w_cline_folded, true); + &wp->w_cline_folded, true, true); } else if (i > wp->w_lines_valid) { // a line that is too long to fit on the last screen line wp->w_cline_height = 0; @@ -673,7 +682,7 @@ void validate_cheight(void) curwin->w_cline_height = plines_win_full(curwin, curwin->w_cursor.lnum, NULL, &curwin->w_cline_folded, - true); + true, true); curwin->w_valid |= VALID_CHEIGHT; } @@ -1261,6 +1270,14 @@ bool scrolldown(long line_count, int byfold) return moved; } +/// Return TRUE if scrollup() will scroll by screen line rather than text line. +static int scrolling_screenlines(bool byfold) +{ + return (curwin->w_p_wrap && curwin->w_p_sms) + || (byfold && hasAnyFolding(curwin)) + || curwin->w_p_diff; +} + /// Scroll the current window up by "line_count" logical lines. "CTRL-E" /// /// @param line_count number of lines to scroll @@ -1271,7 +1288,7 @@ bool scrollup(long line_count, int byfold) linenr_T botline = curwin->w_botline; int do_sms = curwin->w_p_wrap && curwin->w_p_sms; - if (do_sms || (byfold && hasAnyFolding(curwin)) || win_may_fill(curwin)) { + if (scrolling_screenlines(byfold) || win_may_fill(curwin)) { int width1 = curwin->w_width - curwin_col_off(); int width2 = width1 + curwin_col_off2(); unsigned size = 0; @@ -1409,7 +1426,7 @@ void adjust_skipcol(void) long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; bool scrolled = false; - validate_virtcol(); + validate_cheight(); if (curwin->w_cline_height == curwin->w_height) { // the line just fits in the window, don't scroll if (curwin->w_skipcol != 0) { @@ -1419,6 +1436,7 @@ void adjust_skipcol(void) return; } + validate_virtcol(); while (curwin->w_skipcol > 0 && curwin->w_virtcol < curwin->w_skipcol + 3 + scrolloff_cols) { // scroll a screen line down @@ -1940,11 +1958,21 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) if (line_count >= curwin->w_height_inner && line_count > min_scroll) { scroll_cursor_halfway(false, true); } else { - // With 'smoothscroll' scroll at least the height of the cursor line. - if (curwin->w_p_wrap && curwin->w_p_sms && line_count < min_scrolled) { + // With 'smoothscroll' scroll at least the height of the cursor line, + // unless it would move the cursor. + if (curwin->w_p_wrap && curwin->w_p_sms && line_count < min_scrolled + && (curwin->w_cursor.lnum < curwin->w_topline + || (curwin->w_virtcol - curwin->w_skipcol >= + curwin->w_width - curwin_col_off()))) { line_count = min_scrolled; } - scrollup(line_count, true); + if (line_count > 0) { + if (scrolling_screenlines(true)) { + scrollup(scrolled, true); // TODO(vim): + } else { + scrollup(line_count, true); + } + } } // If topline didn't change we need to restore w_botline and w_empty_rows diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 3c8ee7d66d..3e69e547cb 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -189,10 +189,11 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) /// @param[out] nextp if not NULL, the line after a fold /// @param[out] foldedp if not NULL, whether lnum is on a fold /// @param[in] cache whether to use the window's cache for folds +/// @param[in] winheight when true limit to window height /// /// @return the total number of screen lines int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, bool *const foldedp, - const bool cache) + const bool cache, const bool winheight) { bool folded = hasFoldingWin(wp, lnum, NULL, nextp, cache, NULL); if (foldedp) { @@ -201,9 +202,9 @@ int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, bool *const if (folded) { return 1; } else if (lnum == wp->w_topline) { - return plines_win_nofill(wp, lnum, true) + wp->w_topfill; + return plines_win_nofill(wp, lnum, winheight) + wp->w_topfill; } - return plines_win(wp, lnum, true); + return plines_win(wp, lnum, winheight); } int plines_m_win(win_T *wp, linenr_T first, linenr_T last) @@ -212,7 +213,7 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last) while (first <= last) { linenr_T next = first; - count += plines_win_full(wp, first, &next, NULL, false); + count += plines_win_full(wp, first, &next, NULL, false, true); first = next + 1; } return count; diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index b5a571d8b5..42d8f31d3c 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -482,7 +482,14 @@ describe('smoothscroll', function() ]]) -- 'scrolloff' set to 2, scrolling down, cursor moves screen line up feed('gj') - screen:expect_unchanged() + screen:expect([[ + << Date: Thu, 27 Apr 2023 05:27:31 +0200 Subject: [PATCH 29/45] vim-patch:9.0.0892: may redraw when not needed Problem: May redraw when not needed, causing slow scrolling. Solution: Do not redraw when w_skipcol doesn't change. When w_skipcol changes only redraw from the top. (issue vim/vim#11559) https://github.com/vim/vim/commit/f32fb93e431e4db95a8663d86dfeb6bffa5896f6 Co-authored-by: Bram Moolenaar --- src/nvim/ex_getln.c | 2 ++ src/nvim/move.c | 34 ++++++++++++++++++++-------------- test/old/testdir/test_alot.vim | 1 - 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 5018c9268b..af2ec3356f 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -504,6 +504,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat } validate_cursor(); + // May redraw the status line to show the cursor position. if (p_ru && (curwin->w_status_height > 0 || global_stl_height() > 0)) { curwin->w_redr_status = true; @@ -598,6 +599,7 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool magic_overruled = s->magic_overruled_save; validate_cursor(); // needed for TAB + status_redraw_all(); redraw_all_later(UPD_SOME_VALID); if (call_update_screen) { update_screen(); diff --git a/src/nvim/move.c b/src/nvim/move.c index f245890e40..cc8497f544 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -164,6 +164,19 @@ static void redraw_for_cursorcolumn(win_T *wp) } } +/// Set wp->s_skipcol to zero and redraw later if needed. +static void reset_skipcol(win_T *wp) +{ + if (wp->w_skipcol != 0) { + wp->w_skipcol = 0; + + // Should use the least expensive way that displays all that changed. + // UPD_NOT_VALID is too expensive, UPD_REDRAW_TOP does not redraw + // enough when the top line gets another screen line. + redraw_later(wp, UPD_SOME_VALID); + } +} + // Update curwin->w_topline to move the cursor onto the screen. void update_topline(win_T *wp) { @@ -360,12 +373,9 @@ void update_topline(win_T *wp) if (wp->w_topline != old_topline || wp->w_topfill != old_topfill) { dollar_vcol = -1; - if (wp->w_skipcol != 0) { - wp->w_skipcol = 0; - redraw_later(wp, UPD_NOT_VALID); - } else { - redraw_later(wp, UPD_VALID); - } + redraw_later(wp, UPD_VALID); + reset_skipcol(wp); + // May need to set w_skipcol when cursor in w_topline. if (wp->w_cursor.lnum == wp->w_topline) { validate_cursor(); @@ -983,7 +993,7 @@ void curs_columns(win_T *wp, int may_scroll) wp->w_skipcol = 0; } if (prev_skipcol != wp->w_skipcol) { - redraw_later(wp, UPD_NOT_VALID); + redraw_later(wp, UPD_SOME_VALID); } redraw_for_cursorcolumn(wp); @@ -1429,10 +1439,7 @@ void adjust_skipcol(void) validate_cheight(); if (curwin->w_cline_height == curwin->w_height) { // the line just fits in the window, don't scroll - if (curwin->w_skipcol != 0) { - curwin->w_skipcol = 0; - redraw_later(curwin, UPD_NOT_VALID); - } + reset_skipcol(curwin); return; } @@ -1758,8 +1765,7 @@ void scroll_cursor_top(int min_scroll, int always) check_topfill(curwin, false); // TODO(vim): if the line doesn't fit may optimize w_skipcol if (curwin->w_topline == curwin->w_cursor.lnum) { - curwin->w_skipcol = 0; - redraw_later(curwin, UPD_NOT_VALID); + reset_skipcol(curwin); } if (curwin->w_topline != old_topline || curwin->w_skipcol != old_skipcol @@ -2115,7 +2121,7 @@ void cursor_correct(void) // 'smoothscroll is active if (curwin->w_cline_height == curwin->w_height) { // The cursor line just fits in the window, don't scroll. - curwin->w_skipcol = 0; + reset_skipcol(curwin); return; } // TODO(vim): If the cursor line doesn't fit in the window then only adjust w_skipcol. diff --git a/test/old/testdir/test_alot.vim b/test/old/testdir/test_alot.vim index 4a22315b9f..2a959f0834 100644 --- a/test/old/testdir/test_alot.vim +++ b/test/old/testdir/test_alot.vim @@ -17,7 +17,6 @@ source test_global.vim source test_move.vim source test_put.vim source test_reltime.vim -source test_scroll_opt.vim source test_searchpos.vim source test_set.vim source test_shift.vim From a2f3855291a59254346545f9699084fe4fece31f Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 17:51:47 +0200 Subject: [PATCH 30/45] vim-patch:9.0.0893: 'smoothscroll' cursor calculations wrong when 'number' is set Problem: 'smoothscroll' cursor calculations wrong when 'number' is set. Solution: Correct the code that computes the width. (closes vim/vim#11492) https://github.com/vim/vim/commit/01ee52bab6041450095c53f9469b1b266a7e3d4d Co-authored-by: Yee Cheng Chin --- src/nvim/move.c | 9 +-- test/functional/legacy/scroll_opt_spec.lua | 4 +- test/old/testdir/test_scroll_opt.vim | 66 ++++++++++++++++++++++ 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index cc8497f544..8113331c62 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -815,13 +815,14 @@ void curs_columns(win_T *wp, int may_scroll) if (wp->w_cursor.lnum == wp->w_topline && wp->w_skipcol > 0 && wp->w_wcol >= wp->w_skipcol) { - // w_skipcol excludes win_col_off(). Include it here, since w_wcol - // counts actual screen columns. + // Deduct by multiples of width2. This allows the long line wrapping + // formula below to correctly calculate the w_wcol value when wrapping. if (wp->w_skipcol <= width1) { - wp->w_wcol -= wp->w_width; + wp->w_wcol -= width2; } else { - wp->w_wcol -= wp->w_width * (((wp->w_skipcol - width1) / width2) + 1); + wp->w_wcol -= width2 * (((wp->w_skipcol - width1) / width2) + 1); } + did_sub_skipcol = true; } diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 42d8f31d3c..8f0286771a 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -279,8 +279,8 @@ describe('smoothscroll', function() ]]) exec('call DoRel()') screen:expect([[ - 2<<" + + " Move down another line to avoid blocking the <<< display + call s:check_col_calc(1, 2, 41) + exe "normal \" + call s:check_col_calc(1, 3, 41) + normal ggg$ + exe "normal \" + + " Move down only 1 line when we are out of the range of the <<< display + call s:check_col_calc(20, 1, 40) + exe "normal \" + call s:check_col_calc(20, 2, 40) + normal gg + + " Test number, where we have indented lines + setl number + call s:check_col_calc(5, 1, 1) + exe "normal \" + call s:check_col_calc(5, 2, 33) + exe "normal \" + call s:check_col_calc(5, 3, 33) + normal ggg$ + exe "normal \" + call s:check_col_calc(20, 1, 32) + exe "normal \" + call s:check_col_calc(20, 2, 32) + normal gg + + " Test number + showbreak, so test that the additional indentation works + setl number showbreak=+++ + call s:check_col_calc(5, 1, 1) + exe "normal \" + call s:check_col_calc(8, 2, 30) + exe "normal \" + call s:check_col_calc(8, 3, 30) + normal gg + + " Test number + cpo+=n mode, where wrapped lines aren't indented + setl number cpo+=n showbreak= + call s:check_col_calc(5, 1, 1) + exe "normal \" + call s:check_col_calc(1, 2, 37) + exe "normal \" + call s:check_col_calc(1, 3, 37) + normal gg + + bwipeout! +endfunc + " vim: shiftwidth=2 sts=2 expandtab From 0bcf2a6382eca34a827b678987e03874001d9610 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 18:20:34 +0200 Subject: [PATCH 31/45] vim-patch:9.0.0898: with 'smoothscroll' cursor is one screen line too far down Problem: With 'smoothscroll' cursor is one screen line too far down. (Ernie Rael) Solution: Add a test that currently has the wrong result so that a fix can be made. (issue vim/vim#11436) https://github.com/vim/vim/commit/75ac25b4967cdcdfdf2d6c086a6e2308868c280a Co-authored-by: Bram Moolenaar --- test/functional/legacy/scroll_opt_spec.lua | 30 ++++++++++++++++++++++ test/old/testdir/test_scroll_opt.vim | 23 +++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 8f0286771a..2f88221032 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -520,4 +520,34 @@ describe('smoothscroll', function() feed('0') screen:expect(s1) end) + + -- oldtest: Test_smoothscroll_long_line_showbreak() + it("cursor is not one screen line too far down", function() + screen:try_resize(40, 6) + exec([[ + " a line that spans four screen lines + call setline(1, 'with lots of text in one line '->repeat(6)) + set smoothscroll scrolloff=0 showbreak=+++\ + ]]) + local s1 = [[ + ^with lots of text in one line with lots | + +++ of text in one line with lots of tex| + +++ t in one line with lots of text in o| + +++ ne line with lots of text in one lin| + +++ e with lots of text in one line | + | + ]] + screen:expect(s1) + feed('') + screen:expect([[ + +++ of text in one line with lots of tex| + +++ ^t in one line with lots of text in o| + +++ ne line with lots of text in one lin| + +++ e with lots of text in one line | + ~ | + | + ]]) + feed('0') + screen:expect(s1) + end) end) diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index c3600ab4a7..657172edea 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -325,6 +325,29 @@ func Test_smoothscroll_one_long_line() call StopVimInTerminal(buf) endfunc +func Test_smoothscroll_long_line_showbreak() + CheckScreendump + + let lines =<< trim END + vim9script + # a line that spans four screen lines + setline(1, 'with lots of text in one line '->repeat(6)) + set smoothscroll scrolloff=0 showbreak=+++\ + END + call writefile(lines, 'XSmoothLongShowbreak', 'D') + let buf = RunVimInTerminal('-S XSmoothLongShowbreak', #{rows: 6, cols: 40}) + call VerifyScreenDump(buf, 'Test_smooth_long_showbreak_1', {}) + + " FIXME: this currently has the cursor in screen line 2, should be one up. + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_smooth_long_showbreak_2', {}) + + call term_sendkeys(buf, "0") + call VerifyScreenDump(buf, 'Test_smooth_long_showbreak_1', {}) + + call StopVimInTerminal(buf) +endfunc + " Test that if the current cursor is on a smooth scrolled line, we correctly " reposition it. Also check that we don't miscalculate the values by checking " the consistency between wincol() and col('.') as they are calculated From 3621604029119a8806da006eb0468cf65e23b980 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 18:35:25 +0200 Subject: [PATCH 32/45] vim-patch:9.0.0900: cursor moves too far with 'smoothscroll' Problem: Cursor moves too far with 'smoothscroll'. Solution: Only move as far as really needed. (Yee Cheng Chin, closes vim/vim#11504) https://github.com/vim/vim/commit/81ba26e9de24ca6b1c05b6ec03e53b21793f1a4b Co-authored-by: Yee Cheng Chin --- src/nvim/move.c | 45 ++++++++++++++++----- test/functional/legacy/scroll_opt_spec.lua | 32 ++++++++++----- test/old/testdir/test_scroll_opt.vim | 47 ++++++++++++++++++---- 3 files changed, 96 insertions(+), 28 deletions(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index 8113331c62..b0a4a3848a 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -164,6 +164,22 @@ static void redraw_for_cursorcolumn(win_T *wp) } } +/// Calculates how much overlap the smoothscroll marker "<<<" overlaps with +/// buffer text for curwin. +/// Parameter "extra2" should be the padding on the 2nd line, not the first +/// line. +/// Returns the number of columns of overlap with buffer text, excluding the +/// extra padding on the ledge. +static int smoothscroll_marker_overlap(win_T *wp, int extra2) +{ + // We don't draw the <<< marker when in showbreak mode, thus no need to + // account for it. See grid_put_linebuf(). + if (*get_showbreak_value(wp) != NUL) { + return 0; + } + return extra2 > 3 ? 0 : 3 - extra2; +} + /// Set wp->s_skipcol to zero and redraw later if needed. static void reset_skipcol(win_T *wp) { @@ -239,10 +255,12 @@ void update_topline(win_T *wp) } else if (wp->w_skipcol > 0 && wp->w_cursor.lnum == wp->w_topline) { colnr_T vcol; - // check the cursor position is visible. Add 3 for the ">>>" - // displayed in the top-left. + // Check that the cursor position is visible. Add columns for the + // smoothscroll marker "<<<" displayed in the top-left if needed. getvvcol(wp, &wp->w_cursor, &vcol, NULL, NULL); - if (wp->w_skipcol + 3 >= vcol) { + int smoothscroll_overlap = smoothscroll_marker_overlap(wp, + win_col_off(wp) - win_col_off2(wp)); + if (wp->w_skipcol + smoothscroll_overlap > vcol) { check_topline = true; } } @@ -1387,12 +1405,21 @@ bool scrollup(long line_count, int byfold) } if (curwin->w_cursor.lnum == curwin->w_topline && do_sms && curwin->w_skipcol > 0) { - int width1 = curwin->w_width - curwin_col_off(); - int width2 = width1 + curwin_col_off2(); + int col_off = curwin_col_off(); + int col_off2 = curwin_col_off2(); + + int width1 = curwin->w_width - col_off; + int width2 = width1 + col_off2; + int extra2 = col_off - col_off2; long so = get_scrolloff_value(curwin); long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; int space_cols = (curwin->w_height - 1) * width2; + // If we have non-zero scrolloff, just ignore the <<< marker as we are + // going past it anyway. + int smoothscroll_overlap = scrolloff_cols != 0 ? 0 : + smoothscroll_marker_overlap(curwin, extra2); + // Make sure the cursor is in a visible part of the line, taking // 'scrolloff' into account, but using screen lines. // If there are not enough screen lines put the cursor in the middle. @@ -1400,13 +1427,13 @@ bool scrollup(long line_count, int byfold) scrolloff_cols = space_cols / 2; } validate_virtcol(); - if (curwin->w_virtcol < curwin->w_skipcol + 3 + scrolloff_cols) { + if (curwin->w_virtcol < curwin->w_skipcol + smoothscroll_overlap + scrolloff_cols) { colnr_T col = curwin->w_virtcol; if (col < width1) { col += width1; } - while (col < curwin->w_skipcol + 3 + scrolloff_cols) { + while (col < curwin->w_skipcol + smoothscroll_overlap + scrolloff_cols) { col += width2; } curwin->w_curswant = col; @@ -2180,9 +2207,9 @@ void cursor_correct(void) curwin->w_viewport_invalid = true; } -// move screen 'count' pages up or down and update screen +// Move screen "count" pages up or down and update screen. // -// return FAIL for failure, OK otherwise +// Return FAIL for failure, OK otherwise. int onepage(Direction dir, long count) { long n; diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 2f88221032..a05470418f 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -279,8 +279,8 @@ describe('smoothscroll', function() ]]) exec('call DoRel()') screen:expect([[ - 2<<>> marker - no need to show whole line + feed('2gj3l2k') + screen:expect([[ + <<<^h some text with some text | + Line with some text with some text with | + some text with some text with some text | + with some text with some text | + Line with some text with some text with | + some text with some text with some text | + with some text with some text | + | + ]]) + -- moving cursor up where the >>> marker is - whole top line shows + feed('2j02k') screen:expect([[ ^Line with some text with some text with | some text with some text with some text | @@ -524,11 +536,9 @@ describe('smoothscroll', function() -- oldtest: Test_smoothscroll_long_line_showbreak() it("cursor is not one screen line too far down", function() screen:try_resize(40, 6) - exec([[ - " a line that spans four screen lines - call setline(1, 'with lots of text in one line '->repeat(6)) - set smoothscroll scrolloff=0 showbreak=+++\ - ]]) + -- a line that spans four screen lines + exec("call setline(1, 'with lots of text in one line '->repeat(6))") + exec('set smoothscroll scrolloff=0 showbreak=+++\\ ') local s1 = [[ ^with lots of text in one line with lots | +++ of text in one line with lots of tex| @@ -540,8 +550,8 @@ describe('smoothscroll', function() screen:expect(s1) feed('') screen:expect([[ - +++ of text in one line with lots of tex| - +++ ^t in one line with lots of text in o| + +++ ^of text in one line with lots of tex| + +++ t in one line with lots of text in o| +++ ne line with lots of text in one lin| +++ e with lots of text in one line | ~ | diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index 657172edea..b9645898e8 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -247,10 +247,14 @@ func Test_smoothscroll_wrap_scrolloff_zero() call term_sendkeys(buf, "G") call VerifyScreenDump(buf, 'Test_smooth_wrap_4', {}) - " moving cursor up - whole top line shows - call term_sendkeys(buf, "2k") + " moving cursor up right after the >>> marker - no need to show whole line + call term_sendkeys(buf, "2gj3l2k") call VerifyScreenDump(buf, 'Test_smooth_wrap_5', {}) + " moving cursor up where the >>> marker is - whole top line shows + call term_sendkeys(buf, "2j02k") + call VerifyScreenDump(buf, 'Test_smooth_wrap_6', {}) + call StopVimInTerminal(buf) endfunc @@ -338,7 +342,6 @@ func Test_smoothscroll_long_line_showbreak() let buf = RunVimInTerminal('-S XSmoothLongShowbreak', #{rows: 6, cols: 40}) call VerifyScreenDump(buf, 'Test_smooth_long_showbreak_1', {}) - " FIXME: this currently has the cursor in screen line 2, should be one up. call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_long_showbreak_2', {}) @@ -370,10 +373,16 @@ func Test_smoothscroll_cursor_position() call s:check_col_calc(1, 2, 41) exe "normal \" call s:check_col_calc(1, 3, 41) - normal ggg$ + + normal gg3l exe "normal \" " Move down only 1 line when we are out of the range of the <<< display + call s:check_col_calc(4, 1, 24) + exe "normal \" + call s:check_col_calc(4, 2, 24) + normal ggg$ + exe "normal \" call s:check_col_calc(20, 1, 40) exe "normal \" call s:check_col_calc(20, 2, 40) @@ -383,9 +392,11 @@ func Test_smoothscroll_cursor_position() setl number call s:check_col_calc(5, 1, 1) exe "normal \" - call s:check_col_calc(5, 2, 33) + + " Move down only 1 line when the <<< display is on the number column + call s:check_col_calc(5, 1, 17) exe "normal \" - call s:check_col_calc(5, 3, 33) + call s:check_col_calc(5, 2, 17) normal ggg$ exe "normal \" call s:check_col_calc(20, 1, 32) @@ -393,13 +404,33 @@ func Test_smoothscroll_cursor_position() call s:check_col_calc(20, 2, 32) normal gg + setl numberwidth=1 + + " Move down another line when numberwidth is too short to cover the whole + " <<< display + call s:check_col_calc(3, 1, 1) + exe "normal \" + call s:check_col_calc(3, 2, 37) + exe "normal \" + call s:check_col_calc(3, 3, 37) + normal ggl + + " Only move 1 line down when we are just past the <<< display + call s:check_col_calc(4, 1, 2) + exe "normal \" + call s:check_col_calc(4, 1, 20) + exe "normal \" + call s:check_col_calc(4, 2, 20) + normal gg + setl numberwidth& + " Test number + showbreak, so test that the additional indentation works setl number showbreak=+++ call s:check_col_calc(5, 1, 1) exe "normal \" - call s:check_col_calc(8, 2, 30) + call s:check_col_calc(8, 1, 17) exe "normal \" - call s:check_col_calc(8, 3, 30) + call s:check_col_calc(8, 2, 17) normal gg " Test number + cpo+=n mode, where wrapped lines aren't indented From 26a9f0e94eb62047f0c2bb99401a8ac09840d0dd Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 19:08:20 +0200 Subject: [PATCH 33/45] vim-patch:9.0.0901: setting w_leftcol and handling side effects is confusing Problem: Setting w_leftcol and handling side effects is confusing. Solution: Use a function to set w_leftcol() and handle side effects. https://github.com/vim/vim/commit/0c34d562647f029faca40f7733ccfb7b5377672b Co-authored-by: Bram Moolenaar --- src/nvim/cursor.c | 23 ++++++++++++++--------- src/nvim/mouse.c | 4 +--- src/nvim/normal.c | 23 ++++++++--------------- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index cdadc89e31..8ba0b2ffb3 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -439,23 +439,27 @@ void adjust_cursor_col(void) } } -/// When curwin->w_leftcol has changed, adjust the cursor position. +/// Set "curwin->w_leftcol" to "leftcol". +/// Adjust the cursor position if needed. /// /// @return true if the cursor was moved. -bool leftcol_changed(void) +bool set_leftcol(colnr_T leftcol) { - // TODO(hinidu): I think it should be colnr_T or int, but p_siso is long. - // Perhaps we can change p_siso to int. - int64_t lastcol; - colnr_T s, e; - bool retval = false; + // Return quickly when there is no change. + if (curwin->w_leftcol == leftcol) { + return false; + } + curwin->w_leftcol = leftcol; changed_cline_bef_curs(); - lastcol = curwin->w_leftcol + curwin->w_width_inner - curwin_col_off() - 1; + // TODO(hinidu): I think it should be colnr_T or int, but p_siso is long. + // Perhaps we can change p_siso to int. + int64_t lastcol = curwin->w_leftcol + curwin->w_width_inner - curwin_col_off() - 1; validate_virtcol(); + bool retval = false; // If the cursor is right or left of the screen, move it to last or first - // character. + // visible character. long siso = get_sidescrolloff_value(curwin); if (curwin->w_virtcol > (colnr_T)(lastcol - siso)) { retval = true; @@ -468,6 +472,7 @@ bool leftcol_changed(void) // If the start of the character under the cursor is not on the screen, // advance the cursor one more char. If this fails (last char of the // line) adjust the scrolling. + colnr_T s, e; getvvcol(curwin, &curwin->w_cursor, &s, NULL, &e); if (e > (colnr_T)lastcol) { retval = true; diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 09352b370e..ead8dc2195 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -1648,8 +1648,6 @@ bool mouse_scroll_horiz(int dir) return false; } - curwin->w_leftcol = (colnr_T)leftcol; - // When the line of the cursor is too short, move the cursor to the // longest visible line. if (!virtual_active() @@ -1658,7 +1656,7 @@ bool mouse_scroll_horiz(int dir) curwin->w_cursor.col = 0; } - return leftcol_changed(); + return set_leftcol(leftcol); } /// Adjusts the clicked column position when 'conceallevel' > 0 diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 9792f5950e..e39a5e1ab7 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2158,9 +2158,8 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff) } // do the horizontal scroll - if (want_hor && curwin->w_leftcol != tgt_leftcol) { - curwin->w_leftcol = tgt_leftcol; - leftcol_changed(); + if (want_hor) { + (void)set_leftcol(tgt_leftcol); } } @@ -2643,7 +2642,7 @@ void scroll_redraw(int up, long count) scrollup(count, true) : scrolldown(count, true); - if (get_scrolloff_value(curwin)) { + if (get_scrolloff_value(curwin) > 0) { // Adjust the cursor position for 'scrolloff'. Mark w_topline as // valid, otherwise the screen jumps back at the end of the file. cursor_correct(); @@ -2894,27 +2893,21 @@ static void nv_zet(cmdarg_T *cap) case 'h': case K_LEFT: if (!curwin->w_p_wrap) { - if ((colnr_T)cap->count1 > curwin->w_leftcol) { - curwin->w_leftcol = 0; - } else { - curwin->w_leftcol -= (colnr_T)cap->count1; - } - leftcol_changed(); + (void)set_leftcol((colnr_T)cap->count1 > curwin->w_leftcol + ? 0 : curwin->w_leftcol - (colnr_T)cap->count1); } break; - // "zL" - scroll screen left half-page + // "zL" - scroll window left half-page case 'L': cap->count1 *= curwin->w_width_inner / 2; FALLTHROUGH; - // "zl" - scroll screen to the left + // "zl" - scroll window to the left if not wrapping case 'l': case K_RIGHT: if (!curwin->w_p_wrap) { - // scroll the window left - curwin->w_leftcol += (colnr_T)cap->count1; - leftcol_changed(); + (void)set_leftcol(curwin->w_leftcol + (colnr_T)cap->count1); } break; From 46646a9bb81b72d5579beade64006d6f3dc64d19 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 19:40:00 +0200 Subject: [PATCH 34/45] vim-patch:9.0.0908: with 'smoothscroll' cursor may end up in wrong position Problem: With 'smoothscroll' cursor may end up in wrong position. Solution: Correct the computation of screen lines. (Yee Cheng Chin, closes vim/vim#11502) https://github.com/vim/vim/commit/361895d2a15b4b0bbbb4c009261eab5b3d69ebf1 Co-authored-by: Yee Cheng Chin --- src/nvim/move.c | 37 +++++++++++++++---- test/functional/legacy/scroll_opt_spec.lua | 41 +++++++++++++++++++++- test/old/testdir/test_scroll_opt.vim | 26 +++++++++++++- 3 files changed, 96 insertions(+), 8 deletions(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index b0a4a3848a..abe908cfa0 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1877,18 +1877,43 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) // The lines of the cursor line itself are always used. used = plines_win_nofill(curwin, cln, true); - // If the cursor is below botline, we will at least scroll by the height - // of the cursor line. Correct for empty lines, which are really part of - // botline. + // If the cursor is on or below botline, we will at least scroll by the + // height of the cursor line, which is "used". Correct for empty lines, + // which are really part of botline. if (cln >= curwin->w_botline) { scrolled = used; if (cln == curwin->w_botline) { scrolled -= curwin->w_empty_rows; } min_scrolled = scrolled; - if (cln > curwin->w_botline && curwin->w_p_sms && curwin->w_p_wrap) { - for (linenr_T lnum = curwin->w_botline + 1; lnum <= cln; lnum++) { - min_scrolled += plines_win_nofill(curwin, lnum, true); + if (curwin->w_p_sms && curwin->w_p_wrap) { + // 'smoothscroll' and 'wrap' are set + if (cln > curwin->w_botline) { + // add screen lines below w_botline + for (linenr_T lnum = curwin->w_botline + 1; lnum <= cln; lnum++) { + min_scrolled += plines_win_nofill(curwin, lnum, true); + } + } + + // Calculate how many screen lines the current top line of window + // occupies. If it is occupying more than the entire window, we + // need to scroll the additional clipped lines to scroll past the + // top line before we can move on to the other lines. + int top_plines = plines_win_nofill(curwin, curwin->w_topline, false); + int skip_lines = 0; + int width1 = curwin->w_width - curwin_col_off(); + int width2 = width1 + curwin_col_off2(); + // similar formula is used in curs_columns() + if (curwin->w_skipcol > width1) { + skip_lines += (curwin->w_skipcol - width1) / width2 + 1; + } else if (curwin->w_skipcol > 0) { + skip_lines = 1; + } + + top_plines -= skip_lines; + if (top_plines > curwin->w_height) { + scrolled += (top_plines - curwin->w_height); + min_scrolled += (top_plines - curwin->w_height); } } } diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index a05470418f..6c8ca2cf97 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -410,7 +410,7 @@ describe('smoothscroll', function() it("adjusts the cursor position in a long line", function() screen:try_resize(40, 6) exec([[ - call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(30))]) + call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(30)) .. ' end', 'four']) set smoothscroll scrolloff=0 normal 3G10|zt ]]) @@ -502,6 +502,45 @@ describe('smoothscroll', function() ith lots of text with lots of text with | | ]]) + -- 'scrolloff' set to 0, move cursor down one line. Cursor should move properly, + -- and since this is a really long line, it will be put on top of the screen. + exec('set scrolloff=0') + feed('0j') + screen:expect([[ + ^four | + ~ | + ~ | + ~ | + ~ | + | + ]]) + -- Repeat the step and move the cursor down again. + -- This time, use a shorter long line that is barely long enough to span more + -- than one window. Note that the cursor is at the bottom this time because + -- Vim prefers to do so if we are scrolling a few lines only. + exec("call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(10)) .. ' end', 'four'])") + feed('3Gztj') + screen:expect([[ + <<j') + screen:expect([[ + <<repeat(30))]) + setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(30)) .. ' end', 'four']) set smoothscroll scrolloff=0 normal 3G10|zt END @@ -304,6 +304,30 @@ func Test_smoothscroll_wrap_long_line() call term_sendkeys(buf, "gj") call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_smooth_long_9', {}) + + " 'scrolloff' set to 0, move cursor down one line. + " Cursor should move properly, and since this is a really long line, it will + " be put on top of the screen. + call term_sendkeys(buf, ":set scrolloff=0\") + call term_sendkeys(buf, "0j") + call VerifyScreenDump(buf, 'Test_smooth_long_10', {}) + + " Repeat the step and move the cursor down again. + " This time, use a shorter long line that is barely long enough to span more + " than one window. Note that the cursor is at the bottom this time because + " Vim prefers to do so if we are scrolling a few lines only. + call term_sendkeys(buf, ":call setline(1, ['one', 'two', 'Line' .. (' with lots of text'->repeat(10)) .. ' end', 'four'])\") + call term_sendkeys(buf, "3Gzt") + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_smooth_long_11', {}) + + " Repeat the step but this time start it when the line is smooth-scrolled by + " one line. This tests that the offset calculation is still correct and + " still end up scrolling down to the next line with cursor at bottom of + " screen. + call term_sendkeys(buf, "3Gzt") + call term_sendkeys(buf, "\j") + call VerifyScreenDump(buf, 'Test_smooth_long_12', {}) call StopVimInTerminal(buf) endfunc From 5ba11087b60eb7cbcaa1ea4ccbb0b1a6bcf3c1be Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Fri, 28 Apr 2023 03:59:53 +0200 Subject: [PATCH 35/45] vim-patch:9.0.0911: with 'smoothscroll' set mouse click position may be wrong Problem: With 'smoothscroll' set mouse click position may be wrong. Solution: Adjust computations for w_skipcol. (Yee Cheng Chin, closes vim/vim#11514) https://github.com/vim/vim/commit/e6392b102151ec69fad232bcf00591230cef8e1c Co-authored-by: Yee Cheng Chin --- src/nvim/mouse.c | 24 +++++++++-- test/old/testdir/test_scroll_opt.vim | 61 ++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 4 deletions(-) diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index ead8dc2195..b890727a98 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -1410,9 +1410,22 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) } else { row -= win_get_fill(win, lnum); } - count = plines_win_nofill(win, lnum, true); + count = plines_win_nofill(win, lnum, false); } else { - count = plines_win(win, lnum, true); + count = plines_win(win, lnum, false); + } + + if (win->w_skipcol > 0 && lnum == win->w_topline) { + // Adjust for 'smoothscroll' clipping the top screen lines. + // A similar formula is used in curs_columns(). + int width1 = win->w_width - win_col_off(win); + int skip_lines = 0; + if (win->w_skipcol > width1) { + skip_lines = (win->w_skipcol - width1) / (width1 + win_col_off2(win)) + 1; + } else if (win->w_skipcol > 0) { + skip_lines = 1; + } + count -= skip_lines; } if (count > row) { @@ -1436,8 +1449,11 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) col = off; } col += row * (win->w_width_inner - off); - // add skip column (for long wrapping line) - col += win->w_skipcol; + + // Add skip column for the topline. + if (lnum == win->w_topline) { + col += win->w_skipcol; + } } if (!win->w_p_wrap) { diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index 8d1942e037..3707e211fc 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -2,6 +2,7 @@ source check.vim source screendump.vim +source mouse.vim func Test_reset_scroll() let scr = &l:scroll @@ -469,5 +470,65 @@ func Test_smoothscroll_cursor_position() bwipeout! endfunc +" Test that mouse picking is still accurate when we have smooth scrolled lines +func Test_smoothscroll_mouse_pos() + CheckNotGui + CheckUnix + + let save_mouse = &mouse + "let save_term = &term + "let save_ttymouse = &ttymouse + set mouse=a "term=xterm ttymouse=xterm2 + + call NewWindow(10, 20) + setl smoothscroll wrap + " First line will wrap to 3 physical lines. 2nd/3rd lines are short lines. + call setline(1, ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", "line 2", "line 3"]) + + func s:check_mouse_click(row, col, buf_row, buf_col) + call MouseLeftClick(a:row, a:col) + + call assert_equal(a:col, wincol()) + call assert_equal(a:row, winline()) + call assert_equal(a:buf_row, line('.')) + call assert_equal(a:buf_col, col('.')) + endfunc + + " Check that clicking without scroll works first. + call s:check_mouse_click(3, 5, 1, 45) + call s:check_mouse_click(4, 1, 2, 1) + call s:check_mouse_click(4, 6, 2, 6) + call s:check_mouse_click(5, 1, 3, 1) + call s:check_mouse_click(5, 6, 3, 6) + + " Smooth scroll, and checks that this didn't mess up mouse clicking + exe "normal \" + call s:check_mouse_click(2, 5, 1, 45) + call s:check_mouse_click(3, 1, 2, 1) + call s:check_mouse_click(3, 6, 2, 6) + call s:check_mouse_click(4, 1, 3, 1) + call s:check_mouse_click(4, 6, 3, 6) + + exe "normal \" + call s:check_mouse_click(1, 5, 1, 45) + call s:check_mouse_click(2, 1, 2, 1) + call s:check_mouse_click(2, 6, 2, 6) + call s:check_mouse_click(3, 1, 3, 1) + call s:check_mouse_click(3, 6, 3, 6) + + " Make a new first line 11 physical lines tall so it's taller than window + " height, to test overflow calculations with really long lines wrapping. + normal gg + call setline(1, "12345678901234567890"->repeat(11)) + exe "normal 6\" + call s:check_mouse_click(5, 1, 1, 201) + call s:check_mouse_click(6, 1, 2, 1) + call s:check_mouse_click(7, 1, 3, 1) + + let &mouse = save_mouse + "let &term = save_term + "let &ttymouse = save_ttymouse +endfunc + " vim: shiftwidth=2 sts=2 expandtab From c25fd85c2e618535b7780b965a883cb20ab1aa1a Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Fri, 28 Apr 2023 12:32:39 +0200 Subject: [PATCH 36/45] vim-patch:9.0.0998: "gk" may reset skipcol when not needed Problem: "gk" may reset skipcol when not needed. Solution: Only reset skipcol if the cursor column is less. https://github.com/vim/vim/commit/1b73edd9ee40aec400f3611f59823cec5fd1c489 Co-authored-by: Bram Moolenaar --- src/nvim/move.c | 5 +-- test/old/testdir/test_scroll_opt.vim | 46 +++++++++++++++++++++++----- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index abe908cfa0..2bae811e6d 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1776,7 +1776,7 @@ void scroll_cursor_top(int min_scroll, int always) scroll_cursor_halfway(false, false); } else { // If "always" is false, only adjust topline to a lower value, higher - // value may happen with wrapping lines + // value may happen with wrapping lines. if (new_topline < curwin->w_topline || always) { curwin->w_topline = new_topline; } @@ -1792,7 +1792,8 @@ void scroll_cursor_top(int min_scroll, int always) } check_topfill(curwin, false); // TODO(vim): if the line doesn't fit may optimize w_skipcol - if (curwin->w_topline == curwin->w_cursor.lnum) { + if (curwin->w_topline == curwin->w_cursor.lnum + && curwin->w_skipcol >= curwin->w_cursor.col) { reset_skipcol(curwin); } if (curwin->w_topline != old_topline diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index 3707e211fc..c8d0f51384 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -376,6 +376,12 @@ func Test_smoothscroll_long_line_showbreak() call StopVimInTerminal(buf) endfunc +func s:check_col_calc(win_col, win_line, buf_col) + call assert_equal(a:win_col, wincol()) + call assert_equal(a:win_line, winline()) + call assert_equal(a:buf_col, col('.')) +endfunc + " Test that if the current cursor is on a smooth scrolled line, we correctly " reposition it. Also check that we don't miscalculate the values by checking " the consistency between wincol() and col('.') as they are calculated @@ -385,12 +391,6 @@ func Test_smoothscroll_cursor_position() setl smoothscroll wrap call setline(1, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") - func s:check_col_calc(win_col, win_line, buf_col) - call assert_equal(a:win_col, wincol()) - call assert_equal(a:win_line, winline()) - call assert_equal(a:buf_col, col('.')) - endfunc - call s:check_col_calc(1, 1, 1) exe "normal \" @@ -467,9 +467,41 @@ func Test_smoothscroll_cursor_position() call s:check_col_calc(1, 3, 37) normal gg - bwipeout! + bwipe! endfunc +func Test_smoothscroll_cursor_scrolloff() + call NewWindow(10, 20) + setl smoothscroll wrap + setl scrolloff=3 + + " 120 chars are 6 screen lines + call setline(1, "abcdefghijklmnopqrstABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrstABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrstABCDEFGHIJKLMNOPQRST") + call setline(2, "below") + + call s:check_col_calc(1, 1, 1) + + " CTRL-E shows "<<" + call s:check_col_calc(1, 4, 81) + + " cursor on start of second line, "gk" moves into first line, skipcol doesn't + " change + exe "normal G0gk" + call s:check_col_calc(1, 5, 101) + + " move cursor left one window width worth, scrolls one screen line + exe "normal 20h" + call s:check_col_calc(1, 5, 81) + + " move cursor left one window width worth, scrolls one screen line + exe "normal 20h" + call s:check_col_calc(1, 4, 61) + + bwipe! +endfunc + + " Test that mouse picking is still accurate when we have smooth scrolled lines func Test_smoothscroll_mouse_pos() CheckNotGui From 6fd7e3bea4c38e79c77fe506a45781ffd17ebe4f Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Fri, 28 Apr 2023 12:36:11 +0200 Subject: [PATCH 37/45] vim-patch:9.0.1000: with 'smoothscroll' skipcol may be reset unnecessarily Problem: With 'smoothscroll' skipcol may be reset unnecessarily. Solution: Check the line does actually fit in the window. https://github.com/vim/vim/commit/b21b8e9ed081a6ef6b6745fe65d219b3ac046c3b Co-authored-by: Bram Moolenaar --- src/nvim/move.c | 5 ++++- test/functional/legacy/scroll_opt_spec.lua | 9 +-------- test/old/testdir/test_scroll_opt.vim | 7 +++++++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index 2bae811e6d..a6c87b641a 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1465,7 +1465,10 @@ void adjust_skipcol(void) bool scrolled = false; validate_cheight(); - if (curwin->w_cline_height == curwin->w_height) { + if (curwin->w_cline_height == curwin->w_height + // w_cline_height may be capped at w_height, check there aren't + // actually more lines. + && plines_win(curwin, curwin->w_cursor.lnum, false) <= curwin->w_height) { // the line just fits in the window, don't scroll reset_skipcol(curwin); return; diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 6c8ca2cf97..31d851f571 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -494,14 +494,7 @@ describe('smoothscroll', function() ]]) -- 'scrolloff' set to 2, scrolling down, cursor moves screen line up feed('gj') - screen:expect([[ - << Date: Fri, 28 Apr 2023 13:34:07 +0200 Subject: [PATCH 38/45] vim-patch:9.0.1121: cursor positioning and display problems with 'smoothscroll' Problem: Cursor positioning and display problems with 'smoothscroll' and using "zt", "zb" or "zz". Solution: Adjust computations and conditions. (Yee Cheng Chin, closes vim/vim#11764) https://github.com/vim/vim/commit/db4d88c2adfe8f8122341ac9d6cae27ef78451c8 Co-authored-by: Bram Moolenaar --- src/nvim/move.c | 173 ++++++++++++++++----- test/functional/legacy/scroll_opt_spec.lua | 28 ++++ test/old/testdir/test_scroll_opt.vim | 12 +- 3 files changed, 169 insertions(+), 44 deletions(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index a6c87b641a..7cc65c83ce 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -180,6 +180,22 @@ static int smoothscroll_marker_overlap(win_T *wp, int extra2) return extra2 > 3 ? 0 : 3 - extra2; } +/// Calculates the skipcol offset for window "wp" given how many +/// physical lines we want to scroll down. +static int skipcol_from_plines(win_T *wp, int plines_off) +{ + int width1 = wp->w_width - win_col_off(wp); + + int skipcol = 0; + if (plines_off > 0) { + skipcol += width1; + } + if (plines_off > 1) { + skipcol += (width1 + win_col_off2(wp)) * (plines_off - 1); + } + return skipcol; +} + /// Set wp->s_skipcol to zero and redraw later if needed. static void reset_skipcol(win_T *wp) { @@ -1628,7 +1644,8 @@ void scrollup_clamp(void) // a (wrapped) text line. Uses and sets "lp->fill". // Returns the height of the added line in "lp->height". // Lines above the first one are incredibly high: MAXCOL. -static void topline_back(win_T *wp, lineoff_T *lp) +// When "winheight" is true limit to window height. +static void topline_back_winheight(win_T *wp, lineoff_T *lp, int winheight) { if (lp->fill < win_get_fill(wp, lp->lnum)) { // Add a filler line @@ -1643,11 +1660,16 @@ static void topline_back(win_T *wp, lineoff_T *lp) // Add a closed fold lp->height = 1; } else { - lp->height = plines_win_nofill(wp, lp->lnum, true); + lp->height = plines_win_nofill(wp, lp->lnum, winheight); } } } +static void topline_back(win_T *wp, lineoff_T *lp) +{ + topline_back_winheight(wp, lp, true); +} + // Add one line below "lp->lnum". This can be a filler line, a closed fold or // a (wrapped) text line. Uses and sets "lp->fill". // Returns the height of the added line in "lp->height". @@ -1700,13 +1722,9 @@ static void topline_botline(lineoff_T *lp) // If "always" is true, always set topline (for "zt"). void scroll_cursor_top(int min_scroll, int always) { - int scrolled = 0; - linenr_T top; // just above displayed lines - linenr_T bot; // just below displayed lines linenr_T old_topline = curwin->w_topline; int old_skipcol = curwin->w_skipcol; linenr_T old_topfill = curwin->w_topfill; - linenr_T new_topline; int off = (int)get_scrolloff_value(curwin); if (mouse_dragging > 0) { @@ -1719,11 +1737,14 @@ void scroll_cursor_top(int min_scroll, int always) // - moved at least 'scrolljump' lines and // - at least 'scrolloff' lines above and below the cursor validate_cheight(); + int scrolled = 0; int used = curwin->w_cline_height; // includes filler lines above if (curwin->w_cursor.lnum < curwin->w_topline) { scrolled = used; } + linenr_T top; // just above displayed lines + linenr_T bot; // just below displayed lines if (hasFolding(curwin->w_cursor.lnum, &top, &bot)) { top--; bot++; @@ -1731,7 +1752,7 @@ void scroll_cursor_top(int min_scroll, int always) top = curwin->w_cursor.lnum - 1; bot = curwin->w_cursor.lnum + 1; } - new_topline = top + 1; + linenr_T new_topline = top + 1; // "used" already contains the number of filler lines above, don't add it // again. @@ -1744,6 +1765,15 @@ void scroll_cursor_top(int min_scroll, int always) int i = hasFolding(top, &top, NULL) ? 1 // count one logical line for a sequence of folded lines : plines_win_nofill(curwin, top, true); + if (top < curwin->w_topline) { + scrolled += i; + } + + // If scrolling is needed, scroll at least 'sj' lines. + if ((new_topline >= curwin->w_topline || scrolled > min_scroll) && extra >= off) { + break; + } + used += i; if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) { if (hasFolding(bot, NULL, &bot)) { @@ -1756,15 +1786,6 @@ void scroll_cursor_top(int min_scroll, int always) if (used > curwin->w_height_inner) { break; } - if (top < curwin->w_topline) { - scrolled += i; - } - - // If scrolling is needed, scroll at least 'sj' lines. - if ((new_topline >= curwin->w_topline || scrolled > min_scroll) - && extra >= off) { - break; - } extra += i; new_topline = top; @@ -1838,21 +1859,18 @@ void set_empty_rows(win_T *wp, int used) void scroll_cursor_bot(int min_scroll, int set_topbot) { int used; - int scrolled = 0; - int min_scrolled = 1; - int extra = 0; lineoff_T loff; - lineoff_T boff; - int fill_below_window; - linenr_T old_topline = curwin->w_topline; - int old_topfill = curwin->w_topfill; - linenr_T old_botline = curwin->w_botline; - int old_valid = curwin->w_valid; + linenr_T old_topline = curwin->w_topline; + int old_skipcol = curwin->w_skipcol; + int old_topfill = curwin->w_topfill; + linenr_T old_botline = curwin->w_botline; + int old_valid = curwin->w_valid; int old_empty_rows = curwin->w_empty_rows; - linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number - long so = get_scrolloff_value(curwin); + linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number if (set_topbot) { + bool set_skipcol = false; + used = 0; curwin->w_botline = cln + 1; loff.fill = 0; @@ -1860,9 +1878,24 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) curwin->w_topline > 1; curwin->w_topline = loff.lnum) { loff.lnum = curwin->w_topline; - topline_back(curwin, &loff); - if (loff.height == MAXCOL - || used + loff.height > curwin->w_height_inner) { + topline_back_winheight(curwin, &loff, false); + if (loff.height == MAXCOL) { + break; + } + if (used + loff.height > curwin->w_height) { + if (curwin->w_p_sms && curwin->w_p_wrap) { + // 'smoothscroll' and 'wrap' are set. The above line is + // too long to show in its entirety, so we show just a part + // of it. + if (used < curwin->w_height) { + int plines_offset = used + loff.height - curwin->w_height; + used = curwin->w_height; + curwin->w_topfill = loff.fill; + curwin->w_topline = loff.lnum; + curwin->w_skipcol = skipcol_from_plines(curwin, plines_offset); + set_skipcol = true; + } + } break; } used += loff.height; @@ -1871,8 +1904,15 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) set_empty_rows(curwin, used); curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; if (curwin->w_topline != old_topline - || curwin->w_topfill != old_topfill) { + || curwin->w_topfill != old_topfill + || set_skipcol + || curwin->w_skipcol != 0) { curwin->w_valid &= ~(VALID_WROW|VALID_CROW); + if (set_skipcol) { + redraw_later(curwin, UPD_NOT_VALID); + } else { + reset_skipcol(curwin); + } } } else { validate_botline(curwin); @@ -1881,6 +1921,8 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) // The lines of the cursor line itself are always used. used = plines_win_nofill(curwin, cln, true); + int scrolled = 0; + int min_scrolled = 1; // If the cursor is on or below botline, we will at least scroll by the // height of the cursor line, which is "used". Correct for empty lines, // which are really part of botline. @@ -1922,6 +1964,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) } } + lineoff_T boff; // Stop counting lines to scroll when // - hitting start of the file // - scrolled nothing or at least 'sj' lines @@ -1933,9 +1976,10 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) } loff.fill = 0; boff.fill = 0; - fill_below_window = win_get_fill(curwin, curwin->w_botline) - - curwin->w_filler_rows; + int fill_below_window = win_get_fill(curwin, curwin->w_botline) - curwin->w_filler_rows; + int extra = 0; + long so = get_scrolloff_value(curwin); while (loff.lnum > 1) { // Stop when scrolled nothing or at least "min_scroll", found "extra" // context for 'scrolloff' and counted all lines below the window. @@ -2041,7 +2085,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) // If topline didn't change we need to restore w_botline and w_empty_rows // (we changed them). // If topline did change, update_screen() will set botline. - if (curwin->w_topline == old_topline && set_topbot) { + if (curwin->w_topline == old_topline && curwin->w_skipcol == old_skipcol && set_topbot) { curwin->w_botline = old_botline; curwin->w_empty_rows = old_empty_rows; curwin->w_valid = old_valid; @@ -2056,27 +2100,65 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) /// void scroll_cursor_halfway(bool atend, bool prefer_above) { - int above = 0; - int topfill = 0; - int below = 0; - lineoff_T loff; - lineoff_T boff; linenr_T old_topline = curwin->w_topline; - - loff.lnum = boff.lnum = curwin->w_cursor.lnum; + lineoff_T loff = { .lnum = curwin->w_cursor.lnum }; + lineoff_T boff = { .lnum = curwin->w_cursor.lnum }; (void)hasFolding(loff.lnum, &loff.lnum, &boff.lnum); int used = plines_win_nofill(curwin, loff.lnum, true); loff.fill = 0; boff.fill = 0; linenr_T topline = loff.lnum; + colnr_T skipcol = 0; + bool set_skipcol = false; + int half_height = 0; + bool smooth_scroll = false; + if (curwin->w_p_sms && curwin->w_p_wrap) { + // 'smoothscroll' and 'wrap' are set + smooth_scroll = true; + half_height = (curwin->w_height - used) / 2; + used = 0; + } + + int topfill = 0; while (topline > 1) { + // If using smoothscroll, we can precisely scroll to the + // exact point where the cursor is halfway down the screen. + if (smooth_scroll) { + topline_back_winheight(curwin, &loff, false); + if (loff.height == MAXCOL) { + break; + } else { + used += loff.height; + } + if (used > half_height) { + if (used - loff.height < half_height) { + int plines_offset = used - half_height; + loff.height -= plines_offset; + used = half_height; + + topline = loff.lnum; + topfill = loff.fill; + skipcol = skipcol_from_plines(curwin, plines_offset); + set_skipcol = true; + } + break; + } + topline = loff.lnum; + topfill = loff.fill; + continue; + } + + // If not using smoothscroll, we have to iteratively find how many + // lines to scroll down to roughly fit the cursor. // This may not be right in the middle if the lines' // physical height > 1 (e.g. 'wrap' is on). // Depending on "prefer_above" we add a line above or below first. // Loop twice to avoid duplicating code. bool done = false; + int above = 0; + int below = 0; for (int round = 1; round <= 2; round++) { if (prefer_above ? (round == 2 && below < above) @@ -2122,8 +2204,15 @@ void scroll_cursor_halfway(bool atend, bool prefer_above) } } - if (!hasFolding(topline, &curwin->w_topline, NULL)) { + if (!hasFolding(topline, &curwin->w_topline, NULL) + && (curwin->w_topline != topline || set_skipcol || curwin->w_skipcol != 0)) { curwin->w_topline = topline; + if (set_skipcol) { + curwin->w_skipcol = skipcol; + redraw_later(curwin, UPD_NOT_VALID); + } else { + reset_skipcol(curwin); + } } curwin->w_topfill = topfill; if (old_topline > curwin->w_topline + curwin->w_height_inner) { diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 31d851f571..e58b95ecc1 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -499,6 +499,34 @@ describe('smoothscroll', function() -- and since this is a really long line, it will be put on top of the screen. exec('set scrolloff=0') feed('0j') + screen:expect([[ + <<repeat(10)) .. ' end', 'four'])\") call term_sendkeys(buf, "3Gzt") call term_sendkeys(buf, "j") - call VerifyScreenDump(buf, 'Test_smooth_long_11', {}) + call VerifyScreenDump(buf, 'Test_smooth_long_14', {}) " Repeat the step but this time start it when the line is smooth-scrolled by " one line. This tests that the offset calculation is still correct and @@ -328,7 +336,7 @@ func Test_smoothscroll_wrap_long_line() " screen. call term_sendkeys(buf, "3Gzt") call term_sendkeys(buf, "\j") - call VerifyScreenDump(buf, 'Test_smooth_long_12', {}) + call VerifyScreenDump(buf, 'Test_smooth_long_15', {}) call StopVimInTerminal(buf) endfunc From 4e4383ffa2b9ec94e703ecbd6178737fc24f6866 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Fri, 28 Apr 2023 16:22:42 +0200 Subject: [PATCH 39/45] vim-patch:9.0.1247: divide by zero with 'smoothscroll' set and a narrow window Problem: Divide by zero with 'smoothscroll' set and a narrow window. Solution: Bail out when the window is too narrow. https://github.com/vim/vim/commit/870219c58c0804bdc55419b2e455c06ac715a835 Co-authored-by: Bram Moolenaar --- runtime/doc/options.txt | 6 +++-- src/nvim/move.c | 3 +++ test/functional/legacy/scroll_opt_spec.lua | 26 +++++++++++++++++++++ test/old/testdir/test_scroll_opt.vim | 27 ++++++++++++++++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 7cb88441f1..ab86e56a62 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5657,8 +5657,10 @@ A jump table for the options with a short description can be found at |Q_op|. 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. + above the window. "<<<" is displayed at the start of the first line, + highlighted with |hl-NonText|. + NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y + and scrolling with the mouse. *'softtabstop'* *'sts'* 'softtabstop' 'sts' number (default 0) diff --git a/src/nvim/move.c b/src/nvim/move.c index 7cc65c83ce..e635b381d3 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1475,6 +1475,9 @@ void adjust_skipcol(void) } int width1 = curwin->w_width - curwin_col_off(); + if (width1 <= 0) { + return; // no text will be displayed + } int width2 = width1 + curwin_col_off2(); long so = get_scrolloff_value(curwin); long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index e58b95ecc1..f9dc9e81ca 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -620,4 +620,30 @@ describe('smoothscroll', function() feed('0') screen:expect(s1) end) + + -- oldtest: Test_smoothscroll_zero_width() + it("does not divide by zero with a narrow window", function() + screen:try_resize(12, 2) + screen:set_default_attr_ids({ + [1] = {foreground = Screen.colors.Brown}, + [2] = {foreground = Screen.colors.Blue1, bold = true}, + }) + exec([[ + call setline(1, ['a'->repeat(100)]) + set wrap smoothscroll number laststatus=0 + wincmd v + wincmd v + wincmd v + wincmd v + ]]) + screen:expect([[ + {1: 1^ }│{1: }│{1: }│{1: }│{1: }| + | + ]]) + feed('llllllllllo') + screen:expect([[ + {2:<<<}{1: }aa^aaaaaa| + | + ]]) + end) end) diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index 3c95c36b4f..4ed54591af 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -577,5 +577,32 @@ func Test_smoothscroll_mouse_pos() "let &ttymouse = save_ttymouse endfunc +" this was dividing by zero +func Test_smoothscrol_zero_width() + CheckScreendump + + let lines =<< trim END + winsize 0 0 + vsplit + vsplit + vsplit + vsplit + vsplit + sil norm H + set wrap + set smoothscroll + set number + END + call writefile(lines, 'XSmoothScrollZero', 'D') + let buf = RunVimInTerminal('-u NONE -i NONE -n -m -X -Z -e -s -S XSmoothScrollZero', #{rows: 6, cols: 60, wait_for_ruler: 0}) + call TermWait(buf, 3000) + call VerifyScreenDump(buf, 'Test_smoothscroll_zero_1', {}) + + call term_sendkeys(buf, ":sil norm \\\\\") + call VerifyScreenDump(buf, 'Test_smoothscroll_zero_2', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab From 7d5673b18b8b4ff085473b40fce317e760e48ce3 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Sat, 29 Apr 2023 00:16:37 +0200 Subject: [PATCH 40/45] vim-patch:9.0.1367: divide by zero in zero-width window Problem: Divide by zero in zero-width window. Solution: Check the width is positive. https://github.com/vim/vim/commit/e0f869196930ef5f25a0ac41c9215b09c9ce2d3c Co-authored-by: Bram Moolenaar --- src/nvim/move.c | 2 +- test/old/testdir/test_window_cmd.vim | 39 +++++++++++++++++++++------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index e635b381d3..69245f2a67 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1303,7 +1303,7 @@ bool scrolldown(long line_count, int byfold) col -= width1; row++; } - if (col > width2) { + if (col > width2 && width2 > 0) { row += (int)col / width2; col = col % width2; } diff --git a/test/old/testdir/test_window_cmd.vim b/test/old/testdir/test_window_cmd.vim index f938203736..f18d1719c0 100644 --- a/test/old/testdir/test_window_cmd.vim +++ b/test/old/testdir/test_window_cmd.vim @@ -1734,7 +1734,7 @@ func Test_splitkeep_options() " let &t_WS = save_WS endfunc -function Test_splitkeep_cmdwin_cursor_position() +func Test_splitkeep_cmdwin_cursor_position() set splitkeep=screen call setline(1, range(&lines)) @@ -1759,9 +1759,9 @@ function Test_splitkeep_cmdwin_cursor_position() %bwipeout! set splitkeep& -endfunction +endfunc -function Test_splitkeep_misc() +func Test_splitkeep_misc() set splitkeep=screen set splitbelow @@ -1794,7 +1794,7 @@ function Test_splitkeep_misc() set splitkeep& endfunc -function Test_splitkeep_callback() +func Test_splitkeep_callback() CheckScreendump let lines =<< trim END set splitkeep=screen @@ -1827,7 +1827,7 @@ function Test_splitkeep_callback() call StopVimInTerminal(buf) endfunc -function Test_splitkeep_fold() +func Test_splitkeep_fold() CheckScreendump let lines =<< trim END @@ -1857,9 +1857,9 @@ function Test_splitkeep_fold() call VerifyScreenDump(buf, 'Test_splitkeep_fold_4', {}) call StopVimInTerminal(buf) -endfunction +endfunc -function Test_splitkeep_status() +func Test_splitkeep_status() CheckScreendump let lines =<< trim END @@ -1877,9 +1877,9 @@ function Test_splitkeep_status() call VerifyScreenDump(buf, 'Test_splitkeep_status_1', {}) call StopVimInTerminal(buf) -endfunction +endfunc -function Test_new_help_window_on_error() +func Test_new_help_window_on_error() help change.txt execute "normal! /CTRL-@\" silent! execute "normal! \]" @@ -1889,7 +1889,26 @@ function Test_new_help_window_on_error() call assert_equal(wincount, winnr('$')) call assert_equal(expand(""), "'mod'") -endfunction +endfunc + +func Test_smoothscroll_in_zero_width_window() + let save_lines = &lines + let save_columns = &columns + + winsize 0 24 + set cpo+=n + exe "noremap 0 \n\L" + norm 000000 + set number smoothscroll + exe "norm \" + + only! + let &lines = save_lines + let &columns = save_columns + set cpo-=n + unmap 0 + set nonumber nosmoothscroll +endfunc " vim: shiftwidth=2 sts=2 expandtab From 35ed79a9150fed877e660395c64d6659bf0a07ec Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Sat, 29 Apr 2023 00:34:09 +0200 Subject: [PATCH 41/45] vim-patch:9.0.1435: scrolling too many lines when 'wrap' and 'diff' are set Problem: Scrolling too many lines when 'wrap' and 'diff' are set. Solution: Only scroll by screenlines for 'diff' when 'wrap' is not set. (closes vim/vim#12211) https://github.com/vim/vim/commit/38d867f041349e1400c2cce9cac06f59ae6ccbb1 Co-authored-by: Bram Moolenaar --- src/nvim/move.c | 2 +- test/old/testdir/test_diffmode.vim | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/nvim/move.c b/src/nvim/move.c index 69245f2a67..b597853c13 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1320,7 +1320,7 @@ static int scrolling_screenlines(bool byfold) { return (curwin->w_p_wrap && curwin->w_p_sms) || (byfold && hasAnyFolding(curwin)) - || curwin->w_p_diff; + || (curwin->w_p_diff && !curwin->w_p_wrap); } /// Scroll the current window up by "line_count" logical lines. "CTRL-E" diff --git a/test/old/testdir/test_diffmode.vim b/test/old/testdir/test_diffmode.vim index 0049398776..ac90aaaa02 100644 --- a/test/old/testdir/test_diffmode.vim +++ b/test/old/testdir/test_diffmode.vim @@ -1605,6 +1605,21 @@ func Test_diff_scroll() call delete('Xright') endfunc +" This was scrolling too many lines. +func Test_diff_scroll_wrap_on() + 20new + 40vsplit + call setline(1, map(range(1, 9), 'repeat(v:val, 200)')) + setlocal number diff so=0 + redraw + normal! jj + call assert_equal(1, winsaveview().topline) + normal! j + call assert_equal(2, winsaveview().topline) + bwipe! + bwipe! +endfunc + " This was trying to update diffs for a buffer being closed func Test_diff_only() silent! lfile From f78130b2d84a950ed7a13a5cbd946cba6c5c68f7 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Sat, 29 Apr 2023 23:02:29 +0200 Subject: [PATCH 42/45] test: 'smoothscroll' works with virt_lines above and below --- runtime/doc/news.txt | 3 + test/functional/legacy/scroll_opt_spec.lua | 73 ++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index f33cffa22e..bc357ac534 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -49,6 +49,9 @@ iterators |luaref-in|. • Added |vim.treesitter.query.omnifunc()| for treesitter query files (set by default). +• |'smoothscroll'| option to scroll by screen line rather than by text line +when |'wrap'| is set. + ============================================================================== CHANGED FEATURES *news-changed* diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index f9dc9e81ca..253b20df5c 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -646,4 +646,77 @@ describe('smoothscroll', function() | ]]) end) + + it("works with virt_lines above and below", function() + screen:try_resize(55, 7) + exec([=[ + call setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(3)) + set smoothscroll + let ns = nvim_create_namespace('') + call nvim_buf_set_extmark(0, ns, 0, 0, {'virt_lines':[[['virt_below1']]]}) + call nvim_buf_set_extmark(0, ns, 1, 0, {'virt_lines':[[['virt_above1']]],'virt_lines_above':1}) + call nvim_buf_set_extmark(0, ns, 1, 0, {'virt_lines':[[['virt_below2']]]}) + call nvim_buf_set_extmark(0, ns, 2, 0, {'virt_lines':[[['virt_above2']]],'virt_lines_above':1}) + norm ggL + ]=]) + screen:expect([[ + Line with some text with some text with some text with | + some text with some text with some text with some text | + virt_below1 | + virt_above1 | + ^Line with some text with some text with some text with | + some text with some text with some text with some text | + | + ]]) + feed('') + screen:expect([[ + <<') + screen:expect([[ + virt_below1 | + virt_above1 | + ^Line with some text with some text with some text with | + some text with some text with some text with some text | + virt_below2 | + virt_above2 | + | + ]]) + feed('') + screen:expect([[ + virt_above1 | + ^Line with some text with some text with some text with | + some text with some text with some text with some text | + virt_below2 | + virt_above2 | + Line with some text with some text with some text wi@@@| + | + ]]) + feed('') + screen:expect([[ + ^Line with some text with some text with some text with | + some text with some text with some text with some text | + virt_below2 | + virt_above2 | + Line with some text with some text with some text with | + some text with some text with some text with some text | + | + ]]) + feed('') + screen:expect([[ + << Date: Sun, 30 Apr 2023 22:04:35 +0200 Subject: [PATCH 43/45] vim-patch:9.0.1502: no test for deleting the end of a long wrapped line Problem: No test for deleting the end of a long wrapped line. Solution: Add a test to check the right text is displayed. (Luuk van Baal, closes vim/vim#12318) https://github.com/vim/vim/commit/5b10a140983c16140d69a214494c4b8af8b34763 Co-authored-by: Luuk van Baal --- test/functional/legacy/display_spec.lua | 47 +++++++++++++++++++++++++ test/old/testdir/test_display.vim | 21 +++++++++++ 2 files changed, 68 insertions(+) diff --git a/test/functional/legacy/display_spec.lua b/test/functional/legacy/display_spec.lua index f9b78f5dcd..4952a5c4fe 100644 --- a/test/functional/legacy/display_spec.lua +++ b/test/functional/legacy/display_spec.lua @@ -194,4 +194,51 @@ describe('display', function() it('display "lastline" works correctly with multibyte fillchar', function() run_test_display_lastline(true) end) + + -- oldtest: Test_display_long_lastline + it('display "lastline" shows correct text when end of wrapped line is deleted', function() + local screen = Screen.new(35, 14) + screen:attach() + exec([[ + set display=lastline scrolloff=5 + call setline(1, [ + \'aaaaa'->repeat(100), + \'bbbbb '->repeat(7) .. 'ccccc '->repeat(7) .. 'ddddd '->repeat(7) + \]) + ]]) + feed('482|') + screen:expect([[ + <<repeat(100), + \'bbbbb '->repeat(7) .. 'ccccc '->repeat(7) .. 'ddddd '->repeat(7) + \]) + END + + call writefile(lines, 'XdispLongline', 'D') + let buf = RunVimInTerminal('-S XdispLongline', #{rows: 14, cols: 35}) + + call term_sendkeys(buf, "482|") + call VerifyScreenDump(buf, 'Test_display_long_line_1', {}) + call term_sendkeys(buf, "D") + call VerifyScreenDump(buf, 'Test_display_long_line_2', {}) + + call StopVimInTerminal(buf) +endfunc " vim: shiftwidth=2 sts=2 expandtab From 3b906af27fbd27a148ed6ca66e79b273ba5e8521 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Mon, 1 May 2023 14:42:30 +0200 Subject: [PATCH 44/45] test: 'smoothscroll' <<< marker shows with tabline, winbar and splits --- test/functional/legacy/scroll_opt_spec.lua | 55 ++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 253b20df5c..b00ff0bc7a 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -719,4 +719,59 @@ describe('smoothscroll', function() | ]]) end) + + it('<<< marker shows with tabline, winbar and splits', function() + screen:try_resize(40, 12) + exec([[ + call setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(7)) + set smoothscroll scrolloff=0 + norm sj + ]]) + screen:expect([[ + <<') + screen:expect([[ + 2+ [No Name] | + <<k') + screen:expect([[ + 2+ [No Name] | + winbar | + << Date: Tue, 2 May 2023 12:00:42 +0200 Subject: [PATCH 45/45] fix(ui): adjust 'smoothscroll' for inner dimensions --- src/nvim/drawscreen.c | 2 +- src/nvim/grid.c | 4 +-- src/nvim/mouse.c | 2 +- src/nvim/move.c | 62 +++++++++++++++++++++---------------------- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 36ff53aacb..ec5163f37a 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -1451,7 +1451,7 @@ static void win_update(win_T *wp, DecorProviders *providers) // width. if (wp->w_skipcol > 0) { int w = 0; - int width1 = wp->w_width - win_col_off(wp); + int width1 = wp->w_width_inner - win_col_off(wp); int width2 = width1 + win_col_off2(wp); int add = width1; diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 9e830413bd..037606c38f 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -541,13 +541,13 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle if (wp->w_p_nu && wp->w_p_rnu) { // do not overwrite the line number, change "123 text" to // "123>>>xt". - while (skip < wp->w_width && ascii_isdigit(*linebuf_char[off])) { + while (skip < wp->w_width_inner && ascii_isdigit(*linebuf_char[off])) { off++; skip++; } } - for (int i = 0; i < 3 && i + skip < wp->w_width; i++) { + for (int i = 0; i < 3 && i + skip < wp->w_width_inner; i++) { schar_from_ascii(linebuf_char[off], '<'); linebuf_attr[off] = HL_ATTR(HLF_AT); off++; diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index b890727a98..79bd65a88f 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -1418,7 +1418,7 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) if (win->w_skipcol > 0 && lnum == win->w_topline) { // Adjust for 'smoothscroll' clipping the top screen lines. // A similar formula is used in curs_columns(). - int width1 = win->w_width - win_col_off(win); + int width1 = win->w_width_inner - win_col_off(win); int skip_lines = 0; if (win->w_skipcol > width1) { skip_lines = (win->w_skipcol - width1) / (width1 + win_col_off2(win)) + 1; diff --git a/src/nvim/move.c b/src/nvim/move.c index b597853c13..447926ceb8 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -65,7 +65,7 @@ static int adjust_plines_for_skipcol(win_T *wp, int n) } int off = 0; - int width = wp->w_width - win_col_off(wp); + int width = wp->w_width_inner - win_col_off(wp); if (wp->w_skipcol >= width) { off++; int skip = wp->w_skipcol - width; @@ -88,8 +88,8 @@ static int plines_correct_topline(win_T *wp, linenr_T lnum, linenr_T *nextp, boo if (lnum == wp->w_topline) { n = adjust_plines_for_skipcol(wp, n); } - if (n > wp->w_height) { - return wp->w_height; + if (n > wp->w_height_inner) { + return wp->w_height_inner; } return n; } @@ -184,7 +184,7 @@ static int smoothscroll_marker_overlap(win_T *wp, int extra2) /// physical lines we want to scroll down. static int skipcol_from_plines(win_T *wp, int plines_off) { - int width1 = wp->w_width - win_col_off(wp); + int width1 = wp->w_width_inner - win_col_off(wp); int skipcol = 0; if (plines_off > 0) { @@ -830,8 +830,8 @@ void curs_columns(win_T *wp, int may_scroll) wp->w_wrow = wp->w_cline_row; int n; - int width1 = wp->w_width - extra; // text width for first screen line - int width2 = 0; // text width for second and later screen line + int width1 = wp->w_width_inner - extra; // text width for first screen line + int width2 = 0; // text width for second and later screen line bool did_sub_skipcol = false; if (width1 <= 0) { // No room for text, put cursor in last char of window. @@ -973,7 +973,7 @@ void curs_columns(win_T *wp, int may_scroll) extra += 2; } - if (extra == 3 || wp->w_height <= so * 2) { + if (extra == 3 || wp->w_height_inner <= so * 2) { // not enough room for 'scrolloff', put cursor in the middle n = wp->w_virtcol / width2; if (n > wp->w_height_inner / 2) { @@ -1188,7 +1188,7 @@ bool scrolldown(long line_count, int byfold) bool do_sms = curwin->w_p_wrap && curwin->w_p_sms; if (do_sms) { - width1 = curwin->w_width - curwin_col_off(); + width1 = curwin->w_width_inner - curwin_col_off(); width2 = width1 + curwin_col_off2(); } @@ -1307,8 +1307,8 @@ bool scrolldown(long line_count, int byfold) row += (int)col / width2; col = col % width2; } - if (row >= curwin->w_height) { - curwin->w_curswant = curwin->w_virtcol - (row - curwin->w_height + 1) * width2; + if (row >= curwin->w_height_inner) { + curwin->w_curswant = curwin->w_virtcol - (row - curwin->w_height_inner + 1) * width2; coladvance(curwin->w_curswant); } } @@ -1334,7 +1334,7 @@ bool scrollup(long line_count, int byfold) int do_sms = curwin->w_p_wrap && curwin->w_p_sms; if (scrolling_screenlines(byfold) || win_may_fill(curwin)) { - int width1 = curwin->w_width - curwin_col_off(); + int width1 = curwin->w_width_inner - curwin_col_off(); int width2 = width1 + curwin_col_off2(); unsigned size = 0; linenr_T prev_topline = curwin->w_topline; @@ -1424,12 +1424,12 @@ bool scrollup(long line_count, int byfold) int col_off = curwin_col_off(); int col_off2 = curwin_col_off2(); - int width1 = curwin->w_width - col_off; + int width1 = curwin->w_width_inner - col_off; int width2 = width1 + col_off2; int extra2 = col_off - col_off2; long so = get_scrolloff_value(curwin); long scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; - int space_cols = (curwin->w_height - 1) * width2; + int space_cols = (curwin->w_height_inner - 1) * width2; // If we have non-zero scrolloff, just ignore the <<< marker as we are // going past it anyway. @@ -1474,7 +1474,7 @@ void adjust_skipcol(void) return; } - int width1 = curwin->w_width - curwin_col_off(); + int width1 = curwin->w_width_inner - curwin_col_off(); if (width1 <= 0) { return; // no text will be displayed } @@ -1484,10 +1484,10 @@ void adjust_skipcol(void) bool scrolled = false; validate_cheight(); - if (curwin->w_cline_height == curwin->w_height - // w_cline_height may be capped at w_height, check there aren't + if (curwin->w_cline_height == curwin->w_height_inner + // w_cline_height may be capped at w_height_inner, check there aren't // actually more lines. - && plines_win(curwin, curwin->w_cursor.lnum, false) <= curwin->w_height) { + && plines_win(curwin, curwin->w_cursor.lnum, false) <= curwin->w_height_inner) { // the line just fits in the window, don't scroll reset_skipcol(curwin); return; @@ -1519,13 +1519,13 @@ void adjust_skipcol(void) row += (int)col / width2; col = col % width2; } - if (row >= curwin->w_height) { + if (row >= curwin->w_height_inner) { if (curwin->w_skipcol == 0) { curwin->w_skipcol += width1; row--; } - if (row >= curwin->w_height) { - curwin->w_skipcol += (row - curwin->w_height) * width2; + if (row >= curwin->w_height_inner) { + curwin->w_skipcol += (row - curwin->w_height_inner) * width2; } redraw_later(curwin, UPD_NOT_VALID); } @@ -1885,14 +1885,14 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) if (loff.height == MAXCOL) { break; } - if (used + loff.height > curwin->w_height) { + if (used + loff.height > curwin->w_height_inner) { if (curwin->w_p_sms && curwin->w_p_wrap) { // 'smoothscroll' and 'wrap' are set. The above line is // too long to show in its entirety, so we show just a part // of it. - if (used < curwin->w_height) { - int plines_offset = used + loff.height - curwin->w_height; - used = curwin->w_height; + if (used < curwin->w_height_inner) { + int plines_offset = used + loff.height - curwin->w_height_inner; + used = curwin->w_height_inner; curwin->w_topfill = loff.fill; curwin->w_topline = loff.lnum; curwin->w_skipcol = skipcol_from_plines(curwin, plines_offset); @@ -1950,7 +1950,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) // top line before we can move on to the other lines. int top_plines = plines_win_nofill(curwin, curwin->w_topline, false); int skip_lines = 0; - int width1 = curwin->w_width - curwin_col_off(); + int width1 = curwin->w_width_inner - curwin_col_off(); int width2 = width1 + curwin_col_off2(); // similar formula is used in curs_columns() if (curwin->w_skipcol > width1) { @@ -1960,9 +1960,9 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) } top_plines -= skip_lines; - if (top_plines > curwin->w_height) { - scrolled += (top_plines - curwin->w_height); - min_scrolled += (top_plines - curwin->w_height); + if (top_plines > curwin->w_height_inner) { + scrolled += (top_plines - curwin->w_height_inner); + min_scrolled += (top_plines - curwin->w_height_inner); } } } @@ -2073,7 +2073,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) if (curwin->w_p_wrap && curwin->w_p_sms && line_count < min_scrolled && (curwin->w_cursor.lnum < curwin->w_topline || (curwin->w_virtcol - curwin->w_skipcol >= - curwin->w_width - curwin_col_off()))) { + curwin->w_width_inner - curwin_col_off()))) { line_count = min_scrolled; } if (line_count > 0) { @@ -2119,7 +2119,7 @@ void scroll_cursor_halfway(bool atend, bool prefer_above) if (curwin->w_p_sms && curwin->w_p_wrap) { // 'smoothscroll' and 'wrap' are set smooth_scroll = true; - half_height = (curwin->w_height - used) / 2; + half_height = (curwin->w_height_inner - used) / 2; used = 0; } @@ -2268,7 +2268,7 @@ void cursor_correct(void) if (curwin->w_p_sms && !curwin->w_p_wrap) { // 'smoothscroll is active - if (curwin->w_cline_height == curwin->w_height) { + if (curwin->w_cline_height == curwin->w_height_inner) { // The cursor line just fits in the window, don't scroll. reset_skipcol(curwin); return;