aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/ops.c')
-rw-r--r--src/nvim/ops.c1453
1 files changed, 1221 insertions, 232 deletions
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index b4b9545daf..badc00fb39 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"
@@ -36,7 +38,7 @@
#include "nvim/memline.h"
#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"
@@ -133,10 +135,18 @@ static char opchars[][3] =
{ Ctrl_X, NUL, OPF_CHANGE }, // OP_NR_SUB
};
-/*
- * Translate a command name into an operator type.
- * Must only be called with a valid operator name!
- */
+yankreg_T *get_y_previous(void)
+{
+ return y_previous;
+}
+
+void set_y_previous(yankreg_T *yreg)
+{
+ y_previous = yreg;
+}
+
+/// Translate a command name into an operator type.
+/// Must only be called with a valid operator name!
int get_op_type(int char1, int char2)
{
int i;
@@ -265,14 +275,14 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
msg_attr_keep((char *)IObuff, 0, true, false);
}
- /*
- * Set "'[" and "']" marks.
- */
- curbuf->b_op_start = oap->start;
- curbuf->b_op_end.lnum = oap->end.lnum;
- curbuf->b_op_end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
- if (curbuf->b_op_end.col > 0) {
- curbuf->b_op_end.col--;
+ if (!cmdmod.lockmarks) {
+ // Set "'[" and "']" marks.
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end.lnum = oap->end.lnum;
+ curbuf->b_op_end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
+ if (curbuf->b_op_end.col > 0) {
+ curbuf->b_op_end.col--;
+ }
}
changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L, true);
@@ -379,8 +389,8 @@ static void shift_block(oparg_T *oap, int amount)
}
}
for (; ascii_iswhite(*bd.textstart);) {
- // TODO: is passing bd.textstart for start of the line OK?
- incr = lbr_chartabsize_adv(bd.textstart, &bd.textstart, (bd.start_vcol));
+ // TODO(fmoralesc): is passing bd.textstart for start of the line OK?
+ incr = lbr_chartabsize_adv(bd.textstart, &bd.textstart, bd.start_vcol);
total += incr;
bd.start_vcol += incr;
}
@@ -558,21 +568,18 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
}
if (spaces > 0) {
- int off;
-
- // Avoid starting halfway through a multi-byte character.
- if (b_insert) {
- off = utf_head_off(oldp, oldp + offset + spaces);
- } else {
- off = (*mb_off_next)(oldp, oldp + offset);
- offset += off;
- }
- spaces -= off;
- count -= off;
+ // avoid copying part of a multi-byte character
+ offset -= utf_head_off(oldp, oldp + offset);
+ }
+ if (spaces < 0) { // can happen when the cursor was moved
+ spaces = 0;
}
assert(count >= 0);
- newp = (char_u *)xmalloc(STRLEN(oldp) + s_len + (size_t)count + 1);
+ // Make sure the allocated size matches what is actually copied below.
+ newp = xmalloc(STRLEN(oldp) + (size_t)spaces + s_len
+ + (spaces > 0 && !bdp->is_short ? (size_t)p_ts - (size_t)spaces : 0)
+ + (size_t)count + 1);
// copy up to shifted part
memmove(newp, oldp, (size_t)offset);
@@ -587,14 +594,19 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
offset += (int)s_len;
int skipped = 0;
- if (spaces && !bdp->is_short) {
- // insert post-padding
- memset(newp + offset + spaces, ' ', (size_t)(p_ts - spaces));
- // We're splitting a TAB, don't copy it.
- oldp++;
- // We allowed for that TAB, remember this now
- count++;
- skipped = 1;
+ if (spaces > 0 && !bdp->is_short) {
+ if (*oldp == TAB) {
+ // insert post-padding
+ memset(newp + offset + spaces, ' ', (size_t)(p_ts - spaces));
+ // We're splitting a TAB, don't copy it.
+ oldp++;
+ // We allowed for that TAB, remember this now
+ count++;
+ skipped = 1;
+ } else {
+ // Not a TAB, no extra spaces
+ count = spaces;
+ }
}
if (spaces > 0) {
@@ -692,9 +704,11 @@ void op_reindent(oparg_T *oap, Indenter how)
"%" PRId64 " lines indented ", i),
(int64_t)i);
}
- // set '[ and '] marks
- curbuf->b_op_start = oap->start;
- curbuf->b_op_end = oap->end;
+ if (!cmdmod.lockmarks) {
+ // set '[ and '] marks
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end = oap->end;
+ }
}
/*
@@ -910,30 +924,45 @@ int do_record(int c)
showmode();
regname = c;
retval = OK;
+ apply_autocmds(EVENT_RECORDINGENTER, NULL, NULL, false, curbuf);
}
- } else { // stop recording
- /*
- * Get the recorded key hits. K_SPECIAL and CSI will be escaped, this
- * needs to be removed again to put it in a register. exec_reg then
- * adds the escaping back later.
- */
+ } else { // stop recording
+ save_v_event_T save_v_event;
+ // Set the v:event dictionary with information about the recording.
+ dict_T *dict = get_v_event(&save_v_event);
+
+ // The recorded text contents.
+ p = get_recorded();
+ if (p != NULL) {
+ // Remove escaping for K_SPECIAL in multi-byte chars.
+ vim_unescape_ks(p);
+ (void)tv_dict_add_str(dict, S_LEN("regcontents"), (const char *)p);
+ }
+
+ // Name of requested register, or empty string for unnamed operation.
+ char buf[NUMBUFLEN+2];
+ buf[0] = (char)regname;
+ buf[1] = NUL;
+ (void)tv_dict_add_str(dict, S_LEN("regname"), buf);
+ tv_dict_set_keys_readonly(dict);
+
+ // Get the recorded key hits. K_SPECIAL will be escaped, this
+ // needs to be removed again to put it in a register. exec_reg then
+ // adds the escaping back later.
+ apply_autocmds(EVENT_RECORDINGLEAVE, NULL, NULL, false, curbuf);
+ restore_v_event(dict, &save_v_event);
+ reg_recorded = reg_recording;
reg_recording = 0;
if (ui_has(kUIMessages)) {
showmode();
} else {
msg("");
}
- p = get_recorded();
if (p == NULL) {
retval = FAIL;
} else {
- // Remove escaping for CSI and K_SPECIAL in multi-byte chars.
- vim_unescape_csi(p);
-
- /*
- * We don't want to change the default register here, so save and
- * restore the current register name.
- */
+ // We don't want to change the default register here, so save and
+ // restore the current register name.
old_y_previous = y_previous;
retval = stuff_yank(regname, p);
@@ -995,6 +1024,60 @@ static int stuff_yank(int regname, char_u *p)
static int execreg_lastc = NUL;
+/// When executing a register as a series of ex-commands, if the
+/// line-continuation character is used for a line, then join it with one or
+/// more previous lines. Note that lines are processed backwards starting from
+/// the last line in the register.
+///
+/// @param lines list of lines in the register
+/// @param idx index of the line starting with \ or "\. Join this line with all the immediate
+/// predecessor lines that start with a \ and the first line that doesn't start
+/// with a \. Lines that start with a comment "\ character are ignored.
+/// @returns the concatenated line. The index of the line that should be
+/// processed next is returned in idx.
+static char_u *execreg_line_continuation(char_u **lines, size_t *idx)
+{
+ size_t i = *idx;
+ assert(i > 0);
+ const size_t cmd_end = i;
+
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char_u), 400);
+
+ char_u *p;
+
+ // search backwards to find the first line of this command.
+ // Any line not starting with \ or "\ is the start of the
+ // command.
+ while (--i > 0) {
+ p = skipwhite(lines[i]);
+ if (*p != '\\' && (p[0] != '"' || p[1] != '\\' || p[2] != ' ')) {
+ break;
+ }
+ }
+ const size_t cmd_start = i;
+
+ // join all the lines
+ ga_concat(&ga, (char *)lines[cmd_start]);
+ for (size_t j = cmd_start + 1; j <= cmd_end; j++) {
+ p = skipwhite(lines[j]);
+ if (*p == '\\') {
+ // Adjust the growsize to the current length to
+ // speed up concatenating many lines.
+ if (ga.ga_len > 400) {
+ ga_set_growsize(&ga, MIN(ga.ga_len, 8000));
+ }
+ ga_concat(&ga, (char *)(p + 1));
+ }
+ }
+ ga_append(&ga, NUL);
+ char_u *str = vim_strsave(ga.ga_data);
+ ga_clear(&ga);
+
+ *idx = i;
+ return str;
+}
+
/// Execute a yank register: copy it into the stuff buffer
///
/// @param colon insert ':' before each line
@@ -1083,7 +1166,21 @@ int do_execreg(int regname, int colon, int addcr, int silent)
return FAIL;
}
}
- escaped = vim_strsave_escape_csi(reg->y_array[i]);
+
+ // Handle line-continuation for :@<register>
+ char_u *str = reg->y_array[i];
+ bool free_str = false;
+ if (colon && i > 0) {
+ p = skipwhite(str);
+ if (*p == '\\' || (p[0] == '"' && p[1] == '\\' && p[2] == ' ')) {
+ str = execreg_line_continuation(reg->y_array, &i);
+ free_str = true;
+ }
+ }
+ escaped = vim_strsave_escape_ks(str);
+ if (free_str) {
+ xfree(str);
+ }
retval = ins_typebuf(escaped, remap, 0, true, silent);
xfree(escaped);
if (retval == FAIL) {
@@ -1125,7 +1222,7 @@ static void put_reedit_in_typebuf(int silent)
/// Insert register contents "s" into the typeahead buffer, so that it will be
/// executed again.
///
-/// @param esc when true then it is to be taken literally: Escape CSI
+/// @param esc when true then it is to be taken literally: Escape K_SPECIAL
/// characters and no remapping.
/// @param colon add ':' before the line
static int put_in_typebuf(char_u *s, bool esc, bool colon, int silent)
@@ -1140,7 +1237,7 @@ static int put_in_typebuf(char_u *s, bool esc, bool colon, int silent)
char_u *p;
if (esc) {
- p = vim_strsave_escape_csi(s);
+ p = vim_strsave_escape_ks(s);
} else {
p = s;
}
@@ -1419,6 +1516,11 @@ int op_delete(oparg_T *oap)
return FAIL;
}
+ if (VIsual_select && oap->is_VIsual) {
+ // Use the register given with CTRL_R, defaults to zero
+ oap->regname = VIsual_select_reg;
+ }
+
mb_adjust_opend(oap);
/*
@@ -1471,20 +1573,20 @@ int op_delete(oparg_T *oap)
yankreg_T *reg = NULL;
int did_yank = false;
if (oap->regname != 0) {
- // yank without message
- did_yank = op_yank(oap, false, true);
- if (!did_yank) {
- // op_yank failed, don't do anything
+ // check for read-only register
+ if (!valid_yank_reg(oap->regname, true)) {
+ beep_flush();
return OK;
}
+ reg = get_yank_register(oap->regname, YREG_YANK); // yank into specif'd reg
+ op_yank_reg(oap, false, reg, is_append_register(oap->regname)); // yank without message
+ did_yank = true;
}
- /*
- * Put deleted text into register 1 and shift number registers if the
- * delete contains a line break, or when a regname has been specified.
- */
- if (oap->regname != 0 || oap->motion_type == kMTLineWise
- || oap->line_count > 1 || oap->use_reg_one) {
+ // Put deleted text into register 1 and shift number registers if the
+ // delete contains a line break, or when using a specific operator (Vi
+ // compatible)
+ if (oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) {
shift_delete_registers(is_append_register(oap->regname));
reg = &y_regs[1];
op_yank_reg(oap, false, reg, false);
@@ -1715,13 +1817,15 @@ int op_delete(oparg_T *oap)
msgmore(curbuf->b_ml.ml_line_count - old_lcount);
setmarks:
- if (oap->motion_type == kMTBlockWise) {
- curbuf->b_op_end.lnum = oap->end.lnum;
- curbuf->b_op_end.col = oap->start.col;
- } else {
- curbuf->b_op_end = oap->start;
+ if (!cmdmod.lockmarks) {
+ if (oap->motion_type == kMTBlockWise) {
+ curbuf->b_op_end.lnum = oap->end.lnum;
+ curbuf->b_op_end.col = oap->start.col;
+ } else {
+ curbuf->b_op_end = oap->start;
+ }
+ curbuf->b_op_start = oap->start;
}
- curbuf->b_op_start = oap->start;
return OK;
}
@@ -1768,7 +1872,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;
@@ -1926,11 +2030,14 @@ int op_replace(oparg_T *oap, int c)
while (ltoreq(curwin->w_cursor, oap->end)) {
n = gchar_cursor();
if (n != NUL) {
- if (utf_char2len(c) > 1 || utf_char2len(n) > 1) {
+ int new_byte_len = utf_char2len(c);
+ int old_byte_len = utfc_ptr2len(get_cursor_pos_ptr());
+
+ if (new_byte_len > 1 || old_byte_len > 1) {
// This is slow, but it handles replacing a single-byte
// with a multi-byte and the other way around.
if (curwin->w_cursor.lnum == oap->end.lnum) {
- oap->end.col += utf_char2len(c) - utf_char2len(n);
+ oap->end.col += new_byte_len - old_byte_len;
}
replace_character(c);
} else {
@@ -1986,9 +2093,11 @@ int op_replace(oparg_T *oap, int c)
check_cursor();
changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0L, true);
- // Set "'[" and "']" marks.
- curbuf->b_op_start = oap->start;
- curbuf->b_op_end = oap->end;
+ if (!cmdmod.lockmarks) {
+ // Set "'[" and "']" marks.
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end = oap->end;
+ }
return OK;
}
@@ -2057,11 +2166,11 @@ void op_tilde(oparg_T *oap)
redraw_curbuf_later(INVERTED);
}
- /*
- * Set '[ and '] marks.
- */
- curbuf->b_op_start = oap->start;
- curbuf->b_op_end = oap->end;
+ if (!cmdmod.lockmarks) {
+ // Set '[ and '] marks.
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end = oap->end;
+ }
if (oap->line_count > p_report) {
smsg(NGETTEXT("%" PRId64 " line changed",
@@ -2162,7 +2271,8 @@ void op_insert(oparg_T *oap, long count1)
{
long ins_len, pre_textlen = 0;
char_u *firstline, *ins_text;
- colnr_T ind_pre = 0;
+ colnr_T ind_pre_col = 0, ind_post_col;
+ int ind_pre_vcol = 0, ind_post_vcol = 0;
struct block_def bd;
int i;
pos_T t1;
@@ -2179,24 +2289,28 @@ void op_insert(oparg_T *oap, long count1)
// doing block_prep(). When only "block" is used, virtual edit is
// already disabled, but still need it when calling
// coladvance_force().
+ // coladvance_force() uses get_ve_flags() to get the 'virtualedit'
+ // state for the current window. To override that state, we need to
+ // set the window-local value of ve_flags rather than the global value.
if (curwin->w_cursor.coladd > 0) {
- unsigned old_ve_flags = ve_flags;
+ unsigned old_ve_flags = curwin->w_ve_flags;
- ve_flags = VE_ALL;
if (u_save_cursor() == FAIL) {
return;
}
+ curwin->w_ve_flags = VE_ALL;
coladvance_force(oap->op_type == OP_APPEND
? oap->end_vcol + 1 : getviscol());
if (oap->op_type == OP_APPEND) {
--curwin->w_cursor.col;
}
- ve_flags = old_ve_flags;
+ curwin->w_ve_flags = old_ve_flags;
}
// Get the info about the block before entering the text
block_prep(oap, &bd, oap->start.lnum, true);
// Get indent information
- ind_pre = (colnr_T)getwhitecols_curline();
+ ind_pre_col = (colnr_T)getwhitecols_curline();
+ ind_pre_vcol = get_indent();
firstline = ml_get(oap->start.lnum) + bd.textcol;
if (oap->op_type == OP_APPEND) {
@@ -2238,6 +2352,7 @@ void op_insert(oparg_T *oap, long count1)
}
t1 = oap->start;
+ const pos_T start_insert = curwin->w_cursor;
(void)edit(NUL, false, (linenr_T)count1);
// When a tab was inserted, and the characters in front of the tab
@@ -2261,33 +2376,29 @@ void op_insert(oparg_T *oap, long count1)
// if indent kicked in, the firstline might have changed
// but only do that, if the indent actually increased
- const colnr_T ind_post = (colnr_T)getwhitecols_curline();
- if (curbuf->b_op_start.col > ind_pre && ind_post > ind_pre) {
- bd.textcol += ind_post - ind_pre;
- bd.start_vcol += ind_post - ind_pre;
+ ind_post_col = (colnr_T)getwhitecols_curline();
+ if (curbuf->b_op_start.col > ind_pre_col && ind_post_col > ind_pre_col) {
+ bd.textcol += ind_post_col - ind_pre_col;
+ ind_post_vcol = get_indent();
+ bd.start_vcol += ind_post_vcol - ind_pre_vcol;
did_indent = true;
}
// The user may have moved the cursor before inserting something, try
// to adjust the block for that. But only do it, if the difference
// does not come from indent kicking in.
- if (oap->start.lnum == curbuf->b_op_start_orig.lnum
- && !bd.is_MAX
- && !did_indent) {
+ if (oap->start.lnum == curbuf->b_op_start_orig.lnum && !bd.is_MAX && !did_indent) {
+ const int t = getviscol2(curbuf->b_op_start_orig.col, curbuf->b_op_start_orig.coladd);
+
if (oap->op_type == OP_INSERT
&& oap->start.col + oap->start.coladd
!= curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) {
- int t = getviscol2(curbuf->b_op_start_orig.col,
- curbuf->b_op_start_orig.coladd);
oap->start.col = curbuf->b_op_start_orig.col;
pre_textlen -= t - oap->start_vcol;
oap->start_vcol = t;
} else if (oap->op_type == OP_APPEND
- && oap->end.col + oap->end.coladd
- >= curbuf->b_op_start_orig.col
- + curbuf->b_op_start_orig.coladd) {
- int t = getviscol2(curbuf->b_op_start_orig.col,
- curbuf->b_op_start_orig.coladd);
+ && oap->start.col + oap->start.coladd
+ >= curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) {
oap->start.col = curbuf->b_op_start_orig.col;
// reset pre_textlen to the value of OP_INSERT
pre_textlen += bd.textlen;
@@ -2297,12 +2408,26 @@ void op_insert(oparg_T *oap, long count1)
}
}
- /*
- * Spaces and tabs in the indent may have changed to other spaces and
- * tabs. Get the starting column again and correct the length.
- * Don't do this when "$" used, end-of-line will have changed.
- */
+ // Spaces and tabs in the indent may have changed to other spaces and
+ // tabs. Get the starting column again and correct the length.
+ // Don't do this when "$" used, end-of-line will have changed.
+ //
+ // if indent was added and the inserted text was after the indent,
+ // correct the selection for the new indent.
+ if (did_indent && bd.textcol - ind_post_col > 0) {
+ oap->start.col += ind_post_col - ind_pre_col;
+ oap->start_vcol += ind_post_vcol - ind_pre_vcol;
+ oap->end.col += ind_post_col - ind_pre_col;
+ oap->end_vcol += ind_post_vcol - ind_pre_vcol;
+ }
block_prep(oap, &bd2, oap->start.lnum, true);
+ if (did_indent && bd.textcol - ind_post_col > 0) {
+ // undo for where "oap" is used below
+ oap->start.col -= ind_post_col - ind_pre_col;
+ oap->start_vcol -= ind_post_vcol - ind_pre_vcol;
+ oap->end.col -= ind_post_col - ind_pre_col;
+ oap->end_vcol -= ind_post_vcol - ind_pre_vcol;
+ }
if (!bd.is_MAX || bd2.textlen < bd.textlen) {
if (oap->op_type == OP_APPEND) {
pre_textlen += bd2.textlen - bd.textlen;
@@ -2321,15 +2446,27 @@ void op_insert(oparg_T *oap, long count1)
firstline = ml_get(oap->start.lnum);
const size_t len = STRLEN(firstline);
colnr_T add = bd.textcol;
+ colnr_T offset = 0; // offset when cursor was moved in insert mode
if (oap->op_type == OP_APPEND) {
add += bd.textlen;
+ // account for pressing cursor in insert mode when '$' was used
+ if (bd.is_MAX && start_insert.lnum == Insstart.lnum && start_insert.col > Insstart.col) {
+ offset = start_insert.col - Insstart.col;
+ add -= offset;
+ if (oap->end_vcol > offset) {
+ oap->end_vcol -= offset + 1;
+ } else {
+ // moved outside of the visual block, what to do?
+ return;
+ }
+ }
}
if ((size_t)add > len) {
firstline += len; // short line, point to the NUL
} else {
firstline += add;
}
- ins_len = (long)STRLEN(firstline) - pre_textlen;
+ ins_len = (long)STRLEN(firstline) - pre_textlen - offset;
if (pre_textlen >= 0 && ins_len > 0) {
ins_text = vim_strnsave(firstline, (size_t)ins_len);
// block handled here
@@ -2511,12 +2648,12 @@ void free_register(yankreg_T *reg)
/// Yanks the text between "oap->start" and "oap->end" into a yank register.
/// If we are to append (uppercase register), we first yank into a new yank
/// register and then concatenate the old and the new one.
+/// Do not call this from a delete operation. Use op_yank_reg() instead.
///
/// @param oap operator arguments
/// @param message show message when more than `&report` lines are yanked.
-/// @param deleting whether the function was called from a delete operation.
/// @returns whether the operation register was writable.
-bool op_yank(oparg_T *oap, bool message, int deleting)
+bool op_yank(oparg_T *oap, bool message)
FUNC_ATTR_NONNULL_ALL
{
// check for read-only register
@@ -2530,11 +2667,8 @@ bool op_yank(oparg_T *oap, bool message, int deleting)
yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK);
op_yank_reg(oap, message, reg, is_append_register(oap->regname));
- // op_delete will set_clipboard and do_autocmd
- if (!deleting) {
- set_clipboard(oap->regname, reg);
- do_autocmd_textyankpost(oap, reg);
- }
+ set_clipboard(oap->regname, reg);
+ do_autocmd_textyankpost(oap, reg);
return true;
}
@@ -2702,9 +2836,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
curr->y_size = j;
xfree(reg->y_array);
}
- if (curwin->w_p_rnu) {
- redraw_later(curwin, SOME_VALID); // cursor moved to start
- }
+
if (message) { // Display message about yank?
if (yank_type == kMTCharWise && yanklines == 1) {
yanklines = 0;
@@ -2733,17 +2865,15 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
}
}
- /*
- * Set "'[" and "']" marks.
- */
- curbuf->b_op_start = oap->start;
- curbuf->b_op_end = oap->end;
- if (yank_type == kMTLineWise) {
- curbuf->b_op_start.col = 0;
- curbuf->b_op_end.col = MAXCOL;
+ if (!cmdmod.lockmarks) {
+ // Set "'[" and "']" marks.
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end = oap->end;
+ if (yank_type == kMTLineWise) {
+ curbuf->b_op_start.col = 0;
+ curbuf->b_op_end.col = MAXCOL;
+ }
}
-
- return;
}
// Copy a block range into a register.
@@ -2768,7 +2898,7 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx,
if (exclude_trailing_space) {
int s = bd->textlen + bd->endspaces;
- while (ascii_iswhite(*(bd->textstart + s - 1)) && s > 0) {
+ while (s > 0 && ascii_iswhite(*(bd->textstart + s - 1))) {
s = s - utf_head_off(bd->textstart, bd->textstart + s - 1) - 1;
pnew--;
}
@@ -2792,8 +2922,9 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
recursive = true;
+ save_v_event_T save_v_event;
// Set the v:event dictionary with information about the yank.
- dict_T *dict = get_vim_var_dict(VV_EVENT);
+ dict_T *dict = get_v_event(&save_v_event);
// The yanked text contents.
list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
@@ -2830,7 +2961,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
textlock++;
apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf);
textlock--;
- tv_dict_clear(dict);
+ restore_v_event(dict, &save_v_event);
recursive = false;
}
@@ -2872,6 +3003,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
char_u *insert_string = NULL;
bool allocated = false;
long cnt;
+ const pos_T orig_start = curbuf->b_op_start;
+ const pos_T orig_end = curbuf->b_op_end;
+ unsigned int cur_ve_flags = get_ve_flags();
if (flags & PUT_FIXINDENT) {
orig_indent = get_indent();
@@ -2942,7 +3076,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
eol = (*(cursor_pos + utfc_ptr2len(cursor_pos)) == NUL);
}
- bool ve_allows = (ve_flags == VE_ALL || ve_flags == VE_ONEMORE);
+ bool ve_allows = (cur_ve_flags == VE_ALL || cur_ve_flags == VE_ONEMORE);
bool eof = curbuf->b_ml.ml_line_count == curwin->w_cursor.lnum
&& one_past_line;
if (ve_allows || !(eol || eof)) {
@@ -3118,13 +3252,14 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
yanklen = (int)STRLEN(y_array[0]);
- if (ve_flags == VE_ALL && y_type == kMTCharWise) {
+ if (cur_ve_flags == VE_ALL && y_type == kMTCharWise) {
if (gchar_cursor() == TAB) {
- /* Don't need to insert spaces when "p" on the last position of a
- * tab or "P" on the first position. */
int viscol = getviscol();
+ long ts = curbuf->b_p_ts;
+ // Don't need to insert spaces when "p" on the last position of a
+ // tab or "P" on the first position.
if (dir == FORWARD
- ? tabstop_padding(viscol, curbuf->b_p_ts, curbuf->b_p_vts_array) != 1
+ ? tabstop_padding(viscol, ts, curbuf->b_p_vts_array) != 1
: curwin->w_cursor.coladd > 0) {
coladvance_force(viscol);
} else {
@@ -3146,7 +3281,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
colnr_T endcol2 = 0;
if (dir == FORWARD && c != NUL) {
- if (ve_flags == VE_ALL) {
+ if (cur_ve_flags == VE_ALL) {
getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2);
} else {
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
@@ -3160,9 +3295,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
col += curwin->w_cursor.coladd;
- if (ve_flags == VE_ALL
- && (curwin->w_cursor.coladd > 0
- || endcol2 == curwin->w_cursor.col)) {
+ if (cur_ve_flags == VE_ALL
+ && (curwin->w_cursor.coladd > 0 || endcol2 == curwin->w_cursor.col)) {
if (dir == FORWARD && c == NUL) {
col++;
}
@@ -3244,18 +3378,28 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
}
- // insert the new text
+ // Insert the new text.
+ // First check for multiplication overflow.
+ if (yanklen + spaces != 0
+ && count > ((INT_MAX - (bd.startspaces + bd.endspaces)) / (yanklen + spaces))) {
+ emsg(_(e_resulting_text_too_long));
+ break;
+ }
+
totlen = (size_t)(count * (yanklen + spaces)
+ bd.startspaces + bd.endspaces);
int addcount = (int)totlen + lines_appended;
newp = (char_u *)xmalloc(totlen + oldlen + 1);
+
// copy part up to cursor to new line
ptr = newp;
memmove(ptr, oldp, (size_t)bd.textcol);
ptr += bd.textcol;
+
// may insert some spaces before the new text
memset(ptr, ' ', (size_t)bd.startspaces);
ptr += bd.startspaces;
+
// insert the new text
for (long j = 0; j < count; j++) {
memmove(ptr, y_array[i], (size_t)yanklen);
@@ -3269,9 +3413,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
addcount -= spaces;
}
}
+
// may insert some spaces after the new text
memset(ptr, ' ', (size_t)bd.endspaces);
ptr += bd.endspaces;
+
// move the text after the cursor to the end of the line.
int columns = (int)oldlen - bd.textcol - delcount + 1;
assert(columns >= 0);
@@ -3340,6 +3486,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (y_type == kMTCharWise && y_size == 1) {
linenr_T end_lnum = 0; // init for gcc
linenr_T start_lnum = lnum;
+ int first_byte_off = 0;
if (VIsual_active) {
end_lnum = curbuf->b_visual.vi_end.lnum;
@@ -3359,10 +3506,18 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
}
- do {
+ if (count == 0 || yanklen == 0) {
+ if (VIsual_active) {
+ lnum = end_lnum;
+ }
+ } else if (count > INT_MAX / yanklen) {
+ // multiplication overflow
+ emsg(_(e_resulting_text_too_long));
+ } else {
totlen = (size_t)(count * yanklen);
- if (totlen > 0) {
+ do {
oldp = ml_get(lnum);
+ oldlen = STRLEN(oldp);
if (lnum > start_lnum) {
pos_T pos = {
.lnum = lnum,
@@ -3373,11 +3528,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
col = MAXCOL;
}
}
- if (VIsual_active && col > (int)STRLEN(oldp)) {
+ if (VIsual_active && col > (colnr_T)oldlen) {
lnum++;
continue;
}
- newp = (char_u *)xmalloc((size_t)(STRLEN(oldp) + totlen + 1));
+ newp = (char_u *)xmalloc(totlen + oldlen + 1);
memmove(newp, oldp, (size_t)col);
ptr = newp + col;
for (i = 0; i < (size_t)count; i++) {
@@ -3386,6 +3541,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
STRMOVE(ptr, oldp + col);
ml_replace(lnum, newp, false);
+
+ // compute the byte offset for the last character
+ first_byte_off = utf_head_off(newp, ptr - 1);
+
// Place cursor on last putted char.
if (lnum == curwin->w_cursor.lnum) {
// make sure curwin->w_virtcol is updated
@@ -3395,22 +3554,30 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
changed_bytes(lnum, col);
extmark_splice_cols(curbuf, (int)lnum-1, col,
0, (int)totlen, kExtmarkUndo);
- }
- if (VIsual_active) {
- lnum++;
- }
- } while (VIsual_active && lnum <= end_lnum);
+ if (VIsual_active) {
+ lnum++;
+ }
+ } while (VIsual_active && lnum <= end_lnum);
- if (VIsual_active) { // reset lnum to the last visual line
- lnum--;
+ if (VIsual_active) { // reset lnum to the last visual line
+ lnum--;
+ }
}
+ // put '] at the first byte of the last character
curbuf->b_op_end = curwin->w_cursor;
+ curbuf->b_op_end.col -= first_byte_off;
+
// For "CTRL-O p" in Insert mode, put cursor after last char
if (totlen && (restart_edit != 0 || (flags & PUT_CURSEND))) {
curwin->w_cursor.col++;
+ } else {
+ curwin->w_cursor.col -= first_byte_off;
}
} else {
+ linenr_T new_lnum = new_cursor.lnum;
+ size_t len;
+
// Insert at least one line. When y_type is kMTCharWise, break the first
// line in two.
for (cnt = 1; cnt <= count; cnt++) {
@@ -3427,6 +3594,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
STRCAT(newp, ptr);
// insert second line
ml_append(lnum, newp, (colnr_T)0, false);
+ new_lnum++;
xfree(newp);
oldp = ml_get(lnum);
@@ -3442,10 +3610,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
for (; i < y_size; i++) {
- if ((y_type != kMTCharWise || i < y_size - 1)
- && ml_append(lnum, y_array[i], (colnr_T)0, false)
- == FAIL) {
- goto error;
+ if ((y_type != kMTCharWise || i < y_size - 1)) {
+ if (ml_append(lnum, y_array[i], (colnr_T)0, false) == FAIL) {
+ goto error;
+ }
+ new_lnum++;
}
lnum++;
++nr_lines;
@@ -3495,6 +3664,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
extmark_splice(curbuf, (int)new_cursor.lnum-1, col + 1, 0, 0, 0,
(int)y_size+1, 0, totsize+2, kExtmarkUndo);
}
+
+ if (cnt == 1) {
+ new_lnum = lnum;
+ }
}
error:
@@ -3520,12 +3693,14 @@ error:
curbuf->b_op_start.lnum, nr_lines, true);
}
- // put '] mark at last inserted character
- curbuf->b_op_end.lnum = lnum;
- // correct length for change in indent
- col = (colnr_T)STRLEN(y_array[y_size - 1]) - lendiff;
+ // Put the '] mark on the first byte of the last inserted character.
+ // Correct the length for change in indent.
+ curbuf->b_op_end.lnum = new_lnum;
+ len = STRLEN(y_array[y_size - 1]);
+ col = (colnr_T)len - lendiff;
if (col > 1) {
- curbuf->b_op_end.col = col - 1;
+ curbuf->b_op_end.col = col - 1 - utf_head_off(y_array[y_size - 1],
+ y_array[y_size - 1] + len - 1);
} else {
curbuf->b_op_end.col = 0;
}
@@ -3544,8 +3719,12 @@ error:
}
curwin->w_cursor.col = 0;
} else {
- curwin->w_cursor.lnum = lnum;
+ curwin->w_cursor.lnum = new_lnum;
curwin->w_cursor.col = col;
+ curbuf->b_op_end = curwin->w_cursor;
+ if (col > 1) {
+ curbuf->b_op_end.col = col - 1;
+ }
}
} else if (y_type == kMTLineWise) {
// put cursor on first non-blank in first inserted line
@@ -3564,6 +3743,10 @@ error:
curwin->w_set_curswant = TRUE;
end:
+ if (cmdmod.lockmarks) {
+ curbuf->b_op_start = orig_start;
+ curbuf->b_op_end = orig_end;
+ }
if (allocated) {
xfree(insert_string);
}
@@ -3583,14 +3766,16 @@ end:
*/
void adjust_cursor_eol(void)
{
+ unsigned int cur_ve_flags = get_ve_flags();
+
if (curwin->w_cursor.col > 0
&& gchar_cursor() == NUL
- && (ve_flags & VE_ONEMORE) == 0
+ && (cur_ve_flags & VE_ONEMORE) == 0
&& !(restart_edit || (State & INSERT))) {
// Put the cursor on the last character in the line.
dec_cursor();
- if (ve_flags == VE_ALL) {
+ if (cur_ve_flags == VE_ALL) {
colnr_T scol, ecol;
// Coladd is set to the width of the last character.
@@ -3644,7 +3829,7 @@ void ex_display(exarg_T *eap)
int name;
char_u *arg = eap->arg;
int clen;
- char_u type[2];
+ int type;
if (arg != NULL && *arg == NUL) {
arg = NULL;
@@ -3657,11 +3842,11 @@ void ex_display(exarg_T *eap)
name = get_register_name(i);
switch (get_reg_type(name, NULL)) {
case kMTLineWise:
- type[0] = 'l'; break;
+ type = 'l'; break;
case kMTCharWise:
- type[0] = 'c'; break;
+ type = 'c'; break;
default:
- type[0] = 'b'; break;
+ type = 'b'; break;
}
if (arg != NULL && vim_strchr(arg, name) == NULL) {
@@ -3688,88 +3873,87 @@ void ex_display(exarg_T *eap)
}
if (yb->y_array != NULL) {
- msg_putchar('\n');
- msg_puts(" ");
- msg_putchar(type[0]);
- msg_puts(" ");
- msg_putchar('"');
- msg_putchar(name);
- msg_puts(" ");
-
- int n = Columns - 11;
- for (size_t j = 0; j < yb->y_size && n > 1; j++) {
- if (j) {
- msg_puts_attr("^J", attr);
- n -= 2;
+ bool do_show = false;
+
+ for (size_t j = 0; !do_show && j < yb->y_size; j++) {
+ do_show = !message_filtered(yb->y_array[j]);
+ }
+
+ if (do_show || yb->y_size == 0) {
+ msg_putchar('\n');
+ msg_puts(" ");
+ msg_putchar(type);
+ msg_puts(" ");
+ msg_putchar('"');
+ msg_putchar(name);
+ msg_puts(" ");
+
+ int n = Columns - 11;
+ for (size_t j = 0; j < yb->y_size && n > 1; j++) {
+ if (j) {
+ msg_puts_attr("^J", attr);
+ n -= 2;
+ }
+ for (p = yb->y_array[j]; *p != NUL && (n -= ptr2cells(p)) >= 0; p++) { // -V1019
+ clen = utfc_ptr2len(p);
+ msg_outtrans_len(p, clen);
+ p += clen - 1;
+ }
}
- for (p = yb->y_array[j]; *p && (n -= ptr2cells(p)) >= 0; p++) { // -V1019 NOLINT(whitespace/line_length)
- clen = utfc_ptr2len(p);
- msg_outtrans_len(p, clen);
- p += clen - 1;
+ if (n > 1 && yb->y_type == kMTLineWise) {
+ msg_puts_attr("^J", attr);
}
+ ui_flush(); // show one line at a time
}
- if (n > 1 && yb->y_type == kMTLineWise) {
- msg_puts_attr("^J", attr);
- }
- ui_flush(); // show one line at a time
+ os_breakcheck();
}
- os_breakcheck();
}
- /*
- * display last inserted text
- */
+ // display last inserted text
if ((p = get_last_insert()) != NULL
- && (arg == NULL || vim_strchr(arg, '.') != NULL) && !got_int) {
+ && (arg == NULL || vim_strchr(arg, '.') != NULL) && !got_int
+ && !message_filtered(p)) {
msg_puts("\n c \". ");
dis_msg(p, true);
}
- /*
- * display last command line
- */
+ // display last command line
if (last_cmdline != NULL && (arg == NULL || vim_strchr(arg, ':') != NULL)
- && !got_int) {
+ && !got_int && !message_filtered(last_cmdline)) {
msg_puts("\n c \": ");
dis_msg(last_cmdline, false);
}
- /*
- * display current file name
- */
+ // display current file name
if (curbuf->b_fname != NULL
- && (arg == NULL || vim_strchr(arg, '%') != NULL) && !got_int) {
+ && (arg == NULL || vim_strchr(arg, '%') != NULL) && !got_int
+ && !message_filtered(curbuf->b_fname)) {
msg_puts("\n c \"% ");
dis_msg(curbuf->b_fname, false);
}
- /*
- * display alternate file name
- */
+ // display alternate file name
if ((arg == NULL || vim_strchr(arg, '%') != NULL) && !got_int) {
char_u *fname;
linenr_T dummy;
- if (buflist_name_nr(0, &fname, &dummy) != FAIL) {
+ if (buflist_name_nr(0, &fname, &dummy) != FAIL && !message_filtered(fname)) {
msg_puts("\n c \"# ");
dis_msg(fname, false);
}
}
- /*
- * display last search pattern
- */
+ // display last search pattern
if (last_search_pat() != NULL
- && (arg == NULL || vim_strchr(arg, '/') != NULL) && !got_int) {
+ && (arg == NULL || vim_strchr(arg, '/') != NULL) && !got_int
+ && !message_filtered(last_search_pat())) {
msg_puts("\n c \"/ ");
dis_msg(last_search_pat(), false);
}
- /*
- * display last used expression
- */
+ // display last used expression
if (expr_line != NULL && (arg == NULL || vim_strchr(arg, '=') != NULL)
- && !got_int) {
+ && !got_int && !message_filtered(expr_line)) {
msg_puts("\n c \"= ");
dis_msg(expr_line, false);
}
@@ -3908,7 +4092,7 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
// and setup the array of space strings lengths
for (t = 0; t < (linenr_T)count; t++) {
curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t));
- if (t == 0 && setmark) {
+ if (t == 0 && setmark && !cmdmod.lockmarks) {
// Set the '[ mark.
curwin->w_buffer->b_op_start.lnum = curwin->w_cursor.lnum;
curwin->w_buffer->b_op_start.col = (colnr_T)STRLEN(curr);
@@ -4029,7 +4213,7 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
ml_replace(curwin->w_cursor.lnum, newp, false);
- if (setmark) {
+ if (setmark && !cmdmod.lockmarks) {
// Set the '] mark.
curwin->w_buffer->b_op_end.lnum = curwin->w_cursor.lnum;
curwin->w_buffer->b_op_end.col = sumsize;
@@ -4093,7 +4277,7 @@ static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, in
* If first leader has 'f' flag, the lines can be joined only if the
* second line does not have a leader.
* If first leader has 'e' flag, the lines can never be joined.
- * If fist leader has 's' flag, the lines can only be joined if there is
+ * If first leader has 's' flag, the lines can only be joined if there is
* some text after it and the second line has the 'm' flag.
*/
if (leader1_flags != NULL) {
@@ -4148,7 +4332,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;
@@ -4167,8 +4351,10 @@ void op_format(oparg_T *oap, int keep_cursor)
redraw_curbuf_later(INVERTED);
}
- // Set '[ mark at the start of the formatted area
- curbuf->b_op_start = oap->start;
+ if (!cmdmod.lockmarks) {
+ // Set '[ mark at the start of the formatted area
+ curbuf->b_op_start = oap->start;
+ }
// For "gw" remember the cursor position and put it back below (adjusted
// for joined and split lines).
@@ -4190,8 +4376,10 @@ void op_format(oparg_T *oap, int keep_cursor)
old_line_count = curbuf->b_ml.ml_line_count - old_line_count;
msgmore(old_line_count);
- // put '] mark on the end of the formatted area
- curbuf->b_op_end = curwin->w_cursor;
+ if (!cmdmod.lockmarks) {
+ // put '] mark on the end of the formatted area
+ curbuf->b_op_end = curwin->w_cursor;
+ }
if (keep_cursor) {
curwin->w_cursor = saved_cursor;
@@ -4216,7 +4404,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
@@ -4278,7 +4466,7 @@ void format_lines(linenr_T line_count, int avoid_fex)
int leader_len = 0; // leader len of current line
int next_leader_len; // leader len of next line
char_u *leader_flags = NULL; // flags for leader of current line
- char_u *next_leader_flags; // flags for leader of next line
+ char_u *next_leader_flags = NULL; // flags for leader of next line
bool advance = true;
int second_indent = -1; // indent for second line (comment aware)
bool first_par_line = true;
@@ -4395,7 +4583,14 @@ void format_lines(linenr_T line_count, int avoid_fex)
leader_len, leader_flags,
next_leader_len,
next_leader_flags)) {
- is_end_par = true;
+ // Special case: If the next line starts with a line comment
+ // and this line has a line comment after some text, the
+ // paragraph doesn't really end.
+ if (next_leader_flags == NULL
+ || STRNCMP(next_leader_flags, "://", 3) != 0
+ || check_linecomment(get_cursor_line_ptr()) == MAXCOL) {
+ is_end_par = true;
+ }
}
/*
@@ -4789,7 +4984,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd)
// Set '[ mark if something changed. Keep the last end
// position from do_addsub().
- if (change_cnt > 0) {
+ if (change_cnt > 0 && !cmdmod.lockmarks) {
curbuf->b_op_start = startpos;
}
@@ -5143,11 +5338,13 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
}
}
- // set the '[ and '] marks
- curbuf->b_op_start = startpos;
- curbuf->b_op_end = endpos;
- if (curbuf->b_op_end.col > 0) {
- curbuf->b_op_end.col--;
+ if (!cmdmod.lockmarks) {
+ // set the '[ and '] marks
+ curbuf->b_op_start = startpos;
+ curbuf->b_op_end = endpos;
+ if (curbuf->b_op_end.col > 0) {
+ curbuf->b_op_end.col--;
+ }
}
theend:
@@ -5578,7 +5775,9 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char_u *str
// When appending, copy the previous line and free it after.
size_t extra = append ? STRLEN(pp[--lnum]) : 0;
char_u *s = xmallocz(line_len + extra);
- memcpy(s, pp[lnum], extra);
+ if (extra > 0) {
+ memcpy(s, pp[lnum], extra);
+ }
memcpy(s + extra, start, line_len);
size_t s_len = extra + line_len;
@@ -5907,6 +6106,797 @@ 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;
+ const pos_T orig_start = curbuf->b_op_start;
+ const pos_T orig_end = curbuf->b_op_end;
+
+ 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;
+ if (cmdmod.lockmarks) {
+ curbuf->b_op_start = orig_start;
+ curbuf->b_op_end = orig_end;
+ }
+ }
+}
+
+/// 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);
+ }
+ 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.
@@ -6388,7 +7378,6 @@ const yankreg_T *op_reg_get(const char name)
///
/// @return true on success, false on failure.
bool op_reg_set_previous(const char name)
- FUNC_ATTR_WARN_UNUSED_RESULT
{
int i = op_reg_index(name);
if (i == -1) {