To: vim_dev@googlegroups.com Subject: Patch 9.0.0202 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0202 Problem: Code and help for indexof() is not ideal. Solution: Refactor the code, improve the help. (Yegappan Lakshmanan, closes #10908) Files: runtime/doc/builtin.txt, src/evalfunc.c, src/testdir/test_vim9_builtin.vim *** ../vim-9.0.0201/runtime/doc/builtin.txt 2022-08-13 13:08:30.282914740 +0100 --- runtime/doc/builtin.txt 2022-08-13 21:28:47.863793349 +0100 *************** *** 4727,4733 **** index({object}, {expr} [, {start} [, {ic}]]) *index()* Find {expr} in {object} and return its index. See ! |filterof()| for using a lambda to select the item. If {object} is a |List| return the lowest index where the item has a value equal to {expr}. There is no automatic --- 4733,4739 ---- index({object}, {expr} [, {start} [, {ic}]]) *index()* Find {expr} in {object} and return its index. See ! |indexof()| for using a lambda to select the item. If {object} is a |List| return the lowest index where the item has a value equal to {expr}. There is no automatic *************** *** 4753,4767 **** < Can also be used as a |method|: > GetObject()->index(what) ! indexof({object}, {expr} [, {opt}]) *indexof()* ! {object} must be a |List| or a |Blob|. If {object} is a |List|, evaluate {expr} for each item in the ! List until the expression returns v:true and return the index ! of this item. If {object} is a |Blob| evaluate {expr} for each byte in the ! Blob until the expression returns v:true and return the index ! of this byte. {expr} must be a |string| or |Funcref|. --- 4759,4775 ---- < Can also be used as a |method|: > GetObject()->index(what) ! indexof({object}, {expr} [, {opts}]) *indexof()* ! Returns the index of an item in {object} where {expr} is ! v:true. {object} must be a |List| or a |Blob|. ! If {object} is a |List|, evaluate {expr} for each item in the ! List until the expression is v:true and return the index of ! this item. If {object} is a |Blob| evaluate {expr} for each byte in the ! Blob until the expression is v:true and return the index of ! this byte. {expr} must be a |string| or |Funcref|. *************** *** 4777,4792 **** The function must return |TRUE| if the item is found and the search should stop. ! The optional argument {opt} is a Dict and supports the following items: ! start start evaluating {expr} at the item with index ! {start} (may be negative for an item relative ! to the end). Returns -1 when {expr} evaluates to v:false for all the items. Example: > ! :let l = [#{n: 10}, #{n: 20}, #{n: 30]] ! :let idx = indexof(l, "v:val.n == 20") ! :let idx = indexof(l, {i, v -> v.n == 30}) < Can also be used as a |method|: > mylist->indexof(expr) --- 4785,4801 ---- The function must return |TRUE| if the item is found and the search should stop. ! The optional argument {opts} is a Dict and supports the following items: ! startidx start evaluating {expr} at the item with this ! index; may be negative for an item relative to ! the end Returns -1 when {expr} evaluates to v:false for all the items. Example: > ! :let l = [#{n: 10}, #{n: 20}, #{n: 30}] ! :echo indexof(l, "v:val.n == 20") ! :echo indexof(l, {i, v -> v.n == 30}) ! :echo indexof(l, "v:val.n == 20", #{startidx: 1}) < Can also be used as a |method|: > mylist->indexof(expr) *************** *** 5815,5822 **** message will appear and the match will not be added. An ID is specified as a positive integer (zero excluded). IDs 1, 2 and 3 are reserved for |:match|, |:2match| and |:3match|, ! respectively. If the {id} argument is not specified or -1, ! |matchadd()| automatically chooses a free ID. The optional {dict} argument allows for further custom values. Currently this is used to specify a match specific --- 5824,5833 ---- message will appear and the match will not be added. An ID is specified as a positive integer (zero excluded). IDs 1, 2 and 3 are reserved for |:match|, |:2match| and |:3match|, ! respectively. 3 is reserved for use by the |matchparen| ! plugin. ! If the {id} argument is not specified or -1, |matchadd()| ! automatically chooses a free ID. The optional {dict} argument allows for further custom values. Currently this is used to specify a match specific *** ../vim-9.0.0201/src/evalfunc.c 2022-08-13 13:08:30.286914784 +0100 --- src/evalfunc.c 2022-08-13 21:18:55.028253903 +0100 *************** *** 6819,6834 **** } /* * "indexof()" function */ static void f_indexof(typval_T *argvars, typval_T *rettv) { - list_T *l; - listitem_T *item; - blob_T *b; long startidx = 0; - long idx = 0; typval_T save_val; typval_T save_key; int save_did_emsg; --- 6819,6907 ---- } /* + * Evaluate 'expr' for each byte in the Blob 'b' starting with the byte at + * 'startidx' and return the index of the byte where 'expr' is TRUE. Returns + * -1 if 'expr' doesn't evaluate to TRUE for any of the bytes. + */ + static int + indexof_blob(blob_T *b, long startidx, typval_T *expr) + { + long idx = 0; + + if (b == NULL) + return -1; + + if (startidx < 0) + { + // negative index: index from the last byte + startidx = blob_len(b) + startidx; + if (startidx < 0) + startidx = 0; + } + + set_vim_var_type(VV_KEY, VAR_NUMBER); + set_vim_var_type(VV_VAL, VAR_NUMBER); + + for (idx = startidx; idx < blob_len(b); ++idx) + { + set_vim_var_nr(VV_KEY, idx); + set_vim_var_nr(VV_VAL, blob_get(b, idx)); + + if (indexof_eval_expr(expr)) + return idx; + } + + return -1; + } + + /* + * Evaluate 'expr' for each item in the List 'l' starting with the item at + * 'startidx' and return the index of the item where 'expr' is TRUE. Returns + * -1 if 'expr' doesn't evaluate to TRUE for any of the items. + */ + static int + indexof_list(list_T *l, long startidx, typval_T *expr) + { + listitem_T *item; + long idx = 0; + + if (l == NULL) + return -1; + + CHECK_LIST_MATERIALIZE(l); + + if (startidx == 0) + item = l->lv_first; + else + { + // Start at specified item. Use the cached index that list_find() + // sets, so that a negative number also works. + item = list_find(l, startidx); + if (item != NULL) + idx = l->lv_u.mat.lv_idx; + } + + set_vim_var_type(VV_KEY, VAR_NUMBER); + + for ( ; item != NULL; item = item->li_next, ++idx) + { + set_vim_var_nr(VV_KEY, idx); + copy_tv(&item->li_tv, get_vim_var_tv(VV_VAL)); + + if (indexof_eval_expr(expr)) + return idx; + } + + return -1; + } + + /* * "indexof()" function */ static void f_indexof(typval_T *argvars, typval_T *rettv) { long startidx = 0; typval_T save_val; typval_T save_key; int save_did_emsg; *************** *** 6857,6923 **** did_emsg = FALSE; if (argvars[0].v_type == VAR_BLOB) ! { ! b = argvars[0].vval.v_blob; ! if (b == NULL) ! goto theend; ! if (startidx < 0) ! { ! startidx = blob_len(b) + startidx; ! if (startidx < 0) ! startidx = 0; ! } ! ! set_vim_var_type(VV_KEY, VAR_NUMBER); ! set_vim_var_type(VV_VAL, VAR_NUMBER); ! ! for (idx = startidx; idx < blob_len(b); ++idx) ! { ! set_vim_var_nr(VV_KEY, idx); ! set_vim_var_nr(VV_VAL, blob_get(b, idx)); ! ! if (indexof_eval_expr(&argvars[1])) ! { ! rettv->vval.v_number = idx; ! break; ! } ! } ! } else ! { ! l = argvars[0].vval.v_list; ! if (l == NULL) ! goto theend; ! ! CHECK_LIST_MATERIALIZE(l); ! ! if (startidx == 0) ! item = l->lv_first; ! else ! { ! // Start at specified item. Use the cached index that list_find() ! // sets, so that a negative number also works. ! item = list_find(l, startidx); ! if (item != NULL) ! idx = l->lv_u.mat.lv_idx; ! } ! ! set_vim_var_type(VV_KEY, VAR_NUMBER); ! ! for ( ; item != NULL; item = item->li_next, ++idx) ! { ! set_vim_var_nr(VV_KEY, idx); ! copy_tv(&item->li_tv, get_vim_var_tv(VV_VAL)); ! ! if (indexof_eval_expr(&argvars[1])) ! { ! rettv->vval.v_number = idx; ! break; ! } ! } ! } - theend: restore_vimvar(VV_KEY, &save_key); restore_vimvar(VV_VAL, &save_val); did_emsg |= save_did_emsg; --- 6930,6941 ---- did_emsg = FALSE; if (argvars[0].v_type == VAR_BLOB) ! rettv->vval.v_number = indexof_blob(argvars[0].vval.v_blob, startidx, ! &argvars[1]); else ! rettv->vval.v_number = indexof_list(argvars[0].vval.v_list, startidx, ! &argvars[1]); restore_vimvar(VV_KEY, &save_key); restore_vimvar(VV_VAL, &save_val); did_emsg |= save_did_emsg; *** ../vim-9.0.0201/src/testdir/test_vim9_builtin.vim 2022-08-13 13:08:30.286914784 +0100 --- src/testdir/test_vim9_builtin.vim 2022-08-13 21:31:43.527591969 +0100 *************** *** 2067,2076 **** enddef def Test_indexof() ! var l = [{color: 'red'}, {color: 'blue'}, {color: 'green'}] ! indexof(l, (i, v) => v.color == 'green')->assert_equal(2) var b = 0zdeadbeef indexof(b, "v:val == 0xef")->assert_equal(3) enddef def Test_input() --- 2067,2083 ---- enddef def Test_indexof() ! var l = [{color: 'red'}, {color: 'blue'}, {color: 'green'}, {color: 'blue'}] ! indexof(l, (i, v) => v.color == 'blue')->assert_equal(1) ! indexof(l, (i, v) => v.color == 'blue', {startidx: 1})->assert_equal(1) ! indexof(l, (i, v) => v.color == 'blue', {startidx: 2})->assert_equal(3) var b = 0zdeadbeef indexof(b, "v:val == 0xef")->assert_equal(3) + + def TestIdx(k: number, v: dict): bool + return v.color == 'blue' + enddef + indexof(l, TestIdx)->assert_equal(1) enddef def Test_input() *** ../vim-9.0.0201/src/version.c 2022-08-13 20:17:31.254884264 +0100 --- src/version.c 2022-08-13 21:25:14.344008189 +0100 *************** *** 737,738 **** --- 737,740 ---- { /* Add new patch number below this line */ + /**/ + 202, /**/ -- I have to exercise early in the morning before my brain figures out what I'm doing. /// 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 ///