Patch 8.2.3619
Problem:    Cannot use a lambda for 'operatorfunc'.
Solution:   Support using a lambda or partial. (Yegappan Lakshmanan,
            closes #8775)
Files:      runtime/doc/map.txt, runtime/doc/options.txt, src/ops.c,
            src/option.c, src/optionstr.c, src/proto/ops.pro,
            src/proto/option.pro, src/quickfix.c, src/testdir/test_normal.vim


*** ../vim-8.2.3618/runtime/doc/map.txt 2021-11-12 11:25:06.291264320 +0000
--- runtime/doc/map.txt 2021-11-18 21:54:34.311812618 +0000
***************
*** 985,990 ****
--- 1006,1025 ----
  clobbering the `"*` or `"+` registers, if its value contains the item 
`unnamed`
  or `unnamedplus`.
  
+ The `mode()` function will return the state as it will be after applying the
+ operator.
+ 
+ Here is an example for using a lambda function to create a normal-mode
+ operator to add quotes around text in the current line: >
+ 
+       nnoremap <F4> <Cmd>let &opfunc='{t ->
+                               \ getline(".")
+                               \ ->split("\\zs")
+                               \ ->insert("\"", col("'']"))
+                               \ ->insert("\"", col("''[") - 1)
+                               \ ->join("")
+                               \ ->setline(".")}'<CR>g@
+ 
  ==============================================================================
  2. Abbreviations                      *abbreviations* *Abbreviations*
  
*** ../vim-8.2.3618/runtime/doc/options.txt     2021-11-15 17:13:07.342685617 
+0000
--- runtime/doc/options.txt     2021-11-18 21:58:38.707549469 +0000
***************
*** 364,369 ****
--- 371,387 ----
  ":setlocal" on a global option might work differently then.
  
  
+                                               *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")
+       set opfunc=funcref("MyOpFunc")
+       set opfunc={t\ ->\ MyOpFunc(t)}
+ <
+ 
  Setting the filetype
  
  :setf[iletype] [FALLBACK] {filetype}                  *:setf* *:setfiletype*
***************
*** 5607,5613 ****
  'operatorfunc' 'opfunc'       string  (default: empty)
                        global
        This option specifies a function to be called by the |g@| operator.
!       See |:map-operator| for more info and an example.
  
        This option cannot be set from a |modeline| or in the |sandbox|, for
        security reasons.
--- 5634,5642 ----
  'operatorfunc' 'opfunc'       string  (default: empty)
                        global
        This option specifies a function to be called by the |g@| operator.
!       See |:map-operator| for more info and an example.  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.
***************
*** 6005,6012 ****
        customize the information displayed in the quickfix or location window
        for each entry in the corresponding quickfix or location list.  See
        |quickfix-window-function| for an explanation of how to write the
!       function and an example. The value can be the name of a function or a
!       lambda.
  
        This option cannot be set from a |modeline| or in the |sandbox|, for
        security reasons.
--- 6036,6044 ----
        customize the information displayed in the quickfix or location window
        for each entry in the corresponding quickfix or location list.  See
        |quickfix-window-function| for an explanation of how to write the
!       function and an example.  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.3618/src/ops.c   2021-10-19 11:15:36.114656487 +0100
--- src/ops.c   2021-11-18 22:02:17.182423735 +0000
***************
*** 3305,3310 ****
--- 3305,3333 ----
      // do_cmdline() does the rest
  }
  
+ // callback function for 'operatorfunc'
+ static callback_T opfunc_cb;
+ 
+ /*
+  * Process the 'operatorfunc' option value.
+  * Returns OK or FAIL.
+  */
+     int
+ set_operatorfunc_option(void)
+ {
+     return option_set_callback_func(p_opfunc, &opfunc_cb);
+ }
+ 
+ #if defined(EXITFREE) || defined(PROTO)
+     void
+ free_operatorfunc_option(void)
+ {
+ #  ifdef FEAT_EVAL
+     free_callback(&opfunc_cb);
+ #  endif
+ }
+ #endif
+ 
  /*
   * Handle the "g@" operator: call 'operatorfunc'.
   */
***************
*** 3317,3322 ****
--- 3340,3346 ----
      int               save_finish_op = finish_op;
      pos_T     orig_start = curbuf->b_op_start;
      pos_T     orig_end = curbuf->b_op_end;
+     typval_T  rettv;
  
      if (*p_opfunc == NUL)
        emsg(_("E774: 'operatorfunc' is empty"));
***************
*** 3345,3351 ****
        // Reset finish_op so that mode() returns the right value.
        finish_op = FALSE;
  
!       (void)call_func_noret(p_opfunc, 1, argv);
  
        virtual_op = save_virtual_op;
        finish_op = save_finish_op;
--- 3369,3376 ----
        // Reset finish_op so that mode() returns the right value.
        finish_op = FALSE;
  
!       if (call_callback(&opfunc_cb, 0, &rettv, 1, argv) != FAIL)
!           clear_tv(&rettv);
  
        virtual_op = save_virtual_op;
        finish_op = save_finish_op;
*** ../vim-8.2.3618/src/option.c        2021-10-17 14:13:04.832665843 +0100
--- src/option.c        2021-11-18 22:04:08.233515803 +0000
***************
*** 809,814 ****
--- 809,815 ----
            // buffer-local option: free global value
            clear_string_option((char_u **)options[i].var);
      }
+     free_operatorfunc_option();
  }
  #endif
  
***************
*** 7184,7186 ****
--- 7185,7233 ----
  #endif
      return p_magic;
  }
+ 
+ /*
+  * Set the callback function value for an option that accepts a function name,
+  * lambda, et al. (e.g. 'operatorfunc', 'tagfunc', etc.)
+  * Returns OK if the option is successfully set to a function, otherwise
+  * returns FAIL.
+  */
+     int
+ option_set_callback_func(char_u *optval UNUSED, callback_T *optcb UNUSED)
+ {
+ #ifdef FEAT_EVAL
+     typval_T  *tv;
+     callback_T        cb;
+ 
+     if (optval == NULL || *optval == NUL)
+     {
+       free_callback(optcb);
+       return OK;
+     }
+ 
+     if (*optval == '{'
+           || (STRNCMP(optval, "function(", 9) == 0)
+           || (STRNCMP(optval, "funcref(", 8) == 0))
+       // Lambda expression or a funcref
+       tv = eval_expr(optval, NULL);
+     else
+       // treat everything else as a function name string
+       tv = alloc_string_tv(vim_strsave(optval));
+     if (tv == NULL)
+       return FAIL;
+ 
+     cb = get_callback(tv);
+     if (cb.cb_name == NULL)
+     {
+       free_tv(tv);
+       return FAIL;
+     }
+ 
+     free_callback(optcb);
+     set_callback(optcb, &cb);
+     free_tv(tv);
+     return OK;
+ #else
+     return FAIL;
+ #endif
+ }
*** ../vim-8.2.3618/src/optionstr.c     2021-11-12 19:52:44.508731448 +0000
--- src/optionstr.c     2021-11-18 22:02:29.070320056 +0000
***************
*** 2320,2329 ****
  # endif
  #endif
  
  #ifdef FEAT_QUICKFIX
      else if (varp == &p_qftf)
      {
!       if (qf_process_qftf_option() == FALSE)
            errmsg = e_invarg;
      }
  #endif
--- 2320,2337 ----
  # endif
  #endif
  
+     // 'operatorfunc'
+     else if (varp == &p_opfunc)
+     {
+       if (set_operatorfunc_option() == FAIL)
+           errmsg = e_invarg;
+     }
+ 
  #ifdef FEAT_QUICKFIX
+     // 'quickfixtextfunc'
      else if (varp == &p_qftf)
      {
!       if (qf_process_qftf_option() == FAIL)
            errmsg = e_invarg;
      }
  #endif
*** ../vim-8.2.3618/src/proto/ops.pro   2020-05-01 13:26:17.132949262 +0100
--- src/proto/ops.pro   2021-11-18 21:54:34.315812615 +0000
***************
*** 17,21 ****
--- 17,23 ----
  void op_addsub(oparg_T *oap, linenr_T Prenum1, int g_cmd);
  void clear_oparg(oparg_T *oap);
  void cursor_pos_info(dict_T *dict);
+ int set_operatorfunc_option(void);
+ void free_operatorfunc_option(void);
  void do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank);
  /* vim: set ft=c : */
*** ../vim-8.2.3618/src/proto/option.pro        2021-07-26 21:19:05.380122574 
+0100
--- src/proto/option.pro        2021-11-18 22:04:50.789200768 +0000
***************
*** 10,16 ****
  void set_helplang_default(char_u *lang);
  void set_title_defaults(void);
  void ex_set(exarg_T *eap);
! int do_set(char_u *arg, int opt_flags);
  void did_set_option(int opt_idx, int opt_flags, int new_value, int 
value_checked);
  int string_to_key(char_u *arg, int multi_byte);
  void did_set_title(void);
--- 10,16 ----
  void set_helplang_default(char_u *lang);
  void set_title_defaults(void);
  void ex_set(exarg_T *eap);
! int do_set(char_u *arg_start, int opt_flags);
  void did_set_option(int opt_idx, int opt_flags, int new_value, int 
value_checked);
  int string_to_key(char_u *arg, int multi_byte);
  void did_set_title(void);
***************
*** 78,81 ****
--- 78,82 ----
  dict_T *get_winbuf_options(int bufopt);
  int fill_culopt_flags(char_u *val, win_T *wp);
  int magic_isset(void);
+ int option_set_callback_func(char_u *optval, callback_T *optcb);
  /* vim: set ft=c : */
*** ../vim-8.2.3618/src/quickfix.c      2021-10-20 21:58:37.235792695 +0100
--- src/quickfix.c      2021-11-18 22:02:02.654552771 +0000
***************
*** 4437,4481 ****
  
  /*
   * Process the 'quickfixtextfunc' option value.
   */
      int
  qf_process_qftf_option(void)
  {
!     typval_T  *tv;
!     callback_T        cb;
! 
!     if (p_qftf == NULL || *p_qftf == NUL)
!     {
!       free_callback(&qftf_cb);
!       return TRUE;
!     }
! 
!     if (*p_qftf == '{')
!     {
!       // Lambda expression
!       tv = eval_expr(p_qftf, NULL);
!       if (tv == NULL)
!           return FALSE;
!     }
!     else
!     {
!       // treat everything else as a function name string
!       tv = alloc_string_tv(vim_strsave(p_qftf));
!       if (tv == NULL)
!           return FALSE;
!     }
! 
!     cb = get_callback(tv);
!     if (cb.cb_name == NULL)
!     {
!       free_tv(tv);
!       return FALSE;
!     }
! 
!     free_callback(&qftf_cb);
!     set_callback(&qftf_cb, &cb);
!     free_tv(tv);
!     return TRUE;
  }
  
  /*
--- 4437,4448 ----
  
  /*
   * Process the 'quickfixtextfunc' option value.
+  * Returns OK or FAIL.
   */
      int
  qf_process_qftf_option(void)
  {
!     return option_set_callback_func(p_qftf, &qftf_cb);
  }
  
  /*
*** ../vim-8.2.3618/src/testdir/test_normal.vim 2021-11-04 13:28:26.082236210 
+0000
--- src/testdir/test_normal.vim 2021-11-18 21:54:34.319812609 +0000
***************
*** 386,391 ****
--- 386,455 ----
    norm V10j,,
    call assert_equal(22, g:a)
  
+   " Use a lambda function for 'opfunc'
+   unmap <buffer> ,,
+   call cursor(1, 1)
+   let g:a=0
+   nmap <buffer><silent> ,, :set opfunc={type\ ->\ CountSpaces(type)}<CR>g@
+   vmap <buffer><silent> ,, :<C-U>call CountSpaces(visualmode(), 1)<CR>
+   50
+   norm V2j,,
+   call assert_equal(6, g:a)
+   norm V,,
+   call assert_equal(2, g:a)
+   norm ,,l
+   call assert_equal(0, g:a)
+   50
+   exe "norm 0\<c-v>10j2l,,"
+   call assert_equal(11, g:a)
+   50
+   norm V10j,,
+   call assert_equal(22, g:a)
+ 
+   " use a partial function for 'opfunc'
+   let g:OpVal = 0
+   func! Test_opfunc1(x, y, type)
+     let g:OpVal =  a:x + a:y
+   endfunc
+   set opfunc=function('Test_opfunc1',\ [5,\ 7])
+   normal! g@l
+   call assert_equal(12, g:OpVal)
+   " delete the function and try to use g@
+   delfunc Test_opfunc1
+   call test_garbagecollect_now()
+   call assert_fails('normal! g@l', 'E117:')
+   set opfunc=
+ 
+   " use a funcref for 'opfunc'
+   let g:OpVal = 0
+   func! Test_opfunc2(x, y, type)
+     let g:OpVal =  a:x + a:y
+   endfunc
+   set opfunc=funcref('Test_opfunc2',\ [4,\ 3])
+   normal! g@l
+   call assert_equal(7, g:OpVal)
+   " delete the function and try to use g@
+   delfunc Test_opfunc2
+   call test_garbagecollect_now()
+   call assert_fails('normal! g@l', 'E933:')
+   set opfunc=
+ 
+   " Try to use a function with two arguments for 'operatorfunc'
+   let g:OpVal = 0
+   func! Test_opfunc3(x, y)
+     let g:OpVal = 4
+   endfunc
+   set opfunc=Test_opfunc3
+   call assert_fails('normal! g@l', 'E119:')
+   call assert_equal(0, g:OpVal)
+   set opfunc=
+   delfunc Test_opfunc3
+   unlet g:OpVal
+ 
+   " Try to use a lambda function with two arguments for 'operatorfunc'
+   set opfunc={x,\ y\ ->\ 'done'}
+   call assert_fails('normal! g@l', 'E119:')
+ 
    " clean up
    unmap <buffer> ,,
    set opfunc=
*** ../vim-8.2.3618/src/version.c       2021-11-18 20:47:25.814140290 +0000
--- src/version.c       2021-11-18 21:56:22.087699612 +0000
***************
*** 759,760 ****
--- 759,762 ----
  {   /* Add new patch number below this line */
+ /**/
+     3619,
  /**/

-- 
ARTHUR: Listen, old crone!  Unless you tell us where we can buy a shrubbery,
        my friend and I will ... we will say "Ni!"
CRONE:  Do your worst!
                 "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/20211118220934.BF0E51C6567%40moolenaar.net.

Raspunde prin e-mail lui