mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 7.4.2120
Problem: User defined functions can't be a closure. Solution: Add the "closure" argument. Allow using :unlet on a bound variable. (Yasuhiro Matsumoto, Ken Takata)
This commit is contained in:
parent
1e96d9bf98
commit
10ce39a0d5
@ -1240,6 +1240,7 @@ function returns: >
|
|||||||
:let Bar = Foo(4)
|
:let Bar = Foo(4)
|
||||||
:echo Bar(6)
|
:echo Bar(6)
|
||||||
< 5
|
< 5
|
||||||
|
See also |:func-closure|.
|
||||||
|
|
||||||
Examples for using a lambda expression with |sort()|, |map()| and |filter()|: >
|
Examples for using a lambda expression with |sort()|, |map()| and |filter()|: >
|
||||||
:echo map([1, 2, 3], {idx, val -> val + 1})
|
:echo map([1, 2, 3], {idx, val -> val + 1})
|
||||||
@ -8217,7 +8218,7 @@ last defined. Example: >
|
|||||||
See |:verbose-cmd| for more information.
|
See |:verbose-cmd| for more information.
|
||||||
|
|
||||||
*E124* *E125* *E853* *E884*
|
*E124* *E125* *E853* *E884*
|
||||||
:fu[nction][!] {name}([arguments]) [range] [abort] [dict]
|
:fu[nction][!] {name}([arguments]) [range] [abort] [dict] [closure]
|
||||||
Define a new function by the name {name}. The name
|
Define a new function by the name {name}. The name
|
||||||
must be made of alphanumeric characters and '_', and
|
must be made of alphanumeric characters and '_', and
|
||||||
must start with a capital or "s:" (see above). Note
|
must start with a capital or "s:" (see above). Note
|
||||||
@ -8260,6 +8261,28 @@ See |:verbose-cmd| for more information.
|
|||||||
be invoked through an entry in a |Dictionary|. The
|
be invoked through an entry in a |Dictionary|. The
|
||||||
local variable "self" will then be set to the
|
local variable "self" will then be set to the
|
||||||
dictionary. See |Dictionary-function|.
|
dictionary. See |Dictionary-function|.
|
||||||
|
*:func-closure* *E932*
|
||||||
|
When the [closure] argument is added, the function
|
||||||
|
can access variables and arguments from the outer
|
||||||
|
scope. This is usually called a closure. In this
|
||||||
|
example Bar() uses "x" from the scope of Foo(). It
|
||||||
|
remains referenced even after Foo() returns: >
|
||||||
|
:function! Foo()
|
||||||
|
: let x = 0
|
||||||
|
: function! Bar() closure
|
||||||
|
: let x += 1
|
||||||
|
: return x
|
||||||
|
: endfunction
|
||||||
|
: return function('Bar')
|
||||||
|
:endfunction
|
||||||
|
|
||||||
|
:let F = Foo()
|
||||||
|
:echo F()
|
||||||
|
< 1 >
|
||||||
|
:echo F()
|
||||||
|
< 2 >
|
||||||
|
:echo F()
|
||||||
|
< 3
|
||||||
|
|
||||||
*function-search-undo*
|
*function-search-undo*
|
||||||
The last used search pattern and the redo command "."
|
The last used search pattern and the redo command "."
|
||||||
|
@ -2837,7 +2837,9 @@ do_unlet(char_u *name, int forceit)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
hi = hash_find(ht, varname);
|
hi = hash_find(ht, varname);
|
||||||
if (!HASHITEM_EMPTY(hi))
|
if (HASHITEM_EMPTY(hi))
|
||||||
|
hi = find_hi_in_scoped_ht(name, &varname, &ht);
|
||||||
|
if (hi != NULL && !HASHITEM_EMPTY(hi))
|
||||||
{
|
{
|
||||||
di = HI2DI(hi);
|
di = HI2DI(hi);
|
||||||
if (var_check_fixed(di->di_flags, name, FALSE)
|
if (var_check_fixed(di->di_flags, name, FALSE)
|
||||||
|
@ -46,6 +46,7 @@ void *clear_current_funccal(void);
|
|||||||
void restore_current_funccal(void *f);
|
void restore_current_funccal(void *f);
|
||||||
void list_func_vars(int *first);
|
void list_func_vars(int *first);
|
||||||
dict_T *get_current_funccal_dict(hashtab_T *ht);
|
dict_T *get_current_funccal_dict(hashtab_T *ht);
|
||||||
|
hashitem_T *find_hi_in_scoped_ht(char_u *name, char_u **varname, hashtab_T **pht);
|
||||||
dictitem_T *find_var_in_scoped_ht(char_u *name, char_u **varname, int no_autoload);
|
dictitem_T *find_var_in_scoped_ht(char_u *name, char_u **varname, int no_autoload);
|
||||||
int set_ref_in_previous_funccal(int copyID);
|
int set_ref_in_previous_funccal(int copyID);
|
||||||
int set_ref_in_call_stack(int copyID);
|
int set_ref_in_call_stack(int copyID);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
" Test for lambda and closure
|
||||||
|
|
||||||
function! Test_lambda_with_filter()
|
function! Test_lambda_with_filter()
|
||||||
let s:x = 2
|
let s:x = 2
|
||||||
call assert_equal([2, 3], filter([1, 2, 3], {i, v -> v >= s:x}))
|
call assert_equal([2, 3], filter([1, 2, 3], {i, v -> v >= s:x}))
|
||||||
@ -100,7 +102,7 @@ function! Test_lambda_do_not_share_local_variable()
|
|||||||
call assert_equal('no', l:F[1]())
|
call assert_equal('no', l:F[1]())
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! Test_lambda_closure()
|
function! Test_lambda_closure_counter()
|
||||||
function! s:foo()
|
function! s:foo()
|
||||||
let x = 0
|
let x = 0
|
||||||
return {-> [execute("let x += 1"), x][-1]}
|
return {-> [execute("let x += 1"), x][-1]}
|
||||||
@ -209,3 +211,35 @@ function! Test_lambda_combination()
|
|||||||
let Fact = {f -> {x -> x == 0 ? 1 : x * f(x - 1)}}
|
let Fact = {f -> {x -> x == 0 ? 1 : x * f(x - 1)}}
|
||||||
call assert_equal(120, Z(Fact)(5))
|
call assert_equal(120, Z(Fact)(5))
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! Test_closure_counter()
|
||||||
|
function! s:foo()
|
||||||
|
let x = 0
|
||||||
|
function! s:bar() closure
|
||||||
|
let x += 1
|
||||||
|
return x
|
||||||
|
endfunction
|
||||||
|
return function('s:bar')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let l:F = s:foo()
|
||||||
|
call test_garbagecollect_now()
|
||||||
|
call assert_equal(1, l:F())
|
||||||
|
call assert_equal(2, l:F())
|
||||||
|
call assert_equal(3, l:F())
|
||||||
|
call assert_equal(4, l:F())
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_closure_unlet()
|
||||||
|
function! s:foo()
|
||||||
|
let x = 1
|
||||||
|
function! s:bar() closure
|
||||||
|
unlet x
|
||||||
|
endfunction
|
||||||
|
call s:bar()
|
||||||
|
return l:
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
call assert_false(has_key(s:foo(), 'x'))
|
||||||
|
call test_garbagecollect_now()
|
||||||
|
endfunction
|
||||||
|
@ -59,6 +59,7 @@ struct ufunc
|
|||||||
#define FC_ABORT 1 /* abort function on error */
|
#define FC_ABORT 1 /* abort function on error */
|
||||||
#define FC_RANGE 2 /* function accepts range */
|
#define FC_RANGE 2 /* function accepts range */
|
||||||
#define FC_DICT 4 /* Dict function, uses "self" */
|
#define FC_DICT 4 /* Dict function, uses "self" */
|
||||||
|
#define FC_CLOSURE 8 /* closure, uses outer scope variables */
|
||||||
|
|
||||||
/* From user function to hashitem and back. */
|
/* From user function to hashitem and back. */
|
||||||
#define UF2HIKEY(fp) ((fp)->uf_name)
|
#define UF2HIKEY(fp) ((fp)->uf_name)
|
||||||
@ -312,7 +313,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
|
|
||||||
if (evaluate)
|
if (evaluate)
|
||||||
{
|
{
|
||||||
int len;
|
int len, flags = 0;
|
||||||
char_u *p;
|
char_u *p;
|
||||||
|
|
||||||
sprintf((char*)name, "<lambda>%d", ++lambda_no);
|
sprintf((char*)name, "<lambda>%d", ++lambda_no);
|
||||||
@ -341,6 +342,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
fp->uf_lines = newlines;
|
fp->uf_lines = newlines;
|
||||||
if (current_funccal != NULL && eval_lavars)
|
if (current_funccal != NULL && eval_lavars)
|
||||||
{
|
{
|
||||||
|
flags |= FC_CLOSURE;
|
||||||
fp->uf_scoped = current_funccal;
|
fp->uf_scoped = current_funccal;
|
||||||
current_funccal->fc_refcount++;
|
current_funccal->fc_refcount++;
|
||||||
if (ga_grow(¤t_funccal->fc_funcs, 1) == FAIL)
|
if (ga_grow(¤t_funccal->fc_funcs, 1) == FAIL)
|
||||||
@ -361,7 +363,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
func_do_profile(fp);
|
func_do_profile(fp);
|
||||||
#endif
|
#endif
|
||||||
fp->uf_varargs = TRUE;
|
fp->uf_varargs = TRUE;
|
||||||
fp->uf_flags = 0;
|
fp->uf_flags = flags;
|
||||||
fp->uf_calls = 0;
|
fp->uf_calls = 0;
|
||||||
fp->uf_script_ID = current_SID;
|
fp->uf_script_ID = current_SID;
|
||||||
|
|
||||||
@ -1487,6 +1489,8 @@ list_func_head(ufunc_T *fp, int indent)
|
|||||||
MSG_PUTS(" range");
|
MSG_PUTS(" range");
|
||||||
if (fp->uf_flags & FC_DICT)
|
if (fp->uf_flags & FC_DICT)
|
||||||
MSG_PUTS(" dict");
|
MSG_PUTS(" dict");
|
||||||
|
if (fp->uf_flags & FC_CLOSURE)
|
||||||
|
MSG_PUTS(" closure");
|
||||||
msg_clr_eos();
|
msg_clr_eos();
|
||||||
if (p_verbose > 0)
|
if (p_verbose > 0)
|
||||||
last_set_msg(fp->uf_script_ID);
|
last_set_msg(fp->uf_script_ID);
|
||||||
@ -1948,7 +1952,7 @@ ex_function(exarg_T *eap)
|
|||||||
if (get_function_args(&p, ')', &newargs, &varargs, eap->skip) == FAIL)
|
if (get_function_args(&p, ')', &newargs, &varargs, eap->skip) == FAIL)
|
||||||
goto errret_2;
|
goto errret_2;
|
||||||
|
|
||||||
/* find extra arguments "range", "dict" and "abort" */
|
/* find extra arguments "range", "dict", "abort" and "closure" */
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
p = skipwhite(p);
|
p = skipwhite(p);
|
||||||
@ -1967,6 +1971,11 @@ ex_function(exarg_T *eap)
|
|||||||
flags |= FC_ABORT;
|
flags |= FC_ABORT;
|
||||||
p += 5;
|
p += 5;
|
||||||
}
|
}
|
||||||
|
else if (STRNCMP(p, "closure", 7) == 0)
|
||||||
|
{
|
||||||
|
flags |= FC_CLOSURE;
|
||||||
|
p += 7;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2299,7 +2308,25 @@ ex_function(exarg_T *eap)
|
|||||||
}
|
}
|
||||||
fp->uf_args = newargs;
|
fp->uf_args = newargs;
|
||||||
fp->uf_lines = newlines;
|
fp->uf_lines = newlines;
|
||||||
fp->uf_scoped = NULL;
|
if ((flags & FC_CLOSURE) != 0)
|
||||||
|
{
|
||||||
|
if (current_funccal == NULL)
|
||||||
|
{
|
||||||
|
emsg_funcname(N_("E932 Closure function should not be at top level: %s"),
|
||||||
|
name);
|
||||||
|
goto erret;
|
||||||
|
}
|
||||||
|
fp->uf_scoped = current_funccal;
|
||||||
|
current_funccal->fc_refcount++;
|
||||||
|
if (ga_grow(¤t_funccal->fc_funcs, 1) == FAIL)
|
||||||
|
goto erret;
|
||||||
|
((ufunc_T **)current_funccal->fc_funcs.ga_data)
|
||||||
|
[current_funccal->fc_funcs.ga_len++] = fp;
|
||||||
|
func_ref(current_funccal->func->uf_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fp->uf_scoped = NULL;
|
||||||
|
|
||||||
#ifdef FEAT_PROFILE
|
#ifdef FEAT_PROFILE
|
||||||
fp->uf_tml_count = NULL;
|
fp->uf_tml_count = NULL;
|
||||||
fp->uf_tml_total = NULL;
|
fp->uf_tml_total = NULL;
|
||||||
@ -3535,6 +3562,42 @@ get_current_funccal_dict(hashtab_T *ht)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Search hashitem in parent scope.
|
||||||
|
*/
|
||||||
|
hashitem_T *
|
||||||
|
find_hi_in_scoped_ht(char_u *name, char_u **varname, hashtab_T **pht)
|
||||||
|
{
|
||||||
|
funccall_T *old_current_funccal = current_funccal;
|
||||||
|
hashtab_T *ht;
|
||||||
|
hashitem_T *hi = NULL;
|
||||||
|
|
||||||
|
if (current_funccal == NULL || current_funccal->func->uf_scoped == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Search in parent scope which is possible to reference from lambda */
|
||||||
|
current_funccal = current_funccal->func->uf_scoped;
|
||||||
|
while (current_funccal)
|
||||||
|
{
|
||||||
|
ht = find_var_ht(name, varname);
|
||||||
|
if (ht != NULL && **varname != NUL)
|
||||||
|
{
|
||||||
|
hi = hash_find(ht, *varname);
|
||||||
|
if (!HASHITEM_EMPTY(hi))
|
||||||
|
{
|
||||||
|
*pht = ht;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current_funccal == current_funccal->func->uf_scoped)
|
||||||
|
break;
|
||||||
|
current_funccal = current_funccal->func->uf_scoped;
|
||||||
|
}
|
||||||
|
current_funccal = old_current_funccal;
|
||||||
|
|
||||||
|
return hi;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Search variable in parent scope.
|
* Search variable in parent scope.
|
||||||
*/
|
*/
|
||||||
|
@ -758,6 +758,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
2120,
|
||||||
/**/
|
/**/
|
||||||
2119,
|
2119,
|
||||||
/**/
|
/**/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user