Patch 8.2.0845
Problem:    Text properties crossing lines not handled correctly.
Solution:   When joining lines merge text properties if possible.
            (Axel Forsman, closes #5839, closes #5683)
Files:      src/testdir/test_textprop.vim, src/memline.c, src/ops.c,
            src/proto/textprop.pro, src/textprop.c,
            src/testdir/dumps/Test_textprop_01.dump


*** ../vim-8.2.0844/src/testdir/test_textprop.vim       2020-04-26 
15:59:51.206952132 +0200
--- src/testdir/test_textprop.vim       2020-05-30 14:51:35.020003000 +0200
***************
*** 460,468 ****
--- 460,470 ----
    call assert_equal('nex xtwoxx', getline(2))
    let exp_first = [deepcopy(expected[0])]
    let exp_first[0].length = 1
+   let exp_first[0].end = 0
    call assert_equal(exp_first, prop_list(1))
    let expected[0].col = 1
    let expected[0].length = 2
+   let expected[0].start = 0
    let expected[1].col -= 2
    call assert_equal(expected, prop_list(2))
    call DeletePropTypes()
***************
*** 575,585 ****
--- 577,589 ----
        \ copy(expected_props[3]),
        \ ]
    let expected_props[0].length = 5
+   let expected_props[0].end = 0
    unlet expected_props[3]
    unlet expected_props[2]
    call assert_equal(expected_props, prop_list(1))
  
    let new_props[0].length = 6
+   let new_props[0].start = 0
    let new_props[1].col = 1
    let new_props[1].length = 1
    let new_props[2].col = 3
***************
*** 1228,1231 ****
--- 1232,1256 ----
    call assert_fails("call prop_type_list([])", 'E715:')
  endfunc
  
+ func Test_split_join()
+   new
+   call prop_type_add('test', {'highlight': 'ErrorMsg'})
+   call setline(1, 'just some text')
+   call prop_add(1, 6, {'length': 4, 'type': 'test'})
+ 
+   " Split in middle of "some"
+   execute "normal! 8|i\<CR>"
+   call assert_equal([{'id': 0, 'col': 6, 'end': 0, 'type': 'test', 'length': 
2, 'start': 1}],
+                         \ prop_list(1))
+   call assert_equal([{'id': 0, 'col': 1, 'end': 1, 'type': 'test', 'length': 
2, 'start': 0}],
+                         \ prop_list(2))
+ 
+   " Join the two lines back together
+   normal! 1GJ
+   call assert_equal([{'id': 0, 'col': 6, 'end': 1, 'type': 'test', 'length': 
5, 'start': 1}], prop_list(1))
+ 
+   bwipe!
+   call prop_type_delete('test')
+ endfunc
+ 
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-8.2.0844/src/memline.c       2020-05-30 14:46:48.861163777 +0200
--- src/memline.c       2020-05-30 14:54:13.891360549 +0200
***************
*** 3420,3426 ****
      int               done_del;
      int               done_this;
      textprop_T        prop_del;
-     textprop_T        prop_this;
      bhdr_T    *hp;
      DATA_BL   *dp;
      int               idx;
--- 3420,3425 ----
***************
*** 3451,3457 ****
                if (idx == 0)           // first line in block, text at the end
                    line_size = dp->db_txt_end - line_start;
                else
!                   line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - 
line_start;
                text = (char_u *)dp + line_start;
                textlen = STRLEN(text) + 1;
                if ((long)textlen >= line_size)
--- 3450,3457 ----
                if (idx == 0)           // first line in block, text at the end
                    line_size = dp->db_txt_end - line_start;
                else
!                   line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK)
!                                                                 - line_start;
                text = (char_u *)dp + line_start;
                textlen = STRLEN(text) + 1;
                if ((long)textlen >= line_size)
***************
*** 3466,3489 ****
            }
  
            found = FALSE;
!           for (done_this = 0; done_this < this_props_len; done_this += 
sizeof(textprop_T))
            {
!               mch_memmove(&prop_this, text + textlen + done_del, 
sizeof(textprop_T));
!               if (prop_del.tp_id == prop_this.tp_id
                        && prop_del.tp_type == prop_this.tp_type)
                {
-                   int flag = above ? TP_FLAG_CONT_NEXT : TP_FLAG_CONT_PREV;
- 
                    found = TRUE;
!                   if (prop_this.tp_flags & flag)
!                   {
!                       prop_this.tp_flags &= ~flag;
!                       mch_memmove(text + textlen + done_del, &prop_this, 
sizeof(textprop_T));
!                   }
!                   else if (above)
!                       internal_error("text property above deleted line does 
not continue");
!                   else
!                       internal_error("text property below deleted line does 
not continue");
                }
            }
            if (!found)
--- 3466,3489 ----
            }
  
            found = FALSE;
!           for (done_this = 0; done_this < this_props_len;
!                                              done_this += sizeof(textprop_T))
            {
!               int         flag = above ? TP_FLAG_CONT_NEXT
!                                                          : TP_FLAG_CONT_PREV;
!               textprop_T  prop_this;
! 
!               mch_memmove(&prop_this, text + textlen + done_del,
!                                                          sizeof(textprop_T));
!               if ((prop_this.tp_flags & flag)
!                       && prop_del.tp_id == prop_this.tp_id
                        && prop_del.tp_type == prop_this.tp_type)
                {
                    found = TRUE;
!                   prop_this.tp_flags &= ~flag;
!                   mch_memmove(text + textlen + done_del, &prop_this,
!                                                          sizeof(textprop_T));
!                   break;
                }
            }
            if (!found)
*** ../vim-8.2.0844/src/ops.c   2020-05-01 14:26:17.128949276 +0200
--- src/ops.c   2020-05-30 14:59:08.418171382 +0200
***************
*** 1887,1892 ****
--- 1887,1893 ----
      char_u      *curr_start = NULL;
      char_u    *cend;
      char_u    *newp;
+     size_t    newp_len;
      char_u    *spaces;        // number of spaces inserted before a line
      int               endcurr1 = NUL;
      int               endcurr2 = NUL;
***************
*** 1900,1907 ****
                                  && has_format_option(FO_REMOVE_COMS);
      int               prev_was_comment;
  #ifdef FEAT_PROP_POPUP
!     textprop_T        **prop_lines = NULL;
!     int               *prop_lengths = NULL;
  #endif
  
      if (save_undo && u_save((linenr_T)(curwin->w_cursor.lnum - 1),
--- 1901,1908 ----
                                  && has_format_option(FO_REMOVE_COMS);
      int               prev_was_comment;
  #ifdef FEAT_PROP_POPUP
!     int               propcount = 0;  // number of props over all joined lines
!     int               props_remaining;
  #endif
  
      if (save_undo && u_save((linenr_T)(curwin->w_cursor.lnum - 1),
***************
*** 1932,1937 ****
--- 1933,1941 ----
      for (t = 0; t < count; ++t)
      {
        curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t));
+ #ifdef FEAT_PROP_POPUP
+       propcount += count_props((linenr_T) (curwin->w_cursor.lnum + t), t > 0);
+ #endif
        if (t == 0 && setmark && !cmdmod.lockmarks)
        {
            // Set the '[ mark.
***************
*** 2014,2020 ****
      col = sumsize - currsize - spaces[count - 1];
  
      // allocate the space for the new line
!     newp = alloc(sumsize + 1);
      if (newp == NULL)
      {
        ret = FAIL;
--- 2018,2028 ----
      col = sumsize - currsize - spaces[count - 1];
  
      // allocate the space for the new line
!     newp_len = sumsize + 1;
! #ifdef FEAT_PROP_POPUP
!     newp_len += propcount * sizeof(textprop_T);
! #endif
!     newp = alloc(newp_len);
      if (newp == NULL)
      {
        ret = FAIL;
***************
*** 2023,2042 ****
      cend = newp + sumsize;
      *cend = 0;
  
- #ifdef FEAT_PROP_POPUP
-     // We need to move properties of the lines that are going to be deleted to
-     // the new long one.
-     if (curbuf->b_has_textprop && !text_prop_frozen)
-     {
-       // Allocate an array to copy the text properties of joined lines into.
-       // And another array to store the number of properties in each line.
-       prop_lines = ALLOC_CLEAR_MULT(textprop_T *, count - 1);
-       prop_lengths = ALLOC_CLEAR_MULT(int, count - 1);
-       if (prop_lengths == NULL)
-           VIM_CLEAR(prop_lines);
-     }
- #endif
- 
      /*
       * Move affected lines to the new long one.
       * This loops backwards over the joined lines, including the original 
line.
--- 2031,2036 ----
***************
*** 2045,2056 ****
--- 2039,2054 ----
       * column.  This is not Vi compatible, but Vi deletes the marks, thus that
       * should not really be a problem.
       */
+ #ifdef FEAT_PROP_POPUP
+     props_remaining = propcount;
+ #endif
      for (t = count - 1; ; --t)
      {
        int spaces_removed;
  
        cend -= currsize;
        mch_memmove(cend, curr, (size_t)currsize);
+ 
        if (spaces[t] > 0)
        {
            cend -= spaces[t];
***************
*** 2063,2077 ****
  
        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;
  #ifdef FEAT_PROP_POPUP
!       if (prop_lines != NULL)
!           adjust_props_for_join(curwin->w_cursor.lnum + t,
!                                     prop_lines + t - 1, prop_lengths + t - 1,
!                        (long)(cend - newp - spaces_removed), spaces_removed);
  #endif
  
        curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
        if (remove_comments)
            curr += comments[t - 1];
--- 2061,2074 ----
  
        mark_col_adjust(curwin->w_cursor.lnum + t, (colnr_T)0, (linenr_T)-t,
                         (long)(cend - newp - spaces_removed), spaces_removed);
  #ifdef FEAT_PROP_POPUP
!       prepend_joined_props(newp + sumsize + 1, propcount, &props_remaining,
!               curwin->w_cursor.lnum + t, t == count - 1,
!               (long)(cend - newp), spaces_removed);
  #endif
  
+       if (t == 0)
+           break;
        curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
        if (remove_comments)
            curr += comments[t - 1];
***************
*** 2080,2092 ****
        currsize = (int)STRLEN(curr);
      }
  
! #ifdef FEAT_PROP_POPUP
!     if (prop_lines != NULL)
!       join_prop_lines(curwin->w_cursor.lnum, newp,
!                                             prop_lines, prop_lengths, count);
!     else
! #endif
!       ml_replace(curwin->w_cursor.lnum, newp, FALSE);
  
      if (setmark && !cmdmod.lockmarks)
      {
--- 2077,2083 ----
        currsize = (int)STRLEN(curr);
      }
  
!     ml_replace_len(curwin->w_cursor.lnum, newp, newp_len, TRUE, FALSE);
  
      if (setmark && !cmdmod.lockmarks)
      {
*** ../vim-8.2.0844/src/proto/textprop.pro      2020-01-10 19:56:42.774995632 
+0100
--- src/proto/textprop.pro      2020-05-30 15:28:51.555427691 +0200
***************
*** 3,8 ****
--- 3,9 ----
  void f_prop_add(typval_T *argvars, typval_T *rettv);
  void prop_add_common(linenr_T start_lnum, colnr_T start_col, dict_T *dict, 
buf_T *default_buf, typval_T *dict_arg);
  int get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int 
will_change);
+ int count_props(linenr_T lnum, int only_starting);
  int find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, 
linenr_T *found_lnum);
  proptype_T *text_prop_type_by_id(buf_T *buf, int id);
  void f_prop_clear(typval_T *argvars, typval_T *rettv);
***************
*** 18,23 ****
  void clear_buf_prop_types(buf_T *buf);
  int adjust_prop_columns(linenr_T lnum, colnr_T col, int bytes_added, int 
flags);
  void adjust_props_for_split(linenr_T lnum_props, linenr_T lnum_top, int kept, 
int deleted);
! void adjust_props_for_join(linenr_T lnum, textprop_T **prop_line, int 
*prop_length, long col, int removed);
! void join_prop_lines(linenr_T lnum, char_u *newp, textprop_T **prop_lines, 
int *prop_lengths, int count);
  /* vim: set ft=c : */
--- 19,23 ----
  void clear_buf_prop_types(buf_T *buf);
  int adjust_prop_columns(linenr_T lnum, colnr_T col, int bytes_added, int 
flags);
  void adjust_props_for_split(linenr_T lnum_props, linenr_T lnum_top, int kept, 
int deleted);
! void prepend_joined_props(char_u *new_props, int propcount, int 
*props_remaining, linenr_T lnum, int add_all, long col, int removed);
  /* vim: set ft=c : */
*** ../vim-8.2.0844/src/textprop.c      2020-03-14 07:46:36.661672970 +0100
--- src/textprop.c      2020-05-30 15:28:10.047589461 +0200
***************
*** 380,385 ****
--- 380,409 ----
      return (int)(proplen / sizeof(textprop_T));
  }
  
+ /**
+  * Return the number of text properties on line "lnum" in the current buffer.
+  * When "only_starting" is true only text properties starting in this line 
will
+  * be considered.
+  */
+     int
+ count_props(linenr_T lnum, int only_starting)
+ {
+     char_u    *props;
+     int               proplen = get_text_props(curbuf, lnum, &props, 0);
+     int               result = proplen;
+     int               i;
+     textprop_T        prop;
+ 
+     if (only_starting)
+       for (i = 0; i < proplen; ++i)
+       {
+           mch_memmove(&prop, props + i * sizeof(prop), sizeof(prop));
+           if (prop.tp_flags & TP_FLAG_CONT_PREV)
+               --result;
+       }
+     return result;
+ }
+ 
  /*
   * Find text property "type_id" in the visible lines of window "wp".
   * Match "id" when it is > 0.
***************
*** 564,578 ****
      dict_T      *dict;
      buf_T       *buf = curbuf;
      dictitem_T  *di;
!     int         lnum_start;
!     int         start_pos_has_prop = 0;
!     int         seen_end = 0;
!     int         id = -1;
!     int         type_id = -1;
!     int         skipstart = 0;
!     int         lnum = -1;
!     int         col = -1;
!     int         dir = 1;    // 1 = forward, -1 = backward
  
      if (argvars[0].v_type != VAR_DICT || argvars[0].vval.v_dict == NULL)
      {
--- 588,602 ----
      dict_T      *dict;
      buf_T       *buf = curbuf;
      dictitem_T  *di;
!     int               lnum_start;
!     int               start_pos_has_prop = 0;
!     int               seen_end = 0;
!     int               id = -1;
!     int               type_id = -1;
!     int               skipstart = 0;
!     int               lnum = -1;
!     int               col = -1;
!     int               dir = 1;    // 1 = forward, -1 = backward
  
      if (argvars[0].v_type != VAR_DICT || argvars[0].vval.v_dict == NULL)
      {
***************
*** 652,658 ****
        char_u  *text = ml_get_buf(buf, lnum, FALSE);
        size_t  textlen = STRLEN(text) + 1;
        int     count = (int)((buf->b_ml.ml_line_len - textlen)
!                                 / sizeof(textprop_T));
        int         i;
        textprop_T  prop;
        int         prop_start;
--- 676,682 ----
        char_u  *text = ml_get_buf(buf, lnum, FALSE);
        size_t  textlen = STRLEN(text) + 1;
        int     count = (int)((buf->b_ml.ml_line_len - textlen)
!                                                        / sizeof(textprop_T));
        int         i;
        textprop_T  prop;
        int         prop_start;
***************
*** 856,863 ****
        len = STRLEN(text) + 1;
        if ((size_t)buf->b_ml.ml_line_len > len)
        {
!           static textprop_T textprop;  // static because of alignment
!           unsigned          idx;
  
            for (idx = 0; idx < (buf->b_ml.ml_line_len - len)
                                                   / sizeof(textprop_T); ++idx)
--- 880,887 ----
        len = STRLEN(text) + 1;
        if ((size_t)buf->b_ml.ml_line_len > len)
        {
!           static textprop_T   textprop;  // static because of alignment
!           unsigned            idx;
  
            for (idx = 0; idx < (buf->b_ml.ml_line_len - len)
                                                   / sizeof(textprop_T); ++idx)
***************
*** 1212,1217 ****
--- 1236,1312 ----
      buf->b_proptypes = NULL;
  }
  
+ // Struct used to return two values from adjust_prop().
+ typedef struct
+ {
+     int dirty;            // if the property was changed
+     int can_drop;   // whether after this change, the prop may be removed
+ } adjustres_T;
+ 
+ /*
+  * Adjust the property for "added" bytes (can be negative) inserted at "col".
+  *
+  * Note that "col" is zero-based, while tp_col is one-based.
+  * Only for the current buffer.
+  * "flags" can have:
+  * APC_SUBSTITUTE:    Text is replaced, not inserted.
+  */
+     static adjustres_T
+ adjust_prop(
+       textprop_T *prop,
+       colnr_T col,
+       int added,
+       int flags)
+ {
+     proptype_T        *pt = text_prop_type_by_id(curbuf, prop->tp_type);
+     int               start_incl = (pt != NULL
+                                   && (pt->pt_flags & PT_FLAG_INS_START_INCL))
+                                                  || (flags & APC_SUBSTITUTE);
+     int               end_incl = (pt != NULL
+                                    && (pt->pt_flags & PT_FLAG_INS_END_INCL));
+               // Do not drop zero-width props if they later can increase in
+               // size.
+     int               droppable = !(start_incl || end_incl);
+     adjustres_T res = {TRUE, FALSE};
+ 
+     if (added > 0)
+     {
+       if (col + 1 <= prop->tp_col
+               - (start_incl || (prop->tp_len == 0 && end_incl)))
+           // Change is entirely before the text property: Only shift
+           prop->tp_col += added;
+       else if (col + 1 < prop->tp_col + prop->tp_len + end_incl)
+           // Insertion was inside text property
+           prop->tp_len += added;
+     }
+     else if (prop->tp_col > col + 1)
+     {
+       if (prop->tp_col + added < col + 1)
+       {
+           prop->tp_len += (prop->tp_col - 1 - col) + added;
+           prop->tp_col = col + 1;
+           if (prop->tp_len <= 0)
+           {
+               prop->tp_len = 0;
+               res.can_drop = droppable;
+           }
+       }
+       else
+           prop->tp_col += added;
+     }
+     else if (prop->tp_len > 0 && prop->tp_col + prop->tp_len > col)
+     {
+       int after = col - added - (prop->tp_col - 1 + prop->tp_len);
+ 
+       prop->tp_len += after > 0 ? added + after : added;
+       res.can_drop = prop->tp_len <= 0 && droppable;
+     }
+     else
+       res.dirty = FALSE;
+ 
+     return res;
+ }
+ 
  /*
   * Adjust the columns of text properties in line "lnum" after position "col" 
to
   * shift by "bytes_added" (can be negative).
***************
*** 1232,1238 ****
  {
      int               proplen;
      char_u    *props;
-     proptype_T  *pt;
      int               dirty = FALSE;
      int               ri, wi;
      size_t    textlen;
--- 1327,1332 ----
***************
*** 1249,1326 ****
      for (ri = 0; ri < proplen; ++ri)
      {
        textprop_T      prop;
!       int             start_incl, end_incl;
!       int             can_drop;
! 
!       mch_memmove(&prop, props + ri * sizeof(textprop_T), sizeof(textprop_T));
!       pt = text_prop_type_by_id(curbuf, prop.tp_type);
!       start_incl = (pt != NULL && (pt->pt_flags & PT_FLAG_INS_START_INCL))
!                                                  || (flags & APC_SUBSTITUTE);
!       end_incl = (pt != NULL && (pt->pt_flags & PT_FLAG_INS_END_INCL));
!       // Do not drop zero-width props if they later can increase in size
!       can_drop = !(start_incl || end_incl);
  
!       if (bytes_added > 0)
!       {
!           if (col + 1 <= prop.tp_col
!                             - (start_incl || (prop.tp_len == 0 && end_incl)))
!           {
!               // Change is entirely before the text property: Only shift
!               prop.tp_col += bytes_added;
!               // Save for undo if requested and not done yet.
!               if ((flags & APC_SAVE_FOR_UNDO) && !dirty)
!                   u_savesub(lnum);
!               dirty = TRUE;
!           }
!           else if (col + 1 < prop.tp_col + prop.tp_len + end_incl)
!           {
!               // Insertion was inside text property
!               prop.tp_len += bytes_added;
!               // Save for undo if requested and not done yet.
!               if ((flags & APC_SAVE_FOR_UNDO) && !dirty)
!                   u_savesub(lnum);
!               dirty = TRUE;
!           }
!       }
!       else if (prop.tp_col > col + 1)
!       {
!           int len_changed = FALSE;
! 
!           if (prop.tp_col + bytes_added < col + 1)
!           {
!               prop.tp_len += (prop.tp_col - 1 - col) + bytes_added;
!               prop.tp_col = col + 1;
!               len_changed = TRUE;
!           }
!           else
!               prop.tp_col += bytes_added;
!           // Save for undo if requested and not done yet.
!           if ((flags & APC_SAVE_FOR_UNDO) && !dirty)
!               u_savesub(lnum);
!           dirty = TRUE;
!           if (len_changed && prop.tp_len <= 0)
!           {
!               prop.tp_len = 0;
!               if (can_drop)
!                   continue;  // drop this text property
!           }
!       }
!       else if (prop.tp_len > 0 && prop.tp_col + prop.tp_len > col)
        {
-           int after = col - bytes_added - (prop.tp_col - 1 + prop.tp_len);
- 
-           if (after > 0)
-               prop.tp_len += bytes_added + after;
-           else
-               prop.tp_len += bytes_added;
            // Save for undo if requested and not done yet.
            if ((flags & APC_SAVE_FOR_UNDO) && !dirty)
                u_savesub(lnum);
            dirty = TRUE;
-           if (prop.tp_len <= 0 && can_drop)
-               continue;  // drop this text property
        }
! 
        mch_memmove(props + wi * sizeof(textprop_T), &prop, sizeof(textprop_T));
        ++wi;
      }
--- 1343,1361 ----
      for (ri = 0; ri < proplen; ++ri)
      {
        textprop_T      prop;
!       adjustres_T     res;
  
!       mch_memmove(&prop, props + ri * sizeof(prop), sizeof(prop));
!       res = adjust_prop(&prop, col, bytes_added, flags);
!       if (res.dirty)
        {
            // Save for undo if requested and not done yet.
            if ((flags & APC_SAVE_FOR_UNDO) && !dirty)
                u_savesub(lnum);
            dirty = TRUE;
        }
!       if (res.can_drop)
!           continue; // Drop this text property
        mch_memmove(props + wi * sizeof(textprop_T), &prop, sizeof(textprop_T));
        ++wi;
      }
***************
*** 1372,1397 ****
      for (i = 0; i < count; ++i)
      {
        textprop_T  prop;
!       textprop_T *p;
  
        // copy the prop to an aligned structure
        mch_memmove(&prop, props + i * sizeof(textprop_T), sizeof(textprop_T));
  
!       if (prop.tp_col < kept && ga_grow(&prevprop, 1) == OK)
        {
!           p = ((textprop_T *)prevprop.ga_data) + prevprop.ga_len;
            *p = prop;
            if (p->tp_col + p->tp_len >= kept)
                p->tp_len = kept - p->tp_col;
!           ++prevprop.ga_len;
        }
  
        // Only add the property to the next line if the length is bigger than
        // zero.
!       if (prop.tp_col + prop.tp_len > skipped && ga_grow(&nextprop, 1) == OK)
        {
!           p = ((textprop_T *)nextprop.ga_data) + nextprop.ga_len;
            *p = prop;
            if (p->tp_col > skipped)
                p->tp_col -= skipped - 1;
            else
--- 1407,1444 ----
      for (i = 0; i < count; ++i)
      {
        textprop_T  prop;
!       proptype_T *pt;
!       int         start_incl, end_incl;
!       int         cont_prev, cont_next;
  
        // copy the prop to an aligned structure
        mch_memmove(&prop, props + i * sizeof(textprop_T), sizeof(textprop_T));
  
!       pt = text_prop_type_by_id(curbuf, prop.tp_type);
!       start_incl = (pt != NULL && (pt->pt_flags & PT_FLAG_INS_START_INCL));
!       end_incl = (pt != NULL && (pt->pt_flags & PT_FLAG_INS_END_INCL));
!       cont_prev = prop.tp_col + !start_incl <= kept;
!       cont_next = skipped <= prop.tp_col + prop.tp_len - !end_incl;
! 
!       if (cont_prev && ga_grow(&prevprop, 1) == OK)
        {
!           textprop_T *p = ((textprop_T *)prevprop.ga_data) + prevprop.ga_len;
! 
            *p = prop;
+           ++prevprop.ga_len;
            if (p->tp_col + p->tp_len >= kept)
                p->tp_len = kept - p->tp_col;
!           if (cont_next)
!               p->tp_flags |= TP_FLAG_CONT_NEXT;
        }
  
        // Only add the property to the next line if the length is bigger than
        // zero.
!       if (cont_next && ga_grow(&nextprop, 1) == OK)
        {
!           textprop_T *p = ((textprop_T *)nextprop.ga_data) + nextprop.ga_len;
            *p = prop;
+           ++nextprop.ga_len;
            if (p->tp_col > skipped)
                p->tp_col -= skipped - 1;
            else
***************
*** 1399,1405 ****
                p->tp_len -= skipped - p->tp_col;
                p->tp_col = 1;
            }
!           ++nextprop.ga_len;
        }
      }
  
--- 1446,1453 ----
                p->tp_len -= skipped - p->tp_col;
                p->tp_col = 1;
            }
!           if (cont_prev)
!               p->tp_flags |= TP_FLAG_CONT_PREV;
        }
      }
  
***************
*** 1412,1522 ****
  }
  
  /*
!  * Line "lnum" has been joined and will end up at column "col" in the new 
line.
!  * "removed" bytes have been removed from the start of the line, properties
!  * there are to be discarded.
!  * Move the adjusted text properties to an allocated string, store it in
!  * "prop_line" and adjust the columns.
   */
      void
! adjust_props_for_join(
        linenr_T    lnum,
!       textprop_T  **prop_line,
!       int         *prop_length,
        long        col,
        int         removed)
  {
!     int               proplen;
!     char_u    *props;
!     int               ri;
!     int               wi = 0;
  
!     proplen = get_text_props(curbuf, lnum, &props, FALSE);
!     if (proplen > 0)
      {
!       *prop_line = ALLOC_MULT(textprop_T, proplen);
!       if (*prop_line != NULL)
        {
!           for (ri = 0; ri < proplen; ++ri)
            {
!               textprop_T *cp = *prop_line + wi;
  
!               mch_memmove(cp, props + ri * sizeof(textprop_T),
!                                                          sizeof(textprop_T));
!               if (cp->tp_col + cp->tp_len > removed)
                {
!                   if (cp->tp_col > removed)
!                       cp->tp_col += col;
!                   else
!                   {
!                       // property was partly deleted, make it shorter
!                       cp->tp_len -= removed - cp->tp_col;
!                       cp->tp_col = col;
!                   }
!                   ++wi;
                }
            }
        }
-       *prop_length = wi;
      }
  }
  
- /*
-  * After joining lines: concatenate the text and the properties of all joined
-  * lines into one line and replace the line.
-  */
-     void
- join_prop_lines(
-       linenr_T    lnum,
-       char_u      *newp,
-       textprop_T  **prop_lines,
-       int         *prop_lengths,
-       int         count)
- {
-     size_t    proplen = 0;
-     size_t    oldproplen;
-     char_u    *props;
-     int               i;
-     size_t    len;
-     char_u    *line;
-     size_t    l;
- 
-     for (i = 0; i < count - 1; ++i)
-       proplen += prop_lengths[i];
-     if (proplen == 0)
-     {
-       ml_replace(lnum, newp, FALSE);
-       return;
-     }
- 
-     // get existing properties of the joined line
-     oldproplen = get_text_props(curbuf, lnum, &props, FALSE);
- 
-     len = STRLEN(newp) + 1;
-     line = alloc(len + (oldproplen + proplen) * sizeof(textprop_T));
-     if (line == NULL)
-       return;
-     mch_memmove(line, newp, len);
-     if (oldproplen > 0)
-     {
-       l = oldproplen * sizeof(textprop_T);
-       mch_memmove(line + len, props, l);
-       len += l;
-     }
- 
-     for (i = 0; i < count - 1; ++i)
-       if (prop_lines[i] != NULL)
-       {
-           l = prop_lengths[i] * sizeof(textprop_T);
-           mch_memmove(line + len, prop_lines[i], l);
-           len += l;
-           vim_free(prop_lines[i]);
-       }
- 
-     ml_replace_len(lnum, line, (colnr_T)len, TRUE, FALSE);
-     vim_free(newp);
-     vim_free(prop_lines);
-     vim_free(prop_lengths);
- }
- 
  #endif // FEAT_PROP_POPUP
--- 1460,1522 ----
  }
  
  /*
!  * Prepend properties of joined line "lnum" to "new_props".
   */
      void
! prepend_joined_props(
!       char_u      *new_props,
!       int         propcount,
!       int         *props_remaining,
        linenr_T    lnum,
!       int         add_all,
        long        col,
        int         removed)
  {
!     char_u *props;
!     int           proplen = get_text_props(curbuf, lnum, &props, FALSE);
!     int           i;
  
!     for (i = proplen; i-- > 0; )
      {
!       textprop_T  prop;
!       int         end;
! 
!       mch_memmove(&prop, props + i * sizeof(prop), sizeof(prop));
!       end = !(prop.tp_flags & TP_FLAG_CONT_NEXT);
! 
!       adjust_prop(&prop, 0, -removed, 0); // Remove leading spaces
!       adjust_prop(&prop, -1, col, 0); // Make line start at its final colum
! 
!       if (add_all || end)
!           mch_memmove(new_props + --(*props_remaining) * sizeof(prop),
!                                                         &prop, sizeof(prop));
!       else
        {
!           int j;
!           int found = FALSE;
! 
!           // Search for continuing prop.
!           for (j = *props_remaining; j < propcount; ++j)
            {
!               textprop_T op;
  
!               mch_memmove(&op, new_props + j * sizeof(op), sizeof(op));
!               if ((op.tp_flags & TP_FLAG_CONT_PREV)
!                       && op.tp_id == prop.tp_id && op.tp_type == prop.tp_type)
                {
!                   found = TRUE;
!                   op.tp_len += op.tp_col - prop.tp_col;
!                   op.tp_col = prop.tp_col;
!                   // Start/end is taken care of when deleting joined lines
!                   op.tp_flags = prop.tp_flags;
!                   mch_memmove(new_props + j * sizeof(op), &op, sizeof(op));
!                   break;
                }
            }
+           if (!found)
+               internal_error("text property above joined line not found");
        }
      }
  }
  
  #endif // FEAT_PROP_POPUP
*** ../vim-8.2.0844/src/testdir/dumps/Test_textprop_01.dump     2019-11-09 
21:17:04.000000000 +0100
--- src/testdir/dumps/Test_textprop_01.dump     2020-05-30 15:13:03.827077317 
+0200
***************
*** 2,8 ****
  | +0#af5f00255&@1|2| |N+0#0000000#ffff4012|u|m|b|é|r| |1+0#4040ff13&|2|3| 
+0#0000000&|ä|n|d| |t|h|œ|n| |4+0#4040ff13&|¾|7|.+0#0000000&| +0&#ffffff0@46
  | +8#af5f00255&@1|3| 
>-+8#0000000#ffff4012|x+8&#ffffff0|a+8#4040ff13&@1|x+8#0000000&|-@1|x+8#4040ff13&|b@1|x+8#0000000&|-@1|x|c+8#4040ff13&@1|x|-+8#0000000&@1|x+8#4040ff13&|d@1|x|-+8#0000000&@1|
 @45
  | +0#af5f00255&@1|4| |/+0#40ff4011&@1| 
|c|o|m+0#0000000#e0e0e08@1|e|n+0#40ff4011#ffffff0|t| |w+0&#e0e0e08|i|t|h| 
|e+8&&|r@1|o|r| +0&#ffffff0|i|n| |i|t| +0#0000000&@43
! | +0#af5f00255&@1|5| |f+0#0000000&|i|r|s|t| |l+0&#ffff4012|i|n|e| 
@1|s|e|c|o|n|d| +0&#ffffff0|l|i|n|e| @1|t|h|i|r|d| |l|i|n|e| |f|o|u|r|t|h| 
|l+0&#ffff4012|i|n|e| +0&#ffffff0@23
  |~+0#4040ff13&| @73
  |~| @73
  | +0#0000000&@56|3|,|1| @10|A|l@1| 
--- 2,8 ----
  | +0#af5f00255&@1|2| |N+0#0000000#ffff4012|u|m|b|é|r| |1+0#4040ff13&|2|3| 
+0#0000000&|ä|n|d| |t|h|œ|n| |4+0#4040ff13&|¾|7|.+0#0000000&| +0&#ffffff0@46
  | +8#af5f00255&@1|3| 
>-+8#0000000#ffff4012|x+8&#ffffff0|a+8#4040ff13&@1|x+8#0000000&|-@1|x+8#4040ff13&|b@1|x+8#0000000&|-@1|x|c+8#4040ff13&@1|x|-+8#0000000&@1|x+8#4040ff13&|d@1|x|-+8#0000000&@1|
 @45
  | +0#af5f00255&@1|4| |/+0#40ff4011&@1| 
|c|o|m+0#0000000#e0e0e08@1|e|n+0#40ff4011#ffffff0|t| |w+0&#e0e0e08|i|t|h| 
|e+8&&|r@1|o|r| +0&#ffffff0|i|n| |i|t| +0#0000000&@43
! | +0#af5f00255&@1|5| |f+0#0000000&|i|r|s|t| |l+0&#ffff4012|i|n|e| 
@1|s|e|c|o|n|d| +0&#ffffff0|l|i|n|e| @1|t|h|i|r|d| |l|i|n|e| 
+0&#ffff4012|f+0&#ffffff0|o|u|r|t|h| |l+0&#ffff4012|i|n|e| +0&#ffffff0@23
  |~+0#4040ff13&| @73
  |~| @73
  | +0#0000000&@56|3|,|1| @10|A|l@1| 
*** ../vim-8.2.0844/src/version.c       2020-05-30 14:46:48.861163777 +0200
--- src/version.c       2020-05-30 15:29:41.971231122 +0200
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     845,
  /**/

-- 
msdn.microsoft.com:
ERROR_SUCCESS 0 (0x0) The operation completed successfully.
I have always suspected that for Microsoft success is an error.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202005301332.04UDWTGp331736%40masaka.moolenaar.net.

Raspunde prin e-mail lui