From be11f80d018797b514ed7d01cde2e4c8f88cc8d2 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 01:55:00 +0200 Subject: 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 --- 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 ++++ 6 files changed, 141 insertions(+), 21 deletions(-) (limited to 'src/nvim') 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"), -- cgit From 7cc3062e9c13ea13986654119278997d8971baec Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 03:45:10 +0200 Subject: 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(-) (limited to 'src/nvim') 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); -- cgit From e51e63c9bae88bb2d4da669c667e8dbee3ec3562 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 03:48:02 +0200 Subject: 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(+) (limited to 'src/nvim') 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 -- cgit From 9128fc79f0630e0d1e5bedf9bd32417ecd42c4f5 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 03:57:23 +0200 Subject: 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 + 1 file changed, 1 insertion(+) (limited to 'src/nvim') 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; -- cgit From 69af5e8782e601fe9c1e39adf49ce16728719a73 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 04:00:38 +0200 Subject: 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 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim') 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; -- cgit From d6050e9bda7f8b080c577100ae94e017dc146c88 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 04:32:50 +0200 Subject: 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 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 40 deletions(-) (limited to 'src/nvim') 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); -- cgit From a43b28a34c568eb3e280e75a81424f80f0ed980b Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 13:11:33 +0200 Subject: 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 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/nvim') 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) { -- cgit From f3b44cf23d9c240471826e4f35555ee85ded1e5b Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 04:54:50 +0200 Subject: 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 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'src/nvim') 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; -- cgit From 8e4a4629cab3377ba6fd5e291cf5f17fb4ff8a5c Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 16:54:23 +0200 Subject: 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 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'src/nvim') 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); } -- cgit From 3a1973debceca29e65c4f7c83d025cb3314ebaf2 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 17:08:35 +0200 Subject: 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 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/nvim') 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); } -- cgit From d95697d6d4533e84bbb9d262b355ee9f71bd7452 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 17:23:42 +0200 Subject: 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 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'src/nvim') 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); -- cgit From 7dc2c087e7c2071166772dc6b167c802612112fe Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 18:08:55 +0200 Subject: 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 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim') 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++) { -- cgit From 1757ffad4bc0f43d73cd12bf552c95c233d0e77e Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 19:35:16 +0200 Subject: 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(-) (limited to 'src/nvim') 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. -- cgit From 34a4f3729ce4a047ae945238276c8e2dabc38048 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 19:40:22 +0200 Subject: 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(-) (limited to 'src/nvim') 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 { -- cgit From 2918720addd3837abcf06f457e8e2cf674ece5e7 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 20:30:39 +0200 Subject: 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 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/nvim') 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); -- cgit From f3de7f44685c8ec99c1f5c7a624a668044c5aa19 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 21:56:31 +0200 Subject: 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 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) (limited to 'src/nvim') 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; -- cgit From 36c98b47a3526dc61d149f951f8b8e3a677c26eb Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 22:15:25 +0200 Subject: 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 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) (limited to 'src/nvim') 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); } } -- cgit From bf12a85a69f03bb5321fe0a32e1a9ac81f31750c Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 22:32:38 +0200 Subject: 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 ++ 4 files changed, 81 insertions(+), 4 deletions(-) (limited to 'src/nvim') 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; } -- cgit From e8dfff5f288f5fcf4a11d62e973080600f05c980 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 23:23:50 +0200 Subject: 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 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/nvim') 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; -- cgit From 223c7173eea2a4abec75611bd70051ea0b4c5d20 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Wed, 26 Apr 2023 23:36:59 +0200 Subject: 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 ++++++++++++++++++++++++++------------------------ 2 files changed, 41 insertions(+), 37 deletions(-) (limited to 'src/nvim') 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 -- cgit From c426f7a6228cb82af0f75ac4f2421543408ff091 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 00:03:46 +0200 Subject: 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(-) (limited to 'src/nvim') 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. -- cgit From 6146400605af93ac48dae4393569c44e8a2e39d2 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 00:57:48 +0200 Subject: 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 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) (limited to 'src/nvim') 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++; } } -- cgit From 72c525d5675bc017fa52bcc74a394db7d01e6359 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 02:54:51 +0200 Subject: 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 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/nvim') 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) { -- cgit From 88d13d2778a4fab3fdcc2bf4c1adf631b38ea3ce Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 03:36:31 +0200 Subject: 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 +++++---- 2 files changed, 48 insertions(+), 19 deletions(-) (limited to 'src/nvim') 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; -- cgit From 5e4df766f6e428659221f8eae144e9ed18574f8d Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 05:27:31 +0200 Subject: 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 ++++++++++++++++++++-------------- 2 files changed, 22 insertions(+), 14 deletions(-) (limited to 'src/nvim') 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. -- cgit From a2f3855291a59254346545f9699084fe4fece31f Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 17:51:47 +0200 Subject: 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 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/nvim') 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; } -- cgit From 3621604029119a8806da006eb0468cf65e23b980 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 18:35:25 +0200 Subject: 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 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) (limited to 'src/nvim') 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; -- cgit From 26a9f0e94eb62047f0c2bb99401a8ac09840d0dd Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 19:08:20 +0200 Subject: 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(-) (limited to 'src/nvim') 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; -- cgit From 46646a9bb81b72d5579beade64006d6f3dc64d19 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 19:40:00 +0200 Subject: 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 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) (limited to 'src/nvim') 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); } } } -- cgit From 5ba11087b60eb7cbcaa1ea4ccbb0b1a6bcf3c1be Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Fri, 28 Apr 2023 03:59:53 +0200 Subject: 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 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'src/nvim') 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) { -- cgit From c25fd85c2e618535b7780b965a883cb20ab1aa1a Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Fri, 28 Apr 2023 12:32:39 +0200 Subject: 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 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/nvim') 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 -- cgit From 6fd7e3bea4c38e79c77fe506a45781ffd17ebe4f Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Fri, 28 Apr 2023 12:36:11 +0200 Subject: 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 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim') 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; -- cgit From 9b9ccac62563326c1ad59a403aa89851a379ddbb Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Fri, 28 Apr 2023 13:34:07 +0200 Subject: 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 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 131 insertions(+), 42 deletions(-) (limited to 'src/nvim') 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) { -- cgit From 4e4383ffa2b9ec94e703ecbd6178737fc24f6866 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Fri, 28 Apr 2023 16:22:42 +0200 Subject: 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 --- src/nvim/move.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/nvim') 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; -- cgit From 7d5673b18b8b4ff085473b40fce317e760e48ce3 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Sat, 29 Apr 2023 00:16:37 +0200 Subject: 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 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim') 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; } -- cgit From 35ed79a9150fed877e660395c64d6659bf0a07ec Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Sat, 29 Apr 2023 00:34:09 +0200 Subject: 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 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim') 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" -- cgit From 5b111a8f00f8dbe458a3d437c9f06c9419d24840 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Tue, 2 May 2023 12:00:42 +0200 Subject: 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(-) (limited to 'src/nvim') 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; -- cgit