To: vim_dev@googlegroups.com Subject: Patch 8.2.3838 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3838 Problem: Cannot use script-local function for setting *func options. Solution: Use the script context. (Yegappan Lakshmanan, closes #9362) Files: src/option.c, src/testdir/dumps/Test_set_tagfunc_on_cmdline.dump, src/testdir/test_ins_complete.vim, src/testdir/test_normal.vim, src/testdir/test_quickfix.vim, src/testdir/test_tagfunc.vim *** ../vim-8.2.3837/src/option.c 2021-12-16 19:45:18.040003129 +0000 --- src/option.c 2021-12-17 16:15:32.565361612 +0000 *************** *** 7199,7215 **** return OK; } - if (STRNCMP(optval, "s:", 2) == 0 && !SCRIPT_ID_VALID(current_sctx.sc_sid)) - return FAIL; - if (*optval == '{' || (in_vim9script() && *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; --- 7199,7237 ---- return OK; } if (*optval == '{' || (in_vim9script() && *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 ! ! // Function name starting with "s:" are supported only in a vimscript ! // context. ! if (STRNCMP(optval, "s:", 2) == 0) ! { ! char sid_buf[25]; ! char_u *funcname; ! ! if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) ! { ! emsg(_(e_using_sid_not_in_script_context)); ! return FAIL; ! } ! // Expand s: prefix into nr_ ! sprintf(sid_buf, "%ld_", (long)current_sctx.sc_sid); ! funcname = alloc(STRLEN(sid_buf) + STRLEN(optval + 2) + 1); ! if (funcname == NULL) ! return FAIL; ! STRCPY(funcname, sid_buf); ! STRCAT(funcname, optval + 2); ! tv = alloc_string_tv(funcname); ! } ! else ! tv = alloc_string_tv(vim_strsave(optval)); ! } if (tv == NULL) return FAIL; *** ../vim-8.2.3837/src/testdir/dumps/Test_set_tagfunc_on_cmdline.dump 2021-12-16 19:45:18.044003124 +0000 --- src/testdir/dumps/Test_set_tagfunc_on_cmdline.dump 1970-01-01 00:00:00.000000000 +0000 *************** *** 1,6 **** - | +0&#ffffff0@74 - @75 - @75 - |E+0#ffffff16#e000002|r@1|o|r| |d|e|t|e|c|t|e|d| |w|h|i|l|e| |p|r|o|c|e|s@1|i|n|g| |c|o|m@1|a|n|d| |l|i|n|e|:| +0#0000000#ffffff0@29 - |E+0#ffffff16#e000002|4|7|4|:| |I|n|v|a|l|i|d| |a|r|g|u|m|e|n|t|:| |t|a|g|f|u|n|c|=|s|:|F|u|n|c| +0#0000000#ffffff0@36 - |P+0#00e0003&|r|e|s@1| |E|N|T|E|R| |o|r| |t|y|p|e| |c|o|m@1|a|n|d| |t|o| |c|o|n|t|i|n|u|e> +0#0000000&@35 --- 0 ---- *** ../vim-8.2.3837/src/testdir/test_ins_complete.vim 2021-12-12 20:07:58.720337336 +0000 --- src/testdir/test_ins_complete.vim 2021-12-17 16:15:32.565361612 +0000 *************** *** 880,892 **** endfunc let lines =<< trim END ! #" Test for using a function name LET &completefunc = 'g:CompleteFunc2' new ! call setline(1, 'zero') LET g:CompleteFunc2Args = [] call feedkeys("A\\\", 'x') ! call assert_equal([[1, ''], [0, 'zero']], g:CompleteFunc2Args) bw! #" Test for using a function() --- 880,892 ---- endfunc let lines =<< trim END ! #" Test for using a global function name LET &completefunc = 'g:CompleteFunc2' new ! call setline(1, 'global') LET g:CompleteFunc2Args = [] call feedkeys("A\\\", 'x') ! call assert_equal([[1, ''], [0, 'global']], g:CompleteFunc2Args) bw! #" Test for using a function() *************** *** 1022,1027 **** --- 1022,1050 ---- 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\\\", '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\\\", 'x') + call assert_equal([[1, ''], [0, 'script2']], g:CompleteFunc3Args) + bw! + delfunc s:CompleteFunc3 + + " invalid return value let &completefunc = {a -> 'abc'} call feedkeys("A\\\", 'x') *************** *** 1056,1066 **** 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]) return findstart ? 0 : [] enddef set completefunc=function('Vim9CompleteFunc',\ [60]) new | only setline(1, 'one') --- 1079,1090 ---- let lines =<< trim END vim9script ! 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') *************** *** 1068,1073 **** --- 1092,1119 ---- feedkeys("A\\\", '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\\\", '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\\\", 'x') + assert_equal([[1, ''], [0, 'three']], g:LocalCompleteFuncArgs) + bw! END call CheckScriptSuccess(lines) *************** *** 1233,1238 **** --- 1279,1307 ---- 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\\\", '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\\\", 'x') + call assert_equal([[1, ''], [0, 'script2']], g:OmniFunc3Args) + bw! + delfunc s:OmniFunc3 + + " invalid return value let &omnifunc = {a -> 'abc'} call feedkeys("A\\\", 'x') *************** *** 1267,1277 **** 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]) return findstart ? 0 : [] enddef set omnifunc=function('Vim9omniFunc',\ [60]) new | only setline(1, 'one') --- 1336,1347 ---- let lines =<< trim END vim9script ! 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') *************** *** 1279,1284 **** --- 1349,1376 ---- feedkeys("A\\\", '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\\\", '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\\\", 'x') + assert_equal([[1, ''], [0, 'three']], g:LocalOmniFuncArgs) + bw! END call CheckScriptSuccess(lines) *************** *** 1467,1472 **** --- 1559,1587 ---- 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\\\", '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\\\", 'x') + call assert_equal([[1, ''], [0, 'script2']], g:TsrFunc3Args) + bw! + delfunc s:TsrFunc3 + + " invalid return value let &thesaurusfunc = {a -> 'abc'} call feedkeys("A\\\", 'x') *************** *** 1514,1524 **** 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]) return findstart ? 0 : [] enddef set thesaurusfunc=function('Vim9tsrFunc',\ [60]) new | only setline(1, 'one') --- 1629,1640 ---- let lines =<< trim END vim9script ! 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') *************** *** 1526,1531 **** --- 1642,1669 ---- feedkeys("A\\\", '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\\\", '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\\\", 'x') + assert_equal([[1, ''], [0, 'three']], g:LocalTsrFuncArgs) + bw! END call CheckScriptSuccess(lines) *** ../vim-8.2.3837/src/testdir/test_normal.vim 2021-12-13 13:11:00.696262332 +0000 --- src/testdir/test_normal.vim 2021-12-17 16:15:32.569361649 +0000 *************** *** 575,580 **** --- 575,595 ---- 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 = [] *************** *** 598,611 **** 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 set opfunc=function('g:Vim9opFunc',\ [60]) g:OpFunc1Args = [] normal! g@l assert_equal([60, 'char'], g:OpFunc1Args) END call CheckScriptSuccess(lines) --- 613,644 ---- let lines =<< trim END vim9script 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) *** ../vim-8.2.3837/src/testdir/test_quickfix.vim 2021-12-15 12:28:19.151133449 +0000 --- src/testdir/test_quickfix.vim 2021-12-17 16:15:32.569361649 +0000 *************** *** 5387,5392 **** --- 5387,5409 ---- 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')} *** ../vim-8.2.3837/src/testdir/test_tagfunc.vim 2021-12-16 19:45:18.044003124 +0000 --- src/testdir/test_tagfunc.vim 2021-12-17 16:15:32.569361649 +0000 *************** *** 267,304 **** END call CheckLegacyAndVim9Success(lines) 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 let g:TagFunc1Args = [] call assert_fails("tag a17", "E117:") call assert_equal([], g:TagFunc1Args) " Test for using a script local function set tagfunc=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= Fn ! new | only let g:ScriptLocalFuncArgs = [] call assert_fails('tag a16', 'E433:') call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs) " Test for using a string(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) " set 'tagfunc' to a partial with dict. This used to cause a crash. func SetTagFunc() --- 267,328 ---- 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 let g:TagFunc1Args = [] call assert_fails("tag a17", "E117:") call assert_equal([], g:TagFunc1Args) + bw! " Test for using a script local function set tagfunc=ScriptLocalTagFunc ! 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 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 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() *************** *** 324,339 **** let lines =<< trim END vim9script ! # Test for using function() ! def Vim9tagFunc(val: number, pat: string, flags: string, info: dict): any ! g:Vim9tagFuncArgs = [val, pat, flags, info] return null enddef set tagfunc=function('Vim9tagFunc',\ [60]) ! new | only g:Vim9tagFuncArgs = [] assert_fails('tag a10', 'E433:') assert_equal([60, 'a10', '', {}], g:Vim9tagFuncArgs) END call CheckScriptSuccess(lines) --- 348,384 ---- let lines =<< trim END vim9script ! def Vim9tagFunc(callnr: number, pat: string, flags: string, info: dict): any ! g:Vim9tagFuncArgs = [callnr, pat, flags, info] return null enddef + + # Test for using a def function with completefunc set tagfunc=function('Vim9tagFunc',\ [60]) ! 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 + 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) *************** *** 344,356 **** %bw! endfunc - func Test_set_tagfunc_on_cmdline() - CheckScreendump - - let buf = RunVimInTerminal(' +"set tagfunc=s:Func"', #{rows: 6, wait_for_ruler: 0}) - call VerifyScreenDump(buf, 'Test_set_tagfunc_on_cmdline', {}) - call StopVimInTerminal(buf) - endfunc - - " vim: shiftwidth=2 sts=2 expandtab --- 389,392 ---- *** ../vim-8.2.3837/src/version.c 2021-12-17 16:00:00.664431516 +0000 --- src/version.c 2021-12-17 16:17:25.558307115 +0000 *************** *** 751,752 **** --- 751,754 ---- { /* Add new patch number below this line */ + /**/ + 3838, /**/ -- Q: What is a patch 22? A: A patch you need to include to make it possible to include patches. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///