To: vim_dev@googlegroups.com Subject: Patch 8.2.0845 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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\" + 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 -- Bram@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 ///