To: vim_dev@googlegroups.com Subject: Patch 8.2.3725 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3725 Problem: Cannot use a lambda for 'completefunc' and 'omnifunc'. Solution: Implement lambda support. (Yegappan Lakshmanan, closes #9257) Files: runtime/doc/options.txt, src/buffer.c, src/insexpand.c, src/option.c, src/optionstr.c, src/proto/insexpand.pro, src/proto/tag.pro, src/proto/userfunc.pro, src/structs.h, src/tag.c, src/userfunc.c, src/testdir/test_ins_complete.vim, src/testdir/test_tagfunc.vim *** ../vim-8.2.3724/runtime/doc/options.txt 2021-12-01 10:30:02.364416798 +0000 --- runtime/doc/options.txt 2021-12-03 10:31:50.669436260 +0000 *************** *** 366,373 **** *option-value-function* Some options ('completefunc', 'imactivatefunc', 'imstatusfunc', 'omnifunc', ! 'operatorfunc', 'quickfixtextfunc' and 'tagfunc') are set to a function name ! or a function reference or a lambda function. Examples: > set opfunc=MyOpFunc set opfunc=function('MyOpFunc') --- 373,380 ---- *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: > set opfunc=MyOpFunc set opfunc=function('MyOpFunc') *************** *** 1925,1931 **** This option specifies a function to be used for Insert mode completion with CTRL-X CTRL-U. |i_CTRL-X_CTRL-U| See |complete-functions| for an explanation of how the function is ! invoked and what it should return. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. --- 1942,1950 ---- This option specifies a function to be used for Insert mode completion with CTRL-X CTRL-U. |i_CTRL-X_CTRL-U| See |complete-functions| for an explanation of how the function is ! invoked and what it should return. The value can be the name of a ! function, a |lambda| or a |Funcref|. See |option-value-function| for ! more information. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. *************** *** 5600,5606 **** This option specifies a function to be used for Insert mode omni completion with CTRL-X CTRL-O. |i_CTRL-X_CTRL-O| See |complete-functions| for an explanation of how the function is ! invoked and what it should return. This option is usually set by a filetype plugin: |:filetype-plugin-on| This option cannot be set from a |modeline| or in the |sandbox|, for --- 5621,5629 ---- This option specifies a function to be used for Insert mode omni completion with CTRL-X CTRL-O. |i_CTRL-X_CTRL-O| See |complete-functions| for an explanation of how the function is ! invoked and what it should return. The value can be the name of a ! function, a |lambda| or a |Funcref|. See |option-value-function| for ! more information. This option is usually set by a filetype plugin: |:filetype-plugin-on| This option cannot be set from a |modeline| or in the |sandbox|, for *************** *** 7577,7582 **** --- 7604,7611 ---- 'switchbuf' 'swb' string (default "") global This option controls the behavior when switching between buffers. + Mostly for |quickfix| commands some values are also used for other + commands, as mentioned below. Possible values (comma separated list): useopen If included, jump to the first open window that contains the specified buffer (if there is one). *************** *** 8053,8058 **** --- 8082,8089 ---- feature} This option specifies a function to be used for thesaurus completion with CTRL-X CTRL-T. |i_CTRL-X_CTRL-T| See |compl-thesaurusfunc|. + The value can be the name of a function, a |lambda| or a |Funcref|. + See |option-value-function| for more information. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. *** ../vim-8.2.3724/src/buffer.c 2021-11-29 20:39:06.670101630 +0000 --- src/buffer.c 2021-12-03 10:31:50.669436260 +0000 *************** *** 2326,2333 **** --- 2326,2336 ---- clear_string_option(&buf->b_p_cpt); #ifdef FEAT_COMPL_FUNC clear_string_option(&buf->b_p_cfu); + free_callback(&buf->b_cfu_cb); clear_string_option(&buf->b_p_ofu); + free_callback(&buf->b_ofu_cb); clear_string_option(&buf->b_p_tsrfu); + free_callback(&buf->b_tsrfu_cb); #endif #ifdef FEAT_QUICKFIX clear_string_option(&buf->b_p_gp); *** ../vim-8.2.3724/src/insexpand.c 2021-12-01 10:30:02.364416798 +0000 --- src/insexpand.c 2021-12-03 10:41:42.721945510 +0000 *************** *** 2237,2242 **** --- 2237,2349 ---- } #ifdef FEAT_COMPL_FUNC + + # ifdef FEAT_EVAL + static callback_T cfu_cb; // 'completefunc' callback function + static callback_T ofu_cb; // 'omnifunc' callback function + static callback_T tsrfu_cb; // 'thesaurusfunc' callback function + # endif + + /* + * Copy a global callback function to a buffer local callback. + */ + static void + copy_global_to_buflocal_cb(callback_T *globcb, callback_T *bufcb) + { + free_callback(bufcb); + if (globcb->cb_name != NULL && *globcb->cb_name != NUL) + copy_callback(bufcb, globcb); + } + + /* + * Parse the 'completefunc' option value and set the callback function. + * Invoked when the 'completefunc' option is set. The option value can be a + * name of a function (string), or function() or funcref() or a + * lambda expression. + */ + int + set_completefunc_option(void) + { + int retval; + + retval = option_set_callback_func(curbuf->b_p_cfu, &cfu_cb); + if (retval == OK) + set_buflocal_cfu_callback(curbuf); + + return retval; + } + + /* + * Copy the global 'completefunc' callback function to the buffer-local + * 'completefunc' callback for 'buf'. + */ + void + set_buflocal_cfu_callback(buf_T *buf UNUSED) + { + # ifdef FEAT_EVAL + copy_global_to_buflocal_cb(&cfu_cb, &buf->b_cfu_cb); + # endif + } + + /* + * Parse the 'omnifunc' option value and set the callback function. + * Invoked when the 'omnifunc' option is set. The option value can be a + * name of a function (string), or function() or funcref() or a + * lambda expression. + */ + int + set_omnifunc_option(void) + { + int retval; + + retval = option_set_callback_func(curbuf->b_p_ofu, &ofu_cb); + if (retval == OK) + set_buflocal_ofu_callback(curbuf); + + return retval; + } + + /* + * Copy the global 'omnifunc' callback function to the buffer-local 'omnifunc' + * callback for 'buf'. + */ + void + set_buflocal_ofu_callback(buf_T *buf UNUSED) + { + # ifdef FEAT_EVAL + copy_global_to_buflocal_cb(&ofu_cb, &buf->b_ofu_cb); + # endif + } + + /* + * Parse the 'thesaurusfunc' option value and set the callback function. + * Invoked when the 'thesaurusfunc' option is set. The option value can be a + * name of a function (string), or function() or funcref() or a + * lambda expression. + */ + int + set_thesaurusfunc_option(void) + { + int retval; + + if (*curbuf->b_p_tsrfu != NUL) + { + // buffer-local option set + free_callback(&curbuf->b_tsrfu_cb); + retval = option_set_callback_func(curbuf->b_p_tsrfu, + &curbuf->b_tsrfu_cb); + } + else + { + // global option set + free_callback(&tsrfu_cb); + retval = option_set_callback_func(p_tsrfu, &tsrfu_cb); + } + + return retval; + } + + /* * Get the user-defined completion function name for completion 'type' */ *************** *** 2257,2262 **** --- 2364,2383 ---- } /* + * Get the callback to use for insert mode completion. + */ + callback_T * + get_insert_callback(int type) + { + if (type == CTRL_X_FUNCTION) + return &curbuf->b_cfu_cb; + if (type == CTRL_X_OMNI) + return &curbuf->b_ofu_cb; + // CTRL_X_THESAURUS + return (*curbuf->b_p_tsrfu != NUL) ? &curbuf->b_tsrfu_cb : &tsrfu_cb; + } + + /* * Execute user defined complete function 'completefunc', 'omnifunc' or * 'thesaurusfunc', and get matches in "matches". * "type" is either CTRL_X_OMNI or CTRL_X_FUNCTION or CTRL_X_THESAURUS. *************** *** 2269,2276 **** --- 2390,2399 ---- typval_T args[3]; char_u *funcname; pos_T pos; + callback_T *cb; typval_T rettv; int save_State = State; + int retval; funcname = get_complete_funcname(type); if (*funcname == NUL) *************** *** 2289,2296 **** // Insert mode in another buffer. ++textwinlock; // Call a function, which returns a list or dict. ! if (call_vim_function(funcname, 2, args, &rettv) == OK) { switch (rettv.v_type) { --- 2412,2422 ---- // Insert mode in another buffer. ++textwinlock; + cb = get_insert_callback(type); + retval = call_callback(cb, 0, &rettv, 2, args); + // Call a function, which returns a list or dict. ! if (retval == OK) { switch (rettv.v_type) { *************** *** 3971,3976 **** --- 4097,4103 ---- char_u *funcname; pos_T pos; int save_State = State; + callback_T *cb; // Call 'completefunc' or 'omnifunc' and get pattern length as a // string *************** *** 3991,3997 **** args[2].v_type = VAR_UNKNOWN; pos = curwin->w_cursor; ++textwinlock; ! col = call_func_retnr(funcname, 2, args); --textwinlock; State = save_State; --- 4118,4125 ---- args[2].v_type = VAR_UNKNOWN; pos = curwin->w_cursor; ++textwinlock; ! cb = get_insert_callback(ctrl_x_mode); ! col = call_callback_retnr(cb, 2, args); --textwinlock; State = save_State; *************** *** 4339,4344 **** --- 4467,4477 ---- free_insexpand_stuff(void) { VIM_CLEAR(compl_orig_text); + # ifdef FEAT_EVAL + free_callback(&cfu_cb); + free_callback(&ofu_cb); + free_callback(&tsrfu_cb); + # endif } #endif *** ../vim-8.2.3724/src/option.c 2021-12-01 10:30:02.368416797 +0000 --- src/option.c 2021-12-03 10:31:50.673436268 +0000 *************** *** 5927,5939 **** #ifdef FEAT_COMPL_FUNC buf->b_p_cfu = vim_strsave(p_cfu); COPY_OPT_SCTX(buf, BV_CFU); buf->b_p_ofu = vim_strsave(p_ofu); COPY_OPT_SCTX(buf, BV_OFU); #endif #ifdef FEAT_EVAL buf->b_p_tfu = vim_strsave(p_tfu); COPY_OPT_SCTX(buf, BV_TFU); ! buf_set_tfu_callback(buf); #endif buf->b_p_sts = p_sts; COPY_OPT_SCTX(buf, BV_STS); --- 5927,5941 ---- #ifdef FEAT_COMPL_FUNC buf->b_p_cfu = vim_strsave(p_cfu); COPY_OPT_SCTX(buf, BV_CFU); + set_buflocal_cfu_callback(buf); buf->b_p_ofu = vim_strsave(p_ofu); COPY_OPT_SCTX(buf, BV_OFU); + set_buflocal_ofu_callback(buf); #endif #ifdef FEAT_EVAL buf->b_p_tfu = vim_strsave(p_tfu); COPY_OPT_SCTX(buf, BV_TFU); ! set_buflocal_tfu_callback(buf); #endif buf->b_p_sts = p_sts; COPY_OPT_SCTX(buf, BV_STS); *** ../vim-8.2.3724/src/optionstr.c 2021-11-29 20:39:06.678101624 +0000 --- src/optionstr.c 2021-12-03 10:31:50.673436268 +0000 *************** *** 2307,2312 **** --- 2307,2335 ---- # endif #endif + #ifdef FEAT_COMPL_FUNC + // 'completefunc' + else if (gvarp == &p_cfu) + { + if (set_completefunc_option() == FAIL) + errmsg = e_invarg; + } + + // 'omnifunc' + else if (gvarp == &p_ofu) + { + if (set_omnifunc_option() == FAIL) + errmsg = e_invarg; + } + + // 'thesaurusfunc' + else if (gvarp == &p_tsrfu) + { + if (set_thesaurusfunc_option() == FAIL) + errmsg = e_invarg; + } + #endif + // 'operatorfunc' else if (varp == &p_opfunc) { *** ../vim-8.2.3724/src/proto/insexpand.pro 2019-12-12 11:55:25.000000000 +0000 --- src/proto/insexpand.pro 2021-12-03 10:46:45.701780370 +0000 *************** *** 39,44 **** --- 39,50 ---- void ins_compl_addleader(int c); void ins_compl_addfrommatch(void); int ins_compl_prep(int c); + int set_completefunc_option(void); + void set_buflocal_cfu_callback(buf_T *buf); + int set_omnifunc_option(void); + void set_buflocal_ofu_callback(buf_T *buf); + int set_thesaurusfunc_option(void); + callback_T *get_insert_callback(int type); void f_complete(typval_T *argvars, typval_T *rettv); void f_complete_add(typval_T *argvars, typval_T *rettv); void f_complete_check(typval_T *argvars, typval_T *rettv); *** ../vim-8.2.3724/src/proto/tag.pro 2021-11-24 16:32:50.720422469 +0000 --- src/proto/tag.pro 2021-12-03 10:46:38.893785739 +0000 *************** *** 1,7 **** /* tag.c */ int set_tagfunc_option(void); void free_tagfunc_option(void); ! void buf_set_tfu_callback(buf_T *buf); int do_tag(char_u *tag, int type, int count, int forceit, int verbose); void tag_freematch(void); void do_tags(exarg_T *eap); --- 1,7 ---- /* tag.c */ int set_tagfunc_option(void); void free_tagfunc_option(void); ! void set_buflocal_tfu_callback(buf_T *buf); int do_tag(char_u *tag, int type, int count, int forceit, int verbose); void tag_freematch(void); void do_tags(exarg_T *eap); *** ../vim-8.2.3724/src/proto/userfunc.pro 2021-11-30 18:25:04.980458299 +0000 --- src/proto/userfunc.pro 2021-12-03 10:31:50.673436268 +0000 *************** *** 28,33 **** --- 28,34 ---- int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv); int get_callback_depth(void); int call_callback(callback_T *callback, int len, typval_T *rettv, int argcount, typval_T *argvars); + varnumber_T call_callback_retnr(callback_T *callback, int argcount, typval_T *argvars); void user_func_error(int error, char_u *name); int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, funcexe_T *funcexe); char_u *printable_func_name(ufunc_T *fp); *** ../vim-8.2.3724/src/structs.h 2021-11-28 22:00:08.148081412 +0000 --- src/structs.h 2021-12-03 10:31:50.673436268 +0000 *************** *** 2876,2882 **** --- 2876,2884 ---- #endif #ifdef FEAT_COMPL_FUNC char_u *b_p_cfu; // 'completefunc' + callback_T b_cfu_cb; // 'completefunc' callback char_u *b_p_ofu; // 'omnifunc' + callback_T b_ofu_cb; // 'omnifunc' callback #endif #ifdef FEAT_EVAL char_u *b_p_tfu; // 'tagfunc' option value *************** *** 2982,2987 **** --- 2984,2990 ---- char_u *b_p_tsr; // 'thesaurus' local value #ifdef FEAT_COMPL_FUNC char_u *b_p_tsrfu; // 'thesaurusfunc' local value + callback_T b_tsrfu_cb; // 'thesaurusfunc' callback #endif long b_p_ul; // 'undolevels' local value #ifdef FEAT_PERSISTENT_UNDO *** ../vim-8.2.3724/src/tag.c 2021-11-24 16:32:50.720422469 +0000 --- src/tag.c 2021-12-03 10:31:50.673436268 +0000 *************** *** 115,121 **** * a function (string), or function() or funcref() or a lambda. */ int ! set_tagfunc_option() { #ifdef FEAT_EVAL free_callback(&tfu_cb); --- 115,121 ---- * a function (string), or function() or funcref() or a lambda. */ int ! set_tagfunc_option(void) { #ifdef FEAT_EVAL free_callback(&tfu_cb); *************** *** 148,154 **** * callback for 'buf'. */ void ! buf_set_tfu_callback(buf_T *buf UNUSED) { #ifdef FEAT_EVAL free_callback(&buf->b_tfu_cb); --- 148,154 ---- * callback for 'buf'. */ void ! set_buflocal_tfu_callback(buf_T *buf UNUSED) { #ifdef FEAT_EVAL free_callback(&buf->b_tfu_cb); *** ../vim-8.2.3724/src/userfunc.c 2021-11-30 18:25:04.980458299 +0000 --- src/userfunc.c 2021-12-03 10:31:50.673436268 +0000 *************** *** 3169,3174 **** --- 3169,3197 ---- } /* + * call the 'callback' function and return the result as a number. + * Returns -1 when calling the function fails. Uses argv[0] to argv[argc - 1] + * for the function arguments. argv[argc] should have type VAR_UNKNOWN. + */ + varnumber_T + call_callback_retnr( + callback_T *callback, + int argcount, // number of "argvars" + typval_T *argvars) // vars for arguments, must have "argcount" + // PLUS ONE elements! + { + typval_T rettv; + varnumber_T retval; + + if (call_callback(callback, 0, &rettv, argcount, argvars) == FAIL) + return -1; + + retval = tv_get_number_chk(&rettv, NULL); + clear_tv(&rettv); + return retval; + } + + /* * Give an error message for the result of a function. * Nothing if "error" is FCERR_NONE. */ *** ../vim-8.2.3724/src/testdir/test_ins_complete.vim 2021-11-21 11:35:59.456938797 +0000 --- src/testdir/test_ins_complete.vim 2021-12-03 10:31:50.673436268 +0000 *************** *** 2,7 **** --- 2,8 ---- source screendump.vim source check.vim + source vim9.vim " Test for insert expansion func Test_ins_complete() *************** *** 867,870 **** --- 868,1370 ---- close! endfunc + " Test for different ways of setting the 'completefunc' option + func Test_completefunc_callback() + " Test for using a function() + func MycompleteFunc1(findstart, base) + call add(g:MycompleteFunc1_args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set completefunc=function('MycompleteFunc1') + new | only + call setline(1, 'one') + let g:MycompleteFunc1_args = [] + call feedkeys("A\\\", 'x') + call assert_equal([[1, ''], [0, 'one']], g:MycompleteFunc1_args) + bw! + + " Using a 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\\\", 'x') + call assert_equal([[1, ''], [0, 'two']], g:MycompleteFunc1_args) + call assert_fails('let &completefunc = Fn', 'E729:') + bw! + + " Test for using a funcref() + func MycompleteFunc2(findstart, base) + call add(g:MycompleteFunc2_args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set completefunc=funcref('MycompleteFunc2') + new | only + call setline(1, 'three') + let g:MycompleteFunc2_args = [] + call feedkeys("A\\\", 'x') + call assert_equal([[1, ''], [0, 'three']], g:MycompleteFunc2_args) + bw! + + " Using a 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\\\", 'x') + call assert_equal([[1, ''], [0, 'four']], g:MycompleteFunc2_args) + call assert_fails('let &completefunc = Fn', 'E729:') + bw! + + " Test for using a lambda function + func MycompleteFunc3(findstart, base) + call add(g:MycompleteFunc3_args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set completefunc={a,\ b,\ ->\ MycompleteFunc3(a,\ b,)} + new | only + call setline(1, 'five') + let g:MycompleteFunc3_args = [] + call feedkeys("A\\\", 'x') + call assert_equal([[1, ''], [0, 'five']], g:MycompleteFunc3_args) + 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\\\", 'x') + call assert_equal([[1, ''], [0, 'six']], g:MycompleteFunc3_args) + bw! + + " Set 'completefunc' to a 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\\\", '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) + new | only + call setline(1, 'eight') + call feedkeys("A\\\", 'x') + bw! + + " Test for clearing the 'completefunc' option + set completefunc='' + set completefunc& + + call assert_fails("set completefunc=function('abc')", "E700:") + call assert_fails("set completefunc=funcref('abc')", "E700:") + let &completefunc = "{a -> 'abc'}" + call feedkeys("A\\\", 'x') + + " Vim9 tests + let lines =<< trim END + vim9script + + # Test for using function() + def MycompleteFunc1(findstart: number, base: string): any + add(g:MycompleteFunc1_args, [findstart, base]) + return findstart ? 0 : [] + enddef + set completefunc=function('MycompleteFunc1') + new | only + setline(1, 'one') + g:MycompleteFunc1_args = [] + feedkeys("A\\\", 'x') + assert_equal([[1, ''], [0, 'one']], g:MycompleteFunc1_args) + bw! + + # Test for using a lambda + def MycompleteFunc2(findstart: number, base: string): any + add(g:MycompleteFunc2_args, [findstart, base]) + return findstart ? 0 : [] + enddef + &completefunc = '(a, b) => MycompleteFunc2(a, b)' + new | only + setline(1, 'two') + g:MycompleteFunc2_args = [] + feedkeys("A\\\", 'x') + assert_equal([[1, ''], [0, 'two']], g:MycompleteFunc2_args) + bw! + + # Test for using a variable with a lambda expression + var Fn: func = (a, b) => MycompleteFunc2(a, b) + &completefunc = string(Fn) + new | only + setline(1, 'three') + g:MycompleteFunc2_args = [] + feedkeys("A\\\", 'x') + assert_equal([[1, ''], [0, 'three']], g:MycompleteFunc2_args) + bw! + END + call CheckScriptSuccess(lines) + + " Using Vim9 lambda expression in legacy context should fail + set completefunc=(a,\ b)\ =>\ g:MycompleteFunc2(a,\ b) + new | only + let g:MycompleteFunc2_args = [] + call assert_fails('call feedkeys("A\\\", "x")', 'E117:') + call assert_equal([], g:MycompleteFunc2_args) + + " cleanup + delfunc MycompleteFunc1 + delfunc MycompleteFunc2 + delfunc MycompleteFunc3 + set completefunc& + %bw! + endfunc + + " Test for different ways of setting the 'omnifunc' option + func Test_omnifunc_callback() + " Test for using a function() + func MyomniFunc1(findstart, base) + call add(g:MyomniFunc1_args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set omnifunc=function('MyomniFunc1') + new | only + call setline(1, 'one') + let g:MyomniFunc1_args = [] + call feedkeys("A\\\", 'x') + call assert_equal([[1, ''], [0, 'one']], g:MyomniFunc1_args) + bw! + + " Using a 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\\\", 'x') + call assert_equal([[1, ''], [0, 'two']], g:MyomniFunc1_args) + call assert_fails('let &omnifunc = Fn', 'E729:') + bw! + + " Test for using a funcref() + func MyomniFunc2(findstart, base) + call add(g:MyomniFunc2_args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set omnifunc=funcref('MyomniFunc2') + new | only + call setline(1, 'three') + let g:MyomniFunc2_args = [] + call feedkeys("A\\\", 'x') + call assert_equal([[1, ''], [0, 'three']], g:MyomniFunc2_args) + bw! + + " Using a 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\\\", 'x') + call assert_equal([[1, ''], [0, 'four']], g:MyomniFunc2_args) + call assert_fails('let &omnifunc = Fn', 'E729:') + bw! + + " Test for using a lambda function + func MyomniFunc3(findstart, base) + call add(g:MyomniFunc3_args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set omnifunc={a,\ b,\ ->\ MyomniFunc3(a,\ b,)} + new | only + call setline(1, 'five') + let g:MyomniFunc3_args = [] + call feedkeys("A\\\", 'x') + call assert_equal([[1, ''], [0, 'five']], g:MyomniFunc3_args) + 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\\\", 'x') + call assert_equal([[1, ''], [0, 'six']], g:MyomniFunc3_args) + bw! + + " Set 'omnifunc' to a 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\\\", '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) + new | only + call setline(1, 'eight') + call feedkeys("A\\\", 'x') + bw! + + " Test for clearing the 'omnifunc' option + set omnifunc='' + set omnifunc& + + call assert_fails("set omnifunc=function('abc')", "E700:") + call assert_fails("set omnifunc=funcref('abc')", "E700:") + let &omnifunc = "{a -> 'abc'}" + call feedkeys("A\\\", 'x') + + " Vim9 tests + let lines =<< trim END + vim9script + + # Test for using function() + def MyomniFunc1(findstart: number, base: string): any + add(g:MyomniFunc1_args, [findstart, base]) + return findstart ? 0 : [] + enddef + set omnifunc=function('MyomniFunc1') + new | only + setline(1, 'one') + g:MyomniFunc1_args = [] + feedkeys("A\\\", 'x') + assert_equal([[1, ''], [0, 'one']], g:MyomniFunc1_args) + bw! + + # Test for using a lambda + def MyomniFunc2(findstart: number, base: string): any + 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\\\", 'x') + assert_equal([[1, ''], [0, 'two']], g:MyomniFunc2_args) + bw! + + # Test for using a variable with a lambda expression + var Fn: func = (a, b) => MyomniFunc2(a, b) + &omnifunc = string(Fn) + new | only + setline(1, 'three') + g:MyomniFunc2_args = [] + feedkeys("A\\\", 'x') + assert_equal([[1, ''], [0, 'three']], g:MyomniFunc2_args) + bw! + END + call CheckScriptSuccess(lines) + + " Using Vim9 lambda expression in legacy context should fail + set omnifunc=(a,\ b)\ =>\ g:MyomniFunc2(a,\ b) + new | only + let g:MyomniFunc2_args = [] + call assert_fails('call feedkeys("A\\\", "x")', 'E117:') + call assert_equal([], g:MyomniFunc2_args) + + " cleanup + delfunc MyomniFunc1 + delfunc MyomniFunc2 + delfunc MyomniFunc3 + set omnifunc& + %bw! + endfunc + + " Test for different ways of setting the 'thesaurusfunc' option + func Test_thesaurusfunc_callback() + " Test for using a function() + func MytsrFunc1(findstart, base) + call add(g:MytsrFunc1_args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set thesaurusfunc=function('MytsrFunc1') + new | only + call setline(1, 'one') + let g:MytsrFunc1_args = [] + call feedkeys("A\\\", 'x') + call assert_equal([[1, ''], [0, 'one']], g:MytsrFunc1_args) + bw! + + " Using a 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\\\", 'x') + call assert_equal([[1, ''], [0, 'two']], g:MytsrFunc1_args) + call assert_fails('let &thesaurusfunc = Fn', 'E729:') + bw! + + " Test for using a funcref() + func MytsrFunc2(findstart, base) + call add(g:MytsrFunc2_args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set thesaurusfunc=funcref('MytsrFunc2') + new | only + call setline(1, 'three') + let g:MytsrFunc2_args = [] + call feedkeys("A\\\", 'x') + call assert_equal([[1, ''], [0, 'three']], g:MytsrFunc2_args) + bw! + + " Using a 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\\\", 'x') + call assert_equal([[1, ''], [0, 'four']], g:MytsrFunc2_args) + call assert_fails('let &thesaurusfunc = Fn', 'E729:') + bw! + + " Test for using a lambda function + func MytsrFunc3(findstart, base) + call add(g:MytsrFunc3_args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set thesaurusfunc={a,\ b,\ ->\ MytsrFunc3(a,\ b,)} + new | only + call setline(1, 'five') + let g:MytsrFunc3_args = [] + call feedkeys("A\\\", 'x') + call assert_equal([[1, ''], [0, 'five']], g:MytsrFunc3_args) + 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\\\", 'x') + call assert_equal([[1, ''], [0, 'six']], g:MytsrFunc3_args) + bw! + + " Set 'thesaurusfunc' to a 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\\\", '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) + new | only + call setline(1, 'eight') + call feedkeys("A\\\", 'x') + bw! + + " Test for clearing the 'thesaurusfunc' option + set thesaurusfunc='' + set thesaurusfunc& + + call assert_fails("set thesaurusfunc=function('abc')", "E700:") + call assert_fails("set thesaurusfunc=funcref('abc')", "E700:") + let &thesaurusfunc = "{a -> 'abc'}" + call feedkeys("A\\\", 'x') + + " Vim9 tests + let lines =<< trim END + vim9script + + # Test for using function() + def MytsrFunc1(findstart: number, base: string): any + add(g:MytsrFunc1_args, [findstart, base]) + return findstart ? 0 : [] + enddef + set thesaurusfunc=function('MytsrFunc1') + new | only + setline(1, 'one') + g:MytsrFunc1_args = [] + feedkeys("A\\\", 'x') + assert_equal([[1, ''], [0, 'one']], g:MytsrFunc1_args) + bw! + + # Test for using a lambda + def MytsrFunc2(findstart: number, base: string): any + 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\\\", 'x') + assert_equal([[1, ''], [0, 'two']], g:MytsrFunc2_args) + bw! + + # Test for using a variable with a lambda expression + var Fn: func = (a, b) => MytsrFunc2(a, b) + &thesaurusfunc = string(Fn) + new | only + setline(1, 'three') + g:MytsrFunc2_args = [] + feedkeys("A\\\", 'x') + assert_equal([[1, ''], [0, 'three']], g:MytsrFunc2_args) + bw! + END + call CheckScriptSuccess(lines) + + " Using Vim9 lambda expression in legacy context should fail + set thesaurusfunc=(a,\ b)\ =>\ g:MytsrFunc2(a,\ b) + new | only + let g:MytsrFunc2_args = [] + call assert_fails('call feedkeys("A\\\", "x")', 'E117:') + call assert_equal([], g:MytsrFunc2_args) + bw! + + " Use a buffer-local value and a global value + func MytsrFunc4(findstart, base) + call add(g:MytsrFunc4_args, [a:findstart, a:base]) + return a:findstart ? 0 : ['sunday'] + endfunc + set thesaurusfunc& + setlocal thesaurusfunc=function('MytsrFunc4') + call setline(1, 'sun') + let g:MytsrFunc4_args = [] + call feedkeys("A\\\", "x") + call assert_equal('sunday', getline(1)) + call assert_equal([[1, ''], [0, 'sun']], g:MytsrFunc4_args) + new + call setline(1, 'sun') + let g:MytsrFunc4_args = [] + call feedkeys("A\\\", "x") + call assert_equal('sun', getline(1)) + call assert_equal([], g:MytsrFunc4_args) + set thesaurusfunc=function('MytsrFunc1') + wincmd w + call setline(1, 'sun') + let g:MytsrFunc4_args = [] + call feedkeys("A\\\", "x") + call assert_equal('sunday', getline(1)) + call assert_equal([[1, ''], [0, 'sun']], g:MytsrFunc4_args) + + " cleanup + set thesaurusfunc& + delfunc MytsrFunc1 + delfunc MytsrFunc2 + delfunc MytsrFunc3 + delfunc MytsrFunc4 + %bw! + endfunc + " vim: shiftwidth=2 sts=2 expandtab *** ../vim-8.2.3724/src/testdir/test_tagfunc.vim 2021-12-01 10:30:02.368416797 +0000 --- src/testdir/test_tagfunc.vim 2021-12-03 10:31:50.673436268 +0000 *************** *** 119,124 **** --- 119,130 ---- delfunc Mytagfunc2 endfunc + " Script local tagfunc callback function + func s:ScriptLocalTagFunc(pat, flags, info) + let g:ScriptLocalFuncArgs = [a:pat, a:flags, a:info] + return v:null + endfunc + " Test for different ways of setting the 'tagfunc' option func Test_tagfunc_callback() " Test for using a function() *************** *** 161,166 **** --- 167,187 ---- call assert_equal(['a14', '', {}], g:MytagFunc2_args) call assert_fails('let &tagfunc = Fn', 'E729:') + " Test for using a script local function + set tagfunc=ScriptLocalTagFunc + new | only + let g:ScriptLocalFuncArgs = [] + call assert_fails('tag a15', 'E433:') + call assert_equal(['a15', '', {}], g:ScriptLocalFuncArgs) + + " Test for using a script local funcref variable + let Fn = function("s:ScriptLocalTagFunc") + let &tagfunc= string(Fn) + new | only + let g:ScriptLocalFuncArgs = [] + call assert_fails('tag a16', 'E433:') + call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs) + " Test for using a lambda function func MytagFunc3(pat, flags, info) let g:MytagFunc3_args = [a:pat, a:flags, a:info] *************** *** 169,198 **** set tagfunc={a,\ b,\ c\ ->\ MytagFunc3(a,\ b,\ c)} new | only let g:MytagFunc3_args = [] ! call assert_fails('tag a15', 'E433:') ! call assert_equal(['a15', '', {}], 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 a16', 'E433:') ! call assert_equal(['a16', '', {}], g:MytagFunc3_args) " Set 'tagfunc' to a 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 a17", "E433:") ! call assert_equal(['a17', '', {}], 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)} let &tagfunc = string(Lambda) new | only ! call assert_fails("tag a17", "E987:") " Test for clearing the 'tagfunc' option set tagfunc='' --- 190,219 ---- set tagfunc={a,\ b,\ c\ ->\ MytagFunc3(a,\ b,\ c)} new | only let g:MytagFunc3_args = [] ! call assert_fails('tag a17', 'E433:') ! 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 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)} let &tagfunc = string(Lambda) new | only ! call assert_fails("tag a20", "E987:") " Test for clearing the 'tagfunc' option set tagfunc='' *** ../vim-8.2.3724/src/version.c 2021-12-02 20:44:38.703106821 +0000 --- src/version.c 2021-12-03 10:33:30.785647409 +0000 *************** *** 755,756 **** --- 755,758 ---- { /* Add new patch number below this line */ + /**/ + 3725, /**/ -- BEDEVERE: Wait. Wait ... tell me, what also floats on water? ALL: Bread? No, no, no. Apples .... gravy ... very small rocks ... ARTHUR: A duck. "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// 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 ///