To: vim_dev@googlegroups.com Subject: Patch 8.2.3356 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3356 Problem: Adding many text properties requires a lot of function calls. Solution: Add the prop_add_list() function. (Yegappan Lakshmanan, closes #8751) Files: runtime/doc/eval.txt, runtime/doc/textprop.txt, runtime/doc/usr_41.txt, src/evalfunc.c, src/proto/textprop.pro, src/testdir/test_textprop.vim, src/testdir/test_vim9_builtin.vim, src/textprop.c *** ../vim-8.2.3355/runtime/doc/eval.txt 2021-08-10 10:23:22.280476685 +0200 --- runtime/doc/eval.txt 2021-08-16 21:33:59.131967420 +0200 *************** *** 2784,2790 **** prompt_setcallback({buf}, {expr}) none set prompt callback function prompt_setinterrupt({buf}, {text}) none set prompt interrupt function prompt_setprompt({buf}, {text}) none set prompt text ! prop_add({lnum}, {col}, {props}) none add a text property prop_clear({lnum} [, {lnum-end} [, {props}]]) none remove all text properties prop_find({props} [, {direction}]) --- 2801,2809 ---- prompt_setcallback({buf}, {expr}) none set prompt callback function prompt_setinterrupt({buf}, {text}) none set prompt interrupt function prompt_setprompt({buf}, {text}) none set prompt text ! prop_add({lnum}, {col}, {props}) none add one text property ! prop_add_list({props}, [[{lnum}, {col}, {end-lnum}, {end-col}], ...]) ! none add multiple text properties prop_clear({lnum} [, {lnum-end} [, {props}]]) none remove all text properties prop_find({props} [, {direction}]) *** ../vim-8.2.3355/runtime/doc/textprop.txt 2021-07-28 13:30:12.208929917 +0200 --- runtime/doc/textprop.txt 2021-08-16 21:34:57.767789811 +0200 *************** *** 108,113 **** --- 108,116 ---- Manipulating text properties: prop_add({lnum}, {col}, {props}) add a text property + prop_add_list({props}, [[{lnum}, {col}, {end-lnum}, {end-col}], ...]) + add a text property at multiple + positions. prop_clear({lnum} [, {lnum-end} [, {bufnr}]]) remove all text properties prop_find({props} [, {direction}]) search for a text property *************** *** 126,132 **** length length of text in bytes, can only be used for a property that does not continue in another line; can be zero ! end_lnum line number for the end of text end_col column just after the text; not used when "length" is present; when {col} and "end_col" are equal, and "end_lnum" is omitted or equal --- 129,135 ---- length length of text in bytes, can only be used for a property that does not continue in another line; can be zero ! end_lnum line number for the end of text (inclusive) end_col column just after the text; not used when "length" is present; when {col} and "end_col" are equal, and "end_lnum" is omitted or equal *************** *** 158,163 **** --- 161,195 ---- Can also be used as a |method|: > GetLnum()->prop_add(col, props) + *prop_add_list()* + prop_add_list({props}, [[{lnum}, {col}, {end-lnum}, {end-col}], ...]) + Similar to prop_add(), but attaches a text property at + multiple positions in a buffer. + + {props} is a dictionary with these fields: + bufnr buffer to add the property to; when omitted + the current buffer is used + id user defined ID for the property; must be a + number; when omitted zero is used + type name of the text property type + All fields except "type" are optional. + + The second argument is a List of Lists where each list + specifies the starting and ending position of the text. The + first two items {lnum} and {col} specify the starting position + of the text where the property will be attached and the last + two items {end-lnum} and {end-col} specify the position just + after the text. + + Example: + call prop_add_list(#{type: 'MyProp', id: 2}, + \ [[1, 4, 1, 7], + \ [1, 15, 1, 20], + \ [2, 30, 3, 30]] + + Can also be used as a |method|: > + GetProp()->prop_add_list([[1, 1, 1, 2], [1, 4, 1, 8]]) + prop_clear({lnum} [, {lnum-end} [, {props}]]) *prop_clear()* Remove all text properties from line {lnum}. *** ../vim-8.2.3355/runtime/doc/usr_41.txt 2021-08-08 14:41:48.719930705 +0200 --- runtime/doc/usr_41.txt 2021-08-16 21:28:45.284918529 +0200 *************** *** 1153,1158 **** --- 1161,1167 ---- Text Properties: *text-property-functions* prop_add() attach a property at a position + prop_add_list() attach a property at multiple positions prop_clear() remove all properties from a line or lines prop_find() search for a property prop_list() return a list of all properties in a line *** ../vim-8.2.3355/src/evalfunc.c 2021-08-13 17:48:19.178894940 +0200 --- src/evalfunc.c 2021-08-16 21:28:45.288918517 +0200 *************** *** 708,713 **** --- 708,714 ---- static argcheck_T arg2_buffer_string[] = {arg_buffer, arg_string}; static argcheck_T arg2_chan_or_job_dict[] = {arg_chan_or_job, arg_dict_any}; static argcheck_T arg2_chan_or_job_string[] = {arg_chan_or_job, arg_string}; + static argcheck_T arg2_dict_any_list_any[] = {arg_dict_any, arg_list_any}; static argcheck_T arg2_dict_any_string_or_nr[] = {arg_dict_any, arg_string_or_nr}; static argcheck_T arg2_dict_string[] = {arg_dict_any, arg_string}; static argcheck_T arg2_float_or_nr[] = {arg_float_or_nr, arg_float_or_nr}; *************** *** 1740,1745 **** --- 1741,1748 ---- ret_void, JOB_FUNC(f_prompt_setprompt)}, {"prop_add", 3, 3, FEARG_1, arg3_number_number_dict, ret_void, PROP_FUNC(f_prop_add)}, + {"prop_add_list", 2, 2, FEARG_1, arg2_dict_any_list_any, + ret_void, PROP_FUNC(f_prop_add_list)}, {"prop_clear", 1, 3, FEARG_1, arg3_number_number_dict, ret_void, PROP_FUNC(f_prop_clear)}, {"prop_find", 1, 2, FEARG_1, arg2_dict_string, *** ../vim-8.2.3355/src/proto/textprop.pro 2020-05-30 15:31:57.858700863 +0200 --- src/proto/textprop.pro 2021-08-16 21:28:45.288918517 +0200 *************** *** 1,6 **** --- 1,7 ---- /* textprop.c */ int find_prop_type_id(char_u *name, buf_T *buf); void f_prop_add(typval_T *argvars, typval_T *rettv); + void f_prop_add_list(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); *** ../vim-8.2.3355/src/testdir/test_textprop.vim 2021-08-15 15:04:37.188080291 +0200 --- src/testdir/test_textprop.vim 2021-08-16 21:28:45.288918517 +0200 *************** *** 339,344 **** --- 339,379 ---- bwipe! endfunc + " Test for the prop_add_list() function + func Test_prop_add_list() + new + call AddPropTypes() + call setline(1, ['one one one', 'two two two', 'six six six', 'ten ten ten']) + call prop_add_list(#{type: 'one', id: 2}, + \ [[1, 1, 1, 3], [2, 5, 2, 7], [3, 6, 4, 6]]) + call assert_equal([#{id: 2, col: 1, type_bufnr: 0, end: 1, type: 'one', + \ length: 2, start: 1}], prop_list(1)) + call assert_equal([#{id: 2, col: 5, type_bufnr: 0, end: 1, type: 'one', + \ length: 2, start: 1}], prop_list(2)) + call assert_equal([#{id: 2, col: 6, type_bufnr: 0, end: 0, type: 'one', + \ length: 7, start: 1}], prop_list(3)) + call assert_equal([#{id: 2, col: 1, type_bufnr: 0, end: 1, type: 'one', + \ length: 5, start: 0}], prop_list(4)) + call assert_fails('call prop_add_list([1, 2], [[1, 1, 3]])', 'E1206:') + call assert_fails('call prop_add_list({}, {})', 'E1211:') + call assert_fails('call prop_add_list({}, [[1, 1, 3]])', 'E965:') + call assert_fails('call prop_add_list(#{type: "abc"}, [[1, 1, 1, 3]])', 'E971:') + call assert_fails('call prop_add_list(#{type: "one"}, [[]])', 'E474:') + call assert_fails('call prop_add_list(#{type: "one"}, [[1, 1, 1, 1], {}])', 'E714:') + call assert_fails('call prop_add_list(#{type: "one"}, [[1, 1, "a"]])', 'E474:') + call assert_fails('call prop_add_list(#{type: "one"}, [[2, 2]])', 'E474:') + call assert_fails('call prop_add_list(#{type: "one"}, [[1, 1, 2], [2, 2]])', 'E474:') + call assert_fails('call prop_add_list(#{type: "one"}, [[1, 1, 1, 2], [4, 1, 5, 2]])', 'E966:') + call assert_fails('call prop_add_list(#{type: "one"}, [[3, 1, 1, 2]])', 'E966:') + call assert_fails('call prop_add_list(#{type: "one"}, [[2, 2, 2, 2], [3, 20, 3, 22]])', 'E964:') + call assert_fails('eval #{type: "one"}->prop_add_list([[2, 2, 2, 2], [3, 20, 3, 22]])', 'E964:') + call assert_fails('call prop_add_list(test_null_dict(), [[2, 2, 2]])', 'E965:') + call assert_fails('call prop_add_list(#{type: "one"}, test_null_list())', 'E714:') + call assert_fails('call prop_add_list(#{type: "one"}, [test_null_list()])', 'E714:') + call DeletePropTypes() + bw! + endfunc + func Test_prop_remove() new call AddPropTypes() *** ../vim-8.2.3355/src/testdir/test_vim9_builtin.vim 2021-08-09 22:22:23.164468820 +0200 --- src/testdir/test_vim9_builtin.vim 2021-08-16 21:28:45.288918517 +0200 *************** *** 2364,2369 **** --- 2364,2374 ---- CheckDefAndScriptFailure2(['prop_add(1, 2, [])'], 'E1013: Argument 3: type mismatch, expected dict but got list', 'E1206: Dictionary required for argument 3') enddef + def Test_prop_add_list() + CheckDefAndScriptFailure2(['prop_add_list([], [])'], 'E1013: Argument 1: type mismatch, expected dict but got list', 'E1206: Dictionary required for argument 1') + CheckDefAndScriptFailure2(['prop_add_list({}, {})'], 'E1013: Argument 2: type mismatch, expected list but got dict', 'E1211: List required for argument 2') + enddef + def Test_prop_clear() CheckDefAndScriptFailure2(['prop_clear("a")'], 'E1013: Argument 1: type mismatch, expected number but got string', 'E1210: Number required for argument 1') CheckDefAndScriptFailure2(['prop_clear(1, "b")'], 'E1013: Argument 2: type mismatch, expected number but got string', 'E1210: Number required for argument 2') *** ../vim-8.2.3355/src/textprop.c 2021-08-01 21:30:08.893199555 +0200 --- src/textprop.c 2021-08-16 21:28:45.288918517 +0200 *************** *** 183,284 **** } /* ! * Shared between prop_add() and popup_create(). ! * "dict_arg" is the function argument of a dict containing "bufnr". ! * it is NULL for popup_create(). */ ! void ! prop_add_common( ! linenr_T start_lnum, ! colnr_T start_col, ! dict_T *dict, ! buf_T *default_buf, ! typval_T *dict_arg) { - linenr_T lnum; - linenr_T end_lnum; - colnr_T end_col; - char_u *type_name; proptype_T *type; ! buf_T *buf = default_buf; ! int id = 0; ! char_u *newtext; int proplen; - size_t textlen; char_u *props = NULL; char_u *newprops; ! textprop_T tmp_prop; int i; ! ! if (dict == NULL || dict_find(dict, (char_u *)"type", -1) == NULL) ! { ! emsg(_("E965: missing property type name")); ! return; ! } ! type_name = dict_get_string(dict, (char_u *)"type", FALSE); ! ! if (dict_find(dict, (char_u *)"end_lnum", -1) != NULL) ! { ! end_lnum = dict_get_number(dict, (char_u *)"end_lnum"); ! if (end_lnum < start_lnum) ! { ! semsg(_(e_invargval), "end_lnum"); ! return; ! } ! } ! else ! end_lnum = start_lnum; ! ! if (dict_find(dict, (char_u *)"length", -1) != NULL) ! { ! long length = dict_get_number(dict, (char_u *)"length"); ! ! if (length < 0 || end_lnum > start_lnum) ! { ! semsg(_(e_invargval), "length"); ! return; ! } ! end_col = start_col + length; ! } ! else if (dict_find(dict, (char_u *)"end_col", -1) != NULL) ! { ! end_col = dict_get_number(dict, (char_u *)"end_col"); ! if (end_col <= 0) ! { ! semsg(_(e_invargval), "end_col"); ! return; ! } ! } ! else if (start_lnum == end_lnum) ! end_col = start_col; ! else ! end_col = 1; ! ! if (dict_find(dict, (char_u *)"id", -1) != NULL) ! id = dict_get_number(dict, (char_u *)"id"); ! ! if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL) ! return; type = lookup_prop_type(type_name, buf); if (type == NULL) ! return; if (start_lnum < 1 || start_lnum > buf->b_ml.ml_line_count) { semsg(_(e_invalid_lnum), (long)start_lnum); ! return; } if (end_lnum < start_lnum || end_lnum > buf->b_ml.ml_line_count) { semsg(_(e_invalid_lnum), (long)end_lnum); ! return; } if (buf->b_ml.ml_mfp == NULL) { emsg(_("E275: Cannot add text property to unloaded buffer")); ! return; } for (lnum = start_lnum; lnum <= end_lnum; ++lnum) --- 183,231 ---- } /* ! * Attach a text property 'type_name' to the text starting ! * at [start_lnum, start_col] and ending at [end_lnum, end_col] in ! * the buffer 'buf' and assign identifier 'id'. */ ! static int ! prop_add_one( ! buf_T *buf, ! char_u *type_name, ! int id, ! linenr_T start_lnum, ! linenr_T end_lnum, ! colnr_T start_col, ! colnr_T end_col) { proptype_T *type; ! linenr_T lnum; int proplen; char_u *props = NULL; char_u *newprops; ! size_t textlen; ! char_u *newtext; int i; ! textprop_T tmp_prop; type = lookup_prop_type(type_name, buf); if (type == NULL) ! return FAIL; if (start_lnum < 1 || start_lnum > buf->b_ml.ml_line_count) { semsg(_(e_invalid_lnum), (long)start_lnum); ! return FAIL; } if (end_lnum < start_lnum || end_lnum > buf->b_ml.ml_line_count) { semsg(_(e_invalid_lnum), (long)end_lnum); ! return FAIL; } if (buf->b_ml.ml_mfp == NULL) { emsg(_("E275: Cannot add text property to unloaded buffer")); ! return FAIL; } for (lnum = start_lnum; lnum <= end_lnum; ++lnum) *************** *** 297,303 **** if (col - 1 > (colnr_T)textlen) { semsg(_(e_invalid_col), (long)start_col); ! return; } if (lnum == end_lnum) --- 244,250 ---- if (col - 1 > (colnr_T)textlen) { semsg(_(e_invalid_col), (long)start_col); ! return FAIL; } if (lnum == end_lnum) *************** *** 312,318 **** // Allocate the new line with space for the new property. newtext = alloc(buf->b_ml.ml_line_len + sizeof(textprop_T)); if (newtext == NULL) ! return; // Copy the text, including terminating NUL. mch_memmove(newtext, buf->b_ml.ml_line_ptr, textlen); --- 259,265 ---- // Allocate the new line with space for the new property. newtext = alloc(buf->b_ml.ml_line_len + sizeof(textprop_T)); if (newtext == NULL) ! return FAIL; // Copy the text, including terminating NUL. mch_memmove(newtext, buf->b_ml.ml_line_ptr, textlen); *************** *** 351,358 **** buf->b_ml.ml_flags |= ML_LINE_DIRTY; } - buf->b_has_textprop = TRUE; // this is never reset changed_lines_buf(buf, start_lnum, end_lnum + 1, 0); redraw_buf_later(buf, VALID); } --- 298,453 ---- buf->b_ml.ml_flags |= ML_LINE_DIRTY; } changed_lines_buf(buf, start_lnum, end_lnum + 1, 0); + return OK; + } + + /* + * prop_add_list() + * First argument specifies the text property: + * {'type': , 'id': , 'bufnr': } + * Second argument is a List where each item is a List with the following + * entries: [lnum, start_col, end_col] + */ + void + f_prop_add_list(typval_T *argvars, typval_T *rettv UNUSED) + { + dict_T *dict; + char_u *type_name; + buf_T *buf = curbuf; + int id = 0; + listitem_T *li; + list_T *pos_list; + linenr_T start_lnum; + colnr_T start_col; + linenr_T end_lnum; + colnr_T end_col; + int error = FALSE; + + if (check_for_dict_arg(argvars, 0) == FAIL + || check_for_list_arg(argvars, 1) == FAIL) + return; + + if (argvars[1].vval.v_list == NULL) + { + emsg(_(e_listreq)); + return; + } + + dict = argvars[0].vval.v_dict; + if (dict == NULL || dict_find(dict, (char_u *)"type", -1) == NULL) + { + emsg(_("E965: missing property type name")); + return; + } + type_name = dict_get_string(dict, (char_u *)"type", FALSE); + + if (dict_find(dict, (char_u *)"id", -1) != NULL) + id = dict_get_number(dict, (char_u *)"id"); + + if (get_bufnr_from_arg(&argvars[0], &buf) == FAIL) + return; + + FOR_ALL_LIST_ITEMS(argvars[1].vval.v_list, li) + { + if (li->li_tv.v_type != VAR_LIST || li->li_tv.vval.v_list == NULL) + { + emsg(_(e_listreq)); + return; + } + + pos_list = li->li_tv.vval.v_list; + start_lnum = list_find_nr(pos_list, 0L, &error); + start_col = list_find_nr(pos_list, 1L, &error); + end_lnum = list_find_nr(pos_list, 2L, &error); + end_col = list_find_nr(pos_list, 3L, &error); + if (error || start_lnum <= 0 || start_col <= 0 + || end_lnum <= 0 || end_col <= 0) + { + emsg(_(e_invarg)); + return; + } + if (prop_add_one(buf, type_name, id, start_lnum, end_lnum, + start_col, end_col) == FAIL) + return; + } + + buf->b_has_textprop = TRUE; // this is never reset + redraw_buf_later(buf, VALID); + } + + /* + * Shared between prop_add() and popup_create(). + * "dict_arg" is the function argument of a dict containing "bufnr". + * it is NULL for popup_create(). + */ + void + prop_add_common( + linenr_T start_lnum, + colnr_T start_col, + dict_T *dict, + buf_T *default_buf, + typval_T *dict_arg) + { + linenr_T end_lnum; + colnr_T end_col; + char_u *type_name; + buf_T *buf = default_buf; + int id = 0; + + if (dict == NULL || dict_find(dict, (char_u *)"type", -1) == NULL) + { + emsg(_("E965: missing property type name")); + return; + } + type_name = dict_get_string(dict, (char_u *)"type", FALSE); + + if (dict_find(dict, (char_u *)"end_lnum", -1) != NULL) + { + end_lnum = dict_get_number(dict, (char_u *)"end_lnum"); + if (end_lnum < start_lnum) + { + semsg(_(e_invargval), "end_lnum"); + return; + } + } + else + end_lnum = start_lnum; + + if (dict_find(dict, (char_u *)"length", -1) != NULL) + { + long length = dict_get_number(dict, (char_u *)"length"); + + if (length < 0 || end_lnum > start_lnum) + { + semsg(_(e_invargval), "length"); + return; + } + end_col = start_col + length; + } + else if (dict_find(dict, (char_u *)"end_col", -1) != NULL) + { + end_col = dict_get_number(dict, (char_u *)"end_col"); + if (end_col <= 0) + { + semsg(_(e_invargval), "end_col"); + return; + } + } + else if (start_lnum == end_lnum) + end_col = start_col; + else + end_col = 1; + + if (dict_find(dict, (char_u *)"id", -1) != NULL) + id = dict_get_number(dict, (char_u *)"id"); + + if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL) + return; + + prop_add_one(buf, type_name, id, start_lnum, end_lnum, start_col, end_col); + + buf->b_has_textprop = TRUE; // this is never reset redraw_buf_later(buf, VALID); } *** ../vim-8.2.3355/src/version.c 2021-08-16 21:15:28.215345122 +0200 --- src/version.c 2021-08-16 21:36:23.647529755 +0200 *************** *** 757,758 **** --- 757,760 ---- { /* Add new patch number below this line */ + /**/ + 3356, /**/ -- Q: Why does /dev/null accept only integers? A: You can't sink a float. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///