# HG changeset patch # User Bram Moolenaar # Date 1639060204 -3600 # Node ID ff0310e6f88916b44f490f062e37c51707b2860a # Parent 8e3d1d2500bb4c143ef08cfb4631583d2b2a8a14 patch 8.2.3765: Vim9: cannot use a lambda for 'opfunc' and others Commit: https://github.com/vim/vim/commit/dcb53be4418fe263a71c7738315241031df6c986 Author: Bram Moolenaar Date: Thu Dec 9 14:23:43 2021 +0000 patch 8.2.3765: Vim9: cannot use a lambda for 'opfunc' and others Problem: Vim9: cannot use a lambda for 'opfunc' and others. Solution: Convert the lambda to a string. diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -316,6 +316,7 @@ def s:ScriptFuncStore() w:windowvar = 'wv' t:tabpagevar = 'tv' &tabstop = 8 + &opfunc = (t) => len(t) $ENVVAR = 'ev' @z = 'rv' enddef @@ -343,12 +344,17 @@ def Test_disassemble_store() ' STOREW w:windowvar.*' .. 't:tabpagevar = ''tv''.*' .. ' STORET t:tabpagevar.*' .. - '&tabstop = 8.*' .. - ' STOREOPT &tabstop.*' .. - '$ENVVAR = ''ev''.*' .. - ' STOREENV $ENVVAR.*' .. + '&tabstop = 8\_s*' .. + '\d\+ PUSHNR 8\_s*' .. + '\d\+ STOREOPT &tabstop\_s*' .. + '&opfunc = (t) => len(t)\_s*' .. + '\d\+ FUNCREF \d\+\_s*' .. + '\d\+ STOREFUNCOPT &opfunc\_s*' .. + '$ENVVAR = ''ev''\_s*' .. + '\d\+ PUSHS "ev"\_s*' .. + '\d\+ STOREENV $ENVVAR\_s*' .. '@z = ''rv''.*' .. - ' STOREREG @z.*', + '\d\+ STOREREG @z.*', res) enddef diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -1202,6 +1202,28 @@ def Test_lambda_in_reduce_line_break() CheckScriptSuccess(lines) enddef +def Test_set_opfunc_to_lambda() + var lines =<< trim END + vim9script + nnoremap CountSpaces() .. '_' + def CountSpaces(type = ''): string + if type == '' + &operatorfunc = (t) => CountSpaces(t) + return 'g@' + endif + normal! '[V']y + g:result = getreg('"')->count(' ') + return '' + enddef + new + 'a b c d e'->setline(1) + feedkeys("\", 'x') + assert_equal(4, g:result) + bwipe! + END + CheckScriptSuccess(lines) +enddef + " Default arg and varargs def MyDefVarargs(one: string, two = 'foo', ...rest: list): string var res = one .. ',' .. two diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3765, +/**/ 3764, /**/ 3763, diff --git a/src/vim9.h b/src/vim9.h --- a/src/vim9.h +++ b/src/vim9.h @@ -55,7 +55,8 @@ typedef enum { ISN_STORES, // pop into script variable isn_arg.loadstore ISN_STOREOUTER, // pop variable into outer scope isn_arg.outer ISN_STORESCRIPT, // pop into script variable isn_arg.script - ISN_STOREOPT, // pop into option isn_arg.string + ISN_STOREOPT, // pop into option isn_arg.storeopt + ISN_STOREFUNCOPT, // pop into option isn_arg.storeopt ISN_STOREENV, // pop into environment variable isn_arg.string ISN_STOREREG, // pop into register isn_arg.number // ISN_STOREOTHER, // pop into other script variable isn_arg.other. @@ -291,7 +292,7 @@ typedef struct { varnumber_T stnr_val; } storenr_T; -// arguments to ISN_STOREOPT +// arguments to ISN_STOREOPT and ISN_STOREFUNCOPT typedef struct { char_u *so_name; int so_flags; diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -118,6 +118,7 @@ typedef struct { typedef enum { dest_local, dest_option, + dest_func_option, dest_env, dest_global, dest_buffer, @@ -1412,15 +1413,19 @@ generate_STORENR(cctx_T *cctx, int idx, } /* - * Generate an ISN_STOREOPT instruction - */ - static int -generate_STOREOPT(cctx_T *cctx, char_u *name, int opt_flags) + * Generate an ISN_STOREOPT or ISN_STOREFUNCOPT instruction + */ + static int +generate_STOREOPT( + cctx_T *cctx, + isntype_T isn_type, + char_u *name, + int opt_flags) { isn_T *isn; RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_drop(cctx, ISN_STOREOPT, 1)) == NULL) + if ((isn = generate_instr_drop(cctx, isn_type, 1)) == NULL) return FAIL; isn->isn_arg.storeopt.so_name = vim_strsave(name); isn->isn_arg.storeopt.so_flags = opt_flags; @@ -5980,6 +5985,7 @@ generate_loadvar( switch (dest) { case dest_option: + case dest_func_option: generate_LOAD(cctx, ISN_LOADOPT, 0, name, type); break; case dest_global: @@ -6094,6 +6100,7 @@ get_var_dest( int cc; long numval; getoption_T opt_type; + int opt_p_flags; *dest = dest_option; if (cmdidx == CMD_final || cmdidx == CMD_const) @@ -6112,7 +6119,7 @@ get_var_dest( cc = *p; *p = NUL; opt_type = get_option_value(skip_option_env_lead(name), - &numval, NULL, NULL, *option_scope); + &numval, NULL, &opt_p_flags, *option_scope); *p = cc; switch (opt_type) { @@ -6121,7 +6128,16 @@ get_var_dest( return FAIL; case gov_string: case gov_hidden_string: - *type = &t_string; + if (opt_p_flags & P_FUNC) + { + // might be a Funcref, check the type later + *type = &t_any; + *dest = dest_func_option; + } + else + { + *type = &t_string; + } break; case gov_bool: case gov_hidden_bool: @@ -6204,8 +6220,11 @@ generate_store_var( switch (dest) { case dest_option: - return generate_STOREOPT(cctx, skip_option_env_lead(name), - opt_flags); + return generate_STOREOPT(cctx, ISN_STOREOPT, + skip_option_env_lead(name), opt_flags); + case dest_func_option: + return generate_STOREOPT(cctx, ISN_STOREFUNCOPT, + skip_option_env_lead(name), opt_flags); case dest_global: // include g: with the name, easier to execute that way return generate_STORE(cctx, vim_strchr(name, AUTOLOAD_CHAR) == NULL @@ -6468,7 +6487,7 @@ compile_lhs( if (lhs->lhs_varlen > 1 || var_start[lhs->lhs_varlen] != ':') var_end = lhs->lhs_dest_end; - if (lhs->lhs_dest != dest_option) + if (lhs->lhs_dest != dest_option && lhs->lhs_dest != dest_func_option) { if (is_decl && *var_end == ':') { @@ -7223,7 +7242,8 @@ compile_assignment(char_u *arg, exarg_T emsg(_(e_const_requires_a_value)); goto theend; } - else if (!lhs.lhs_has_type || lhs.lhs_dest == dest_option) + else if (!lhs.lhs_has_type || lhs.lhs_dest == dest_option + || lhs.lhs_dest == dest_func_option) { emsg(_(e_type_or_initialization_required)); goto theend; @@ -10454,6 +10474,7 @@ delete_instr(isn_T *isn) break; case ISN_STOREOPT: + case ISN_STOREFUNCOPT: vim_free(isn->isn_arg.storeopt.so_name); break; diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2336,10 +2336,14 @@ exec_instructions(ectx_T *ectx) // store option case ISN_STOREOPT: + case ISN_STOREFUNCOPT: { + char_u *opt_name = iptr->isn_arg.storeopt.so_name; + int opt_flags = iptr->isn_arg.storeopt.so_flags; long n = 0; char_u *s = NULL; char *msg; + callback_T cb = {NULL, NULL, 0}; --ectx->ec_stack.ga_len; tv = STACK_TV_BOT(0); @@ -2349,11 +2353,22 @@ exec_instructions(ectx_T *ectx) if (s == NULL) s = (char_u *)""; } + else if (iptr->isn_type == ISN_STOREFUNCOPT) + { + SOURCING_LNUM = iptr->isn_lnum; + cb = get_callback(tv); + if (cb.cb_name == NULL || *cb.cb_name == NUL) + { + clear_tv(tv); + free_callback(&cb); + goto on_error; + } + s = cb.cb_name; + } else // must be VAR_NUMBER, CHECKTYPE makes sure n = tv->vval.v_number; - msg = set_option_value(iptr->isn_arg.storeopt.so_name, - n, s, iptr->isn_arg.storeopt.so_flags); + msg = set_option_value(opt_name, n, s, opt_flags); clear_tv(tv); if (msg != NULL) { @@ -2361,6 +2376,8 @@ exec_instructions(ectx_T *ectx) emsg(_(msg)); goto on_error; } + if (cb.cb_name != NULL) + free_callback(&cb); } break; @@ -5335,7 +5352,9 @@ list_instructions(char *pfx, isn_T *inst } break; case ISN_STOREOPT: - smsg("%s%4d STOREOPT &%s", pfx, current, + case ISN_STOREFUNCOPT: + smsg("%s%4d %s &%s", pfx, current, + iptr->isn_type == ISN_STOREOPT ? "STOREOPT" : "STOREFUNCOPT", iptr->isn_arg.storeopt.so_name); break; case ISN_STOREENV: