diff options
Diffstat (limited to 'src/nvim/normal.c')
-rw-r--r-- | src/nvim/normal.c | 139 |
1 files changed, 71 insertions, 68 deletions
diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 8ba375f29d..be9987cc7f 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -28,12 +28,14 @@ #include "nvim/digraph.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" +#include "nvim/file_search.h" #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" @@ -349,6 +351,7 @@ static const struct nv_cmd { { K_F1, nv_help, NV_NCW, 0 }, { K_XF1, nv_help, NV_NCW, 0 }, { K_SELECT, nv_select, 0, 0 }, + { K_PASTE_START, nv_paste, NV_KEEPREG, 0 }, { K_EVENT, nv_event, NV_KEEPREG, 0 }, { K_COMMAND, nv_colon, 0, 0 }, { K_LUA, nv_colon, 0, 0 }, @@ -835,7 +838,10 @@ static void normal_get_additional_char(NormalState *s) while ((s->c = vpeekc()) > 0 && (s->c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1)) { s->c = plain_vgetc(); - if (!utf_iscomposing(s->c)) { + // TODO(bfredl): only allowing up to two composing chars is cringe af. + // Could reuse/abuse schar_T to at least allow us to input anything we are able + // to display and use the stateful utf8proc algorithm like utf_composinglike + if (!utf_iscomposing_legacy(s->c)) { vungetc(s->c); // it wasn't, put it back break; } else if (s->ca.ncharC1 == 0) { @@ -1659,7 +1665,6 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text // When starting on a ']' count it, so that we include the '['. bn = ptr[col] == ']'; - // // 2. Back up to start of identifier/text. // // Remember class of character under cursor. @@ -1685,9 +1690,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text // If we don't want just any old text, or we've found an // identifier, stop searching. - if (this_class > 2) { - this_class = 2; - } + this_class = MIN(this_class, 2); if (!(find_type & FIND_STRING) || this_class == 2) { break; } @@ -1986,7 +1989,7 @@ bool add_to_showcmd(int c) size_t overflow = old_len + extra_len - limit; memmove(showcmd_buf, showcmd_buf + overflow, old_len - overflow + 1); } - STRCAT(showcmd_buf, p); + strcat(showcmd_buf, p); if (char_avail()) { return false; @@ -2011,9 +2014,7 @@ static void del_from_showcmd(int len) } int old_len = (int)strlen(showcmd_buf); - if (len > old_len) { - len = old_len; - } + len = MIN(len, old_len); showcmd_buf[old_len - len] = NUL; if (!char_avail()) { @@ -2096,17 +2097,23 @@ static void display_showcmd(void) grid_line_flush(); } +int get_vtopline(win_T *wp) +{ + return plines_m_win_fill(wp, 1, wp->w_topline) - wp->w_topfill; +} + /// When "check" is false, prepare for commands that scroll the window. /// When "check" is true, take care of scroll-binding after the window has /// scrolled. Called from normal_cmd() and edit(). void do_check_scrollbind(bool check) { static win_T *old_curwin = NULL; - static linenr_T old_topline = 0; - static int old_topfill = 0; + static linenr_T old_vtopline = 0; static buf_T *old_buf = NULL; static colnr_T old_leftcol = 0; + int vtopline = get_vtopline(curwin); + if (check && curwin->w_p_scb) { // If a ":syncbind" command was just used, don't scroll, only reset // the values. @@ -2119,10 +2126,9 @@ void do_check_scrollbind(bool check) if ((curwin->w_buffer == old_buf || curwin->w_p_diff ) - && (curwin->w_topline != old_topline - || curwin->w_topfill != old_topfill + && (vtopline != old_vtopline || curwin->w_leftcol != old_leftcol)) { - check_scrollbind(curwin->w_topline - old_topline, curwin->w_leftcol - old_leftcol); + check_scrollbind(vtopline - old_vtopline, curwin->w_leftcol - old_leftcol); } } else if (vim_strchr(p_sbo, 'j')) { // jump flag set in 'scrollopt' // When switching between windows, make sure that the relative @@ -2133,14 +2139,13 @@ void do_check_scrollbind(bool check) // resync is performed, some of the other 'scrollbind' windows may // need to jump so that the current window's relative position is // visible on-screen. - check_scrollbind(curwin->w_topline - (linenr_T)curwin->w_scbind_pos, 0); + check_scrollbind(vtopline - curwin->w_scbind_pos, 0); } - curwin->w_scbind_pos = curwin->w_topline; + curwin->w_scbind_pos = vtopline; } old_curwin = curwin; - old_topline = curwin->w_topline; - old_topfill = curwin->w_topfill; + old_vtopline = vtopline; old_buf = curwin->w_buffer; old_leftcol = curwin->w_leftcol; } @@ -2148,20 +2153,18 @@ void do_check_scrollbind(bool check) /// Synchronize any windows that have "scrollbind" set, based on the /// number of rows by which the current window has changed /// (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>) -void check_scrollbind(linenr_T topline_diff, int leftcol_diff) +void check_scrollbind(linenr_T vtopline_diff, int leftcol_diff) { win_T *old_curwin = curwin; buf_T *old_curbuf = curbuf; int old_VIsual_select = VIsual_select; int old_VIsual_active = VIsual_active; colnr_T tgt_leftcol = curwin->w_leftcol; - linenr_T topline; - linenr_T y; // check 'scrollopt' string for vertical and horizontal scroll options - bool want_ver = (vim_strchr(p_sbo, 'v') && topline_diff != 0); - want_ver |= old_curwin->w_p_diff; - bool want_hor = (vim_strchr(p_sbo, 'h') && (leftcol_diff || topline_diff != 0)); + bool want_ver = old_curwin->w_p_diff + || (vim_strchr(p_sbo, 'v') && vtopline_diff != 0); + bool want_hor = (vim_strchr(p_sbo, 'h') && (leftcol_diff || vtopline_diff != 0)); // loop through the scrollbound windows and scroll accordingly VIsual_select = VIsual_active = 0; @@ -2178,16 +2181,19 @@ void check_scrollbind(linenr_T topline_diff, int leftcol_diff) if (old_curwin->w_p_diff && curwin->w_p_diff) { diff_set_topline(old_curwin, curwin); } else { - curwin->w_scbind_pos += topline_diff; - topline = (linenr_T)curwin->w_scbind_pos; - if (topline > curbuf->b_ml.ml_line_count) { - topline = curbuf->b_ml.ml_line_count; - } - if (topline < 1) { - topline = 1; - } + curwin->w_scbind_pos += vtopline_diff; + int curr_vtopline = get_vtopline(curwin); - y = topline - curwin->w_topline; + // Perf: reuse curr_vtopline to reduce the time in plines_m_win_fill(). + // Equivalent to: + // int max_vtopline = plines_m_win_fill(curwin, 1, curbuf->b_ml.ml_line_count); + int max_vtopline = curr_vtopline + curwin->w_topfill + + plines_m_win_fill(curwin, curwin->w_topline + 1, + curbuf->b_ml.ml_line_count); + + int new_vtopline = MAX(MIN((linenr_T)curwin->w_scbind_pos, max_vtopline), 1); + + int y = new_vtopline - curr_vtopline; if (y > 0) { scrollup(curwin, y, false); } else { @@ -2515,9 +2521,7 @@ bool nv_screengo(oparg_T *oap, int dir, int dist) } else { n = width1; } - if (curwin->w_curswant >= n) { - curwin->w_curswant = n - 1; - } + curwin->w_curswant = MIN(curwin->w_curswant, n - 1); } while (dist--) { @@ -2776,11 +2780,7 @@ static void nv_zet(cmdarg_T *cap) if (cap->count0 == 0) { // No count given: put cursor at the line below screen validate_botline(curwin); // make sure w_botline is valid - if (curwin->w_botline > curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - } else { - curwin->w_cursor.lnum = curwin->w_botline; - } + curwin->w_cursor.lnum = MIN(curwin->w_botline, curbuf->b_ml.ml_line_count); } FALLTHROUGH; case NL: @@ -3049,9 +3049,7 @@ static void nv_zet(cmdarg_T *cap) case 'm': if (curwin->w_p_fdl > 0) { curwin->w_p_fdl -= cap->count1; - if (curwin->w_p_fdl < 0) { - curwin->w_p_fdl = 0; - } + curwin->w_p_fdl = MAX(curwin->w_p_fdl, 0); } old_fdl = -1; // force an update curwin->w_p_fen = true; @@ -3069,9 +3067,7 @@ static void nv_zet(cmdarg_T *cap) curwin->w_p_fdl += cap->count1; { int d = getDeepestNesting(curwin); - if (curwin->w_p_fdl >= d) { - curwin->w_p_fdl = d; - } + curwin->w_p_fdl = MIN(curwin->w_p_fdl, d); } break; @@ -3662,10 +3658,7 @@ static void nv_scroll(cmdarg_T *cap) n = lnum - curwin->w_topline; } } - curwin->w_cursor.lnum = curwin->w_topline + n; - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - } + curwin->w_cursor.lnum = MIN(curwin->w_topline + n, curbuf->b_ml.ml_line_count); } // Correct for 'so', except when an operator is pending. @@ -3972,6 +3965,12 @@ static void nv_next(cmdarg_T *cap) normal_search(cap, 0, NULL, 0, SEARCH_MARK | cap->arg, NULL); cap->count1 -= 1; } + + // Redraw the window to refresh the highlighted matches. + if (i > 0 && p_hls && !no_hlsearch + && win_hl_attr(curwin, HLF_LC) != win_hl_attr(curwin, HLF_L)) { + redraw_later(curwin, UPD_SOME_VALID); + } } /// Search for "pat" in direction "dir" ('/' or '?', 0 for repeat). @@ -3983,6 +3982,7 @@ static void nv_next(cmdarg_T *cap) static int normal_search(cmdarg_T *cap, int dir, char *pat, size_t patlen, int opt, int *wrapped) { searchit_arg_T sia; + pos_T const prev_cursor = curwin->w_cursor; cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; @@ -4006,6 +4006,11 @@ static int normal_search(cmdarg_T *cap, int dir, char *pat, size_t patlen, int o foldOpenCursor(); } } + // Redraw the window to refresh the highlighted matches. + if (!equalpos(curwin->w_cursor, prev_cursor) && p_hls && !no_hlsearch + && win_hl_attr(curwin, HLF_LC) != win_hl_attr(curwin, HLF_L)) { + redraw_later(curwin, UPD_SOME_VALID); + } // "/$" will put the cursor after the end of the line, may need to // correct that here @@ -4332,12 +4337,8 @@ static void nv_percent(cmdarg_T *cap) curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count * cap->count0 + 99) / 100; } - if (curwin->w_cursor.lnum < 1) { - curwin->w_cursor.lnum = 1; - } - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - } + curwin->w_cursor.lnum = MIN(MAX(curwin->w_cursor.lnum, 1), curbuf->b_ml.ml_line_count); + beginline(BL_SOL | BL_FIX); } } else { // "%" : go to matching paren @@ -5634,6 +5635,7 @@ static void nv_g_cmd(cmdarg_T *cap) // "go": goto byte count from start of buffer case 'o': + oap->inclusive = false; goto_byte(cap->count0); break; @@ -6076,11 +6078,7 @@ static void nv_goto(cmdarg_T *cap) if (cap->count0 != 0) { lnum = cap->count0; } - if (lnum < 1) { - lnum = 1; - } else if (lnum > curbuf->b_ml.ml_line_count) { - lnum = curbuf->b_ml.ml_line_count; - } + lnum = MIN(MAX(lnum, 1), curbuf->b_ml.ml_line_count); curwin->w_cursor.lnum = lnum; beginline(BL_SOL | BL_FIX); if ((fdo_flags & FDO_JUMP) && KeyTyped && cap->oap->op_type == OP_NOP) { @@ -6410,9 +6408,8 @@ static void nv_join(cmdarg_T *cap) return; } - if (cap->count0 <= 1) { - cap->count0 = 2; // default for join is two lines! - } + cap->count0 = MAX(cap->count0, 2); // default for join is two lines! + if (curwin->w_cursor.lnum + cap->count0 - 1 > curbuf->b_ml.ml_line_count) { // can't join when on the last line @@ -6597,15 +6594,21 @@ static void nv_open(cmdarg_T *cap) } } +/// Handles K_PASTE_START, repeats pasted text. +static void nv_paste(cmdarg_T *cap) +{ + paste_repeat(cap->count1); +} + /// Handle an arbitrary event in normal mode static void nv_event(cmdarg_T *cap) { // Garbage collection should have been executed before blocking for events in - // the `os_inchar` in `state_enter`, but we also disable it here in case the - // `os_inchar` branch was not executed (!multiqueue_empty(loop.events), which + // the `input_get` in `state_enter`, but we also disable it here in case the + // `input_get` branch was not executed (!multiqueue_empty(loop.events), which // could have `may_garbage_collect` set to true in `normal_check`). // - // That is because here we may run code that calls `os_inchar` + // That is because here we may run code that calls `input_get` // later(`f_confirm` or `get_keystroke` for example), but in these cases it is // not safe to perform garbage collection because there could be unreferenced // lists or dicts being used. |