From b559b302e0ecc6fced03d5201dc30f10cff7af0a Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 15 May 2022 13:08:02 +0100 Subject: [PATCH 1/4] patch 8.2.4957: text properties in a wrong position after a block change Problem: Text properties in a wrong position after a block change. Solution: Adjust the properties columns. (closes #10427) --- src/ops.c | 6 ++++++ src/testdir/test_textprop.vim | 33 +++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 41 insertions(+) diff --git a/src/ops.c b/src/ops.c index b11cbf5416..5b4c2957bc 100644 --- a/src/ops.c +++ b/src/ops.c @@ -1814,6 +1814,12 @@ op_change(oparg_T *oap) oldp += bd.textcol; STRMOVE(newp + offset, oldp); ml_replace(linenr, newp, FALSE); +#ifdef FEAT_PROP_POPUP + // Shift the properties for linenr as edit() would do. + if (curbuf->b_has_textprop) + adjust_prop_columns(linenr, bd.textcol, + vpos.coladd + ins_len, 0); +#endif } } check_cursor(); diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim index 50012acaf8..8e12041427 100644 --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -2003,4 +2003,37 @@ func Test_prop_insert_multiline() bwipe! endfunc +func Test_prop_blockwise_change() + new + call AddPropTypes() + + call setline(1, ['foooooo', 'bar', 'baaaaz']) + call prop_add(1, 1, #{end_col: 3, type: 'one'}) + call prop_add(2, 1, #{end_col: 3, type: 'two'}) + call prop_add(3, 1, #{end_col: 3, type: 'three'}) + + " Replace the first two columns with '123', since 'start_incl' is false the + " prop is not extended. + call feedkeys("gg\2jc123\", 'nxt') + + let lines =<< trim END + 123oooooo + 123ar + 123aaaaz + END + call assert_equal(lines, getline(1, '$')) + let expected = [ + \ {'lnum': 1, 'id': 0, 'col': 4, 'type_bufnr': 0, 'end': 1, 'type': 'one', + \ 'length': 1, 'start': 1}, + \ {'lnum': 2, 'id': 0, 'col': 4, 'type_bufnr': 0, 'end': 1, 'type': 'two', + \ 'length': 1, 'start': 1}, + \ {'lnum': 3, 'id': 0, 'col': 4, 'type_bufnr': 0, 'end': 1 , + \ 'type': 'three', 'length': 1, 'start': 1} + \ ] + call assert_equal(expected, prop_list(1, #{end_lnum: 10})) + + call DeletePropTypes() + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 821f3680e2..711349a35f 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4957, /**/ 4956, /**/ From dd41037552c1be3548d2ce34bb1c889f14edb553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=3D=3FUTF-8=3Fq=3FDundar=3D20G=3DC3=3DB6c=3F=3D?= Date: Sun, 15 May 2022 13:59:11 +0100 Subject: [PATCH 2/4] patch 8.2.4958: a couple conditions are always true Problem: A couple conditions are always true. Solution: Remove the conditions. (Goc Dundar, closes #10428) --- src/evalfunc.c | 61 ++++++++++++++++++++++++-------------------------- src/quickfix.c | 2 +- src/version.c | 2 ++ 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index f7a04fdc98..2c012d047e 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -9788,43 +9788,40 @@ f_spellsuggest(typval_T *argvars UNUSED, typval_T *rettv) } #ifdef FEAT_SPELL - if (*curwin->w_s->b_p_spl != NUL) + str = tv_get_string(&argvars[0]); + if (argvars[1].v_type != VAR_UNKNOWN) { - str = tv_get_string(&argvars[0]); - if (argvars[1].v_type != VAR_UNKNOWN) + maxcount = (int)tv_get_number_chk(&argvars[1], &typeerr); + if (maxcount <= 0) + return; + if (argvars[2].v_type != VAR_UNKNOWN) { - maxcount = (int)tv_get_number_chk(&argvars[1], &typeerr); - if (maxcount <= 0) + need_capital = (int)tv_get_bool_chk(&argvars[2], &typeerr); + if (typeerr) return; - if (argvars[2].v_type != VAR_UNKNOWN) - { - need_capital = (int)tv_get_bool_chk(&argvars[2], &typeerr); - if (typeerr) - return; - } } - else - maxcount = 25; - - spell_suggest_list(&ga, str, maxcount, need_capital, FALSE); - - for (i = 0; i < ga.ga_len; ++i) - { - str = ((char_u **)ga.ga_data)[i]; - - li = listitem_alloc(); - if (li == NULL) - vim_free(str); - else - { - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = str; - list_append(rettv->vval.v_list, li); - } - } - ga_clear(&ga); } + else + maxcount = 25; + + spell_suggest_list(&ga, str, maxcount, need_capital, FALSE); + + for (i = 0; i < ga.ga_len; ++i) + { + str = ((char_u **)ga.ga_data)[i]; + + li = listitem_alloc(); + if (li == NULL) + vim_free(str); + else + { + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = 0; + li->li_tv.vval.v_string = str; + list_append(rettv->vval.v_list, li); + } + } + ga_clear(&ga); curwin->w_p_spell = wo_spell_save; #endif } diff --git a/src/quickfix.c b/src/quickfix.c index aab1683f32..dde3f9d566 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -2494,7 +2494,7 @@ qf_push_dir(char_u *dirbuf, struct dir_stack_T **stackptr, int is_file_stack) // store directory on the stack if (vim_isAbsName(dirbuf) || (*stackptr)->next == NULL - || (*stackptr && is_file_stack)) + || is_file_stack) (*stackptr)->dirname = vim_strsave(dirbuf); else { diff --git a/src/version.c b/src/version.c index 711349a35f..856922e1f5 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4958, /**/ 4957, /**/ From b62dc5e7825bc195efe3041d5b3a9f1528359e1c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 15 May 2022 14:50:12 +0100 Subject: [PATCH 3/4] patch 8.2.4959: using NULL regexp program Problem: Using NULL regexp program. Solution: Check for regexp program becoming NULL in more places. --- src/buffer.c | 35 ++++++++++++++++++++++------------- src/testdir/test_buffer.vim | 6 ++++++ src/version.c | 2 ++ 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index dfdb0c430d..3234138aeb 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -2642,13 +2642,15 @@ buflist_findpat( if (*p == '^' && !(attempt & 1)) // add/remove '^' ++p; regmatch.regprog = vim_regcomp(p, magic_isset() ? RE_MAGIC : 0); - if (regmatch.regprog == NULL) - { - vim_free(pat); - return -1; - } FOR_ALL_BUFS_FROM_LAST(buf) + { + if (regmatch.regprog == NULL) + { + // invalid pattern, possibly after switching engine + vim_free(pat); + return -1; + } if (buf->b_p_bl == find_listed #ifdef FEAT_DIFF && (!diffmode || diff_mode_buf(buf)) @@ -2674,6 +2676,7 @@ buflist_findpat( } match = buf->b_fnum; // remember first match } + } vim_regfree(regmatch.regprog); if (match >= 0) // found one match @@ -2766,12 +2769,6 @@ ExpandBufnames( if (attempt > 0 && patc == pat) break; // there was no anchor, no need to try again regmatch.regprog = vim_regcomp(patc + attempt * 11, RE_MAGIC); - if (regmatch.regprog == NULL) - { - if (patc != pat) - vim_free(patc); - return FAIL; - } } // round == 1: Count the matches. @@ -2792,7 +2789,16 @@ ExpandBufnames( #endif if (!fuzzy) + { + if (regmatch.regprog == NULL) + { + // invalid pattern, possibly after recompiling + if (patc != pat) + vim_free(patc); + return FAIL; + } p = buflist_match(®match, buf, p_wic); + } else { p = NULL; @@ -2921,6 +2927,7 @@ ExpandBufnames( /* * Check for a match on the file name for buffer "buf" with regprog "prog". + * Note that rmp->regprog may become NULL when switching regexp engine. */ static char_u * buflist_match( @@ -2939,7 +2946,8 @@ buflist_match( } /* - * Try matching the regexp in "prog" with file name "name". + * Try matching the regexp in "rmp->regprog" with file name "name". + * Note that rmp->regprog may become NULL when switching regexp engine. * Return "name" when there is a match, NULL when not. */ static char_u * @@ -2951,7 +2959,8 @@ fname_match( char_u *match = NULL; char_u *p; - if (name != NULL) + // extra check for valid arguments + if (name != NULL && rmp->regprog != NULL) { // Ignore case when 'fileignorecase' or the argument is set. rmp->rm_ic = p_fic || ignore_case; diff --git a/src/testdir/test_buffer.vim b/src/testdir/test_buffer.vim index 55ef0fda76..4dcfd2d8f7 100644 --- a/src/testdir/test_buffer.vim +++ b/src/testdir/test_buffer.vim @@ -419,6 +419,12 @@ func Test_buf_pattern_invalid() vsplit 00000000000000000000000000 silent! buf [0--]\&\zs*\zs*e bwipe! + + " similar case with different code path + split 0 + edit ΓΏ + silent! buf [0--]\&\zs*\zs*0 + bwipe! endfunc " Test for the 'maxmem' and 'maxmemtot' options diff --git a/src/version.c b/src/version.c index 856922e1f5..dca2f92092 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4959, /**/ 4958, /**/ From 1bdc60eb91b9e6320318d96ea59bd2aad775f408 Mon Sep 17 00:00:00 2001 From: Paul Ollis Date: Sun, 15 May 2022 22:24:55 +0100 Subject: [PATCH 4/4] patch 8.2.4960: text properties that cross lines not updated for deleted line Problem: Text properties that cross line boundary are not correctly updated for a deleted line. Solution: Correct computing location of text property entry. (Paul Ollis, closes #10431, closes #10430) --- src/memline.c | 9 ++++--- src/testdir/test_textprop.vim | 51 +++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/memline.c b/src/memline.c index e098aef552..2cdd2277dc 100644 --- a/src/memline.c +++ b/src/memline.c @@ -3501,8 +3501,9 @@ ml_replace_len( #ifdef FEAT_PROP_POPUP /* * Adjust text properties in line "lnum" for a deleted line. - * When "above" is true this is the line above the deleted line. - * "del_props" are the properties of the deleted line. + * When "above" is true this is the line above the deleted line, otherwise this + * is the line below the deleted line. + * "del_props[del_props_len]" are the properties of the deleted line. */ static void adjust_text_props_for_delete( @@ -3569,7 +3570,7 @@ adjust_text_props_for_delete( : TP_FLAG_CONT_PREV; textprop_T prop_this; - mch_memmove(&prop_this, text + textlen + done_del, + mch_memmove(&prop_this, text + textlen + done_this, sizeof(textprop_T)); if ((prop_this.tp_flags & flag) && prop_del.tp_id == prop_this.tp_id @@ -3577,7 +3578,7 @@ adjust_text_props_for_delete( { found = TRUE; prop_this.tp_flags &= ~flag; - mch_memmove(text + textlen + done_del, &prop_this, + mch_memmove(text + textlen + done_this, &prop_this, sizeof(textprop_T)); break; } diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim index 8e12041427..3203305e8c 100644 --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -1645,6 +1645,57 @@ def Test_prop_add_delete_line() bwipe! enddef +" This test is to detect a regression related to #10430. It is not an attempt +" fully cover deleting lines in the presence of multi-line properties. +def Test_delete_line_within_multiline_prop() + new + setline(1, '# Top.') + append(1, ['some_text = """', 'A string.', '"""', '# Bottom.']) + prop_type_add('Identifier', {'highlight': 'ModeMsg', 'priority': 0, 'combine': 0, 'start_incl': 0, 'end_incl': 0}) + prop_type_add('String', {'highlight': 'MoreMsg', 'priority': 0, 'combine': 0, 'start_incl': 0, 'end_incl': 0}) + prop_add(2, 1, {'type': 'Identifier', 'end_lnum': 2, 'end_col': 9}) + prop_add(2, 13, {'type': 'String', 'end_lnum': 4, 'end_col': 4}) + + # The property for line 3 should extend into the previous and next lines. + var props = prop_list(3) + var prop = props[0] + assert_equal(1, len(props)) + assert_equal(0, prop['start']) + assert_equal(0, prop['end']) + + # This deletion should run without raising an exception. + try + :2 del + catch + assert_report('Line delete should have workd, but it raised an error.') + endtry + + # The property for line 2 (was 3) should no longer extend into the previous + # line. + props = prop_list(2) + prop = props[0] + assert_equal(1, len(props)) + assert_equal(1, prop['start'], 'Property was not changed to start within the line.') + + # This deletion should run without raising an exception. + try + :3 del + catch + assert_report('Line delete should have workd, but it raised an error.') + endtry + + # The property for line 2 (originally 3) should no longer extend into the next + # line. + props = prop_list(2) + prop = props[0] + assert_equal(1, len(props)) + assert_equal(1, prop['end'], 'Property was not changed to end within the line.') + + prop_type_delete('Identifier') + prop_type_delete('String') + bwip! +enddef + func Test_prop_in_linebreak() CheckRunVimInTerminal diff --git a/src/version.c b/src/version.c index dca2f92092..52144c56c5 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4960, /**/ 4959, /**/