Patch 8.2.0845

7 views
Skip to first unread message

Bram Moolenaar

unread,
May 30, 2020, 9:32:38 AM5/30/20
to vim...@googlegroups.com

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 -- Br...@Moolenaar.net -- 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 ///
Reply all
Reply to author
Forward
0 new messages