# HG changeset patch # User Bram Moolenaar # Date 1661025603 -7200 # Node ID d08aa1bfe31978dbbf948c00e48fa77ed29c6a70 # Parent f3901ba52a12be11caced12483f578e7eb678595 patch 9.0.0233: removing multiple text properties takes many calls Commit: https://github.com/vim/vim/commit/a7704226a26b95b15bf87d3a3a5128e23e4aaa06 Author: Ben Jackson Date: Sat Aug 20 20:54:51 2022 +0100 patch 9.0.0233: removing multiple text properties takes many calls Problem: Removing multiple text properties takes many calls. Solution: Pass a list to prop_remove(). (Ben Jackson, closes https://github.com/vim/vim/issues/10945) diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -2208,8 +2208,8 @@ EXTERN char e_failed_to_convert_returned INIT(= N_("E859: Failed to convert returned python object to a Vim value")); #endif #ifdef FEAT_PROP_POPUP -EXTERN char e_need_id_and_type_with_both[] - INIT(= N_("E860: Need 'id' and 'type' with 'both'")); +EXTERN char e_need_id_and_type_or_types_with_both[] + INIT(= N_("E860: Need 'id' and 'type' or 'types' with 'both'")); # ifdef FEAT_TERMINAL EXTERN char e_cannot_open_second_popup_with_terminal[] INIT(= N_("E861: Cannot open a second popup with a terminal")); @@ -3316,3 +3316,7 @@ EXTERN char e_cannot_use_negative_id_aft EXTERN char e_can_only_use_text_align_when_column_is_zero[] INIT(= N_("E1294: Can only use text_align when column is zero")); #endif +#ifdef FEAT_PROP_POPUP +EXTERN char e_cannot_specify_both_type_and_types[] + INIT(= N_("E1295: Cannot specify both 'type' and 'types'")); +#endif 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 @@ -161,7 +161,7 @@ func Test_prop_find() \ ] " Starting at line 5 col 1 this should find the prop at line 5 col 4. - call cursor(5,1) + call cursor(5, 1) let result = prop_find({'type': 'prop_name'}, 'f') call assert_equal(expected[2], result) @@ -182,7 +182,7 @@ func Test_prop_find() " with skipstart set to false, if the start position is anywhere between the " start and end lines of a text prop (searching forward or backward), the " result should be the prop on the first line (the line with 'start' set to 1). - call cursor(3,1) + call cursor(3, 1) let result = prop_find({'type': 'prop_name'}, 'f') unlet result.length call assert_equal(expected[1], result) @@ -230,12 +230,12 @@ func Test_prop_find() endwhile " Starting from line 6 col 1 search backwards for prop with id 10. - call cursor(6,1) + call cursor(6, 1) let result = prop_find({'id': 10, 'skipstart': 1}, 'b') call assert_equal(expected[0], result) " Starting from line 1 col 1 search forwards for prop with id 12. - call cursor(1,1) + call cursor(1, 1) let result = prop_find({'id': 12}, 'f') call assert_equal(expected[2], result) @@ -426,6 +426,37 @@ func Test_prop_remove() call DeletePropTypes() bwipe! + + new + call AddPropTypes() + call SetupPropsInFirstLine() + let props = Get_expected_props() " [whole, one, two, three] + call assert_equal(props, prop_list(1)) + + " remove one by types + call assert_equal(1, prop_remove({'types': ['one', 'two', 'three']}, 1)) + unlet props[1] " [whole, two, three] + call assert_equal(props, prop_list(1)) + + " remove 'all' by types + call assert_equal(2, prop_remove({'types': ['three', 'whole'], 'all': 1}, 1)) + unlet props[0] " [two, three] + unlet props[1] " [three] + call assert_equal(props, prop_list(1)) + + " remove none by types + call assert_equal(0, prop_remove({'types': ['three', 'whole'], 'all': 1}, 1)) + call assert_equal(props, prop_list(1)) + + " no types + call assert_fails("call prop_remove({'types': []}, 1)", 'E968:') + call assert_fails("call prop_remove({'types': ['not_a_real_type']}, 1)", 'E971:') + + " only one of types and type can be supplied + call assert_fails("call prop_remove({'type': 'one', 'types': ['three'], 'all': 1}, 1)", 'E1295:') + + call DeletePropTypes() + bwipe! endfunc def Test_prop_add_vim9() @@ -1396,7 +1427,7 @@ endfunc func Test_textprop_invalid_highlight() call assert_fails("call prop_type_add('dni', {'highlight': 'DoesNotExist'})", 'E970:') new - call setline(1, ['asdf','asdf']) + call setline(1, ['asdf', 'asdf']) call prop_add(1, 1, {'length': 4, 'type': 'dni'}) redraw bwipe! @@ -2207,7 +2238,7 @@ func Test_prop_find_prev_on_same_line() call prop_add(1, col, #{type: 'misspell', length: 2}) endfor - call cursor(1,18) + call cursor(1, 18) let expected = [ \ #{lnum: 1, id: 0, col: 14, end: 1, type: 'misspell', type_bufnr: 0, length: 2, start: 1}, \ #{lnum: 1, id: 0, col: 24, end: 1, type: 'misspell', type_bufnr: 0, length: 2, start: 1} @@ -2310,7 +2341,7 @@ func Test_prop_insert_multiline() call assert_equal(lines, getline(1, '$')) let expected = [ \ {'lnum': 1, 'id': 0, 'col': 4, 'type_bufnr': 0, 'end': 0, 'type': 'one', - \ 'length': 4 ,'start': 1}, + \ 'length': 4 , 'start': 1}, \ {'lnum': 2, 'id': 0, 'col': 1, 'type_bufnr': 0, 'end': 0, 'type': 'one', \ 'length': 7, 'start': 0}, \ {'lnum': 3, 'id': 0, 'col': 1, 'type_bufnr': 0, 'end': 0, 'type': 'one', diff --git a/src/textprop.c b/src/textprop.c --- a/src/textprop.c +++ b/src/textprop.c @@ -1010,7 +1010,7 @@ f_prop_find(typval_T *argvars, typval_T } if (both && (!id_found || type_id == -1)) { - emsg(_(e_need_id_and_type_with_both)); + emsg(_(e_need_id_and_type_or_types_with_both)); return; } @@ -1378,7 +1378,9 @@ f_prop_remove(typval_T *argvars, typval_ buf_T *buf = curbuf; int do_all; int id = -MAXCOL; - int type_id = -1; + int type_id = -1; // for a single "type" + int *type_ids = NULL; // array, for a list of "types", allocated + int num_type_ids = 0; // number of elements in "type_ids" int both; int did_remove_text = FALSE; @@ -1420,6 +1422,9 @@ f_prop_remove(typval_T *argvars, typval_ if (dict_has_key(dict, "id")) id = dict_get_number(dict, "id"); + + // if a specific type was supplied "type": check that (and ignore "types". + // Otherwise check against the list of "types". if (dict_has_key(dict, "type")) { char_u *name = dict_get_string(dict, "type", FALSE); @@ -1429,17 +1434,48 @@ f_prop_remove(typval_T *argvars, typval_ return; type_id = type->pt_id; } + if (dict_has_key(dict, "types")) + { + typval_T types; + listitem_T *li = NULL; + + dict_get_tv(dict, "types", &types); + if (types.v_type == VAR_LIST && types.vval.v_list->lv_len > 0) + { + type_ids = alloc( sizeof(int) * types.vval.v_list->lv_len ); + + FOR_ALL_LIST_ITEMS(types.vval.v_list, li) + { + proptype_T *prop_type; + + if (li->li_tv.v_type != VAR_STRING) + continue; + + prop_type = lookup_prop_type(li->li_tv.vval.v_string, buf); + + if (!prop_type) + goto cleanup_prop_remove; + + type_ids[num_type_ids++] = prop_type->pt_id; + } + } + } both = dict_get_bool(dict, "both", FALSE); - if (id == -MAXCOL && type_id == -1) + if (id == -MAXCOL && (type_id == -1 && num_type_ids == 0)) { emsg(_(e_need_at_least_one_of_id_or_type)); - return; + goto cleanup_prop_remove; } - if (both && (id == -MAXCOL || type_id == -1)) + if (both && (id == -MAXCOL || (type_id == -1 && num_type_ids == 0))) { - emsg(_(e_need_id_and_type_with_both)); - return; + emsg(_(e_need_id_and_type_or_types_with_both)); + goto cleanup_prop_remove; + } + if (type_id != -1 && num_type_ids > 0) + { + emsg(_(e_cannot_specify_both_type_and_types)); + goto cleanup_prop_remove; } if (end == 0) @@ -1464,10 +1500,26 @@ f_prop_remove(typval_T *argvars, typval_ char_u *cur_prop = buf->b_ml.ml_line_ptr + len + idx * sizeof(textprop_T); size_t taillen; + int matches_id = 0; + int matches_type = 0; mch_memmove(&textprop, cur_prop, sizeof(textprop_T)); - if (both ? textprop.tp_id == id && textprop.tp_type == type_id - : textprop.tp_id == id || textprop.tp_type == type_id) + + matches_id = textprop.tp_id == id; + if (num_type_ids > 0) + { + int idx2; + + for (idx2 = 0; !matches_type && idx2 < num_type_ids; ++idx2) + matches_type = textprop.tp_type == type_ids[idx2]; + } + else + { + matches_type = textprop.tp_type == type_id; + } + + if (both ? matches_id && matches_type + : matches_id || matches_type) { if (!(buf->b_ml.ml_flags & ML_LINE_DIRTY)) { @@ -1475,7 +1527,7 @@ f_prop_remove(typval_T *argvars, typval_ // need to allocate the line to be able to change it if (newptr == NULL) - return; + goto cleanup_prop_remove; mch_memmove(newptr, buf->b_ml.ml_line_ptr, buf->b_ml.ml_line_len); if (buf->b_ml.ml_flags & ML_ALLOCATED) @@ -1537,6 +1589,9 @@ f_prop_remove(typval_T *argvars, typval_ && ((char_u **)gap->ga_data)[gap->ga_len - 1] == NULL) --gap->ga_len; } + +cleanup_prop_remove: + vim_free(type_ids); } /* diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -732,6 +732,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 233, +/**/ 232, /**/ 231,