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.c412
1 files changed, 267 insertions, 145 deletions
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 4f0c1b5cb9..595a699563 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -31,6 +31,7 @@
#include "nvim/indent.h"
#include "nvim/log.h"
#include "nvim/mark.h"
+#include "nvim/extmark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -49,6 +50,7 @@
#include "nvim/undo.h"
#include "nvim/macros.h"
#include "nvim/window.h"
+#include "nvim/lib/kvec.h"
#include "nvim/os/input.h"
#include "nvim/os/time.h"
@@ -87,6 +89,10 @@ struct block_def {
# include "ops.c.generated.h"
#endif
+// Flags for third item in "opchars".
+#define OPF_LINES 1 // operator always works on lines
+#define OPF_CHANGE 2 // operator changes text
+
/*
* The names of operators.
* IMPORTANT: Index must correspond with defines in vim.h!!!
@@ -94,36 +100,36 @@ struct block_def {
*/
static char opchars[][3] =
{
- { NUL, NUL, false }, // OP_NOP
- { 'd', NUL, false }, // OP_DELETE
- { 'y', NUL, false }, // OP_YANK
- { 'c', NUL, false }, // OP_CHANGE
- { '<', NUL, true }, // OP_LSHIFT
- { '>', NUL, true }, // OP_RSHIFT
- { '!', NUL, true }, // OP_FILTER
- { 'g', '~', false }, // OP_TILDE
- { '=', NUL, true }, // OP_INDENT
- { 'g', 'q', true }, // OP_FORMAT
- { ':', NUL, true }, // OP_COLON
- { 'g', 'U', false }, // OP_UPPER
- { 'g', 'u', false }, // OP_LOWER
- { 'J', NUL, true }, // DO_JOIN
- { 'g', 'J', true }, // DO_JOIN_NS
- { 'g', '?', false }, // OP_ROT13
- { 'r', NUL, false }, // OP_REPLACE
- { 'I', NUL, false }, // OP_INSERT
- { 'A', NUL, false }, // OP_APPEND
- { 'z', 'f', true }, // OP_FOLD
- { 'z', 'o', true }, // OP_FOLDOPEN
- { 'z', 'O', true }, // OP_FOLDOPENREC
- { 'z', 'c', true }, // OP_FOLDCLOSE
- { 'z', 'C', true }, // OP_FOLDCLOSEREC
- { 'z', 'd', true }, // OP_FOLDDEL
- { 'z', 'D', true }, // OP_FOLDDELREC
- { 'g', 'w', true }, // OP_FORMAT2
- { 'g', '@', false }, // OP_FUNCTION
- { Ctrl_A, NUL, false }, // OP_NR_ADD
- { Ctrl_X, NUL, false }, // OP_NR_SUB
+ { NUL, NUL, 0 }, // OP_NOP
+ { 'd', NUL, OPF_CHANGE }, // OP_DELETE
+ { 'y', NUL, 0 }, // OP_YANK
+ { 'c', NUL, OPF_CHANGE }, // OP_CHANGE
+ { '<', NUL, OPF_LINES | OPF_CHANGE }, // OP_LSHIFT
+ { '>', NUL, OPF_LINES | OPF_CHANGE }, // OP_RSHIFT
+ { '!', NUL, OPF_LINES | OPF_CHANGE }, // OP_FILTER
+ { 'g', '~', OPF_CHANGE }, // OP_TILDE
+ { '=', NUL, OPF_LINES | OPF_CHANGE }, // OP_INDENT
+ { 'g', 'q', OPF_LINES | OPF_CHANGE }, // OP_FORMAT
+ { ':', NUL, OPF_LINES }, // OP_COLON
+ { 'g', 'U', OPF_CHANGE }, // OP_UPPER
+ { 'g', 'u', OPF_CHANGE }, // OP_LOWER
+ { 'J', NUL, OPF_LINES | OPF_CHANGE }, // DO_JOIN
+ { 'g', 'J', OPF_LINES | OPF_CHANGE }, // DO_JOIN_NS
+ { 'g', '?', OPF_CHANGE }, // OP_ROT13
+ { 'r', NUL, OPF_CHANGE }, // OP_REPLACE
+ { 'I', NUL, OPF_CHANGE }, // OP_INSERT
+ { 'A', NUL, OPF_CHANGE }, // OP_APPEND
+ { 'z', 'f', OPF_LINES }, // OP_FOLD
+ { 'z', 'o', OPF_LINES }, // OP_FOLDOPEN
+ { 'z', 'O', OPF_LINES }, // OP_FOLDOPENREC
+ { 'z', 'c', OPF_LINES }, // OP_FOLDCLOSE
+ { 'z', 'C', OPF_LINES }, // OP_FOLDCLOSEREC
+ { 'z', 'd', OPF_LINES }, // OP_FOLDDEL
+ { 'z', 'D', OPF_LINES }, // OP_FOLDDELREC
+ { 'g', 'w', OPF_LINES | OPF_CHANGE }, // OP_FORMAT2
+ { 'g', '@', OPF_CHANGE }, // OP_FUNCTION
+ { Ctrl_A, NUL, OPF_CHANGE }, // OP_NR_ADD
+ { Ctrl_X, NUL, OPF_CHANGE }, // OP_NR_SUB
};
/*
@@ -167,7 +173,13 @@ int get_op_type(int char1, int char2)
*/
int op_on_lines(int op)
{
- return opchars[op][2];
+ return opchars[op][2] & OPF_LINES;
+}
+
+// Return TRUE if operator "op" changes text.
+int op_is_change(int op)
+{
+ return opchars[op][2] & OPF_CHANGE;
}
/*
@@ -219,8 +231,6 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
++curwin->w_cursor.lnum;
}
- changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L, true);
-
if (oap->motion_type == kMTBlockWise) {
curwin->w_cursor.lnum = oap->start.lnum;
curwin->w_cursor.col = block_col;
@@ -251,7 +261,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
sprintf((char *)IObuff, _("%" PRId64 " lines %sed %d times"),
(int64_t)oap->line_count, s, amount);
}
- msg(IObuff);
+ msg_attr_keep(IObuff, 0, true, false);
}
/*
@@ -260,8 +270,11 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
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 (curbuf->b_op_end.col > 0) {
+ curbuf->b_op_end.col--;
+ }
+
+ changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L, true);
}
// Shift the current line one shiftwidth left (if left != 0) or right
@@ -270,20 +283,21 @@ void shift_line(
int left,
int round,
int amount,
- int call_changed_bytes /* call changed_bytes() */
+ int call_changed_bytes // call changed_bytes()
)
{
int count;
int i, j;
int p_sw = get_sw_value(curbuf);
- count = get_indent(); /* get current indent */
+ count = get_indent(); // get current indent
- if (round) { /* round off indent */
- i = count / p_sw; /* number of p_sw rounded down */
- j = count % p_sw; /* extra spaces */
- if (j && left) /* first remove extra spaces */
- --amount;
+ if (round) { // round off indent
+ i = count / p_sw; // number of p_sw rounded down
+ j = count % p_sw; // extra spaces
+ if (j && left) { // first remove extra spaces
+ amount--;
+ }
if (left) {
i -= amount;
if (i < 0)
@@ -291,7 +305,7 @@ void shift_line(
} else
i += amount;
count = i * p_sw;
- } else { /* original vi indent */
+ } else { // original vi indent
if (left) {
count -= p_sw * amount;
if (count < 0)
@@ -300,11 +314,12 @@ void shift_line(
count += p_sw * amount;
}
- /* Set new indent */
- if (State & VREPLACE_FLAG)
- change_indent(INDENT_SET, count, FALSE, NUL, call_changed_bytes);
- else
+ // Set new indent
+ if (State & VREPLACE_FLAG) {
+ change_indent(INDENT_SET, count, false, NUL, call_changed_bytes);
+ } else {
(void)set_indent(count, call_changed_bytes ? SIN_CHANGED : 0);
+ }
}
/*
@@ -340,6 +355,8 @@ static void shift_block(oparg_T *oap, int amount)
char_u *const oldp = get_cursor_line_ptr();
+ int startcol, oldlen, newlen;
+
if (!left) {
/*
* 1. Get start vcol
@@ -349,16 +366,13 @@ static void shift_block(oparg_T *oap, int amount)
*/
total += bd.pre_whitesp; // all virtual WS up to & incl a split TAB
colnr_T ws_vcol = bd.start_vcol - bd.pre_whitesp;
+ char_u * old_textstart = bd.textstart;
if (bd.startspaces) {
- if (has_mbyte) {
- if ((*mb_ptr2len)(bd.textstart) == 1) {
- bd.textstart++;
- } else {
- ws_vcol = 0;
- bd.startspaces = 0;
- }
- } else {
+ if (utfc_ptr2len(bd.textstart) == 1) {
bd.textstart++;
+ } else {
+ ws_vcol = 0;
+ bd.startspaces = 0;
}
}
for (; ascii_iswhite(*bd.textstart); ) {
@@ -375,14 +389,19 @@ static void shift_block(oparg_T *oap, int amount)
j = ((ws_vcol % p_ts) + total) % p_ts; /* number of spp */
else
j = total;
- /* if we're splitting a TAB, allow for it */
- bd.textcol -= bd.pre_whitesp_c - (bd.startspaces != 0);
+
+ // if we're splitting a TAB, allow for it
+ int col_pre = bd.pre_whitesp_c - (bd.startspaces != 0);
+ bd.textcol -= col_pre;
const int len = (int)STRLEN(bd.textstart) + 1;
int col = bd.textcol + i +j + len;
assert(col >= 0);
newp = (char_u *)xmalloc((size_t)col);
memset(newp, NUL, (size_t)col);
memmove(newp, oldp, (size_t)bd.textcol);
+ startcol = bd.textcol;
+ oldlen = (int)(bd.textstart-old_textstart) + col_pre;
+ newlen = i+j;
memset(newp + bd.textcol, TAB, (size_t)i);
memset(newp + bd.textcol + i, ' ', (size_t)j);
/* the end */
@@ -466,7 +485,10 @@ static void shift_block(oparg_T *oap, int amount)
// - the rest of the line, pointed to by non_white.
new_line_len = verbatim_diff + fill + STRLEN(non_white) + 1;
- newp = (char_u *) xmalloc(new_line_len);
+ newp = (char_u *)xmalloc(new_line_len);
+ startcol = (int)verbatim_diff;
+ oldlen = bd.textcol + (int)(non_white - bd.textstart) - (int)verbatim_diff;
+ newlen = (int)fill;
memmove(newp, oldp, verbatim_diff);
memset(newp + verbatim_diff, ' ', fill);
STRMOVE(newp + verbatim_diff + fill, non_white);
@@ -474,6 +496,9 @@ static void shift_block(oparg_T *oap, int amount)
// replace the line
ml_replace(curwin->w_cursor.lnum, newp, false);
changed_bytes(curwin->w_cursor.lnum, (colnr_T)bd.textcol);
+ extmark_splice(curbuf, (int)curwin->w_cursor.lnum-1, startcol,
+ 0, oldlen, 0, newlen,
+ kExtmarkUndo);
State = oldstate;
curwin->w_cursor.col = oldcol;
p_ri = old_p_ri;
@@ -545,6 +570,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
// copy up to shifted part
memmove(newp, oldp, (size_t)offset);
oldp += offset;
+ int startcol = offset;
// insert pre-padding
memset(newp + offset, ' ', (size_t)spaces);
@@ -553,6 +579,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
memmove(newp + offset + spaces, s, s_len);
offset += (int)s_len;
+ int skipped = 0;
if (spaces && !bdp->is_short) {
// insert post-padding
memset(newp + offset + spaces, ' ', (size_t)(p_ts - spaces));
@@ -560,6 +587,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
oldp++;
// We allowed for that TAB, remember this now
count++;
+ skipped = 1;
}
if (spaces > 0)
@@ -567,6 +595,9 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
STRMOVE(newp + offset, oldp);
ml_replace(lnum, newp, false);
+ extmark_splice(curbuf, (int)lnum-1, startcol,
+ 0, skipped,
+ 0, offset-startcol, kExtmarkUndo);
if (lnum == oap->end.lnum) {
/* Set "']" mark to the end of the block instead of the end of
@@ -621,9 +652,10 @@ void op_reindent(oparg_T *oap, Indenter how)
amount = how(); /* get the indent for this line */
if (amount >= 0 && set_indent(amount, SIN_UNDO)) {
- /* did change the indent, call changed_lines() later */
- if (first_changed == 0)
+ // did change the indent, call changed_lines() later
+ if (first_changed == 0) {
first_changed = curwin->w_cursor.lnum;
+ }
last_changed = curwin->w_cursor.lnum;
}
}
@@ -671,13 +703,15 @@ int get_expr_register(void)
{
char_u *new_line;
- new_line = getcmdline('=', 0L, 0);
- if (new_line == NULL)
+ new_line = getcmdline('=', 0L, 0, true);
+ if (new_line == NULL) {
return NUL;
- if (*new_line == NUL) /* use previous line */
+ }
+ if (*new_line == NUL) { // use previous line
xfree(new_line);
- else
+ } else {
set_expr_line(new_line);
+ }
return '=';
}
@@ -808,6 +842,15 @@ static bool is_append_register(int regname)
return ASCII_ISUPPER(regname);
}
+/// @see get_yank_register
+/// @returns true when register should be inserted literally
+/// (selection or clipboard)
+static inline bool is_literal_register(int regname)
+ FUNC_ATTR_CONST
+{
+ return regname == '*' || regname == '+';
+}
+
/// Returns a copy of contents in register `name`
/// for use in do_put. Should be freed by caller.
yankreg_T *copy_register(int name)
@@ -1118,11 +1161,12 @@ static int put_in_typebuf(
*/
int insert_reg(
int regname,
- int literally /* insert literally, not as if typed */
+ bool literally_arg // insert literally, not as if typed
)
{
int retval = OK;
bool allocated;
+ const bool literally = literally_arg || is_literal_register(regname);
/*
* It is possible to get into an endless loop by having CTRL-R a in
@@ -1188,9 +1232,7 @@ static void stuffescaped(const char *arg, int literally)
/* stuff a single special character */
if (*arg != NUL) {
- const int c = (has_mbyte
- ? mb_cptr2char_adv((const char_u **)&arg)
- : (uint8_t)(*arg++));
+ const int c = mb_cptr2char_adv((const char_u **)&arg);
if (literally && ((c < ' ' && c != TAB) || c == DEL)) {
stuffcharReadbuff(Ctrl_V);
}
@@ -1294,12 +1336,14 @@ bool get_spec_reg(
/// register contents will be interpreted as commands.
///
/// @param regname Register name.
-/// @param literally Insert text literally instead of "as typed".
+/// @param literally_arg Insert text literally instead of "as typed".
/// @param remcr When true, don't add CR characters.
///
/// @returns FAIL for failure, OK otherwise
-bool cmdline_paste_reg(int regname, bool literally, bool remcr)
+bool cmdline_paste_reg(int regname, bool literally_arg, bool remcr)
{
+ const bool literally = literally_arg || is_literal_register(regname);
+
yankreg_T *reg = get_yank_register(regname, YREG_PASTE);
if (reg->y_array == NULL)
return FAIL;
@@ -1345,7 +1389,7 @@ int op_delete(oparg_T *oap)
linenr_T lnum;
char_u *ptr;
char_u *newp, *oldp;
- struct block_def bd;
+ struct block_def bd = { 0 };
linenr_T old_lcount = curbuf->b_ml.ml_line_count;
if (curbuf->b_ml.ml_flags & ML_EMPTY) { // nothing to do
@@ -1362,8 +1406,7 @@ int op_delete(oparg_T *oap)
return FAIL;
}
- if (has_mbyte)
- mb_adjust_opend(oap);
+ mb_adjust_opend(oap);
/*
* Imitate the strange Vi behaviour: If the delete spans more than one
@@ -1490,6 +1533,11 @@ int op_delete(oparg_T *oap)
STRMOVE(newp + bd.textcol + bd.startspaces + bd.endspaces, oldp);
// replace the line
ml_replace(lnum, newp, false);
+
+ extmark_splice(curbuf, (int)lnum-1, bd.textcol,
+ 0, bd.textlen,
+ 0, bd.startspaces+bd.endspaces,
+ kExtmarkUndo);
}
check_cursor_col();
@@ -1558,6 +1606,7 @@ int op_delete(oparg_T *oap)
oap->end = curwin->w_cursor;
curwin->w_cursor = oap->start;
}
+ mb_adjust_opend(oap);
}
if (oap->line_count == 1) { /* delete characters within one line */
@@ -1605,6 +1654,8 @@ int op_delete(oparg_T *oap)
(linenr_T)(curwin->w_cursor.lnum + oap->line_count)) == FAIL)
return FAIL;
+ curbuf_splice_pending++;
+ pos_T startpos = curwin->w_cursor; // start position for delete
truncate_line(true); // delete from cursor to end of line
curpos = curwin->w_cursor; // remember curwin->w_cursor
@@ -1618,6 +1669,9 @@ int op_delete(oparg_T *oap)
oap->op_type == OP_DELETE && !oap->is_VIsual);
curwin->w_cursor = curpos; // restore curwin->w_cursor
(void)do_join(2, false, false, false, false);
+ curbuf_splice_pending--;
+ extmark_splice(curbuf, (int)startpos.lnum-1, startpos.col,
+ (int)oap->line_count-1, n, 0, 0, kExtmarkUndo);
}
}
@@ -1627,8 +1681,9 @@ setmarks:
if (oap->motion_type == kMTBlockWise) {
curbuf->b_op_end.lnum = oap->end.lnum;
curbuf->b_op_end.col = oap->start.col;
- } else
+ } else {
curbuf->b_op_end = oap->start;
+ }
curbuf->b_op_start = oap->start;
return OK;
@@ -1653,8 +1708,11 @@ static void mb_adjust_opend(oparg_T *oap)
*/
static inline void pbyte(pos_T lp, int c)
{
- assert(c <= UCHAR_MAX);
- *(ml_get_buf(curbuf, lp.lnum, true) + lp.col) = (char_u)c;
+ assert(c <= UCHAR_MAX);
+ *(ml_get_buf(curbuf, lp.lnum, true) + lp.col) = (char_u)c;
+ if (!curbuf_splice_pending) {
+ extmark_splice(curbuf, (int)lp.lnum-1, lp.col, 0, 1, 0, 1, kExtmarkUndo);
+ }
}
// Replace the character under the cursor with "c".
@@ -1694,8 +1752,7 @@ int op_replace(oparg_T *oap, int c)
c = NL;
}
- if (has_mbyte)
- mb_adjust_opend(oap);
+ mb_adjust_opend(oap);
if (u_save((linenr_T)(oap->start.lnum - 1),
(linenr_T)(oap->end.lnum + 1)) == FAIL)
@@ -1775,6 +1832,7 @@ int op_replace(oparg_T *oap, int c)
size_t after_p_len = 0;
int col = oldlen - bd.textcol - bd.textlen + 1;
assert(col >= 0);
+ int newrows = 0, newcols = 0;
if (had_ctrl_v_cr || (c != '\r' && c != '\n')) {
// strlen(newp) at this point
int newp_len = bd.textcol + bd.startspaces;
@@ -1787,21 +1845,27 @@ int op_replace(oparg_T *oap, int c)
newp_len += bd.endspaces;
// copy the part after the changed part
memmove(newp + newp_len, oldp, (size_t)col);
- }
+ }
+ newcols = newp_len - bd.textcol;
} else {
// Replacing with \r or \n means splitting the line.
after_p_len = (size_t)col;
after_p = (char_u *)xmalloc(after_p_len);
memmove(after_p, oldp, after_p_len);
+ newrows = 1;
}
// replace the line
ml_replace(curwin->w_cursor.lnum, newp, false);
+ linenr_T baselnum = curwin->w_cursor.lnum;
if (after_p != NULL) {
ml_append(curwin->w_cursor.lnum++, after_p, (int)after_p_len, false);
appended_lines_mark(curwin->w_cursor.lnum, 1L);
oap->end.lnum++;
xfree(after_p);
}
+ extmark_splice(curbuf, (int)baselnum-1, bd.textcol,
+ 0, bd.textlen,
+ newrows, newcols, kExtmarkUndo);
}
} else {
// Characterwise or linewise motion replace.
@@ -1814,6 +1878,8 @@ int op_replace(oparg_T *oap, int c)
} else if (!oap->inclusive)
dec(&(oap->end));
+ // TODO(bfredl): we could batch all the splicing
+ // done on the same line, at least
while (ltoreq(curwin->w_cursor, oap->end)) {
n = gchar_cursor();
if (n != NUL) {
@@ -1961,17 +2027,16 @@ void op_tilde(oparg_T *oap)
* Returns TRUE if some character was changed.
*/
static int swapchars(int op_type, pos_T *pos, int length)
+ FUNC_ATTR_NONNULL_ALL
{
- int todo;
int did_change = 0;
- for (todo = length; todo > 0; --todo) {
- if (has_mbyte) {
- int len = (*mb_ptr2len)(ml_get_pos(pos));
+ for (int todo = length; todo > 0; todo--) {
+ const int len = utfc_ptr2len(ml_get_pos(pos));
- /* we're counting bytes, not characters */
- if (len > 0)
- todo -= len - 1;
+ // we're counting bytes, not characters
+ if (len > 0) {
+ todo -= len - 1;
}
did_change |= swapchar(op_type, pos);
if (inc(pos) == -1) /* at end of file */
@@ -2026,8 +2091,8 @@ bool swapchar(int op_type, pos_T *pos)
pos_T sp = curwin->w_cursor;
curwin->w_cursor = *pos;
- /* don't use del_char(), it also removes composing chars */
- del_bytes(utf_ptr2len(get_cursor_pos_ptr()), FALSE, FALSE);
+ // don't use del_char(), it also removes composing chars
+ del_bytes(utf_ptr2len(get_cursor_pos_ptr()), false, false);
ins_char(nc);
curwin->w_cursor = sp;
} else {
@@ -2100,8 +2165,9 @@ void op_insert(oparg_T *oap, long count1)
* values in "bd". */
if (u_save_cursor() == FAIL)
return;
- for (i = 0; i < bd.endspaces; i++)
+ for (i = 0; i < bd.endspaces; i++) {
ins_char(' ');
+ }
bd.textlen += bd.endspaces;
}
} else {
@@ -2333,6 +2399,9 @@ int op_change(oparg_T *oap)
oldp += bd.textcol;
STRMOVE(newp + offset, oldp);
ml_replace(linenr, newp, false);
+ extmark_splice(curbuf, (int)linenr-1, bd.textcol,
+ 0, 0,
+ 0, vpos.coladd+(int)ins_len, kExtmarkUndo);
}
}
check_cursor();
@@ -2477,7 +2546,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
case kMTCharWise:
{
colnr_T startcol = 0, endcol = MAXCOL;
- int is_oneChar = FALSE;
+ int is_oneChar = false;
colnr_T cs, ce;
p = ml_get(lnum);
bd.startspaces = 0;
@@ -2508,8 +2577,8 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
&& utf_head_off(p, p + endcol) == 0)) {
if (oap->start.lnum == oap->end.lnum
&& oap->start.col == oap->end.col) {
- /* Special case: inside a single char */
- is_oneChar = TRUE;
+ // Special case: inside a single char
+ is_oneChar = true;
bd.startspaces = oap->end.coladd
- oap->start.coladd + oap->inclusive;
endcol = startcol;
@@ -2671,14 +2740,18 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
tv_dict_add_str(dict, S_LEN("regname"), buf);
// Motion type: inclusive or exclusive.
- tv_dict_add_special(dict, S_LEN("inclusive"),
- oap->inclusive ? kSpecialVarTrue : kSpecialVarFalse);
+ tv_dict_add_bool(dict, S_LEN("inclusive"),
+ oap->inclusive ? kBoolVarTrue : kBoolVarFalse);
// Kind of operation: yank, delete, change).
buf[0] = (char)get_op_char(oap->op_type);
buf[1] = NUL;
tv_dict_add_str(dict, S_LEN("operator"), buf);
+ // Selection type: visual or not.
+ tv_dict_add_bool(dict, S_LEN("visual"),
+ oap->is_VIsual ? kBoolVarTrue : kBoolVarFalse);
+
tv_dict_set_keys_readonly(dict);
textlock++;
apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf);
@@ -2688,7 +2761,6 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
recursive = false;
}
-
/*
* Put contents of register "regname" into the text.
* Caller must check "regname" to be valid!
@@ -2703,8 +2775,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
char_u *oldp;
int yanklen;
size_t totlen = 0; // init for gcc
- linenr_T lnum;
- colnr_T col;
+ linenr_T lnum = 0;
+ colnr_T col = 0;
size_t i; // index in y_array[]
MotionType y_type;
size_t y_size;
@@ -2998,7 +3070,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
// move to start of next multi-byte character
- curwin->w_cursor.col += (*mb_ptr2len)(get_cursor_pos_ptr());
+ curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr());
col++;
} else {
getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2);
@@ -3008,10 +3080,12 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (ve_flags == VE_ALL
&& (curwin->w_cursor.coladd > 0
|| endcol2 == curwin->w_cursor.col)) {
- if (dir == FORWARD && c == NUL)
- ++col;
- if (dir != FORWARD && c != NUL)
- ++curwin->w_cursor.col;
+ if (dir == FORWARD && c == NUL) {
+ col++;
+ }
+ if (dir != FORWARD && c != NUL && curwin->w_cursor.coladd > 0) {
+ curwin->w_cursor.col++;
+ }
if (c == TAB) {
if (dir == BACKWARD && curwin->w_cursor.col)
curwin->w_cursor.col--;
@@ -3108,6 +3182,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
assert(columns >= 0);
memmove(ptr, oldp + bd.textcol + delcount, (size_t)columns);
ml_replace(curwin->w_cursor.lnum, newp, false);
+ extmark_splice(curbuf, (int)curwin->w_cursor.lnum-1, bd.textcol,
+ 0, delcount,
+ 0, (int)totlen,
+ kExtmarkUndo);
++curwin->w_cursor.lnum;
if (i == 0)
@@ -3209,6 +3287,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (totlen && (restart_edit != 0 || (flags & PUT_CURSEND)))
++curwin->w_cursor.col;
changed_bytes(lnum, col);
+ extmark_splice(curbuf, (int)lnum-1, col,
+ 0, 0,
+ 0, (int)totlen, kExtmarkUndo);
} else {
// Insert at least one line. When y_type is kMTCharWise, break the first
// line in two.
@@ -3264,13 +3345,22 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
first_indent = FALSE;
} else if ((indent = get_indent() + indent_diff) < 0)
indent = 0;
- (void)set_indent(indent, 0);
+ (void)set_indent(indent, SIN_NOMARK);
curwin->w_cursor = old_pos;
/* remember how many chars were removed */
if (cnt == count && i == y_size - 1)
lendiff -= (int)STRLEN(ml_get(lnum));
}
}
+
+ if (y_type == kMTCharWise) {
+ extmark_splice(curbuf, (int)new_cursor.lnum-1, col, 0, 0,
+ (int)y_size-1, (int)STRLEN(y_array[y_size-1]),
+ kExtmarkUndo);
+ } else if (y_type == kMTLineWise && flags & PUT_LINE_SPLIT) {
+ extmark_splice(curbuf, (int)new_cursor.lnum-1, col, 0, 0,
+ (int)y_size+1, 0, kExtmarkUndo);
+ }
}
error:
@@ -3281,11 +3371,13 @@ error:
curbuf->b_op_start.lnum++;
}
// Skip mark_adjust when adding lines after the last one, there
- // can't be marks there. But still needed in diff mode.
+ // can't be marks there.
if (curbuf->b_op_start.lnum + (y_type == kMTCharWise) - 1 + nr_lines
- < curbuf->b_ml.ml_line_count || curwin->w_p_diff) {
+ < curbuf->b_ml.ml_line_count) {
+ ExtmarkOp kind = (y_type == kMTLineWise && !(flags & PUT_LINE_SPLIT))
+ ? kExtmarkUndo : kExtmarkNOOP;
mark_adjust(curbuf->b_op_start.lnum + (y_type == kMTCharWise),
- (linenr_T)MAXLNUM, nr_lines, 0L, false);
+ (linenr_T)MAXLNUM, nr_lines, 0L, kind);
}
// note changed text for displaying and folding
@@ -3347,7 +3439,7 @@ end:
/* If the cursor is past the end of the line put it at the end. */
adjust_cursor_eol();
-}
+} // NOLINT(readability/fn_size)
/*
* When the cursor is on the NUL past the end of the line and it should not be
@@ -3543,7 +3635,7 @@ dis_msg(
while (*p != NUL
&& !(*p == ESC && skip_esc && *(p + 1) == NUL)
&& (n -= ptr2cells(p)) >= 0) {
- if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) {
+ if ((l = utfc_ptr2len(p)) > 1) {
msg_outtrans_len(p, l);
p += l;
} else
@@ -3689,7 +3781,10 @@ int do_join(size_t count,
if (insert_space && t > 0) {
curr = skipwhite(curr);
- if (*curr != ')' && currsize != 0 && endcurr1 != TAB
+ if (*curr != NUL
+ && *curr != ')'
+ && sumsize != 0
+ && endcurr1 != TAB
&& (!has_format_option(FO_MBYTE_JOIN)
|| (utf_ptr2char(curr) < 0x100 && endcurr1 < 0x100))
&& (!has_format_option(FO_MBYTE_JOIN2)
@@ -3706,6 +3801,13 @@ int do_join(size_t count,
}
}
}
+
+ if (t > 0 && curbuf_splice_pending == 0) {
+ extmark_splice(curbuf, (int)curwin->w_cursor.lnum-1, sumsize,
+ 1, (int)(curr- curr_start),
+ 0, spaces[t],
+ kExtmarkUndo);
+ }
currsize = (int)STRLEN(curr);
sumsize += currsize + spaces[t];
endcurr1 = endcurr2 = NUL;
@@ -3740,6 +3842,9 @@ int do_join(size_t count,
* column. This is not Vi compatible, but Vi deletes the marks, thus that
* should not really be a problem.
*/
+
+ curbuf_splice_pending++;
+
for (t = (linenr_T)count - 1;; t--) {
cend -= currsize;
memmove(cend, curr, (size_t)currsize);
@@ -3751,12 +3856,17 @@ int do_join(size_t count,
// If deleting more spaces than adding, the cursor moves no more than
// what is added if it is inside these spaces.
const int spaces_removed = (int)((curr - curr_start) - spaces[t]);
+ linenr_T lnum = curwin->w_cursor.lnum + t;
+ colnr_T mincol = (colnr_T)0;
+ long lnum_amount = (linenr_T)-t;
+ long col_amount = (long)(cend - newp - spaces_removed);
+
+ mark_col_adjust(lnum, mincol, lnum_amount, col_amount, spaces_removed);
- mark_col_adjust(curwin->w_cursor.lnum + t, (colnr_T)0, (linenr_T)-t,
- (long)(cend - newp - spaces_removed), spaces_removed);
if (t == 0) {
break;
}
+
curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
if (remove_comments)
curr += comments[t - 1];
@@ -3764,6 +3874,7 @@ int do_join(size_t count,
curr = skipwhite(curr);
currsize = (int)STRLEN(curr);
}
+
ml_replace(curwin->w_cursor.lnum, newp, false);
if (setmark) {
@@ -3786,6 +3897,7 @@ int do_join(size_t count,
curwin->w_cursor.lnum++;
del_lines((long)count - 1, false);
curwin->w_cursor.lnum = t;
+ curbuf_splice_pending--;
/*
* Set the cursor column:
@@ -4189,7 +4301,7 @@ format_lines(
int indent = (int)getwhitecols_curline();
if (indent > 0) {
- (void)del_bytes(indent, FALSE, FALSE);
+ (void)del_bytes(indent, false, false);
mark_col_adjust(curwin->w_cursor.lnum,
(colnr_T)0, 0L, (long)-indent, 0);
}
@@ -4279,15 +4391,13 @@ int paragraph_start(linenr_T lnum)
return TRUE; /* after empty line */
do_comments = has_format_option(FO_Q_COMS);
- if (fmt_check_par(lnum - 1
- , &leader_len, &leader_flags, do_comments
- ))
- return TRUE; /* after non-paragraph line */
+ if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments)) {
+ return true; // after non-paragraph line
+ }
- if (fmt_check_par(lnum
- , &next_leader_len, &next_leader_flags, do_comments
- ))
- return TRUE; /* "lnum" is not a paragraph line */
+ if (fmt_check_par(lnum, &next_leader_len, &next_leader_flags, do_comments)) {
+ return true; // "lnum" is not a paragraph line
+ }
if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1))
return TRUE; /* missing trailing space in previous line. */
@@ -4324,14 +4434,17 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum,
char_u *line;
char_u *prev_pstart;
char_u *prev_pend;
+ const int lbr_saved = curwin->w_p_lbr;
+ // Avoid a problem with unwanted linebreaks in block mode.
+ curwin->w_p_lbr = false;
bdp->startspaces = 0;
bdp->endspaces = 0;
bdp->textlen = 0;
bdp->start_vcol = 0;
bdp->end_vcol = 0;
- bdp->is_short = FALSE;
- bdp->is_oneChar = FALSE;
+ bdp->is_short = false;
+ bdp->is_oneChar = false;
bdp->pre_whitesp = 0;
bdp->pre_whitesp_c = 0;
bdp->end_char_vcols = 0;
@@ -4357,9 +4470,10 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum,
bdp->start_char_vcols = incr;
if (bdp->start_vcol < oap->start_vcol) { /* line too short */
bdp->end_vcol = bdp->start_vcol;
- bdp->is_short = TRUE;
- if (!is_del || oap->op_type == OP_APPEND)
+ bdp->is_short = true;
+ if (!is_del || oap->op_type == OP_APPEND) {
bdp->endspaces = oap->end_vcol - oap->start_vcol + 1;
+ }
} else {
/* notice: this converts partly selected Multibyte characters to
* spaces, too. */
@@ -4368,11 +4482,11 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum,
bdp->startspaces = bdp->start_char_vcols - bdp->startspaces;
pend = pstart;
bdp->end_vcol = bdp->start_vcol;
- if (bdp->end_vcol > oap->end_vcol) { /* it's all in one character */
- bdp->is_oneChar = TRUE;
- if (oap->op_type == OP_INSERT)
+ if (bdp->end_vcol > oap->end_vcol) { // it's all in one character
+ bdp->is_oneChar = true;
+ if (oap->op_type == OP_INSERT) {
bdp->endspaces = bdp->start_char_vcols - bdp->startspaces;
- else if (oap->op_type == OP_APPEND) {
+ } else if (oap->op_type == OP_APPEND) {
bdp->startspaces += oap->end_vcol - oap->start_vcol + 1;
bdp->endspaces = bdp->start_char_vcols - bdp->startspaces;
} else {
@@ -4397,17 +4511,16 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum,
if (bdp->end_vcol <= oap->end_vcol
&& (!is_del
|| oap->op_type == OP_APPEND
- || oap->op_type == OP_REPLACE)) { /* line too short */
- bdp->is_short = TRUE;
- /* Alternative: include spaces to fill up the block.
- * Disadvantage: can lead to trailing spaces when the line is
- * short where the text is put */
- /* if (!is_del || oap->op_type == OP_APPEND) */
- if (oap->op_type == OP_APPEND || virtual_op)
+ || oap->op_type == OP_REPLACE)) { // line too short
+ bdp->is_short = true;
+ // Alternative: include spaces to fill up the block.
+ // Disadvantage: can lead to trailing spaces when the line is
+ // short where the text is put
+ // if (!is_del || oap->op_type == OP_APPEND)
+ if (oap->op_type == OP_APPEND || virtual_op) {
bdp->endspaces = oap->end_vcol - bdp->end_vcol
+ oap->inclusive;
- else
- bdp->endspaces = 0; /* replace doesn't add characters */
+ }
} else if (bdp->end_vcol > oap->end_vcol) {
bdp->endspaces = bdp->end_vcol - oap->end_vcol - 1;
if (!is_del && bdp->endspaces) {
@@ -4424,6 +4537,7 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum,
}
bdp->textcol = (colnr_T) (pstart - line);
bdp->textstart = pstart;
+ curwin->w_p_lbr = lbr_saved;
}
/// Handle the add/subtract operator.
@@ -4536,7 +4650,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd)
int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
{
int col;
- char_u *buf1;
+ char_u *buf1 = NULL;
char_u buf2[NUMBUFLEN];
int pre; // 'X' or 'x': hex; '0': octal; 'B' or 'b': bin
static bool hexupper = false; // 0xABC
@@ -4559,17 +4673,23 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
int maxlen = 0;
pos_T startpos;
pos_T endpos;
+ colnr_T save_coladd = 0;
dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL); // "heX"
dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL); // "Octal"
dobin = (vim_strchr(curbuf->b_p_nf, 'b') != NULL); // "Bin"
doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL); // "alPha"
+ if (virtual_active()) {
+ save_coladd = pos->coladd;
+ pos->coladd = 0;
+ }
+
curwin->w_cursor = *pos;
ptr = ml_get(pos->lnum);
col = pos->col;
- if (*ptr == NUL) {
+ if (*ptr == NUL || col + !!save_coladd >= (int)STRLEN(ptr)) {
goto theend;
}
@@ -4641,7 +4761,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
if (visual) {
while (ptr[col] != NUL && length > 0 && !ascii_isdigit(ptr[col])
&& !(doalp && ASCII_ISALPHA(ptr[col]))) {
- int mb_len = MB_PTR2LEN(ptr + col);
+ int mb_len = utfc_ptr2len(ptr + col);
col += mb_len;
length -= mb_len;
@@ -4845,7 +4965,6 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
*ptr = NUL;
STRCAT(buf1, buf2);
ins_str(buf1); // insert the new number
- xfree(buf1);
endpos = curwin->w_cursor;
if (curwin->w_cursor.col) {
curwin->w_cursor.col--;
@@ -4860,10 +4979,13 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
}
theend:
+ xfree(buf1);
if (visual) {
curwin->w_cursor = save_cursor;
} else if (did_change) {
curwin->w_set_curswant = true;
+ } else if (virtual_active()) {
+ curwin->w_cursor.coladd = save_coladd;
}
return did_change;
@@ -5114,8 +5236,7 @@ void write_reg_contents_lst(int name, char_u **strings,
/// write_reg_contents_ex - store `str` in register `name`
///
-/// If `str` ends in '\n' or '\r', use linewise, otherwise use
-/// characterwise.
+/// If `str` ends in '\n' or '\r', use linewise, otherwise use charwise.
///
/// @warning when `name` is '/', `len` and `must_append` are ignored. This
/// means that `str` MUST be NUL-terminated.
@@ -5576,7 +5697,8 @@ void cursor_pos_info(dict_T *dict)
bom_count = bomb_size();
if (dict == NULL && bom_count > 0) {
- vim_snprintf((char *)IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff),
+ const size_t len = STRLEN(IObuff);
+ vim_snprintf((char *)IObuff + len, IOSIZE - len,
_("(+%" PRId64 " for BOM)"), (int64_t)bom_count);
}
if (dict == NULL) {