diff options
Diffstat (limited to 'src/nvim/ops.c')
| -rw-r--r-- | src/nvim/ops.c | 416 | 
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, ®name, 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, ®type, 1); -  const char regname = (char)name; -  tv_list_append_string(args, ®name, 1); +  list_T *args = tv_list_alloc(3); +  tv_list_append_list(args, lines); +  tv_list_append_string(args, ®type, 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; +} | 
