To: vim_dev@googlegroups.com Subject: Patch 8.2.3735 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3735 Problem: Cannot use a lambda for 'imactivatefunc'. Solution: Add lambda support for 'imactivatefunc' and 'imstatusfunc'. (Yegappan Lakshmanan, closes #9275) Files: runtime/doc/options.txt, src/alloc.c, src/gui_xim.c, src/optionstr.c, src/proto/gui_xim.pro, src/testdir/test_iminsert.vim, src/testdir/test_ins_complete.vim *** ../vim-8.2.3734/runtime/doc/options.txt 2021-12-03 11:08:34.256842709 +0000 --- runtime/doc/options.txt 2021-12-04 13:58:20.200881316 +0000 *************** *** 4228,4234 **** 'imactivatefunc' 'imaf' string (default "") global This option specifies a function that will be called to ! activate or deactivate the Input Method. It is not used in the MS-Windows GUI version. The expression will be evaluated in the |sandbox| when set from a modeline, see |sandbox-option|. --- 4247,4255 ---- 'imactivatefunc' 'imaf' string (default "") global This option specifies a function that will be called to ! activate or deactivate the Input Method. The value can be the name of ! a function, a |lambda| or a |Funcref|. See |option-value-function| for ! more information. It is not used in the MS-Windows GUI version. The expression will be evaluated in the |sandbox| when set from a modeline, see |sandbox-option|. *************** *** 4338,4343 **** --- 4359,4366 ---- global This option specifies a function that is called to obtain the status of Input Method. It must return a positive number when IME is active. + The value can be the name of a function, a |lambda| or a |Funcref|. + See |option-value-function| for more information. It is not used in the MS-Windows GUI version. Example: > *** ../vim-8.2.3734/src/alloc.c 2021-11-29 20:39:06.666101630 +0000 --- src/alloc.c 2021-12-04 13:58:20.200881316 +0000 *************** *** 440,445 **** --- 440,446 ---- free_prev_shellcmd(); free_regexp_stuff(); free_tag_stuff(); + free_xim_stuff(); free_cd_dir(); # ifdef FEAT_SIGNS free_signs(); *** ../vim-8.2.3734/src/gui_xim.c 2021-08-02 17:07:15.186473836 +0100 --- src/gui_xim.c 2021-12-04 13:58:20.200881316 +0000 *************** *** 67,74 **** # define USE_IMSTATUSFUNC (*p_imsf != NUL) #endif ! #if defined(FEAT_EVAL) && \ ! (defined(FEAT_XIM) || defined(IME_WITHOUT_XIM) || defined(VIMDLL)) static void call_imactivatefunc(int active) { --- 67,90 ---- # define USE_IMSTATUSFUNC (*p_imsf != NUL) #endif ! #if (defined(FEAT_EVAL) && \ ! (defined(FEAT_XIM) || defined(IME_WITHOUT_XIM) || defined(VIMDLL))) || \ ! defined(PROTO) ! static callback_T imaf_cb; // 'imactivatefunc' callback function ! static callback_T imsf_cb; // 'imstatusfunc' callback function ! ! int ! set_imactivatefunc_option(void) ! { ! return option_set_callback_func(p_imaf, &imaf_cb); ! } ! ! int ! set_imstatusfunc_option(void) ! { ! return option_set_callback_func(p_imsf, &imsf_cb); ! } ! static void call_imactivatefunc(int active) { *************** *** 77,83 **** argv[0].v_type = VAR_NUMBER; argv[0].vval.v_number = active ? 1 : 0; argv[1].v_type = VAR_UNKNOWN; ! (void)call_func_retnr(p_imaf, 1, argv); } static int --- 93,99 ---- argv[0].v_type = VAR_NUMBER; argv[0].vval.v_number = active ? 1 : 0; argv[1].v_type = VAR_UNKNOWN; ! (void)call_callback_retnr(&imaf_cb, 1, argv); } static int *************** *** 91,102 **** // FIXME: :py print 'xxx' is shown duplicate result. // Use silent to avoid it. ++msg_silent; ! is_active = call_func_retnr(p_imsf, 0, NULL); --msg_silent; return (is_active > 0); } #endif #if defined(FEAT_XIM) || defined(PROTO) # if defined(FEAT_GUI_GTK) || defined(PROTO) --- 107,131 ---- // FIXME: :py print 'xxx' is shown duplicate result. // Use silent to avoid it. ++msg_silent; ! is_active = call_callback_retnr(&imsf_cb, 0, NULL); --msg_silent; return (is_active > 0); } #endif + #if defined(EXITFREE) || defined(PROTO) + void + free_xim_stuff(void) + { + #if defined(FEAT_EVAL) && \ + (defined(FEAT_XIM) || defined(IME_WITHOUT_XIM) || defined(VIMDLL)) + free_callback(&imaf_cb); + free_callback(&imsf_cb); + # endif + } + #endif + + #if defined(FEAT_XIM) || defined(PROTO) # if defined(FEAT_GUI_GTK) || defined(PROTO) *** ../vim-8.2.3734/src/optionstr.c 2021-12-03 11:08:34.256842709 +0000 --- src/optionstr.c 2021-12-04 13:58:20.200881316 +0000 *************** *** 2330,2335 **** --- 2330,2352 ---- } #endif + #if defined(FEAT_EVAL) && \ + (defined(FEAT_XIM) || defined(IME_WITHOUT_XIM) || defined(VIMDLL)) + // 'imactivatefunc' + else if (gvarp == &p_imaf) + { + if (set_imactivatefunc_option() == FAIL) + errmsg = e_invarg; + } + + // 'imstatusfunc' + else if (gvarp == &p_imsf) + { + if (set_imstatusfunc_option() == FAIL) + errmsg = e_invarg; + } + #endif + // 'operatorfunc' else if (varp == &p_opfunc) { *** ../vim-8.2.3734/src/proto/gui_xim.pro 2020-06-01 13:34:22.027462262 +0100 --- src/proto/gui_xim.pro 2021-12-04 13:58:20.200881316 +0000 *************** *** 1,4 **** --- 1,7 ---- /* gui_xim.c */ + int set_imactivatefunc_option(void); + int set_imstatusfunc_option(void); + void free_xim_stuff(void); void im_set_active(int active); void xim_set_focus(int focus); void im_set_position(int row, int col); *** ../vim-8.2.3734/src/testdir/test_iminsert.vim 2020-08-13 20:05:31.789361598 +0100 --- src/testdir/test_iminsert.vim 2021-12-04 13:58:20.200881316 +0000 *************** *** 2,7 **** --- 2,8 ---- source view_util.vim source check.vim + source vim9.vim let s:imactivatefunc_called = 0 let s:imstatusfunc_called = 0 *************** *** 107,110 **** --- 108,250 ---- close! endfunc + " Test for different ways of setting the 'imactivatefunc' and 'imstatusfunc' + " options + func Test_imactivatefunc_imstatusfunc_callback() + CheckNotMSWindows + func IMactivatefunc1(active) + let g:IMactivatefunc_called += 1 + endfunc + func IMstatusfunc1() + let g:IMstatusfunc_called += 1 + return 1 + endfunc + let g:IMactivatefunc_called = 0 + let g:IMstatusfunc_called = 0 + set iminsert=2 + + " Test for using a function() + set imactivatefunc=function('IMactivatefunc1') + set imstatusfunc=function('IMstatusfunc1') + normal! i + + " Using a funcref variable to set 'completefunc' + let Fn1 = function('IMactivatefunc1') + let &imactivatefunc = string(Fn1) + let Fn2 = function('IMstatusfunc1') + let &imstatusfunc = string(Fn2) + normal! i + call assert_fails('let &imactivatefunc = Fn1', 'E729:') + call assert_fails('let &imstatusfunc = Fn2', 'E729:') + + " Test for using a funcref() + set imactivatefunc=funcref('IMactivatefunc1') + set imstatusfunc=funcref('IMstatusfunc1') + normal! i + + " Using a funcref variable to set 'imactivatefunc' + let Fn1 = funcref('IMactivatefunc1') + let &imactivatefunc = string(Fn1) + let Fn2 = funcref('IMstatusfunc1') + let &imstatusfunc = string(Fn2) + normal! i + call assert_fails('let &imactivatefunc = Fn1', 'E729:') + call assert_fails('let &imstatusfunc = Fn2', 'E729:') + + " Test for using a lambda function + set imactivatefunc={a\ ->\ IMactivatefunc1(a)} + set imstatusfunc={\ ->\ IMstatusfunc1()} + normal! i + + " Set 'imactivatefunc' and 'imstatusfunc' to a lambda expression + let &imactivatefunc = '{a -> IMactivatefunc1(a)}' + let &imstatusfunc = '{ -> IMstatusfunc1()}' + normal! i + + " Set 'imactivatefunc' 'imstatusfunc' to a variable with a lambda expression + let Lambda1 = {a -> IMactivatefunc1(a)} + let Lambda2 = { -> IMstatusfunc1()} + let &imactivatefunc = string(Lambda1) + let &imstatusfunc = string(Lambda2) + normal! i + call assert_fails('let &imactivatefunc = Lambda1', 'E729:') + call assert_fails('let &imstatusfunc = Lambda2', 'E729:') + + " Test for clearing the 'completefunc' option + set imactivatefunc='' imstatusfunc='' + set imactivatefunc& imstatusfunc& + + call assert_fails("set imactivatefunc=function('abc')", "E700:") + call assert_fails("set imstatusfunc=function('abc')", "E700:") + call assert_fails("set imactivatefunc=funcref('abc')", "E700:") + call assert_fails("set imstatusfunc=funcref('abc')", "E700:") + + call assert_equal(7, g:IMactivatefunc_called) + call assert_equal(14, g:IMstatusfunc_called) + + " Vim9 tests + let lines =<< trim END + vim9script + + # Test for using function() + def IMactivatefunc1(active: number): any + g:IMactivatefunc_called += 1 + return 1 + enddef + def IMstatusfunc1(): number + g:IMstatusfunc_called += 1 + return 1 + enddef + g:IMactivatefunc_called = 0 + g:IMstatusfunc_called = 0 + set iminsert=2 + set imactivatefunc=function('IMactivatefunc1') + set imstatusfunc=function('IMstatusfunc1') + normal! i + + # Test for using a lambda + &imactivatefunc = '(a) => IMactivatefunc1(a)' + &imstatusfunc = '() => IMstatusfunc1()' + normal! i + + # Test for using a variable with a lambda expression + var Fn1: func = (active) => { + g:IMactivatefunc_called += 1 + return 1 + } + var Fn2: func = () => { + g:IMstatusfunc_called += 1 + return 1 + } + &imactivatefunc = string(Fn1) + &imstatusfunc = string(Fn2) + normal! i + + assert_equal(3, g:IMactivatefunc_called) + assert_equal(6, g:IMstatusfunc_called) + + set iminsert=0 + set imactivatefunc= + set imstatusfunc= + END + call CheckScriptSuccess(lines) + + " Using Vim9 lambda expression in legacy context should fail + set imactivatefunc=(a)\ =>\ IMactivatefunc1(a) + set imstatusfunc=IMstatusfunc1 + call assert_fails('normal! i', 'E117:') + set imactivatefunc=IMactivatefunc1 + set imstatusfunc=()\ =>\ IMstatusfunc1(a) + call assert_fails('normal! i', 'E117:') + + " cleanup + delfunc IMactivatefunc1 + delfunc IMstatusfunc1 + set iminsert=0 + set imactivatefunc= + set imstatusfunc= + + %bw! + endfunc + " vim: shiftwidth=2 sts=2 expandtab *** ../vim-8.2.3734/src/testdir/test_ins_complete.vim 2021-12-03 11:08:34.260842706 +0000 --- src/testdir/test_ins_complete.vim 2021-12-04 13:58:20.200881316 +0000 *************** *** 923,929 **** 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 = [] --- 923,929 ---- 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 = [] *************** *** 986,1011 **** 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\\\", '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\\\", 'x') ! assert_equal([[1, ''], [0, 'three']], g:MycompleteFunc2_args) bw! END call CheckScriptSuccess(lines) --- 986,1014 ---- bw! # Test for using a lambda ! def LambdaComplete1(findstart: number, base: string): any ! add(g:LambdaComplete1_args, [findstart, base]) return findstart ? 0 : [] enddef ! &completefunc = '(a, b) => LambdaComplete1(a, b)' new | only setline(1, 'two') ! g:LambdaComplete1_args = [] feedkeys("A\\\", 'x') ! assert_equal([[1, ''], [0, 'two']], g:LambdaComplete1_args) bw! # Test for using a variable with a lambda expression ! var Fn: func = (findstart, base) => { ! add(g:LambdaComplete2_args, [findstart, base]) ! return findstart ? 0 : [] ! } &completefunc = string(Fn) new | only setline(1, 'three') ! g:LambdaComplete2_args = [] feedkeys("A\\\", 'x') ! assert_equal([[1, ''], [0, 'three']], g:LambdaComplete2_args) bw! END call CheckScriptSuccess(lines) *************** *** 1080,1086 **** 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 = [] --- 1083,1089 ---- 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 = [] *************** *** 1237,1243 **** 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 = [] --- 1240,1246 ---- 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 = [] *** ../vim-8.2.3734/src/version.c 2021-12-04 13:15:07.336273914 +0000 --- src/version.c 2021-12-04 13:59:46.328943183 +0000 *************** *** 755,756 **** --- 755,758 ---- { /* Add new patch number below this line */ + /**/ + 3735, /**/ -- If the Universe is constantly expanding, why can't I ever find a parking space? /// 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 ///