To: vim_dev@googlegroups.com Subject: Patch 8.2.0324 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0324 Problem: Text property not updated correctly when inserting/deleting. Solution: Use the right column when deleting. Make zero-width text properties respect start_incl and end_incl. (Axel Forsman, closes #5696, closes #5679) Files: src/change.c, src/textprop.c, src/testdir/test_listener.vim, src/testdir/test_textprop.vim *** ../vim-8.2.0323/src/change.c 2020-01-01 16:18:33.204997405 +0100 --- src/change.c 2020-02-26 21:30:39.696404345 +0100 *************** *** 1301,1307 **** #endif // mark the buffer as changed and prepare for displaying ! inserted_bytes(lnum, curwin->w_cursor.col, -count); return OK; } --- 1301,1307 ---- #endif // mark the buffer as changed and prepare for displaying ! inserted_bytes(lnum, col, -count); return OK; } *** ../vim-8.2.0323/src/textprop.c 2020-01-10 19:56:42.774995632 +0100 --- src/textprop.c 2020-02-26 21:47:14.650393448 +0100 *************** *** 1232,1238 **** { int proplen; char_u *props; - textprop_T tmp_prop; proptype_T *pt; int dirty = FALSE; int ri, wi; --- 1232,1237 ---- *************** *** 1249,1310 **** wi = 0; // write index for (ri = 0; ri < proplen; ++ri) { ! int start_incl; ! mch_memmove(&tmp_prop, props + ri * sizeof(textprop_T), ! sizeof(textprop_T)); ! pt = text_prop_type_by_id(curbuf, tmp_prop.tp_type); ! start_incl = (flags & APC_SUBSTITUTE) || ! (pt != NULL && (pt->pt_flags & PT_FLAG_INS_START_INCL)); ! ! if (bytes_added > 0 ! && (tmp_prop.tp_col >= col + (start_incl ? 2 : 1))) { ! tmp_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 (bytes_added <= 0 && (tmp_prop.tp_col > col + 1)) { int len_changed = FALSE; ! if (tmp_prop.tp_col + bytes_added < col + 1) { ! tmp_prop.tp_len += (tmp_prop.tp_col - 1 - col) + bytes_added; ! tmp_prop.tp_col = col + 1; len_changed = TRUE; } else ! tmp_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 && tmp_prop.tp_len <= 0) ! continue; // drop this text property } ! else if (tmp_prop.tp_len > 0 ! && tmp_prop.tp_col + tmp_prop.tp_len > col ! + ((pt != NULL && (pt->pt_flags & PT_FLAG_INS_END_INCL)) ! ? 0 : 1)) { ! int after = col - bytes_added ! - (tmp_prop.tp_col - 1 + tmp_prop.tp_len); if (after > 0) ! tmp_prop.tp_len += bytes_added + after; else ! tmp_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 (tmp_prop.tp_len <= 0) continue; // drop this text property } ! mch_memmove(props + wi * sizeof(textprop_T), &tmp_prop, ! sizeof(textprop_T)); ++wi; } if (dirty) --- 1248,1327 ---- wi = 0; // write index 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; } if (dirty) *** ../vim-8.2.0323/src/testdir/test_listener.vim 2020-01-03 20:59:55.847158293 +0100 --- src/testdir/test_listener.vim 2020-02-26 21:30:39.696404345 +0100 *************** *** 326,328 **** --- 326,341 ---- bwipe! delfunc Listener endfunc + + func Test_col_after_deletion_moved_cur() + func Listener(bufnr, start, end, added, changes) + call assert_equal([#{lnum: 1, end: 2, added: 0, col: 2}], a:changes) + endfunc + new + call setline(1, ['foo']) + let lid = listener_add('Listener') + call feedkeys("lD", 'xt') + call listener_flush() + bwipe! + delfunc Listener + endfunc *** ../vim-8.2.0323/src/testdir/test_textprop.vim 2020-01-13 20:40:47.694773941 +0100 --- src/testdir/test_textprop.vim 2020-02-26 21:43:43.762965932 +0100 *************** *** 6,13 **** source screendump.vim - " test length zero - func Test_proptype_global() call prop_type_add('comment', {'highlight': 'Directory', 'priority': 123, 'start_incl': 1, 'end_incl': 1}) let proptypes = prop_type_list() --- 6,11 ---- *************** *** 233,245 **** " Prop without length or end column is zero length call prop_clear(1) ! call prop_add(1, 5, {'type': 'two'}) ! let expected = [{'col': 5, 'length': 0, 'type': 'two', 'id': 0, 'start': 1, 'end': 1}] call assert_equal(expected, prop_list(1)) call assert_fails("call prop_add(1, 5, {'type': 'two', 'bufnr': 234343})", 'E158:') call DeletePropTypes() bwipe! endfunc --- 231,250 ---- " Prop without length or end column is zero length call prop_clear(1) ! call prop_type_add('included', {'start_incl': 1, 'end_incl': 1}) ! call prop_add(1, 5, #{type: 'included'}) ! let expected = [#{col: 5, length: 0, type: 'included', id: 0, start: 1, end: 1}] ! call assert_equal(expected, prop_list(1)) ! ! " Inserting text makes the prop bigger. ! exe "normal 5|ixx\" ! let expected = [#{col: 5, length: 2, type: 'included', id: 0, start: 1, end: 1}] call assert_equal(expected, prop_list(1)) call assert_fails("call prop_add(1, 5, {'type': 'two', 'bufnr': 234343})", 'E158:') call DeletePropTypes() + call prop_type_delete('included') bwipe! endfunc *** ../vim-8.2.0323/src/version.c 2020-02-26 21:24:19.157582116 +0100 --- src/version.c 2020-02-26 21:33:38.852232085 +0100 *************** *** 740,741 **** --- 740,743 ---- { /* Add new patch number below this line */ + /**/ + 324, /**/ -- A: Because it messes up the order in which people normally read text. Q: Why is top-posting such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail? /// 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 ///