Repository: neovim/neovim
File: src/nvim/ops.c (C)

Code before:
                     kExtmarkUndo);
    }
    currsize = (int)strlen(curr);
    sumsize += currsize + spaces[t];
    endcurr1 = endcurr2 = NUL;
    if (insert_space && currsize > 0) {
      cend = curr + currsize;
      MB_PTR_BACK(curr, cend);
      endcurr1 = utf_ptr2char(cend);
      if (cend > curr) {
        MB_PTR_BACK(curr, cend);
        endcurr2 = utf_ptr2char(cend);
      }
    }
    line_breakcheck();
    if (got_int) {
      ret = FAIL;
      goto theend;
    }
  }

  // store the column position before last line
  colnr_T col = sumsize - currsize - spaces[count - 1];

  // allocate the space for the new line
  size_t newp_len = (size_t)sumsize + 1;
  char *newp = xmalloc(newp_len);
  cend = newp + sumsize;
  *cend = 0;

  // Move affected lines to the new long one.
  // This loops backwards over the joined lines, including the original line.
  //
  // Move marks from each deleted line to the joined line, adjusting the
  // column.  This is not Vi compatible, but Vi deletes the marks, thus that
  // should not really be a problem.

  curbuf_splice_pending++;

  for (linenr_T t = (linenr_T)count - 1;; t--) {
    cend -= currsize;
    memmove(cend, curr, (size_t)currsize);
    if (spaces[t] > 0) {
      cend -= spaces[t];
      memset(cend, ' ', (size_t)(spaces[t]));
    }

    // 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;

Review comment (bug):
Off-by-one error because of `+ 1`???

```
TEST_FILE=test/functional/api/buffer_updates_spec.lua make functionaltest
TEST_FILE=test/functional/legacy/069_multibyte_formatting_spec.lua make functionaltest
```

will pass.

```diff
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 92258c6..9d410ab 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -4014,7 +4014,7 @@ int do_join(size_t count, bool insert_space, bool save_undo, bool use_formatopti
   colnr_T col = sumsize - currsize - spaces[count - 1];
 
   // allocate the space for the new line
-  size_t newp_len = (size_t)sumsize + 1;
+  size_t newp_len = (size_t)sumsize;
   char *newp = xmalloc(newp_len);
   cend = newp + sumsize;
   *cend = 0;
```

Code after:
                     kExtmarkUndo);
    }
    currsize = (int)strlen(curr);
    sumsize += currsize + spaces[t];
    endcurr1 = endcurr2 = NUL;
    if (insert_space && currsize > 0) {
      cend = curr + currsize;
      MB_PTR_BACK(curr, cend);
      endcurr1 = utf_ptr2char(cend);
      if (cend > curr) {
        MB_PTR_BACK(curr, cend);
        endcurr2 = utf_ptr2char(cend);
      }
    }
    line_breakcheck();
    if (got_int) {
      ret = FAIL;
      goto theend;
    }
  }

  // store the column position before last line
  colnr_T col = sumsize - currsize - spaces[count - 1];

  // allocate the space for the new line
  char *newp = xmalloc((size_t)sumsize + 1);
  cend = newp + sumsize;
  *cend = 0;

  // Move affected lines to the new long one.
  // This loops backwards over the joined lines, including the original line.
  //
  // Move marks from each deleted line to the joined line, adjusting the
  // column.  This is not Vi compatible, but Vi deletes the marks, thus that
  // should not really be a problem.

  curbuf_splice_pending++;

  for (linenr_T t = (linenr_T)count - 1;; t--) {
    cend -= currsize;
    memmove(cend, curr, (size_t)currsize);
    if (spaces[t] > 0) {
      cend -= spaces[t];
      memset(cend, ' ', (size_t)(spaces[t]));
    }

    // 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 = 0;