diff options
| author | zeertzjq <zeertzjq@outlook.com> | 2023-05-02 19:42:31 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-02 19:42:31 +0800 |
| commit | 808752f1b09d2f03af5b58167602fef8320c11dc (patch) | |
| tree | 0cf72ae00bd1b08053daee0ede8aa5495be704f0 /src | |
| parent | fba18a3b62310f4535d979a05288101b9af2ef50 (diff) | |
| parent | 5b111a8f00f8dbe458a3d437c9f06c9419d24840 (diff) | |
| download | rneovim-808752f1b09d2f03af5b58167602fef8320c11dc.tar.gz rneovim-808752f1b09d2f03af5b58167602fef8320c11dc.tar.bz2 rneovim-808752f1b09d2f03af5b58167602fef8320c11dc.zip | |
Merge pull request #23320 from luukvbaal/smoothscroll
feat(ui): add 'smoothscroll' option
Diffstat (limited to 'src')
| -rw-r--r-- | src/nvim/buffer_defs.h | 10 | ||||
| -rw-r--r-- | src/nvim/cursor.c | 27 | ||||
| -rw-r--r-- | src/nvim/drawline.c | 26 | ||||
| -rw-r--r-- | src/nvim/drawscreen.c | 20 | ||||
| -rw-r--r-- | src/nvim/edit.c | 7 | ||||
| -rw-r--r-- | src/nvim/ex_cmds.c | 6 | ||||
| -rw-r--r-- | src/nvim/ex_getln.c | 2 | ||||
| -rw-r--r-- | src/nvim/grid.c | 27 | ||||
| -rw-r--r-- | src/nvim/mouse.c | 28 | ||||
| -rw-r--r-- | src/nvim/move.c | 717 | ||||
| -rw-r--r-- | src/nvim/normal.c | 35 | ||||
| -rw-r--r-- | src/nvim/ops.c | 2 | ||||
| -rw-r--r-- | src/nvim/option.c | 16 | ||||
| -rw-r--r-- | src/nvim/option_defs.h | 1 | ||||
| -rw-r--r-- | src/nvim/options.lua | 9 | ||||
| -rw-r--r-- | src/nvim/plines.c | 22 |
16 files changed, 764 insertions, 191 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 02226f3cc4..ce8ee21882 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; 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 @@ -1220,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/cursor.c b/src/nvim/cursor.c index 6d90b32545..8ba0b2ffb3 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--; } @@ -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/drawline.c b/src/nvim/drawline.c index 9c6b5c3b8c..ef9912e503 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -595,19 +595,23 @@ 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)) { - // If 'signcolumn' is set to 'number' and a sign is present - // in "lnum", then display the sign instead of the line - // number. + && (wlv->row == wlv->startrow + wlv->filler_lines || !has_cpo_n) + // 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 { // 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 || (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) { + 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 +758,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 +808,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 +1383,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 +2599,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/drawscreen.c b/src/nvim/drawscreen.c index b5e516005b..ec5163f37a 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_inner - win_col_off(wp); + int width2 = width1 + win_col_off2(wp); + int add = width1; + + while (w < wp->w_skipcol) { + if (w > 0) { + add = width2; + } + w += add; + } + if (w != wp->w_skipcol) { + // always round down, the higher value may not be valid + wp->w_skipcol = w - add; + } + } + // Force redraw when width of 'number' or 'relativenumber' column // changes. int nrwidth = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 0fb1102f4f..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; @@ -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/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/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/grid.c b/src/nvim/grid.c index 7745daf69a..037606c38f 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" @@ -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,30 @@ 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; + // 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) { + // do not overwrite the line number, change "123 text" to + // "123>>>xt". + while (skip < wp->w_width_inner && ascii_isdigit(*linebuf_char[off])) { + off++; + skip++; + } + } + + 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++; + } + } + if (rlflag) { // Clear rest first, because it's left of the text. if (clear_width > 0) { diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 09352b370e..79bd65a88f 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_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; + } 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) { @@ -1648,8 +1664,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 +1672,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/move.c b/src/nvim/move.c index 58f8b1c893..447926ceb8 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -57,6 +57,43 @@ 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_inner - 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; +} + +/// 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_inner) { + return wp->w_height_inner; + } + 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) @@ -78,7 +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); + 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; @@ -127,6 +164,51 @@ 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; +} + +/// 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_inner - 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) +{ + 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) { @@ -178,7 +260,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. @@ -186,6 +268,17 @@ 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 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); + 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; + } } } // Check if there are more filler lines than allowed. @@ -314,12 +407,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(); @@ -392,7 +482,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 != @@ -564,7 +662,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); + int n = plines_correct_topline(wp, lnum, &last, &folded); lnum = last + 1; if (folded && lnum > wp->w_cursor.lnum) { break; @@ -581,7 +679,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; @@ -628,7 +726,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; } @@ -679,8 +777,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) { @@ -694,19 +792,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); // First make sure that w_topline is valid (after moving the cursor). update_topline(wp); @@ -736,8 +829,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_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. // If not wrapping, the last non-empty line. wp->w_wcol = wp->w_width_inner - 1; @@ -747,13 +843,28 @@ 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) { + // 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 -= width2; + } else { + wp->w_wcol -= width2 * (((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 @@ -775,6 +886,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 = @@ -785,8 +897,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); @@ -823,9 +935,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) @@ -833,7 +945,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 @@ -842,7 +954,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 @@ -857,13 +969,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 || plines <= 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 / width; + n = wp->w_virtcol / width2; if (n > wp->w_height_inner / 2) { n -= wp->w_height_inner / 2; } else { @@ -873,51 +985,58 @@ 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; } } - wp->w_wrow -= wp->w_skipcol / width; + // adjust w_wrow for the changed w_skipcol + if (did_sub_skipcol) { + wp->w_wrow -= (wp->w_skipcol - prev_skipcol) / width2; + } else { + 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 { + } else if (!wp->w_p_sms) { 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(curwin); + redraw_for_cursorcolumn(wp); - // 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; } @@ -1064,32 +1183,69 @@ void f_virtcol2col(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) bool scrolldown(long line_count, int byfold) |