Merge remote-tracking branch 'vim/master'

This commit is contained in:
Kazuki Sakamoto
2016-07-29 20:54:43 -07:00
26 changed files with 900 additions and 288 deletions
+4 -1
View File
@@ -1,7 +1,7 @@
" The default vimrc file.
"
" Maintainer: Bram Moolenaar <Bram@vim.org>
" Last change: 2016 Jul 28
" Last change: 2016 Jul 29
"
" This is loaded if no vimrc file was found.
" Except when Vim is run with "-u NONE" or "-C".
@@ -25,6 +25,9 @@ set ruler " show the cursor position all the time
set showcmd " display incomplete commands
set wildmenu " display completion matches in a status line
" Show @@@ in the last line if it is truncated.
set display=truncate
" Do incremental searching when it's possible to timeout.
if has('reltime')
set incsearch
+47 -5
View File
@@ -1,4 +1,4 @@
*eval.txt* For Vim version 7.4. Last change: 2016 Jul 24
*eval.txt* For Vim version 7.4. Last change: 2016 Jul 29
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -40,7 +40,7 @@ done, the features in this document are not available. See |+eval| and
There are nine types of variables:
Number A 32 or 64 bit signed number. |expr-number| *Number*
64-bit Number is available only when compiled with the
64-bit Numbers are available only when compiled with the
|+num64| feature.
Examples: -123 0x10 0177
@@ -1219,7 +1219,7 @@ the following ways:
1. The body of the lambda expression is an |expr1| and not a sequence of |Ex|
commands.
2. The prefix "a:" is optional for arguments. E.g.: >
2. The prefix "a:" should not be used for arguments. E.g.: >
:let F = {arg1, arg2 -> arg1 - arg2}
:echo F(5, 2)
< 3
@@ -1228,6 +1228,19 @@ The arguments are optional. Example: >
:let F = {-> 'error function'}
:echo F()
< error function
*closure*
Lambda expressions can access outer scope variables and arguments. This is
often called a closure. Example where "i" a and "a:arg" are used in a lambda
while they exists in the function scope. They remain valid even after the
function returns: >
:function Foo(arg)
: let i = 3
: return {x -> x + i - a:arg}
:endfunction
:let Bar = Foo(4)
:echo Bar(6)
< 5
See also |:func-closure|.
Examples for using a lambda expression with |sort()|, |map()| and |filter()|: >
:echo map([1, 2, 3], {idx, val -> val + 1})
@@ -1245,6 +1258,12 @@ The lambda expression is also useful for Channel, Job and timer: >
Note how execute() is used to execute an Ex command. That's ugly though.
Lambda expressions have internal names like '<lambda>42'. If you get an error
for a lambda expression, you can find what it is with the following command: >
:function {'<lambda>42'}
See also: |numbered-function|
==============================================================================
3. Internal variable *internal-variables* *E461*
@@ -7494,7 +7513,8 @@ test_null_string() *test_null_string()*
test_settime({expr}) *test_settime()*
Set the time Vim uses internally. Currently only used for
timestamps in the history, as they are used in viminfo.
timestamps in the history, as they are used in viminfo, and
for undo.
{expr} must evaluate to a number. When the value is zero the
normal behavior is restored.
@@ -8202,7 +8222,7 @@ last defined. Example: >
See |:verbose-cmd| for more information.
*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
must be made of alphanumeric characters and '_', and
must start with a capital or "s:" (see above). Note
@@ -8245,6 +8265,28 @@ See |:verbose-cmd| for more information.
be invoked through an entry in a |Dictionary|. The
local variable "self" will then be set to the
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*
The last used search pattern and the redo command "."
+4 -3
View File
@@ -2061,7 +2061,7 @@ test1 \
test30 test31 test32 test33 test34 test36 test37 test38 test39 \
test40 test41 test42 test43 test44 test45 test46 test47 test48 test49 \
test50 test51 test52 test53 test54 test55 test56 test57 test58 test59 \
test60 test61 test62 test63 test64 test65 test66 test67 test68 test69 \
test60 test62 test63 test64 test65 test66 test67 test68 test69 \
test70 test71 test72 test73 test74 test75 test76 test77 test78 test79 \
test80 test81 test82 test83 test84 test85 test86 test87 test88 test89 \
test90 test91 test92 test93 test94 test95 test97 test98 test99 \
@@ -2124,6 +2124,7 @@ test_arglist \
test_regexp_utf8 \
test_reltime \
test_ruby \
test_startup \
test_searchpos \
test_set \
test_sort \
@@ -2136,9 +2137,9 @@ test_arglist \
test_textobjects \
test_timers \
test_true_false \
test_undolevels \
test_usercommands \
test_undo \
test_unlet \
test_usercommands \
test_viminfo \
test_viml \
test_visual \
+1 -2
View File
@@ -1153,10 +1153,9 @@ install_vimrc(int idx)
fprintf(fd, "set compatible\n");
break;
case compat_some_enhancements:
fprintf(fd, "set nocompatible\n");
fprintf(fd, "source $VIMRUNTIME/defaults.vim\n");
break;
case compat_all_enhancements:
fprintf(fd, "set nocompatible\n");
fprintf(fd, "source $VIMRUNTIME/vimrc_example.vim\n");
break;
}
+57 -4
View File
@@ -237,8 +237,8 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate);
static int get_env_len(char_u **arg);
static char_u * make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end);
static void check_vars(char_u *name, int len);
static typval_T *alloc_string_tv(char_u *string);
static hashtab_T *find_var_ht(char_u *name, char_u **varname);
static void delete_var(hashtab_T *ht, hashitem_T *hi);
static void list_one_var(dictitem_T *v, char_u *prefix, int *first);
static void list_one_var_a(char_u *prefix, char_u *name, int type, char_u *string, int *first);
@@ -2837,7 +2837,9 @@ do_unlet(char_u *name, int forceit)
}
}
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);
if (var_check_fixed(di->di_flags, name, FALSE)
@@ -4332,6 +4334,9 @@ eval7(
{
partial_T *partial;
if (!evaluate)
check_vars(s, len);
/* If "s" is the name of a variable of type VAR_FUNC
* use its contents. */
s = deref_func_name(s, &len, &partial, !evaluate);
@@ -4363,7 +4368,10 @@ eval7(
else if (evaluate)
ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE);
else
{
check_vars(s, len);
ret = OK;
}
}
vim_free(alias);
}
@@ -5540,6 +5548,10 @@ set_ref_in_item(
}
}
}
else if (tv->v_type == VAR_FUNC)
{
abort = set_ref_in_func(tv->vval.v_string, copyID);
}
else if (tv->v_type == VAR_PARTIAL)
{
partial_T *pt = tv->vval.v_partial;
@@ -5549,6 +5561,8 @@ set_ref_in_item(
*/
if (pt != NULL)
{
abort = set_ref_in_func(pt->pt_name, copyID);
if (pt->pt_dict != NULL)
{
typval_T dtv;
@@ -6790,6 +6804,34 @@ get_var_tv(
return ret;
}
/*
* Check if variable "name[len]" is a local variable or an argument.
* If so, "*eval_lavars_used" is set to TRUE.
*/
static void
check_vars(char_u *name, int len)
{
int cc;
char_u *varname;
hashtab_T *ht;
if (eval_lavars_used == NULL)
return;
/* truncate the name, so that we can use strcmp() */
cc = name[len];
name[len] = NUL;
ht = find_var_ht(name, &varname);
if (ht == get_funccal_local_ht() || ht == get_funccal_args_ht())
{
if (find_var(name, NULL, TRUE) != NULL)
*eval_lavars_used = TRUE;
}
name[len] = cc;
}
/*
* Handle expr[expr], expr[expr:expr] subscript and .name lookup.
* Also handle function call with Funcref variable: func(expr)
@@ -7274,13 +7316,20 @@ find_var(char_u *name, hashtab_T **htp, int no_autoload)
{
char_u *varname;
hashtab_T *ht;
dictitem_T *ret = NULL;
ht = find_var_ht(name, &varname);
if (htp != NULL)
*htp = ht;
if (ht == NULL)
return NULL;
return find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL);
ret = find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL);
if (ret != NULL)
return ret;
/* Search in parent scope for lambda */
return find_var_in_scoped_ht(name, varname ? &varname : NULL,
no_autoload || htp != NULL);
}
/*
@@ -7341,7 +7390,7 @@ find_var_in_ht(
* Return NULL if the name is not valid.
* Set "varname" to the start of name without ':'.
*/
static hashtab_T *
hashtab_T *
find_var_ht(char_u *name, char_u **varname)
{
hashitem_T *hi;
@@ -7617,6 +7666,10 @@ set_var(
}
v = find_var_in_ht(ht, 0, varname, TRUE);
/* Search in parent scope which is possible to reference from lambda */
if (v == NULL)
v = find_var_in_scoped_ht(name, varname ? &varname : NULL, TRUE);
if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
&& var_check_func_name(name, v == NULL))
return;
+1
View File
@@ -5218,6 +5218,7 @@ f_has(typval_T *argvars, typval_T *rettv)
#ifdef FEAT_KEYMAP
"keymap",
#endif
"lambda", /* always with FEAT_EVAL, since 7.4.2120 with closure */
#ifdef FEAT_LANGMAP
"langmap",
#endif
-2
View File
@@ -2867,7 +2867,6 @@ write_viminfo_barlines(vir_T *virp, FILE *fp_out)
}
#endif /* FEAT_VIMINFO */
#if defined(FEAT_CMDHIST) || defined(FEAT_VIMINFO) || defined(PROTO)
/*
* Return the current time in seconds. Calls time(), unless test_settime()
* was used.
@@ -2881,7 +2880,6 @@ vim_time(void)
return time(NULL);
# endif
}
#endif
/*
* Implementation of ":fixdel", also used by get_stty().
+10 -2
View File
@@ -1265,8 +1265,16 @@ set_ref_in_timer(int copyID)
for (timer = first_timer; timer != NULL; timer = timer->tr_next)
{
tv.v_type = VAR_PARTIAL;
tv.vval.v_partial = timer->tr_partial;
if (timer->tr_partial != NULL)
{
tv.v_type = VAR_PARTIAL;
tv.vval.v_partial = timer->tr_partial;
}
else
{
tv.v_type = VAR_FUNC;
tv.vval.v_string = timer->tr_callback;
}
abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
}
return abort;
+43 -6
View File
@@ -7796,6 +7796,7 @@ static AutoPatCmd *active_apc_list = NULL; /* stack of active autocommands */
*/
static garray_T augroups = {0, 0, sizeof(char_u *), 10, NULL};
#define AUGROUP_NAME(i) (((char_u **)augroups.ga_data)[i])
static char_u *deleted_augroup = NULL;
/*
* The ID of the current group. Group 0 is the default one.
@@ -7850,7 +7851,7 @@ show_autocmd(AutoPat *ap, event_T event)
if (ap->group != AUGROUP_DEFAULT)
{
if (AUGROUP_NAME(ap->group) == NULL)
msg_puts_attr((char_u *)_("--Deleted--"), hl_attr(HLF_E));
msg_puts_attr(deleted_augroup, hl_attr(HLF_E));
else
msg_puts_attr(AUGROUP_NAME(ap->group), hl_attr(HLF_T));
msg_puts((char_u *)" ");
@@ -8047,8 +8048,31 @@ au_del_group(char_u *name)
EMSG2(_("E367: No such group: \"%s\""), name);
else
{
event_T event;
AutoPat *ap;
int in_use = FALSE;
for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
event = (event_T)((int)event + 1))
{
for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next)
if (ap->group == i)
{
give_warning((char_u *)_("W19: Deleting augroup that is still in use"), TRUE);
in_use = TRUE;
event = NUM_EVENTS;
break;
}
}
vim_free(AUGROUP_NAME(i));
AUGROUP_NAME(i) = NULL;
if (in_use)
{
if (deleted_augroup == NULL)
deleted_augroup = (char_u *)_("--Deleted--");
AUGROUP_NAME(i) = deleted_augroup;
}
else
AUGROUP_NAME(i) = NULL;
}
}
@@ -8062,7 +8086,8 @@ au_find_group(char_u *name)
int i;
for (i = 0; i < augroups.ga_len; ++i)
if (AUGROUP_NAME(i) != NULL && STRCMP(AUGROUP_NAME(i), name) == 0)
if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != deleted_augroup
&& STRCMP(AUGROUP_NAME(i), name) == 0)
return i;
return AUGROUP_ERROR;
}
@@ -8119,10 +8144,20 @@ do_augroup(char_u *arg, int del_group)
void
free_all_autocmds(void)
{
int i;
char_u *s;
for (current_augroup = -1; current_augroup < augroups.ga_len;
++current_augroup)
do_autocmd((char_u *)"", TRUE);
ga_clear_strings(&augroups);
for (i = 0; i < augroups.ga_len; ++i)
{
s = ((char_u **)(augroups.ga_data))[i];
if (s != deleted_augroup)
vim_free(s);
}
ga_clear(&augroups);
}
#endif
@@ -9868,7 +9903,8 @@ get_augroup_name(expand_T *xp UNUSED, int idx)
return (char_u *)"END";
if (idx >= augroups.ga_len) /* end of list */
return NULL;
if (AUGROUP_NAME(idx) == NULL) /* skip deleted entries */
if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == deleted_augroup)
/* skip deleted entries */
return (char_u *)"";
return AUGROUP_NAME(idx); /* return a name */
}
@@ -9932,7 +9968,8 @@ get_event_name(expand_T *xp UNUSED, int idx)
{
if (idx < augroups.ga_len) /* First list group names, if wanted */
{
if (!include_groups || AUGROUP_NAME(idx) == NULL)
if (!include_groups || AUGROUP_NAME(idx) == NULL
|| AUGROUP_NAME(idx) == deleted_augroup)
return (char_u *)""; /* skip deleted entries */
return AUGROUP_NAME(idx); /* return a name */
}
+3
View File
@@ -1662,6 +1662,9 @@ EXTERN time_T time_for_testing INIT(= 0);
/* Abort conversion to string after a recursion error. */
EXTERN int did_echo_string_emsg INIT(= FALSE);
/* Used for checking if local variables or arguments used in a lambda. */
EXTERN int *eval_lavars_used INIT(= NULL);
#endif
/*
+4 -1
View File
@@ -90,6 +90,8 @@ static char *(main_errors[]) =
static char_u *start_dir = NULL; /* current working dir on startup */
static int has_dash_c_arg = FALSE;
int
# ifdef VIMDLL
_export
@@ -2006,6 +2008,7 @@ command_line_scan(mparm_T *parmp)
case 'C': /* "-C" Compatible */
change_compatible(TRUE);
has_dash_c_arg = TRUE;
break;
case 'e': /* "-e" Ex mode */
@@ -3079,7 +3082,7 @@ source_startup_scripts(mparm_T *parmp)
#ifdef USR_EXRC_FILE2
&& do_source((char_u *)USR_EXRC_FILE2, FALSE, DOSO_NONE) == FAIL
#endif
)
&& !has_dash_c_arg)
{
/* When no .vimrc file was found: source defaults.vim. */
do_source((char_u *)VIM_DEFAULTS_FILE, FALSE, DOSO_NONE);
+1
View File
@@ -87,6 +87,7 @@ char_u *get_tv_string_chk(typval_T *varp);
char_u *get_tv_string_buf_chk(typval_T *varp, char_u *buf);
dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload);
dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload);
hashtab_T *find_var_ht(char_u *name, char_u **varname);
char_u *get_var_value(char_u *name);
void new_script_vars(scid_T id);
void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope);
+3
View File
@@ -46,7 +46,10 @@ void *clear_current_funccal(void);
void restore_current_funccal(void *f);
void list_func_vars(int *first);
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);
int set_ref_in_previous_funccal(int copyID);
int set_ref_in_call_stack(int copyID);
int set_ref_in_func_args(int copyID);
int set_ref_in_func(char_u *name, int copyID);
/* vim: set ft=c : */
+2 -1
View File
@@ -51,7 +51,6 @@ SCRIPTS_ALL = \
test56.out \
test57.out \
test60.out \
test61.out \
test62.out \
test63.out \
test64.out \
@@ -185,9 +184,11 @@ NEW_TESTS = test_arglist.res \
test_perl.res \
test_quickfix.res \
test_ruby.res \
test_startup.res \
test_stat.res \
test_syntax.res \
test_textobjects.res \
test_undo.res \
test_usercommands.res \
test_viminfo.res \
test_viml.res \
-113
View File
@@ -1,113 +0,0 @@
Tests for undo tree.
Since this script is sourced we need to explicitly break changes up in
undo-able pieces. Do that by setting 'undolevels'.
Also tests :earlier and :later.
STARTTEST
:echo undotree().entries
ENDTEST
STARTTEST
:" Delete three characters and undo
Gx:set ul=100
x:set ul=100
x:.w! test.out
g-:.w >>test.out
g-:.w >>test.out
g-:.w >>test.out
g-:.w >>test.out
:"
:/^111/w >>test.out
:" Delete three other characters and go back in time step by step
$x:set ul=100
x:set ul=100
x:.w >>test.out
:sleep 1
g-:.w >>test.out
g-:.w >>test.out
g-:.w >>test.out
g-:.w >>test.out
g-:.w >>test.out
g-:.w >>test.out
g-:.w >>test.out
g-:.w >>test.out
10g+:.w >>test.out
:"
:/^222/w >>test.out
:" Delay for three seconds and go some seconds forward and backward
:sleep 2
Aa:set ul=100
Ab:set ul=100
Ac:set ul=100
:.w >>test.out
:ear 1s
:.w >>test.out
:ear 3s
:.w >>test.out
:later 1s
:.w >>test.out
:later 1h
:.w >>test.out
:"
:" test undojoin
Goaaaa:set ul=100
obbbbu:.w >>test.out
obbbb:set ul=100
:undojoin
occccu:.w >>test.out
:e! Xtest
ione one one:set ul=100
:w!
otwo:set ul=100
otwo:set ul=100
:w
othree:earlier 1f
:" expect "one one one\ntwo\ntwo"
:%yank a
:earlier 1f
:" expect "one one one"
:%yank b
:earlier 1f
:" expect empty line
:%yank c
:later 1f
:" expect "one one one"
:%yank d
:later 1f
:" expect "one one one\ntwo\ntwo"
:%yank e
:later 1f
:" expect "one one one\ntwo\ntwo\nthree"
ggO---:0put e
ggO---:0put d
ggO---:0put c
ggO---:0put b
ggO---:0put a
ggO---:w >>test.out
:so small.vim
:set nocp viminfo+=nviminfo
:enew!
oa:
:set ul=100
ob:
:set ul=100
o1a2=setline('.','1234')
uu:"
oc:
:set ul=100
o1a2=setline('.','1234')
u:"
od:
:set ul=100
o1a2=string(123)
u:"
:%w >>test.out
:qa!
ENDTEST
1111 -----
2222 -----
123456789
-49
View File
@@ -1,49 +0,0 @@
456789
3456789
23456789
123456789
123456789
1111 -----
123456
1234567
12345678
456789
3456789
23456789
123456789
123456789
123456789
123456
2222 -----
123456abc
123456
123456789
123456
123456abc
aaaa
aaaa
---
one one one
two
two
---
one one one
---
---
one one one
---
one one one
two
two
---
one one one
two
two
three
a
b
c
12
d
-1
View File
@@ -36,6 +36,5 @@ source test_tabline.vim
source test_tagjump.vim
source test_timers.vim
source test_true_false.vim
source test_undolevels.vim
source test_unlet.vim
source test_window_cmd.vim
+17
View File
@@ -151,3 +151,20 @@ func Test_early_bar()
au! vimBarTest|echo 'hello'
call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
endfunc
func Test_augroup_warning()
augroup TheWarning
au VimEnter * echo 'entering'
augroup END
call assert_true(match(execute('au VimEnter'), "TheWarning.*VimEnter") >= 0)
redir => res
augroup! TheWarning
redir END
call assert_true(match(res, "W19:") >= 0)
call assert_true(match(execute('au VimEnter'), "-Deleted-.*VimEnter") >= 0)
" check "Another" does not take the pace of the deleted entry
augroup Another
augroup END
call assert_true(match(execute('au VimEnter'), "-Deleted-.*VimEnter") >= 0)
endfunc
+197 -1
View File
@@ -1,3 +1,9 @@
" Test for lambda and closure
function! Test_lambda_feature()
call assert_equal(1, has('lambda'))
endfunction
function! Test_lambda_with_filter()
let s:x = 2
call assert_equal([2, 3], filter([1, 2, 3], {i, v -> v >= s:x}))
@@ -21,7 +27,7 @@ function! Test_lambda_with_timer()
let s:timer_id = 0
function! s:Foo()
"let n = 0
let s:timer_id = timer_start(50, {-> execute("let s:n += 1 | echo s:n")}, {"repeat": -1})
let s:timer_id = timer_start(50, {-> execute("let s:n += 1 | echo s:n", "")}, {"repeat": -1})
endfunction
call s:Foo()
@@ -51,3 +57,193 @@ func Test_not_lamda()
let x = {'>' : 'foo'}
call assert_equal('foo', x['>'])
endfunc
function! Test_lambda_capture_by_reference()
let v = 1
let l:F = {x -> x + v}
let v = 2
call assert_equal(12, l:F(10))
endfunction
function! Test_lambda_side_effect()
function! s:update_and_return(arr)
let a:arr[1] = 5
return a:arr
endfunction
function! s:foo(arr)
return {-> s:update_and_return(a:arr)}
endfunction
let arr = [3,2,1]
call assert_equal([3, 5, 1], s:foo(arr)())
endfunction
function! Test_lambda_refer_local_variable_from_other_scope()
function! s:foo(X)
return a:X() " refer l:x in s:bar()
endfunction
function! s:bar()
let x = 123
return s:foo({-> x})
endfunction
call assert_equal(123, s:bar())
endfunction
function! Test_lambda_do_not_share_local_variable()
function! s:define_funcs()
let l:One = {-> split(execute("let a = 'abc' | echo a"))[0]}
let l:Two = {-> exists("a") ? a : "no"}
return [l:One, l:Two]
endfunction
let l:F = s:define_funcs()
call assert_equal('no', l:F[1]())
call assert_equal('abc', l:F[0]())
call assert_equal('no', l:F[1]())
endfunction
function! Test_lambda_closure_counter()
function! s:foo()
let x = 0
return {-> [execute("let x += 1"), x][-1]}
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_lambda_with_a_var()
function! s:foo()
let x = 2
return {... -> a:000 + [x]}
endfunction
function! s:bar()
return s:foo()(1)
endfunction
call assert_equal([1, 2], s:bar())
endfunction
function! Test_lambda_call_lambda_from_lambda()
function! s:foo(x)
let l:F1 = {-> {-> a:x}}
return {-> l:F1()}
endfunction
let l:F = s:foo(1)
call assert_equal(1, l:F()())
endfunction
function! Test_lambda_delfunc()
function! s:gen()
let pl = l:
let l:Foo = {-> get(pl, "Foo", get(pl, "Bar", {-> 0}))}
let l:Bar = l:Foo
delfunction l:Foo
return l:Bar
endfunction
let l:F = s:gen()
call assert_fails(':call l:F()', 'E117:')
endfunction
function! Test_lambda_scope()
function! s:NewCounter()
let c = 0
return {-> [execute('let c += 1'), c][-1]}
endfunction
function! s:NewCounter2()
return {-> [execute('let c += 100'), c][-1]}
endfunction
let l:C = s:NewCounter()
let l:D = s:NewCounter2()
call assert_equal(1, l:C())
call assert_fails(':call l:D()', 'E15:') " E121: then E15:
call assert_equal(2, l:C())
endfunction
function! Test_lambda_share_scope()
function! s:New()
let c = 0
let l:Inc0 = {-> [execute('let c += 1'), c][-1]}
let l:Dec0 = {-> [execute('let c -= 1'), c][-1]}
return [l:Inc0, l:Dec0]
endfunction
let [l:Inc, l:Dec] = s:New()
call assert_equal(1, l:Inc())
call assert_equal(2, l:Inc())
call assert_equal(1, l:Dec())
endfunction
function! Test_lambda_circular_reference()
function! s:Foo()
let d = {}
let d.f = {-> d}
return d.f
endfunction
call s:Foo()
call test_garbagecollect_now()
let i = 0 | while i < 10000 | call s:Foo() | let i+= 1 | endwhile
call test_garbagecollect_now()
endfunction
function! Test_lambda_combination()
call assert_equal(2, {x -> {x -> x}}(1)(2))
call assert_equal(10, {y -> {x -> x(y)(10)}({y -> y})}({z -> z}))
call assert_equal(5.0, {x -> {y -> x / y}}(10)(2.0))
call assert_equal(6, {x -> {y -> {z -> x + y + z}}}(1)(2)(3))
call assert_equal(6, {x -> {f -> f(x)}}(3)({x -> x * 2}))
call assert_equal(6, {f -> {x -> f(x)}}({x -> x * 2})(3))
" Z combinator
let Z = {f -> {x -> f({y -> x(x)(y)})}({x -> f({y -> x(x)(y)})})}
let Fact = {f -> {x -> x == 0 ? 1 : x * f(x - 1)}}
call assert_equal(120, Z(Fact)(5))
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
+8
View File
@@ -0,0 +1,8 @@
" Check that loading startup.vim works.
func Test_startup_script()
set compatible
source $VIMRUNTIME/defaults.vim
call assert_equal(0, &compatible)
endfunc
+204
View File
@@ -0,0 +1,204 @@
" Tests for the undo tree.
" Since this script is sourced we need to explicitly break changes up in
" undo-able pieces. Do that by setting 'undolevels'.
" Also tests :earlier and :later.
func Test_undotree()
exe "normal Aabc\<Esc>"
set ul=100
exe "normal Adef\<Esc>"
set ul=100
undo
let d = undotree()
call assert_true(d.seq_last > 0)
call assert_true(d.seq_cur > 0)
call assert_true(d.seq_cur < d.seq_last)
call assert_true(len(d.entries) > 0)
" TODO: check more members of d
w! Xtest
call assert_equal(d.save_last + 1, undotree().save_last)
call delete('Xtest')
bwipe Xtest
endfunc
func FillBuffer()
for i in range(1,13)
put=i
" Set 'undolevels' to split undo.
exe "setg ul=" . &g:ul
endfor
endfunc
func Test_global_local_undolevels()
new one
set undolevels=5
call FillBuffer()
" will only undo the last 5 changes, end up with 13 - (5 + 1) = 7 lines
earlier 10
call assert_equal(5, &g:undolevels)
call assert_equal(-123456, &l:undolevels)
call assert_equal('7', getline('$'))
new two
setlocal undolevels=2
call FillBuffer()
" will only undo the last 2 changes, end up with 13 - (2 + 1) = 10 lines
earlier 10
call assert_equal(5, &g:undolevels)
call assert_equal(2, &l:undolevels)
call assert_equal('10', getline('$'))
setlocal ul=10
call assert_equal(5, &g:undolevels)
call assert_equal(10, &l:undolevels)
" Setting local value in "two" must not change local value in "one"
wincmd p
call assert_equal(5, &g:undolevels)
call assert_equal(-123456, &l:undolevels)
new three
setglobal ul=50
call assert_equal(50, &g:undolevels)
call assert_equal(-123456, &l:undolevels)
" Drop created windows
set ul&
new
only!
endfunc
func BackOne(expected)
call feedkeys('g-', 'xt')
call assert_equal(a:expected, getline(1))
endfunc
func Test_undo_del_chars()
" Setup a buffer without creating undo entries
new
set ul=-1
call setline(1, ['123-456'])
set ul=100
1
call test_settime(100)
" Delete three characters and undo with g-
call feedkeys('x', 'xt')
call feedkeys('x', 'xt')
call feedkeys('x', 'xt')
call assert_equal('-456', getline(1))
call BackOne('3-456')
call BackOne('23-456')
call BackOne('123-456')
call assert_fails("BackOne('123-456')")
:" Delete three other characters and go back in time with g-
call feedkeys('$x', 'xt')
call feedkeys('x', 'xt')
call feedkeys('x', 'xt')
call assert_equal('123-', getline(1))
call test_settime(101)
call BackOne('123-4')
call BackOne('123-45')
" skips '123-456' because it's older
call BackOne('-456')
call BackOne('3-456')
call BackOne('23-456')
call BackOne('123-456')
call assert_fails("BackOne('123-456')")
normal 10g+
call assert_equal('123-', getline(1))
:" Jump two seconds and go some seconds forward and backward
call test_settime(103)
call feedkeys("Aa\<Esc>", 'xt')
call feedkeys("Ab\<Esc>", 'xt')
call feedkeys("Ac\<Esc>", 'xt')
call assert_equal('123-abc', getline(1))
earlier 1s
call assert_equal('123-', getline(1))
earlier 3s
call assert_equal('123-456', getline(1))
later 1s
call assert_equal('123-', getline(1))
later 1h
call assert_equal('123-abc', getline(1))
close!
endfunc
func Test_undojoin()
new
call feedkeys("Goaaaa\<Esc>", 'xt')
call feedkeys("obbbb\<Esc>", 'xt')
call assert_equal(['aaaa', 'bbbb'], getline(2, '$'))
call feedkeys("u", 'xt')
call assert_equal(['aaaa'], getline(2, '$'))
call feedkeys("obbbb\<Esc>", 'xt')
undojoin
" Note: next change must not be as if typed
call feedkeys("occcc\<Esc>", 'x')
call assert_equal(['aaaa', 'bbbb', 'cccc'], getline(2, '$'))
call feedkeys("u", 'xt')
call assert_equal(['aaaa'], getline(2, '$'))
close!
endfunc
func Test_undo_write()
split Xtest
call feedkeys("ione one one\<Esc>", 'xt')
w!
call feedkeys("otwo\<Esc>", 'xt')
call feedkeys("otwo\<Esc>", 'xt')
w
call feedkeys("othree\<Esc>", 'xt')
call assert_equal(['one one one', 'two', 'two', 'three'], getline(1, '$'))
earlier 1f
call assert_equal(['one one one', 'two', 'two'], getline(1, '$'))
earlier 1f
call assert_equal(['one one one'], getline(1, '$'))
earlier 1f
call assert_equal([''], getline(1, '$'))
later 1f
call assert_equal(['one one one'], getline(1, '$'))
later 1f
call assert_equal(['one one one', 'two', 'two'], getline(1, '$'))
later 1f
call assert_equal(['one one one', 'two', 'two', 'three'], getline(1, '$'))
close!
call delete('Xtest')
bwipe! Xtest
endfunc
func Test_insert_expr()
new
" calling setline() triggers undo sync
call feedkeys("oa\<Esc>", 'xt')
call feedkeys("ob\<Esc>", 'xt')
set ul=100
call feedkeys("o1\<Esc>a2\<C-R>=setline('.','1234')\<CR>\<CR>\<Esc>", 'x')
call assert_equal(['a', 'b', '120', '34'], getline(2, '$'))
call feedkeys("u", 'x')
call assert_equal(['a', 'b', '12'], getline(2, '$'))
call feedkeys("u", 'x')
call assert_equal(['a', 'b'], getline(2, '$'))
call feedkeys("oc\<Esc>", 'xt')
set ul=100
call feedkeys("o1\<Esc>a2\<C-R>=setline('.','1234')\<CR>\<CR>\<Esc>", 'x')
call assert_equal(['a', 'b', 'c', '120', '34'], getline(2, '$'))
call feedkeys("u", 'x')
call assert_equal(['a', 'b', 'c', '12'], getline(2, '$'))
call feedkeys("od\<Esc>", 'xt')
set ul=100
call feedkeys("o1\<Esc>a2\<C-R>=string(123)\<CR>\<Esc>", 'x')
call assert_equal(['a', 'b', 'c', '12', 'd', '12123'], getline(2, '$'))
call feedkeys("u", 'x')
call assert_equal(['a', 'b', 'c', '12', 'd'], getline(2, '$'))
close!
endfunc
-48
View File
@@ -1,48 +0,0 @@
" Tests for 'undolevels'
func FillBuffer()
for i in range(1,13)
put=i
" Set 'undolevels' to split undo.
exe "setg ul=" . &g:ul
endfor
endfunc
func Test_global_local_undolevels()
new one
set undolevels=5
call FillBuffer()
" will only undo the last 5 changes, end up with 13 - (5 + 1) = 7 lines
earlier 10
call assert_equal(5, &g:undolevels)
call assert_equal(-123456, &l:undolevels)
call assert_equal('7', getline('$'))
new two
setlocal undolevels=2
call FillBuffer()
" will only undo the last 2 changes, end up with 13 - (2 + 1) = 10 lines
earlier 10
call assert_equal(5, &g:undolevels)
call assert_equal(2, &l:undolevels)
call assert_equal('10', getline('$'))
setlocal ul=10
call assert_equal(5, &g:undolevels)
call assert_equal(10, &l:undolevels)
" Setting local value in "two" must not change local value in "one"
wincmd p
call assert_equal(5, &g:undolevels)
call assert_equal(-123456, &l:undolevels)
new three
setglobal ul=50
call assert_equal(50, &g:undolevels)
call assert_equal(-123456, &l:undolevels)
" Drop created windows
set ul&
new
only!
endfunc
+5 -5
View File
@@ -534,7 +534,7 @@ u_savecommon(
uhp->uh_seq = ++curbuf->b_u_seq_last;
curbuf->b_u_seq_cur = uhp->uh_seq;
uhp->uh_time = time(NULL);
uhp->uh_time = vim_time();
uhp->uh_save_nr = 0;
curbuf->b_u_time_cur = uhp->uh_time + 1;
@@ -2350,7 +2350,7 @@ undo_time(
else
{
if (dosec)
closest = (long)(time(NULL) - starttime + 1);
closest = (long)(vim_time() - starttime + 1);
else if (dofile)
closest = curbuf->b_u_save_nr_last + 2;
else
@@ -3104,10 +3104,10 @@ u_add_time(char_u *buf, size_t buflen, time_t tt)
#ifdef HAVE_STRFTIME
struct tm *curtime;
if (time(NULL) - tt >= 100)
if (vim_time() - tt >= 100)
{
curtime = localtime(&tt);
if (time(NULL) - tt < (60L * 60L * 12L))
if (vim_time() - tt < (60L * 60L * 12L))
/* within 12 hours */
(void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
else
@@ -3117,7 +3117,7 @@ u_add_time(char_u *buf, size_t buflen, time_t tt)
else
#endif
vim_snprintf((char *)buf, buflen, _("%ld seconds ago"),
(long)(time(NULL) - tt));
(long)(vim_time() - tt));
}
/*
+262 -40
View File
@@ -15,6 +15,8 @@
#if defined(FEAT_EVAL) || defined(PROTO)
typedef struct funccall_S funccall_T;
/*
* Structure to hold info for a user function.
*/
@@ -47,6 +49,7 @@ struct ufunc
scid_T uf_script_ID; /* ID of script where function was defined,
used for s: variables */
int uf_refcount; /* for numbered function: reference count */
funccall_T *uf_scoped; /* l: local variables for closure */
char_u uf_name[1]; /* name of function (actually longer); can
start with <SNR>123_ (<SNR> is K_SPECIAL
KS_EXTRA KE_SNR) */
@@ -56,6 +59,7 @@ struct ufunc
#define FC_ABORT 1 /* abort function on error */
#define FC_RANGE 2 /* function accepts range */
#define FC_DICT 4 /* Dict function, uses "self" */
#define FC_CLOSURE 8 /* closure, uses outer scope variables */
/* From user function to hashitem and back. */
#define UF2HIKEY(fp) ((fp)->uf_name)
@@ -70,8 +74,6 @@ struct ufunc
#define FIXVAR_CNT 12 /* number of fixed variables */
/* structure to hold info for a function that is currently being executed. */
typedef struct funccall_S funccall_T;
struct funccall_S
{
ufunc_T *func; /* function being called */
@@ -96,6 +98,11 @@ struct funccall_S
proftime_T prof_child; /* time spent in a child */
#endif
funccall_T *caller; /* calling function or NULL */
/* for closure */
int fc_refcount;
int fc_copyID; /* for garbage collection */
garray_T fc_funcs; /* list of ufunc_T* which refer this */
};
/*
@@ -259,6 +266,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
{
garray_T newargs;
garray_T newlines;
garray_T *pnewargs;
ufunc_T *fp = NULL;
int varargs;
int ret;
@@ -266,6 +274,8 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
char_u *start = skipwhite(*arg + 1);
char_u *s, *e;
static int lambda_no = 0;
int *old_eval_lavars = eval_lavars_used;
int eval_lavars = FALSE;
ga_init(&newargs);
ga_init(&newlines);
@@ -276,11 +286,19 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
return NOTDONE;
/* Parse the arguments again. */
if (evaluate)
pnewargs = &newargs;
else
pnewargs = NULL;
*arg = skipwhite(*arg + 1);
ret = get_function_args(arg, '-', &newargs, &varargs, FALSE);
ret = get_function_args(arg, '-', pnewargs, &varargs, FALSE);
if (ret == FAIL || **arg != '>')
goto errret;
/* Set up dictionaries for checking local variables and arguments. */
if (evaluate)
eval_lavars_used = &eval_lavars;
/* Get the start and the end of the expression. */
*arg = skipwhite(*arg + 1);
s = *arg;
@@ -295,35 +313,46 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
if (evaluate)
{
int len;
int len, flags = 0;
char_u *p;
fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + 20));
sprintf((char*)name, "<lambda>%d", ++lambda_no);
fp = (ufunc_T *)alloc((unsigned)(sizeof(ufunc_T) + STRLEN(name)));
if (fp == NULL)
goto errret;
sprintf((char*)name, "<lambda>%d", ++lambda_no);
ga_init2(&newlines, (int)sizeof(char_u *), 1);
if (ga_grow(&newlines, 1) == FAIL)
goto errret;
/* Add "return " before the expression.
* TODO: Support multiple expressions. */
/* Add "return " before the expression. */
len = 7 + e - s + 1;
p = (char_u *)alloc(len);
if (p == NULL)
goto errret;
((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
STRCPY(p, "return ");
STRNCPY(p + 7, s, e - s);
p[7 + e - s] = NUL;
vim_strncpy(p + 7, s, e - s);
fp->uf_refcount = 1;
STRCPY(fp->uf_name, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
fp->uf_args = newargs;
fp->uf_lines = newlines;
if (current_funccal != NULL && eval_lavars)
{
flags |= FC_CLOSURE;
fp->uf_scoped = current_funccal;
current_funccal->fc_refcount++;
if (ga_grow(&current_funccal->fc_funcs, 1) == FAIL)
goto errret;
((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
fp->uf_tml_count = NULL;
@@ -334,22 +363,22 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
func_do_profile(fp);
#endif
fp->uf_varargs = TRUE;
fp->uf_flags = 0;
fp->uf_flags = flags;
fp->uf_calls = 0;
fp->uf_script_ID = current_SID;
rettv->vval.v_string = vim_strsave(name);
rettv->v_type = VAR_FUNC;
}
else
ga_clear_strings(&newargs);
eval_lavars_used = old_eval_lavars;
return OK;
errret:
ga_clear_strings(&newargs);
ga_clear_strings(&newlines);
vim_free(fp);
eval_lavars_used = old_eval_lavars;
return FAIL;
}
@@ -624,6 +653,15 @@ free_funccal(
int free_val) /* a: vars were allocated */
{
listitem_T *li;
int i;
for (i = 0; i < fc->fc_funcs.ga_len; ++i)
{
ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i];
if (fp != NULL)
fp->uf_scoped = NULL;
}
/* The a: variables typevals may not have been allocated, only free the
* allocated variables. */
@@ -637,6 +675,16 @@ free_funccal(
for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next)
clear_tv(&li->li_tv);
for (i = 0; i < fc->fc_funcs.ga_len; ++i)
{
ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i];
if (fp != NULL)
func_unref(fc->func->uf_name);
}
ga_clear(&fc->fc_funcs);
func_unref(fc->func->uf_name);
vim_free(fc);
}
@@ -696,6 +744,11 @@ call_user_func(
/* Check if this function has a breakpoint. */
fc->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0);
fc->dbg_tick = debug_tick;
/* Set up fields for closure. */
fc->fc_refcount = 0;
fc->fc_copyID = 0;
ga_init2(&fc->fc_funcs, sizeof(ufunc_T *), 1);
func_ref(fp->uf_name);
if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0)
islambda = TRUE;
@@ -758,7 +811,6 @@ call_user_func(
for (i = 0; i < argcount; ++i)
{
int addlocal = FALSE;
dictitem_T *v2;
ai = i - fp->uf_args.ga_len;
if (ai < 0)
@@ -778,9 +830,6 @@ call_user_func(
{
v = &fc->fixvar[fixvar_idx++].var;
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
if (addlocal)
v2 = v;
}
else
{
@@ -789,36 +838,23 @@ call_user_func(
if (v == NULL)
break;
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
if (addlocal)
{
v2 = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T)
+ STRLEN(name)));
if (v2 == NULL)
{
vim_free(v);
break;
}
v2->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
}
}
STRCPY(v->di_key, name);
hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
/* Note: the values are copied directly to avoid alloc/free.
* "argvars" must have VAR_FIXED for v_lock. */
v->di_tv = argvars[i];
v->di_tv.v_lock = VAR_FIXED;
/* Named arguments can be accessed without the "a:" prefix in lambda
* expressions. Add to the l: dict. */
if (addlocal)
{
STRCPY(v2->di_key, name);
copy_tv(&v->di_tv, &v2->di_tv);
v2->di_tv.v_lock = VAR_FIXED;
hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v2));
/* Named arguments should be accessed without the "a:" prefix in
* lambda expressions. Add to the l: dict. */
copy_tv(&v->di_tv, &v->di_tv);
hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v));
}
else
hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
if (ai >= 0 && ai < MAX_FUNC_ARGS)
{
@@ -1014,7 +1050,8 @@ call_user_func(
* free the funccall_T and what's in it. */
if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT)
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT
&& fc->fc_refcount <= 0)
{
free_funccal(fc, FALSE);
}
@@ -1048,6 +1085,52 @@ call_user_func(
}
}
/*
* Unreference "fc": decrement the reference count and free it when it
* becomes zero. If "fp" is not NULL, "fp" is detached from "fc".
*/
static void
funccal_unref(funccall_T *fc, ufunc_T *fp)
{
funccall_T **pfc;
int i;
int freed = FALSE;
if (fc == NULL)
return;
if (--fc->fc_refcount <= 0)
{
for (pfc = &previous_funccal; *pfc != NULL; )
{
if (fc == *pfc
&& fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT)
{
*pfc = fc->caller;
free_funccal(fc, TRUE);
freed = TRUE;
}
else
pfc = &(*pfc)->caller;
}
}
if (!freed)
{
func_unref(fc->func->uf_name);
if (fp != NULL)
{
for (i = 0; i < fc->fc_funcs.ga_len; ++i)
{
if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp)
((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL;
}
}
}
}
/*
* Free a function and remove it from the list of functions.
*/
@@ -1072,6 +1155,8 @@ func_free(ufunc_T *fp)
else
hash_remove(&func_hashtab, hi);
funccal_unref(fp->uf_scoped, fp);
vim_free(fp);
}
@@ -1404,6 +1489,8 @@ list_func_head(ufunc_T *fp, int indent)
MSG_PUTS(" range");
if (fp->uf_flags & FC_DICT)
MSG_PUTS(" dict");
if (fp->uf_flags & FC_CLOSURE)
MSG_PUTS(" closure");
msg_clr_eos();
if (p_verbose > 0)
last_set_msg(fp->uf_script_ID);
@@ -1865,7 +1952,7 @@ ex_function(exarg_T *eap)
if (get_function_args(&p, ')', &newargs, &varargs, eap->skip) == FAIL)
goto errret_2;
/* find extra arguments "range", "dict" and "abort" */
/* find extra arguments "range", "dict", "abort" and "closure" */
for (;;)
{
p = skipwhite(p);
@@ -1884,6 +1971,11 @@ ex_function(exarg_T *eap)
flags |= FC_ABORT;
p += 5;
}
else if (STRNCMP(p, "closure", 7) == 0)
{
flags |= FC_CLOSURE;
p += 7;
}
else
break;
}
@@ -2216,6 +2308,25 @@ ex_function(exarg_T *eap)
}
fp->uf_args = newargs;
fp->uf_lines = newlines;
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(&current_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
fp->uf_tml_count = NULL;
fp->uf_tml_total = NULL;
@@ -2705,7 +2816,8 @@ can_free_funccal(funccall_T *fc, int copyID)
{
return (fc->l_varlist.lv_copyID != copyID
&& fc->l_vars.dv_copyID != copyID
&& fc->l_avars.dv_copyID != copyID);
&& fc->l_avars.dv_copyID != copyID
&& fc->fc_copyID != copyID);
}
/*
@@ -3450,6 +3562,76 @@ get_current_funccal_dict(hashtab_T *ht)
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.
*/
dictitem_T *
find_var_in_scoped_ht(char_u *name, char_u **varname, int no_autoload)
{
dictitem_T *v = NULL;
funccall_T *old_current_funccal = current_funccal;
hashtab_T *ht;
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 ? &(*varname) : NULL);
if (ht != NULL)
{
v = find_var_in_ht(ht, *name,
varname ? *varname : NULL, no_autoload);
if (v != NULL)
break;
}
if (current_funccal == current_funccal->func->uf_scoped)
break;
current_funccal = current_funccal->func->uf_scoped;
}
current_funccal = old_current_funccal;
return v;
}
/*
* Set "copyID + 1" in previous_funccal and callers.
*/
@@ -3461,6 +3643,7 @@ set_ref_in_previous_funccal(int copyID)
for (fc = previous_funccal; fc != NULL; fc = fc->caller)
{
fc->fc_copyID = copyID + 1;
abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1,
NULL);
abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1,
@@ -3480,6 +3663,7 @@ set_ref_in_call_stack(int copyID)
for (fc = current_funccal; fc != NULL; fc = fc->caller)
{
fc->fc_copyID = copyID;
abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL);
abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL);
}
@@ -3501,4 +3685,42 @@ set_ref_in_func_args(int copyID)
return abort;
}
/*
* Mark all lists and dicts referenced through function "name" with "copyID".
* "list_stack" is used to add lists to be marked. Can be NULL.
* "ht_stack" is used to add hashtabs to be marked. Can be NULL.
*
* Returns TRUE if setting references failed somehow.
*/
int
set_ref_in_func(char_u *name, int copyID)
{
ufunc_T *fp;
funccall_T *fc;
int error = ERROR_NONE;
char_u fname_buf[FLEN_FIXED + 1];
char_u *tofree = NULL;
char_u *fname;
if (name == NULL)
return FALSE;
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
fp = find_func(fname);
if (fp != NULL)
{
for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped)
{
if (fc->fc_copyID != copyID)
{
fc->fc_copyID = copyID;
set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL);
set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL);
}
}
}
vim_free(tofree);
return FALSE;
}
#endif /* FEAT_EVAL */
+23
View File
@@ -309,6 +309,11 @@ static char *(features[]) =
#else
"-keymap",
#endif
#ifdef FEAT_EVAL
"+lambda",
#else
"-lambda",
#endif
#ifdef FEAT_LANGMAP
"+langmap",
#else
@@ -773,6 +778,24 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2121,
/**/
2120,
/**/
2119,
/**/
2118,
/**/
2117,
/**/
2116,
/**/
2115,
/**/
2114,
/**/
2113,
/**/
2112,
/**/
+4 -4
View File
@@ -98,11 +98,11 @@
# ifndef HAVE_CONFIG_H
# define UNIX
# endif
# ifndef FEAT_CLIPBOARD
# if defined(FEAT_SMALL) && !defined(FEAT_CLIPBOARD)
# define FEAT_CLIPBOARD
# if defined(FEAT_SMALL) && !defined(FEAT_MOUSE)
# define FEAT_MOUSE
# endif
# endif
# if defined(FEAT_SMALL) && !defined(FEAT_MOUSE)
# define FEAT_MOUSE
# endif
#endif
#if defined(MACOS_X) || defined(MACOS_CLASSIC)