To: vim_dev@googlegroups.com Subject: Patch 8.2.2318 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2318 Problem: Vim9: string and list index work differently. Solution: Make string index work like list index. (closes #7643) Files: src/eval.c, src/proto/eval.pro, src/vim9execute.c, src/list.c, src/proto/vim9execute.pro, src/testdir/test_vim9_expr.vim *** ../vim-8.2.2317/src/eval.c 2021-01-06 21:59:35.170021945 +0100 --- src/eval.c 2021-01-09 12:54:26.802568463 +0100 *************** *** 5549,5645 **** } /* - * Return the character "str[index]" where "index" is the character index. If - * "index" is out of range NULL is returned. - */ - char_u * - char_from_string(char_u *str, varnumber_T index) - { - size_t nbyte = 0; - varnumber_T nchar = index; - size_t slen; - - if (str == NULL || index < 0) - return NULL; - slen = STRLEN(str); - while (nchar > 0 && nbyte < slen) - { - nbyte += MB_CPTR2LEN(str + nbyte); - --nchar; - } - if (nbyte >= slen) - return NULL; - return vim_strnsave(str + nbyte, MB_CPTR2LEN(str + nbyte)); - } - - /* - * Get the byte index for character index "idx" in string "str" with length - * "str_len". - * If going over the end return "str_len". - * If "idx" is negative count from the end, -1 is the last character. - * When going over the start return -1. - */ - static long - char_idx2byte(char_u *str, size_t str_len, varnumber_T idx) - { - varnumber_T nchar = idx; - size_t nbyte = 0; - - if (nchar >= 0) - { - while (nchar > 0 && nbyte < str_len) - { - nbyte += MB_CPTR2LEN(str + nbyte); - --nchar; - } - } - else - { - nbyte = str_len; - while (nchar < 0 && nbyte > 0) - { - --nbyte; - nbyte -= mb_head_off(str, str + nbyte); - ++nchar; - } - if (nchar < 0) - return -1; - } - return (long)nbyte; - } - - /* - * Return the slice "str[first:last]" using character indexes. - * Return NULL when the result is empty. - */ - char_u * - string_slice(char_u *str, varnumber_T first, varnumber_T last) - { - long start_byte, end_byte; - size_t slen; - - if (str == NULL) - return NULL; - slen = STRLEN(str); - start_byte = char_idx2byte(str, slen, first); - if (start_byte < 0) - start_byte = 0; // first index very negative: use zero - if (last == -1) - end_byte = (long)slen; - else - { - end_byte = char_idx2byte(str, slen, last); - if (end_byte >= 0 && end_byte < (long)slen) - // end index is inclusive - end_byte += MB_CPTR2LEN(str + end_byte); - } - - if (start_byte >= (long)slen || end_byte <= start_byte) - return NULL; - return vim_strnsave(str + start_byte, end_byte - start_byte); - } - - /* * Handle: * - expr[expr], expr[expr:expr] subscript * - ".name" lookup --- 5549,5554 ---- *** ../vim-8.2.2317/src/proto/eval.pro 2021-01-05 22:08:17.205806639 +0100 --- src/proto/eval.pro 2021-01-09 12:54:08.242616422 +0100 *************** *** 64,71 **** int eval_isnamec(int c); int eval_isnamec1(int c); int eval_isdictc(int c); - char_u *char_from_string(char_u *str, varnumber_T index); - char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last); int handle_subscript(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose); int item_copy(typval_T *from, typval_T *to, int deep, int copyID); void echo_one(typval_T *rettv, int with_space, int *atstart, int *needclr); --- 64,69 ---- *** ../vim-8.2.2317/src/vim9execute.c 2021-01-08 20:53:05.736404852 +0100 --- src/vim9execute.c 2021-01-09 13:11:00.382282339 +0100 *************** *** 863,868 **** --- 863,970 ---- } } + /* + * Return the character "str[index]" where "index" is the character index. If + * "index" is out of range NULL is returned. + */ + char_u * + char_from_string(char_u *str, varnumber_T index) + { + size_t nbyte = 0; + varnumber_T nchar = index; + size_t slen; + + if (str == NULL) + return NULL; + slen = STRLEN(str); + + // do the same as for a list: a negative index counts from the end + if (index < 0) + { + int clen = 0; + + for (nbyte = 0; nbyte < slen; ++clen) + nbyte += MB_CPTR2LEN(str + nbyte); + nchar = clen + index; + if (nchar < 0) + // unlike list: index out of range results in empty string + return NULL; + } + + for (nbyte = 0; nchar > 0 && nbyte < slen; --nchar) + nbyte += MB_CPTR2LEN(str + nbyte); + if (nbyte >= slen) + return NULL; + return vim_strnsave(str + nbyte, MB_CPTR2LEN(str + nbyte)); + } + + /* + * Get the byte index for character index "idx" in string "str" with length + * "str_len". + * If going over the end return "str_len". + * If "idx" is negative count from the end, -1 is the last character. + * When going over the start return -1. + */ + static long + char_idx2byte(char_u *str, size_t str_len, varnumber_T idx) + { + varnumber_T nchar = idx; + size_t nbyte = 0; + + if (nchar >= 0) + { + while (nchar > 0 && nbyte < str_len) + { + nbyte += MB_CPTR2LEN(str + nbyte); + --nchar; + } + } + else + { + nbyte = str_len; + while (nchar < 0 && nbyte > 0) + { + --nbyte; + nbyte -= mb_head_off(str, str + nbyte); + ++nchar; + } + if (nchar < 0) + return -1; + } + return (long)nbyte; + } + + /* + * Return the slice "str[first:last]" using character indexes. + * Return NULL when the result is empty. + */ + char_u * + string_slice(char_u *str, varnumber_T first, varnumber_T last) + { + long start_byte, end_byte; + size_t slen; + + if (str == NULL) + return NULL; + slen = STRLEN(str); + start_byte = char_idx2byte(str, slen, first); + if (start_byte < 0) + start_byte = 0; // first index very negative: use zero + if (last == -1) + end_byte = (long)slen; + else + { + end_byte = char_idx2byte(str, slen, last); + if (end_byte >= 0 && end_byte < (long)slen) + // end index is inclusive + end_byte += MB_CPTR2LEN(str + end_byte); + } + + if (start_byte >= (long)slen || end_byte <= start_byte) + return NULL; + return vim_strnsave(str + start_byte, end_byte - start_byte); + } + static svar_T * get_script_svar(scriptref_T *sref, ectx_T *ectx) { *** ../vim-8.2.2317/src/list.c 2021-01-02 15:41:00.189079039 +0100 --- src/list.c 2021-01-09 13:05:02.775563656 +0100 *************** *** 924,930 **** if (!range) { if (verbose) ! semsg(_(e_listidx), n1); return FAIL; } n1 = n1 < 0 ? 0 : len; --- 924,930 ---- if (!range) { if (verbose) ! semsg(_(e_listidx), n1_arg); return FAIL; } n1 = n1 < 0 ? 0 : len; *** ../vim-8.2.2317/src/proto/vim9execute.pro 2020-12-22 17:35:50.043978116 +0100 --- src/proto/vim9execute.pro 2021-01-09 12:54:05.318623985 +0100 *************** *** 1,6 **** --- 1,8 ---- /* vim9execute.c */ void to_string_error(vartype_T vartype); void funcstack_check_refcount(funcstack_T *funcstack); + char_u *char_from_string(char_u *str, varnumber_T index); + char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last); int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx); int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, typval_T *rettv); void ex_disassemble(exarg_T *eap); *** ../vim-8.2.2317/src/testdir/test_vim9_expr.vim 2021-01-05 22:08:17.205806639 +0100 --- src/testdir/test_vim9_expr.vim 2021-01-09 13:10:27.422393673 +0100 *************** *** 2304,2310 **** # string is permissive, index out of range accepted g:teststring = 'abcdef' assert_equal('b', g:teststring[1]) ! assert_equal('', g:teststring[-1]) assert_equal('', g:teststring[99]) assert_equal('b', g:teststring[1 : 1]) --- 2304,2310 ---- # string is permissive, index out of range accepted g:teststring = 'abcdef' assert_equal('b', g:teststring[1]) ! assert_equal('f', g:teststring[-1]) assert_equal('', g:teststring[99]) assert_equal('b', g:teststring[1 : 1]) *************** *** 2368,2377 **** CheckDefExecFailure(['echo g:testblob[-3]'], 'E979:', 1) CheckScriptFailure(['vim9script', 'echo g:testblob[-3]'], 'E979:', 2) ! CheckDefExecFailure(['echo g:testlist[4]'], 'E684:', 1) CheckScriptFailure(['vim9script', 'echo g:testlist[4]'], 'E684:', 2) CheckDefExecFailure(['echo g:testlist[-5]'], 'E684:', 1) ! CheckScriptFailure(['vim9script', 'echo g:testlist[-5]'], 'E684:', 2) CheckDefExecFailure(['echo g:testdict["a" : "b"]'], 'E719:', 1) CheckScriptFailure(['vim9script', 'echo g:testdict["a" : "b"]'], 'E719:', 2) --- 2368,2377 ---- CheckDefExecFailure(['echo g:testblob[-3]'], 'E979:', 1) CheckScriptFailure(['vim9script', 'echo g:testblob[-3]'], 'E979:', 2) ! CheckDefExecFailure(['echo g:testlist[4]'], 'E684: list index out of range: 4', 1) CheckScriptFailure(['vim9script', 'echo g:testlist[4]'], 'E684:', 2) CheckDefExecFailure(['echo g:testlist[-5]'], 'E684:', 1) ! CheckScriptFailure(['vim9script', 'echo g:testlist[-5]'], 'E684: list index out of range: -5', 2) CheckDefExecFailure(['echo g:testdict["a" : "b"]'], 'E719:', 1) CheckScriptFailure(['vim9script', 'echo g:testdict["a" : "b"]'], 'E719:', 2) *************** *** 2802,2816 **** def Test_expr7_string_subscript() var lines =<< trim END var text = 'abcdef' ! assert_equal('', text[-1]) assert_equal('a', text[0]) assert_equal('e', text[4]) assert_equal('f', text[5]) assert_equal('', text[6]) text = 'ábçdëf' assert_equal('', text[-999]) ! assert_equal('', text[-1]) assert_equal('á', text[0]) assert_equal('b', text[1]) assert_equal('ç', text[2]) --- 2802,2824 ---- def Test_expr7_string_subscript() var lines =<< trim END var text = 'abcdef' ! assert_equal('f', text[-1]) assert_equal('a', text[0]) assert_equal('e', text[4]) assert_equal('f', text[5]) assert_equal('', text[6]) + text = 'ábçdë' + assert_equal('ë', text[-1]) + assert_equal('d', text[-2]) + assert_equal('ç', text[-3]) + assert_equal('b', text[-4]) + assert_equal('á', text[-5]) + assert_equal('', text[-6]) + text = 'ábçdëf' assert_equal('', text[-999]) ! assert_equal('f', text[-1]) assert_equal('á', text[0]) assert_equal('b', text[1]) assert_equal('ç', text[2]) *************** *** 2904,2911 **** assert_equal([], list[0 : -6]) assert_equal([], list[0 : -99]) END ! CheckDefSuccess(lines) ! CheckScriptSuccess(['vim9script'] + lines) lines = ['var l = [0, 1, 2]', 'echo l[g:astring : g:theone]'] CheckDefExecFailure(lines, 'E1012:') --- 2912,2918 ---- assert_equal([], list[0 : -6]) assert_equal([], list[0 : -99]) END ! CheckDefAndScriptSuccess(lines) lines = ['var l = [0, 1, 2]', 'echo l[g:astring : g:theone]'] CheckDefExecFailure(lines, 'E1012:') *** ../vim-8.2.2317/src/version.c 2021-01-09 12:09:18.403881398 +0100 --- src/version.c 2021-01-09 12:39:51.453011675 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2318, /**/ -- hundred-and-one symptoms of being an internet addict: 117. You are more comfortable typing in html. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///