# HG changeset patch # User Bram Moolenaar # Date 1558299605 -7200 # Node ID ba592f30c0822228ece0b8ab4375959ecff4d9f4 # Parent ba3f15a7e5c9002f3c5de0366c31356100af2646 patch 8.1.1359: text property wrong after :substitute with backslash commit https://github.com/vim/vim/commit/f3333b02f34526da46cdae608f7e2d869bb8c654 Author: Bram Moolenaar Date: Sun May 19 22:53:40 2019 +0200 patch 8.1.1359: text property wrong after :substitute with backslash Problem: Text property wrong after :substitute with backslash. Solution: Adjust text property columns when removing backslashes. (closes #4397) diff --git a/src/change.c b/src/change.c --- a/src/change.c +++ b/src/change.c @@ -684,7 +684,7 @@ inserted_bytes(linenr_T lnum, colnr_T co { #ifdef FEAT_TEXT_PROP if (curbuf->b_has_textprop && added != 0) - adjust_prop_columns(lnum, col, added, FALSE); + adjust_prop_columns(lnum, col, added, 0); #endif changed_bytes(lnum, col); diff --git a/src/edit.c b/src/edit.c --- a/src/edit.c +++ b/src/edit.c @@ -4104,7 +4104,7 @@ replace_do_bs(int limit_col) --text_prop_frozen; adjust_prop_columns(curwin->w_cursor.lnum, curwin->w_cursor.col, - (int)(len_now - len_before), FALSE); + (int)(len_now - len_before), 0); } #endif } diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -5189,7 +5189,7 @@ do_sub(exarg_T *eap) int skip_match = FALSE; linenr_T sub_firstlnum; /* nr of first sub line */ #ifdef FEAT_TEXT_PROP - int save_for_undo = TRUE; + int apc_flags = APC_SAVE_FOR_UNDO | APC_SUBSTITUTE; #endif /* @@ -5612,8 +5612,9 @@ do_sub(exarg_T *eap) // undo first, unless done already. if (adjust_prop_columns(lnum, regmatch.startpos[0].col, sublen - 1 - (regmatch.endpos[0].col - - regmatch.startpos[0].col), save_for_undo)) - save_for_undo = FALSE; + - regmatch.startpos[0].col), + apc_flags)) + apc_flags &= ~APC_SAVE_FOR_UNDO; } #endif } @@ -5715,7 +5716,20 @@ do_sub(exarg_T *eap) for (p1 = new_end; *p1; ++p1) { if (p1[0] == '\\' && p1[1] != NUL) /* remove backslash */ + { STRMOVE(p1, p1 + 1); +#ifdef FEAT_TEXT_PROP + if (curbuf->b_has_textprop) + { + // When text properties are changed, need to save + // for undo first, unless done already. + if (adjust_prop_columns(lnum, + (colnr_T)(p1 - new_start), -1, + apc_flags)) + apc_flags &= ~APC_SAVE_FOR_UNDO; + } +#endif + } else if (*p1 == CAR) { if (u_inssub(lnum) == OK) // prepare for undo diff --git a/src/misc1.c b/src/misc1.c --- a/src/misc1.c +++ b/src/misc1.c @@ -441,7 +441,7 @@ set_indent( // the old indent, when decreasing indent it behaves like spaces // were deleted at the new indent. adjust_prop_columns(curwin->w_cursor.lnum, - (colnr_T)(added > 0 ? (p - oldline) : ind_len), added, FALSE); + (colnr_T)(added > 0 ? (p - oldline) : ind_len), added, 0); } #endif retval = TRUE; diff --git a/src/ops.c b/src/ops.c --- a/src/ops.c +++ b/src/ops.c @@ -1937,7 +1937,7 @@ op_delete(oparg_T *oap) #ifdef FEAT_TEXT_PROP if (curbuf->b_has_textprop && n != 0) - adjust_prop_columns(lnum, bd.textcol, -n, FALSE); + adjust_prop_columns(lnum, bd.textcol, -n, 0); #endif } diff --git a/src/proto/textprop.pro b/src/proto/textprop.pro --- a/src/proto/textprop.pro +++ b/src/proto/textprop.pro @@ -13,7 +13,7 @@ void f_prop_type_get(typval_T *argvars, void f_prop_type_list(typval_T *argvars, typval_T *rettv); 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 save_for_undo); +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); diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -620,6 +620,25 @@ func Test_prop_undo() undo let expected[0].col = 12 call assert_equal(expected, prop_list(1)) + call prop_clear(1) + + " substitute with backslash + call setline(1, 'the number 123 is highlighted.') + call prop_add(1, 12, {'length': 3, 'type': 'comment'}) + let expected = [{'col': 12, 'length': 3, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] + call assert_equal(expected, prop_list(1)) + 1s/the/\The + call assert_equal(expected, prop_list(1)) + 1s/^/\\ + let expected[0].col += 1 + call assert_equal(expected, prop_list(1)) + 1s/^/\~ + let expected[0].col += 1 + call assert_equal(expected, prop_list(1)) + 1s/123/12\\3 + let expected[0].length += 1 + call assert_equal(expected, prop_list(1)) + call prop_clear(1) bwipe! call prop_type_delete('comment') diff --git a/src/textprop.c b/src/textprop.c --- a/src/textprop.c +++ b/src/textprop.c @@ -957,8 +957,9 @@ clear_buf_prop_types(buf_T *buf) * shift by "bytes_added" (can be negative). * Note that "col" is zero-based, while tp_col is one-based. * Only for the current buffer. - * When "save_for_undo" is TRUE then call u_savesub() before making changes to - * the line. + * "flags" can have: + * APC_SAVE_FOR_UNDO: Call u_savesub() before making changes to the line. + * APC_SUBSTITUTE: Text is replaced, not inserted. * Caller is expected to check b_has_textprop and "bytes_added" being non-zero. * Returns TRUE when props were changed. */ @@ -967,7 +968,7 @@ adjust_prop_columns( linenr_T lnum, colnr_T col, int bytes_added, - int save_for_undo) + int flags) { int proplen; char_u *props; @@ -988,15 +989,30 @@ adjust_prop_columns( 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 - + (pt != NULL && (pt->pt_flags & PT_FLAG_INS_START_INCL) - ? 2 : 1)) - : (tmp_prop.tp_col > col + 1)) + && (tmp_prop.tp_col >= col + (start_incl ? 2 : 1))) + { + if (tmp_prop.tp_col < col + (start_incl ? 2 : 1)) + { + tmp_prop.tp_len += (tmp_prop.tp_col - 1 - col) + bytes_added; + tmp_prop.tp_col = col + 1; + } + 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; + } + else if (bytes_added <= 0 && (tmp_prop.tp_col > col + 1)) { if (tmp_prop.tp_col + bytes_added < col + 1) { @@ -1006,7 +1022,7 @@ adjust_prop_columns( else tmp_prop.tp_col += bytes_added; // Save for undo if requested and not done yet. - if (save_for_undo && !dirty) + if ((flags & APC_SAVE_FOR_UNDO) && !dirty) u_savesub(lnum); dirty = TRUE; if (tmp_prop.tp_len <= 0) @@ -1024,7 +1040,7 @@ adjust_prop_columns( else tmp_prop.tp_len += bytes_added; // Save for undo if requested and not done yet. - if (save_for_undo && !dirty) + if ((flags & APC_SAVE_FOR_UNDO) && !dirty) u_savesub(lnum); dirty = TRUE; if (tmp_prop.tp_len <= 0) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -768,6 +768,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1359, +/**/ 1358, /**/ 1357, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -2571,4 +2571,8 @@ long elapsed(DWORD start_tick); #define SAVE_RESTORE_ICON 2 #define SAVE_RESTORE_BOTH (SAVE_RESTORE_TITLE | SAVE_RESTORE_ICON) +// Flags for adjust_prop_columns() +#define APC_SAVE_FOR_UNDO 1 // call u_savesub() before making changes +#define APC_SUBSTITUTE 2 // text is replaced, not inserted + #endif /* VIM__H */