diff options
Diffstat (limited to 'src/nvim/normal.c')
-rw-r--r-- | src/nvim/normal.c | 664 |
1 files changed, 323 insertions, 341 deletions
diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 58a18ca5a8..1f789dc153 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - // // normal.c: Contains the main routine for processing characters in command // mode. Communicates closely with the code in ops.c to handle @@ -17,9 +14,8 @@ #include <string.h> #include <time.h> -#include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" @@ -38,14 +34,15 @@ #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/func_attr.h" #include "nvim/getchar.h" #include "nvim/gettext.h" #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/help.h" -#include "nvim/highlight_defs.h" +#include "nvim/highlight.h" #include "nvim/keycodes.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" #include "nvim/mapping.h" #include "nvim/mark.h" #include "nvim/mbyte.h" @@ -57,12 +54,12 @@ #include "nvim/normal.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_vars.h" #include "nvim/os/input.h" #include "nvim/os/time.h" #include "nvim/plines.h" #include "nvim/profile.h" #include "nvim/quickfix.h" -#include "nvim/screen.h" #include "nvim/search.h" #include "nvim/spell.h" #include "nvim/spellfile.h" @@ -74,10 +71,9 @@ #include "nvim/tag.h" #include "nvim/textformat.h" #include "nvim/textobject.h" -#include "nvim/types.h" #include "nvim/ui.h" #include "nvim/undo.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" #include "nvim/window.h" typedef struct normal_state { @@ -107,6 +103,10 @@ static int VIsual_mode_orig = NUL; // saved Visual mode # include "normal.c.generated.h" #endif +static const char e_changelist_is_empty[] = N_("E664: Changelist is empty"); +static const char e_cmdline_window_already_open[] + = N_("E1292: Command-line window is already open"); + static inline void normal_state_init(NormalState *s) { memset(s, 0, sizeof(NormalState)); @@ -118,7 +118,7 @@ static inline void normal_state_init(NormalState *s) // n_*(): functions called to handle Normal mode commands. // v_*(): functions called to handle Visual mode commands. -static char *e_noident = N_("E349: No identifier under cursor"); +static const char *e_noident = N_("E349: No identifier under cursor"); /// Function to be called for a Normal or Visual mode command. /// The argument is a cmdarg_T. @@ -359,11 +359,9 @@ static int nv_max_linear; /// through the index in nv_cmd_idx[]. static int nv_compare(const void *s1, const void *s2) { - int c1, c2; - // The commands are sorted on absolute value. - c1 = nv_cmds[*(const int16_t *)s1].cmd_char; - c2 = nv_cmds[*(const int16_t *)s2].cmd_char; + int c1 = nv_cmds[*(const int16_t *)s1].cmd_char; + int c2 = nv_cmds[*(const int16_t *)s2].cmd_char; if (c1 < 0) { c1 = -c1; } @@ -401,11 +399,6 @@ void init_normal_cmds(void) /// @return -1 for invalid command. static int find_command(int cmdchar) { - int i; - int idx; - int top, bot; - int c; - // A multi-byte character is never a command. if (cmdchar >= 0x100) { return -1; @@ -425,12 +418,12 @@ static int find_command(int cmdchar) } // Perform a binary search. - bot = nv_max_linear + 1; - top = NV_CMDS_SIZE - 1; - idx = -1; + int bot = nv_max_linear + 1; + int top = NV_CMDS_SIZE - 1; + int idx = -1; while (bot <= top) { - i = (top + bot) / 2; - c = nv_cmds[nv_cmd_idx[i]].cmd_char; + int i = (top + bot) / 2; + int c = nv_cmds[nv_cmd_idx[i]].cmd_char; if (c < 0) { c = -c; } @@ -465,7 +458,7 @@ static bool check_text_locked(oparg_T *oap) /// If text is locked, "curbuf->b_ro_locked" or "allbuf_lock" is set: /// Give an error message, possibly beep and return true. /// "oap" may be NULL. -static bool check_text_or_curbuf_locked(oparg_T *oap) +bool check_text_or_curbuf_locked(oparg_T *oap) { if (check_text_locked(oap)) { return true; @@ -481,6 +474,20 @@ static bool check_text_or_curbuf_locked(oparg_T *oap) return true; } +static oparg_T *current_oap = NULL; + +/// Check if an operator was started but not finished yet. +/// Includes typing a count or a register name. +bool op_pending(void) +{ + return !(current_oap != NULL + && !finish_op + && current_oap->prev_opcount == 0 + && current_oap->prev_count0 == 0 + && current_oap->op_type == OP_NOP + && current_oap->regname == NUL); +} + /// Normal state entry point. This is called on: /// /// - Startup, In this case the function never returns. @@ -489,15 +496,18 @@ static bool check_text_or_curbuf_locked(oparg_T *oap) /// for example. Returns when re-entering ex mode(because ex mode recursion is /// not allowed) /// -/// This used to be called main_loop on main.c +/// This used to be called main_loop() on main.c void normal_enter(bool cmdwin, bool noexmode) { NormalState state; normal_state_init(&state); + oparg_T *prev_oap = current_oap; + current_oap = &state.oa; state.cmdwin = cmdwin; state.noexmode = noexmode; state.toplevel = (!cmdwin || cmdwin_result == 0) && !noexmode; state_enter(&state.state); + current_oap = prev_oap; } static void normal_prepare(NormalState *s) @@ -671,16 +681,16 @@ static void normal_redraw_mode_message(NormalState *s) keep_msg = kmsg; kmsg = xstrdup(keep_msg); - msg_attr((const char *)kmsg, keep_msg_attr); + msg(kmsg, keep_msg_attr); xfree(kmsg); } setcursor(); ui_cursor_shape(); // show different cursor shape ui_flush(); if (msg_scroll || emsg_on_display) { - os_delay(1003L, true); // wait at least one second + os_delay(1003, true); // wait at least one second } - os_delay(3003L, false); // wait up to three seconds + os_delay(3003, false); // wait up to three seconds State = save_State; msg_scroll = false; @@ -693,7 +703,6 @@ static void normal_get_additional_char(NormalState *s) int *cp; bool repl = false; // get character for replace mode bool lit = false; // get extra character literally - bool langmap_active = false; // using :lmap mappings int lang; // getting a text character no_mapping++; @@ -729,6 +738,7 @@ static void normal_get_additional_char(NormalState *s) // Get a second or third character. if (cp != NULL) { + bool langmap_active = false; // using :lmap mappings if (repl) { State = MODE_REPLACE; // pretend Replace mode ui_cursor_shape(); // show different cursor shape @@ -771,10 +781,6 @@ static void normal_get_additional_char(NormalState *s) // adjust chars > 127, except after "tTfFr" commands LANGMAP_ADJUST(*cp, !lang); - // adjust Hebrew mapped char - if (p_hkmap && lang && KeyTyped) { - *cp = hkmap(*cp); - } } // When the next character is CTRL-\ a following CTRL-N means the @@ -789,13 +795,13 @@ static void normal_get_additional_char(NormalState *s) && s->ca.cmdchar == 'g') { s->ca.oap->op_type = get_op_type(*cp, NUL); } else if (*cp == Ctrl_BSL) { - long towait = (p_ttm >= 0 ? p_ttm : p_tm); + int towait = (p_ttm >= 0 ? (int)p_ttm : (int)p_tm); // There is a busy wait here when typing "f<C-\>" and then // something different from CTRL-N. Can't be avoided. - while ((s->c = vpeekc()) <= 0 && towait > 0L) { - do_sleep(towait > 50L ? 50L : towait); - towait -= 50L; + while ((s->c = vpeekc()) <= 0 && towait > 0) { + do_sleep(towait > 50 ? 50 : towait); + towait -= 50; } if (s->c > 0) { s->c = plain_vgetc(); @@ -810,25 +816,34 @@ static void normal_get_additional_char(NormalState *s) } } - // When getting a text character and the next character is a - // multi-byte character, it could be a composing character. - // However, don't wait for it to arrive. Also, do enable mapping, - // because if it's put back with vungetc() it's too late to apply - // mapping. - no_mapping--; - while (lang && (s->c = vpeekc()) > 0 - && (s->c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1)) { - s->c = plain_vgetc(); - if (!utf_iscomposing(s->c)) { - vungetc(s->c); // it wasn't, put it back - break; - } else if (s->ca.ncharC1 == 0) { - s->ca.ncharC1 = s->c; - } else { - s->ca.ncharC2 = s->c; + if (lang) { + // When getting a text character and the next character is a + // multi-byte character, it could be a composing character. + // However, don't wait for it to arrive. Also, do enable mapping, + // because if it's put back with vungetc() it's too late to apply + // mapping. + no_mapping--; + while ((s->c = vpeekc()) > 0 + && (s->c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1)) { + s->c = plain_vgetc(); + if (!utf_iscomposing(s->c)) { + vungetc(s->c); // it wasn't, put it back + break; + } else if (s->ca.ncharC1 == 0) { + s->ca.ncharC1 = s->c; + } else { + s->ca.ncharC2 = s->c; + } } + no_mapping++; + // Vim may be in a different mode when the user types the next key, + // but when replaying a recording the next key is already in the + // typeahead buffer, so record a <Nop> before that to prevent the + // vpeekc() above from applying wrong mappings when replaying. + no_u_sync++; + gotchars_nop(); + no_u_sync--; } - no_mapping++; } no_mapping--; allow_keys--; @@ -874,8 +889,8 @@ static bool normal_get_command_count(NormalState *s) if (s->c == K_DEL || s->c == K_KDEL) { s->ca.count0 /= 10; del_from_showcmd(4); // delete the digit and ~@% - } else if (s->ca.count0 > 99999999L) { - s->ca.count0 = 999999999L; + } else if (s->ca.count0 > 99999999) { + s->ca.count0 = 999999999; } else { s->ca.count0 = s->ca.count0 * 10 + (s->c - '0'); } @@ -963,13 +978,16 @@ normal_end: set_reg_var(get_default_register_name()); } - // Reset finish_op, in case it was set - s->c = finish_op; - finish_op = false; - may_trigger_modechanged(); + const bool prev_finish_op = finish_op; + if (s->oa.op_type == OP_NOP) { + // Reset finish_op, in case it was set + finish_op = false; + may_trigger_modechanged(); + } // Redraw the cursor with another shape, if we were in Operator-pending // mode or did a replace command. - if (s->c || s->ca.cmdchar == 'r') { + if (prev_finish_op || s->ca.cmdchar == 'r' + || (s->ca.cmdchar == 'g' && s->ca.nchar == 'r')) { ui_cursor_shape(); // may show different cursor shape } @@ -1010,7 +1028,7 @@ normal_end: restart_VIsual_select = 0; } if (restart_edit != 0 && !VIsual_active && s->old_mapped_len == 0) { - (void)edit(restart_edit, false, 1L); + (void)edit(restart_edit, false, 1); } } @@ -1088,8 +1106,8 @@ static int normal_execute(VimState *state, int key) // If you give a count before AND after the operator, they are // multiplied. if (s->ca.count0) { - if (s->ca.opcount >= 999999999L / s->ca.count0) { - s->ca.count0 = 999999999L; + if (s->ca.opcount >= 999999999 / s->ca.count0) { + s->ca.count0 = 999999999; } else { s->ca.count0 *= s->ca.opcount; } @@ -1168,7 +1186,7 @@ static int normal_execute(VimState *state, int key) State = MODE_NORMAL; - if (s->ca.nchar == ESC) { + if (s->ca.nchar == ESC || s->ca.extra_char == ESC) { clearop(&s->oa); s->command_finished = true; goto finish; @@ -1179,7 +1197,7 @@ static int normal_execute(VimState *state, int key) msg_col = 0; } - s->old_pos = curwin->w_cursor; // remember where cursor was + s->old_pos = curwin->w_cursor; // remember where the cursor was // When 'keymodel' contains "startsel" some keys start Select/Visual // mode. @@ -1260,9 +1278,11 @@ static void normal_check_cursor_moved(NormalState *s) { // Trigger CursorMoved if the cursor moved. if (!finish_op && has_event(EVENT_CURSORMOVED) - && !equalpos(curwin->w_last_cursormoved, curwin->w_cursor)) { + && (last_cursormoved_win != curwin + || !equalpos(last_cursormoved, curwin->w_cursor))) { apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf); - curwin->w_last_cursormoved = curwin->w_cursor; + last_cursormoved_win = curwin; + last_cursormoved = curwin->w_cursor; } } @@ -1286,6 +1306,13 @@ static void normal_check_buffer_modified(NormalState *s) } } +/// If nothing is pending and we are going to wait for the user to +/// type a character, trigger SafeState. +static void normal_check_safe_state(NormalState *s) +{ + may_trigger_safestate(!op_pending() && restart_edit == 0); +} + static void normal_check_folds(NormalState *s) { // Include a closed fold completely in the Visual area. @@ -1311,16 +1338,20 @@ static void normal_redraw(NormalState *s) update_topline(curwin); validate_cursor(); + show_cursor_info_later(false); + if (VIsual_active) { redraw_curbuf_later(UPD_INVERTED); // update inverted part - update_screen(); - } else if (must_redraw) { - update_screen(); - } else if (redraw_cmdline || clear_cmdline || redraw_mode) { - showmode(); } - redraw_statuslines(); + if (must_redraw) { + update_screen(); + } else { + redraw_statuslines(); + if (redraw_cmdline || clear_cmdline || redraw_mode) { + showmode(); + } + } if (need_maketitle) { maketitle(); @@ -1338,7 +1369,7 @@ static void normal_redraw(NormalState *s) // check for duplicates. Never put this message in // history. msg_hist_off = true; - msg_attr((const char *)p, keep_msg_attr); + msg(p, keep_msg_attr); msg_hist_off = false; xfree(p); } @@ -1353,7 +1384,6 @@ static void normal_redraw(NormalState *s) did_emsg = false; msg_didany = false; // reset lines_left in msg_start() may_clear_sb_text(); // clear scroll-back text on next msg - show_cursor_info(false); setcursor(); } @@ -1375,11 +1405,14 @@ static int normal_check(VimState *state) } quit_more = false; + state_no_longer_safe(NULL); + // If skip redraw is set (for ":" in wait_return()), don't redraw now. // If there is nothing in the stuff_buffer or do_redraw is true, // update cursor and redraw. if (skip_redraw || exmode_active) { skip_redraw = false; + setcursor(); } else if (do_redraw || stuff_empty()) { // Ensure curwin->w_topline and curwin->w_leftcol are up to date // before triggering a WinScrolled autocommand. @@ -1390,6 +1423,7 @@ static int normal_check(VimState *state) normal_check_text_changed(s); normal_check_window_scrolled(s); normal_check_buffer_modified(s); + normal_check_safe_state(s); // Updating diffs from changed() does not always work properly, // esp. updating folds. Do an update just before redrawing if @@ -1402,7 +1436,7 @@ static int normal_check(VimState *state) // Scroll-binding for diff mode may have been postponed until // here. Avoids doing it for every change. if (diff_need_scrollbind) { - check_scrollbind((linenr_T)0, 0L); + check_scrollbind(0, 0); diff_need_scrollbind = false; } @@ -1455,7 +1489,7 @@ static int normal_check(VimState *state) /// Set v:prevcount only when "set_prevcount" is true. static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount) { - long count = cap->count0; + int64_t count = cap->count0; // multiply with cap->opcount the same way as above if (cap->opcount != 0) { @@ -1597,7 +1631,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text // if i == 0: try to find an identifier // if i == 1: try to find any non-white text - char *ptr = ml_get_buf(wp->w_buffer, lnum, false); + char *ptr = ml_get_buf(wp->w_buffer, lnum); for (i = (find_type & FIND_IDENT) ? 0 : 1; i < 2; i++) { // 1. skip to start of identifier/text col = startcol; @@ -1671,7 +1705,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text col = 0; // Search for point of changing multibyte character class. this_class = mb_get_class(ptr); - while (ptr[col] != NUL // -V781 + while (ptr[col] != NUL && ((i == 0 ? mb_get_class(ptr + col) == this_class : mb_get_class(ptr + col) != 0) @@ -1694,13 +1728,13 @@ static void prep_redo_cmd(cmdarg_T *cap) /// Prepare for redo of any command. /// Note that only the last argument can be a multi-byte char. -void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5) +void prep_redo(int regname, int num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5) { - prep_redo_num2(regname, num, cmd1, cmd2, 0L, cmd3, cmd4, cmd5); + prep_redo_num2(regname, num, cmd1, cmd2, 0, cmd3, cmd4, cmd5); } /// Prepare for redo of any command with extra count after "cmd2". -void prep_redo_num2(int regname, long num1, int cmd1, int cmd2, long num2, int cmd3, int cmd4, +void prep_redo_num2(int regname, int num1, int cmd1, int cmd2, int num2, int cmd3, int cmd4, int cmd5) { ResetRedobuff(); @@ -1731,9 +1765,9 @@ void prep_redo_num2(int regname, long num1, int cmd1, int cmd2, long num2, int c } } -/// check for operator active and clear it +/// Check for operator active and clear it. /// -/// @return true if operator was active +/// Beep and return true if an operator was active. static bool checkclearop(oparg_T *oap) { if (oap->op_type == OP_NOP) { @@ -1745,7 +1779,7 @@ static bool checkclearop(oparg_T *oap) /// Check for operator or Visual active. Clear active operator. /// -/// @return true if operator or Visual was active. +/// Beep and return true if an operator or Visual was active. static bool checkclearopq(oparg_T *oap) { if (oap->op_type == OP_NOP && !VIsual_active) { @@ -1815,7 +1849,7 @@ void clear_showcmd(void) if (VIsual_active && !char_avail()) { int cursor_bot = lt(VIsual, curwin->w_cursor); - long lines; + int lines; colnr_T leftcol, rightcol; linenr_T top, bot; @@ -1837,8 +1871,8 @@ void clear_showcmd(void) char *const saved_w_sbr = curwin->w_p_sbr; // Make 'sbr' empty for a moment to get the correct size. - p_sbr = empty_option; - curwin->w_p_sbr = empty_option; + p_sbr = empty_string_option; + curwin->w_p_sbr = empty_string_option; getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol); p_sbr = saved_sbr; curwin->w_p_sbr = saved_w_sbr; @@ -1848,7 +1882,6 @@ void clear_showcmd(void) snprintf(showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64, (int64_t)lines); } else { char *s, *e; - int l; int bytes = 0; int chars = 0; @@ -1860,7 +1893,7 @@ void clear_showcmd(void) e = ml_get_pos(&VIsual); } while ((*p_sel != 'e') ? s <= e : s < e) { - l = utfc_ptr2len(s); + int l = utfc_ptr2len(s); if (l == 0) { bytes++; chars++; @@ -1957,13 +1990,11 @@ void add_to_showcmd_c(int c) /// Delete 'len' characters from the end of the shown command. static void del_from_showcmd(int len) { - int old_len; - if (!p_sc) { return; } - old_len = (int)strlen(showcmd_buf); + int old_len = (int)strlen(showcmd_buf); if (len > old_len) { len = old_len; } @@ -2000,13 +2031,21 @@ static void display_showcmd(void) showcmd_is_clear = (len == 0); if (*p_sloc == 's') { - win_redr_status(curwin); - setcursor(); // put cursor back where it belongs + if (showcmd_is_clear) { + curwin->w_redr_status = true; + } else { + win_redr_status(curwin); + setcursor(); // put cursor back where it belongs + } return; } if (*p_sloc == 't') { - draw_tabline(); - setcursor(); // put cursor back where it belongs + if (showcmd_is_clear) { + redraw_tabline = true; + } else { + draw_tabline(); + setcursor(); // put cursor back where it belongs + } return; } // 'showcmdloc' is "last" or empty @@ -2020,7 +2059,7 @@ static void display_showcmd(void) if (len > 0) { // placeholder for future highlight support ADD_C(chunk, INTEGER_OBJ(0)); - ADD_C(chunk, STRING_OBJ(cstr_as_string(showcmd_buf))); + ADD_C(chunk, CSTR_AS_OBJ(showcmd_buf)); ADD_C(content, ARRAY_OBJ(chunk)); } ui_call_msg_showcmd(content); @@ -2029,18 +2068,16 @@ static void display_showcmd(void) msg_grid_validate(); int showcmd_row = Rows - 1; - grid_puts_line_start(&msg_grid_adj, showcmd_row); + grid_line_start(&msg_grid_adj, showcmd_row); if (!showcmd_is_clear) { - grid_puts(&msg_grid_adj, showcmd_buf, showcmd_row, sc_col, - HL_ATTR(HLF_MSG)); + grid_line_puts(sc_col, showcmd_buf, -1, HL_ATTR(HLF_MSG)); } // clear the rest of an old message by outputting up to SHOWCMD_COLS spaces - grid_puts(&msg_grid_adj, (char *)" " + len, showcmd_row, - sc_col + len, HL_ATTR(HLF_MSG)); + grid_line_puts(sc_col + len, (char *)" " + len, -1, HL_ATTR(HLF_MSG)); - grid_puts_line_flush(false); + grid_line_flush(); } /// When "check" is false, prepare for commands that scroll the window. @@ -2069,8 +2106,7 @@ void do_check_scrollbind(bool check) && (curwin->w_topline != old_topline || curwin->w_topfill != old_topfill || curwin->w_leftcol != old_leftcol)) { - check_scrollbind(curwin->w_topline - old_topline, - (long)(curwin->w_leftcol - old_leftcol)); + check_scrollbind(curwin->w_topline - old_topline, 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 @@ -2081,7 +2117,7 @@ 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, 0L); + check_scrollbind(curwin->w_topline - (linenr_T)curwin->w_scbind_pos, 0); } curwin->w_scbind_pos = curwin->w_topline; } @@ -2096,22 +2132,20 @@ 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, long leftcol_diff) +void check_scrollbind(linenr_T topline_diff, int leftcol_diff) { - bool want_ver; - bool want_hor; 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; - long topline; - long y; + linenr_T topline; + linenr_T y; // check 'scrollopt' string for vertical and horizontal scroll options - want_ver = (vim_strchr(p_sbo, 'v') && topline_diff != 0); + bool want_ver = (vim_strchr(p_sbo, 'v') && topline_diff != 0); want_ver |= old_curwin->w_p_diff; - want_hor = (vim_strchr(p_sbo, 'h') && (leftcol_diff || topline_diff != 0)); + bool want_hor = (vim_strchr(p_sbo, 'h') && (leftcol_diff || topline_diff != 0)); // loop through the scrollbound windows and scroll accordingly VIsual_select = VIsual_active = 0; @@ -2129,7 +2163,7 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff) diff_set_topline(old_curwin, curwin); } else { curwin->w_scbind_pos += topline_diff; - topline = curwin->w_scbind_pos; + topline = (linenr_T)curwin->w_scbind_pos; if (topline > curbuf->b_ml.ml_line_count) { topline = curbuf->b_ml.ml_line_count; } @@ -2151,9 +2185,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); } } @@ -2175,7 +2208,8 @@ static void nv_ignore(cmdarg_T *cap) /// Command character that doesn't do anything, but unlike nv_ignore() does /// start edit(). Used for "startinsert" executed while starting up. static void nv_nop(cmdarg_T *cap) -{} +{ +} /// Command character doesn't exist. static void nv_error(cmdarg_T *cap) @@ -2199,7 +2233,7 @@ static void nv_addsub(cmdarg_T *cap) } else if (!VIsual_active && cap->oap->op_type == OP_NOP) { prep_redo_cmd(cap); cap->oap->op_type = cap->cmdchar == Ctrl_A ? OP_NR_ADD : OP_NR_SUB; - op_addsub(cap->oap, (linenr_T)cap->count1, cap->arg); + op_addsub(cap->oap, cap->count1, cap->arg); cap->oap->op_type = OP_NOP; } else if (VIsual_active) { nv_operator(cap); @@ -2218,9 +2252,9 @@ static void nv_page(cmdarg_T *cap) if (mod_mask & MOD_MASK_CTRL) { // <C-PageUp>: tab page back; <C-PageDown>: tab page forward if (cap->arg == BACKWARD) { - goto_tabpage(-(int)cap->count1); + goto_tabpage(-cap->count1); } else { - goto_tabpage((int)cap->count0); + goto_tabpage(cap->count0); } } else { (void)onepage(cap->arg, cap->count1); @@ -2292,34 +2326,30 @@ static bool is_ident(const char *line, int offset) /// @return fail when not found. bool find_decl(char *ptr, size_t len, bool locally, bool thisblock, int flags_arg) { - char *pat; - pos_T old_pos; pos_T par_pos; pos_T found_pos; bool t; - bool save_p_ws; - bool save_p_scs; bool retval = true; bool incll; int searchflags = flags_arg; - pat = xmalloc(len + 7); + size_t patlen = len + 7; + char *pat = xmalloc(patlen); // Put "\V" before the pattern to avoid that the special meaning of "." // and "~" causes trouble. - assert(len <= INT_MAX); - sprintf(pat, vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s", // NOLINT(runtime/printf) - (int)len, ptr); - old_pos = curwin->w_cursor; - save_p_ws = p_ws; - save_p_scs = p_scs; + assert(patlen <= INT_MAX); + snprintf(pat, patlen, vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s", (int)len, ptr); + pos_T old_pos = curwin->w_cursor; + bool save_p_ws = p_ws; + bool save_p_scs = p_scs; p_ws = false; // don't wrap around end of file now p_scs = false; // don't switch ignorecase off now // With "gD" go to line 1. // With "gd" Search back for the start of the current function, then go // back until a blank line. If this fails go to line 1. - if (!locally || !findpar(&incll, BACKWARD, 1L, '{', false)) { + if (!locally || !findpar(&incll, BACKWARD, 1, '{', false)) { setpcmark(); // Set in findpar() otherwise curwin->w_cursor.lnum = 1; par_pos = curwin->w_cursor; @@ -2334,9 +2364,9 @@ bool find_decl(char *ptr, size_t len, bool locally, bool thisblock, int flags_ar // Search forward for the identifier, ignore comment lines. clearpos(&found_pos); - for (;;) { + while (true) { t = searchit(curwin, curbuf, &curwin->w_cursor, NULL, FORWARD, - pat, 1L, searchflags, RE_LAST, NULL); + pat, 1, searchflags, RE_LAST, NULL); if (curwin->w_cursor.lnum >= old_pos.lnum) { t = false; // match after start is failure too } @@ -2422,12 +2452,11 @@ bool find_decl(char *ptr, size_t len, bool locally, bool thisblock, int flags_ar /// 'dist' must be positive. /// /// @return true if able to move cursor, false otherwise. -static bool nv_screengo(oparg_T *oap, int dir, long dist) +static bool nv_screengo(oparg_T *oap, int dir, int dist) { - int linelen = linetabsize(get_cursor_line_ptr()); + int linelen = linetabsize(curwin, curwin->w_cursor.lnum); bool retval = true; bool atend = false; - int n; int col_off1; // margin offset for first screen line int col_off2; // margin offset for wrapped screen line int width1; // text width for first screen line @@ -2446,6 +2475,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) } if (curwin->w_width_inner != 0) { + int n; // Instead of sticking at the last character of the buffer line we // try to stick in the last column of the screen. if (curwin->w_curswant == MAXCOL) { @@ -2482,20 +2512,13 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) curwin->w_curswant -= width2; } else { // to previous line - - // Move to the start of a closed fold. Don't do that when - // 'foldopen' contains "all": it will open in a moment. - if (!(fdo_flags & FDO_ALL)) { - (void)hasFolding(curwin->w_cursor.lnum, - &curwin->w_cursor.lnum, NULL); - } - if (curwin->w_cursor.lnum == 1) { + if (curwin->w_cursor.lnum <= 1) { retval = false; break; } - curwin->w_cursor.lnum--; + cursor_up_inner(curwin, 1); - linelen = linetabsize(get_cursor_line_ptr()); + linelen = linetabsize(curwin, curwin->w_cursor.lnum); if (linelen > width1) { int w = (((linelen - width1 - 1) / width2) + 1) * width2; assert(curwin->w_curswant <= INT_MAX - w); @@ -2514,16 +2537,13 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) curwin->w_curswant += width2; } else { // to next line - - // Move to the end of a closed fold. - (void)hasFolding(curwin->w_cursor.lnum, NULL, - &curwin->w_cursor.lnum); - if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) { + if (curwin->w_cursor.lnum >= curwin->w_buffer->b_ml.ml_line_count) { retval = false; break; } - curwin->w_cursor.lnum++; + 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 @@ -2531,7 +2551,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(curwin, curwin->w_cursor.lnum); } } } @@ -2572,63 +2592,14 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) if (atend) { curwin->w_curswant = MAXCOL; // stick in the last column } - return retval; -} - -/// Mouse scroll wheel: Default action is to scroll three lines, or one page -/// when Shift or Ctrl is used. -/// K_MOUSEUP (cap->arg == 1) or K_MOUSEDOWN (cap->arg == 0) or -/// K_MOUSELEFT (cap->arg == -1) or K_MOUSERIGHT (cap->arg == -2) -static void nv_mousescroll(cmdarg_T *cap) -{ - win_T *old_curwin = curwin; - - if (mouse_row >= 0 && mouse_col >= 0) { - int grid, row, col; - - grid = mouse_grid; - row = mouse_row; - col = mouse_col; - - // find the window at the pointer coordinates - win_T *wp = mouse_find_win(&grid, &row, &col); - if (wp == NULL) { - return; - } - curwin = wp; - curbuf = curwin->w_buffer; - } + adjust_skipcol(); - if (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN) { - if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) { - (void)onepage(cap->arg ? FORWARD : BACKWARD, 1L); - } else if (p_mousescroll_vert > 0) { - cap->count1 = p_mousescroll_vert; - cap->count0 = p_mousescroll_vert; - nv_scroll_line(cap); - } - } else { - mouse_scroll_horiz(cap->arg); - } - if (curwin != old_curwin && curwin->w_p_cul) { - redraw_for_cursorline(curwin); - } - - curwin->w_redr_status = true; - - curwin = old_curwin; - curbuf = curwin->w_buffer; -} - -/// Mouse clicks and drags. -static void nv_mouse(cmdarg_T *cap) -{ - (void)do_mouse(cap->oap, cap->cmdchar, BACKWARD, cap->count1, 0); + return retval; } /// Handle CTRL-E and CTRL-Y commands: scroll a line up or down. /// cap->arg must be true for CTRL-E. -static void nv_scroll_line(cmdarg_T *cap) +void nv_scroll_line(cmdarg_T *cap) { if (!checkclearop(cap->oap)) { scroll_redraw(cap->arg, cap->count1); @@ -2636,17 +2607,18 @@ static void nv_scroll_line(cmdarg_T *cap) } /// Scroll "count" lines up or down, and redraw. -void scroll_redraw(int up, long count) +void scroll_redraw(int up, linenr_T 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; - bool moved = up ? - scrollup(count, true) : - scrolldown(count, true); + bool moved = up + ? 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(); @@ -2657,16 +2629,17 @@ 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 - || cursor_down(1L, false) == false) { + || cursor_down(1, false) == false) { break; } } else { if (curwin->w_cursor.lnum < prev_lnum - || prev_topline == 1L - || cursor_up(1L, false) == false) { + || prev_topline == 1 + || cursor_up(1, false) == false) { break; } } @@ -2696,9 +2669,9 @@ static bool nv_z_get_count(cmdarg_T *cap, int *nchar_arg) if (checkclearop(cap->oap)) { return false; } - long n = nchar - '0'; + int n = nchar - '0'; - for (;;) { + while (true) { no_mapping++; allow_keys++; // no mapping for nchar, but allow key codes nchar = plain_vgetc(); @@ -2710,9 +2683,13 @@ static bool nv_z_get_count(cmdarg_T *cap, int *nchar_arg) if (nchar == K_DEL || nchar == K_KDEL) { n /= 10; } else if (ascii_isdigit(nchar)) { + if (n > INT_MAX / 10) { + clearopbeep(cap->oap); + break; + } n = n * 10 + (nchar - '0'); } else if (nchar == CAR) { - win_setheight((int)n); + win_setheight(n); break; } else if (nchar == 'l' || nchar == 'h' @@ -2784,7 +2761,7 @@ static int nv_zg_zw(cmdarg_T *cap, int nchar) assert(len <= INT_MAX); spell_add_word(ptr, (int)len, nchar == 'w' || nchar == 'W' ? SPELL_ADD_BAD : SPELL_ADD_GOOD, - (nchar == 'G' || nchar == 'W') ? 0 : (int)cap->count1, + (nchar == 'G' || nchar == 'W') ? 0 : cap->count1, undo); return OK; @@ -2793,13 +2770,12 @@ static int nv_zg_zw(cmdarg_T *cap, int nchar) /// Commands that start with "z". static void nv_zet(cmdarg_T *cap) { - int n; colnr_T col; int nchar = cap->nchar; - long old_fdl = curwin->w_p_fdl; + int old_fdl = (int)curwin->w_p_fdl; int old_fen = curwin->w_p_fen; - int siso = (int)get_sidescrolloff_value(curwin); + int siso = get_sidescrolloff_value(curwin); if (ascii_isdigit(nchar) && !nv_z_get_count(cap, &nchar)) { return; @@ -2824,7 +2800,7 @@ static void nv_zet(cmdarg_T *cap) if (cap->count0 > curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; } else { - curwin->w_cursor.lnum = (linenr_T)cap->count0; + curwin->w_cursor.lnum = cap->count0; } check_cursor_col(); } @@ -2860,7 +2836,7 @@ static void nv_zet(cmdarg_T *cap) FALLTHROUGH; case 'z': - scroll_cursor_halfway(true); + scroll_cursor_halfway(true, false); redraw_later(curwin, UPD_VALID); set_fraction(curwin); break; @@ -2897,27 +2873,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; @@ -2949,7 +2919,7 @@ static void nv_zet(cmdarg_T *cap) } else { getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col); } - n = curwin->w_width_inner - curwin_col_off(); + int n = curwin->w_width_inner - curwin_col_off(); if (col + siso < n) { col = 0; } else { @@ -3011,7 +2981,7 @@ static void nv_zet(cmdarg_T *cap) clearFolding(curwin); changed_window_setting(); } else if (foldmethodIsMarker(curwin)) { - deleteFold(curwin, (linenr_T)1, curbuf->b_ml.ml_line_count, true, false); + deleteFold(curwin, 1, curbuf->b_ml.ml_line_count, true, false); } else { emsg(_("E352: Cannot erase folds with current 'foldmethod'")); } @@ -3165,7 +3135,7 @@ static void nv_zet(cmdarg_T *cap) case '=': // "z=": suggestions for a badly spelled word if (!checkclearop(cap->oap)) { - spell_suggest((int)cap->count0); + spell_suggest(cap->count0); } break; @@ -3230,7 +3200,7 @@ static void nv_colon(cmdarg_T *cap) stuffcharReadbuff('.'); if (cap->count0 > 1) { stuffReadbuff(",.+"); - stuffnumReadbuff(cap->count0 - 1L); + stuffnumReadbuff(cap->count0 - 1); } } @@ -3240,7 +3210,7 @@ static void nv_colon(cmdarg_T *cap) } if (is_lua) { - cmd_result = map_execute_lua(); + cmd_result = map_execute_lua(true); } else { // get a command line and execute it cmd_result = do_cmdline(NULL, is_cmdkey ? getcmdkeycmd : getexline, NULL, @@ -3269,7 +3239,7 @@ static void nv_ctrlg(cmdarg_T *cap) showmode(); } else if (!checkclearop(cap->oap)) { // print full name if count given or :cd used - fileinfo((int)cap->count0, false, true); + fileinfo(cap->count0, false, true); } } @@ -3319,7 +3289,7 @@ static void nv_ctrlo(cmdarg_T *cap) static void nv_hat(cmdarg_T *cap) { if (!checkclearopq(cap->oap)) { - (void)buflist_getfile((int)cap->count0, (linenr_T)0, + (void)buflist_getfile(cap->count0, 0, GETF_SETMARK|GETF_ALT, false); } } @@ -3438,7 +3408,6 @@ static void nv_ident(cmdarg_T *cap) int cmdchar; bool g_cmd; // "g" command bool tag_cmd = false; - char *aux_ptr; if (cap->cmdchar == 'g') { // "g*", "g#", "g]" and "gCTRL-]" cmdchar = cap->nchar; @@ -3531,7 +3500,7 @@ static void nv_ident(cmdarg_T *cap) ptr = xstrnsave(ptr, n); if (kp_ex) { // Escape the argument properly for an Ex command - p = vim_strsave_fnameescape((const char *)ptr, VSE_NONE); + p = vim_strsave_fnameescape(ptr, VSE_NONE); } else { // Escape the argument properly for a shell command p = vim_strsave_shellescape(ptr, true, true); @@ -3542,6 +3511,7 @@ static void nv_ident(cmdarg_T *cap) STRCAT(buf, p); xfree(p); } else { + char *aux_ptr; if (cmdchar == '*') { aux_ptr = (magic_isset() ? "/.*~[^$\\" : "/^$\\"); } else if (cmdchar == '#') { @@ -3646,17 +3616,15 @@ bool get_visual_text(cmdarg_T *cap, char **pp, size_t *lenp) static void nv_tagpop(cmdarg_T *cap) { if (!checkclearopq(cap->oap)) { - do_tag("", DT_POP, (int)cap->count1, false, true); + do_tag("", DT_POP, cap->count1, false, true); } } /// Handle scrolling command 'H', 'L' and 'M'. static void nv_scroll(cmdarg_T *cap) { - int used = 0; - long n; + int n; linenr_T lnum; - int half; cap->oap->motion_type = kMTLineWise; setpcmark(); @@ -3678,28 +3646,29 @@ static void nv_scroll(cmdarg_T *cap) } } } else { - curwin->w_cursor.lnum -= (linenr_T)cap->count1 - 1; + curwin->w_cursor.lnum -= cap->count1 - 1; } } } else { if (cap->cmdchar == 'M') { + int used = 0; // Don't count filler lines above the window. used -= win_get_fill(curwin, curwin->w_topline) - curwin->w_topfill; validate_botline(curwin); // make sure w_empty_rows is valid - half = (curwin->w_height_inner - curwin->w_empty_rows + 1) / 2; + int half = (curwin->w_height_inner - curwin->w_empty_rows + 1) / 2; for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; n++) { // Count half the number of filler lines to be "below this // line" and half to be "above the next line". - if (n > 0 && used + win_get_fill(curwin, curwin->w_topline + (linenr_T)n) / 2 >= half) { + if (n > 0 && used + win_get_fill(curwin, curwin->w_topline + n) / 2 >= half) { n--; break; } - used += plines_win(curwin, curwin->w_topline + (linenr_T)n, true); + used += plines_win(curwin, curwin->w_topline + n, true); if (used >= half) { break; } - if (hasFolding(curwin->w_topline + (linenr_T)n, NULL, &lnum)) { + if (hasFolding(curwin->w_topline + n, NULL, &lnum)) { n = lnum - curwin->w_topline; } } @@ -3718,7 +3687,7 @@ static void nv_scroll(cmdarg_T *cap) n = lnum - curwin->w_topline; } } - curwin->w_cursor.lnum = curwin->w_topline + (linenr_T)n; + 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; } @@ -3734,7 +3703,7 @@ static void nv_scroll(cmdarg_T *cap) /// Cursor right commands. static void nv_right(cmdarg_T *cap) { - long n; + int n; if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) { // <C-Right> and <S-Right> move a word or WORD right @@ -3812,7 +3781,7 @@ static void nv_right(cmdarg_T *cap) /// @return true when operator end should not be adjusted. static void nv_left(cmdarg_T *cap) { - long n; + int n; if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) { // <C-Left> and <S-Left> move a word or WORD left @@ -3921,14 +3890,13 @@ static void nv_down(cmdarg_T *cap) /// Grab the file name under the cursor and edit it. static void nv_gotofile(cmdarg_T *cap) { - char *ptr; linenr_T lnum = -1; if (check_text_or_curbuf_locked(cap->oap)) { return; } - ptr = grab_file_name(cap->count1, &lnum); + char *ptr = grab_file_name(cap->count1, &lnum); if (ptr != NULL) { // do autowrite if necessary @@ -3940,7 +3908,7 @@ static void nv_gotofile(cmdarg_T *cap) buf_hide(curbuf) ? ECMD_HIDE : 0, curwin) == OK && cap->nchar == 'F' && lnum >= 0) { curwin->w_cursor.lnum = lnum; - check_cursor_lnum(); + check_cursor_lnum(curwin); beginline(BL_SOL | BL_FIX); } xfree(ptr); @@ -4113,9 +4081,8 @@ static void nv_bracket_block(cmdarg_T *cap, const pos_T *old_pos) pos_T new_pos = { 0, 0, 0 }; pos_T *pos = NULL; // init for GCC pos_T prev_pos; - long n; + int n; int findc; - int c; if (cap->nchar == '*') { cap->nchar = '/'; @@ -4155,6 +4122,7 @@ static void nv_bracket_block(cmdarg_T *cap, const pos_T *old_pos) // Try finding the '{' or '}' we want to be at. // Also repeat for the given count. if (cap->nchar == 'm' || cap->nchar == 'M') { + int c; // norm is true for "]M" and "[m" int norm = ((findc == '{') == (cap->nchar == 'm')); @@ -4170,7 +4138,7 @@ static void nv_bracket_block(cmdarg_T *cap, const pos_T *old_pos) pos = NULL; } while (n > 0) { - for (;;) { + while (true) { if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0) { // if not found anything, that's an error if (pos == NULL) { @@ -4225,13 +4193,12 @@ static void nv_bracket_block(cmdarg_T *cap, const pos_T *old_pos) /// cap->arg is BACKWARD for "[" and FORWARD for "]". static void nv_brackets(cmdarg_T *cap) { - pos_T old_pos; // cursor position before command int flag; - long n; + int n; cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; - old_pos = curwin->w_cursor; + pos_T old_pos = curwin->w_cursor; // cursor position before command curwin->w_cursor.coladd = 0; // TODO(Unknown): don't do this for an error. // "[f" or "]f" : Edit file under the cursor (same as "gf") @@ -4252,19 +4219,19 @@ static void nv_brackets(cmdarg_T *cap) clearop(cap->oap); } else { // Make a copy, if the line was changed it will be freed. - ptr = xstrnsave(ptr, len); + ptr = xmemdupz(ptr, len); find_pattern_in_path(ptr, 0, len, true, cap->count0 == 0 ? !isupper(cap->nchar) : false, (((cap->nchar & 0xf) == ('d' & 0xf)) ? FIND_DEFINE : FIND_ANY), cap->count1, - (isupper(cap->nchar) ? ACTION_SHOW_ALL : - islower(cap->nchar) ? ACTION_SHOW : - ACTION_GOTO), + (isupper(cap->nchar) ? ACTION_SHOW_ALL + : islower(cap->nchar) ? ACTION_SHOW + : ACTION_GOTO), (cap->cmdchar == ']' ? curwin->w_cursor.lnum + 1 - : (linenr_T)1), + : 1), MAXLNUM); xfree(ptr); curwin->w_set_curswant = true; @@ -4318,7 +4285,7 @@ static void nv_brackets(cmdarg_T *cap) fm = prev_fm; } MarkMove flags = kMarkContext; - flags |= cap->nchar == '\'' ? kMarkBeginLine: 0; + flags |= cap->nchar == '\'' ? kMarkBeginLine : 0; nv_mark_move_to(cap, flags, fm); } else if (cap->nchar >= K_RIGHTRELEASE && cap->nchar <= K_LEFTMOUSE) { // [ or ] followed by a middle mouse click: put selected text with @@ -4361,7 +4328,6 @@ static void nv_brackets(cmdarg_T *cap) /// Handle Normal mode "%" command. static void nv_percent(cmdarg_T *cap) { - pos_T *pos; linenr_T lnum = curwin->w_cursor.lnum; cap->oap->inclusive = true; @@ -4377,10 +4343,10 @@ static void nv_percent(cmdarg_T *cap) // to avoid overflows. if (curbuf->b_ml.ml_line_count >= 21474836) { curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count + 99) - / 100 * (linenr_T)cap->count0; + / 100 * cap->count0; } else { curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count * - (linenr_T)cap->count0 + 99) / 100; + cap->count0 + 99) / 100; } if (curwin->w_cursor.lnum < 1) { curwin->w_cursor.lnum = 1; @@ -4391,6 +4357,7 @@ static void nv_percent(cmdarg_T *cap) beginline(BL_SOL | BL_FIX); } } else { // "%" : go to matching paren + pos_T *pos; cap->oap->motion_type = kMTCharWise; cap->oap->use_reg_one = true; if ((pos = findmatch(cap->oap, NUL)) == NULL) { @@ -4490,14 +4457,13 @@ static void nv_kundo(cmdarg_T *cap) clearopbeep(cap->oap); return; } - u_undo((int)cap->count1); + u_undo(cap->count1); curwin->w_set_curswant = true; } /// Handle the "r" command. static void nv_replace(cmdarg_T *cap) { - char *ptr; int had_ctrl_v; if (checkclearop(cap->oap)) { @@ -4509,7 +4475,7 @@ static void nv_replace(cmdarg_T *cap) } // get another character - if (cap->nchar == Ctrl_V) { + if (cap->nchar == Ctrl_V || cap->nchar == Ctrl_Q) { had_ctrl_v = Ctrl_V; cap->nchar = get_literal(false); // Don't redo a multibyte character with CTRL-V. @@ -4529,7 +4495,7 @@ static void nv_replace(cmdarg_T *cap) // Visual mode "r" if (VIsual_active) { if (got_int) { - reset_VIsual(); + got_int = false; } if (had_ctrl_v) { // Use a special (negative) number to make a difference between a @@ -4560,7 +4526,7 @@ static void nv_replace(cmdarg_T *cap) } // Abort if not enough characters to replace. - ptr = get_cursor_pos_ptr(); + char *ptr = get_cursor_pos_ptr(); if (strlen(ptr) < (unsigned)cap->count1 || (mb_charlen(ptr) < cap->count1)) { clearopbeep(cap->oap); @@ -4614,7 +4580,7 @@ static void nv_replace(cmdarg_T *cap) // This is slow, but it handles replacing a single-byte with a // multi-byte and the other way around. Also handles adding // composing characters for utf-8. - for (long n = cap->count1; n > 0; n--) { + for (int n = cap->count1; n > 0; n--) { State = MODE_REPLACE; if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y) { int c = ins_copychar(curwin->w_cursor.lnum @@ -4652,11 +4618,10 @@ static void nv_replace(cmdarg_T *cap) /// 'O': same, but in block mode exchange left and right corners. static void v_swap_corners(int cmdchar) { - pos_T old_cursor; colnr_T left, right; if (cmdchar == 'O' && VIsual_mode == Ctrl_V) { - old_cursor = curwin->w_cursor; + pos_T old_cursor = curwin->w_cursor; getvcols(curwin, &old_cursor, &VIsual, &left, &right); curwin->w_cursor.lnum = VIsual.lnum; coladvance(left); @@ -4686,7 +4651,7 @@ static void v_swap_corners(int cmdchar) curwin->w_curswant = left; } } else { - old_cursor = curwin->w_cursor; + pos_T old_cursor = curwin->w_cursor; curwin->w_cursor = VIsual; VIsual = old_cursor; curwin->w_set_curswant = true; @@ -4736,9 +4701,15 @@ static void nv_vreplace(cmdarg_T *cap) if (!MODIFIABLE(curbuf)) { emsg(_(e_modifiable)); } else { - if (cap->extra_char == Ctrl_V) { // get another character + if (cap->extra_char == Ctrl_V || cap->extra_char == Ctrl_Q) { + // get another character cap->extra_char = get_literal(false); } + if (cap->extra_char < ' ') { + // Prefix a control character with CTRL-V to avoid it being used as + // a command. + stuffcharReadbuff(Ctrl_V); + } stuffcharReadbuff(cap->extra_char); stuffcharReadbuff(ESC); if (virtual_active()) { @@ -4751,8 +4722,6 @@ static void nv_vreplace(cmdarg_T *cap) /// Swap case for "~" command, when it does not work like an operator. static void n_swapchar(cmdarg_T *cap) { - long n; - pos_T startpos; int did_change = 0; if (checkclearopq(cap->oap)) { @@ -4770,8 +4739,8 @@ static void n_swapchar(cmdarg_T *cap) return; } - startpos = curwin->w_cursor; - for (n = cap->count1; n > 0; n--) { + pos_T startpos = curwin->w_cursor; + for (int n = cap->count1; n > 0; n--) { did_change |= swapchar(cap->oap->op_type, &curwin->w_cursor); inc_cursor(); if (gchar_cursor() == NUL) { @@ -4783,7 +4752,7 @@ static void n_swapchar(cmdarg_T *cap) if (u_savesub(curwin->w_cursor.lnum) == false) { break; } - u_clearline(); + u_clearline(curbuf); } } else { break; @@ -4794,8 +4763,8 @@ static void n_swapchar(cmdarg_T *cap) check_cursor(); curwin->w_set_curswant = true; if (did_change) { - changed_lines(startpos.lnum, startpos.col, curwin->w_cursor.lnum + 1, - 0L, true); + changed_lines(curbuf, startpos.lnum, startpos.col, curwin->w_cursor.lnum + 1, + 0, true); curbuf->b_op_start = startpos; curbuf->b_op_end = curwin->w_cursor; if (curbuf->b_op_end.col > 0) { @@ -4957,9 +4926,9 @@ static void nv_pcmark(cmdarg_T *cap) } if (cap->cmdchar == 'g') { - fm = get_changelist(curbuf, curwin, (int)cap->count1); + fm = get_changelist(curbuf, curwin, cap->count1); } else { - fm = get_jumplist(curwin, (int)cap->count1); + fm = get_jumplist(curwin, cap->count1); flags |= KMarkNoContext | kMarkJumpList; } // Changelist and jumplist have their own error messages. Therefore avoid @@ -4969,7 +4938,7 @@ static void nv_pcmark(cmdarg_T *cap) move_res = nv_mark_move_to(cap, flags, fm); } else if (cap->cmdchar == 'g') { if (curbuf->b_changelistlen == 0) { - emsg(_("E664: changelist is empty")); + emsg(_(e_changelist_is_empty)); } else if (cap->count1 < 0) { emsg(_("E662: At start of changelist")); } else { @@ -5051,7 +5020,7 @@ static void nv_visual(cmdarg_T *cap) // For V and ^V, we multiply the number of lines even if there // was only one -- webb if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1) { - curwin->w_cursor.lnum += resel_VIsual_line_count * (linenr_T)cap->count0 - 1; + curwin->w_cursor.lnum += resel_VIsual_line_count * cap->count0 - 1; check_cursor(); } VIsual_mode = resel_VIsual_mode; @@ -5059,7 +5028,7 @@ static void nv_visual(cmdarg_T *cap) if (resel_VIsual_line_count <= 1) { update_curswant_force(); assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX); - curwin->w_curswant += resel_VIsual_vcol * (int)cap->count0; + curwin->w_curswant += resel_VIsual_vcol * cap->count0; if (*p_sel != 'e') { curwin->w_curswant--; } @@ -5072,9 +5041,13 @@ static void nv_visual(cmdarg_T *cap) curwin->w_curswant = MAXCOL; coladvance(MAXCOL); } else if (VIsual_mode == Ctrl_V) { + // Update curswant on the original line, that is where "col" is valid. + linenr_T lnum = curwin->w_cursor.lnum; + curwin->w_cursor.lnum = VIsual.lnum; update_curswant_force(); assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX); - curwin->w_curswant += resel_VIsual_vcol * (int)cap->count0 - 1; + curwin->w_curswant += resel_VIsual_vcol * cap->count0 - 1; + curwin->w_cursor.lnum = lnum; coladvance(curwin->w_curswant); } else { curwin->w_set_curswant = true; @@ -5268,10 +5241,11 @@ static void nv_g_home_m_cmd(cmdarg_T *cap) if (flag) { do { i = gchar_cursor(); - } while (ascii_iswhite(i) && oneright()); + } while (ascii_iswhite(i) && oneright() == OK); curwin->w_valid &= ~VALID_WCOL; } curwin->w_set_curswant = true; + adjust_skipcol(); } /// "g_": to the last non-blank character in the line or <count> lines downward. @@ -5306,6 +5280,7 @@ static void nv_g_dollar_cmd(cmdarg_T *cap) oparg_T *oap = cap->oap; int i; int col_off = curwin_col_off(); + const bool flag = cap->nchar == K_END || cap->nchar == K_KEND; oap->motion_type = kMTCharWise; oap->inclusive = true; @@ -5344,11 +5319,11 @@ static void nv_g_dollar_cmd(cmdarg_T *cap) coladvance((colnr_T)i); // if the character doesn't fit move one back - if (curwin->w_cursor.col > 0 && utf_ptr2cells((const char *)get_cursor_pos_ptr()) > 1) { + if (curwin->w_cursor.col > 0 && utf_ptr2cells(get_cursor_pos_ptr()) > 1) { colnr_T vcol; getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol); - if (vcol >= curwin->w_leftcol + curwin->w_width - col_off) { + if (vcol >= curwin->w_leftcol + curwin->w_width_inner - col_off) { curwin->w_cursor.col--; } } @@ -5356,6 +5331,12 @@ static void nv_g_dollar_cmd(cmdarg_T *cap) // Make sure we stick in this column. update_curswant_force(); } + if (flag) { + do { + i = gchar_cursor(); + } while (ascii_iswhite(i) && oneleft() == OK); + curwin->w_valid &= ~VALID_WCOL; + } } /// "gi": start Insert at the last position. @@ -5363,7 +5344,7 @@ static void nv_gi_cmd(cmdarg_T *cap) { if (curbuf->b_last_insert.mark.lnum != 0) { curwin->w_cursor = curbuf->b_last_insert.mark; - check_cursor_lnum(); + check_cursor_lnum(curwin); int i = (int)strlen(get_cursor_line_ptr()); if (curwin->w_cursor.col > (colnr_T)i) { if (virtual_active()) { @@ -5492,7 +5473,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(curwin, curwin->w_cursor.lnum); if (cap->count0 > 0 && cap->count0 <= 100) { coladvance((colnr_T)(i * cap->count0 / 100)); } else { @@ -5570,7 +5551,7 @@ static void nv_g_cmd(cmdarg_T *cap) // "gs": Goto sleep. case 's': - do_sleep(cap->count1 * 1000L); + do_sleep(cap->count1 * 1000); break; // "ga": Display the ascii value of the character under the @@ -5625,7 +5606,7 @@ static void nv_g_cmd(cmdarg_T *cap) // "gD": idem, but in the current file. case 'd': case 'D': - nv_gd(oap, cap->nchar, (int)cap->count0); + nv_gd(oap, cap->nchar, cap->count0); break; // g<*Mouse> : <C-*mouse> @@ -5681,12 +5662,12 @@ static void nv_g_cmd(cmdarg_T *cap) case 't': if (!checkclearop(oap)) { - goto_tabpage((int)cap->count0); + goto_tabpage(cap->count0); } break; case 'T': if (!checkclearop(oap)) { - goto_tabpage(-(int)cap->count1); + goto_tabpage(-cap->count1); } break; @@ -5726,10 +5707,10 @@ static void n_opencmd(cmdarg_T *cap) (void)hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum); } - if (u_save((linenr_T)(curwin->w_cursor.lnum - - (cap->cmdchar == 'O' ? 1 : 0)), - (linenr_T)(curwin->w_cursor.lnum + - (cap->cmdchar == 'o' ? 1 : 0))) + // trigger TextChangedI for the 'o/O' command + curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf); + if (u_save(curwin->w_cursor.lnum - (cap->cmdchar == 'O' ? 1 : 0), + curwin->w_cursor.lnum + (cap->cmdchar == 'o' ? 1 : 0)) && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD, has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM : 0, 0, NULL)) { @@ -5760,10 +5741,9 @@ static void nv_dot(cmdarg_T *cap) static void nv_redo_or_register(cmdarg_T *cap) { if (VIsual_select && VIsual_active) { - int reg; // Get register name no_mapping++; - reg = plain_vgetc(); + int reg = plain_vgetc(); LANGMAP_ADJUST(reg, true); no_mapping--; @@ -5780,7 +5760,7 @@ static void nv_redo_or_register(cmdarg_T *cap) return; } - u_redo((int)cap->count1); + u_redo(cap->count1); curwin->w_set_curswant = true; } @@ -5823,9 +5803,7 @@ static void nv_tilde(cmdarg_T *cap) /// The actual work is done by do_pending_operator(). static void nv_operator(cmdarg_T *cap) { - int op_type; - - op_type = get_op_type(cap->cmdchar, cap->nchar); + int op_type = get_op_type(cap->cmdchar, cap->nchar); if (bt_prompt(curbuf) && op_is_change(op_type) && !prompt_curpos_editable()) { @@ -5872,7 +5850,7 @@ static void set_op_var(int optype) static void nv_lineop(cmdarg_T *cap) { cap->oap->motion_type = kMTLineWise; - if (cursor_down(cap->count1 - 1L, cap->oap->op_type == OP_NOP) == false) { + if (cursor_down(cap->count1 - 1, cap->oap->op_type == OP_NOP) == false) { clearopbeep(cap->oap); } else if ((cap->oap->op_type == OP_DELETE // only with linewise motions @@ -6042,9 +6020,8 @@ static void adjust_for_sel(cmdarg_T *cap) /// @return true when backed up to the previous line. bool unadjust_for_sel(void) { - pos_T *pp; - if (*p_sel == 'e' && !equalpos(VIsual, curwin->w_cursor)) { + pos_T *pp; if (lt(VIsual, curwin->w_cursor)) { pp = &curwin->w_cursor; } else { @@ -6086,17 +6063,17 @@ static void nv_goto(cmdarg_T *cap) if (cap->arg) { lnum = curbuf->b_ml.ml_line_count; } else { - lnum = 1L; + lnum = 1; } cap->oap->motion_type = kMTLineWise; setpcmark(); // When a count is given, use it instead of the default lnum if (cap->count0 != 0) { - lnum = (linenr_T)cap->count0; + lnum = cap->count0; } - if (lnum < 1L) { - lnum = 1L; + if (lnum < 1) { + lnum = 1; } else if (lnum > curbuf->b_ml.ml_line_count) { lnum = curbuf->b_ml.ml_line_count; } @@ -6132,20 +6109,18 @@ static void nv_normal(cmdarg_T *cap) /// Don't even beep if we are canceling a command. static void nv_esc(cmdarg_T *cap) { - int no_reason; - - no_reason = (cap->oap->op_type == OP_NOP - && cap->opcount == 0 - && cap->count0 == 0 - && cap->oap->regname == 0); + int no_reason = (cap->oap->op_type == OP_NOP + && cap->opcount == 0 + && cap->count0 == 0 + && cap->oap->regname == 0); if (cap->arg) { // true for CTRL-C if (restart_edit == 0 && cmdwin_type == 0 && !VIsual_active && no_reason) { if (anyBufIsChanged()) { msg(_("Type :qa! and press <Enter> to abandon all changes" - " and exit Nvim")); + " and exit Nvim"), 0); } else { - msg(_("Type :qa and press <Enter> to exit Nvim")); + msg(_("Type :qa and press <Enter> to exit Nvim"), 0); } } @@ -6270,6 +6245,11 @@ static void invoke_edit(cmdarg_T *cap, int repl, int cmd, int startln) // Always reset "restart_edit", this is not a restarted edit. restart_edit = 0; + // Reset Changedtick_i, so that TextChangedI will only be triggered for stuff + // from insert mode, for 'o/O' this has already been done in n_opencmd + if (cap->cmdchar != 'O' && cap->cmdchar != 'o') { + curbuf->b_last_changedtick_i = buf_get_changedtick(curbuf); + } if (edit(cmd, startln, cap->count1)) { cap->retval |= CA_COMMAND_BUSY; } @@ -6284,7 +6264,6 @@ static void nv_object(cmdarg_T *cap) { bool flag; bool include; - char *mps_save; if (cap->cmdchar == 'i') { include = false; // "ix" = inner object: exclude white space @@ -6292,7 +6271,7 @@ static void nv_object(cmdarg_T *cap) include = true; // "ax" = an object: include white space } // Make sure (), [], {} and <> are in 'matchpairs' - mps_save = curbuf->b_p_mps; + char *mps_save = curbuf->b_p_mps; curbuf->b_p_mps = "(:),{:},[:],<:>"; switch (cap->nchar) { @@ -6372,6 +6351,10 @@ static void nv_record(cmdarg_T *cap) } if (cap->nchar == ':' || cap->nchar == '/' || cap->nchar == '?') { + if (cmdwin_type != 0) { + emsg(_(e_cmdline_window_already_open)); + return; + } stuffcharReadbuff(cap->nchar); stuffcharReadbuff(K_CMDWIN); } else { @@ -6411,7 +6394,7 @@ static void nv_halfpage(cmdarg_T *cap) && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)) { clearopbeep(cap->oap); } else if (!checkclearop(cap->oap)) { - halfpage(cap->cmdchar == Ctrl_D, (linenr_T)cap->count0); + halfpage(cap->cmdchar == Ctrl_D, cap->count0); } } @@ -6456,7 +6439,6 @@ static void nv_put(cmdarg_T *cap) /// @param fix_indent true for "[p", "[P", "]p" and "]P". static void nv_put_opt(cmdarg_T *cap, bool fix_indent) { - int regname = 0; yankreg_T *savereg = NULL; bool empty = false; bool was_visual = false; @@ -6482,7 +6464,7 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) if (fix_indent) { dir = (cap->cmdchar == ']' && cap->nchar == 'p') - ? FORWARD : BACKWARD; + ? FORWARD : BACKWARD; flags |= PUT_FIXINDENT; } else { dir = (cap->cmdchar == 'P' @@ -6502,7 +6484,7 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) // Need to save and restore the registers that the delete // overwrites if the old contents is being put. was_visual = true; - regname = cap->oap->regname; + int regname = cap->oap->regname; bool keep_registers = cap->cmdchar == 'P'; // '+' and '*' could be the same selection bool clipoverwrite = (regname == '+' || regname == '*') && (cb_flags & CB_UNNAMEDMASK); |