diff options
Diffstat (limited to 'src/nvim/ops.c')
-rw-r--r-- | src/nvim/ops.c | 428 |
1 files changed, 256 insertions, 172 deletions
diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 1e4d392754..432c9a8b47 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 @@ -14,8 +17,10 @@ #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" +#include "nvim/assert.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" @@ -41,6 +46,7 @@ #include "nvim/terminal.h" #include "nvim/ui.h" #include "nvim/undo.h" +#include "nvim/macros.h" #include "nvim/window.h" #include "nvim/os/input.h" #include "nvim/os/time.h" @@ -49,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 @@ -799,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); @@ -888,7 +892,7 @@ static void set_yreg_additional_data(yankreg_T *reg, dict_T *additional_data) if (reg->additional_data == additional_data) { return; } - dict_unref(reg->additional_data); + tv_dict_unref(reg->additional_data); reg->additional_data = additional_data; } @@ -933,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 */ @@ -1106,7 +1108,6 @@ int insert_reg( ) { int retval = OK; - char_u *arg; int allocated; /* @@ -1122,21 +1123,24 @@ int insert_reg( if (regname != NUL && !valid_yank_reg(regname, false)) return FAIL; - if (regname == '.') /* insert last inserted text */ - retval = stuff_inserted(NUL, 1L, TRUE); - else if (get_spec_reg(regname, &arg, &allocated, TRUE)) { - if (arg == NULL) + char_u *arg; + if (regname == '.') { // Insert last inserted text. + retval = stuff_inserted(NUL, 1L, true); + } else if (get_spec_reg(regname, &arg, &allocated, true)) { + if (arg == NULL) { return FAIL; - stuffescaped(arg, literally); - if (allocated) + } + stuffescaped((const char *)arg, literally); + if (allocated) { xfree(arg); - } else { /* name or number register */ + } + } else { // Name or number register. yankreg_T *reg = get_yank_register(regname, YREG_PASTE); if (reg->y_array == NULL) { retval = FAIL; } else { for (size_t i = 0; i < reg->y_size; i++) { - stuffescaped(reg->y_array[i], literally); + stuffescaped((const char *)reg->y_array[i], literally); // Insert a newline between lines and after last line if // y_type is kMTLineWise. if (reg->y_type == kMTLineWise || i < reg->y_size - 1) { @@ -1153,29 +1157,29 @@ int insert_reg( * Stuff a string into the typeahead buffer, such that edit() will insert it * literally ("literally" TRUE) or interpret is as typed characters. */ -static void stuffescaped(char_u *arg, int literally) +static void stuffescaped(const char *arg, int literally) { - int c; - char_u *start; - while (*arg != NUL) { - /* Stuff a sequence of normal ASCII characters, that's fast. Also - * stuff K_SPECIAL to get the effect of a special key when "literally" - * is TRUE. */ - start = arg; - while ((*arg >= ' ' && *arg < DEL) || (*arg == K_SPECIAL && !literally)) - ++arg; - if (arg > start) + // Stuff a sequence of normal ASCII characters, that's fast. Also + // stuff K_SPECIAL to get the effect of a special key when "literally" + // is TRUE. + const char *const start = arg; + while ((*arg >= ' ' && *arg < DEL) || ((uint8_t)(*arg) == K_SPECIAL + && !literally)) { + arg++; + } + if (arg > start) { stuffReadbuffLen(start, (long)(arg - start)); + } /* stuff a single special character */ if (*arg != NUL) { - if (has_mbyte) - c = mb_cptr2char_adv(&arg); - else - c = *arg++; - if (literally && ((c < ' ' && c != TAB) || c == DEL)) + const int c = (has_mbyte + ? mb_cptr2char_adv((const char_u **)&arg) + : (uint8_t)(*arg++)); + if (literally && ((c < ' ' && c != TAB) || c == DEL)) { stuffcharReadbuff(Ctrl_V); + } stuffcharReadbuff(c); } } @@ -1403,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); } @@ -1951,16 +1958,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)) { @@ -2051,7 +2060,7 @@ void op_insert(oparg_T *oap, long count1) } 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 @@ -2555,33 +2564,33 @@ 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 = list_alloc(); + list_T *list = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(list, reg->y_array[i], -1); + tv_list_append_string(list, (const char *)reg->y_array[i], -1); } list->lv_lock = VAR_FIXED; - dict_add_list(dict, "regcontents", list); + tv_dict_add_list(dict, S_LEN("regcontents"), list); // the register type char buf[NUMBUFLEN+2]; format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); - dict_add_nr_str(dict, "regtype", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("regtype"), buf); // name of requested register or the empty string for an unnamed operation. buf[0] = (char)oap->regname; buf[1] = NUL; - dict_add_nr_str(dict, "regname", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("regname"), buf); // kind of operation (yank/delete/change) buf[0] = (char)get_op_char(oap->op_type); buf[1] = NUL; - dict_add_nr_str(dict, "operator", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("operator"), buf); - dict_set_keys_readonly(dict); + tv_dict_set_keys_readonly(dict); textlock++; apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); textlock--; - dict_clear(dict); + tv_dict_clear(dict); recursive = false; } @@ -2660,7 +2669,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // back to the previous line in the case of 'noautoindent' and // 'backspace' includes "eol". So we insert a dummy space for Ctrl_U // to consume. - stuffReadbuff((char_u *)"\n "); + stuffReadbuff("\n "); stuffcharReadbuff(Ctrl_U); } } @@ -2672,7 +2681,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // character. Simulate it with motion commands after the insert. if (flags & PUT_CURSEND) { if (flags & PUT_LINE) { - stuffReadbuff((char_u *)"j0"); + stuffReadbuff("j0"); } else { // Avoid ringing the bell from attempting to move into the space after // the current line. We can stuff the readbuffer with "l" if: @@ -2702,7 +2711,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } } } else if (flags & PUT_LINE) { - stuffReadbuff((char_u *)"g'["); + stuffReadbuff("g'["); } // So the 'u' command restores cursor position after ".p, save the cursor @@ -2726,7 +2735,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; } } @@ -2779,7 +2788,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) { @@ -3172,7 +3181,7 @@ error: if (curbuf->b_op_start.lnum + (y_type == kMTCharWise) - 1 + nr_lines < curbuf->b_ml.ml_line_count) { 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 @@ -3322,10 +3331,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'); @@ -3847,6 +3857,7 @@ fex_format ( int use_sandbox = was_set_insecurely((char_u *)"formatexpr", OPT_LOCAL); int r; + char_u *fex; /* * Set v:lnum to the first line number and v:count to the number of lines. @@ -3856,16 +3867,22 @@ fex_format ( set_vim_var_nr(VV_COUNT, (varnumber_T)count); set_vim_var_char(c); - /* - * Evaluate the function. - */ - if (use_sandbox) - ++sandbox; - r = eval_to_number(curbuf->b_p_fex); - if (use_sandbox) - --sandbox; + // 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++; + } + r = (int)eval_to_number(fex); + if (use_sandbox) { + sandbox--; + } set_vim_var_string(VV_CHAR, NULL, -1); + xfree(fex); return r; } @@ -4422,8 +4439,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; @@ -4618,20 +4635,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; } } @@ -4837,8 +4854,8 @@ static void *get_reg_wrap_one_line(char_u *s, int flags) if (!(flags & kGRegList)) { return s; } - list_T *list = list_alloc(); - list_append_string(list, NULL, -1); + list_T *list = tv_list_alloc(); + tv_list_append_string(list, NULL, 0); list->lv_first->li_tv.vval.v_string = s; return list; } @@ -4888,9 +4905,9 @@ void *get_reg_contents(int regname, int flags) return NULL; if (flags & kGRegList) { - list_T *list = list_alloc(); + list_T *list = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(list, reg->y_array[i], -1); + tv_list_append_string(list, (const char *)reg->y_array[i], -1); } return list; @@ -4971,7 +4988,7 @@ void write_reg_contents(int name, const char_u *str, ssize_t len, write_reg_contents_ex(name, str, len, must_append, kMTUnknown, 0L); } -void write_reg_contents_lst(int name, char_u **strings, int maxlen, +void write_reg_contents_lst(int name, char_u **strings, bool must_append, MotionType yank_type, colnr_T block_len) { @@ -5221,11 +5238,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; ) { @@ -5263,15 +5282,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; @@ -5378,15 +5397,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. @@ -5475,17 +5495,19 @@ void cursor_pos_info(dict_T *dict) if (dict != NULL) { // Don't shorten this message, the user asked for it. - dict_add_nr_str(dict, "words", word_count, NULL); - dict_add_nr_str(dict, "chars", char_count, NULL); - dict_add_nr_str(dict, "bytes", byte_count + bom_count, NULL); - - dict_add_nr_str(dict, l_VIsual_active ? "visual_bytes" : "cursor_bytes", - byte_count_cursor, NULL); - dict_add_nr_str(dict, l_VIsual_active ? "visual_chars" : "cursor_chars", - char_count_cursor, NULL); - dict_add_nr_str(dict, l_VIsual_active ? "visual_words" : "cursor_words", - word_count_cursor, NULL); - } + tv_dict_add_nr(dict, S_LEN("words"), (varnumber_T)word_count); + tv_dict_add_nr(dict, S_LEN("chars"), (varnumber_T)char_count); + tv_dict_add_nr(dict, S_LEN("bytes"), (varnumber_T)(byte_count + bom_count)); + + STATIC_ASSERT(sizeof("visual") == sizeof("cursor"), + "key_len argument in tv_dict_add_nr is wrong"); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_bytes" : "cursor_bytes", + sizeof("visual_bytes") - 1, (varnumber_T)byte_count_cursor); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_chars" : "cursor_chars", + sizeof("visual_chars") - 1, (varnumber_T)char_count_cursor); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_words" : "cursor_words", + sizeof("visual_words") - 1, (varnumber_T)word_count_cursor); + } } /// Check if the default register (used in an unnamed paste) should be a @@ -5501,7 +5523,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. @@ -5512,33 +5534,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]; @@ -5546,10 +5576,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) @@ -5563,9 +5594,9 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) } free_register(reg); - list_T *args = list_alloc(); - char_u regname = (char_u)name; - list_append_string(args, ®name, 1); + list_T *const args = tv_list_alloc(); + const char regname = (char)name; + tv_list_append_string(args, ®name, 1); typval_T result = eval_call_provider("clipboard", "get", args); @@ -5577,7 +5608,8 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) goto err; } - list_T *res = result.vval.v_list, *lines = NULL; + 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) { @@ -5621,7 +5653,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) if (li->li_tv.v_type != VAR_STRING) { goto err; } - reg->y_array[i++] = (uint8_t *)xstrdup((char *)li->li_tv.vval.v_string); + reg->y_array[i++] = (char_u *)xstrdupnul((char *)li->li_tv.vval.v_string); } if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) { @@ -5679,50 +5711,53 @@ static void set_clipboard(int name, yankreg_T *reg) return; } - list_T *lines = list_alloc(); + list_T *lines = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(lines, reg->y_array[i], -1); + tv_list_append_string(lines, (const char *)reg->y_array[i], -1); } - list_T *args = list_alloc(); - list_append_list(args, lines); + list_T *args = tv_list_alloc(); + tv_list_append_list(args, lines); - char_u regtype; + char regtype; switch (reg->y_type) { - case kMTLineWise: - regtype = 'V'; - list_append_string(lines, (char_u*)"", 0); - break; - case kMTCharWise: - regtype = 'v'; - break; - case kMTBlockWise: - regtype = 'b'; - list_append_string(lines, (char_u*)"", 0); - break; - case kMTUnknown: - assert(false); - } - list_append_string(args, ®type, 1); - - char_u regname = (char_u)name; - list_append_string(args, ®name, 1); + case kMTLineWise: { + regtype = 'V'; + tv_list_append_string(lines, NULL, 0); + break; + } + case kMTCharWise: { + regtype = 'v'; + break; + } + case kMTBlockWise: { + regtype = 'b'; + tv_list_append_string(lines, NULL, 0); + break; + } + case kMTUnknown: { + assert(false); + } + } + tv_list_append_string(args, ®type, 1); + + const char regname = (char)name; + tv_list_append_string(args, ®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) { @@ -5731,11 +5766,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 @@ -5756,7 +5817,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; @@ -5772,6 +5833,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; @@ -5796,10 +5858,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) { @@ -5807,6 +5870,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; } @@ -5823,3 +5890,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; +} |