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.c416
1 files changed, 245 insertions, 171 deletions
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 68ef27222c..b39b139f9b 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -1,3 +1,6 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
/*
* ops.c: implementation of various operators: op_shift, op_delete, op_tilde,
* op_change, op_yank, do_put, do_join
@@ -52,12 +55,11 @@ static yankreg_T y_regs[NUM_REGISTERS];
static yankreg_T *y_previous = NULL; /* ptr to last written yankreg */
-static bool clipboard_didwarn_unnamed = false;
-
// for behavior between start_batch_changes() and end_batch_changes())
-static bool clipboard_delay_update = false; // delay clipboard update
static int batch_change_count = 0; // inside a script
+static bool clipboard_delay_update = false; // delay clipboard update
static bool clipboard_needs_update = false; // clipboard was updated
+static bool clipboard_didwarn = false;
/*
* structure used by block_prep, op_delete and op_yank for blockwise operators
@@ -802,7 +804,6 @@ static bool is_append_register(int 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)
- FUNC_ATTR_MALLOC
FUNC_ATTR_NONNULL_RET
{
yankreg_T *reg = get_yank_register(name, YREG_PASTE);
@@ -936,13 +937,11 @@ static int stuff_yank(int regname, char_u *p)
static int execreg_lastc = NUL;
-/*
- * execute a yank register: copy it into the stuff buffer
- *
- * return FAIL for failure, OK otherwise
- */
-int
-do_execreg (
+/// Execute a yank register: copy it into the stuff buffer
+///
+/// Return FAIL for failure, OK otherwise
+int
+do_execreg(
int regname,
int colon, /* insert ':' before each line */
int addcr, /* always add '\n' to end of line */
@@ -1408,6 +1407,9 @@ int op_delete(oparg_T *oap)
}
if (oap->regname == 0) {
+ if (reg == NULL) {
+ abort();
+ }
set_clipboard(0, reg);
do_autocmd_textyankpost(oap, reg);
}
@@ -1627,13 +1629,18 @@ int op_replace(oparg_T *oap, int c)
colnr_T oldlen;
struct block_def bd;
char_u *after_p = NULL;
- int had_ctrl_v_cr = (c == -1 || c == -2);
+ int had_ctrl_v_cr = false;
if ((curbuf->b_ml.ml_flags & ML_EMPTY ) || oap->empty)
return OK; /* nothing to do */
- if (had_ctrl_v_cr)
- c = (c == -1 ? '\r' : '\n');
+ if (c == REPLACE_CR_NCHAR) {
+ had_ctrl_v_cr = true;
+ c = CAR;
+ } else if (c == REPLACE_NL_NCHAR) {
+ had_ctrl_v_cr = true;
+ c = NL;
+ }
if (has_mbyte)
mb_adjust_opend(oap);
@@ -1711,7 +1718,7 @@ int op_replace(oparg_T *oap, int c)
// insert pre-spaces
memset(newp + bd.textcol, ' ', (size_t)bd.startspaces);
// insert replacement chars CHECK FOR ALLOCATED SPACE
- // -1/-2 is used for entering CR literally.
+ // REPLACE_CR_NCHAR/REPLACE_NL_NCHAR is used for entering CR literally.
size_t after_p_len = 0;
if (had_ctrl_v_cr || (c != '\r' && c != '\n')) {
// strlen(newp) at this point
@@ -1956,16 +1963,18 @@ int swapchar(int op_type, pos_T *pos)
if (enc_dbcs != 0 && c >= 0x100) /* No lower/uppercase letter */
return FALSE;
nc = c;
- if (vim_islower(c)) {
- if (op_type == OP_ROT13)
+ if (mb_islower(c)) {
+ if (op_type == OP_ROT13) {
nc = ROT13(c, 'a');
- else if (op_type != OP_LOWER)
- nc = vim_toupper(c);
- } else if (vim_isupper(c)) {
- if (op_type == OP_ROT13)
+ } else if (op_type != OP_LOWER) {
+ nc = mb_toupper(c);
+ }
+ } else if (mb_isupper(c)) {
+ if (op_type == OP_ROT13) {
nc = ROT13(c, 'A');
- else if (op_type != OP_UPPER)
- nc = vim_tolower(c);
+ } else if (op_type != OP_UPPER) {
+ nc = mb_tolower(c);
+ }
}
if (nc != c) {
if (enc_utf8 && (c >= 0x80 || nc >= 0x80)) {
@@ -2048,15 +2057,16 @@ void op_insert(oparg_T *oap, long count1)
curwin->w_cursor = oap->end;
check_cursor_col();
- /* Works just like an 'i'nsert on the next character. */
- if (!lineempty(curwin->w_cursor.lnum)
- && oap->start_vcol != oap->end_vcol)
+ // Works just like an 'i'nsert on the next character.
+ if (!LINEEMPTY(curwin->w_cursor.lnum)
+ && oap->start_vcol != oap->end_vcol) {
inc_cursor();
+ }
}
}
t1 = oap->start;
- edit(NUL, false, (linenr_T)count1);
+ (void)edit(NUL, false, (linenr_T)count1);
// When a tab was inserted, and the characters in front of the tab
// have been converted to a tab as well, the column of the cursor
@@ -2176,9 +2186,10 @@ int op_change(oparg_T *oap)
} else if (op_delete(oap) == FAIL)
return FALSE;
- if ((l > curwin->w_cursor.col) && !lineempty(curwin->w_cursor.lnum)
- && !virtual_op)
+ if ((l > curwin->w_cursor.col) && !LINEEMPTY(curwin->w_cursor.lnum)
+ && !virtual_op) {
inc_cursor();
+ }
// check for still on same line (<CR> in inserted text meaningless)
// skip blank lines too
@@ -2560,11 +2571,11 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
dict_T *dict = get_vim_var_dict(VV_EVENT);
// the yanked text
- list_T *list = tv_list_alloc();
+ list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(list, (const char *)reg->y_array[i], -1);
}
- list->lv_lock = VAR_FIXED;
+ tv_list_set_lock(list, VAR_FIXED);
tv_dict_add_list(dict, S_LEN("regcontents"), list);
// the register type
@@ -2731,7 +2742,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
// Autocommands may be executed when saving lines for undo, which may make
// y_array invalid. Start undo now to avoid that.
if (u_save(curwin->w_cursor.lnum, curwin->w_cursor.lnum + 1) == FAIL) {
- ELOG(_("Failed to save undo information"));
+ ELOG("Failed to save undo information");
return;
}
}
@@ -2784,7 +2795,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
if (curbuf->terminal) {
- for (int i = 0; i < count; i++) {
+ for (int i = 0; i < count; i++) { // -V756
// feed the lines to the terminal
for (size_t j = 0; j < y_size; j++) {
if (j) {
@@ -2851,25 +2862,30 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
} else if (y_type == kMTLineWise) {
lnum = curwin->w_cursor.lnum;
- /* Correct line number for closed fold. Don't move the cursor yet,
- * u_save() uses it. */
- if (dir == BACKWARD)
+ // Correct line number for closed fold. Don't move the cursor yet,
+ // u_save() uses it.
+ if (dir == BACKWARD) {
(void)hasFolding(lnum, &lnum, NULL);
- else
+ } else {
(void)hasFolding(lnum, NULL, &lnum);
- if (dir == FORWARD)
- ++lnum;
- /* In an empty buffer the empty line is going to be replaced, include
- * it in the saved lines. */
- if ((bufempty() ? u_save(0, 2) : u_save(lnum - 1, lnum)) == FAIL)
+ }
+ if (dir == FORWARD) {
+ lnum++;
+ }
+ // In an empty buffer the empty line is going to be replaced, include
+ // it in the saved lines.
+ if ((BUFEMPTY() ? u_save(0, 2) : u_save(lnum - 1, lnum)) == FAIL) {
goto end;
- if (dir == FORWARD)
+ }
+ if (dir == FORWARD) {
curwin->w_cursor.lnum = lnum - 1;
- else
+ } else {
curwin->w_cursor.lnum = lnum;
- curbuf->b_op_start = curwin->w_cursor; /* for mark_adjust() */
- } else if (u_save_cursor() == FAIL)
+ }
+ curbuf->b_op_start = curwin->w_cursor; // for mark_adjust()
+ } else if (u_save_cursor() == FAIL) {
goto end;
+ }
yanklen = (int)STRLEN(y_array[0]);
@@ -3062,15 +3078,26 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
--lnum;
new_cursor = curwin->w_cursor;
- /*
- * simple case: insert into current line
- */
+ // simple case: insert into current line
if (y_type == kMTCharWise && y_size == 1) {
+ linenr_T end_lnum = 0; // init for gcc
+
+ if (VIsual_active) {
+ end_lnum = curbuf->b_visual.vi_end.lnum;
+ if (end_lnum < curbuf->b_visual.vi_start.lnum) {
+ end_lnum = curbuf->b_visual.vi_start.lnum;
+ }
+ }
+
do {
totlen = (size_t)(count * yanklen);
if (totlen > 0) {
oldp = ml_get(lnum);
- newp = (char_u *) xmalloc((size_t)(STRLEN(oldp) + totlen + 1));
+ if (VIsual_active && col > (int)STRLEN(oldp)) {
+ lnum++;
+ continue;
+ }
+ newp = (char_u *)xmalloc((size_t)(STRLEN(oldp) + totlen + 1));
memmove(newp, oldp, (size_t)col);
ptr = newp + col;
for (i = 0; i < (size_t)count; i++) {
@@ -3086,11 +3113,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
curwin->w_cursor.col += (colnr_T)(totlen - 1);
}
}
- if (VIsual_active)
+ if (VIsual_active) {
lnum++;
- } while (VIsual_active
- && (lnum <= curbuf->b_visual.vi_end.lnum
- || lnum <= curbuf->b_visual.vi_start.lnum));
+ }
+ } while (VIsual_active && lnum <= end_lnum);
if (VIsual_active) { /* reset lnum to the last visual line */
lnum--;
@@ -3173,11 +3199,11 @@ error:
curbuf->b_op_start.lnum++;
}
// Skip mark_adjust when adding lines after the last one, there
- // can't be marks there.
+ // can't be marks there. But still needed in diff mode.
if (curbuf->b_op_start.lnum + (y_type == kMTCharWise) - 1 + nr_lines
- < curbuf->b_ml.ml_line_count) {
+ < curbuf->b_ml.ml_line_count || curwin->w_p_diff) {
mark_adjust(curbuf->b_op_start.lnum + (y_type == kMTCharWise),
- (linenr_T)MAXLNUM, nr_lines, 0L);
+ (linenr_T)MAXLNUM, nr_lines, 0L, false);
}
// note changed text for displaying and folding
@@ -3327,10 +3353,11 @@ void ex_display(exarg_T *eap)
get_clipboard(name, &yb, true);
- if (name == vim_tolower(redir_reg)
- || (redir_reg == '"' && yb == y_previous))
- continue; /* do not list register being written to, the
- * pointer can be freed */
+ if (name == mb_tolower(redir_reg)
+ || (redir_reg == '"' && yb == y_previous)) {
+ continue; // do not list register being written to, the
+ // pointer can be freed
+ }
if (yb->y_array != NULL) {
msg_putchar('\n');
@@ -3541,7 +3568,7 @@ int do_join(size_t count,
int *comments = NULL;
int remove_comments = (use_formatoptions == TRUE)
&& has_format_option(FO_REMOVE_COMS);
- bool prev_was_comment;
+ bool prev_was_comment = false;
if (save_undo && u_save(curwin->w_cursor.lnum - 1,
curwin->w_cursor.lnum + (linenr_T)count) == FAIL) {
@@ -3565,17 +3592,16 @@ int do_join(size_t count,
curwin->w_buffer->b_op_start.col = (colnr_T)STRLEN(curr);
}
if (remove_comments) {
- /* We don't want to remove the comment leader if the
- * previous line is not a comment. */
+ // We don't want to remove the comment leader if the
+ // previous line is not a comment.
if (t > 0 && prev_was_comment) {
-
- char_u *new_curr = skip_comment(curr, TRUE, insert_space,
- &prev_was_comment);
+ char_u *new_curr = skip_comment(curr, true, insert_space,
+ &prev_was_comment);
comments[t] = (int)(new_curr - curr);
curr = new_curr;
- } else
- curr = skip_comment(curr, FALSE, insert_space,
- &prev_was_comment);
+ } else {
+ curr = skip_comment(curr, false, insert_space, &prev_was_comment);
+ }
}
if (insert_space && t > 0) {
@@ -3864,9 +3890,6 @@ fex_format (
// Make a copy, the option could be changed while calling it.
fex = vim_strsave(curbuf->b_p_fex);
- if (fex == NULL) {
- return 0;
- }
// Evaluate the function.
if (use_sandbox) {
sandbox++;
@@ -3992,7 +4015,7 @@ format_lines (
&& (do_second_indent || do_number_indent)
&& prev_is_end_par
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- if (do_second_indent && !lineempty(curwin->w_cursor.lnum + 1)) {
+ if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1)) {
if (leader_len == 0 && next_leader_len == 0) {
/* no comment found */
second_indent =
@@ -4434,8 +4457,8 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
char_u buf2[NUMBUFLEN];
int pre; // 'X' or 'x': hex; '0': octal; 'B' or 'b': bin
static bool hexupper = false; // 0xABC
- unsigned long n;
- unsigned long oldn;
+ uvarnumber_T n;
+ uvarnumber_T oldn;
char_u *ptr;
int c;
int todel;
@@ -4583,9 +4606,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
}
}
curwin->w_cursor.col = col;
- if (!did_change) {
- startpos = curwin->w_cursor;
- }
+ startpos = curwin->w_cursor;
did_change = true;
(void)del_char(false);
ins_char(firstdigit);
@@ -4630,20 +4651,20 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
oldn = n;
- n = subtract ? n - (unsigned long) Prenum1
- : n + (unsigned long) Prenum1;
+ n = subtract ? n - (uvarnumber_T)Prenum1
+ : n + (uvarnumber_T)Prenum1;
// handle wraparound for decimal numbers
if (!pre) {
if (subtract) {
if (n > oldn) {
- n = 1 + (n ^ (unsigned long)-1);
+ n = 1 + (n ^ (uvarnumber_T)-1);
negative ^= true;
}
} else {
// add
if (n < oldn) {
- n = (n ^ (unsigned long)-1);
+ n = (n ^ (uvarnumber_T)-1);
negative ^= true;
}
}
@@ -4660,9 +4681,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
// Delete the old number.
curwin->w_cursor.col = col;
- if (!did_change) {
- startpos = curwin->w_cursor;
- }
+ startpos = curwin->w_cursor;
did_change = true;
todel = length;
c = gchar_cursor();
@@ -4689,9 +4708,6 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
// When there are many leading zeros it could be very long.
// Allocate a bit too much.
buf1 = xmalloc((size_t)length + NUMBUFLEN);
- if (buf1 == NULL) {
- goto theend;
- }
ptr = buf1;
if (negative && (!visual || was_positive)) {
*ptr++ = '-';
@@ -4727,7 +4743,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIu64, (uint64_t)n);
} else if (pre == '0') {
vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIo64, (uint64_t)n);
- } else if (pre && hexupper) {
+ } else if (hexupper) {
vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIX64, (uint64_t)n);
} else {
vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIx64, (uint64_t)n);
@@ -4748,18 +4764,16 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
ins_str(buf1); // insert the new number
xfree(buf1);
endpos = curwin->w_cursor;
- if (did_change && curwin->w_cursor.col) {
+ if (curwin->w_cursor.col) {
curwin->w_cursor.col--;
}
}
- if (did_change) {
- // 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--;
- }
+ // 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:
@@ -4849,9 +4863,8 @@ static void *get_reg_wrap_one_line(char_u *s, int flags)
if (!(flags & kGRegList)) {
return s;
}
- list_T *list = tv_list_alloc();
- tv_list_append_string(list, NULL, 0);
- list->lv_first->li_tv.vval.v_string = s;
+ list_T *const list = tv_list_alloc(1);
+ tv_list_append_allocated_string(list, (char *)s);
return list;
}
@@ -4900,7 +4913,7 @@ void *get_reg_contents(int regname, int flags)
return NULL;
if (flags & kGRegList) {
- list_T *list = tv_list_alloc();
+ list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(list, (const char *)reg->y_array[i], -1);
}
@@ -5233,11 +5246,13 @@ void clear_oparg(oparg_T *oap)
* case, eol_size will be added to the character count to account for
* the size of the EOL character.
*/
-static long line_count_info(char_u *line, long *wc, long *cc, long limit, int eol_size)
+static varnumber_T line_count_info(char_u *line, varnumber_T *wc,
+ varnumber_T *cc, varnumber_T limit,
+ int eol_size)
{
- long i;
- long words = 0;
- long chars = 0;
+ varnumber_T i;
+ varnumber_T words = 0;
+ varnumber_T chars = 0;
int is_word = 0;
for (i = 0; i < limit && line[i] != NUL; ) {
@@ -5275,15 +5290,15 @@ void cursor_pos_info(dict_T *dict)
char_u buf1[50];
char_u buf2[40];
linenr_T lnum;
- long byte_count = 0;
- long bom_count = 0;
- long byte_count_cursor = 0;
- long char_count = 0;
- long char_count_cursor = 0;
- long word_count = 0;
- long word_count_cursor = 0;
+ varnumber_T byte_count = 0;
+ varnumber_T bom_count = 0;
+ varnumber_T byte_count_cursor = 0;
+ varnumber_T char_count = 0;
+ varnumber_T char_count_cursor = 0;
+ varnumber_T word_count = 0;
+ varnumber_T word_count_cursor = 0;
int eol_size;
- long last_check = 100000L;
+ varnumber_T last_check = 100000L;
long line_count_selected = 0;
pos_T min_pos, max_pos;
oparg_T oparg;
@@ -5390,15 +5405,16 @@ void cursor_pos_info(dict_T *dict)
if (lnum == curwin->w_cursor.lnum) {
word_count_cursor += word_count;
char_count_cursor += char_count;
- byte_count_cursor = byte_count +
- line_count_info(ml_get(lnum),
- &word_count_cursor, &char_count_cursor,
- (long)(curwin->w_cursor.col + 1), eol_size);
+ byte_count_cursor = byte_count
+ + line_count_info(ml_get(lnum), &word_count_cursor,
+ &char_count_cursor,
+ (varnumber_T)(curwin->w_cursor.col + 1),
+ eol_size);
}
}
- /* Add to the running totals */
- byte_count += line_count_info(ml_get(lnum), &word_count,
- &char_count, (long)MAXCOL, eol_size);
+ // Add to the running totals
+ byte_count += line_count_info(ml_get(lnum), &word_count, &char_count,
+ (varnumber_T)MAXCOL, eol_size);
}
// Correction for when last line doesn't have an EOL.
@@ -5515,7 +5531,7 @@ int get_default_register_name(void)
}
/// Determine if register `*name` should be used as a clipboard.
-/// In an unnammed operation, `*name` is `NUL` and will be adjusted to `'*'/'+'` if
+/// In an unnamed operation, `*name` is `NUL` and will be adjusted to */+ if
/// `clipboard=unnamed[plus]` is set.
///
/// @param name The name of register, or `NUL` if unnamed.
@@ -5526,33 +5542,41 @@ int get_default_register_name(void)
/// if the register isn't a clipboard or provider isn't available.
static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
{
- if (*name == '*' || *name == '+') {
- if(!eval_has_provider("clipboard")) {
- if (!quiet) {
- EMSG("clipboard: No provider. Try \":CheckHealth\" or "
- "\":h clipboard\".");
- }
- return NULL;
- }
- return &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER];
- } else if ((*name == NUL) && (cb_flags & CB_UNNAMEDMASK)) {
- if(!eval_has_provider("clipboard")) {
- if (!quiet && !clipboard_didwarn_unnamed) {
- msg((char_u *)"clipboard: No provider. Try \":CheckHealth\" or "
- "\":h clipboard\".");
- clipboard_didwarn_unnamed = true;
- }
- return NULL;
+#define MSG_NO_CLIP "clipboard: No provider. " \
+ "Try \":checkhealth\" or \":h clipboard\"."
+
+ yankreg_T *target = NULL;
+ bool explicit_cb_reg = (*name == '*' || *name == '+');
+ bool implicit_cb_reg = (*name == NUL) && (cb_flags & CB_UNNAMEDMASK);
+ if (!explicit_cb_reg && !implicit_cb_reg) {
+ goto end;
+ }
+
+ if (!eval_has_provider("clipboard")) {
+ if (batch_change_count == 1 && !quiet
+ && (!clipboard_didwarn || (explicit_cb_reg && !redirecting()))) {
+ clipboard_didwarn = true;
+ // Do NOT error (emsg()) here--if it interrupts :redir we get into
+ // a weird state, stuck in "redirect mode".
+ msg((char_u *)MSG_NO_CLIP);
}
+ // ... else, be silent (don't flood during :while, :redir, etc.).
+ goto end;
+ }
+
+ if (explicit_cb_reg) {
+ target = &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER];
+ goto end;
+ } else { // unnamed register: "implicit" clipboard
if (writing && clipboard_delay_update) {
+ // For "set" (copy), defer the clipboard call.
clipboard_needs_update = true;
- return NULL;
+ goto end;
} else if (!writing && clipboard_needs_update) {
- // use the internal value
- return NULL;
+ // For "get" (paste), use the internal value.
+ goto end;
}
- yankreg_T *target;
if (cb_flags & CB_UNNAMEDPLUS) {
*name = (cb_flags & CB_UNNAMED && writing) ? '"': '+';
target = &y_regs[PLUS_REGISTER];
@@ -5560,10 +5584,11 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
*name = '*';
target = &y_regs[STAR_REGISTER];
}
- return target; // unnamed register
+ goto end;
}
- // don't do anything for other register names
- return NULL;
+
+end:
+ return target;
}
static bool get_clipboard(int name, yankreg_T **target, bool quiet)
@@ -5577,7 +5602,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
}
free_register(reg);
- list_T *const args = tv_list_alloc();
+ list_T *const args = tv_list_alloc(1);
const char regname = (char)name;
tv_list_append_string(args, &regname, 1);
@@ -5593,13 +5618,14 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
list_T *res = result.vval.v_list;
list_T *lines = NULL;
- if (res->lv_len == 2 && res->lv_first->li_tv.v_type == VAR_LIST) {
- lines = res->lv_first->li_tv.vval.v_list;
- if (res->lv_last->li_tv.v_type != VAR_STRING) {
+ if (tv_list_len(res) == 2
+ && TV_LIST_ITEM_TV(tv_list_first(res))->v_type == VAR_LIST) {
+ lines = TV_LIST_ITEM_TV(tv_list_first(res))->vval.v_list;
+ if (TV_LIST_ITEM_TV(tv_list_last(res))->v_type != VAR_STRING) {
goto err;
}
- char_u *regtype = res->lv_last->li_tv.vval.v_string;
- if (regtype == NULL || strlen((char*)regtype) > 1) {
+ char_u *regtype = TV_LIST_ITEM_TV(tv_list_last(res))->vval.v_string;
+ if (regtype == NULL || strlen((char *)regtype) > 1) {
goto err;
}
switch (regtype[0]) {
@@ -5624,20 +5650,21 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
reg->y_type = kMTUnknown;
}
- reg->y_array = xcalloc((size_t)lines->lv_len, sizeof(uint8_t *));
- reg->y_size = (size_t)lines->lv_len;
+ reg->y_array = xcalloc((size_t)tv_list_len(lines), sizeof(char_u *));
+ reg->y_size = (size_t)tv_list_len(lines);
reg->additional_data = NULL;
reg->timestamp = 0;
// Timestamp is not saved for clipboard registers because clipboard registers
// are not saved in the ShaDa file.
int i = 0;
- for (listitem_T *li = lines->lv_first; li != NULL; li = li->li_next) {
- if (li->li_tv.v_type != VAR_STRING) {
+ TV_LIST_ITER_CONST(lines, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
goto err;
}
- reg->y_array[i++] = (char_u *)xstrdupnul((char *)li->li_tv.vval.v_string);
- }
+ reg->y_array[i++] = (char_u *)xstrdupnul(
+ (const char *)TV_LIST_ITEM_TV(li)->vval.v_string);
+ });
if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) {
// a known-to-be charwise yank might have a final linebreak
@@ -5694,15 +5721,13 @@ static void set_clipboard(int name, yankreg_T *reg)
return;
}
- list_T *lines = tv_list_alloc();
+ list_T *const lines = tv_list_alloc(
+ (ptrdiff_t)reg->y_size + (reg->y_type != kMTCharWise));
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(lines, (const char *)reg->y_array[i], -1);
}
- list_T *args = tv_list_alloc();
- tv_list_append_list(args, lines);
-
char regtype;
switch (reg->y_type) {
case kMTLineWise: {
@@ -5723,25 +5748,25 @@ static void set_clipboard(int name, yankreg_T *reg)
assert(false);
}
}
- tv_list_append_string(args, &regtype, 1);
- const char regname = (char)name;
- tv_list_append_string(args, &regname, 1);
+ list_T *args = tv_list_alloc(3);
+ tv_list_append_list(args, lines);
+ tv_list_append_string(args, &regtype, 1); // -V614
+ tv_list_append_string(args, ((char[]) { (char)name }), 1);
(void)eval_call_provider("clipboard", "set", args);
}
-/// Avoid clipboard (slow) during batch operations (i.e., a script).
+/// Avoid slow things (clipboard) during batch operations (while/for-loops).
void start_batch_changes(void)
{
if (++batch_change_count > 1) {
return;
}
clipboard_delay_update = true;
- clipboard_needs_update = false;
}
-/// Update the clipboard after batch changes finished.
+/// Counterpart to start_batch_changes().
void end_batch_changes(void)
{
if (--batch_change_count > 0) {
@@ -5750,11 +5775,37 @@ void end_batch_changes(void)
}
clipboard_delay_update = false;
if (clipboard_needs_update) {
+ // must be before, as set_clipboard will invoke
+ // start/end_batch_changes recursively
+ clipboard_needs_update = false;
+ // unnamed ("implicit" clipboard)
set_clipboard(NUL, y_previous);
+ }
+}
+
+int save_batch_count(void)
+{
+ int save_count = batch_change_count;
+ batch_change_count = 0;
+ clipboard_delay_update = false;
+ if (clipboard_needs_update) {
clipboard_needs_update = false;
+ // unnamed ("implicit" clipboard)
+ set_clipboard(NUL, y_previous);
}
+ return save_count;
}
+void restore_batch_count(int save_count)
+{
+ assert(batch_change_count == 0);
+ batch_change_count = save_count;
+ if (batch_change_count > 0) {
+ clipboard_delay_update = true;
+ }
+}
+
+
/// Check whether register is empty
static inline bool reg_empty(const yankreg_T *const reg)
FUNC_ATTR_PURE
@@ -5775,7 +5826,7 @@ static inline bool reg_empty(const yankreg_T *const reg)
/// @return Pointer that needs to be passed to next `op_register_iter` call or
/// NULL if iteration is over.
const void *op_register_iter(const void *const iter, char *const name,
- yankreg_T *const reg)
+ yankreg_T *const reg, bool *is_unnamed)
FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
*name = NUL;
@@ -5791,6 +5842,7 @@ const void *op_register_iter(const void *const iter, char *const name,
int iter_off = (int)(iter_reg - &(y_regs[0]));
*name = (char)get_register_name(iter_off);
*reg = *iter_reg;
+ *is_unnamed = (iter_reg == y_previous);
while (++iter_reg - &(y_regs[0]) < NUM_SAVED_REGISTERS) {
if (!reg_empty(iter_reg)) {
return (void *) iter_reg;
@@ -5815,10 +5867,11 @@ size_t op_register_amount(void)
/// Set register to a given value
///
/// @param[in] name Register name.
-/// @param[in] reg Register value.
+/// @param[in] reg Register value.
+/// @param[in] is_unnamed Whether to set the unnamed regiseter to reg
///
/// @return true on success, false on failure.
-bool op_register_set(const char name, const yankreg_T reg)
+bool op_register_set(const char name, const yankreg_T reg, bool is_unnamed)
{
int i = op_reg_index(name);
if (i == -1) {
@@ -5826,6 +5879,10 @@ bool op_register_set(const char name, const yankreg_T reg)
}
free_register(&y_regs[i]);
y_regs[i] = reg;
+
+ if (is_unnamed) {
+ y_previous = &y_regs[i];
+ }
return true;
}
@@ -5842,3 +5899,20 @@ const yankreg_T *op_register_get(const char name)
}
return &y_regs[i];
}
+
+/// Set the previous yank register
+///
+/// @param[in] name Register name.
+///
+/// @return true on success, false on failure.
+bool op_register_set_previous(const char name)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ int i = op_reg_index(name);
+ if (i == -1) {
+ return false;
+ }
+
+ y_previous = &y_regs[i];
+ return true;
+}