mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
patch 9.0.0379: cleaning up after writefile() is a hassle
Problem: Cleaning up after writefile() is a hassle. Solution: Add the 'D' flag to defer deleting the written file. Very useful in tests.
This commit is contained in:
@@ -10448,34 +10448,43 @@ writefile({object}, {fname} [, {flags}])
|
|||||||
When {object} is a |List| write it to file {fname}. Each list
|
When {object} is a |List| write it to file {fname}. Each list
|
||||||
item is separated with a NL. Each list item must be a String
|
item is separated with a NL. Each list item must be a String
|
||||||
or Number.
|
or Number.
|
||||||
When {flags} contains "b" then binary mode is used: There will
|
|
||||||
not be a NL after the last list item. An empty item at the
|
|
||||||
end does cause the last line in the file to end in a NL.
|
|
||||||
|
|
||||||
When {object} is a |Blob| write the bytes to file {fname}
|
|
||||||
unmodified.
|
|
||||||
|
|
||||||
When {flags} contains "a" then append mode is used, lines are
|
|
||||||
appended to the file: >
|
|
||||||
:call writefile(["foo"], "event.log", "a")
|
|
||||||
:call writefile(["bar"], "event.log", "a")
|
|
||||||
<
|
|
||||||
When {flags} contains "s" then fsync() is called after writing
|
|
||||||
the file. This flushes the file to disk, if possible. This
|
|
||||||
takes more time but avoids losing the file if the system
|
|
||||||
crashes.
|
|
||||||
When {flags} does not contain "S" or "s" then fsync() is
|
|
||||||
called if the 'fsync' option is set.
|
|
||||||
When {flags} contains "S" then fsync() is not called, even
|
|
||||||
when 'fsync' is set.
|
|
||||||
|
|
||||||
All NL characters are replaced with a NUL character.
|
All NL characters are replaced with a NUL character.
|
||||||
Inserting CR characters needs to be done before passing {list}
|
Inserting CR characters needs to be done before passing {list}
|
||||||
to writefile().
|
to writefile().
|
||||||
|
|
||||||
|
When {object} is a |Blob| write the bytes to file {fname}
|
||||||
|
unmodified, also when binary mode is not specified.
|
||||||
|
|
||||||
|
{flags} must be a String. These characters are recognized:
|
||||||
|
|
||||||
|
'b' Binary mode is used: There will not be a NL after the
|
||||||
|
last list item. An empty item at the end does cause the
|
||||||
|
last line in the file to end in a NL.
|
||||||
|
|
||||||
|
'a' Append mode is used, lines are appended to the file: >
|
||||||
|
:call writefile(["foo"], "event.log", "a")
|
||||||
|
:call writefile(["bar"], "event.log", "a")
|
||||||
|
<
|
||||||
|
'D' Delete the file when the current function ends. This
|
||||||
|
works like: >
|
||||||
|
:defer delete({fname})
|
||||||
|
< Fails when not in a function. Also see |:defer|.
|
||||||
|
|
||||||
|
's' fsync() is called after writing the file. This flushes
|
||||||
|
the file to disk, if possible. This takes more time but
|
||||||
|
avoids losing the file if the system crashes.
|
||||||
|
|
||||||
|
'S' fsync() is not called, even when 'fsync' is set.
|
||||||
|
|
||||||
|
When {flags} does not contain "S" or "s" then fsync() is
|
||||||
|
called if the 'fsync' option is set.
|
||||||
|
|
||||||
An existing file is overwritten, if possible.
|
An existing file is overwritten, if possible.
|
||||||
|
|
||||||
When the write fails -1 is returned, otherwise 0. There is an
|
When the write fails -1 is returned, otherwise 0. There is an
|
||||||
error message if the file can't be created or when writing
|
error message if the file can't be created or when writing
|
||||||
fails.
|
fails.
|
||||||
|
|
||||||
Also see |readfile()|.
|
Also see |readfile()|.
|
||||||
To copy a file byte for byte: >
|
To copy a file byte for byte: >
|
||||||
:let fl = readfile("foo", "b")
|
:let fl = readfile("foo", "b")
|
||||||
|
@@ -2232,6 +2232,7 @@ f_writefile(typval_T *argvars, typval_T *rettv)
|
|||||||
{
|
{
|
||||||
int binary = FALSE;
|
int binary = FALSE;
|
||||||
int append = FALSE;
|
int append = FALSE;
|
||||||
|
int defer = FALSE;
|
||||||
#ifdef HAVE_FSYNC
|
#ifdef HAVE_FSYNC
|
||||||
int do_fsync = p_fs;
|
int do_fsync = p_fs;
|
||||||
#endif
|
#endif
|
||||||
@@ -2285,6 +2286,8 @@ f_writefile(typval_T *argvars, typval_T *rettv)
|
|||||||
binary = TRUE;
|
binary = TRUE;
|
||||||
if (vim_strchr(arg2, 'a') != NULL)
|
if (vim_strchr(arg2, 'a') != NULL)
|
||||||
append = TRUE;
|
append = TRUE;
|
||||||
|
if (vim_strchr(arg2, 'D') != NULL)
|
||||||
|
defer = TRUE;
|
||||||
#ifdef HAVE_FSYNC
|
#ifdef HAVE_FSYNC
|
||||||
if (vim_strchr(arg2, 's') != NULL)
|
if (vim_strchr(arg2, 's') != NULL)
|
||||||
do_fsync = TRUE;
|
do_fsync = TRUE;
|
||||||
@@ -2297,38 +2300,60 @@ f_writefile(typval_T *argvars, typval_T *rettv)
|
|||||||
if (fname == NULL)
|
if (fname == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (defer && !in_def_function() && get_current_funccal() == NULL)
|
||||||
|
{
|
||||||
|
semsg(_(e_str_not_inside_function), "defer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Always open the file in binary mode, library functions have a mind of
|
// Always open the file in binary mode, library functions have a mind of
|
||||||
// their own about CR-LF conversion.
|
// their own about CR-LF conversion.
|
||||||
if (*fname == NUL || (fd = mch_fopen((char *)fname,
|
if (*fname == NUL || (fd = mch_fopen((char *)fname,
|
||||||
append ? APPENDBIN : WRITEBIN)) == NULL)
|
append ? APPENDBIN : WRITEBIN)) == NULL)
|
||||||
{
|
{
|
||||||
semsg(_(e_cant_create_file_str), *fname == NUL ? (char_u *)_("<empty>") : fname);
|
semsg(_(e_cant_create_file_str),
|
||||||
|
*fname == NUL ? (char_u *)_("<empty>") : fname);
|
||||||
ret = -1;
|
ret = -1;
|
||||||
}
|
}
|
||||||
else if (blob)
|
else
|
||||||
|
{
|
||||||
|
if (defer)
|
||||||
|
{
|
||||||
|
typval_T tv;
|
||||||
|
|
||||||
|
tv.v_type = VAR_STRING;
|
||||||
|
tv.v_lock = 0;
|
||||||
|
tv.vval.v_string = vim_strsave(fname);
|
||||||
|
if (tv.vval.v_string == NULL
|
||||||
|
|| add_defer((char_u *)"delete", 1, &tv) == FAIL)
|
||||||
|
{
|
||||||
|
ret = -1;
|
||||||
|
fclose(fd);
|
||||||
|
(void)mch_remove(fname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
if (blob)
|
||||||
{
|
{
|
||||||
if (write_blob(fd, blob) == FAIL)
|
if (write_blob(fd, blob) == FAIL)
|
||||||
ret = -1;
|
ret = -1;
|
||||||
#ifdef HAVE_FSYNC
|
|
||||||
else if (do_fsync)
|
|
||||||
// Ignore the error, the user wouldn't know what to do about it.
|
|
||||||
// May happen for a device.
|
|
||||||
vim_ignored = vim_fsync(fileno(fd));
|
|
||||||
#endif
|
|
||||||
fclose(fd);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (write_list(fd, list, binary) == FAIL)
|
if (write_list(fd, list, binary) == FAIL)
|
||||||
ret = -1;
|
ret = -1;
|
||||||
|
}
|
||||||
#ifdef HAVE_FSYNC
|
#ifdef HAVE_FSYNC
|
||||||
else if (do_fsync)
|
if (ret == 0 && do_fsync)
|
||||||
// Ignore the error, the user wouldn't know what to do about it.
|
// Ignore the error, the user wouldn't know what to do about
|
||||||
// May happen for a device.
|
// it. May happen for a device.
|
||||||
vim_ignored = vim_fsync(fileno(fd));
|
vim_ignored = vim_fsync(fileno(fd));
|
||||||
#endif
|
#endif
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rettv->vval.v_number = ret;
|
rettv->vval.v_number = ret;
|
||||||
}
|
}
|
||||||
|
@@ -58,6 +58,7 @@ void func_ptr_unref(ufunc_T *fp);
|
|||||||
void func_ref(char_u *name);
|
void func_ref(char_u *name);
|
||||||
void func_ptr_ref(ufunc_T *fp);
|
void func_ptr_ref(ufunc_T *fp);
|
||||||
void ex_return(exarg_T *eap);
|
void ex_return(exarg_T *eap);
|
||||||
|
int add_defer(char_u *name, int argcount_arg, typval_T *argvars);
|
||||||
void handle_defer(void);
|
void handle_defer(void);
|
||||||
void ex_call(exarg_T *eap);
|
void ex_call(exarg_T *eap);
|
||||||
int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv);
|
int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv);
|
||||||
|
@@ -21,6 +21,7 @@ char_u *compile_finally(char_u *arg, cctx_T *cctx);
|
|||||||
char_u *compile_endtry(char_u *arg, cctx_T *cctx);
|
char_u *compile_endtry(char_u *arg, cctx_T *cctx);
|
||||||
char_u *compile_throw(char_u *arg, cctx_T *cctx);
|
char_u *compile_throw(char_u *arg, cctx_T *cctx);
|
||||||
char_u *compile_eval(char_u *arg, cctx_T *cctx);
|
char_u *compile_eval(char_u *arg, cctx_T *cctx);
|
||||||
|
int get_defer_var_idx(cctx_T *cctx);
|
||||||
char_u *compile_defer(char_u *arg_start, cctx_T *cctx);
|
char_u *compile_defer(char_u *arg_start, cctx_T *cctx);
|
||||||
char_u *compile_mult_expr(char_u *arg, int cmdidx, cctx_T *cctx);
|
char_u *compile_mult_expr(char_u *arg, int cmdidx, cctx_T *cctx);
|
||||||
char_u *compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx);
|
char_u *compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx);
|
||||||
|
@@ -3,6 +3,8 @@ void to_string_error(vartype_T vartype);
|
|||||||
void update_has_breakpoint(ufunc_T *ufunc);
|
void update_has_breakpoint(ufunc_T *ufunc);
|
||||||
void funcstack_check_refcount(funcstack_T *funcstack);
|
void funcstack_check_refcount(funcstack_T *funcstack);
|
||||||
int set_ref_in_funcstacks(int copyID);
|
int set_ref_in_funcstacks(int copyID);
|
||||||
|
int in_def_function(void);
|
||||||
|
int add_defer_function(char_u *name, int argcount, typval_T *argvars);
|
||||||
char_u *char_from_string(char_u *str, varnumber_T index);
|
char_u *char_from_string(char_u *str, varnumber_T index);
|
||||||
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
|
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
|
||||||
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
|
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
|
||||||
|
@@ -933,6 +933,23 @@ func Test_write_binary_file()
|
|||||||
call delete('Xwbfile3')
|
call delete('Xwbfile3')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func DoWriteDefer()
|
||||||
|
call writefile(['some text'], 'XdeferDelete', 'D')
|
||||||
|
call assert_equal(['some text'], readfile('XdeferDelete'))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
def DefWriteDefer()
|
||||||
|
writefile(['some text'], 'XdefdeferDelete', 'D')
|
||||||
|
assert_equal(['some text'], readfile('XdefdeferDelete'))
|
||||||
|
enddef
|
||||||
|
|
||||||
|
func Test_write_with_deferred_delete()
|
||||||
|
call DoWriteDefer()
|
||||||
|
call assert_equal('', glob('XdeferDelete'))
|
||||||
|
call DefWriteDefer()
|
||||||
|
call assert_equal('', glob('XdefdeferDelete'))
|
||||||
|
endfunc
|
||||||
|
|
||||||
" Check that buffer is written before triggering QuitPre
|
" Check that buffer is written before triggering QuitPre
|
||||||
func Test_wq_quitpre_autocommand()
|
func Test_wq_quitpre_autocommand()
|
||||||
edit Xsomefile
|
edit Xsomefile
|
||||||
|
@@ -1728,6 +1728,7 @@ emsg_funcname(char *ermsg, char_u *name)
|
|||||||
/*
|
/*
|
||||||
* Get function arguments at "*arg" and advance it.
|
* Get function arguments at "*arg" and advance it.
|
||||||
* Return them in "*argvars[MAX_FUNC_ARGS + 1]" and the count in "argcount".
|
* Return them in "*argvars[MAX_FUNC_ARGS + 1]" and the count in "argcount".
|
||||||
|
* On failure FAIL is returned but the "argvars[argcount]" are still set.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
get_func_arguments(
|
get_func_arguments(
|
||||||
@@ -5570,9 +5571,6 @@ ex_defer_inner(char_u *name, char_u **arg, evalarg_T *evalarg)
|
|||||||
{
|
{
|
||||||
typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
|
typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
|
||||||
int argcount = 0; // number of arguments found
|
int argcount = 0; // number of arguments found
|
||||||
defer_T *dr;
|
|
||||||
int ret = FAIL;
|
|
||||||
char_u *saved_name;
|
|
||||||
|
|
||||||
if (current_funccal == NULL)
|
if (current_funccal == NULL)
|
||||||
{
|
{
|
||||||
@@ -5580,11 +5578,38 @@ ex_defer_inner(char_u *name, char_u **arg, evalarg_T *evalarg)
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
if (get_func_arguments(arg, evalarg, FALSE, argvars, &argcount) == FAIL)
|
if (get_func_arguments(arg, evalarg, FALSE, argvars, &argcount) == FAIL)
|
||||||
goto theend;
|
{
|
||||||
saved_name = vim_strsave(name);
|
while (--argcount >= 0)
|
||||||
|
clear_tv(&argvars[argcount]);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return add_defer(name, argcount, argvars);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a deferred call for "name" with arguments "argvars[argcount]".
|
||||||
|
* Consumes "argvars[]".
|
||||||
|
* Caller must check that in_def_function() returns TRUE or current_funccal is
|
||||||
|
* not NULL.
|
||||||
|
* Returns OK or FAIL.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
add_defer(char_u *name, int argcount_arg, typval_T *argvars)
|
||||||
|
{
|
||||||
|
char_u *saved_name = vim_strsave(name);
|
||||||
|
int argcount = argcount_arg;
|
||||||
|
defer_T *dr;
|
||||||
|
int ret = FAIL;
|
||||||
|
|
||||||
if (saved_name == NULL)
|
if (saved_name == NULL)
|
||||||
goto theend;
|
goto theend;
|
||||||
|
if (in_def_function())
|
||||||
|
{
|
||||||
|
if (add_defer_function(saved_name, argcount, argvars) == OK)
|
||||||
|
argcount = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if (current_funccal->fc_defer.ga_itemsize == 0)
|
if (current_funccal->fc_defer.ga_itemsize == 0)
|
||||||
ga_init2(¤t_funccal->fc_defer, sizeof(defer_T), 10);
|
ga_init2(¤t_funccal->fc_defer, sizeof(defer_T), 10);
|
||||||
if (ga_grow(¤t_funccal->fc_defer, 1) == FAIL)
|
if (ga_grow(¤t_funccal->fc_defer, 1) == FAIL)
|
||||||
@@ -5598,6 +5623,7 @@ ex_defer_inner(char_u *name, char_u **arg, evalarg_T *evalarg)
|
|||||||
--argcount;
|
--argcount;
|
||||||
dr->dr_argvars[argcount] = argvars[argcount];
|
dr->dr_argvars[argcount] = argvars[argcount];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ret = OK;
|
ret = OK;
|
||||||
|
|
||||||
theend:
|
theend:
|
||||||
|
@@ -703,6 +703,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 */
|
||||||
|
/**/
|
||||||
|
379,
|
||||||
/**/
|
/**/
|
||||||
378,
|
378,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -1684,6 +1684,27 @@ compile_eval(char_u *arg, cctx_T *cctx)
|
|||||||
return skipwhite(p);
|
return skipwhite(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the local variable index for deferred function calls.
|
||||||
|
* Reserve it when not done already.
|
||||||
|
* Returns zero for failure.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
get_defer_var_idx(cctx_T *cctx)
|
||||||
|
{
|
||||||
|
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
|
||||||
|
+ cctx->ctx_ufunc->uf_dfunc_idx;
|
||||||
|
if (dfunc->df_defer_var_idx == 0)
|
||||||
|
{
|
||||||
|
lvar_T *lvar = reserve_local(cctx, (char_u *)"@defer@", 7,
|
||||||
|
TRUE, &t_list_any);
|
||||||
|
if (lvar == NULL)
|
||||||
|
return 0;
|
||||||
|
dfunc->df_defer_var_idx = lvar->lv_idx + 1;
|
||||||
|
}
|
||||||
|
return dfunc->df_defer_var_idx;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compile "defer func(arg)".
|
* Compile "defer func(arg)".
|
||||||
*/
|
*/
|
||||||
@@ -1693,7 +1714,7 @@ compile_defer(char_u *arg_start, cctx_T *cctx)
|
|||||||
char_u *p;
|
char_u *p;
|
||||||
char_u *arg = arg_start;
|
char_u *arg = arg_start;
|
||||||
int argcount = 0;
|
int argcount = 0;
|
||||||
dfunc_T *dfunc;
|
int defer_var_idx;
|
||||||
type_T *type;
|
type_T *type;
|
||||||
int func_idx;
|
int func_idx;
|
||||||
|
|
||||||
@@ -1730,16 +1751,10 @@ compile_defer(char_u *arg_start, cctx_T *cctx)
|
|||||||
|
|
||||||
// TODO: check argument count with "type"
|
// TODO: check argument count with "type"
|
||||||
|
|
||||||
dfunc = ((dfunc_T *)def_functions.ga_data) + cctx->ctx_ufunc->uf_dfunc_idx;
|
defer_var_idx = get_defer_var_idx(cctx);
|
||||||
if (dfunc->df_defer_var_idx == 0)
|
if (defer_var_idx == 0)
|
||||||
{
|
|
||||||
lvar_T *lvar = reserve_local(cctx, (char_u *)"@defer@", 7,
|
|
||||||
TRUE, &t_list_any);
|
|
||||||
if (lvar == NULL)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
dfunc->df_defer_var_idx = lvar->lv_idx + 1;
|
if (generate_DEFER(cctx, defer_var_idx - 1, argcount) == FAIL)
|
||||||
}
|
|
||||||
if (generate_DEFER(cctx, dfunc->df_defer_var_idx - 1, argcount) == FAIL)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return skipwhite(arg);
|
return skipwhite(arg);
|
||||||
|
@@ -845,41 +845,71 @@ set_ref_in_funcstacks(int copyID)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ugly static to avoid passing the execution context around through many
|
||||||
|
// layers.
|
||||||
|
static ectx_T *current_ectx = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle ISN_DEFER. Stack has a function reference and "argcount" arguments.
|
* Return TRUE if currently executing a :def function.
|
||||||
* The local variable that lists deferred functions is "var_idx".
|
* Can be used by builtin functions only.
|
||||||
* Returns OK or FAIL.
|
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
add_defer_func(int var_idx, int argcount, ectx_T *ectx)
|
in_def_function(void)
|
||||||
|
{
|
||||||
|
return current_ectx != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add an entry for a deferred function call to the currently executing
|
||||||
|
* function.
|
||||||
|
* Return the list or NULL when failed.
|
||||||
|
*/
|
||||||
|
static list_T *
|
||||||
|
add_defer_item(int var_idx, int argcount, ectx_T *ectx)
|
||||||
{
|
{
|
||||||
typval_T *defer_tv = STACK_TV_VAR(var_idx);
|
typval_T *defer_tv = STACK_TV_VAR(var_idx);
|
||||||
list_T *defer_l;
|
list_T *defer_l;
|
||||||
typval_T *func_tv;
|
|
||||||
list_T *l;
|
list_T *l;
|
||||||
int i;
|
|
||||||
typval_T listval;
|
typval_T listval;
|
||||||
|
|
||||||
if (defer_tv->v_type != VAR_LIST)
|
if (defer_tv->v_type != VAR_LIST)
|
||||||
{
|
{
|
||||||
// first time, allocate the list
|
// first time, allocate the list
|
||||||
if (rettv_list_alloc(defer_tv) == FAIL)
|
if (rettv_list_alloc(defer_tv) == FAIL)
|
||||||
return FAIL;
|
return NULL;
|
||||||
}
|
}
|
||||||
defer_l = defer_tv->vval.v_list;
|
defer_l = defer_tv->vval.v_list;
|
||||||
|
|
||||||
l = list_alloc_with_items(argcount + 1);
|
l = list_alloc_with_items(argcount + 1);
|
||||||
if (l == NULL)
|
if (l == NULL)
|
||||||
return FAIL;
|
return NULL;
|
||||||
listval.v_type = VAR_LIST;
|
listval.v_type = VAR_LIST;
|
||||||
listval.vval.v_list = l;
|
listval.vval.v_list = l;
|
||||||
listval.v_lock = 0;
|
listval.v_lock = 0;
|
||||||
if (list_insert_tv(defer_l, &listval, defer_l->lv_first) == FAIL)
|
if (list_insert_tv(defer_l, &listval, defer_l->lv_first) == FAIL)
|
||||||
{
|
{
|
||||||
vim_free(l);
|
vim_free(l);
|
||||||
return FAIL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle ISN_DEFER. Stack has a function reference and "argcount" arguments.
|
||||||
|
* The local variable that lists deferred functions is "var_idx".
|
||||||
|
* Returns OK or FAIL.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
defer_command(int var_idx, int argcount, ectx_T *ectx)
|
||||||
|
{
|
||||||
|
list_T *l = add_defer_item(var_idx, argcount, ectx);
|
||||||
|
int i;
|
||||||
|
typval_T *func_tv;
|
||||||
|
|
||||||
|
if (l == NULL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
func_tv = STACK_TV_BOT(-argcount - 1);
|
func_tv = STACK_TV_BOT(-argcount - 1);
|
||||||
// TODO: check type is a funcref
|
// TODO: check type is a funcref
|
||||||
list_set_item(l, 0, func_tv);
|
list_set_item(l, 0, func_tv);
|
||||||
@@ -890,6 +920,43 @@ add_defer_func(int var_idx, int argcount, ectx_T *ectx)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a deferred function "name" with one argument "arg_tv".
|
||||||
|
* Consumes "name", also on failure.
|
||||||
|
* Only to be called when in_def_function() returns TRUE.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
add_defer_function(char_u *name, int argcount, typval_T *argvars)
|
||||||
|
{
|
||||||
|
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
|
||||||
|
+ current_ectx->ec_dfunc_idx;
|
||||||
|
list_T *l;
|
||||||
|
typval_T func_tv;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (dfunc->df_defer_var_idx == 0)
|
||||||
|
{
|
||||||
|
iemsg("df_defer_var_idx is zero");
|
||||||
|
vim_free(func_tv.vval.v_string);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
func_tv.v_type = VAR_FUNC;
|
||||||
|
func_tv.v_lock = 0;
|
||||||
|
func_tv.vval.v_string = name;
|
||||||
|
|
||||||
|
l = add_defer_item(dfunc->df_defer_var_idx - 1, 1, current_ectx);
|
||||||
|
if (l == NULL)
|
||||||
|
{
|
||||||
|
vim_free(func_tv.vval.v_string);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_set_item(l, 0, &func_tv);
|
||||||
|
for (i = 0; i < argcount; ++i)
|
||||||
|
list_set_item(l, i + 1, argvars + i);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Invoked when returning from a function: Invoke any deferred calls.
|
* Invoked when returning from a function: Invoke any deferred calls.
|
||||||
*/
|
*/
|
||||||
@@ -1068,10 +1135,6 @@ call_prepare(int argcount, typval_T *argvars, ectx_T *ectx)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ugly global to avoid passing the execution context around through many
|
|
||||||
// layers.
|
|
||||||
static ectx_T *current_ectx = NULL;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call a builtin function by index.
|
* Call a builtin function by index.
|
||||||
*/
|
*/
|
||||||
@@ -3748,7 +3811,7 @@ exec_instructions(ectx_T *ectx)
|
|||||||
|
|
||||||
// :defer func(arg)
|
// :defer func(arg)
|
||||||
case ISN_DEFER:
|
case ISN_DEFER:
|
||||||
if (add_defer_func(iptr->isn_arg.defer.defer_var_idx,
|
if (defer_command(iptr->isn_arg.defer.defer_var_idx,
|
||||||
iptr->isn_arg.defer.defer_argcount, ectx) == FAIL)
|
iptr->isn_arg.defer.defer_argcount, ectx) == FAIL)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
break;
|
break;
|
||||||
@@ -5121,7 +5184,7 @@ theend:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute the instructions from a VAR_INSTR typeval and put the result in
|
* Execute the instructions from a VAR_INSTR typval and put the result in
|
||||||
* "rettv".
|
* "rettv".
|
||||||
* Return OK or FAIL.
|
* Return OK or FAIL.
|
||||||
*/
|
*/
|
||||||
|
@@ -833,6 +833,14 @@ compile_call(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (STRCMP(name, "writefile") == 0 && argcount > 2)
|
||||||
|
{
|
||||||
|
// May have the "D" flag, reserve a variable for a deferred
|
||||||
|
// function call.
|
||||||
|
if (get_defer_var_idx(cctx) == 0)
|
||||||
|
idx = -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (idx >= 0)
|
if (idx >= 0)
|
||||||
res = generate_BCALL(cctx, idx, argcount, argcount_init == 1);
|
res = generate_BCALL(cctx, idx, argcount, argcount_init == 1);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user