diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/autocmd.c | 2 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 2 | ||||
-rw-r--r-- | src/nvim/drawline.c | 224 | ||||
-rw-r--r-- | src/nvim/drawline.h | 11 | ||||
-rw-r--r-- | src/nvim/drawscreen.c | 29 | ||||
-rw-r--r-- | src/nvim/edit.c | 27 | ||||
-rw-r--r-- | src/nvim/eval.c | 4 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 21 | ||||
-rw-r--r-- | src/nvim/ex_cmds.lua | 2 | ||||
-rw-r--r-- | src/nvim/grid.c | 9 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 6 | ||||
-rw-r--r-- | src/nvim/move.c | 12 | ||||
-rw-r--r-- | src/nvim/normal.c | 8 | ||||
-rw-r--r-- | src/nvim/ops.c | 1 | ||||
-rw-r--r-- | src/nvim/runtime.c | 4 | ||||
-rw-r--r-- | src/nvim/window.c | 69 |
16 files changed, 223 insertions, 208 deletions
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 14937cfd8f..0e06594663 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -347,7 +347,7 @@ cleanup: /// vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, { /// pattern = {"*.c", "*.h"}, /// callback = function(ev) -/// print(string.format('event fired: %s', vim.inspect(ev))) +/// print(string.format('event fired: \%s', vim.inspect(ev))) /// end /// }) /// </pre> diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 36163859eb..4722195fe4 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2180,7 +2180,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0; if (wp->w_p_cul) { - if (statuscol.foldinfo.fi_level > 0 && statuscol.foldinfo.fi_lines > 0) { + if (statuscol.foldinfo.fi_level != 0 && statuscol.foldinfo.fi_lines > 0) { wp->w_cursorline = statuscol.foldinfo.fi_lnum; } statuscol.use_cul = lnum == wp->w_cursorline && (wp->w_p_culopt_flags & CULOPT_NBR); diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 4968e667ed..49c4b6f32a 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -421,7 +421,7 @@ size_t fill_foldcolumn(char *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum) size_t char_counter = 0; int symbol = 0; int len = 0; - bool closed = foldinfo.fi_lines > 0; + bool closed = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0; // Init to all spaces. memset(p, ' ', MAX_MCO * (size_t)fdc + 1); @@ -1021,21 +1021,21 @@ static void win_line_continue(winlinevars_T *wlv) } } -/// Display line "lnum" of window 'wp' on the screen. +/// Display line "lnum" of window "wp" on the screen. /// wp->w_virtcol needs to be valid. /// /// @param lnum line to display /// @param startrow first row relative to window grid /// @param endrow last grid row to be redrawn -/// @param mod_top top line updated for changed text /// @param number_only only update the number column +/// @param spv 'spell' related variables kept between calls for "wp" /// @param foldinfo fold info for this line /// @param[in, out] providers decoration providers active this line /// items will be disables if they cause errors /// or explicitly return `false`. /// /// @return the number of last row the line occupies. -int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bool number_only, +int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_only, spellvars_T *spv, foldinfo_T foldinfo, DecorProviders *providers, char **provider_err) { winlinevars_T wlv; // variables passed between functions @@ -1079,7 +1079,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo int eol_hl_off = 0; // 1 if highlighted char after EOL bool draw_color_col = false; // highlight colorcolumn int *color_cols = NULL; // pointer to according columns array - bool has_spell = false; // this buffer has spell checking #define SPWORDLEN 150 char nextline[SPWORDLEN * 2]; // text with start of the next line int nextlinecol = 0; // column where nextline[] starts @@ -1087,11 +1086,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo // starts int spell_attr = 0; // attributes desired by spelling int word_end = 0; // last byte with same spell_attr - static linenr_T checked_lnum = 0; // line number for "checked_col" - static int checked_col = 0; // column in "checked_lnum" up to which - // there are no spell errors - static int cap_col = -1; // column to check for Cap word - static linenr_T capcol_lnum = 0; // line number where "cap_col" int cur_checked_col = 0; // checked column for current line int extra_check = 0; // has syntax or linebreak int multi_attr = 0; // attributes desired by multibyte @@ -1203,43 +1197,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo draw_color_col = advance_color_col(VCOL_HLC, &color_cols); } - if (!has_fold && !end_fill && spell_check_window(wp)) { - // Prepare for spell checking. - has_spell = true; - extra_check = true; - - // Get the start of the next line, so that words that wrap to the next - // line are found too: "et<line-break>al.". - // Trick: skip a few chars for C/shell/Vim comments - nextline[SPWORDLEN] = NUL; - if (lnum < wp->w_buffer->b_ml.ml_line_count) { - line = ml_get_buf(wp->w_buffer, lnum + 1, false); - spell_cat_line(nextline + SPWORDLEN, line, SPWORDLEN); - } - - // When a word wrapped from the previous line the start of the current - // line is valid. - if (lnum == checked_lnum) { - cur_checked_col = checked_col; - } - checked_lnum = 0; - - // When there was a sentence end in the previous line may require a - // word starting with capital in this line. In line 1 always check - // the first word. Also check for sentence end in the line above - // when updating the first row in a window, the top line with - // changed text in a window, or if the previous line is folded. - if (lnum == 1 - || ((startrow == 0 || mod_top == lnum - || hasFoldingWin(wp, lnum - 1, NULL, NULL, true, NULL)) - && check_need_cap(wp, lnum, 0))) { - cap_col = 0; - } else if (lnum != capcol_lnum) { - cap_col = -1; - } - capcol_lnum = 0; - } - // handle Visual active in this window if (VIsual_active && wp->w_buffer == curwin->w_buffer) { pos_T *top, *bot; @@ -1420,18 +1377,46 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo line_attr_lowprio_save = wlv.line_attr_lowprio; } - line = end_fill ? "" : ml_get_buf(wp->w_buffer, lnum, false); - ptr = line; - - if (has_spell && !number_only) { - // For checking first word with a capital skip white space. - if (cap_col == 0) { - cap_col = (int)getwhitecols(line); - } + if (spv->spv_has_spell && !number_only) { + // Prepare for spell checking. + extra_check = true; - // To be able to spell-check over line boundaries copy the end of the - // current line into nextline[]. Above the start of the next line was - // copied to nextline[SPWORDLEN]. + // When a word wrapped from the previous line the start of the + // current line is valid. + if (lnum == spv->spv_checked_lnum) { + cur_checked_col = spv->spv_checked_col; + } + // Previous line was not spell checked, check for capital. This happens + // for the first line in an updated region or after a closed fold. + if (spv->spv_capcol_lnum == 0 && check_need_cap(wp, lnum, 0)) { + spv->spv_cap_col = 0; + } else if (lnum != spv->spv_capcol_lnum) { + spv->spv_cap_col = -1; + } + spv->spv_checked_lnum = 0; + + // Get the start of the next line, so that words that wrap to the + // next line are found too: "et<line-break>al.". + // Trick: skip a few chars for C/shell/Vim comments + nextline[SPWORDLEN] = NUL; + if (lnum < wp->w_buffer->b_ml.ml_line_count) { + line = ml_get_buf(wp->w_buffer, lnum + 1, false); + spell_cat_line(nextline + SPWORDLEN, line, SPWORDLEN); + } + assert(!end_fill); + line = ml_get_buf(wp->w_buffer, lnum, false); + + // If current line is empty, check first word in next line for capital. + ptr = skipwhite(line); + if (*ptr == NUL) { + spv->spv_cap_col = 0; + spv->spv_capcol_lnum = lnum + 1; + } else if (spv->spv_cap_col == 0) { + // For checking first word with a capital skip white space. + spv->spv_cap_col = (int)(ptr - line); + } + + // Copy the end of the current line into nextline[]. if (nextline[SPWORDLEN] == NUL) { // No next line or it is empty. nextlinecol = MAXCOL; @@ -1454,6 +1439,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo } } + line = end_fill ? "" : ml_get_buf(wp->w_buffer, lnum, false); + ptr = line; + colnr_T trailcol = MAXCOL; // start of trailing spaces colnr_T leadcol = 0; // start of leading spaces @@ -1541,7 +1529,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo } // When spell checking a word we need to figure out the start of the // word and if it's badly spelled or not. - if (has_spell) { + if (spv->spv_has_spell) { size_t len; colnr_T linecol = (colnr_T)(ptr - line); hlf_T spell_hlf = HLF_COUNT; @@ -1807,7 +1795,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo int extmark_attr = 0; if (wlv.draw_state == WL_LINE - && (area_highlighting || has_spell || extra_check)) { + && (area_highlighting || spv->spv_has_spell || extra_check)) { // handle Visual or match highlighting in this line if (wlv.vcol == wlv.fromcol || (wlv.vcol + 1 == wlv.fromcol && wlv.n_extra == 0 @@ -1825,13 +1813,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo area_active = false; } - if (!has_fold) { - if (has_decor && v >= 0) { - bool selected = (area_active || (area_highlighting && noinvcur - && wlv.vcol == wp->w_virtcol)); - extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, - selected, &decor_state); + if (has_decor && v >= 0) { + bool selected = (area_active || (area_highlighting && noinvcur + && wlv.vcol == wp->w_virtcol)); + extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, selected, &decor_state); + if (!has_fold) { bool do_save = false; handle_inline_virtual_text(wp, &wlv, v, &do_save); if (do_save) { @@ -1847,43 +1834,43 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo n_skip = 0; } } + } - if (wlv.n_extra == 0) { - // Check for start/end of 'hlsearch' and other matches. - // After end, check for start/end of next match. - // When another match, have to check for start again. - v = (ptr - line); - search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &screen_search_hl, - &has_match_conc, &match_conc, lcs_eol_one, - &on_last_col, &search_attr_from_match); - ptr = line + v; // "line" may have been changed - - // Do not allow a conceal over EOL otherwise EOL will be missed - // and bad things happen. - if (*ptr == NUL) { - has_match_conc = 0; - } + if (wlv.n_extra == 0) { + // Check for start/end of 'hlsearch' and other matches. + // After end, check for start/end of next match. + // When another match, have to check for start again. + v = (ptr - line); + search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &screen_search_hl, + &has_match_conc, &match_conc, lcs_eol_one, + &on_last_col, &search_attr_from_match); + ptr = line + v; // "line" may have been changed + + // Do not allow a conceal over EOL otherwise EOL will be missed + // and bad things happen. + if (*ptr == NUL) { + has_match_conc = 0; } + } - if (wlv.diff_hlf != (hlf_T)0) { - // When there is extra text (eg: virtual text) it gets the - // diff highlighting for the line, but not for changed text. - if (wlv.diff_hlf == HLF_CHD && ptr - line >= change_start - && wlv.n_extra == 0) { - wlv.diff_hlf = HLF_TXD; // changed text - } - if (wlv.diff_hlf == HLF_TXD && ((ptr - line > change_end && wlv.n_extra == 0) - || (wlv.n_extra > 0 && wlv.extra_for_extmark))) { - wlv.diff_hlf = HLF_CHD; // changed line - } - wlv.line_attr = win_hl_attr(wp, (int)wlv.diff_hlf); - // Overlay CursorLine onto diff-mode highlight. - if (wlv.cul_attr) { - wlv.line_attr = 0 != wlv.line_attr_lowprio // Low-priority CursorLine - ? hl_combine_attr(hl_combine_attr(wlv.cul_attr, wlv.line_attr), - hl_get_underline()) - : hl_combine_attr(wlv.line_attr, wlv.cul_attr); - } + if (wlv.diff_hlf != (hlf_T)0) { + // When there is extra text (eg: virtual text) it gets the + // diff highlighting for the line, but not for changed text. + if (wlv.diff_hlf == HLF_CHD && ptr - line >= change_start + && wlv.n_extra == 0) { + wlv.diff_hlf = HLF_TXD; // changed text + } + if (wlv.diff_hlf == HLF_TXD && ((ptr - line > change_end && wlv.n_extra == 0) + || (wlv.n_extra > 0 && wlv.extra_for_extmark))) { + wlv.diff_hlf = HLF_CHD; // changed line + } + wlv.line_attr = win_hl_attr(wp, (int)wlv.diff_hlf); + // Overlay CursorLine onto diff-mode highlight. + if (wlv.cul_attr) { + wlv.line_attr = 0 != wlv.line_attr_lowprio // Low-priority CursorLine + ? hl_combine_attr(hl_combine_attr(wlv.cul_attr, wlv.line_attr), + hl_get_underline()) + : hl_combine_attr(wlv.line_attr, wlv.cul_attr); } } @@ -1995,7 +1982,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo } wlv.extra_for_extmark = false; } - } else if (foldinfo.fi_lines > 0) { + } else if (has_fold) { // skip writing the buffer line itself c = NUL; } else { @@ -2126,7 +2113,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo did_emsg = false; decor_attr = get_syntax_attr((colnr_T)v - 1, - has_spell ? &can_spell : NULL, false); + spv->spv_has_spell ? &can_spell : NULL, false); if (did_emsg) { // -V547 wp->w_s->b_syn_error = true; @@ -2185,7 +2172,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo // @Spell cluster is not used or the current syntax item // contains the @Spell cluster. v = (ptr - line); - if (has_spell && v >= word_end && v > cur_checked_col) { + if (spv->spv_has_spell && v >= word_end && v > cur_checked_col) { spell_attr = 0; char *prev_ptr = ptr - mb_l; // do not calculate cap_col at the end of the line or when @@ -2202,8 +2189,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo } else { p = prev_ptr; } - cap_col -= (int)(prev_ptr - line); - size_t tmplen = spell_check(wp, p, &spell_hlf, &cap_col, mod_top == 0); + spv->spv_cap_col -= (int)(prev_ptr - line); + size_t tmplen = spell_check(wp, p, &spell_hlf, &spv->spv_cap_col, spv->spv_unchanged); assert(tmplen <= INT_MAX); int len = (int)tmplen; word_end = (int)v + len; @@ -2224,8 +2211,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo && (p - nextline) + len > nextline_idx) { // Remember that the good word continues at the // start of the next line. - checked_lnum = lnum + 1; - checked_col = (int)((p - nextline) + len - nextline_idx); + spv->spv_checked_lnum = lnum + 1; + spv->spv_checked_col = (int)((p - nextline) + len - nextline_idx); } // Turn index into actual attributes. @@ -2233,17 +2220,15 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo spell_attr = highlight_attr[spell_hlf]; } - if (cap_col > 0) { - if (p != prev_ptr - && (p - nextline) + cap_col >= nextline_idx) { + if (spv->spv_cap_col > 0) { + if (p != prev_ptr && (p - nextline) + spv->spv_cap_col >= nextline_idx) { // Remember that the word in the next line // must start with a capital. - capcol_lnum = lnum + 1; - cap_col = (int)((p - nextline) + cap_col - - nextline_idx); + spv->spv_capcol_lnum = lnum + 1; + spv->spv_cap_col = (int)((p - nextline) + spv->spv_cap_col - nextline_idx); } else { // Compute the actual column. - cap_col += (int)(prev_ptr - line); + spv->spv_cap_col += (int)(prev_ptr - line); } } } @@ -2767,8 +2752,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo ? 1 : 0); if (has_decor) { - has_virttext = decor_redraw_eol(wp, &decor_state, &wlv.line_attr, - wlv.col + eol_skip); + has_virttext = decor_redraw_eol(wp, &decor_state, &wlv.line_attr, wlv.col + eol_skip); } if (((wp->w_p_cuc @@ -2862,7 +2846,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo if (wp == curwin && lnum == curwin->w_cursor.lnum) { curwin->w_cline_row = startrow; curwin->w_cline_height = wlv.row - startrow; - curwin->w_cline_folded = foldinfo.fi_lines > 0; + curwin->w_cline_folded = has_fold; curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW); conceal_cursor_used = conceal_cursor_line(curwin); } @@ -3152,12 +3136,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int mod_top, bo } } // for every character in the line - // After an empty line check first word for capital. - if (*skipwhite(line) == NUL) { - capcol_lnum = lnum + 1; - cap_col = 0; - } - kv_destroy(virt_lines); xfree(wlv.p_extra_free); return wlv.row; diff --git a/src/nvim/drawline.h b/src/nvim/drawline.h index 91261aba78..9a917df0c5 100644 --- a/src/nvim/drawline.h +++ b/src/nvim/drawline.h @@ -23,6 +23,17 @@ EXTERN kvec_t(WinExtmark) win_extmark_arr INIT(= KV_INITIAL_VALUE); EXTERN bool conceal_cursor_used INIT(= false); +// Spell checking variables passed from win_update() to win_line(). +typedef struct { + bool spv_has_spell; ///< drawn window has spell checking + bool spv_unchanged; ///< not updating for changed text + int spv_checked_col; ///< column in "checked_lnum" up to + ///< which there are no spell errors + linenr_T spv_checked_lnum; ///< line number for "checked_col" + int spv_cap_col; ///< column to check for Cap word + linenr_T spv_capcol_lnum; ///< line number for "cap_col" +} spellvars_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "drawline.h.generated.h" #endif diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 4f79ba87af..28a029d758 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -95,6 +95,7 @@ #include "nvim/profile.h" #include "nvim/regexp.h" #include "nvim/search.h" +#include "nvim/spell.h" #include "nvim/state.h" #include "nvim/statusline.h" #include "nvim/syntax.h" @@ -1984,18 +1985,25 @@ static void win_update(win_T *wp, DecorProviders *providers) if (wp->w_p_cul) { // Make sure that the cursorline on a closed fold is redrawn cursorline_fi = fold_info(wp, wp->w_cursor.lnum); - if (cursorline_fi.fi_level > 0 && cursorline_fi.fi_lines > 0) { + if (cursorline_fi.fi_level != 0 && cursorline_fi.fi_lines > 0) { wp->w_cursorline = cursorline_fi.fi_lnum; } } win_check_ns_hl(wp); + spellvars_T spv = { 0 }; + linenr_T lnum = wp->w_topline; // first line shown in window + // Initialize spell related variables for the first drawn line. + if (spell_check_window(wp)) { + spv.spv_has_spell = true; + spv.spv_unchanged = mod_top == 0; + } + // Update all the window rows. int idx = 0; // first entry in w_lines[].wl_size int row = 0; // current window row to display int srow = 0; // starting row of the current line - linenr_T lnum = wp->w_topline; // first line shown in window bool eof = false; // if true, we hit the end of the file bool didline = false; // if true, we finished the last line @@ -2222,8 +2230,10 @@ static void win_update(win_T *wp, DecorProviders *providers) } // Display one line - row = win_line(wp, lnum, srow, foldinfo.fi_lines ? srow : wp->w_grid.rows, - mod_top, false, foldinfo, &line_providers, &provider_err); + spellvars_T zero_spv = { 0 }; + row = win_line(wp, lnum, srow, foldinfo.fi_lines > 0 ? srow : wp->w_grid.rows, false, + foldinfo.fi_lines > 0 ? &zero_spv : &spv, + foldinfo, &line_providers, &provider_err); if (foldinfo.fi_lines == 0) { wp->w_lines[idx].wl_folded = false; @@ -2235,6 +2245,7 @@ static void win_update(win_T *wp, DecorProviders *providers) wp->w_lines[idx].wl_folded = true; wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines; did_update = DID_FOLD; + spv.spv_capcol_lnum = 0; } } @@ -2260,7 +2271,7 @@ static void win_update(win_T *wp, DecorProviders *providers) // text doesn't need to be drawn, but the number column does. foldinfo_T info = wp->w_p_cul && lnum == wp->w_cursor.lnum ? cursorline_fi : fold_info(wp, lnum); - (void)win_line(wp, lnum, srow, wp->w_grid.rows, mod_top, true, + (void)win_line(wp, lnum, srow, wp->w_grid.rows, true, &spv, info, &line_providers, &provider_err); } @@ -2271,6 +2282,7 @@ static void win_update(win_T *wp, DecorProviders *providers) } lnum = wp->w_lines[idx - 1].wl_lastlnum + 1; did_update = DID_NONE; + spv.spv_capcol_lnum = 0; } // 'statuscolumn' width has changed or errored, start from the top. @@ -2356,9 +2368,10 @@ static void win_update(win_T *wp, DecorProviders *providers) if (j > 0 && !wp->w_botfill && row < wp->w_grid.rows) { // Display filler text below last line. win_line() will check // for ml_line_count+1 and only draw filler lines - foldinfo_T info = { 0 }; - row = win_line(wp, wp->w_botline, row, wp->w_grid.rows, - mod_top, false, info, &line_providers, &provider_err); + spellvars_T zero_spv = { 0 }; + foldinfo_T zero_foldinfo = { 0 }; + row = win_line(wp, wp->w_botline, row, wp->w_grid.rows, false, &zero_spv, + zero_foldinfo, &line_providers, &provider_err); } } else if (dollar_vcol == -1) { wp->w_botline = lnum; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index d6d5ff8ac7..6b90c40c7c 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -2549,15 +2549,10 @@ int oneleft(void) /// Move the cursor up "n" lines in window "wp". /// Takes care of closed folds. -/// Returns the new cursor line or zero for failure. -linenr_T cursor_up_inner(win_T *wp, long n) +void cursor_up_inner(win_T *wp, long n) { linenr_T lnum = wp->w_cursor.lnum; - // This fails if the cursor is already in the first line. - if (lnum <= 1) { - return 0; - } if (n >= lnum) { lnum = 1; } else if (hasAnyFolding(wp)) { @@ -2587,15 +2582,16 @@ linenr_T cursor_up_inner(win_T *wp, long n) } wp->w_cursor.lnum = lnum; - return lnum; } /// @param upd_topline When true: update topline int cursor_up(long n, int upd_topline) { - if (n > 0 && cursor_up_inner(curwin, n) == 0) { + // This fails if the cursor is already in the first line. + if (n > 0 && curwin->w_cursor.lnum <= 1) { return FAIL; } + cursor_up_inner(curwin, n); // try to advance to the column we want to be at coladvance(curwin->w_curswant); @@ -2609,18 +2605,11 @@ int cursor_up(long n, int upd_topline) /// Move the cursor down "n" lines in window "wp". /// Takes care of closed folds. -/// Returns the new cursor line or zero for failure. -linenr_T cursor_down_inner(win_T *wp, long n) +void cursor_down_inner(win_T *wp, long n) { linenr_T lnum = wp->w_cursor.lnum; linenr_T line_count = wp->w_buffer->b_ml.ml_line_count; - // Move to last line of fold, will fail if it's the end-of-file. - (void)hasFoldingWin(wp, lnum, NULL, &lnum, true, NULL); - // This fails if the cursor is already in the last line. - if (lnum >= line_count) { - return FAIL; - } if (lnum + n >= line_count) { lnum = line_count; } else if (hasAnyFolding(wp)) { @@ -2628,6 +2617,7 @@ linenr_T cursor_down_inner(win_T *wp, long n) // count each sequence of folded lines as one logical line while (n--) { + // Move to last line of fold, will fail if it's the end-of-file. if (hasFoldingWin(wp, lnum, NULL, &last, true, NULL)) { lnum = last + 1; } else { @@ -2645,15 +2635,16 @@ linenr_T cursor_down_inner(win_T *wp, long n) } wp->w_cursor.lnum = lnum; - return lnum; } /// @param upd_topline When true: update topline int cursor_down(long n, int upd_topline) { - if (n > 0 && cursor_down_inner(curwin, n) == 0) { + // This fails if the cursor is already in the last line. + if (n > 0 && curwin->w_cursor.lnum >= curwin->w_buffer->b_ml.ml_line_count) { return FAIL; } + cursor_down_inner(curwin, n); // try to advance to the column we want to be at coladvance(curwin->w_curswant); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 52924bf9a5..118c1d3012 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6511,6 +6511,10 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret pos.coladd = 0; if (name[0] == 'w' && dollar_lnum) { + // the "w_valid" flags are not reset when moving the cursor, but they + // do matter for update_topline() and validate_botline(). + check_cursor_moved(curwin); + pos.col = 0; if (name[1] == '0') { // "w0": first visible line update_topline(curwin); diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 927c1b3d5c..04fd81c713 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -62,7 +62,7 @@ #include "nvim/getchar.h" #include "nvim/gettext.h" #include "nvim/globals.h" -#include "nvim/grid_defs.h" +#include "nvim/grid.h" #include "nvim/hashtab.h" #include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" @@ -3238,7 +3238,7 @@ static bool has_wsl(void) static TriState has_wsl = kNone; if (has_wsl == kNone) { Error err = ERROR_INIT; - Object o = nlua_exec(STATIC_CSTR_AS_STRING("return vim.loop.os_uname()['release']:lower()" + Object o = nlua_exec(STATIC_CSTR_AS_STRING("return vim.uv.os_uname()['release']:lower()" ":match('microsoft') and true or false"), (Array)ARRAY_DICT_INIT, &err); assert(!ERROR_SET(&err)); @@ -6745,7 +6745,9 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) { c = -1; } else { - c = utf_ptr2char((char *)grid->chars[grid->line_offset[row] + (size_t)col]); + char buf[MB_MAXBYTES + 1]; + grid_getbytes(grid, row, col, buf, NULL); + c = utf_ptr2char(buf); } rettv->vval.v_number = c; } @@ -6763,10 +6765,13 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) tv_list_alloc_ret(rettv, 0); return; } + + char buf[MB_MAXBYTES + 1]; + grid_getbytes(grid, row, col, buf, NULL); int pcc[MAX_MCO]; - int c = utfc_ptr2char((char *)grid->chars[grid->line_offset[row] + (size_t)col], pcc); + int c = utfc_ptr2char(buf, pcc); int composing_len = 0; - while (pcc[composing_len] != 0) { + while (composing_len < MAX_MCO && pcc[composing_len] != 0) { composing_len++; } tv_list_alloc_ret(rettv, composing_len + 1); @@ -6806,7 +6811,9 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, EvalFuncData fptr return; } - rettv->vval.v_string = xstrdup((char *)grid->chars[grid->line_offset[row] + (size_t)col]); + char buf[MB_MAXBYTES + 1]; + grid_getbytes(grid, row, col, buf, NULL); + rettv->vval.v_string = xstrdup(buf); } /// "search()" function @@ -7201,7 +7208,7 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } /// Set the cursor or mark position. -/// If 'charpos' is true, then use the column number as a character offset. +/// If "charpos" is true, then use the column number as a character offset. /// Otherwise use the column number as a byte offset. static void set_position(typval_T *argvars, typval_T *rettv, bool charpos) { diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index b0938fa711..88f3bc0b43 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -2212,7 +2212,7 @@ module.cmds = { }, { command='registers', - flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, CMDWIN, LOCK_OK), + flags=bit.bor(EXTRA, NOTRLCOM, TRLBAR, SBOXOK, CMDWIN, LOCK_OK), addr_type='ADDR_NONE', func='ex_display', }, diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 76dd2a073a..aa542c5a2f 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -138,8 +138,9 @@ void grid_putchar(ScreenGrid *grid, int c, int row, int col, int attr) grid_puts(grid, buf, row, col, attr); } -/// get a single character directly from grid.chars into "bytes[]". -/// Also return its attribute in *attrp; +/// Get a single character directly from grid.chars into "bytes", which must +/// have a size of "MB_MAXBYTES + 1". +/// If "attrp" is not NULL, return the character's attribute in "*attrp". void grid_getbytes(ScreenGrid *grid, int row, int col, char *bytes, int *attrp) { grid_adjust(&grid, &row, &col); @@ -150,7 +151,9 @@ void grid_getbytes(ScreenGrid *grid, int row, int col, char *bytes, int *attrp) } size_t off = grid->line_offset[row] + (size_t)col; - *attrp = grid->attrs[off]; + if (attrp != NULL) { + *attrp = grid->attrs[off]; + } schar_copy(bytes, grid->chars[off]); } diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 8c1d8addcd..36dcb6efcf 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -565,7 +565,7 @@ static void nlua_common_vim_init(lua_State *lstate, bool is_thread, bool is_stan lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.empty_dict"); lua_setfield(lstate, -2, "_empty_dict_mt"); - // vim.loop + // vim.uv if (is_standalone) { // do nothing, use libluv like in a standalone interpreter } else if (is_thread) { @@ -578,9 +578,9 @@ static void nlua_common_vim_init(lua_State *lstate, bool is_thread, bool is_stan } luaopen_luv(lstate); lua_pushvalue(lstate, -1); - lua_setfield(lstate, -3, "loop"); + lua_setfield(lstate, -3, "uv"); - // package.loaded.luv = vim.loop + // package.loaded.luv = vim.uv // otherwise luv will be reinitialized when require'luv' lua_getglobal(lstate, "package"); lua_getfield(lstate, -1, "loaded"); diff --git a/src/nvim/move.c b/src/nvim/move.c index 9e8abbcd96..48691db26d 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -548,18 +548,20 @@ void set_topline(win_T *wp, linenr_T lnum) redraw_later(wp, UPD_VALID); } -// Call this function when the length of the cursor line (in screen -// characters) has changed, and the change is before the cursor. -// Need to take care of w_botline separately! +/// Call this function when the length of the cursor line (in screen +/// characters) has changed, and the change is before the cursor. +/// If the line length changed the number of screen lines might change, +/// requiring updating w_topline. That may also invalidate w_crow. +/// Need to take care of w_botline separately! void changed_cline_bef_curs(void) { - curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL + curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CROW |VALID_CHEIGHT|VALID_TOPLINE); } void changed_cline_bef_curs_win(win_T *wp) { - wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL + wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CROW |VALID_CHEIGHT|VALID_TOPLINE); } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 60fff45323..f3909030c9 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2493,10 +2493,12 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) curwin->w_curswant -= width2; } else { // to previous line - if (!cursor_up_inner(curwin, 1)) { + if (curwin->w_cursor.lnum <= 1) { retval = false; break; } + cursor_up_inner(curwin, 1); + linelen = linetabsize_str(get_cursor_line_ptr()); if (linelen > width1) { int w = (((linelen - width1 - 1) / width2) + 1) * width2; @@ -2516,11 +2518,13 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) curwin->w_curswant += width2; } else { // to next line - if (!cursor_down_inner(curwin, 1)) { + if (curwin->w_cursor.lnum >= curwin->w_buffer->b_ml.ml_line_count) { retval = false; break; } + cursor_down_inner(curwin, 1); curwin->w_curswant %= width2; + // Check if the cursor has moved below the number display // when width1 < width2 (with cpoptions+=n). Subtract width2 // to get a negative value for w_curswant, which will get diff --git a/src/nvim/ops.c b/src/nvim/ops.c index c39a3273da..2711c3d29c 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3484,6 +3484,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) if (lnum == curwin->w_cursor.lnum) { // make sure curwin->w_virtcol is updated changed_cline_bef_curs(); + invalidate_botline(); curwin->w_cursor.col += (colnr_T)(totlen - 1); } changed_bytes(lnum, col); diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 4b27067fb8..964e2d0933 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -1926,8 +1926,8 @@ static void cmd_source_buffer(const exarg_T *const eap) .buf = ga.ga_data, .offset = 0, }; - if (curbuf->b_fname - && path_with_extension(curbuf->b_fname, "lua")) { + if (strequal(curbuf->b_p_ft, "lua") + || (curbuf->b_fname && path_with_extension(curbuf->b_fname, "lua"))) { nlua_source_using_linegetter(get_str_line, (void *)&cookie, ":source (no file)"); } else { source_using_linegetter((void *)&cookie, get_str_line, ":source (no file)"); diff --git a/src/nvim/window.c b/src/nvim/window.c index 90c8ba92f9..ebfa538afb 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1581,7 +1581,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // equalize the window sizes. if (do_equal || dir != 0) { win_equal(wp, true, (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') : (dir == 'h' ? 'b' : 'v')); - } else if (*p_spk != 'c' && !is_aucmd_win(wp)) { + } else if (!is_aucmd_win(wp)) { win_fix_scroll(false); } @@ -2174,7 +2174,7 @@ void win_equal(win_T *next_curwin, bool current, int dir) win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current, topframe, dir, 0, tabline_height(), Columns, topframe->fr_height); - if (*p_spk != 'c' && !is_aucmd_win(next_curwin)) { + if (!is_aucmd_win(next_curwin)) { win_fix_scroll(true); } } @@ -2970,9 +2970,7 @@ int win_close(win_T *win, bool free_buf, bool force) win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir); } else { (void)win_comp_pos(); - if (*p_spk != 'c') { - win_fix_scroll(false); - } + win_fix_scroll(false); } } @@ -5364,7 +5362,7 @@ void win_new_screen_rows(void) compute_cmdrow(); curtab->tp_ch_used = p_ch; - if (*p_spk != 'c' && !skip_win_fix_scroll) { + if (!skip_win_fix_scroll) { win_fix_scroll(true); } } @@ -5811,9 +5809,7 @@ void win_setheight_win(int height, win_T *win) msg_row = row; msg_col = 0; - if (*p_spk != 'c') { - win_fix_scroll(true); - } + win_fix_scroll(true); redraw_all_later(UPD_NOT_VALID); redraw_cmdline = true; @@ -6293,9 +6289,7 @@ void win_drag_status_line(win_T *dragwin, int offset) p_ch = MAX(Rows - cmdline_row, p_ch_was_zero ? 0 : 1); curtab->tp_ch_used = p_ch; - if (*p_spk != 'c') { - win_fix_scroll(true); - } + win_fix_scroll(true); redraw_all_later(UPD_SOME_VALID); showmode(); @@ -6414,15 +6408,18 @@ void set_fraction(win_T *wp) } } -/// Handle scroll position for 'splitkeep'. Replaces scroll_to_fraction() -/// call from win_set_inner_size(). Instead we iterate over all windows in a -/// tabpage and calculate the new scroll position. -/// TODO(luukvbaal): Ensure this also works with wrapped lines. -/// Requires topline to be able to be set to a bufferline with some -/// offset(row-wise scrolling/smoothscroll). +/// Handle scroll position, depending on 'splitkeep'. Replaces the +/// scroll_to_fraction() call from win_new_height() if 'splitkeep' is "screen" +/// or "topline". Instead we iterate over all windows in a tabpage and +/// calculate the new scroll position. +/// TODO(vim): Ensure this also works with wrapped lines. +/// Requires a not fully visible cursor line to be allowed at the bottom of +/// a window("zb"), probably only when 'smoothscroll' is also set. void win_fix_scroll(int resize) { - linenr_T lnum; + if (*p_spk == 'c') { + return; // 'splitkeep' is "cursor" + } skip_update_topline = true; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { @@ -6431,17 +6428,20 @@ void win_fix_scroll(int resize) // If window has moved update botline to keep the same screenlines. if (*p_spk == 's' && wp->w_winrow != wp->w_prev_winrow && wp->w_botline - 1 <= wp->w_buffer->b_ml.ml_line_count) { - lnum = wp->w_cursor.lnum; int diff = (wp->w_winrow - wp->w_prev_winrow) + (wp->w_height - wp->w_prev_height); + linenr_T lnum = wp->w_cursor.lnum; wp->w_cursor.lnum = wp->w_botline - 1; + // Add difference in height and row to botline. if (diff > 0) { cursor_down_inner(wp, diff); } else { cursor_up_inner(wp, -diff); } - // Bring the new cursor position to the bottom of the screen. + + // Scroll to put the new cursor position at the bottom of the + // screen. wp->w_fraction = FRACTION_MULT; scroll_to_fraction(wp, wp->w_prev_height); wp->w_cursor.lnum = lnum; @@ -6470,36 +6470,37 @@ void win_fix_scroll(int resize) static void win_fix_cursor(int normal) { win_T *wp = curwin; - long so = get_scrolloff_value(wp); - linenr_T nlnum = 0; - linenr_T lnum = wp->w_cursor.lnum; - if (wp->w_buffer->b_ml.ml_line_count < wp->w_height - || skip_win_fix_cursor) { + if (skip_win_fix_cursor || wp->w_buffer->b_ml.ml_line_count < wp->w_height) { return; } // Determine valid cursor range. - so = MIN(wp->w_height_inner / 2, so); + long so = MIN(wp->w_height_inner / 2, get_scrolloff_value(wp)); + linenr_T lnum = wp->w_cursor.lnum; + wp->w_cursor.lnum = wp->w_topline; - linenr_T top = cursor_down_inner(wp, so); + cursor_down_inner(wp, so); + linenr_T top = wp->w_cursor.lnum; + wp->w_cursor.lnum = wp->w_botline - 1; - linenr_T bot = cursor_up_inner(wp, so); + cursor_up_inner(wp, so); + linenr_T bot = wp->w_cursor.lnum; + wp->w_cursor.lnum = lnum; // Check if cursor position is above or below valid cursor range. + linenr_T nlnum = 0; if (lnum > bot && (wp->w_botline - wp->w_buffer->b_ml.ml_line_count) != 1) { nlnum = bot; } else if (lnum < top && wp->w_topline != 1) { nlnum = (so == wp->w_height / 2) ? bot : top; } - if (nlnum) { // Cursor is invalid for current scroll position. - if (normal) { - // Save to jumplist and set cursor to avoid scrolling. + if (nlnum != 0) { // Cursor is invalid for current scroll position. + if (normal) { // Save to jumplist and set cursor to avoid scrolling. setmark('\''); wp->w_cursor.lnum = nlnum; - } else { - // Scroll instead when not in normal mode. + } else { // Scroll instead when not in normal mode. wp->w_fraction = (nlnum == bot) ? FRACTION_MULT : 0; scroll_to_fraction(wp, wp->w_prev_height); validate_botline(curwin); |