diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/option.c | 21 | ||||
-rw-r--r-- | src/nvim/runtime.h | 1 | ||||
-rw-r--r-- | src/nvim/testdir/test_ins_complete.vim | 162 | ||||
-rw-r--r-- | src/nvim/testdir/test_normal.vim | 35 | ||||
-rw-r--r-- | src/nvim/testdir/test_quickfix.vim | 17 | ||||
-rw-r--r-- | src/nvim/testdir/test_tagfunc.vim | 63 |
6 files changed, 277 insertions, 22 deletions
diff --git a/src/nvim/option.c b/src/nvim/option.c index e67bacce61..6d461d9b9d 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -5170,7 +5170,26 @@ int option_set_callback_func(char *optval, Callback *optcb) // treat everything else as a function name string tv = xcalloc(1, sizeof(*tv)); tv->v_type = VAR_STRING; - tv->vval.v_string = xstrdup(optval); + + // Function name starting with "s:" are supported only in a vimscript + // context. + if (strncmp(optval, "s:", 2) == 0) { + char sid_buf[25]; + + if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) { + emsg(_(e_usingsid)); + return FAIL; + } + // Expand s: prefix into <SNR>nr_<name> + snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_", + (int64_t)current_sctx.sc_sid); + char *funcname = xmalloc(strlen(sid_buf) + strlen(optval + 2) + 1); + STRCPY(funcname, sid_buf); + STRCAT(funcname, optval + 2); + tv->vval.v_string = funcname; + } else { + tv->vval.v_string = xstrdup(optval); + } } Callback cb; diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h index d40bb6c1c1..de363020f8 100644 --- a/src/nvim/runtime.h +++ b/src/nvim/runtime.h @@ -79,6 +79,7 @@ typedef struct scriptitem_S { /// Growarray to store info about already sourced scripts. extern garray_T script_items; #define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1]) +#define SCRIPT_ID_VALID(id) ((id) > 0 && (id) <= script_items.ga_len) typedef void (*DoInRuntimepathCB)(char *, void *); diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index d788841833..1811c82767 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -1300,13 +1300,13 @@ func Test_completefunc_callback() endfunc let lines =<< trim END - #" Test for using a function name + #" Test for using a global function name LET &completefunc = 'g:CompleteFunc2' new - call setline(1, 'zero') + call setline(1, 'global') LET g:CompleteFunc2Args = [] call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') - call assert_equal([[1, ''], [0, 'zero']], g:CompleteFunc2Args) + call assert_equal([[1, ''], [0, 'global']], g:CompleteFunc2Args) bw! #" Test for using a function() @@ -1442,6 +1442,29 @@ func Test_completefunc_callback() END call CheckLegacyAndVim9Success(lines) + " Test for using a script-local function name + func s:CompleteFunc3(findstart, base) + call add(g:CompleteFunc3Args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set completefunc=s:CompleteFunc3 + new + call setline(1, 'script1') + let g:CompleteFunc3Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'script1']], g:CompleteFunc3Args) + bw! + + let &completefunc = 's:CompleteFunc3' + new + call setline(1, 'script2') + let g:CompleteFunc3Args = [] + call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'script2']], g:CompleteFunc3Args) + bw! + delfunc s:CompleteFunc3 + + " invalid return value let &completefunc = {a -> 'abc'} call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') @@ -1476,11 +1499,12 @@ func Test_completefunc_callback() let lines =<< trim END vim9script - # Test for using a def function with completefunc - def Vim9CompleteFunc(val: number, findstart: number, base: string): any - add(g:Vim9completeFuncArgs, [val, findstart, base]) + def Vim9CompleteFunc(callnr: number, findstart: number, base: string): any + add(g:Vim9completeFuncArgs, [callnr, findstart, base]) return findstart ? 0 : [] enddef + + # Test for using a def function with completefunc set completefunc=function('Vim9CompleteFunc',\ [60]) new | only setline(1, 'one') @@ -1488,6 +1512,28 @@ func Test_completefunc_callback() feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9completeFuncArgs) bw! + + # Test for using a global function name + &completefunc = g:CompleteFunc2 + new | only + setline(1, 'two') + g:CompleteFunc2Args = [] + feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + assert_equal([[1, ''], [0, 'two']], g:CompleteFunc2Args) + bw! + + # Test for using a script-local function name + def s:LocalCompleteFunc(findstart: number, base: string): any + add(g:LocalCompleteFuncArgs, [findstart, base]) + return findstart ? 0 : [] + enddef + &completefunc = s:LocalCompleteFunc + new | only + setline(1, 'three') + g:LocalCompleteFuncArgs = [] + feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') + assert_equal([[1, ''], [0, 'three']], g:LocalCompleteFuncArgs) + bw! END " call CheckScriptSuccess(lines) @@ -1653,6 +1699,29 @@ func Test_omnifunc_callback() END call CheckLegacyAndVim9Success(lines) + " Test for using a script-local function name + func s:OmniFunc3(findstart, base) + call add(g:OmniFunc3Args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set omnifunc=s:OmniFunc3 + new + call setline(1, 'script1') + let g:OmniFunc3Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'script1']], g:OmniFunc3Args) + bw! + + let &omnifunc = 's:OmniFunc3' + new + call setline(1, 'script2') + let g:OmniFunc3Args = [] + call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'script2']], g:OmniFunc3Args) + bw! + delfunc s:OmniFunc3 + + " invalid return value let &omnifunc = {a -> 'abc'} call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') @@ -1687,11 +1756,12 @@ func Test_omnifunc_callback() let lines =<< trim END vim9script - # Test for using a def function with omnifunc - def Vim9omniFunc(val: number, findstart: number, base: string): any - add(g:Vim9omniFunc_Args, [val, findstart, base]) + def Vim9omniFunc(callnr: number, findstart: number, base: string): any + add(g:Vim9omniFunc_Args, [callnr, findstart, base]) return findstart ? 0 : [] enddef + + # Test for using a def function with omnifunc set omnifunc=function('Vim9omniFunc',\ [60]) new | only setline(1, 'one') @@ -1699,6 +1769,28 @@ func Test_omnifunc_callback() feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9omniFunc_Args) bw! + + # Test for using a global function name + &omnifunc = g:OmniFunc2 + new | only + setline(1, 'two') + g:OmniFunc2Args = [] + feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + assert_equal([[1, ''], [0, 'two']], g:OmniFunc2Args) + bw! + + # Test for using a script-local function name + def s:LocalOmniFunc(findstart: number, base: string): any + add(g:LocalOmniFuncArgs, [findstart, base]) + return findstart ? 0 : [] + enddef + &omnifunc = s:LocalOmniFunc + new | only + setline(1, 'three') + g:LocalOmniFuncArgs = [] + feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') + assert_equal([[1, ''], [0, 'three']], g:LocalOmniFuncArgs) + bw! END " call CheckScriptSuccess(lines) @@ -1887,6 +1979,29 @@ func Test_thesaurusfunc_callback() END call CheckLegacyAndVim9Success(lines) + " Test for using a script-local function name + func s:TsrFunc3(findstart, base) + call add(g:TsrFunc3Args, [a:findstart, a:base]) + return a:findstart ? 0 : [] + endfunc + set tsrfu=s:TsrFunc3 + new + call setline(1, 'script1') + let g:TsrFunc3Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'script1']], g:TsrFunc3Args) + bw! + + let &tsrfu = 's:TsrFunc3' + new + call setline(1, 'script2') + let g:TsrFunc3Args = [] + call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + call assert_equal([[1, ''], [0, 'script2']], g:TsrFunc3Args) + bw! + delfunc s:TsrFunc3 + + " invalid return value let &thesaurusfunc = {a -> 'abc'} call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') @@ -1934,11 +2049,12 @@ func Test_thesaurusfunc_callback() let lines =<< trim END vim9script - # Test for using a def function with thesaurusfunc - def Vim9tsrFunc(val: number, findstart: number, base: string): any - add(g:Vim9tsrFunc_Args, [val, findstart, base]) + def Vim9tsrFunc(callnr: number, findstart: number, base: string): any + add(g:Vim9tsrFunc_Args, [callnr, findstart, base]) return findstart ? 0 : [] enddef + + # Test for using a def function with thesaurusfunc set thesaurusfunc=function('Vim9tsrFunc',\ [60]) new | only setline(1, 'one') @@ -1946,6 +2062,28 @@ func Test_thesaurusfunc_callback() feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9tsrFunc_Args) bw! + + # Test for using a global function name + &thesaurusfunc = g:TsrFunc2 + new | only + setline(1, 'two') + g:TsrFunc2Args = [] + feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + assert_equal([[1, ''], [0, 'two']], g:TsrFunc2Args) + bw! + + # Test for using a script-local function name + def s:LocalTsrFunc(findstart: number, base: string): any + add(g:LocalTsrFuncArgs, [findstart, base]) + return findstart ? 0 : [] + enddef + &thesaurusfunc = s:LocalTsrFunc + new | only + setline(1, 'three') + g:LocalTsrFuncArgs = [] + feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') + assert_equal([[1, ''], [0, 'three']], g:LocalTsrFuncArgs) + bw! END " call CheckScriptSuccess(lines) diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 83709420bf..d9b392992f 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -591,6 +591,21 @@ func Test_opfunc_callback() END call CheckTransLegacySuccess(lines) + " Test for using a script-local function name + func s:OpFunc3(type) + let g:OpFunc3Args = [a:type] + endfunc + set opfunc=s:OpFunc3 + let g:OpFunc3Args = [] + normal! g@l + call assert_equal(['char'], g:OpFunc3Args) + + let &opfunc = 's:OpFunc3' + let g:OpFunc3Args = [] + normal! g@l + call assert_equal(['char'], g:OpFunc3Args) + delfunc s:OpFunc3 + " Using Vim9 lambda expression in legacy context should fail " set opfunc=(a)\ =>\ OpFunc1(24,\ a) let g:OpFunc1Args = [] @@ -614,14 +629,32 @@ func Test_opfunc_callback() let lines =<< trim END vim9script - # Test for using a def function with opfunc def g:Vim9opFunc(val: number, type: string): void g:OpFunc1Args = [val, type] enddef + + # Test for using a def function with opfunc set opfunc=function('g:Vim9opFunc',\ [60]) g:OpFunc1Args = [] normal! g@l assert_equal([60, 'char'], g:OpFunc1Args) + + # Test for using a global function name + &opfunc = g:OpFunc2 + g:OpFunc2Args = [] + normal! g@l + assert_equal(['char'], g:OpFunc2Args) + bw! + + # Test for using a script-local function name + def s:LocalOpFunc(type: string): void + g:LocalOpFuncArgs = [type] + enddef + &opfunc = s:LocalOpFunc + g:LocalOpFuncArgs = [] + normal! g@l + assert_equal(['char'], g:LocalOpFuncArgs) + bw! END " call CheckScriptSuccess(lines) diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index bde6f5a4fc..02cee8a8dd 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -5789,6 +5789,23 @@ func Test_qftextfunc_callback() END call CheckLegacyAndVim9Success(lines) + " Test for using a script-local function name + func s:TqfFunc2(info) + let g:TqfFunc2Args = [a:info.start_idx, a:info.end_idx] + return '' + endfunc + let g:TqfFunc2Args = [] + set quickfixtextfunc=s:TqfFunc2 + cexpr "F10:10:10:L10" + cclose + call assert_equal([1, 1], g:TqfFunc2Args) + + let &quickfixtextfunc = 's:TqfFunc2' + cexpr "F11:11:11:L11" + cclose + call assert_equal([1, 1], g:TqfFunc2Args) + delfunc s:TqfFunc2 + " set 'quickfixtextfunc' to a partial with dict. This used to cause a crash. func SetQftfFunc() let params = {'qftf': function('g:DictQftfFunc')} diff --git a/src/nvim/testdir/test_tagfunc.vim b/src/nvim/testdir/test_tagfunc.vim index 7a88723bc0..93b9c67b25 100644 --- a/src/nvim/testdir/test_tagfunc.vim +++ b/src/nvim/testdir/test_tagfunc.vim @@ -1,6 +1,8 @@ " Test 'tagfunc' source vim9.vim +source check.vim +source screendump.vim func TagFunc(pat, flag, info) let g:tagfunc_args = [a:pat, a:flag, a:info] @@ -265,38 +267,62 @@ func Test_tagfunc_callback() END call CheckLegacyAndVim9Success(lines) + " Test for using a script-local function name + func s:TagFunc3(pat, flags, info) + let g:TagFunc3Args = [a:pat, a:flags, a:info] + return v:null + endfunc + set tagfunc=s:TagFunc3 + new + let g:TagFunc3Args = [] + call assert_fails('tag a21', 'E433:') + call assert_equal(['a21', '', {}], g:TagFunc3Args) + bw! + let &tagfunc = 's:TagFunc3' + new + let g:TagFunc3Args = [] + call assert_fails('tag a22', 'E433:') + call assert_equal(['a22', '', {}], g:TagFunc3Args) + bw! + delfunc s:TagFunc3 + + " invalid return value let &tagfunc = "{a -> 'abc'}" call assert_fails("echo taglist('a')", "E987:") " Using Vim9 lambda expression in legacy context should fail " set tagfunc=(a,\ b,\ c)\ =>\ g:TagFunc1(21,\ a,\ b,\ c) - new | only + new let g:TagFunc1Args = [] " call assert_fails("tag a17", "E117:") call assert_equal([], g:TagFunc1Args) + bw! " Test for using a script local function set tagfunc=<SID>ScriptLocalTagFunc - new | only + new let g:ScriptLocalFuncArgs = [] call assert_fails('tag a15', 'E433:') call assert_equal(['a15', '', {}], g:ScriptLocalFuncArgs) + bw! " Test for using a script local funcref variable let Fn = function("s:ScriptLocalTagFunc") let &tagfunc= Fn - new | only + new let g:ScriptLocalFuncArgs = [] call assert_fails('tag a16', 'E433:') call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs) + bw! " Test for using a string(script local funcref variable) let Fn = function("s:ScriptLocalTagFunc") let &tagfunc= string(Fn) - new | only + new let g:ScriptLocalFuncArgs = [] call assert_fails('tag a16', 'E433:') call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs) + bw! " set 'tagfunc' to a partial with dict. This used to cause a crash. func SetTagFunc() @@ -322,16 +348,37 @@ func Test_tagfunc_callback() let lines =<< trim END vim9script - # Test for using function() - def Vim9tagFunc(val: number, pat: string, flags: string, info: dict<any>): any - g:Vim9tagFuncArgs = [val, pat, flags, info] + def Vim9tagFunc(callnr: number, pat: string, flags: string, info: dict<any>): any + g:Vim9tagFuncArgs = [callnr, pat, flags, info] return null enddef + + # Test for using a def function with completefunc set tagfunc=function('Vim9tagFunc',\ [60]) - new | only + new g:Vim9tagFuncArgs = [] assert_fails('tag a10', 'E433:') assert_equal([60, 'a10', '', {}], g:Vim9tagFuncArgs) + + # Test for using a global function name + &tagfunc = g:TagFunc2 + new + g:TagFunc2Args = [] + assert_fails('tag a11', 'E433:') + assert_equal(['a11', '', {}], g:TagFunc2Args) + bw! + + # Test for using a script-local function name + def s:LocalTagFunc(pat: string, flags: string, info: dict<any> ): any + g:LocalTagFuncArgs = [pat, flags, info] + return null + enddef + &tagfunc = s:LocalTagFunc + new + g:LocalTagFuncArgs = [] + assert_fails('tag a12', 'E433:') + assert_equal(['a12', '', {}], g:LocalTagFuncArgs) + bw! END " call CheckScriptSuccess(lines) |