diff options
-rw-r--r-- | src/nvim/globals.h | 6 | ||||
-rw-r--r-- | src/nvim/normal.c | 843 | ||||
-rw-r--r-- | src/nvim/ops.c | 794 |
3 files changed, 811 insertions, 832 deletions
diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 7ae7b65702..b2422fd531 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -4,6 +4,7 @@ #include <inttypes.h> #include <stdbool.h> +#include "nvim/ascii.h" #include "nvim/event/loop.h" #include "nvim/ex_eval.h" #include "nvim/iconv.h" @@ -533,6 +534,11 @@ EXTERN int VIsual_mode INIT(= 'v'); /// true when redoing Visual. EXTERN int redo_VIsual_busy INIT(= false); +// The Visual area is remembered for reselection. +EXTERN int resel_VIsual_mode INIT(= NUL); // 'v', 'V', or Ctrl-V +EXTERN linenr_T resel_VIsual_line_count; // number of lines +EXTERN colnr_T resel_VIsual_vcol; // nr of cols or end col + /// When pasting text with the middle mouse button in visual mode with /// restart_edit set, remember where it started so we can set Insstart. EXTERN pos_T where_paste_started; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index f2b272a13f..9332c55b5f 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -31,8 +31,8 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" +#include "nvim/globals.h" #include "nvim/indent.h" -#include "nvim/indent_c.h" #include "nvim/keymap.h" #include "nvim/log.h" #include "nvim/main.h" @@ -84,12 +84,6 @@ typedef struct normal_state { pos_T old_pos; } NormalState; -/* - * The Visual area is remembered for reselection. - */ -static int resel_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V -static linenr_T resel_VIsual_line_count; // number of lines -static colnr_T resel_VIsual_vcol; // nr of cols or end col static int VIsual_mode_orig = NUL; // saved Visual mode @@ -1455,758 +1449,6 @@ static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount) *set_prevcount = false; // only set v:prevcount once } -// Handle an operator after Visual mode or when the movement is finished. -// "gui_yank" is true when yanking text for the clipboard. -void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) -{ - oparg_T *oap = cap->oap; - pos_T old_cursor; - bool empty_region_error; - int restart_edit_save; - int lbr_saved = curwin->w_p_lbr; - - - // The visual area is remembered for redo - static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V - static linenr_T redo_VIsual_line_count; // number of lines - static colnr_T redo_VIsual_vcol; // number of cols or end column - static long redo_VIsual_count; // count for Visual operator - static int redo_VIsual_arg; // extra argument - bool include_line_break = false; - - old_cursor = curwin->w_cursor; - - /* - * If an operation is pending, handle it... - */ - if ((finish_op - || VIsual_active) - && oap->op_type != OP_NOP) { - // Yank can be redone when 'y' is in 'cpoptions', but not when yanking - // for the clipboard. - const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank; - - // Avoid a problem with unwanted linebreaks in block mode - if (curwin->w_p_lbr) { - curwin->w_valid &= ~VALID_VIRTCOL; - } - curwin->w_p_lbr = false; - oap->is_VIsual = VIsual_active; - if (oap->motion_force == 'V') { - oap->motion_type = kMTLineWise; - } else if (oap->motion_force == 'v') { - // If the motion was linewise, "inclusive" will not have been set. - // Use "exclusive" to be consistent. Makes "dvj" work nice. - if (oap->motion_type == kMTLineWise) { - oap->inclusive = false; - } else if (oap->motion_type == kMTCharWise) { - // 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 charwise motion into Visual block mode. - if (!VIsual_active) { - VIsual_active = true; - VIsual = oap->start; - } - VIsual_mode = Ctrl_V; - VIsual_select = false; - VIsual_reselect = false; - } - - // Only redo yank when 'y' flag is in 'cpoptions'. - // Never redo "zf" (define fold). - if ((redo_yank || oap->op_type != OP_YANK) - && ((!VIsual_active || oap->motion_force) - // Also redo Operator-pending Visual mode mappings. - || ((cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) - && oap->op_type != OP_COLON)) - && cap->cmdchar != 'D' - && oap->op_type != OP_FOLD - && oap->op_type != OP_FOLDOPEN - && oap->op_type != OP_FOLDOPENREC - && oap->op_type != OP_FOLDCLOSE - && oap->op_type != OP_FOLDCLOSEREC - && oap->op_type != OP_FOLDDEL - && oap->op_type != OP_FOLDDELREC) { - prep_redo(oap->regname, cap->count0, - get_op_char(oap->op_type), get_extra_op_char(oap->op_type), - oap->motion_force, cap->cmdchar, cap->nchar); - if (cap->cmdchar == '/' || cap->cmdchar == '?') { // was a search - /* - * If 'cpoptions' does not contain 'r', insert the search - * pattern to really repeat the same command. - */ - if (vim_strchr(p_cpo, CPO_REDO) == NULL) { - AppendToRedobuffLit(cap->searchbuf, -1); - } - AppendToRedobuff(NL_STR); - } else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) { - // do_cmdline() has stored the first typed line in - // "repeat_cmdline". When several lines are typed repeating - // won't be possible. - if (repeat_cmdline == NULL) { - ResetRedobuff(); - } else { - AppendToRedobuffLit(repeat_cmdline, -1); - AppendToRedobuff(NL_STR); - XFREE_CLEAR(repeat_cmdline); - } - } - } - - if (redo_VIsual_busy) { - /* Redo of an operation on a Visual area. Use the same size from - * redo_VIsual_line_count and redo_VIsual_vcol. */ - oap->start = curwin->w_cursor; - curwin->w_cursor.lnum += redo_VIsual_line_count - 1; - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - } - VIsual_mode = redo_VIsual_mode; - if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') { - if (VIsual_mode == 'v') { - if (redo_VIsual_line_count <= 1) { - validate_virtcol(); - curwin->w_curswant = - curwin->w_virtcol + redo_VIsual_vcol - 1; - } else { - curwin->w_curswant = redo_VIsual_vcol; - } - } else { - curwin->w_curswant = MAXCOL; - } - coladvance(curwin->w_curswant); - } - cap->count0 = redo_VIsual_count; - cap->count1 = (cap->count0 == 0 ? 1 : cap->count0); - } else if (VIsual_active) { - if (!gui_yank) { - // Save the current VIsual area for '< and '> marks, and "gv" - curbuf->b_visual.vi_start = VIsual; - curbuf->b_visual.vi_end = curwin->w_cursor; - curbuf->b_visual.vi_mode = VIsual_mode; - if (VIsual_mode_orig != NUL) { - curbuf->b_visual.vi_mode = VIsual_mode_orig; - VIsual_mode_orig = NUL; - } - curbuf->b_visual.vi_curswant = curwin->w_curswant; - curbuf->b_visual_mode_eval = VIsual_mode; - } - - // In Select mode, a linewise selection is operated upon like a - // charwise selection. - // Special case: gH<Del> deletes the last line. - if (VIsual_select && VIsual_mode == 'V' - && cap->oap->op_type != OP_DELETE) { - if (lt(VIsual, curwin->w_cursor)) { - VIsual.col = 0; - curwin->w_cursor.col = - (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum)); - } else { - curwin->w_cursor.col = 0; - VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum)); - } - VIsual_mode = 'v'; - } - /* If 'selection' is "exclusive", backup one character for - * charwise selections. */ - else if (VIsual_mode == 'v') { - include_line_break = - unadjust_for_sel(); - } - - oap->start = VIsual; - if (VIsual_mode == 'V') { - oap->start.col = 0; - oap->start.coladd = 0; - } - } - - /* - * Set oap->start to the first position of the operated text, oap->end - * to the end of the operated text. w_cursor is equal to oap->start. - */ - if (lt(oap->start, curwin->w_cursor)) { - // Include folded lines completely. - if (!VIsual_active) { - if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) { - oap->start.col = 0; - } - 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; - - /* w_virtcol may have been updated; if the cursor goes back to its - * previous position w_virtcol becomes invalid and isn't updated - * automatically. */ - curwin->w_valid &= ~VALID_VIRTCOL; - } else { - // Include folded lines completely. - if (!VIsual_active && oap->motion_type == kMTLineWise) { - if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, - NULL)) { - curwin->w_cursor.col = 0; - } - if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) { - oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum)); - } - } - oap->end = oap->start; - oap->start = curwin->w_cursor; - } - - // Just in case lines were deleted that make the position invalid. - check_pos(curwin->w_buffer, &oap->end); - oap->line_count = oap->end.lnum - oap->start.lnum + 1; - - // Set "virtual_op" before resetting VIsual_active. - virtual_op = virtual_active(); - - if (VIsual_active || redo_VIsual_busy) { - get_op_vcol(oap, redo_VIsual_vcol, true); - - if (!redo_VIsual_busy && !gui_yank) { - /* - * Prepare to reselect and redo Visual: this is based on the - * size of the Visual text - */ - resel_VIsual_mode = VIsual_mode; - if (curwin->w_curswant == MAXCOL) { - resel_VIsual_vcol = MAXCOL; - } else { - if (VIsual_mode != Ctrl_V) { - getvvcol(curwin, &(oap->end), - NULL, NULL, &oap->end_vcol); - } - if (VIsual_mode == Ctrl_V || oap->line_count <= 1) { - if (VIsual_mode != Ctrl_V) { - getvvcol(curwin, &(oap->start), - &oap->start_vcol, NULL, NULL); - } - resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1; - } else { - resel_VIsual_vcol = oap->end_vcol; - } - } - resel_VIsual_line_count = oap->line_count; - } - - // can't redo yank (unless 'y' is in 'cpoptions') and ":" - if ((redo_yank || oap->op_type != OP_YANK) - && oap->op_type != OP_COLON - && oap->op_type != OP_FOLD - && oap->op_type != OP_FOLDOPEN - && oap->op_type != OP_FOLDOPENREC - && oap->op_type != OP_FOLDCLOSE - && oap->op_type != OP_FOLDCLOSEREC - && oap->op_type != OP_FOLDDEL - && oap->op_type != OP_FOLDDELREC - && oap->motion_force == NUL) { - /* Prepare for redoing. Only use the nchar field for "r", - * otherwise it might be the second char of the operator. */ - if (cap->cmdchar == 'g' && (cap->nchar == 'n' - || cap->nchar == 'N')) { - prep_redo(oap->regname, cap->count0, - get_op_char(oap->op_type), get_extra_op_char(oap->op_type), - oap->motion_force, cap->cmdchar, cap->nchar); - } else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) { - int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL; - - // reverse what nv_replace() did - if (nchar == REPLACE_CR_NCHAR) { - nchar = CAR; - } else if (nchar == REPLACE_NL_NCHAR) { - nchar = NL; - } - prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type), - get_extra_op_char(oap->op_type), nchar); - } - if (!redo_VIsual_busy) { - redo_VIsual_mode = resel_VIsual_mode; - redo_VIsual_vcol = resel_VIsual_vcol; - redo_VIsual_line_count = resel_VIsual_line_count; - redo_VIsual_count = cap->count0; - redo_VIsual_arg = cap->arg; - } - } - - // oap->inclusive defaults to true. - // If oap->end is on a NUL (empty line) oap->inclusive becomes - // false. This makes "d}P" and "v}dP" work the same. - if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) { - oap->inclusive = true; - } - if (VIsual_mode == 'V') { - oap->motion_type = kMTLineWise; - } else if (VIsual_mode == 'v') { - oap->motion_type = kMTCharWise; - if (*ml_get_pos(&(oap->end)) == NUL - && (include_line_break || !virtual_op)) { - oap->inclusive = false; - // Try to include the newline, unless it's an operator - // that works on lines only. - if (*p_sel != 'o' - && !op_on_lines(oap->op_type) - && oap->end.lnum < curbuf->b_ml.ml_line_count) { - oap->end.lnum++; - oap->end.col = 0; - oap->end.coladd = 0; - oap->line_count++; - } - } - } - - redo_VIsual_busy = false; - - /* - * Switch Visual off now, so screen updating does - * not show inverted text when the screen is redrawn. - * With OP_YANK and sometimes with OP_COLON and OP_FILTER there is - * no screen redraw, so it is done here to remove the inverted - * part. - */ - if (!gui_yank) { - VIsual_active = false; - setmouse(); - mouse_dragging = 0; - may_clear_cmdline(); - if ((oap->op_type == OP_YANK - || oap->op_type == OP_COLON - || oap->op_type == OP_FUNCTION - || oap->op_type == OP_FILTER) - && oap->motion_force == NUL) { - // Make sure redrawing is correct. - curwin->w_p_lbr = lbr_saved; - redraw_curbuf_later(INVERTED); - } - } - } - - // 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; - - /* - * oap->empty is set when start and end are the same. The inclusive - * flag affects this too, unless yanking and the end is on a NUL. - */ - oap->empty = (oap->motion_type != kMTLineWise - && (!oap->inclusive - || (oap->op_type == OP_YANK - && gchar_pos(&oap->end) == NUL)) - && equalpos(oap->start, oap->end) - && !(virtual_op && oap->start.coladd != oap->end.coladd) - ); - /* - * For delete, change and yank, it's an error to operate on an - * empty region, when 'E' included in 'cpoptions' (Vi compatible). - */ - empty_region_error = (oap->empty - && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL); - - /* Force a redraw when operating on an empty Visual region, when - * 'modifiable is off or creating a fold. */ - if (oap->is_VIsual && (oap->empty || !MODIFIABLE(curbuf) - || oap->op_type == OP_FOLD - )) { - curwin->w_p_lbr = lbr_saved; - redraw_curbuf_later(INVERTED); - } - - /* - * If the end of an operator is in column one while oap->motion_type - * is kMTCharWise and oap->inclusive is false, we put op_end after the last - * character in the previous line. If op_start is on or before the - * first non-blank in the line, the operator becomes linewise - * (strange, but that's the way vi does it). - */ - if (oap->motion_type == kMTCharWise - && oap->inclusive == false - && !(cap->retval & CA_NO_ADJ_OP_END) - && oap->end.col == 0 - && (!oap->is_VIsual || *p_sel == 'o') - && oap->line_count > 1) { - oap->end_adjusted = true; // remember that we did this - oap->line_count--; - oap->end.lnum--; - if (inindent(0)) { - oap->motion_type = kMTLineWise; - } else { - oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); - if (oap->end.col) { - --oap->end.col; - oap->inclusive = true; - } - } - } else { - oap->end_adjusted = false; - } - - switch (oap->op_type) { - case OP_LSHIFT: - case OP_RSHIFT: - op_shift(oap, true, - oap->is_VIsual ? (int)cap->count1 : - 1); - auto_format(false, true); - break; - - case OP_JOIN_NS: - case OP_JOIN: - if (oap->line_count < 2) { - oap->line_count = 2; - } - if (curwin->w_cursor.lnum + oap->line_count - 1 > - curbuf->b_ml.ml_line_count) { - beep_flush(); - } else { - do_join((size_t)oap->line_count, oap->op_type == OP_JOIN, - true, true, true); - auto_format(false, true); - } - break; - - case OP_DELETE: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - (void)op_delete(oap); - // save cursor line for undo if it wasn't saved yet - if (oap->motion_type == kMTLineWise - && has_format_option(FO_AUTO) - && u_save_cursor() == OK) { - auto_format(false, true); - } - } - break; - - case OP_YANK: - if (empty_region_error) { - if (!gui_yank) { - vim_beep(BO_OPER); - CancelRedo(); - } - } else { - curwin->w_p_lbr = lbr_saved; - oap->excl_tr_ws = cap->cmdchar == 'z'; - (void)op_yank(oap, !gui_yank, false); - } - check_cursor_col(); - break; - - case OP_CHANGE: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - /* This is a new edit command, not a restart. Need to - * remember it to make 'insertmode' work with mappings for - * Visual mode. But do this only once and not when typed and - * 'insertmode' isn't set. */ - if (p_im || !KeyTyped) { - restart_edit_save = restart_edit; - } else { - restart_edit_save = 0; - } - restart_edit = 0; - - // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; - - // Reset finish_op now, don't want it set inside edit(). - finish_op = false; - if (op_change(oap)) { // will call edit() - cap->retval |= CA_COMMAND_BUSY; - } - if (restart_edit == 0) { - restart_edit = restart_edit_save; - } - } - break; - - case OP_FILTER: - if (vim_strchr(p_cpo, CPO_FILTER) != NULL) { - AppendToRedobuff("!\r"); // Use any last used !cmd. - } else { - bangredo = true; // do_bang() will put cmd in redo buffer. - } - FALLTHROUGH; - - case OP_INDENT: - case OP_COLON: - - /* - * If 'equalprg' is empty, do the indenting internally. - */ - if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) { - if (curbuf->b_p_lisp) { - op_reindent(oap, get_lisp_indent); - break; - } - op_reindent(oap, - *curbuf->b_p_inde != NUL ? get_expr_indent : - get_c_indent); - break; - } - - op_colon(oap); - break; - - case OP_TILDE: - case OP_UPPER: - case OP_LOWER: - case OP_ROT13: - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - op_tilde(oap); - } - check_cursor_col(); - break; - - case OP_FORMAT: - if (*curbuf->b_p_fex != NUL) { - op_formatexpr(oap); // use expression - } else { - if (*p_fp != NUL || *curbuf->b_p_fp != NUL) { - op_colon(oap); // use external command - } else { - op_format(oap, false); // use internal function - } - } - break; - - case OP_FORMAT2: - op_format(oap, true); // use internal function - break; - - case OP_FUNCTION: - // Restore linebreak, so that when the user edits it looks as - // before. - curwin->w_p_lbr = lbr_saved; - op_function(oap); // call 'operatorfunc' - break; - - case OP_INSERT: - case OP_APPEND: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - /* This is a new edit command, not a restart. Need to - * remember it to make 'insertmode' work with mappings for - * Visual mode. But do this only once. */ - restart_edit_save = restart_edit; - restart_edit = 0; - - // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; - - op_insert(oap, cap->count1); - - // Reset linebreak, so that formatting works correctly. - curwin->w_p_lbr = false; - - /* TODO: when inserting in several lines, should format all - * the lines. */ - auto_format(false, true); - - if (restart_edit == 0) { - restart_edit = restart_edit_save; - } else { - cap->retval |= CA_COMMAND_BUSY; - } - } - break; - - case OP_REPLACE: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; - - op_replace(oap, cap->nchar); - } - break; - - case OP_FOLD: - VIsual_reselect = false; // don't reselect now - foldCreate(curwin, oap->start, oap->end); - break; - - case OP_FOLDOPEN: - case OP_FOLDOPENREC: - case OP_FOLDCLOSE: - case OP_FOLDCLOSEREC: - VIsual_reselect = false; // don't reselect now - opFoldRange(oap->start, oap->end, - oap->op_type == OP_FOLDOPEN - || oap->op_type == OP_FOLDOPENREC, - oap->op_type == OP_FOLDOPENREC - || oap->op_type == OP_FOLDCLOSEREC, - oap->is_VIsual); - break; - - case OP_FOLDDEL: - case OP_FOLDDELREC: - 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: - case OP_NR_SUB: - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - VIsual_active = true; - curwin->w_p_lbr = lbr_saved; - op_addsub(oap, cap->count1, redo_VIsual_arg); - VIsual_active = false; - } - check_cursor_col(); - break; - default: - clearopbeep(oap); - } - virtual_op = kNone; - if (!gui_yank) { - /* - * if 'sol' not set, go back to old column for some commands - */ - if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted - && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT - || oap->op_type == OP_DELETE)) { - curwin->w_p_lbr = false; - coladvance(curwin->w_curswant = old_col); - } - } else { - curwin->w_cursor = old_cursor; - } - clearop(oap); - motion_force = NUL; - } - curwin->w_p_lbr = lbr_saved; -} - -/* - * Handle indent and format operators and visual mode ":". - */ -static void op_colon(oparg_T *oap) -{ - stuffcharReadbuff(':'); - if (oap->is_VIsual) { - stuffReadbuff("'<,'>"); - } else { - // Make the range look nice, so it can be repeated. - if (oap->start.lnum == curwin->w_cursor.lnum) { - stuffcharReadbuff('.'); - } else { - stuffnumReadbuff((long)oap->start.lnum); - } - if (oap->end.lnum != oap->start.lnum) { - stuffcharReadbuff(','); - if (oap->end.lnum == curwin->w_cursor.lnum) { - stuffcharReadbuff('.'); - } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) { - stuffcharReadbuff('$'); - } else if (oap->start.lnum == curwin->w_cursor.lnum) { - stuffReadbuff(".+"); - stuffnumReadbuff(oap->line_count - 1); - } else { - stuffnumReadbuff((long)oap->end.lnum); - } - } - } - if (oap->op_type != OP_COLON) { - stuffReadbuff("!"); - } - if (oap->op_type == OP_INDENT) { - stuffReadbuff((const char *)get_equalprg()); - stuffReadbuff("\n"); - } else if (oap->op_type == OP_FORMAT) { - if (*curbuf->b_p_fp != NUL) { - stuffReadbuff((const char *)curbuf->b_p_fp); - } else if (*p_fp != NUL) { - stuffReadbuff((const char *)p_fp); - } else { - stuffReadbuff("fmt"); - } - stuffReadbuff("\n']"); - } - - /* - * do_cmdline() does the rest - */ -} - -/* - * Handle the "g@" operator: call 'operatorfunc'. - */ -static void op_function(const oparg_T *oap) - FUNC_ATTR_NONNULL_ALL -{ - const TriState save_virtual_op = virtual_op; - const bool save_finish_op = finish_op; - - if (*p_opfunc == NUL) { - emsg(_("E774: 'operatorfunc' is empty")); - } else { - // Set '[ and '] marks to text to be operated on. - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; - if (oap->motion_type != kMTLineWise && !oap->inclusive) { - // Exclude the end position. - decl(&curbuf->b_op_end); - } - - typval_T argv[2]; - argv[0].v_type = VAR_STRING; - argv[1].v_type = VAR_UNKNOWN; - argv[0].vval.v_string = - (char_u *)(((const char *const[]) { - [kMTBlockWise] = "block", - [kMTLineWise] = "line", - [kMTCharWise] = "char", - })[oap->motion_type]); - - // Reset virtual_op so that 'virtualedit' can be changed in the - // function. - virtual_op = kNone; - - // Reset finish_op so that mode() returns the right value. - finish_op = false; - - (void)call_func_retnr(p_opfunc, 1, argv); - - virtual_op = save_virtual_op; - finish_op = save_finish_op; - } -} - // Move the current tab to tab in same column as mouse or to end of the // tabline if there is no tab there. static void move_tab_to_mouse(void) @@ -3096,6 +2338,14 @@ void reset_VIsual(void) } } +void restore_visual_mode(void) +{ + if (VIsual_mode_orig != NUL) { + curbuf->b_visual.vi_mode = VIsual_mode_orig; + VIsual_mode_orig = NUL; + } +} + // Check for a balloon-eval special item to include when searching for an // identifier. When "dir" is BACKWARD "ptr[-1]" must be valid! // Returns true if the character at "*ptr" should be included. @@ -3274,7 +2524,7 @@ 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. */ -static void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5) +void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5) { ResetRedobuff(); if (regname != 0) { // yank from specified buffer @@ -3331,7 +2581,7 @@ static bool checkclearopq(oparg_T *oap) return true; } -static void clearop(oparg_T *oap) +void clearop(oparg_T *oap) { oap->op_type = OP_NOP; oap->regname = 0; @@ -3340,7 +2590,7 @@ static void clearop(oparg_T *oap) motion_force = NUL; } -static void clearopbeep(oparg_T *oap) +void clearopbeep(oparg_T *oap) { clearop(oap); beep_flush(); @@ -3370,7 +2620,7 @@ static void unshift_special(cmdarg_T *cap) /// If the mode is currently displayed clear the command line or update the /// command displayed. -static void may_clear_cmdline(void) +void may_clear_cmdline(void) { if (mode_displayed) { // unshow visual mode later @@ -7738,7 +6988,7 @@ static void adjust_for_sel(cmdarg_T *cap) * Should check VIsual_mode before calling this. * Returns true when backed up to the previous line. */ -static bool unadjust_for_sel(void) +bool unadjust_for_sel(void) { pos_T *pp; @@ -8328,71 +7578,6 @@ static void nv_open(cmdarg_T *cap) } } -/// Calculate start/end virtual columns for operating in block mode. -/// -/// @param initial when true: adjust position for 'selectmode' -static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial) -{ - colnr_T start; - colnr_T end; - - if (VIsual_mode != Ctrl_V - || (!initial && oap->end.col < curwin->w_width_inner)) { - return; - } - - oap->motion_type = kMTBlockWise; - - // prevent from moving onto a trail byte - mark_mb_adjustpos(curwin->w_buffer, &oap->end); - - getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); - if (!redo_VIsual_busy) { - getvvcol(curwin, &(oap->end), &start, NULL, &end); - - if (start < oap->start_vcol) { - oap->start_vcol = start; - } - if (end > oap->end_vcol) { - if (initial && *p_sel == 'e' - && start >= 1 - && start - 1 >= oap->end_vcol) { - oap->end_vcol = start - 1; - } else { - oap->end_vcol = end; - } - } - } - - // if '$' was used, get oap->end_vcol from longest line - if (curwin->w_curswant == MAXCOL) { - curwin->w_cursor.col = MAXCOL; - oap->end_vcol = 0; - for (curwin->w_cursor.lnum = oap->start.lnum; - curwin->w_cursor.lnum <= oap->end.lnum; ++curwin->w_cursor.lnum) { - getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end); - if (end > oap->end_vcol) { - oap->end_vcol = end; - } - } - } else if (redo_VIsual_busy) { - oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1; - } - - // Correct oap->end.col and oap->start.col to be the - // upper-left and lower-right corner of the block area. - // - // (Actually, this does convert column positions into character - // positions) - curwin->w_cursor.lnum = oap->end.lnum; - coladvance(oap->end_vcol); - oap->end = curwin->w_cursor; - - curwin->w_cursor = oap->start; - coladvance(oap->start_vcol); - oap->start = curwin->w_cursor; -} - // Handle an arbitrary event in normal mode static void nv_event(cmdarg_T *cap) { diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 38de24b8ad..52c382028e 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -27,7 +27,9 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" +#include "nvim/globals.h" #include "nvim/indent.h" +#include "nvim/indent_c.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" #include "nvim/macros.h" @@ -37,6 +39,7 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" +#include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/ops.h" @@ -1768,7 +1771,7 @@ static void replace_character(int c) /* * Replace a whole area with one character. */ -int op_replace(oparg_T *oap, int c) +static int op_replace(oparg_T *oap, int c) { int n, numc; int num_chars; @@ -4177,7 +4180,7 @@ static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, in /// Implementation of the format operator 'gq'. /// /// @param keep_cursor keep cursor on same text char -void op_format(oparg_T *oap, int keep_cursor) +static void op_format(oparg_T *oap, int keep_cursor) { long old_line_count = curbuf->b_ml.ml_line_count; @@ -4245,7 +4248,7 @@ void op_format(oparg_T *oap, int keep_cursor) /* * Implementation of the format operator 'gq' for when using 'formatexpr'. */ -void op_formatexpr(oparg_T *oap) +static void op_formatexpr(oparg_T *oap) { if (oap->is_VIsual) { // When there is no change: need to remove the Visual selection @@ -5936,6 +5939,791 @@ void cursor_pos_info(dict_T *dict) } } +// Handle indent and format operators and visual mode ":". +static void op_colon(oparg_T *oap) +{ + stuffcharReadbuff(':'); + if (oap->is_VIsual) { + stuffReadbuff("'<,'>"); + } else { + // Make the range look nice, so it can be repeated. + if (oap->start.lnum == curwin->w_cursor.lnum) { + stuffcharReadbuff('.'); + } else { + stuffnumReadbuff((long)oap->start.lnum); + } + if (oap->end.lnum != oap->start.lnum) { + stuffcharReadbuff(','); + if (oap->end.lnum == curwin->w_cursor.lnum) { + stuffcharReadbuff('.'); + } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) { + stuffcharReadbuff('$'); + } else if (oap->start.lnum == curwin->w_cursor.lnum) { + stuffReadbuff(".+"); + stuffnumReadbuff(oap->line_count - 1); + } else { + stuffnumReadbuff((long)oap->end.lnum); + } + } + } + if (oap->op_type != OP_COLON) { + stuffReadbuff("!"); + } + if (oap->op_type == OP_INDENT) { + stuffReadbuff((const char *)get_equalprg()); + stuffReadbuff("\n"); + } else if (oap->op_type == OP_FORMAT) { + if (*curbuf->b_p_fp != NUL) { + stuffReadbuff((const char *)curbuf->b_p_fp); + } else if (*p_fp != NUL) { + stuffReadbuff((const char *)p_fp); + } else { + stuffReadbuff("fmt"); + } + stuffReadbuff("\n']"); + } + + // do_cmdline() does the rest +} + +// Handle the "g@" operator: call 'operatorfunc'. +static void op_function(const oparg_T *oap) + FUNC_ATTR_NONNULL_ALL +{ + const TriState save_virtual_op = virtual_op; + const bool save_finish_op = finish_op; + + if (*p_opfunc == NUL) { + emsg(_("E774: 'operatorfunc' is empty")); + } else { + // Set '[ and '] marks to text to be operated on. + curbuf->b_op_start = oap->start; + curbuf->b_op_end = oap->end; + if (oap->motion_type != kMTLineWise && !oap->inclusive) { + // Exclude the end position. + decl(&curbuf->b_op_end); + } + + typval_T argv[2]; + argv[0].v_type = VAR_STRING; + argv[1].v_type = VAR_UNKNOWN; + argv[0].vval.v_string = + (char_u *)(((const char *const[]) { + [kMTBlockWise] = "block", + [kMTLineWise] = "line", + [kMTCharWise] = "char", + })[oap->motion_type]); + + // Reset virtual_op so that 'virtualedit' can be changed in the + // function. + virtual_op = kNone; + + // Reset finish_op so that mode() returns the right value. + finish_op = false; + + (void)call_func_retnr(p_opfunc, 1, argv); + + virtual_op = save_virtual_op; + finish_op = save_finish_op; + } +} + +/// Calculate start/end virtual columns for operating in block mode. +/// +/// @param initial when true: adjust position for 'selectmode' +static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial) +{ + colnr_T start; + colnr_T end; + + if (VIsual_mode != Ctrl_V + || (!initial && oap->end.col < curwin->w_width_inner)) { + return; + } + + oap->motion_type = kMTBlockWise; + + // prevent from moving onto a trail byte + mark_mb_adjustpos(curwin->w_buffer, &oap->end); + + getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); + if (!redo_VIsual_busy) { + getvvcol(curwin, &(oap->end), &start, NULL, &end); + + if (start < oap->start_vcol) { + oap->start_vcol = start; + } + if (end > oap->end_vcol) { + if (initial && *p_sel == 'e' + && start >= 1 + && start - 1 >= oap->end_vcol) { + oap->end_vcol = start - 1; + } else { + oap->end_vcol = end; + } + } + } + + // if '$' was used, get oap->end_vcol from longest line + if (curwin->w_curswant == MAXCOL) { + curwin->w_cursor.col = MAXCOL; + oap->end_vcol = 0; + for (curwin->w_cursor.lnum = oap->start.lnum; + curwin->w_cursor.lnum <= oap->end.lnum; curwin->w_cursor.lnum++) { + getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end); + if (end > oap->end_vcol) { + oap->end_vcol = end; + } + } + } else if (redo_VIsual_busy) { + oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1; + } + + // Correct oap->end.col and oap->start.col to be the + // upper-left and lower-right corner of the block area. + // + // (Actually, this does convert column positions into character + // positions) + curwin->w_cursor.lnum = oap->end.lnum; + coladvance(oap->end_vcol); + oap->end = curwin->w_cursor; + + curwin->w_cursor = oap->start; + coladvance(oap->start_vcol); + oap->start = curwin->w_cursor; +} + +// Handle an operator after Visual mode or when the movement is finished. +// "gui_yank" is true when yanking text for the clipboard. +void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) +{ + oparg_T *oap = cap->oap; + pos_T old_cursor; + bool empty_region_error; + int restart_edit_save; + int lbr_saved = curwin->w_p_lbr; + + + // The visual area is remembered for redo + static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V + static linenr_T redo_VIsual_line_count; // number of lines + static colnr_T redo_VIsual_vcol; // number of cols or end column + static long redo_VIsual_count; // count for Visual operator + static int redo_VIsual_arg; // extra argument + bool include_line_break = false; + + old_cursor = curwin->w_cursor; + + // If an operation is pending, handle it... + if ((finish_op + || VIsual_active) + && oap->op_type != OP_NOP) { + // Yank can be redone when 'y' is in 'cpoptions', but not when yanking + // for the clipboard. + const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank; + + // Avoid a problem with unwanted linebreaks in block mode + if (curwin->w_p_lbr) { + curwin->w_valid &= ~VALID_VIRTCOL; + } + curwin->w_p_lbr = false; + oap->is_VIsual = VIsual_active; + if (oap->motion_force == 'V') { + oap->motion_type = kMTLineWise; + } else if (oap->motion_force == 'v') { + // If the motion was linewise, "inclusive" will not have been set. + // Use "exclusive" to be consistent. Makes "dvj" work nice. + if (oap->motion_type == kMTLineWise) { + oap->inclusive = false; + } else if (oap->motion_type == kMTCharWise) { + // 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 charwise motion into Visual block mode. + if (!VIsual_active) { + VIsual_active = true; + VIsual = oap->start; + } + VIsual_mode = Ctrl_V; + VIsual_select = false; + VIsual_reselect = false; + } + + // Only redo yank when 'y' flag is in 'cpoptions'. + // Never redo "zf" (define fold). + if ((redo_yank || oap->op_type != OP_YANK) + && ((!VIsual_active || oap->motion_force) + // Also redo Operator-pending Visual mode mappings. + || ((cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) + && oap->op_type != OP_COLON)) + && cap->cmdchar != 'D' + && oap->op_type != OP_FOLD + && oap->op_type != OP_FOLDOPEN + && oap->op_type != OP_FOLDOPENREC + && oap->op_type != OP_FOLDCLOSE + && oap->op_type != OP_FOLDCLOSEREC + && oap->op_type != OP_FOLDDEL + && oap->op_type != OP_FOLDDELREC) { + prep_redo(oap->regname, cap->count0, + get_op_char(oap->op_type), get_extra_op_char(oap->op_type), + oap->motion_force, cap->cmdchar, cap->nchar); + if (cap->cmdchar == '/' || cap->cmdchar == '?') { // was a search + // If 'cpoptions' does not contain 'r', insert the search + // pattern to really repeat the same command. + if (vim_strchr(p_cpo, CPO_REDO) == NULL) { + AppendToRedobuffLit(cap->searchbuf, -1); + } + AppendToRedobuff(NL_STR); + } else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) { + // do_cmdline() has stored the first typed line in + // "repeat_cmdline". When several lines are typed repeating + // won't be possible. + if (repeat_cmdline == NULL) { + ResetRedobuff(); + } else { + AppendToRedobuffLit(repeat_cmdline, -1); + AppendToRedobuff(NL_STR); + XFREE_CLEAR(repeat_cmdline); + } + } + } + + if (redo_VIsual_busy) { + // Redo of an operation on a Visual area. Use the same size from + // redo_VIsual_line_count and redo_VIsual_vcol. + oap->start = curwin->w_cursor; + curwin->w_cursor.lnum += redo_VIsual_line_count - 1; + if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { + curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + } + VIsual_mode = redo_VIsual_mode; + if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') { + if (VIsual_mode == 'v') { + if (redo_VIsual_line_count <= 1) { + validate_virtcol(); + curwin->w_curswant = + curwin->w_virtcol + redo_VIsual_vcol - 1; + } else { + curwin->w_curswant = redo_VIsual_vcol; + } + } else { + curwin->w_curswant = MAXCOL; + } + coladvance(curwin->w_curswant); + } + cap->count0 = redo_VIsual_count; + cap->count1 = (cap->count0 == 0 ? 1 : cap->count0); + } else if (VIsual_active) { + if (!gui_yank) { + // Save the current VIsual area for '< and '> marks, and "gv" + curbuf->b_visual.vi_start = VIsual; + curbuf->b_visual.vi_end = curwin->w_cursor; + curbuf->b_visual.vi_mode = VIsual_mode; + restore_visual_mode(); + curbuf->b_visual.vi_curswant = curwin->w_curswant; + curbuf->b_visual_mode_eval = VIsual_mode; + } + + // In Select mode, a linewise selection is operated upon like a + // charwise selection. + // Special case: gH<Del> deletes the last line. + if (VIsual_select && VIsual_mode == 'V' + && cap->oap->op_type != OP_DELETE) { + if (lt(VIsual, curwin->w_cursor)) { + VIsual.col = 0; + curwin->w_cursor.col = + (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum)); + } else { + curwin->w_cursor.col = 0; + VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum)); + } + VIsual_mode = 'v'; + } else if (VIsual_mode == 'v') { + // If 'selection' is "exclusive", backup one character for + // charwise selections. + include_line_break = + unadjust_for_sel(); + } + + oap->start = VIsual; + if (VIsual_mode == 'V') { + oap->start.col = 0; + oap->start.coladd = 0; + } + } + + // Set oap->start to the first position of the operated text, oap->end + // to the end of the operated text. w_cursor is equal to oap->start. + if (lt(oap->start, curwin->w_cursor)) { + // Include folded lines completely. + if (!VIsual_active) { + if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) { + oap->start.col = 0; + } + 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; + + // w_virtcol may have been updated; if the cursor goes back to its + // previous position w_virtcol becomes invalid and isn't updated + // automatically. + curwin->w_valid &= ~VALID_VIRTCOL; + } else { + // Include folded lines completely. + if (!VIsual_active && oap->motion_type == kMTLineWise) { + if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, + NULL)) { + curwin->w_cursor.col = 0; + } + if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) { + oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum)); + } + } + oap->end = oap->start; + oap->start = curwin->w_cursor; + } + + // Just in case lines were deleted that make the position invalid. + check_pos(curwin->w_buffer, &oap->end); + oap->line_count = oap->end.lnum - oap->start.lnum + 1; + + // Set "virtual_op" before resetting VIsual_active. + virtual_op = virtual_active(); + + if (VIsual_active || redo_VIsual_busy) { + get_op_vcol(oap, redo_VIsual_vcol, true); + + if (!redo_VIsual_busy && !gui_yank) { + // Prepare to reselect and redo Visual: this is based on the + // size of the Visual text + resel_VIsual_mode = VIsual_mode; + if (curwin->w_curswant == MAXCOL) { + resel_VIsual_vcol = MAXCOL; + } else { + if (VIsual_mode != Ctrl_V) { + getvvcol(curwin, &(oap->end), + NULL, NULL, &oap->end_vcol); + } + if (VIsual_mode == Ctrl_V || oap->line_count <= 1) { + if (VIsual_mode != Ctrl_V) { + getvvcol(curwin, &(oap->start), + &oap->start_vcol, NULL, NULL); + } + resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1; + } else { + resel_VIsual_vcol = oap->end_vcol; + } + } + resel_VIsual_line_count = oap->line_count; + } + + // can't redo yank (unless 'y' is in 'cpoptions') and ":" + if ((redo_yank || oap->op_type != OP_YANK) + && oap->op_type != OP_COLON + && oap->op_type != OP_FOLD + && oap->op_type != OP_FOLDOPEN + && oap->op_type != OP_FOLDOPENREC + && oap->op_type != OP_FOLDCLOSE + && oap->op_type != OP_FOLDCLOSEREC + && oap->op_type != OP_FOLDDEL + && oap->op_type != OP_FOLDDELREC + && oap->motion_force == NUL) { + // Prepare for redoing. Only use the nchar field for "r", + // otherwise it might be the second char of the operator. + if (cap->cmdchar == 'g' && (cap->nchar == 'n' + || cap->nchar == 'N')) { + prep_redo(oap->regname, cap->count0, + get_op_char(oap->op_type), get_extra_op_char(oap->op_type), + oap->motion_force, cap->cmdchar, cap->nchar); + } else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) { + int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL; + + // reverse what nv_replace() did + if (nchar == REPLACE_CR_NCHAR) { + nchar = CAR; + } else if (nchar == REPLACE_NL_NCHAR) { + nchar = NL; + } + prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type), + get_extra_op_char(oap->op_type), nchar); + } + if (!redo_VIsual_busy) { + redo_VIsual_mode = resel_VIsual_mode; + redo_VIsual_vcol = resel_VIsual_vcol; + redo_VIsual_line_count = resel_VIsual_line_count; + redo_VIsual_count = cap->count0; + redo_VIsual_arg = cap->arg; + } + } + + // oap->inclusive defaults to true. + // If oap->end is on a NUL (empty line) oap->inclusive becomes + // false. This makes "d}P" and "v}dP" work the same. + if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) { + oap->inclusive = true; + } + if (VIsual_mode == 'V') { + oap->motion_type = kMTLineWise; + } else if (VIsual_mode == 'v') { + oap->motion_type = kMTCharWise; + if (*ml_get_pos(&(oap->end)) == NUL + && (include_line_break || !virtual_op)) { + oap->inclusive = false; + // Try to include the newline, unless it's an operator + // that works on lines only. + if (*p_sel != 'o' + && !op_on_lines(oap->op_type) + && oap->end.lnum < curbuf->b_ml.ml_line_count) { + oap->end.lnum++; + oap->end.col = 0; + oap->end.coladd = 0; + oap->line_count++; + } + } + } + + redo_VIsual_busy = false; + + // Switch Visual off now, so screen updating does + // not show inverted text when the screen is redrawn. + // With OP_YANK and sometimes with OP_COLON and OP_FILTER there is + // no screen redraw, so it is done here to remove the inverted + // part. + if (!gui_yank) { + VIsual_active = false; + setmouse(); + mouse_dragging = 0; + may_clear_cmdline(); + if ((oap->op_type == OP_YANK + || oap->op_type == OP_COLON + || oap->op_type == OP_FUNCTION + || oap->op_type == OP_FILTER) + && oap->motion_force == NUL) { + // Make sure redrawing is correct. + curwin->w_p_lbr = lbr_saved; + redraw_curbuf_later(INVERTED); + } + } + } + + // 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; + + // oap->empty is set when start and end are the same. The inclusive + // flag affects this too, unless yanking and the end is on a NUL. + oap->empty = (oap->motion_type != kMTLineWise + && (!oap->inclusive + || (oap->op_type == OP_YANK + && gchar_pos(&oap->end) == NUL)) + && equalpos(oap->start, oap->end) + && !(virtual_op && oap->start.coladd != oap->end.coladd)); + // For delete, change and yank, it's an error to operate on an + // empty region, when 'E' included in 'cpoptions' (Vi compatible). + empty_region_error = (oap->empty + && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL); + + // Force a redraw when operating on an empty Visual region, when + // 'modifiable is off or creating a fold. + if (oap->is_VIsual && (oap->empty || !MODIFIABLE(curbuf) + || oap->op_type == OP_FOLD)) { + curwin->w_p_lbr = lbr_saved; + redraw_curbuf_later(INVERTED); + } + + // If the end of an operator is in column one while oap->motion_type + // is kMTCharWise and oap->inclusive is false, we put op_end after the last + // character in the previous line. If op_start is on or before the + // first non-blank in the line, the operator becomes linewise + // (strange, but that's the way vi does it). + if (oap->motion_type == kMTCharWise + && oap->inclusive == false + && !(cap->retval & CA_NO_ADJ_OP_END) + && oap->end.col == 0 + && (!oap->is_VIsual || *p_sel == 'o') + && oap->line_count > 1) { + oap->end_adjusted = true; // remember that we did this + oap->line_count--; + oap->end.lnum--; + if (inindent(0)) { + oap->motion_type = kMTLineWise; + } else { + oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); + if (oap->end.col) { + oap->end.col--; + oap->inclusive = true; + } + } + } else { + oap->end_adjusted = false; + } + + switch (oap->op_type) { + case OP_LSHIFT: + case OP_RSHIFT: + op_shift(oap, true, + oap->is_VIsual ? (int)cap->count1 : + 1); + auto_format(false, true); + break; + + case OP_JOIN_NS: + case OP_JOIN: + if (oap->line_count < 2) { + oap->line_count = 2; + } + if (curwin->w_cursor.lnum + oap->line_count - 1 > + curbuf->b_ml.ml_line_count) { + beep_flush(); + } else { + do_join((size_t)oap->line_count, oap->op_type == OP_JOIN, + true, true, true); + auto_format(false, true); + } + break; + + case OP_DELETE: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + (void)op_delete(oap); + // save cursor line for undo if it wasn't saved yet + if (oap->motion_type == kMTLineWise + && has_format_option(FO_AUTO) + && u_save_cursor() == OK) { + auto_format(false, true); + } + } + break; + + case OP_YANK: + if (empty_region_error) { + if (!gui_yank) { + vim_beep(BO_OPER); + CancelRedo(); + } + } else { + curwin->w_p_lbr = lbr_saved; + oap->excl_tr_ws = cap->cmdchar == 'z'; + (void)op_yank(oap, !gui_yank, false); + } + check_cursor_col(); + break; + + case OP_CHANGE: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + // This is a new edit command, not a restart. Need to + // remember it to make 'insertmode' work with mappings for + // Visual mode. But do this only once and not when typed and + // 'insertmode' isn't set. + if (p_im || !KeyTyped) { + restart_edit_save = restart_edit; + } else { + restart_edit_save = 0; + } + restart_edit = 0; + + // Restore linebreak, so that when the user edits it looks as before. + curwin->w_p_lbr = lbr_saved; + + // Reset finish_op now, don't want it set inside edit(). + finish_op = false; + if (op_change(oap)) { // will call edit() + cap->retval |= CA_COMMAND_BUSY; + } + if (restart_edit == 0) { + restart_edit = restart_edit_save; + } + } + break; + + case OP_FILTER: + if (vim_strchr(p_cpo, CPO_FILTER) != NULL) { + AppendToRedobuff("!\r"); // Use any last used !cmd. + } else { + bangredo = true; // do_bang() will put cmd in redo buffer. + } + FALLTHROUGH; + + case OP_INDENT: + case OP_COLON: + + // If 'equalprg' is empty, do the indenting internally. + if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) { + if (curbuf->b_p_lisp) { + op_reindent(oap, get_lisp_indent); + break; + } + op_reindent(oap, + *curbuf->b_p_inde != NUL ? get_expr_indent : + get_c_indent); + break; + } + + op_colon(oap); + break; + + case OP_TILDE: + case OP_UPPER: + case OP_LOWER: + case OP_ROT13: + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + op_tilde(oap); + } + check_cursor_col(); + break; + + case OP_FORMAT: + if (*curbuf->b_p_fex != NUL) { + op_formatexpr(oap); // use expression + } else { + if (*p_fp != NUL || *curbuf->b_p_fp != NUL) { + op_colon(oap); // use external command + } else { + op_format(oap, false); // use internal function + } + } + break; + + case OP_FORMAT2: + op_format(oap, true); // use internal function + break; + + case OP_FUNCTION: + // Restore linebreak, so that when the user edits it looks as + // before. + curwin->w_p_lbr = lbr_saved; + op_function(oap); // call 'operatorfunc' + break; + + case OP_INSERT: + case OP_APPEND: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + // This is a new edit command, not a restart. Need to + // remember it to make 'insertmode' work with mappings for + // Visual mode. But do this only once. + restart_edit_save = restart_edit; + restart_edit = 0; + + // Restore linebreak, so that when the user edits it looks as before. + curwin->w_p_lbr = lbr_saved; + + op_insert(oap, cap->count1); + + // Reset linebreak, so that formatting works correctly. + curwin->w_p_lbr = false; + + // TODO(brammool): when inserting in several lines, should format all + // the lines. + auto_format(false, true); + + if (restart_edit == 0) { + restart_edit = restart_edit_save; + } else { + cap->retval |= CA_COMMAND_BUSY; + } + } + break; + + case OP_REPLACE: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + // Restore linebreak, so that when the user edits it looks as before. + curwin->w_p_lbr = lbr_saved; + + op_replace(oap, cap->nchar); + } + break; + + case OP_FOLD: + VIsual_reselect = false; // don't reselect now + foldCreate(curwin, oap->start, oap->end); + break; + + case OP_FOLDOPEN: + case OP_FOLDOPENREC: + case OP_FOLDCLOSE: + case OP_FOLDCLOSEREC: + VIsual_reselect = false; // don't reselect now + opFoldRange(oap->start, oap->end, + oap->op_type == OP_FOLDOPEN + || oap->op_type == OP_FOLDOPENREC, + oap->op_type == OP_FOLDOPENREC + || oap->op_type == OP_FOLDCLOSEREC, + oap->is_VIsual); + break; + + case OP_FOLDDEL: + case OP_FOLDDELREC: + 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: + case OP_NR_SUB: + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + VIsual_active = true; + curwin->w_p_lbr = lbr_saved; + op_addsub(oap, cap->count1, redo_VIsual_arg); + VIsual_active = false; + } + check_cursor_col(); + break; + default: + clearopbeep(oap); + } + virtual_op = kNone; + if (!gui_yank) { + // if 'sol' not set, go back to old column for some commands + if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted + && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT + || oap->op_type == OP_DELETE)) { + curwin->w_p_lbr = false; + coladvance(curwin->w_curswant = old_col); + } + } else { + curwin->w_cursor = old_cursor; + } + clearop(oap); + motion_force = NUL; + } + curwin->w_p_lbr = lbr_saved; +} + /// Check if the default register (used in an unnamed paste) should be a /// clipboard register. This happens when `clipboard=unnamed[plus]` is set /// and a provider is available. |