mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	vim-patch:8.2.3848: cannot use reduce() for a string
Problem:    Cannot use reduce() for a string.
Solution:   Make reduce() work with a string. (Naruhiko Nishino, closes vim/vim#9366)
0ccb5842f5
Omit tv_get_first_char() as it doesn't really save much code.
Co-authored-by: rbtnn <naru123456789@gmail.com>
			
			
This commit is contained in:
		
							
								
								
									
										7
									
								
								runtime/doc/builtin.txt
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								runtime/doc/builtin.txt
									
									
									
										generated
									
									
									
								
							| @@ -5514,9 +5514,9 @@ readfile({fname} [, {type} [, {max}]])                              *readfile()* | |||||||
|  |  | ||||||
| reduce({object}, {func} [, {initial}])                           *reduce()* *E998* | reduce({object}, {func} [, {initial}])                           *reduce()* *E998* | ||||||
| 		{func} is called for every item in {object}, which can be a | 		{func} is called for every item in {object}, which can be a | ||||||
| 		|List| or a |Blob|.  {func} is called with two arguments: the | 		|String|, |List| or a |Blob|.  {func} is called with two arguments: | ||||||
| 		result so far and current item.  After processing all items | 		the result so far and current item.  After processing all | ||||||
| 		the result is returned. | 		items the result is returned. | ||||||
|  |  | ||||||
| 		{initial} is the initial result.  When omitted, the first item | 		{initial} is the initial result.  When omitted, the first item | ||||||
| 		in {object} is used and {func} is first called for the second | 		in {object} is used and {func} is first called for the second | ||||||
| @@ -5527,6 +5527,7 @@ reduce({object}, {func} [, {initial}])                           *reduce()* *E99 | |||||||
| 			echo reduce([1, 3, 5], { acc, val -> acc + val }) | 			echo reduce([1, 3, 5], { acc, val -> acc + val }) | ||||||
| 			echo reduce(['x', 'y'], { acc, val -> acc .. val }, 'a') | 			echo reduce(['x', 'y'], { acc, val -> acc .. val }, 'a') | ||||||
| 			echo reduce(0z1122, { acc, val -> 2 * acc + val }) | 			echo reduce(0z1122, { acc, val -> 2 * acc + val }) | ||||||
|  | 			echo reduce('xyz', { acc, val -> acc .. ',' .. val }) | ||||||
| < | < | ||||||
|  |  | ||||||
| reg_executing()                                                *reg_executing()* | reg_executing()                                                *reg_executing()* | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								runtime/lua/vim/_meta/vimfn.lua
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								runtime/lua/vim/_meta/vimfn.lua
									
									
									
										generated
									
									
									
								
							| @@ -6551,9 +6551,9 @@ function vim.fn.readdir(directory, expr) end | |||||||
| function vim.fn.readfile(fname, type, max) end | function vim.fn.readfile(fname, type, max) end | ||||||
|  |  | ||||||
| --- {func} is called for every item in {object}, which can be a | --- {func} is called for every item in {object}, which can be a | ||||||
| --- |List| or a |Blob|.  {func} is called with two arguments: the | --- |String|, |List| or a |Blob|.  {func} is called with two arguments: | ||||||
| --- result so far and current item.  After processing all items | --- the result so far and current item.  After processing all | ||||||
| --- the result is returned. | --- items the result is returned. | ||||||
| --- | --- | ||||||
| --- {initial} is the initial result.  When omitted, the first item | --- {initial} is the initial result.  When omitted, the first item | ||||||
| --- in {object} is used and {func} is first called for the second | --- in {object} is used and {func} is first called for the second | ||||||
| @@ -6564,6 +6564,7 @@ function vim.fn.readfile(fname, type, max) end | |||||||
| ---   echo reduce([1, 3, 5], { acc, val -> acc + val }) | ---   echo reduce([1, 3, 5], { acc, val -> acc + val }) | ||||||
| ---   echo reduce(['x', 'y'], { acc, val -> acc .. val }, 'a') | ---   echo reduce(['x', 'y'], { acc, val -> acc .. val }, 'a') | ||||||
| ---   echo reduce(0z1122, { acc, val -> 2 * acc + val }) | ---   echo reduce(0z1122, { acc, val -> 2 * acc + val }) | ||||||
|  | ---   echo reduce('xyz', { acc, val -> acc .. ',' .. val }) | ||||||
| --- < | --- < | ||||||
| --- | --- | ||||||
| --- @param object any | --- @param object any | ||||||
|   | |||||||
| @@ -108,7 +108,7 @@ static const char e_dot_can_only_be_used_on_dictionary_str[] | |||||||
|   = N_("E1203: Dot can only be used on a dictionary: %s"); |   = N_("E1203: Dot can only be used on a dictionary: %s"); | ||||||
| static const char e_empty_function_name[] | static const char e_empty_function_name[] | ||||||
|   = N_("E1192: Empty function name"); |   = N_("E1192: Empty function name"); | ||||||
| static char e_argument_of_str_must_be_list_string_dictionary_or_blob[] | static const char e_argument_of_str_must_be_list_string_dictionary_or_blob[] | ||||||
|   = N_("E1250: Argument of %s must be a List, String, Dictionary or Blob"); |   = N_("E1250: Argument of %s must be a List, String, Dictionary or Blob"); | ||||||
|  |  | ||||||
| static char * const namespace_char = "abglstvw"; | static char * const namespace_char = "abglstvw"; | ||||||
| @@ -5206,7 +5206,6 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap | |||||||
|       int len; |       int len; | ||||||
|       for (const char *p = tv_get_string(&argvars[0]); *p != NUL; p += len) { |       for (const char *p = tv_get_string(&argvars[0]); *p != NUL; p += len) { | ||||||
|         len = utfc_ptr2len(p); |         len = utfc_ptr2len(p); | ||||||
|  |  | ||||||
|         typval_T tv = { |         typval_T tv = { | ||||||
|           .v_type = VAR_STRING, |           .v_type = VAR_STRING, | ||||||
|           .v_lock = VAR_UNLOCKED, |           .v_lock = VAR_UNLOCKED, | ||||||
|   | |||||||
| @@ -7876,9 +7876,9 @@ M.funcs = { | |||||||
|     tags = { 'E998' }, |     tags = { 'E998' }, | ||||||
|     desc = [=[ |     desc = [=[ | ||||||
|       {func} is called for every item in {object}, which can be a |       {func} is called for every item in {object}, which can be a | ||||||
|       |List| or a |Blob|.  {func} is called with two arguments: the |       |String|, |List| or a |Blob|.  {func} is called with two arguments: | ||||||
|       result so far and current item.  After processing all items |       the result so far and current item.  After processing all | ||||||
|       the result is returned. |       items the result is returned. | ||||||
|  |  | ||||||
|       {initial} is the initial result.  When omitted, the first item |       {initial} is the initial result.  When omitted, the first item | ||||||
|       in {object} is used and {func} is first called for the second |       in {object} is used and {func} is first called for the second | ||||||
| @@ -7889,6 +7889,7 @@ M.funcs = { | |||||||
|       	echo reduce([1, 3, 5], { acc, val -> acc + val }) |       	echo reduce([1, 3, 5], { acc, val -> acc + val }) | ||||||
|       	echo reduce(['x', 'y'], { acc, val -> acc .. val }, 'a') |       	echo reduce(['x', 'y'], { acc, val -> acc .. val }, 'a') | ||||||
|       	echo reduce(0z1122, { acc, val -> 2 * acc + val }) |       	echo reduce(0z1122, { acc, val -> 2 * acc + val }) | ||||||
|  |       	echo reduce('xyz', { acc, val -> acc .. ',' .. val }) | ||||||
|       < |       < | ||||||
|     ]=], |     ]=], | ||||||
|     name = 'reduce', |     name = 'reduce', | ||||||
|   | |||||||
| @@ -150,8 +150,12 @@ static const char *e_invalwindow = N_("E957: Invalid window number"); | |||||||
| static const char e_invalid_submatch_number_nr[] | static const char e_invalid_submatch_number_nr[] | ||||||
|   = N_("E935: Invalid submatch number: %d"); |   = N_("E935: Invalid submatch number: %d"); | ||||||
| static const char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value"); | static const char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value"); | ||||||
|  | static const char e_string_list_or_blob_required[] | ||||||
|  |   = N_("E1098: String, List or Blob required"); | ||||||
| static const char e_missing_function_argument[] | static const char e_missing_function_argument[] | ||||||
|   = N_("E1132: Missing function argument"); |   = N_("E1132: Missing function argument"); | ||||||
|  | static const char e_string_expected_for_argument_nr[] | ||||||
|  |   = N_("E1253: String expected for argument %d"); | ||||||
|  |  | ||||||
| /// Dummy va_list for passing to vim_snprintf | /// Dummy va_list for passing to vim_snprintf | ||||||
| /// | /// | ||||||
| @@ -6168,11 +6172,16 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) | |||||||
| } | } | ||||||
|  |  | ||||||
| /// "reduce(list, { accumulator, element -> value } [, initial])" function | /// "reduce(list, { accumulator, element -> value } [, initial])" function | ||||||
|  | /// "reduce(blob, { accumulator, element -> value } [, initial])" function | ||||||
|  | /// "reduce(string, { accumulator, element -> value } [, initial])" function | ||||||
| static void f_reduce(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) | static void f_reduce(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) | ||||||
| { | { | ||||||
|   if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) { |   const int called_emsg_start = called_emsg; | ||||||
|     emsg(_(e_listblobreq)); |  | ||||||
|     return; |   if (argvars[0].v_type != VAR_STRING | ||||||
|  |       && argvars[0].v_type != VAR_LIST | ||||||
|  |       && argvars[0].v_type != VAR_BLOB) { | ||||||
|  |     emsg(_(e_string_list_or_blob_required)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const char *func_name; |   const char *func_name; | ||||||
| @@ -6217,7 +6226,6 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) | |||||||
|  |  | ||||||
|     if (l != NULL) { |     if (l != NULL) { | ||||||
|       const VarLockStatus prev_locked = tv_list_locked(l); |       const VarLockStatus prev_locked = tv_list_locked(l); | ||||||
|       const int called_emsg_start = called_emsg; |  | ||||||
|  |  | ||||||
|       tv_list_set_lock(l, VAR_FIXED);  // disallow the list changing here |       tv_list_set_lock(l, VAR_FIXED);  // disallow the list changing here | ||||||
|       for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { |       for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { | ||||||
| @@ -6232,6 +6240,44 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) | |||||||
|       } |       } | ||||||
|       tv_list_set_lock(l, prev_locked); |       tv_list_set_lock(l, prev_locked); | ||||||
|     } |     } | ||||||
|  |   } else if (argvars[0].v_type == VAR_STRING) { | ||||||
|  |     const char *p = tv_get_string(&argvars[0]); | ||||||
|  |     int len; | ||||||
|  |  | ||||||
|  |     if (argvars[2].v_type == VAR_UNKNOWN) { | ||||||
|  |       if (*p == NUL) { | ||||||
|  |         semsg(_(e_reduceempty), "String"); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       len = utfc_ptr2len(p); | ||||||
|  |       *rettv = (typval_T){ | ||||||
|  |         .v_type = VAR_STRING, | ||||||
|  |         .v_lock = VAR_UNLOCKED, | ||||||
|  |         .vval.v_string = xstrnsave(p, (size_t)len), | ||||||
|  |       }; | ||||||
|  |       p += len; | ||||||
|  |     } else if (argvars[2].v_type != VAR_STRING) { | ||||||
|  |       semsg(_(e_string_expected_for_argument_nr), 3); | ||||||
|  |       return; | ||||||
|  |     } else { | ||||||
|  |       tv_copy(&argvars[2], rettv); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (; *p != NUL; p += len) { | ||||||
|  |       argv[0] = *rettv; | ||||||
|  |       len = utfc_ptr2len(p); | ||||||
|  |       argv[1] = (typval_T){ | ||||||
|  |         .v_type = VAR_STRING, | ||||||
|  |         .v_lock = VAR_UNLOCKED, | ||||||
|  |         .vval.v_string = xstrnsave(p, (size_t)len), | ||||||
|  |       }; | ||||||
|  |       const int r = call_func(func_name, -1, rettv, 2, argv, &funcexe); | ||||||
|  |       tv_clear(&argv[0]); | ||||||
|  |       tv_clear(&argv[1]); | ||||||
|  |       if (r == FAIL || called_emsg != called_emsg_start) { | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|   } else { |   } else { | ||||||
|     const blob_T *const b = argvars[0].vval.v_blob; |     const blob_T *const b = argvars[0].vval.v_blob; | ||||||
|     int i; |     int i; | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| " Tests for the List and Dict types | " Tests for the List and Dict types | ||||||
|  | scriptencoding utf-8 | ||||||
|  |  | ||||||
| source vim9.vim | source vim9.vim | ||||||
|  |  | ||||||
| @@ -900,7 +901,7 @@ func Test_reverse_sort_uniq() | |||||||
|   call assert_fails("call sort([1, 2], function('min'))", "E118:") |   call assert_fails("call sort([1, 2], function('min'))", "E118:") | ||||||
| endfunc | endfunc | ||||||
|  |  | ||||||
| " reduce a list or a blob | " reduce a list, blob or string | ||||||
| func Test_reduce() | func Test_reduce() | ||||||
|   let lines =<< trim END |   let lines =<< trim END | ||||||
|       call assert_equal(1, reduce([], LSTART acc, val LMIDDLE acc + val LEND, 1)) |       call assert_equal(1, reduce([], LSTART acc, val LMIDDLE acc + val LEND, 1)) | ||||||
| @@ -923,6 +924,16 @@ func Test_reduce() | |||||||
|  |  | ||||||
|       call assert_equal(0xff, reduce(0zff, LSTART acc, val LMIDDLE acc + val LEND)) |       call assert_equal(0xff, reduce(0zff, LSTART acc, val LMIDDLE acc + val LEND)) | ||||||
|       call assert_equal(2 * (2 * 0xaf + 0xbf) + 0xcf, reduce(0zAFBFCF, LSTART acc, val LMIDDLE 2 * acc + val LEND)) |       call assert_equal(2 * (2 * 0xaf + 0xbf) + 0xcf, reduce(0zAFBFCF, LSTART acc, val LMIDDLE 2 * acc + val LEND)) | ||||||
|  |  | ||||||
|  |       call assert_equal('x,y,z', 'xyz'->reduce(LSTART acc, val LMIDDLE acc .. ',' .. val LEND)) | ||||||
|  |       call assert_equal('', ''->reduce(LSTART acc, val LMIDDLE acc .. ',' .. val LEND, '')) | ||||||
|  |       call assert_equal('あ,い,う,え,お,😊,💕', 'あいうえお😊💕'->reduce(LSTART acc, val LMIDDLE acc .. ',' .. val LEND)) | ||||||
|  |       call assert_equal('😊,あ,い,う,え,お,💕', 'あいうえお💕'->reduce(LSTART acc, val LMIDDLE acc .. ',' .. val LEND, '😊')) | ||||||
|  |       call assert_equal('ऊ,ॠ,ॡ', reduce('ऊॠॡ', LSTART acc, val LMIDDLE acc .. ',' .. val LEND)) | ||||||
|  |       call assert_equal('c,à,t', reduce('càt', LSTART acc, val LMIDDLE acc .. ',' .. val LEND)) | ||||||
|  |       call assert_equal('Å,s,t,r,ö,m', reduce('Åström', LSTART acc, val LMIDDLE acc .. ',' .. val LEND)) | ||||||
|  |       call assert_equal('Å,s,t,r,ö,m', reduce('Åström', LSTART acc, val LMIDDLE acc .. ',' .. val LEND)) | ||||||
|  |       call assert_equal(',a,b,c', reduce('abc', LSTART acc, val LMIDDLE acc .. ',' .. val LEND, v:_null_string)) | ||||||
|   END |   END | ||||||
|   call CheckLegacyAndVim9Success(lines) |   call CheckLegacyAndVim9Success(lines) | ||||||
|  |  | ||||||
| @@ -931,13 +942,23 @@ func Test_reduce() | |||||||
|  |  | ||||||
|   call assert_fails("call reduce([], { acc, val -> acc + val })", 'E998: Reduce of an empty List with no initial value') |   call assert_fails("call reduce([], { acc, val -> acc + val })", 'E998: Reduce of an empty List with no initial value') | ||||||
|   call assert_fails("call reduce(0z, { acc, val -> acc + val })", 'E998: Reduce of an empty Blob with no initial value') |   call assert_fails("call reduce(0z, { acc, val -> acc + val })", 'E998: Reduce of an empty Blob with no initial value') | ||||||
|  |   call assert_fails("call reduce('', { acc, val -> acc + val })", 'E998: Reduce of an empty String with no initial value') | ||||||
|  |   call assert_fails("call reduce(v:_null_string, { acc, val -> acc + val })", 'E998: Reduce of an empty String with no initial value') | ||||||
|  |  | ||||||
|   call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E897:') |   call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E1098:') | ||||||
|   call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E897:') |   call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E1098:') | ||||||
|   call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E897:') |  | ||||||
|   call assert_fails("call reduce([1, 2], 'Xdoes_not_exist')", 'E117:') |   call assert_fails("call reduce([1, 2], 'Xdoes_not_exist')", 'E117:') | ||||||
|   call assert_fails("echo reduce(0z01, { acc, val -> 2 * acc + val }, '')", 'E39:') |   call assert_fails("echo reduce(0z01, { acc, val -> 2 * acc + val }, '')", 'E39:') | ||||||
|  |  | ||||||
|  |   " call assert_fails("vim9 reduce(0, (acc, val) => (acc .. val), '')", 'E1252:') | ||||||
|  |   " call assert_fails("vim9 reduce({}, (acc, val) => (acc .. val), '')", 'E1252:') | ||||||
|  |   " call assert_fails("vim9 reduce(0.1, (acc, val) => (acc .. val), '')", 'E1252:') | ||||||
|  |   " call assert_fails("vim9 reduce(function('tr'), (acc, val) => (acc .. val), '')", 'E1252:') | ||||||
|  |   call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E1253:') | ||||||
|  |   call assert_fails("call reduce('', { acc, val -> acc + val }, {})", 'E1253:') | ||||||
|  |   call assert_fails("call reduce('', { acc, val -> acc + val }, 0.1)", 'E1253:') | ||||||
|  |   call assert_fails("call reduce('', { acc, val -> acc + val }, function('tr'))", 'E1253:') | ||||||
|  |  | ||||||
|   let g:lut = [1, 2, 3, 4] |   let g:lut = [1, 2, 3, 4] | ||||||
|   func EvilRemove() |   func EvilRemove() | ||||||
|     call remove(g:lut, 1) |     call remove(g:lut, 1) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 zeertzjq
					zeertzjq