vim-patch:8.2.3751: cannot assign a lambda to an option that takes a function

Problem:    Cannot assign a lambda to an option that takes a function.
Solution:   Automatically convert the lambda to a string. (Yegappan
            Lakshmanan, closes vim/vim#9286)

6409553b6e

Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
zeertzjq 2022-11-07 10:26:54 +08:00
parent f91d200c05
commit 42e44d6d33
12 changed files with 291 additions and 51 deletions

View File

@ -315,7 +315,8 @@ Note: In the future more global options can be made |global-local|. Using
*option-value-function*
Some options ('completefunc', 'imactivatefunc', 'imstatusfunc', 'omnifunc',
'operatorfunc', 'quickfixtextfunc', 'tagfunc' and 'thesaurusfunc') are set to
a function name or a function reference or a lambda function. Examples:
a function name or a function reference or a lambda function. When using a
lambda it will be converted to the name, e.g. "<lambda>123". Examples:
>
set opfunc=MyOpFunc
set opfunc=function('MyOpFunc')
@ -325,10 +326,10 @@ a function name or a function reference or a lambda function. Examples:
let Fn = function('MyTagFunc')
let &tagfunc = string(Fn)
" set using a lambda expression
let &tagfunc = "{t -> MyTagFunc(t)}"
let &tagfunc = {t -> MyTagFunc(t)}
" set using a variable with lambda expression
let L = {a, b, c -> MyTagFunc(a, b , c)}
let &tagfunc = string(L)
let &tagfunc = L
<
Setting the filetype

View File

@ -487,7 +487,7 @@ static getoption_T access_option_value(char *key, long *numval, char **stringval
bool get, Error *err)
{
if (get) {
return get_option_value(key, numval, stringval, opt_flags);
return get_option_value(key, numval, stringval, NULL, opt_flags);
} else {
char *errmsg;
if ((errmsg = set_option_value(key, *numval, *stringval, opt_flags))) {

View File

@ -131,7 +131,7 @@ bool ctx_restore(Context *ctx, const int flags)
}
char *op_shada;
get_option_value("shada", NULL, &op_shada, OPT_GLOBAL);
get_option_value("shada", NULL, &op_shada, NULL, OPT_GLOBAL);
set_option_value("shada", 0L, "!,'100,%", OPT_GLOBAL);
if (flags & kCtxRegs) {

View File

@ -3689,10 +3689,10 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
int get_option_tv(const char **const arg, typval_T *const rettv, const bool evaluate)
FUNC_ATTR_NONNULL_ARG(1)
{
int opt_flags;
int scope;
// Isolate the option name and find its value.
char *option_end = (char *)find_option_end(arg, &opt_flags);
char *option_end = (char *)find_option_end(arg, &scope);
if (option_end == NULL) {
if (rettv != NULL) {
semsg(_("E112: Option name missing: %s"), *arg);
@ -3712,7 +3712,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
char c = *option_end;
*option_end = NUL;
getoption_T opt_type = get_option_value(*arg, &numval,
rettv == NULL ? NULL : &stringval, opt_flags);
rettv == NULL ? NULL : &stringval, NULL, scope);
if (opt_type == gov_unknown) {
if (rettv != NULL) {
@ -7794,19 +7794,19 @@ void ex_execute(exarg_T *eap)
///
/// @return NULL when no option name found. Otherwise pointer to the char
/// after the option name.
const char *find_option_end(const char **const arg, int *const opt_flags)
const char *find_option_end(const char **const arg, int *const scope)
{
const char *p = *arg;
p++;
if (*p == 'g' && p[1] == ':') {
*opt_flags = OPT_GLOBAL;
*scope = OPT_GLOBAL;
p += 2;
} else if (*p == 'l' && p[1] == ':') {
*opt_flags = OPT_LOCAL;
*scope = OPT_LOCAL;
p += 2;
} else {
*opt_flags = 0;
*scope = 0;
}
if (!ASCII_ISALPHA(*p)) {

View File

@ -560,8 +560,6 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
{
char *arg_end = NULL;
int len;
int opt_flags;
char *tofree = NULL;
// ":let $VAR = expr": Set environment variable.
if (*arg == '$') {
@ -582,12 +580,12 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
&& vim_strchr(endchars, *skipwhite(arg)) == NULL) {
emsg(_(e_letunexp));
} else if (!check_secure()) {
char *tofree = NULL;
const char c1 = name[len];
name[len] = NUL;
const char *p = tv_get_string_chk(tv);
if (p != NULL && op != NULL && *op == '.') {
char *s = vim_getenv(name);
if (s != NULL) {
tofree = concat_str(s, p);
p = (const char *)tofree;
@ -611,7 +609,8 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
return NULL;
}
// Find the end of the name.
char *const p = (char *)find_option_end((const char **)&arg, &opt_flags);
int scope;
char *const p = (char *)find_option_end((const char **)&arg, &scope);
if (p == NULL
|| (endchars != NULL
&& vim_strchr(endchars, *skipwhite(p)) == NULL)) {
@ -623,11 +622,13 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
char *stringval = NULL;
const char *s = NULL;
bool failed = false;
uint32_t opt_p_flags;
char *tofree = NULL;
const char c1 = *p;
*p = NUL;
opt_type = get_option_value(arg, &numval, &stringval, opt_flags);
opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, scope);
if (opt_type == gov_bool
|| opt_type == gov_number
|| opt_type == gov_hidden_bool
@ -636,8 +637,13 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
n = (long)tv_get_number(tv);
}
// Avoid setting a string option to the text "v:false" or similar.
if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) {
if ((opt_p_flags & P_FUNC) && tv_is_func(*tv)) {
// If the option can be set to a function reference or a lambda
// and the passed value is a function reference, then convert it to
// the name (string) of the function reference.
s = tofree = encode_tv2string(tv, NULL);
} else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) {
// Avoid setting a string option to the text "v:false" or similar.
s = tv_get_string_chk(tv);
}
@ -674,7 +680,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
if (!failed) {
if (opt_type != gov_string || s != NULL) {
char *err = set_option_value(arg, n, s, opt_flags);
char *err = set_option_value(arg, n, s, scope);
arg_end = p;
if (err != NULL) {
emsg(_(err));
@ -685,6 +691,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
}
*p = c1;
xfree(stringval);
xfree(tofree);
}
// ":let @r = expr": Set register contents.
} else if (*arg == '@') {

View File

@ -29,14 +29,14 @@ local type_flags={
}
local redraw_flags={
ui_option='P_UI_OPTION',
tabline='P_RTABL',
statuslines='P_RSTAT',
tabline = 'P_RTABL',
current_window='P_RWIN',
current_window_only='P_RWINONLY',
current_buffer='P_RBUF',
all_windows='P_RALL',
curswant='P_CURSWANT',
ui_option='P_UI_OPTION',
}
local list_flags={
@ -78,6 +78,7 @@ local get_flags = function(o)
{'deny_in_modelines', 'P_NO_ML'},
{'deny_duplicates', 'P_NODUP'},
{'modelineexpr', 'P_MLE'},
{'func'}
}) do
local key_name = flag_desc[1]
local def_name = flag_desc[2] or ('P_' .. key_name:upper())

View File

@ -2842,6 +2842,7 @@ int findoption(const char *const arg)
/// Gets the value for an option.
///
/// @param stringval NULL when only checking existence
/// @param flagsp set to the option flags (P_xxxx) (if not NULL)
///
/// @returns:
/// Number option: gov_number, *numval gets value.
@ -2851,7 +2852,8 @@ int findoption(const char *const arg)
/// Hidden Toggle option: gov_hidden_bool.
/// Hidden String option: gov_hidden_string.
/// Unknown option: gov_unknown.
getoption_T get_option_value(const char *name, long *numval, char **stringval, int opt_flags)
getoption_T get_option_value(const char *name, long *numval, char **stringval, uint32_t *flagsp,
int scope)
{
if (get_tty_option(name, stringval)) {
return gov_string;
@ -2862,7 +2864,12 @@ getoption_T get_option_value(const char *name, long *numval, char **stringval, i
return gov_unknown;
}
char_u *varp = (char_u *)get_varp_scope(&(options[opt_idx]), opt_flags);
char_u *varp = (char_u *)get_varp_scope(&(options[opt_idx]), scope);
if (flagsp != NULL) {
// Return the P_xxxx option flags.
*flagsp = options[opt_idx].flags;
}
if (options[opt_idx].flags & P_STRING) {
if (varp == NULL) { // hidden option
@ -3092,7 +3099,7 @@ char *set_option_value(const char *const name, const long number, const char *co
numval = -1;
} else {
char *s = NULL;
(void)get_option_value(name, &numval, &s, OPT_GLOBAL);
(void)get_option_value(name, &numval, &s, NULL, OPT_GLOBAL);
}
}
if (flags & P_NUM) {
@ -3701,15 +3708,17 @@ void unset_global_local_option(char *name, void *from)
}
/// Get pointer to option variable, depending on local or global scope.
char *get_varp_scope(vimoption_T *p, int opt_flags)
///
/// @param scope can be OPT_LOCAL, OPT_GLOBAL or a combination.
char *get_varp_scope(vimoption_T *p, int scope)
{
if ((opt_flags & OPT_GLOBAL) && p->indir != PV_NONE) {
if ((scope & OPT_GLOBAL) && p->indir != PV_NONE) {
if (p->var == VAR_WIN) {
return GLOBAL_WO(get_varp(p));
}
return (char *)p->var;
}
if ((opt_flags & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) {
if ((scope & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) {
switch ((int)p->indir) {
case PV_FP:
return (char *)&(curbuf->b_p_fp);
@ -4863,9 +4872,9 @@ void ExpandOldSetting(int *num_file, char ***file)
/// NameBuff[]. Must not be called with a hidden option!
///
/// @param opt_flags OPT_GLOBAL and/or OPT_LOCAL
static void option_value2string(vimoption_T *opp, int opt_flags)
static void option_value2string(vimoption_T *opp, int scope)
{
char_u *varp = (char_u *)get_varp_scope(opp, opt_flags);
char_u *varp = (char_u *)get_varp_scope(opp, scope);
if (opp->flags & P_NUM) {
long wc = 0;

View File

@ -24,6 +24,7 @@
#define P_NO_MKRC 0x200U ///< don't include in :mkvimrc output
// when option changed, what to display:
#define P_UI_OPTION 0x400U ///< send option to remote UI
#define P_RTABL 0x800U ///< redraw tabline
#define P_RSTAT 0x1000U ///< redraw status lines
#define P_RWIN 0x2000U ///< redraw current window and recompute text
@ -50,9 +51,9 @@
#define P_NDNAME 0x8000000U ///< only normal dir name chars allowed
#define P_RWINONLY 0x10000000U ///< only redraw current window
#define P_MLE 0x20000000U ///< under control of 'modelineexpr'
#define P_FUNC 0x40000000U ///< accept a function reference or a lambda
#define P_NO_DEF_EXP 0x40000000U ///< Do not expand default value.
#define P_UI_OPTION 0x80000000U ///< send option to remote ui
#define P_NO_DEF_EXP 0x80000000U ///< Do not expand default value.
/// Flags for option-setting functions
///

View File

@ -10,6 +10,7 @@
-- secure=nil, gettext=nil, noglob=nil, normal_fname_chars=nil,
-- pri_mkrc=nil, deny_in_modelines=nil, normal_dname_chars=nil,
-- modelineexpr=nil,
-- func=nil,
-- expand=nil, nodefault=nil, no_mkrc=nil,
-- alloced=nil,
-- save_pv_indir=nil,
@ -455,6 +456,7 @@ return {
type='string', scope={'buffer'},
secure=true,
alloced=true,
func=true,
varname='p_cfu',
defaults={if_true=""}
},
@ -1638,6 +1640,7 @@ return {
type='string', scope={'buffer'},
secure=true,
alloced=true,
func=true,
varname='p_ofu',
defaults={if_true=""}
},
@ -1653,6 +1656,7 @@ return {
short_desc=N_("function to be called for |g@| operator"),
type='string', scope={'global'},
secure=true,
func=true,
varname='p_opfunc',
defaults={if_true=""}
},
@ -1835,6 +1839,8 @@ return {
full_name='quickfixtextfunc', abbreviation='qftf',
short_desc=N_("customize the quickfix window"),
type='string', scope={'global'},
secure=true,
func=true,
varname='p_qftf',
defaults={if_true=""}
},
@ -2408,6 +2414,8 @@ return {
full_name='tagfunc', abbreviation='tfu',
short_desc=N_("function used to perform tag searches"),
type='string', scope={'buffer'},
secure=true,
func=true,
varname='p_tfu',
defaults={if_true=""}
},
@ -2538,6 +2546,7 @@ return {
type='string', scope={'global', 'buffer'},
secure=true,
alloced=true,
func=true,
varname='p_tsrfu',
defaults={if_true=""}
},

View File

@ -3145,7 +3145,7 @@ void ex_spelldump(exarg_T *eap)
}
char *spl;
long dummy;
(void)get_option_value("spl", &dummy, &spl, OPT_LOCAL);
(void)get_option_value("spl", &dummy, &spl, NULL, OPT_LOCAL);
// Create a new empty buffer in a new window.
do_cmdline_cmd("new");

View File

@ -1302,13 +1302,22 @@ func Test_completefunc_callback()
" Using a funcref variable to set 'completefunc'
let Fn = function('MycompleteFunc1')
let &completefunc = Fn
new | only
call setline(1, 'two')
let g:MycompleteFunc1_args = []
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'two']], g:MycompleteFunc1_args)
bw!
" Using string(funcref_variable) to set 'completefunc'
let Fn = function('MycompleteFunc1')
let &completefunc = string(Fn)
new | only
call setline(1, 'two')
let g:MycompleteFunc1_args = []
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'two']], g:MycompleteFunc1_args)
call assert_fails('let &completefunc = Fn', 'E729:')
bw!
" Test for using a funcref()
@ -1326,13 +1335,22 @@ func Test_completefunc_callback()
" Using a funcref variable to set 'completefunc'
let Fn = funcref('MycompleteFunc2')
let &completefunc = Fn
new | only
call setline(1, 'four')
let g:MycompleteFunc2_args = []
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'four']], g:MycompleteFunc2_args)
bw!
" Using a string(funcref_variable) to set 'completefunc'
let Fn = funcref('MycompleteFunc2')
let &completefunc = string(Fn)
new | only
call setline(1, 'four')
let g:MycompleteFunc2_args = []
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'four']], g:MycompleteFunc2_args)
call assert_fails('let &completefunc = Fn', 'E729:')
bw!
" Test for using a lambda function
@ -1349,6 +1367,15 @@ func Test_completefunc_callback()
bw!
" Set 'completefunc' to a lambda expression
let &completefunc = {a, b -> MycompleteFunc3(a, b)}
new | only
call setline(1, 'six')
let g:MycompleteFunc3_args = []
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'six']], g:MycompleteFunc3_args)
bw!
" Set 'completefunc' to string(lambda_expression)
let &completefunc = '{a, b -> MycompleteFunc3(a, b)}'
new | only
call setline(1, 'six')
@ -1359,18 +1386,27 @@ func Test_completefunc_callback()
" Set 'completefunc' to a variable with a lambda expression
let Lambda = {a, b -> MycompleteFunc3(a, b)}
let &completefunc = Lambda
new | only
call setline(1, 'seven')
let g:MycompleteFunc3_args = []
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'seven']], g:MycompleteFunc3_args)
bw!
" Set 'completefunc' to a string(variable with a lambda expression)
let Lambda = {a, b -> MycompleteFunc3(a, b)}
let &completefunc = string(Lambda)
new | only
call setline(1, 'seven')
let g:MycompleteFunc3_args = []
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'seven']], g:MycompleteFunc3_args)
call assert_fails('let &completefunc = Lambda', 'E729:')
bw!
" Test for using a lambda function with incorrect return value
let Lambda = {s -> strlen(s)}
let &completefunc = string(Lambda)
let &completefunc = Lambda
new | only
call setline(1, 'eight')
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
@ -1382,7 +1418,7 @@ func Test_completefunc_callback()
call assert_fails("set completefunc=function('abc')", "E700:")
call assert_fails("set completefunc=funcref('abc')", "E700:")
let &completefunc = "{a -> 'abc'}"
let &completefunc = {a -> 'abc'}
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
" Vim9 tests
@ -1407,6 +1443,15 @@ func Test_completefunc_callback()
add(g:LambdaComplete1_args, [findstart, base])
return findstart ? 0 : []
enddef
&completefunc = (a, b) => LambdaComplete1(a, b)
new | only
setline(1, 'two')
g:LambdaComplete1_args = []
feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
assert_equal([[1, ''], [0, 'two']], g:LambdaComplete1_args)
bw!
# Test for using a string(lambda)
&completefunc = '(a, b) => LambdaComplete1(a, b)'
new | only
setline(1, 'two')
@ -1420,6 +1465,15 @@ func Test_completefunc_callback()
add(g:LambdaComplete2_args, [findstart, base])
return findstart ? 0 : []
}
&completefunc = Fn
new | only
setline(1, 'three')
g:LambdaComplete2_args = []
feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
assert_equal([[1, ''], [0, 'three']], g:LambdaComplete2_args)
bw!
# Test for using a string(variable with a lambda expression)
&completefunc = string(Fn)
new | only
setline(1, 'three')
@ -1462,13 +1516,22 @@ func Test_omnifunc_callback()
" Using a funcref variable to set 'omnifunc'
let Fn = function('MyomniFunc1')
let &omnifunc = Fn
new | only
call setline(1, 'two')
let g:MyomniFunc1_args = []
call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'two']], g:MyomniFunc1_args)
bw!
" Using a string(funcref_variable) to set 'omnifunc'
let Fn = function('MyomniFunc1')
let &omnifunc = string(Fn)
new | only
call setline(1, 'two')
let g:MyomniFunc1_args = []
call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'two']], g:MyomniFunc1_args)
call assert_fails('let &omnifunc = Fn', 'E729:')
bw!
" Test for using a funcref()
@ -1486,13 +1549,22 @@ func Test_omnifunc_callback()
" Using a funcref variable to set 'omnifunc'
let Fn = funcref('MyomniFunc2')
let &omnifunc = Fn
new | only
call setline(1, 'four')
let g:MyomniFunc2_args = []
call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'four']], g:MyomniFunc2_args)
bw!
" Using a string(funcref_variable) to set 'omnifunc'
let Fn = funcref('MyomniFunc2')
let &omnifunc = string(Fn)
new | only
call setline(1, 'four')
let g:MyomniFunc2_args = []
call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'four']], g:MyomniFunc2_args)
call assert_fails('let &omnifunc = Fn', 'E729:')
bw!
" Test for using a lambda function
@ -1509,6 +1581,15 @@ func Test_omnifunc_callback()
bw!
" Set 'omnifunc' to a lambda expression
let &omnifunc = {a, b -> MyomniFunc3(a, b)}
new | only
call setline(1, 'six')
let g:MyomniFunc3_args = []
call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'six']], g:MyomniFunc3_args)
bw!
" Set 'omnifunc' to a string(lambda_expression)
let &omnifunc = '{a, b -> MyomniFunc3(a, b)}'
new | only
call setline(1, 'six')
@ -1519,18 +1600,27 @@ func Test_omnifunc_callback()
" Set 'omnifunc' to a variable with a lambda expression
let Lambda = {a, b -> MyomniFunc3(a, b)}
let &omnifunc = Lambda
new | only
call setline(1, 'seven')
let g:MyomniFunc3_args = []
call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'seven']], g:MyomniFunc3_args)
bw!
" Set 'omnifunc' to a string(variable with a lambda expression)
let Lambda = {a, b -> MyomniFunc3(a, b)}
let &omnifunc = string(Lambda)
new | only
call setline(1, 'seven')
let g:MyomniFunc3_args = []
call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'seven']], g:MyomniFunc3_args)
call assert_fails('let &omnifunc = Lambda', 'E729:')
bw!
" Test for using a lambda function with incorrect return value
let Lambda = {s -> strlen(s)}
let &omnifunc = string(Lambda)
let &omnifunc = Lambda
new | only
call setline(1, 'eight')
call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
@ -1542,7 +1632,7 @@ func Test_omnifunc_callback()
call assert_fails("set omnifunc=function('abc')", "E700:")
call assert_fails("set omnifunc=funcref('abc')", "E700:")
let &omnifunc = "{a -> 'abc'}"
let &omnifunc = {a -> 'abc'}
call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
" Vim9 tests
@ -1567,6 +1657,15 @@ func Test_omnifunc_callback()
add(g:MyomniFunc2_args, [findstart, base])
return findstart ? 0 : []
enddef
&omnifunc = (a, b) => MyomniFunc2(a, b)
new | only
setline(1, 'two')
g:MyomniFunc2_args = []
feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
assert_equal([[1, ''], [0, 'two']], g:MyomniFunc2_args)
bw!
# Test for using a string(lambda)
&omnifunc = '(a, b) => MyomniFunc2(a, b)'
new | only
setline(1, 'two')
@ -1577,6 +1676,15 @@ func Test_omnifunc_callback()
# Test for using a variable with a lambda expression
var Fn: func = (a, b) => MyomniFunc2(a, b)
&omnifunc = Fn
new | only
setline(1, 'three')
g:MyomniFunc2_args = []
feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
assert_equal([[1, ''], [0, 'three']], g:MyomniFunc2_args)
bw!
# Test for using a string(variable with a lambda expression)
&omnifunc = string(Fn)
new | only
setline(1, 'three')
@ -1619,13 +1727,22 @@ func Test_thesaurusfunc_callback()
" Using a funcref variable to set 'thesaurusfunc'
let Fn = function('MytsrFunc1')
let &thesaurusfunc = Fn
new | only
call setline(1, 'two')
let g:MytsrFunc1_args = []
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'two']], g:MytsrFunc1_args)
bw!
" Using a string(funcref_variable) to set 'thesaurusfunc'
let Fn = function('MytsrFunc1')
let &thesaurusfunc = string(Fn)
new | only
call setline(1, 'two')
let g:MytsrFunc1_args = []
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'two']], g:MytsrFunc1_args)
call assert_fails('let &thesaurusfunc = Fn', 'E729:')
bw!
" Test for using a funcref()
@ -1643,13 +1760,22 @@ func Test_thesaurusfunc_callback()
" Using a funcref variable to set 'thesaurusfunc'
let Fn = funcref('MytsrFunc2')
let &thesaurusfunc = Fn
new | only
call setline(1, 'four')
let g:MytsrFunc2_args = []
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'four']], g:MytsrFunc2_args)
bw!
" Using a string(funcref_variable) to set 'thesaurusfunc'
let Fn = funcref('MytsrFunc2')
let &thesaurusfunc = string(Fn)
new | only
call setline(1, 'four')
let g:MytsrFunc2_args = []
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'four']], g:MytsrFunc2_args)
call assert_fails('let &thesaurusfunc = Fn', 'E729:')
bw!
" Test for using a lambda function
@ -1666,6 +1792,15 @@ func Test_thesaurusfunc_callback()
bw!
" Set 'thesaurusfunc' to a lambda expression
let &thesaurusfunc = {a, b -> MytsrFunc3(a, b)}
new | only
call setline(1, 'six')
let g:MytsrFunc3_args = []
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'six']], g:MytsrFunc3_args)
bw!
" Set 'thesaurusfunc' to a string(lambda expression)
let &thesaurusfunc = '{a, b -> MytsrFunc3(a, b)}'
new | only
call setline(1, 'six')
@ -1676,18 +1811,27 @@ func Test_thesaurusfunc_callback()
" Set 'thesaurusfunc' to a variable with a lambda expression
let Lambda = {a, b -> MytsrFunc3(a, b)}
let &thesaurusfunc = Lambda
new | only
call setline(1, 'seven')
let g:MytsrFunc3_args = []
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'seven']], g:MytsrFunc3_args)
bw!
" Set 'thesaurusfunc' to a string(variable with a lambda expression)
let Lambda = {a, b -> MytsrFunc3(a, b)}
let &thesaurusfunc = string(Lambda)
new | only
call setline(1, 'seven')
let g:MytsrFunc3_args = []
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
call assert_equal([[1, ''], [0, 'seven']], g:MytsrFunc3_args)
call assert_fails('let &thesaurusfunc = Lambda', 'E729:')
bw!
" Test for using a lambda function with incorrect return value
let Lambda = {s -> strlen(s)}
let &thesaurusfunc = string(Lambda)
let &thesaurusfunc = Lambda
new | only
call setline(1, 'eight')
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
@ -1699,7 +1843,7 @@ func Test_thesaurusfunc_callback()
call assert_fails("set thesaurusfunc=function('abc')", "E700:")
call assert_fails("set thesaurusfunc=funcref('abc')", "E700:")
let &thesaurusfunc = "{a -> 'abc'}"
let &thesaurusfunc = {a -> 'abc'}
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
" Vim9 tests
@ -1724,6 +1868,15 @@ func Test_thesaurusfunc_callback()
add(g:MytsrFunc2_args, [findstart, base])
return findstart ? 0 : []
enddef
&thesaurusfunc = (a, b) => MytsrFunc2(a, b)
new | only
setline(1, 'two')
g:MytsrFunc2_args = []
feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
assert_equal([[1, ''], [0, 'two']], g:MytsrFunc2_args)
bw!
# Test for using a string(lambda)
&thesaurusfunc = '(a, b) => MytsrFunc2(a, b)'
new | only
setline(1, 'two')
@ -1734,6 +1887,15 @@ func Test_thesaurusfunc_callback()
# Test for using a variable with a lambda expression
var Fn: func = (a, b) => MytsrFunc2(a, b)
&thesaurusfunc = Fn
new | only
setline(1, 'three')
g:MytsrFunc2_args = []
feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
assert_equal([[1, ''], [0, 'three']], g:MytsrFunc2_args)
bw!
# Test for using a string(variable with a lambda expression)
&thesaurusfunc = string(Fn)
new | only
setline(1, 'three')

View File

@ -138,12 +138,19 @@ func Test_tagfunc_callback()
" Using a funcref variable to set 'tagfunc'
let Fn = function('MytagFunc1')
let &tagfunc = Fn
new | only
let g:MytagFunc1_args = []
call assert_fails('tag a12', 'E433:')
call assert_equal(['a12', '', {}], g:MytagFunc1_args)
" Using a string(funcref_variable) to set 'tagfunc'
let Fn = function('MytagFunc1')
let &tagfunc = string(Fn)
new | only
let g:MytagFunc1_args = []
call assert_fails('tag a12', 'E433:')
call assert_equal(['a12', '', {}], g:MytagFunc1_args)
call assert_fails('let &tagfunc = Fn', 'E729:')
" Test for using a funcref()
func MytagFunc2(pat, flags, info)
@ -158,12 +165,19 @@ func Test_tagfunc_callback()
" Using a funcref variable to set 'tagfunc'
let Fn = funcref('MytagFunc2')
let &tagfunc = Fn
new | only
let g:MytagFunc2_args = []
call assert_fails('tag a14', 'E433:')
call assert_equal(['a14', '', {}], g:MytagFunc2_args)
" Using a string(funcref_variable) to set 'tagfunc'
let Fn = funcref('MytagFunc2')
let &tagfunc = string(Fn)
new | only
let g:MytagFunc2_args = []
call assert_fails('tag a14', 'E433:')
call assert_equal(['a14', '', {}], g:MytagFunc2_args)
call assert_fails('let &tagfunc = Fn', 'E729:')
" Test for using a script local function
set tagfunc=<SID>ScriptLocalTagFunc
@ -174,6 +188,14 @@ func Test_tagfunc_callback()
" Test for using a script local funcref variable
let Fn = function("s:ScriptLocalTagFunc")
let &tagfunc= Fn
new | only
let g:ScriptLocalFuncArgs = []
call assert_fails('tag a16', 'E433:')
call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs)
" Test for using a string(script local funcref variable)
let Fn = function("s:ScriptLocalTagFunc")
let &tagfunc= string(Fn)
new | only
let g:ScriptLocalFuncArgs = []
@ -192,6 +214,13 @@ func Test_tagfunc_callback()
call assert_equal(['a17', '', {}], g:MytagFunc3_args)
" Set 'tagfunc' to a lambda expression
let &tagfunc = {a, b, c -> MytagFunc3(a, b, c)}
new | only
let g:MytagFunc3_args = []
call assert_fails('tag a18', 'E433:')
call assert_equal(['a18', '', {}], g:MytagFunc3_args)
" Set 'tagfunc' to a string(lambda expression)
let &tagfunc = '{a, b, c -> MytagFunc3(a, b, c)}'
new | only
let g:MytagFunc3_args = []
@ -200,12 +229,19 @@ func Test_tagfunc_callback()
" Set 'tagfunc' to a variable with a lambda expression
let Lambda = {a, b, c -> MytagFunc3(a, b, c)}
let &tagfunc = Lambda
new | only
let g:MytagFunc3_args = []
call assert_fails("tag a19", "E433:")
call assert_equal(['a19', '', {}], g:MytagFunc3_args)
" Set 'tagfunc' to a string(variable with a lambda expression)
let Lambda = {a, b, c -> MytagFunc3(a, b, c)}
let &tagfunc = string(Lambda)
new | only
let g:MytagFunc3_args = []
call assert_fails("tag a19", "E433:")
call assert_equal(['a19', '', {}], g:MytagFunc3_args)
call assert_fails('let &tagfunc = Lambda', 'E729:')
" Test for using a lambda function with incorrect return value
let Lambda = {s -> strlen(s)}
@ -242,6 +278,13 @@ func Test_tagfunc_callback()
g:MytagFunc2_args = [pat, flags, info]
return null
enddef
&tagfunc = (a, b, c) => MytagFunc2(a, b, c)
new | only
g:MytagFunc2_args = []
assert_fails('tag a20', 'E433:')
assert_equal(['a20', '', {}], g:MytagFunc2_args)
# Test for using a string(lambda)
&tagfunc = '(a, b, c) => MytagFunc2(a, b, c)'
new | only
g:MytagFunc2_args = []
@ -250,6 +293,13 @@ func Test_tagfunc_callback()
# Test for using a variable with a lambda expression
var Fn: func = (a, b, c) => MytagFunc2(a, b, c)
&tagfunc = Fn
new | only
g:MytagFunc2_args = []
assert_fails('tag a30', 'E433:')
assert_equal(['a30', '', {}], g:MytagFunc2_args)
# Test for using a variable with a lambda expression
&tagfunc = string(Fn)
new | only
g:MytagFunc2_args = []