diff options
Diffstat (limited to 'src/nvim/normal.c')
-rw-r--r-- | src/nvim/normal.c | 476 |
1 files changed, 268 insertions, 208 deletions
diff --git a/src/nvim/normal.c b/src/nvim/normal.c index e7e6d2b365..968cfde388 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -24,7 +24,7 @@ #include "nvim/diff.h" #include "nvim/digraph.h" #include "nvim/edit.h" -#include "nvim/eval.h" +#include "nvim/eval/userfunc.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" @@ -615,14 +615,19 @@ static void normal_redraw_mode_message(NormalState *s) kmsg = keep_msg; keep_msg = NULL; - // showmode() will clear keep_msg, but we want to use it anyway + // Showmode() will clear keep_msg, but we want to use it anyway. + // First update w_topline. + setcursor(); update_screen(0); // now reset it, otherwise it's put in the history again keep_msg = kmsg; + + kmsg = vim_strsave(keep_msg); msg_attr((const char *)kmsg, keep_msg_attr); xfree(kmsg); } setcursor(); + ui_cursor_shape(); // show different cursor shape ui_flush(); if (msg_scroll || emsg_on_display) { os_delay(1000L, true); // wait at least one second @@ -760,7 +765,7 @@ static void normal_get_additional_char(NormalState *s) // because if it's put back with vungetc() it's too late to apply // mapping. no_mapping--; - while (enc_utf8 && lang && (s->c = vpeekc()) > 0 + while (lang && (s->c = vpeekc()) > 0 && (s->c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1)) { s->c = plain_vgetc(); if (!utf_iscomposing(s->c)) { @@ -874,8 +879,10 @@ static void normal_finish_command(NormalState *s) s->old_mapped_len = typebuf_maplen(); } - // If an operation is pending, handle it... - do_pending_operator(&s->ca, s->old_col, false); + // If an operation is pending, handle it. But not for K_IGNORE. + if (s->ca.cmdchar != K_IGNORE) { + do_pending_operator(&s->ca, s->old_col, false); + } // Wait for a moment when a message is displayed that will be overwritten // by the mode message. @@ -1255,13 +1262,20 @@ static void normal_redraw(NormalState *s) maketitle(); } + curbuf->b_last_used = time(NULL); + // Display message after redraw. If an external message is still visible, // it contains the kept message already. if (keep_msg != NULL && !msg_ext_is_visible()) { - // msg_attr_keep() will set keep_msg to NULL, must free the string here. - // Don't reset keep_msg, msg_attr_keep() uses it to check for duplicates. - char *p = (char *)keep_msg; - msg_attr(p, keep_msg_attr); + char_u *const p = vim_strsave(keep_msg); + + // msg_start() will set keep_msg to NULL, make a copy + // first. Don't reset keep_msg, msg_attr_keep() uses it to + // check for duplicates. Never put this message in + // history. + msg_hist_off = true; + msg_attr((const char *)p, keep_msg_attr); + msg_hist_off = false; xfree(p); } @@ -1422,12 +1436,12 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) if (oap->motion_type == kMTLineWise) { oap->inclusive = false; } else if (oap->motion_type == kMTCharWise) { - // If the motion already was characterwise, toggle "inclusive" + // If the motion already was charwise, toggle "inclusive" oap->inclusive = !oap->inclusive; } oap->motion_type = kMTCharWise; } else if (oap->motion_force == Ctrl_V) { - // Change line- or characterwise motion into Visual block mode. + // Change line- or charwise motion into Visual block mode. if (!VIsual_active) { VIsual_active = true; VIsual = oap->start; @@ -1516,7 +1530,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } // In Select mode, a linewise selection is operated upon like a - // characterwise selection. + // charwise selection. // Special case: gH<Del> deletes the last line. if (VIsual_select && VIsual_mode == 'V' && cap->oap->op_type != OP_DELETE) { @@ -1553,9 +1567,13 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) if (!VIsual_active) { if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) oap->start.col = 0; - if (hasFolding(curwin->w_cursor.lnum, NULL, - &curwin->w_cursor.lnum)) + if ((curwin->w_cursor.col > 0 + || oap->inclusive + || oap->motion_type == kMTLineWise) + && hasFolding(curwin->w_cursor.lnum, NULL, + &curwin->w_cursor.lnum)) { curwin->w_cursor.col = (colnr_T)STRLEN(get_cursor_line_ptr()); + } } oap->end = curwin->w_cursor; curwin->w_cursor = oap->start; @@ -1705,13 +1723,12 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } } - /* Include the trailing byte of a multi-byte char. */ - if (has_mbyte && oap->inclusive) { - int l; - - l = (*mb_ptr2len)(ml_get_pos(&oap->end)); - if (l > 1) + // Include the trailing byte of a multi-byte char. + if (oap->inclusive) { + const int l = utfc_ptr2len(ml_get_pos(&oap->end)); + if (l > 1) { oap->end.col += l - 1; + } } curwin->w_set_curswant = true; @@ -1840,10 +1857,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) restart_edit = 0; // Restore linebreak, so that when the user edits it looks as before. - if (curwin->w_p_lbr != lbr_saved) { - curwin->w_p_lbr = lbr_saved; - get_op_vcol(oap, redo_VIsual_mode, false); - } + curwin->w_p_lbr = lbr_saved; // Reset finish_op now, don't want it set inside edit(). finish_op = false; @@ -1929,10 +1943,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) restart_edit = 0; // Restore linebreak, so that when the user edits it looks as before. - if (curwin->w_p_lbr != lbr_saved) { - curwin->w_p_lbr = lbr_saved; - get_op_vcol(oap, redo_VIsual_mode, false); - } + curwin->w_p_lbr = lbr_saved; op_insert(oap, cap->count1); @@ -1958,18 +1969,15 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) CancelRedo(); } else { // Restore linebreak, so that when the user edits it looks as before. - if (curwin->w_p_lbr != lbr_saved) { - curwin->w_p_lbr = lbr_saved; - get_op_vcol(oap, redo_VIsual_mode, false); - } + curwin->w_p_lbr = lbr_saved; op_replace(oap, cap->nchar); } break; case OP_FOLD: - VIsual_reselect = false; /* don't reselect now */ - foldCreate(oap->start.lnum, oap->end.lnum); + VIsual_reselect = false; // don't reselect now + foldCreate(curwin, oap->start.lnum, oap->end.lnum); break; case OP_FOLDOPEN: @@ -1987,9 +1995,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) case OP_FOLDDEL: case OP_FOLDDELREC: - VIsual_reselect = false; /* don't reselect now */ - deleteFold(oap->start.lnum, oap->end.lnum, - oap->op_type == OP_FOLDDELREC, oap->is_VIsual); + VIsual_reselect = false; // don't reselect now + deleteFold(curwin, oap->start.lnum, oap->end.lnum, + oap->op_type == OP_FOLDDELREC, oap->is_VIsual); break; case OP_NR_ADD: @@ -2560,7 +2568,14 @@ do_mouse ( * JUMP! */ jump_flags = jump_to_mouse(jump_flags, - oap == NULL ? NULL : &(oap->inclusive), which_button); + oap == NULL ? NULL : &(oap->inclusive), + which_button); + + // A click in the window toolbar has no side effects. + if (jump_flags & MOUSE_WINBAR) { + return false; + } + moved = (jump_flags & CURSOR_MOVED); in_status_line = (jump_flags & IN_STATUS_LINE); in_sep_line = (jump_flags & IN_SEP_LINE); @@ -2588,12 +2603,13 @@ do_mouse ( /* Set global flag that we are extending the Visual area with mouse * dragging; temporarily minimize 'scrolloff'. */ - if (VIsual_active && is_drag && p_so) { - /* In the very first line, allow scrolling one line */ - if (mouse_row == 0) + if (VIsual_active && is_drag && get_scrolloff_value()) { + // In the very first line, allow scrolling one line + if (mouse_row == 0) { mouse_dragging = 2; - else + } else { mouse_dragging = 1; + } } /* When dragging the mouse above the window, scroll down. */ @@ -2900,17 +2916,17 @@ static void find_end_of_word(pos_T *pos) */ static int get_mouse_class(char_u *p) { - int c; - - if (has_mbyte && MB_BYTE2LEN(p[0]) > 1) + if (MB_BYTE2LEN(p[0]) > 1) { return mb_get_class(p); + } - c = *p; - if (c == ' ' || c == '\t') + const int c = *p; + if (c == ' ' || c == '\t') { return 0; - - if (vim_iswordc(c)) + } + if (vim_iswordc(c)) { return 2; + } /* * There are a few special cases where we want certain combinations of @@ -3646,7 +3662,9 @@ static void nv_help(cmdarg_T *cap) */ static void nv_addsub(cmdarg_T *cap) { - if (!VIsual_active && cap->oap->op_type == OP_NOP) { + if (bt_prompt(curbuf) && !prompt_curpos_editable()) { + clearopbeep(cap->oap); + } 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, cap->count1, cap->arg); @@ -3754,7 +3772,6 @@ find_decl ( bool retval = true; bool incll; int searchflags = flags_arg; - bool valid; pat = xmalloc(len + 7); @@ -3789,10 +3806,8 @@ find_decl ( /* Search forward for the identifier, ignore comment lines. */ clearpos(&found_pos); for (;; ) { - valid = false; - (void)valid; // Avoid "dead assignment" warning. t = searchit(curwin, curbuf, &curwin->w_cursor, NULL, FORWARD, - pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL, NULL); + pat, 1L, searchflags, RE_LAST, NULL); if (curwin->w_cursor.lnum >= old_pos.lnum) { t = false; // match after start is failure too } @@ -3825,7 +3840,7 @@ find_decl ( curwin->w_cursor.col = 0; continue; } - valid = is_ident(get_cursor_line_ptr(), curwin->w_cursor.col); + bool valid = is_ident(get_cursor_line_ptr(), curwin->w_cursor.col); // If the current position is not a valid identifier and a previous match is // present, favor that one instead. @@ -3923,18 +3938,20 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1; else n = width1; - if (curwin->w_curswant > (colnr_T)n + 1) - curwin->w_curswant -= ((curwin->w_curswant - n) / width2 + 1) - * width2; + if (curwin->w_curswant >= n) { + curwin->w_curswant = n - 1; + } } while (dist--) { if (dir == BACKWARD) { - if ((long)curwin->w_curswant >= width2) - /* move back within line */ + if (curwin->w_curswant >= width1) { + // Move back within the line. This can give a negative value + // for w_curswant if width1 < width2 (with cpoptions+=n), + // which will get clipped to column 0. curwin->w_curswant -= width2; - else { - /* to previous line */ + } else { + // to previous line if (curwin->w_cursor.lnum == 1) { retval = false; break; @@ -3971,6 +3988,13 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) } curwin->w_cursor.lnum++; 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 + // clipped to column 0. + if (curwin->w_curswant >= width1) { + curwin->w_curswant -= width2; + } linelen = linetabsize(get_cursor_line_ptr()); } } @@ -4085,9 +4109,9 @@ void scroll_redraw(int up, long count) scrollup(count, true); else scrolldown(count, true); - if (p_so) { - /* Adjust the cursor position for 'scrolloff'. Mark w_topline as - * valid, otherwise the screen jumps back at the end of the file. */ + if (get_scrolloff_value()) { + // Adjust the cursor position for 'scrolloff'. Mark w_topline as + // valid, otherwise the screen jumps back at the end of the file. cursor_correct(); check_cursor_moved(curwin); curwin->w_valid |= VALID_TOPLINE; @@ -4116,6 +4140,7 @@ void scroll_redraw(int up, long count) } if (curwin->w_cursor.lnum != prev_lnum) coladvance(curwin->w_curswant); + curwin->w_viewport_invalid = true; redraw_later(VALID); } @@ -4131,8 +4156,8 @@ static void nv_zet(cmdarg_T *cap) int old_fen = curwin->w_p_fen; bool undo = false; - assert(p_siso <= INT_MAX); - int l_p_siso = (int)p_siso; + int l_p_siso = (int)get_sidescrolloff_value(); + assert(l_p_siso <= INT_MAX); if (ascii_isdigit(nchar)) { /* @@ -4336,11 +4361,12 @@ dozet: /* "zD": delete fold at cursor recursively */ case 'd': case 'D': if (foldManualAllowed(false)) { - if (VIsual_active) + if (VIsual_active) { nv_operator(cap); - else - deleteFold(curwin->w_cursor.lnum, - curwin->w_cursor.lnum, nchar == 'D', false); + } else { + deleteFold(curwin, curwin->w_cursor.lnum, + curwin->w_cursor.lnum, nchar == 'D', false); + } } break; @@ -4348,11 +4374,11 @@ dozet: case 'E': if (foldmethodIsManual(curwin)) { clearFolding(curwin); changed_window_setting(); - } else if (foldmethodIsMarker(curwin)) - deleteFold((linenr_T)1, curbuf->b_ml.ml_line_count, - true, false); - else + } else if (foldmethodIsMarker(curwin)) { + deleteFold(curwin, (linenr_T)1, curbuf->b_ml.ml_line_count, true, false); + } else { EMSG(_("E352: Cannot erase folds with current 'foldmethod'")); + } break; /* "zn": fold none: reset 'foldenable' */ @@ -4454,16 +4480,16 @@ dozet: case 'r': curwin->w_p_fdl += cap->count1; { - int d = getDeepestNesting(); + int d = getDeepestNesting(curwin); if (curwin->w_p_fdl >= d) { curwin->w_p_fdl = d; } } break; - /* "zR": open all folds */ - case 'R': curwin->w_p_fdl = getDeepestNesting(); - old_fdl = -1; /* force an update */ + case 'R': // "zR": open all folds + curwin->w_p_fdl = getDeepestNesting(curwin); + old_fdl = -1; // force an update break; case 'j': /* "zj" move to next fold downwards */ @@ -4577,7 +4603,7 @@ static void nv_colon(cmdarg_T *cap) nv_operator(cap); } else { if (cap->oap->op_type != OP_NOP) { - // Using ":" as a movement is characterwise exclusive. + // Using ":" as a movement is charwise exclusive. cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; } else if (cap->count0 && !is_cmdkey) { @@ -4678,9 +4704,8 @@ static void nv_ctrlo(cmdarg_T *cap) } } -/* - * CTRL-^ command, short for ":e #" - */ +// CTRL-^ command, short for ":e #". Works even when the alternate buffer is +// not named. static void nv_hat(cmdarg_T *cap) { if (!checkclearopq(cap->oap)) @@ -4905,10 +4930,9 @@ static void nv_ident(cmdarg_T *cap) *p++ = '\\'; /* When current byte is a part of multibyte character, copy all * bytes of that character. */ - if (has_mbyte) { - size_t len = (size_t)((*mb_ptr2len)(ptr) - 1); - for (size_t i = 0; i < len && n > 0; ++i, --n) - *p++ = *ptr++; + const size_t len = (size_t)(utfc_ptr2len(ptr) - 1); + for (size_t i = 0; i < len && n > 0; i++, n--) { + *p++ = *ptr++; } *p++ = *ptr++; } @@ -4919,16 +4943,19 @@ static void nv_ident(cmdarg_T *cap) * Execute the command. */ if (cmdchar == '*' || cmdchar == '#') { - if (!g_cmd && ( - has_mbyte ? vim_iswordp(mb_prevptr(get_cursor_line_ptr(), ptr)) : - vim_iswordc(ptr[-1]))) + if (!g_cmd + && vim_iswordp(mb_prevptr(get_cursor_line_ptr(), ptr))) { STRCAT(buf, "\\>"); - /* put pattern in search history */ + } + // put pattern in search history init_history(); add_to_history(HIST_SEARCH, (char_u *)buf, true, NUL); - (void)normal_search(cap, cmdchar == '*' ? '/' : '?', (char_u *)buf, 0); + (void)normal_search(cap, cmdchar == '*' ? '/' : '?', (char_u *)buf, 0, + NULL); } else { + g_tag_at_cursor = true; do_cmdline_cmd(buf); + g_tag_at_cursor = false; } xfree(buf); @@ -4963,9 +4990,8 @@ get_visual_text ( *pp = ml_get_pos(&VIsual); *lenp = (size_t)curwin->w_cursor.col - (size_t)VIsual.col + 1; } - if (has_mbyte) - /* Correct the length to include the whole last character. */ - *lenp += (size_t)((*mb_ptr2len)(*pp + (*lenp - 1)) - 1); + // Correct the length to include the whole last character. + *lenp += (size_t)(utfc_ptr2len(*pp + (*lenp - 1)) - 1); } reset_VIsual_and_resel(); return true; @@ -5125,14 +5151,10 @@ static void nv_right(cmdarg_T *cap) break; } else if (PAST_LINE) { curwin->w_set_curswant = true; - if (virtual_active()) + if (virtual_active()) { oneright(); - else { - if (has_mbyte) - curwin->w_cursor.col += - (*mb_ptr2len)(get_cursor_pos_ptr()); - else - ++curwin->w_cursor.col; + } else { + curwin->w_cursor.col += (*mb_ptr2len)(get_cursor_pos_ptr()); } } } @@ -5187,11 +5209,7 @@ static void nv_left(cmdarg_T *cap) char_u *cp = get_cursor_pos_ptr(); if (*cp != NUL) { - if (has_mbyte) { - curwin->w_cursor.col += (*mb_ptr2len)(cp); - } else { - curwin->w_cursor.col++; - } + curwin->w_cursor.col += utfc_ptr2len(cp); } cap->retval |= CA_NO_ADJ_OP_END; } @@ -5245,6 +5263,13 @@ static void nv_down(cmdarg_T *cap) // In the cmdline window a <CR> executes the command. if (cmdwin_type != 0 && cap->cmdchar == CAR) { cmdwin_result = CAR; + } else if (bt_prompt(curbuf) && cap->cmdchar == CAR + && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) { + // In a prompt buffer a <CR> in the last line invokes the callback. + invoke_prompt_callback(); + if (restart_edit == 0) { + restart_edit = 'a'; + } } else { cap->oap->motion_type = kMTLineWise; if (cursor_down(cap->count1, cap->oap->op_type == OP_NOP) == false) { @@ -5346,7 +5371,7 @@ static void nv_search(cmdarg_T *cap) // When using 'incsearch' the cursor may be moved to set a different search // start position. - cap->searchbuf = getcmdline(cap->cmdchar, cap->count1, 0); + cap->searchbuf = getcmdline(cap->cmdchar, cap->count1, 0, true); if (cap->searchbuf == NULL) { clearop(oap); @@ -5355,7 +5380,7 @@ static void nv_search(cmdarg_T *cap) (void)normal_search(cap, cap->cmdchar, cap->searchbuf, (cap->arg || !equalpos(save_cursor, curwin->w_cursor)) - ? 0 : SEARCH_MARK); + ? 0 : SEARCH_MARK, NULL); } /* @@ -5365,14 +5390,15 @@ static void nv_search(cmdarg_T *cap) static void nv_next(cmdarg_T *cap) { pos_T old = curwin->w_cursor; - int i = normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg); + int wrapped = false; + int i = normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg, &wrapped); - if (i == 1 && equalpos(old, curwin->w_cursor)) { + if (i == 1 && !wrapped && equalpos(old, curwin->w_cursor)) { // Avoid getting stuck on the current cursor position, which can happen when // an offset is given and the cursor is on the last char in the buffer: // Repeat with count + 1. cap->count1 += 1; - (void)normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg); + (void)normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg, NULL); cap->count1 -= 1; } } @@ -5386,18 +5412,24 @@ static int normal_search( cmdarg_T *cap, int dir, char_u *pat, - int opt /* extra flags for do_search() */ + int opt, // extra flags for do_search() + int *wrapped ) { int i; + searchit_arg_T sia; cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; cap->oap->use_reg_one = true; curwin->w_set_curswant = true; + memset(&sia, 0, sizeof(sia)); i = do_search(cap->oap, dir, pat, cap->count1, - opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, NULL, NULL); + opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, &sia); + if (wrapped != NULL) { + *wrapped = sia.sa_wrapped; + } if (i == 0) { clearop(cap->oap); } else { @@ -5830,6 +5862,10 @@ static void nv_undo(cmdarg_T *cap) static void nv_kundo(cmdarg_T *cap) { if (!checkclearopq(cap->oap)) { + if (bt_prompt(curbuf)) { + clearopbeep(cap->oap); + return; + } u_undo((int)cap->count1); curwin->w_set_curswant = true; } @@ -5842,10 +5878,14 @@ static void nv_replace(cmdarg_T *cap) { char_u *ptr; int had_ctrl_v; - long n; - if (checkclearop(cap->oap)) + if (checkclearop(cap->oap)) { return; + } + if (bt_prompt(curbuf) && !prompt_curpos_editable()) { + clearopbeep(cap->oap); + return; + } /* get another character */ if (cap->nchar == Ctrl_V) { @@ -5896,7 +5936,7 @@ static void nv_replace(cmdarg_T *cap) /* Abort if not enough characters to replace. */ ptr = get_cursor_pos_ptr(); if (STRLEN(ptr) < (unsigned)cap->count1 - || (has_mbyte && mb_charlen(ptr) < cap->count1) + || (mb_charlen(ptr) < cap->count1) ) { clearopbeep(cap->oap); return; @@ -5938,71 +5978,44 @@ static void nv_replace(cmdarg_T *cap) NUL, 'r', NUL, had_ctrl_v, cap->nchar); curbuf->b_op_start = curwin->w_cursor; - if (has_mbyte) { - int old_State = State; - - if (cap->ncharC1 != 0) - AppendCharToRedobuff(cap->ncharC1); - if (cap->ncharC2 != 0) - AppendCharToRedobuff(cap->ncharC2); - - /* 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 (n = cap->count1; n > 0; --n) { - State = REPLACE; - if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y) { - int c = ins_copychar(curwin->w_cursor.lnum - + (cap->nchar == Ctrl_Y ? -1 : 1)); - if (c != NUL) - ins_char(c); - else - /* will be decremented further down */ - ++curwin->w_cursor.col; - } else - ins_char(cap->nchar); - State = old_State; - if (cap->ncharC1 != 0) - ins_char(cap->ncharC1); - if (cap->ncharC2 != 0) - ins_char(cap->ncharC2); - } - } else { - /* - * Replace the characters within one line. - */ - for (n = cap->count1; n > 0; --n) { - /* - * Get ptr again, because u_save and/or showmatch() will have - * released the line. At the same time we let know that the - * line will be changed. - */ - ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, true); - if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y) { - int c = ins_copychar(curwin->w_cursor.lnum - + (cap->nchar == Ctrl_Y ? -1 : 1)); - if (c != NUL) { - assert(c >= 0 && c <= UCHAR_MAX); - ptr[curwin->w_cursor.col] = (char_u)c; - } + const int old_State = State; + + if (cap->ncharC1 != 0) { + AppendCharToRedobuff(cap->ncharC1); + } + if (cap->ncharC2 != 0) { + AppendCharToRedobuff(cap->ncharC2); + } + + // 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--) { + State = REPLACE; + if (cap->nchar == Ctrl_E || cap->nchar == Ctrl_Y) { + int c = ins_copychar(curwin->w_cursor.lnum + + (cap->nchar == Ctrl_Y ? -1 : 1)); + if (c != NUL) { + ins_char(c); } else { - assert(cap->nchar >= 0 && cap->nchar <= UCHAR_MAX); - ptr[curwin->w_cursor.col] = (char_u)cap->nchar; + // will be decremented further down + curwin->w_cursor.col++; } - if (p_sm && msg_silent == 0) - showmatch(cap->nchar); - ++curwin->w_cursor.col; + } else { + ins_char(cap->nchar); + } + State = old_State; + if (cap->ncharC1 != 0) { + ins_char(cap->ncharC1); + } + if (cap->ncharC2 != 0) { + ins_char(cap->ncharC2); } - - /* mark the buffer as changed and prepare for displaying */ - changed_bytes(curwin->w_cursor.lnum, - (colnr_T)(curwin->w_cursor.col - cap->count1)); } --curwin->w_cursor.col; /* cursor on the last replaced char */ /* if the character on the left of the current cursor is a multi-byte * character, move two characters left */ - if (has_mbyte) - mb_adjust_cursor(); + mb_adjust_cursor(); curbuf->b_op_end = curwin->w_cursor; curwin->w_set_curswant = true; set_last_insert(cap->nchar); @@ -6209,7 +6222,11 @@ static void v_visop(cmdarg_T *cap) */ static void nv_subst(cmdarg_T *cap) { - if (VIsual_active) { /* "vs" and "vS" are the same as "vc" */ + if (bt_prompt(curbuf) && !prompt_curpos_editable()) { + clearopbeep(cap->oap); + return; + } + if (VIsual_active) { // "vs" and "vS" are the same as "vc" if (cap->cmdchar == 'S') { VIsual_mode_orig = VIsual_mode; VIsual_mode = 'V'; @@ -6292,9 +6309,7 @@ static void nv_gomark(cmdarg_T *cap) } } -/* - * Handle CTRL-O, CTRL-I, "g;" and "g," commands. - */ +// Handle CTRL-O, CTRL-I, "g;", "g,", and "CTRL-Tab" commands. static void nv_pcmark(cmdarg_T *cap) { pos_T *pos; @@ -6302,11 +6317,16 @@ static void nv_pcmark(cmdarg_T *cap) const bool old_KeyTyped = KeyTyped; // getting file may reset it if (!checkclearopq(cap->oap)) { - if (cap->cmdchar == 'g') + if (cap->cmdchar == TAB && mod_mask == MOD_MASK_CTRL) { + goto_tabpage_lastused(); + return; + } + if (cap->cmdchar == 'g') { pos = movechangelist((int)cap->count1); - else + } else { pos = movemark((int)cap->count1); - if (pos == (pos_T *)-1) { /* jump to other file */ + } + if (pos == (pos_T *)-1) { // jump to other file curwin->w_set_curswant = true; check_cursor(); } else if (pos != NULL) /* can jump */ @@ -6356,8 +6376,8 @@ static void nv_visual(cmdarg_T *cap) if (cap->cmdchar == Ctrl_Q) cap->cmdchar = Ctrl_V; - /* 'v', 'V' and CTRL-V can be used while an operator is pending to make it - * characterwise, linewise, or blockwise. */ + // 'v', 'V' and CTRL-V can be used while an operator is pending to make it + // charwise, linewise, or blockwise. if (cap->oap->op_type != OP_NOP) { motion_force = cap->oap->motion_force = cap->cmdchar; finish_op = false; // operator doesn't finish now but later @@ -6733,6 +6753,22 @@ static void nv_g_cmd(cmdarg_T *cap) curwin->w_set_curswant = true; break; + case 'M': + { + const char_u *const ptr = get_cursor_line_ptr(); + + oap->motion_type = kMTCharWise; + oap->inclusive = false; + i = (int)mb_string2cells_len(ptr, STRLEN(ptr)); + if (cap->count0 > 0 && cap->count0 <= 100) { + coladvance((colnr_T)(i * cap->count0 / 100)); + } else { + coladvance((colnr_T)(i / 2)); + } + curwin->w_set_curswant = true; + } + break; + case '_': /* "g_": to the last non-blank character in the line or <count> lines * downward. */ @@ -6797,10 +6833,14 @@ static void nv_g_cmd(cmdarg_T *cap) } else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false) clearopbeep(oap); } else { + if (cap->count1 > 1) { + // if it fails, let the cursor still move to the last char + cursor_down(cap->count1 - 1, false); + } i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1; coladvance((colnr_T)i); - /* Make sure we stick in this column. */ + // Make sure we stick in this column. validate_virtcol(); curwin->w_curswant = curwin->w_virtcol; curwin->w_set_curswant = false; @@ -7019,6 +7059,11 @@ static void nv_g_cmd(cmdarg_T *cap) if (!checkclearop(oap)) goto_tabpage(-(int)cap->count1); break; + case TAB: + if (!checkclearop(oap)) { + goto_tabpage_lastused(); + } + break; case '+': case '-': /* "g+" and "g-": undo or redo along the timeline */ @@ -7119,10 +7164,15 @@ static void nv_tilde(cmdarg_T *cap) { if (!p_to && !VIsual_active - && cap->oap->op_type != OP_TILDE) + && cap->oap->op_type != OP_TILDE) { + if (bt_prompt(curbuf) && !prompt_curpos_editable()) { + clearopbeep(cap->oap); + return; + } n_swapchar(cap); - else + } else { nv_operator(cap); + } } /* @@ -7135,6 +7185,12 @@ static void nv_operator(cmdarg_T *cap) op_type = get_op_type(cap->cmdchar, cap->nchar); + if (bt_prompt(curbuf) && op_is_change(op_type) + && !prompt_curpos_editable()) { + clearopbeep(cap->oap); + return; + } + if (op_type == cap->oap->op_type) /* double operator works on lines */ nv_lineop(cap); else if (!checkclearop(cap->oap)) { @@ -7320,10 +7376,9 @@ static void adjust_cursor(oparg_T *oap) && (!VIsual_active || *p_sel == 'o') && !virtual_active() && (ve_flags & VE_ONEMORE) == 0 ) { - --curwin->w_cursor.col; - /* prevent cursor from moving on the trail byte */ - if (has_mbyte) - mb_adjust_cursor(); + curwin->w_cursor.col--; + // prevent cursor from moving on the trail byte + mb_adjust_cursor(); oap->inclusive = true; } } @@ -7350,10 +7405,7 @@ static void adjust_for_sel(cmdarg_T *cap) { if (VIsual_active && cap->oap->inclusive && *p_sel == 'e' && gchar_cursor() != NUL && lt(VIsual, curwin->w_cursor)) { - if (has_mbyte) - inc_cursor(); - else - ++curwin->w_cursor.col; + inc_cursor(); cap->oap->inclusive = false; } } @@ -7799,8 +7851,11 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) clearop(cap->oap); assert(cap->opcount >= 0); nv_diffgetput(true, (size_t)cap->opcount); - } else + } else { clearopbeep(cap->oap); + } + } else if (bt_prompt(curbuf) && !prompt_curpos_editable()) { + clearopbeep(cap->oap); } else { if (fix_indent) { dir = (cap->cmdchar == ']' && cap->nchar == 'p') @@ -7812,8 +7867,9 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) ? BACKWARD : FORWARD; } prep_redo_cmd(cap); - if (cap->cmdchar == 'g') + if (cap->cmdchar == 'g') { flags |= PUT_CURSEND; + } if (VIsual_active) { /* Putting in Visual mode: The put text replaces the selected @@ -7851,15 +7907,17 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) cap->oap->regname = regname; } - /* When deleted a linewise Visual area, put the register as - * lines to avoid it joined with the next line. When deletion was - * characterwise, split a line when putting lines. */ - if (VIsual_mode == 'V') + // When deleted a linewise Visual area, put the register as + // lines to avoid it joined with the next line. When deletion was + // charwise, split a line when putting lines. + if (VIsual_mode == 'V') { flags |= PUT_LINE; - else if (VIsual_mode == 'v') + } else if (VIsual_mode == 'v') { flags |= PUT_LINE_SPLIT; - if (VIsual_mode == Ctrl_V && dir == FORWARD) + } + if (VIsual_mode == Ctrl_V && dir == FORWARD) { flags |= PUT_LINE_FORWARD; + } dir = BACKWARD; if ((VIsual_mode != 'V' && curwin->w_cursor.col < curbuf->b_op_start.col) @@ -7917,10 +7975,14 @@ static void nv_open(cmdarg_T *cap) clearop(cap->oap); assert(cap->opcount >= 0); nv_diffgetput(false, (size_t)cap->opcount); - } else if (VIsual_active) /* switch start and end of visual */ + } else if (VIsual_active) { + // switch start and end of visual/ v_swap_corners(cap->cmdchar); - else + } else if (bt_prompt(curbuf)) { + clearopbeep(cap->oap); + } else { n_opencmd(cap); + } } // Calculate start/end virtual columns for operating in block mode. @@ -7941,9 +8003,7 @@ static void get_op_vcol( oap->motion_type = kMTBlockWise; // prevent from moving onto a trail byte - if (has_mbyte) { - mark_mb_adjustpos(curwin->w_buffer, &oap->end); - } + mark_mb_adjustpos(curwin->w_buffer, &oap->end); getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); if (!redo_VIsual_busy) { |