patch 8.2.3735: cannot use a lambda for 'imactivatefunc'

Problem:    Cannot use a lambda for 'imactivatefunc'.
Solution:   Add lambda support for 'imactivatefunc' and 'imstatusfunc'.
            (Yegappan Lakshmanan, closes #9275)
diff --git a/src/alloc.c b/src/alloc.c
index 42caa41..38b452c 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -440,6 +440,7 @@
     free_prev_shellcmd();
     free_regexp_stuff();
     free_tag_stuff();
+    free_xim_stuff();
     free_cd_dir();
 # ifdef FEAT_SIGNS
     free_signs();
diff --git a/src/gui_xim.c b/src/gui_xim.c
index 2a12412..6d72ef1 100644
--- a/src/gui_xim.c
+++ b/src/gui_xim.c
@@ -67,8 +67,24 @@
 # define USE_IMSTATUSFUNC (*p_imsf != NUL)
 #endif
 
-#if defined(FEAT_EVAL) && \
-    (defined(FEAT_XIM) || defined(IME_WITHOUT_XIM) || defined(VIMDLL))
+#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,7 +93,7 @@
     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);
+    (void)call_callback_retnr(&imaf_cb, 1, argv);
 }
 
     static int
@@ -91,12 +107,25 @@
     // FIXME: :py print 'xxx' is shown duplicate result.
     // Use silent to avoid it.
     ++msg_silent;
-    is_active = call_func_retnr(p_imsf, 0, NULL);
+    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)
diff --git a/src/optionstr.c b/src/optionstr.c
index fe62cb3..dbb93cd 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -2330,6 +2330,23 @@
     }
 #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)
     {
diff --git a/src/proto/gui_xim.pro b/src/proto/gui_xim.pro
index 01fb56c..60f30d7 100644
--- a/src/proto/gui_xim.pro
+++ b/src/proto/gui_xim.pro
@@ -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);
diff --git a/src/testdir/test_iminsert.vim b/src/testdir/test_iminsert.vim
index d0991c3..14063f9 100644
--- a/src/testdir/test_iminsert.vim
+++ b/src/testdir/test_iminsert.vim
@@ -2,6 +2,7 @@
 
 source view_util.vim
 source check.vim
+source vim9.vim
 
 let s:imactivatefunc_called = 0
 let s:imstatusfunc_called = 0
@@ -107,4 +108,143 @@
   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
diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim
index 083fa68..d39acf4 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -923,7 +923,7 @@
     call add(g:MycompleteFunc3_args, [a:findstart, a:base])
     return a:findstart ? 0 : []
   endfunc
-  set completefunc={a,\ b,\ ->\ MycompleteFunc3(a,\ b,)}
+  set completefunc={a,\ b\ ->\ MycompleteFunc3(a,\ b)}
   new | only
   call setline(1, 'five')
   let g:MycompleteFunc3_args = []
@@ -986,26 +986,29 @@
     bw!
 
     # Test for using a lambda
-    def MycompleteFunc2(findstart: number, base: string): any
-      add(g:MycompleteFunc2_args, [findstart, base])
+    def LambdaComplete1(findstart: number, base: string): any
+      add(g:LambdaComplete1_args, [findstart, base])
       return findstart ? 0 : []
     enddef
-    &completefunc = '(a, b) => MycompleteFunc2(a, b)'
+    &completefunc = '(a, b) => LambdaComplete1(a, b)'
     new | only
     setline(1, 'two')
-    g:MycompleteFunc2_args = []
+    g:LambdaComplete1_args = []
     feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
-    assert_equal([[1, ''], [0, 'two']], g:MycompleteFunc2_args)
+    assert_equal([[1, ''], [0, 'two']], g:LambdaComplete1_args)
     bw!
 
     # Test for using a variable with a lambda expression
-    var Fn: func = (a, b) => MycompleteFunc2(a, b)
+    var Fn: func = (findstart, base) => {
+            add(g:LambdaComplete2_args, [findstart, base])
+            return findstart ? 0 : []
+        }
     &completefunc = string(Fn)
     new | only
     setline(1, 'three')
-    g:MycompleteFunc2_args = []
+    g:LambdaComplete2_args = []
     feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
-    assert_equal([[1, ''], [0, 'three']], g:MycompleteFunc2_args)
+    assert_equal([[1, ''], [0, 'three']], g:LambdaComplete2_args)
     bw!
   END
   call CheckScriptSuccess(lines)
@@ -1080,7 +1083,7 @@
     call add(g:MyomniFunc3_args, [a:findstart, a:base])
     return a:findstart ? 0 : []
   endfunc
-  set omnifunc={a,\ b,\ ->\ MyomniFunc3(a,\ b,)}
+  set omnifunc={a,\ b\ ->\ MyomniFunc3(a,\ b)}
   new | only
   call setline(1, 'five')
   let g:MyomniFunc3_args = []
@@ -1237,7 +1240,7 @@
     call add(g:MytsrFunc3_args, [a:findstart, a:base])
     return a:findstart ? 0 : []
   endfunc
-  set thesaurusfunc={a,\ b,\ ->\ MytsrFunc3(a,\ b,)}
+  set thesaurusfunc={a,\ b\ ->\ MytsrFunc3(a,\ b)}
   new | only
   call setline(1, 'five')
   let g:MytsrFunc3_args = []
diff --git a/src/version.c b/src/version.c
index 7948ecc..55d2331 100644
--- a/src/version.c
+++ b/src/version.c
@@ -754,6 +754,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3735,
+/**/
     3734,
 /**/
     3733,