Skip to content

Commit 7645da5

Browse files
yegappanbrammool
authored andcommitted
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)
1 parent 01a4dcb commit 7645da5

File tree

8 files changed

+215
-16
lines changed

8 files changed

+215
-16
lines changed

runtime/doc/options.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4242,7 +4242,9 @@ A jump table for the options with a short description can be found at |Q_op|.
42424242
'imactivatefunc' 'imaf' string (default "")
42434243
global
42444244
This option specifies a function that will be called to
4245-
activate or deactivate the Input Method.
4245+
activate or deactivate the Input Method. The value can be the name of
4246+
a function, a |lambda| or a |Funcref|. See |option-value-function| for
4247+
more information.
42464248
It is not used in the MS-Windows GUI version.
42474249
The expression will be evaluated in the |sandbox| when set from a
42484250
modeline, see |sandbox-option|.
@@ -4352,6 +4354,8 @@ A jump table for the options with a short description can be found at |Q_op|.
43524354
global
43534355
This option specifies a function that is called to obtain the status
43544356
of Input Method. It must return a positive number when IME is active.
4357+
The value can be the name of a function, a |lambda| or a |Funcref|.
4358+
See |option-value-function| for more information.
43554359
It is not used in the MS-Windows GUI version.
43564360

43574361
Example: >

src/alloc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ free_all_mem(void)
440440
free_prev_shellcmd();
441441
free_regexp_stuff();
442442
free_tag_stuff();
443+
free_xim_stuff();
443444
free_cd_dir();
444445
# ifdef FEAT_SIGNS
445446
free_signs();

src/gui_xim.c

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,24 @@ xim_log(char *s, ...)
6767
# define USE_IMSTATUSFUNC (*p_imsf != NUL)
6868
#endif
6969

70-
#if defined(FEAT_EVAL) && \
71-
(defined(FEAT_XIM) || defined(IME_WITHOUT_XIM) || defined(VIMDLL))
70+
#if (defined(FEAT_EVAL) && \
71+
(defined(FEAT_XIM) || defined(IME_WITHOUT_XIM) || defined(VIMDLL))) || \
72+
defined(PROTO)
73+
static callback_T imaf_cb; // 'imactivatefunc' callback function
74+
static callback_T imsf_cb; // 'imstatusfunc' callback function
75+
76+
int
77+
set_imactivatefunc_option(void)
78+
{
79+
return option_set_callback_func(p_imaf, &imaf_cb);
80+
}
81+
82+
int
83+
set_imstatusfunc_option(void)
84+
{
85+
return option_set_callback_func(p_imsf, &imsf_cb);
86+
}
87+
7288
static void
7389
call_imactivatefunc(int active)
7490
{
@@ -77,7 +93,7 @@ call_imactivatefunc(int active)
7793
argv[0].v_type = VAR_NUMBER;
7894
argv[0].vval.v_number = active ? 1 : 0;
7995
argv[1].v_type = VAR_UNKNOWN;
80-
(void)call_func_retnr(p_imaf, 1, argv);
96+
(void)call_callback_retnr(&imaf_cb, 1, argv);
8197
}
8298

8399
static int
@@ -91,12 +107,25 @@ call_imstatusfunc(void)
91107
// FIXME: :py print 'xxx' is shown duplicate result.
92108
// Use silent to avoid it.
93109
++msg_silent;
94-
is_active = call_func_retnr(p_imsf, 0, NULL);
110+
is_active = call_callback_retnr(&imsf_cb, 0, NULL);
95111
--msg_silent;
96112
return (is_active > 0);
97113
}
98114
#endif
99115

116+
#if defined(EXITFREE) || defined(PROTO)
117+
void
118+
free_xim_stuff(void)
119+
{
120+
#if defined(FEAT_EVAL) && \
121+
(defined(FEAT_XIM) || defined(IME_WITHOUT_XIM) || defined(VIMDLL))
122+
free_callback(&imaf_cb);
123+
free_callback(&imsf_cb);
124+
# endif
125+
}
126+
#endif
127+
128+
100129
#if defined(FEAT_XIM) || defined(PROTO)
101130

102131
# if defined(FEAT_GUI_GTK) || defined(PROTO)

src/optionstr.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2330,6 +2330,23 @@ did_set_string_option(
23302330
}
23312331
#endif
23322332

2333+
#if defined(FEAT_EVAL) && \
2334+
(defined(FEAT_XIM) || defined(IME_WITHOUT_XIM) || defined(VIMDLL))
2335+
// 'imactivatefunc'
2336+
else if (gvarp == &p_imaf)
2337+
{
2338+
if (set_imactivatefunc_option() == FAIL)
2339+
errmsg = e_invarg;
2340+
}
2341+
2342+
// 'imstatusfunc'
2343+
else if (gvarp == &p_imsf)
2344+
{
2345+
if (set_imstatusfunc_option() == FAIL)
2346+
errmsg = e_invarg;
2347+
}
2348+
#endif
2349+
23332350
// 'operatorfunc'
23342351
else if (varp == &p_opfunc)
23352352
{

src/proto/gui_xim.pro

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
/* gui_xim.c */
2+
int set_imactivatefunc_option(void);
3+
int set_imstatusfunc_option(void);
4+
void free_xim_stuff(void);
25
void im_set_active(int active);
36
void xim_set_focus(int focus);
47
void im_set_position(int row, int col);

src/testdir/test_iminsert.vim

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
source view_util.vim
44
source check.vim
5+
source vim9.vim
56

67
let s:imactivatefunc_called = 0
78
let s:imstatusfunc_called = 0
@@ -107,4 +108,143 @@ func Test_iminsert_toggle()
107108
close!
108109
endfunc
109110

111+
" Test for different ways of setting the 'imactivatefunc' and 'imstatusfunc'
112+
" options
113+
func Test_imactivatefunc_imstatusfunc_callback()
114+
CheckNotMSWindows
115+
func IMactivatefunc1(active)
116+
let g:IMactivatefunc_called += 1
117+
endfunc
118+
func IMstatusfunc1()
119+
let g:IMstatusfunc_called += 1
120+
return 1
121+
endfunc
122+
let g:IMactivatefunc_called = 0
123+
let g:IMstatusfunc_called = 0
124+
set iminsert=2
125+
126+
" Test for using a function()
127+
set imactivatefunc=function('IMactivatefunc1')
128+
set imstatusfunc=function('IMstatusfunc1')
129+
normal! i
130+
131+
" Using a funcref variable to set 'completefunc'
132+
let Fn1 = function('IMactivatefunc1')
133+
let &imactivatefunc = string(Fn1)
134+
let Fn2 = function('IMstatusfunc1')
135+
let &imstatusfunc = string(Fn2)
136+
normal! i
137+
call assert_fails('let &imactivatefunc = Fn1', 'E729:')
138+
call assert_fails('let &imstatusfunc = Fn2', 'E729:')
139+
140+
" Test for using a funcref()
141+
set imactivatefunc=funcref('IMactivatefunc1')
142+
set imstatusfunc=funcref('IMstatusfunc1')
143+
normal! i
144+
145+
" Using a funcref variable to set 'imactivatefunc'
146+
let Fn1 = funcref('IMactivatefunc1')
147+
let &imactivatefunc = string(Fn1)
148+
let Fn2 = funcref('IMstatusfunc1')
149+
let &imstatusfunc = string(Fn2)
150+
normal! i
151+
call assert_fails('let &imactivatefunc = Fn1', 'E729:')
152+
call assert_fails('let &imstatusfunc = Fn2', 'E729:')
153+
154+
" Test for using a lambda function
155+
set imactivatefunc={a\ ->\ IMactivatefunc1(a)}
156+
set imstatusfunc={\ ->\ IMstatusfunc1()}
157+
normal! i
158+
159+
" Set 'imactivatefunc' and 'imstatusfunc' to a lambda expression
160+
let &imactivatefunc = '{a -> IMactivatefunc1(a)}'
161+
let &imstatusfunc = '{ -> IMstatusfunc1()}'
162+
normal! i
163+
164+
" Set 'imactivatefunc' 'imstatusfunc' to a variable with a lambda expression
165+
let Lambda1 = {a -> IMactivatefunc1(a)}
166+
let Lambda2 = { -> IMstatusfunc1()}
167+
let &imactivatefunc = string(Lambda1)
168+
let &imstatusfunc = string(Lambda2)
169+
normal! i
170+
call assert_fails('let &imactivatefunc = Lambda1', 'E729:')
171+
call assert_fails('let &imstatusfunc = Lambda2', 'E729:')
172+
173+
" Test for clearing the 'completefunc' option
174+
set imactivatefunc='' imstatusfunc=''
175+
set imactivatefunc& imstatusfunc&
176+
177+
call assert_fails("set imactivatefunc=function('abc')", "E700:")
178+
call assert_fails("set imstatusfunc=function('abc')", "E700:")
179+
call assert_fails("set imactivatefunc=funcref('abc')", "E700:")
180+
call assert_fails("set imstatusfunc=funcref('abc')", "E700:")
181+
182+
call assert_equal(7, g:IMactivatefunc_called)
183+
call assert_equal(14, g:IMstatusfunc_called)
184+
185+
" Vim9 tests
186+
let lines =<< trim END
187+
vim9script
188+
189+
# Test for using function()
190+
def IMactivatefunc1(active: number): any
191+
g:IMactivatefunc_called += 1
192+
return 1
193+
enddef
194+
def IMstatusfunc1(): number
195+
g:IMstatusfunc_called += 1
196+
return 1
197+
enddef
198+
g:IMactivatefunc_called = 0
199+
g:IMstatusfunc_called = 0
200+
set iminsert=2
201+
set imactivatefunc=function('IMactivatefunc1')
202+
set imstatusfunc=function('IMstatusfunc1')
203+
normal! i
204+
205+
# Test for using a lambda
206+
&imactivatefunc = '(a) => IMactivatefunc1(a)'
207+
&imstatusfunc = '() => IMstatusfunc1()'
208+
normal! i
209+
210+
# Test for using a variable with a lambda expression
211+
var Fn1: func = (active) => {
212+
g:IMactivatefunc_called += 1
213+
return 1
214+
}
215+
var Fn2: func = () => {
216+
g:IMstatusfunc_called += 1
217+
return 1
218+
}
219+
&imactivatefunc = string(Fn1)
220+
&imstatusfunc = string(Fn2)
221+
normal! i
222+
223+
assert_equal(3, g:IMactivatefunc_called)
224+
assert_equal(6, g:IMstatusfunc_called)
225+
226+
set iminsert=0
227+
set imactivatefunc=
228+
set imstatusfunc=
229+
END
230+
call CheckScriptSuccess(lines)
231+
232+
" Using Vim9 lambda expression in legacy context should fail
233+
set imactivatefunc=(a)\ =>\ IMactivatefunc1(a)
234+
set imstatusfunc=IMstatusfunc1
235+
call assert_fails('normal! i', 'E117:')
236+
set imactivatefunc=IMactivatefunc1
237+
set imstatusfunc=()\ =>\ IMstatusfunc1(a)
238+
call assert_fails('normal! i', 'E117:')
239+
240+
" cleanup
241+
delfunc IMactivatefunc1
242+
delfunc IMstatusfunc1
243+
set iminsert=0
244+
set imactivatefunc=
245+
set imstatusfunc=
246+
247+
%bw!
248+
endfunc
249+
110250
" vim: shiftwidth=2 sts=2 expandtab

src/testdir/test_ins_complete.vim

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -923,7 +923,7 @@ func Test_completefunc_callback()
923923
call add(g:MycompleteFunc3_args, [a:findstart, a:base])
924924
return a:findstart ? 0 : []
925925
endfunc
926-
set completefunc={a,\ b,\ ->\ MycompleteFunc3(a,\ b,)}
926+
set completefunc={a,\ b\ ->\ MycompleteFunc3(a,\ b)}
927927
new | only
928928
call setline(1, 'five')
929929
let g:MycompleteFunc3_args = []
@@ -986,26 +986,29 @@ func Test_completefunc_callback()
986986
bw!
987987

988988
# Test for using a lambda
989-
def MycompleteFunc2(findstart: number, base: string): any
990-
add(g:MycompleteFunc2_args, [findstart, base])
989+
def LambdaComplete1(findstart: number, base: string): any
990+
add(g:LambdaComplete1_args, [findstart, base])
991991
return findstart ? 0 : []
992992
enddef
993-
&completefunc = '(a, b) => MycompleteFunc2(a, b)'
993+
&completefunc = '(a, b) => LambdaComplete1(a, b)'
994994
new | only
995995
setline(1, 'two')
996-
g:MycompleteFunc2_args = []
996+
g:LambdaComplete1_args = []
997997
feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
998-
assert_equal([[1, ''], [0, 'two']], g:MycompleteFunc2_args)
998+
assert_equal([[1, ''], [0, 'two']], g:LambdaComplete1_args)
999999
bw!
10001000

10011001
# Test for using a variable with a lambda expression
1002-
var Fn: func = (a, b) => MycompleteFunc2(a, b)
1002+
var Fn: func = (findstart, base) => {
1003+
add(g:LambdaComplete2_args, [findstart, base])
1004+
return findstart ? 0 : []
1005+
}
10031006
&completefunc = string(Fn)
10041007
new | only
10051008
setline(1, 'three')
1006-
g:MycompleteFunc2_args = []
1009+
g:LambdaComplete2_args = []
10071010
feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
1008-
assert_equal([[1, ''], [0, 'three']], g:MycompleteFunc2_args)
1011+
assert_equal([[1, ''], [0, 'three']], g:LambdaComplete2_args)
10091012
bw!
10101013
END
10111014
call CheckScriptSuccess(lines)
@@ -1080,7 +1083,7 @@ func Test_omnifunc_callback()
10801083
call add(g:MyomniFunc3_args, [a:findstart, a:base])
10811084
return a:findstart ? 0 : []
10821085
endfunc
1083-
set omnifunc={a,\ b,\ ->\ MyomniFunc3(a,\ b,)}
1086+
set omnifunc={a,\ b\ ->\ MyomniFunc3(a,\ b)}
10841087
new | only
10851088
call setline(1, 'five')
10861089
let g:MyomniFunc3_args = []
@@ -1237,7 +1240,7 @@ func Test_thesaurusfunc_callback()
12371240
call add(g:MytsrFunc3_args, [a:findstart, a:base])
12381241
return a:findstart ? 0 : []
12391242
endfunc
1240-
set thesaurusfunc={a,\ b,\ ->\ MytsrFunc3(a,\ b,)}
1243+
set thesaurusfunc={a,\ b\ ->\ MytsrFunc3(a,\ b)}
12411244
new | only
12421245
call setline(1, 'five')
12431246
let g:MytsrFunc3_args = []

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,8 @@ static char *(features[]) =
753753

754754
static int included_patches[] =
755755
{ /* Add new patch number below this line */
756+
/**/
757+
3735,
756758
/**/
757759
3734,
758760
/**/

0 commit comments

Comments
 (0)