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(<name>) or funcref(<name>) 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(<name>) or funcref(<name>) 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(<name>) or funcref(<name>) 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(<name>) or funcref(<name>) or a lambda.
   */
      int
! set_tagfunc_option()
  {
  #ifdef FEAT_EVAL
      free_callback(&tfu_cb);
--- 115,121 ----
   * a function (string), or function(<name>) or funcref(<name>) 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\<C-X>\<C-U>\<Esc>", '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\<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()
+   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\<C-X>\<C-U>\<Esc>", '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\<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
+   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\<C-X>\<C-U>\<Esc>", '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\<C-X>\<C-U>\<Esc>", '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\<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)
+   new | only
+   call setline(1, 'eight')
+   call feedkeys("A\<C-X>\<C-U>\<Esc>", '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\<C-X>\<C-U>\<Esc>", '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\<C-X>\<C-U>\<Esc>", '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\<C-X>\<C-U>\<Esc>", '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\<C-X>\<C-U>\<Esc>", '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\<C-X>\<C-U>\<Esc>", "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\<C-X>\<C-O>\<Esc>", '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\<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()
+   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\<C-X>\<C-O>\<Esc>", '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\<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
+   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\<C-X>\<C-O>\<Esc>", '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\<C-X>\<C-O>\<Esc>", '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\<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)
+   new | only
+   call setline(1, 'eight')
+   call feedkeys("A\<C-X>\<C-O>\<Esc>", '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\<C-X>\<C-O>\<Esc>", '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\<C-X>\<C-O>\<Esc>", '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\<C-X>\<C-O>\<Esc>", '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\<C-X>\<C-O>\<Esc>", '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\<C-X>\<C-O>\<Esc>", "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\<C-X>\<C-T>\<Esc>", '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\<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()
+   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\<C-X>\<C-T>\<Esc>", '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\<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
+   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\<C-X>\<C-T>\<Esc>", '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\<C-X>\<C-T>\<Esc>", '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\<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)
+   new | only
+   call setline(1, 'eight')
+   call feedkeys("A\<C-X>\<C-T>\<Esc>", '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\<C-X>\<C-T>\<Esc>", '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\<C-X>\<C-T>\<Esc>", '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\<C-X>\<C-T>\<Esc>", '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\<C-X>\<C-T>\<Esc>", '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\<C-X>\<C-T>\<Esc>", "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\<C-X>\<C-T>\<Esc>", "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\<C-X>\<C-T>\<Esc>", "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\<C-X>\<C-T>\<Esc>", "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=<SID>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 -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20211203111001.B839E1C28D5%40moolenaar.net.

Raspunde prin e-mail lui