mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
vim-patch:8.2.3725: cannot use a lambda for 'completefunc' and 'omnifunc'
Problem: Cannot use a lambda for 'completefunc' and 'omnifunc'.
Solution: Implement lambda support. (Yegappan Lakshmanan, closes vim/vim#9257)
8658c759f0
Comment out Vim9 script in tests.
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
parent
1508618d4c
commit
1e4adf4b56
@ -314,8 +314,8 @@ Note: In the future more global options can be made |global-local|. Using
|
||||
|
||||
*option-value-function*
|
||||
Some options ('completefunc', 'imactivatefunc', 'imstatusfunc', 'omnifunc',
|
||||
'operatorfunc', 'quickfixtextfunc' and 'tagfunc') are set to a function name
|
||||
or a function reference or a lambda function. Examples:
|
||||
'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')
|
||||
@ -1454,7 +1454,9 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
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.
|
||||
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.
|
||||
|
||||
@ -4421,7 +4423,9 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
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.
|
||||
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
|
||||
@ -6576,6 +6580,8 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
global or local to buffer |global-local|
|
||||
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.
|
||||
|
@ -1971,8 +1971,11 @@ void free_buf_options(buf_T *buf, int free_p_ff)
|
||||
clear_string_option(&buf->b_p_cinw);
|
||||
clear_string_option(&buf->b_p_cpt);
|
||||
clear_string_option(&buf->b_p_cfu);
|
||||
callback_free(&buf->b_cfu_cb);
|
||||
clear_string_option(&buf->b_p_ofu);
|
||||
callback_free(&buf->b_ofu_cb);
|
||||
clear_string_option(&buf->b_p_tsrfu);
|
||||
callback_free(&buf->b_tsrfu_cb);
|
||||
clear_string_option(&buf->b_p_gp);
|
||||
clear_string_option(&buf->b_p_mp);
|
||||
clear_string_option(&buf->b_p_efm);
|
||||
|
@ -675,7 +675,9 @@ struct file_buffer {
|
||||
char *b_p_csl; ///< 'completeslash'
|
||||
#endif
|
||||
char *b_p_cfu; ///< 'completefunc'
|
||||
Callback b_cfu_cb; ///< 'completefunc' callback
|
||||
char *b_p_ofu; ///< 'omnifunc'
|
||||
Callback b_ofu_cb; ///< 'omnifunc' callback
|
||||
char *b_p_tfu; ///< 'tagfunc'
|
||||
Callback b_tfu_cb; ///< 'tagfunc' callback
|
||||
int b_p_eof; ///< 'endoffile'
|
||||
@ -749,6 +751,7 @@ struct file_buffer {
|
||||
char *b_p_dict; ///< 'dictionary' local value
|
||||
char *b_p_tsr; ///< 'thesaurus' local value
|
||||
char *b_p_tsrfu; ///< 'thesaurusfunc' local value
|
||||
Callback b_tsrfu_cb; ///< 'thesaurusfunc' callback
|
||||
long b_p_ul; ///< 'undolevels' local value
|
||||
int b_p_udf; ///< 'undofile'
|
||||
char *b_p_lw; ///< 'lispwords' local value
|
||||
|
@ -1110,25 +1110,7 @@ fail:
|
||||
|
||||
return ret;
|
||||
}
|
||||
/// Call Vim script function and return the result as a number
|
||||
///
|
||||
/// @param[in] func Function name.
|
||||
/// @param[in] argc Number of arguments.
|
||||
/// @param[in] argv Array with typval_T arguments.
|
||||
///
|
||||
/// @return -1 when calling function fails, result of function otherwise.
|
||||
varnumber_T call_func_retnr(const char *func, int argc, typval_T *argv)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
typval_T rettv;
|
||||
|
||||
if (call_vim_function((char *)func, argc, argv, &rettv) == FAIL) {
|
||||
return -1;
|
||||
}
|
||||
varnumber_T retval = tv_get_number_chk(&rettv, NULL);
|
||||
tv_clear(&rettv);
|
||||
return retval;
|
||||
}
|
||||
/// Call Vim script function and return the result as a string
|
||||
///
|
||||
/// @param[in] func Function name.
|
||||
@ -1151,6 +1133,7 @@ char *call_func_retstr(const char *const func, int argc, typval_T *argv)
|
||||
tv_clear(&rettv);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Call Vim script function and return the result as a List
|
||||
///
|
||||
/// @param[in] func Function name.
|
||||
|
@ -1393,6 +1393,24 @@ func_call_skip_call:
|
||||
return r;
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// @param argcount number of "argvars"
|
||||
/// @param argvars vars for arguments, must have "argcount" PLUS ONE elements!
|
||||
varnumber_T callback_call_retnr(Callback *callback, int argcount, typval_T *argvars)
|
||||
{
|
||||
typval_T rettv;
|
||||
if (!callback_call(callback, argcount, argvars, &rettv)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
varnumber_T retval = tv_get_number_chk(&rettv, NULL);
|
||||
tv_clear(&rettv);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Give an error message for the result of a function.
|
||||
/// Nothing if "error" is FCERR_NONE.
|
||||
static void user_func_error(int error, const char_u *name)
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "nvim/edit.h"
|
||||
#include "nvim/eval.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/eval/userfunc.h"
|
||||
#include "nvim/ex_docmd.h"
|
||||
#include "nvim/ex_eval.h"
|
||||
#include "nvim/ex_getln.h"
|
||||
@ -2224,6 +2225,82 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag)
|
||||
return buf;
|
||||
}
|
||||
|
||||
static Callback cfu_cb; ///< 'completefunc' callback function
|
||||
static Callback ofu_cb; ///< 'omnifunc' callback function
|
||||
static Callback tsrfu_cb; ///< 'thesaurusfunc' callback function
|
||||
|
||||
/// Copy a global callback function to a buffer local callback.
|
||||
static void copy_global_to_buflocal_cb(Callback *globcb, Callback *bufcb)
|
||||
{
|
||||
callback_free(bufcb);
|
||||
if (globcb->data.funcref != NULL && *globcb->data.funcref != NUL) {
|
||||
callback_copy(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 = 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)
|
||||
{
|
||||
copy_global_to_buflocal_cb(&cfu_cb, &buf->b_cfu_cb);
|
||||
}
|
||||
|
||||
/// 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 = 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)
|
||||
{
|
||||
copy_global_to_buflocal_cb(&ofu_cb, &buf->b_ofu_cb);
|
||||
}
|
||||
|
||||
/// 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
|
||||
callback_free(&curbuf->b_tsrfu_cb);
|
||||
retval = option_set_callback_func(curbuf->b_p_tsrfu, &curbuf->b_tsrfu_cb);
|
||||
} else {
|
||||
// global option set
|
||||
callback_free(&tsrfu_cb);
|
||||
retval = option_set_callback_func(p_tsrfu, &tsrfu_cb);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// Get the user-defined completion function name for completion "type"
|
||||
static char_u *get_complete_funcname(int type)
|
||||
{
|
||||
@ -2239,6 +2316,19 @@ static char_u *get_complete_funcname(int type)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the callback to use for insert mode completion.
|
||||
static Callback *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".
|
||||
///
|
||||
@ -2272,8 +2362,10 @@ static void expand_by_function(int type, char_u *base)
|
||||
// Insert mode in another buffer.
|
||||
textlock++;
|
||||
|
||||
Callback *cb = get_insert_callback(type);
|
||||
|
||||
// Call a function, which returns a list or dict.
|
||||
if (call_vim_function((char *)funcname, 2, args, &rettv) == OK) {
|
||||
if (callback_call(cb, 2, args, &rettv)) {
|
||||
switch (rettv.v_type) {
|
||||
case VAR_LIST:
|
||||
matchlist = rettv.vval.v_list;
|
||||
@ -3851,7 +3943,8 @@ static int get_userdefined_compl_info(colnr_T curs_col)
|
||||
|
||||
pos_T pos = curwin->w_cursor;
|
||||
textlock++;
|
||||
colnr_T col = (colnr_T)call_func_retnr((char *)funcname, 2, args);
|
||||
Callback *cb = get_insert_callback(ctrl_x_mode);
|
||||
colnr_T col = (colnr_T)callback_call_retnr(cb, 2, args);
|
||||
textlock--;
|
||||
|
||||
State = save_State;
|
||||
@ -4354,6 +4447,9 @@ static unsigned quote_meta(char_u *dest, char_u *src, int len)
|
||||
void free_insexpand_stuff(void)
|
||||
{
|
||||
XFREE_CLEAR(compl_orig_text);
|
||||
callback_free(&cfu_cb);
|
||||
callback_free(&ofu_cb);
|
||||
callback_free(&tsrfu_cb);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "nvim/highlight_group.h"
|
||||
#include "nvim/indent.h"
|
||||
#include "nvim/indent_c.h"
|
||||
#include "nvim/insexpand.h"
|
||||
#include "nvim/keycodes.h"
|
||||
#include "nvim/locale.h"
|
||||
#include "nvim/macros.h"
|
||||
@ -4381,11 +4382,13 @@ void buf_copy_options(buf_T *buf, int flags)
|
||||
#endif
|
||||
buf->b_p_cfu = xstrdup(p_cfu);
|
||||
COPY_OPT_SCTX(buf, BV_CFU);
|
||||
set_buflocal_cfu_callback(buf);
|
||||
buf->b_p_ofu = xstrdup(p_ofu);
|
||||
COPY_OPT_SCTX(buf, BV_OFU);
|
||||
set_buflocal_ofu_callback(buf);
|
||||
buf->b_p_tfu = xstrdup(p_tfu);
|
||||
COPY_OPT_SCTX(buf, BV_TFU);
|
||||
buf_set_tfu_callback(buf);
|
||||
set_buflocal_tfu_callback(buf);
|
||||
buf->b_p_sts = p_sts;
|
||||
COPY_OPT_SCTX(buf, BV_STS);
|
||||
buf->b_p_sts_nopaste = p_sts_nopaste;
|
||||
|
@ -1473,6 +1473,18 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (gvarp == &p_cfu) { // 'completefunc'
|
||||
if (set_completefunc_option() == FAIL) {
|
||||
errmsg = e_invarg;
|
||||
}
|
||||
} else if (gvarp == &p_ofu) { // 'omnifunc'
|
||||
if (set_omnifunc_option() == FAIL) {
|
||||
errmsg = e_invarg;
|
||||
}
|
||||
} else if (gvarp == &p_tsrfu) { // 'thesaurusfunc'
|
||||
if (set_thesaurusfunc_option() == FAIL) {
|
||||
errmsg = e_invarg;
|
||||
}
|
||||
} else if (varp == &p_opfunc) { // 'operatorfunc'
|
||||
if (set_operatorfunc_option() == FAIL) {
|
||||
errmsg = e_invarg;
|
||||
|
@ -149,7 +149,7 @@ void free_tagfunc_option(void)
|
||||
|
||||
/// Copy the global 'tagfunc' callback function to the buffer-local 'tagfunc'
|
||||
/// callback for 'buf'.
|
||||
void buf_set_tfu_callback(buf_T *buf)
|
||||
void set_buflocal_tfu_callback(buf_T *buf)
|
||||
{
|
||||
callback_free(&buf->b_tfu_cb);
|
||||
if (tfu_cb.data.funcref != NULL && *tfu_cb.data.funcref != NUL) {
|
||||
|
@ -1285,6 +1285,505 @@ func Test_no_mapping_for_ctrl_x_key()
|
||||
bwipe!
|
||||
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
|
||||
|
||||
func FooBarComplete(findstart, base)
|
||||
if a:findstart
|
||||
return col('.') - 1
|
||||
|
@ -117,6 +117,12 @@ func Test_tagfunc_settagstack()
|
||||
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()
|
||||
@ -159,6 +165,21 @@ func Test_tagfunc_callback()
|
||||
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]
|
||||
@ -167,30 +188,30 @@ func Test_tagfunc_callback()
|
||||
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)
|
||||
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 a16', 'E433:')
|
||||
call assert_equal(['a16', '', {}], 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 a17", "E433:")
|
||||
call assert_equal(['a17', '', {}], 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 a17", "E987:")
|
||||
call assert_fails("tag a20", "E987:")
|
||||
|
||||
" Test for clearing the 'tagfunc' option
|
||||
set tagfunc=''
|
||||
|
Loading…
Reference in New Issue
Block a user