]> granicus.if.org Git - vim/commitdiff
patch 8.2.0845: text properties crossing lines not handled correctly v8.2.0845
authorBram Moolenaar <Bram@vim.org>
Sat, 30 May 2020 13:32:02 +0000 (15:32 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 30 May 2020 13:32:02 +0000 (15:32 +0200)
Problem:    Text properties crossing lines not handled correctly.
Solution:   When joining lines merge text properties if possible.
            (Axel Forsman, closes #5839, closes #5683)

src/memline.c
src/ops.c
src/proto/textprop.pro
src/testdir/dumps/Test_textprop_01.dump
src/testdir/test_textprop.vim
src/textprop.c
src/version.c

index 6d02119e556234792758a13970f38e199fa606ce..3379a396506470fb230c32cbce0d26f4f9cff2c4 100644 (file)
@@ -3420,7 +3420,6 @@ adjust_text_props_for_delete(
     int                done_del;
     int                done_this;
     textprop_T prop_del;
-    textprop_T prop_this;
     bhdr_T     *hp;
     DATA_BL    *dp;
     int                idx;
@@ -3451,7 +3450,8 @@ adjust_text_props_for_delete(
                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;
+                   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,24 +3466,24 @@ adjust_text_props_for_delete(
            }
 
            found = FALSE;
-           for (done_this = 0; done_this < this_props_len; done_this += sizeof(textprop_T))
+           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
+               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)
                {
-                   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");
+                   prop_this.tp_flags &= ~flag;
+                   mch_memmove(text + textlen + done_del, &prop_this,
+                                                          sizeof(textprop_T));
+                   break;
                }
            }
            if (!found)
index 9ab355596b6061008e86948fbdf925d61d4cde62..ed1688635d9b377eeb14ab688776dc81e21e0a41 100644 (file)
--- a/src/ops.c
+++ b/src/ops.c
@@ -1887,6 +1887,7 @@ do_join(
     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,8 +1901,8 @@ do_join(
                                  && has_format_option(FO_REMOVE_COMS);
     int                prev_was_comment;
 #ifdef FEAT_PROP_POPUP
-    textprop_T **prop_lines = NULL;
-    int                *prop_lengths = NULL;
+    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,6 +1933,9 @@ do_join(
     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,7 +2018,11 @@ do_join(
     col = sumsize - currsize - spaces[count - 1];
 
     // allocate the space for the new line
-    newp = alloc(sumsize + 1);
+    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,20 +2031,6 @@ do_join(
     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.
@@ -2045,12 +2039,16 @@ do_join(
      * 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,15 +2061,14 @@ do_join(
 
        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);
+       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,13 +2077,7 @@ do_join(
        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);
+    ml_replace_len(curwin->w_cursor.lnum, newp, newp_len, TRUE, FALSE);
 
     if (setmark && !cmdmod.lockmarks)
     {
index 30a846ef639ed4dd174f6193e7cd6639238c8426..8a4a22fa0bc858c994f9d2de37a25b61f500a8af 100644 (file)
@@ -3,6 +3,7 @@ int find_prop_type_id(char_u *name, buf_T *buf);
 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,6 +19,5 @@ void clear_global_prop_types(void);
 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);
+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 : */
index aabe25b081d39cdca1778d877c24b61a3c996bd8..b8a50bed3897dca4598616269c49c60a8226ac9d 100644 (file)
@@ -2,7 +2,7 @@
 | +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#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| 
index f91597ff5108e144cd8b01728d679f9054816574..5dddd16910196292f46aefeee5f752d362c24224 100644 (file)
@@ -460,9 +460,11 @@ func Test_prop_open_line()
   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,11 +577,13 @@ func Test_prop_substitute()
        \ 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,4 +1232,25 @@ func Test_prop_func_invalid_args()
   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
index 580c3e7a97e3f49a5d4e695b8118e253ebf8de51..2145db262f64867fe0e6b96251ed1a3ba4740688 100644 (file)
@@ -380,6 +380,30 @@ get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change)
     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,15 +588,15 @@ f_prop_find(typval_T *argvars, typval_T *rettv)
     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
+    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,7 +676,7 @@ f_prop_find(typval_T *argvars, typval_T *rettv)
        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));
+                                                        / sizeof(textprop_T));
        int         i;
        textprop_T  prop;
        int         prop_start;
@@ -856,8 +880,8 @@ f_prop_remove(typval_T *argvars, typval_T *rettv)
        len = STRLEN(text) + 1;
        if ((size_t)buf->b_ml.ml_line_len > len)
        {
-           static textprop_T textprop;  // static because of alignment
-           unsigned          idx;
+           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,6 +1236,77 @@ clear_buf_prop_types(buf_T *buf)
     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,7 +1327,6 @@ adjust_prop_columns(
 {
     int                proplen;
     char_u     *props;
-    proptype_T  *pt;
     int                dirty = FALSE;
     int                ri, wi;
     size_t     textlen;
@@ -1249,78 +1343,19 @@ adjust_prop_columns(
     for (ri = 0; ri < proplen; ++ri)
     {
        textprop_T      prop;
-       int             start_incl, end_incl;
-       int             can_drop;
+       adjustres_T     res;
 
-       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)
+       mch_memmove(&prop, props + ri * sizeof(prop), sizeof(prop));
+       res = adjust_prop(&prop, col, bytes_added, flags);
+       if (res.dirty)
        {
-           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
        }
-
+       if (res.can_drop)
+           continue; // Drop this text property
        mch_memmove(props + wi * sizeof(textprop_T), &prop, sizeof(textprop_T));
        ++wi;
     }
@@ -1372,26 +1407,38 @@ adjust_props_for_split(
     for (i = 0; i < count; ++i)
     {
        textprop_T  prop;
-       textprop_T *p;
+       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));
 
-       if (prop.tp_col < kept && ga_grow(&prevprop, 1) == OK)
+       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)
        {
-           p = ((textprop_T *)prevprop.ga_data) + prevprop.ga_len;
+           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;
-           ++prevprop.ga_len;
+           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 (prop.tp_col + prop.tp_len > skipped && ga_grow(&nextprop, 1) == OK)
+       if (cont_next && ga_grow(&nextprop, 1) == OK)
        {
-           p = ((textprop_T *)nextprop.ga_data) + nextprop.ga_len;
+           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,7 +1446,8 @@ adjust_props_for_split(
                p->tp_len -= skipped - p->tp_col;
                p->tp_col = 1;
            }
-           ++nextprop.ga_len;
+           if (cont_prev)
+               p->tp_flags |= TP_FLAG_CONT_PREV;
        }
     }
 
@@ -1412,111 +1460,63 @@ adjust_props_for_split(
 }
 
 /*
- * 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.
+ * Prepend properties of joined line "lnum" to "new_props".
  */
     void
-adjust_props_for_join(
+prepend_joined_props(
+       char_u      *new_props,
+       int         propcount,
+       int         *props_remaining,
        linenr_T    lnum,
-       textprop_T  **prop_line,
-       int         *prop_length,
+       int         add_all,
        long        col,
        int         removed)
 {
-    int                proplen;
-    char_u     *props;
-    int                ri;
-    int                wi = 0;
+    char_u *props;
+    int            proplen = get_text_props(curbuf, lnum, &props, FALSE);
+    int            i;
 
-    proplen = get_text_props(curbuf, lnum, &props, FALSE);
-    if (proplen > 0)
+    for (i = proplen; i-- > 0; )
     {
-       *prop_line = ALLOC_MULT(textprop_T, proplen);
-       if (*prop_line != NULL)
+       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
        {
-           for (ri = 0; ri < proplen; ++ri)
+           int j;
+           int found = FALSE;
+
+           // Search for continuing prop.
+           for (j = *props_remaining; j < propcount; ++j)
            {
-               textprop_T *cp = *prop_line + wi;
+               textprop_T op;
 
-               mch_memmove(cp, props + ri * sizeof(textprop_T),
-                                                          sizeof(textprop_T));
-               if (cp->tp_col + cp->tp_len > removed)
+               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)
                {
-                   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;
+                   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");
        }
-       *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
index 2a33137dee99ba5a96af34b35e84d9c60da0d536..16dfa277dd388c15f6b4e563ed9be99057080de0 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    845,
 /**/
     844,
 /**/