mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
patch 8.2.3619: cannot use a lambda for 'operatorfunc'
Problem: Cannot use a lambda for 'operatorfunc'. Solution: Support using a lambda or partial. (Yegappan Lakshmanan, closes #8775)
This commit is contained in:
committed by
Bram Moolenaar
parent
851c7a699a
commit
777175b0df
@@ -1009,6 +1009,20 @@ or `unnamedplus`.
|
|||||||
The `mode()` function will return the state as it will be after applying the
|
The `mode()` function will return the state as it will be after applying the
|
||||||
operator.
|
operator.
|
||||||
|
|
||||||
|
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*
|
2. Abbreviations *abbreviations* *Abbreviations*
|
||||||
|
|
||||||
|
@@ -371,6 +371,17 @@ Note: In the future more global options can be made global-local. Using
|
|||||||
":setlocal" on a global option might work differently then.
|
":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
|
Setting the filetype
|
||||||
|
|
||||||
:setf[iletype] [FALLBACK] {filetype} *:setf* *:setfiletype*
|
:setf[iletype] [FALLBACK] {filetype} *:setf* *:setfiletype*
|
||||||
@@ -5623,7 +5634,9 @@ A jump table for the options with a short description can be found at |Q_op|.
|
|||||||
'operatorfunc' 'opfunc' string (default: empty)
|
'operatorfunc' 'opfunc' string (default: empty)
|
||||||
global
|
global
|
||||||
This option specifies a function to be called by the |g@| operator.
|
This option specifies a function to be called by the |g@| operator.
|
||||||
See |:map-operator| for more info and an example.
|
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
|
This option cannot be set from a |modeline| or in the |sandbox|, for
|
||||||
security reasons.
|
security reasons.
|
||||||
@@ -6023,8 +6036,9 @@ A jump table for the options with a short description can be found at |Q_op|.
|
|||||||
customize the information displayed in the quickfix or location window
|
customize the information displayed in the quickfix or location window
|
||||||
for each entry in the corresponding quickfix or location list. See
|
for each entry in the corresponding quickfix or location list. See
|
||||||
|quickfix-window-function| for an explanation of how to write the
|
|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
|
function and an example. The value can be the name of a function, a
|
||||||
lambda.
|
|lambda| or a |Funcref|. See |option-value-function| for more
|
||||||
|
information.
|
||||||
|
|
||||||
This option cannot be set from a |modeline| or in the |sandbox|, for
|
This option cannot be set from a |modeline| or in the |sandbox|, for
|
||||||
security reasons.
|
security reasons.
|
||||||
|
27
src/ops.c
27
src/ops.c
@@ -3305,6 +3305,29 @@ op_colon(oparg_T *oap)
|
|||||||
// do_cmdline() does the rest
|
// 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'.
|
* Handle the "g@" operator: call 'operatorfunc'.
|
||||||
*/
|
*/
|
||||||
@@ -3317,6 +3340,7 @@ op_function(oparg_T *oap UNUSED)
|
|||||||
int save_finish_op = finish_op;
|
int save_finish_op = finish_op;
|
||||||
pos_T orig_start = curbuf->b_op_start;
|
pos_T orig_start = curbuf->b_op_start;
|
||||||
pos_T orig_end = curbuf->b_op_end;
|
pos_T orig_end = curbuf->b_op_end;
|
||||||
|
typval_T rettv;
|
||||||
|
|
||||||
if (*p_opfunc == NUL)
|
if (*p_opfunc == NUL)
|
||||||
emsg(_("E774: 'operatorfunc' is empty"));
|
emsg(_("E774: 'operatorfunc' is empty"));
|
||||||
@@ -3345,7 +3369,8 @@ op_function(oparg_T *oap UNUSED)
|
|||||||
// Reset finish_op so that mode() returns the right value.
|
// Reset finish_op so that mode() returns the right value.
|
||||||
finish_op = FALSE;
|
finish_op = FALSE;
|
||||||
|
|
||||||
(void)call_func_noret(p_opfunc, 1, argv);
|
if (call_callback(&opfunc_cb, 0, &rettv, 1, argv) != FAIL)
|
||||||
|
clear_tv(&rettv);
|
||||||
|
|
||||||
virtual_op = save_virtual_op;
|
virtual_op = save_virtual_op;
|
||||||
finish_op = save_finish_op;
|
finish_op = save_finish_op;
|
||||||
|
47
src/option.c
47
src/option.c
@@ -809,6 +809,7 @@ free_all_options(void)
|
|||||||
// buffer-local option: free global value
|
// buffer-local option: free global value
|
||||||
clear_string_option((char_u **)options[i].var);
|
clear_string_option((char_u **)options[i].var);
|
||||||
}
|
}
|
||||||
|
free_operatorfunc_option();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -7184,3 +7185,49 @@ magic_isset(void)
|
|||||||
#endif
|
#endif
|
||||||
return p_magic;
|
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
|
||||||
|
}
|
||||||
|
@@ -2320,10 +2320,18 @@ ambw_end:
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// 'operatorfunc'
|
||||||
|
else if (varp == &p_opfunc)
|
||||||
|
{
|
||||||
|
if (set_operatorfunc_option() == FAIL)
|
||||||
|
errmsg = e_invarg;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef FEAT_QUICKFIX
|
#ifdef FEAT_QUICKFIX
|
||||||
|
// 'quickfixtextfunc'
|
||||||
else if (varp == &p_qftf)
|
else if (varp == &p_qftf)
|
||||||
{
|
{
|
||||||
if (qf_process_qftf_option() == FALSE)
|
if (qf_process_qftf_option() == FAIL)
|
||||||
errmsg = e_invarg;
|
errmsg = e_invarg;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -17,5 +17,7 @@ void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, int is_del);
|
|||||||
void op_addsub(oparg_T *oap, linenr_T Prenum1, int g_cmd);
|
void op_addsub(oparg_T *oap, linenr_T Prenum1, int g_cmd);
|
||||||
void clear_oparg(oparg_T *oap);
|
void clear_oparg(oparg_T *oap);
|
||||||
void cursor_pos_info(dict_T *dict);
|
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);
|
void do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank);
|
||||||
/* vim: set ft=c : */
|
/* vim: set ft=c : */
|
||||||
|
@@ -10,7 +10,7 @@ void set_init_3(void);
|
|||||||
void set_helplang_default(char_u *lang);
|
void set_helplang_default(char_u *lang);
|
||||||
void set_title_defaults(void);
|
void set_title_defaults(void);
|
||||||
void ex_set(exarg_T *eap);
|
void ex_set(exarg_T *eap);
|
||||||
int do_set(char_u *arg, int opt_flags);
|
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);
|
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);
|
int string_to_key(char_u *arg, int multi_byte);
|
||||||
void did_set_title(void);
|
void did_set_title(void);
|
||||||
@@ -78,4 +78,5 @@ char_u *get_showbreak_value(win_T *win);
|
|||||||
dict_T *get_winbuf_options(int bufopt);
|
dict_T *get_winbuf_options(int bufopt);
|
||||||
int fill_culopt_flags(char_u *val, win_T *wp);
|
int fill_culopt_flags(char_u *val, win_T *wp);
|
||||||
int magic_isset(void);
|
int magic_isset(void);
|
||||||
|
int option_set_callback_func(char_u *optval, callback_T *optcb);
|
||||||
/* vim: set ft=c : */
|
/* vim: set ft=c : */
|
||||||
|
@@ -4437,45 +4437,12 @@ qf_find_buf(qf_info_T *qi)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Process the 'quickfixtextfunc' option value.
|
* Process the 'quickfixtextfunc' option value.
|
||||||
|
* Returns OK or FAIL.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
qf_process_qftf_option(void)
|
qf_process_qftf_option(void)
|
||||||
{
|
{
|
||||||
typval_T *tv;
|
return option_set_callback_func(p_qftf, &qftf_cb);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -386,6 +386,70 @@ func Test_normal09_operatorfunc()
|
|||||||
norm V10j,,
|
norm V10j,,
|
||||||
call assert_equal(22, g:a)
|
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
|
" clean up
|
||||||
unmap <buffer> ,,
|
unmap <buffer> ,,
|
||||||
set opfunc=
|
set opfunc=
|
||||||
|
@@ -757,6 +757,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
3619,
|
||||||
/**/
|
/**/
|
||||||
3618,
|
3618,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user