mirror of
https://github.com/neovim/neovim.git
synced 2024-12-19 18:55:14 -07:00
vim-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.
806a273f3c
Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
parent
b75634e55e
commit
0167649ce4
@ -9507,31 +9507,43 @@ writefile({object}, {fname} [, {flags}])
|
||||
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
|
||||
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" fsync() call is not used, with "s"
|
||||
it is used, 'fsync' option applies by default. No fsync()
|
||||
means that writefile() will finish faster, but writes may be
|
||||
left in OS buffers and not yet written to disk. Such changes
|
||||
will disappear if system crashes before OS does writing.
|
||||
|
||||
All NL characters are replaced with a NUL character.
|
||||
Inserting CR characters needs to be done before passing {list}
|
||||
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.
|
||||
|
||||
When the write fails -1 is returned, otherwise 0. There is an
|
||||
error message if the file can't be created or when writing
|
||||
fails.
|
||||
|
||||
Also see |readfile()|.
|
||||
To copy a file byte for byte: >
|
||||
:let fl = readfile("foo", "b")
|
||||
|
@ -9296,6 +9296,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
|
||||
bool binary = false;
|
||||
bool append = false;
|
||||
bool defer = false;
|
||||
bool do_fsync = !!p_fs;
|
||||
bool mkdir_p = false;
|
||||
if (argvars[2].v_type != VAR_UNKNOWN) {
|
||||
@ -9309,6 +9310,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
binary = true; break;
|
||||
case 'a':
|
||||
append = true; break;
|
||||
case 'D':
|
||||
defer = true; break;
|
||||
case 's':
|
||||
do_fsync = true; break;
|
||||
case 'S':
|
||||
@ -9328,6 +9331,12 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
if (fname == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (defer && get_current_funccal() == NULL) {
|
||||
semsg(_(e_str_not_inside_function), "defer");
|
||||
return;
|
||||
}
|
||||
|
||||
FileDescriptor fp;
|
||||
int error;
|
||||
if (*fname == NUL) {
|
||||
@ -9336,9 +9345,17 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
((append ? kFileAppend : kFileTruncate)
|
||||
| (mkdir_p ? kFileMkDir : kFileCreate)
|
||||
| kFileCreate), 0666)) != 0) {
|
||||
semsg(_("E482: Can't open file %s for writing: %s"),
|
||||
fname, os_strerror(error));
|
||||
semsg(_("E482: Can't open file %s for writing: %s"), fname, os_strerror(error));
|
||||
} else {
|
||||
if (defer) {
|
||||
typval_T tv = {
|
||||
.v_type = VAR_STRING,
|
||||
.v_lock = VAR_UNLOCKED,
|
||||
.vval.v_string = xstrdup(fname),
|
||||
};
|
||||
add_defer("delete", 1, &tv);
|
||||
}
|
||||
|
||||
bool write_ok;
|
||||
if (argvars[0].v_type == VAR_BLOB) {
|
||||
write_ok = write_blob(&fp, argvars[0].vval.v_blob);
|
||||
|
@ -478,6 +478,7 @@ void emsg_funcname(const char *errmsg, const char *name)
|
||||
|
||||
/// Get function arguments at "*arg" and advance it.
|
||||
/// 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 get_func_arguments(char **arg, evalarg_T *const evalarg, int partial_argc,
|
||||
typval_T *argvars, int *argcount)
|
||||
{
|
||||
@ -3119,16 +3120,28 @@ static int ex_defer_inner(char *name, char **arg, evalarg_T *const evalarg)
|
||||
{
|
||||
typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
|
||||
int argcount = 0; // number of arguments found
|
||||
int ret = FAIL;
|
||||
|
||||
if (current_funccal == NULL) {
|
||||
semsg(_(e_str_not_inside_function), "defer");
|
||||
return FAIL;
|
||||
}
|
||||
if (get_func_arguments(arg, evalarg, false, argvars, &argcount) == FAIL) {
|
||||
goto theend;
|
||||
while (--argcount >= 0) {
|
||||
tv_clear(&argvars[argcount]);
|
||||
}
|
||||
return FAIL;
|
||||
}
|
||||
add_defer(name, argcount, argvars);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/// Add a deferred call for "name" with arguments "argvars[argcount]".
|
||||
/// Consumes "argvars[]".
|
||||
/// Caller must check that current_funccal is not NULL.
|
||||
void add_defer(char *name, int argcount_arg, typval_T *argvars)
|
||||
{
|
||||
char *saved_name = xstrdup(name);
|
||||
int argcount = argcount_arg;
|
||||
|
||||
if (current_funccal->fc_defer.ga_itemsize == 0) {
|
||||
ga_init(¤t_funccal->fc_defer, sizeof(defer_T), 10);
|
||||
@ -3140,13 +3153,6 @@ static int ex_defer_inner(char *name, char **arg, evalarg_T *const evalarg)
|
||||
argcount--;
|
||||
dr->dr_argvars[argcount] = argvars[argcount];
|
||||
}
|
||||
ret = OK;
|
||||
|
||||
theend:
|
||||
while (--argcount >= 0) {
|
||||
tv_clear(&argvars[argcount]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Invoked after a function has finished: invoke ":defer" functions.
|
||||
|
@ -6250,28 +6250,6 @@ func Test_very_long_error_line()
|
||||
call setqflist([], 'f')
|
||||
endfunc
|
||||
|
||||
" The test depends on deferred delete and string interpolation, which haven't
|
||||
" been ported, so override it with a rewrite that doesn't use these features.
|
||||
func! Test_very_long_error_line()
|
||||
let msg = repeat('abcdefghijklmn', 146)
|
||||
let emsg = 'Xlonglines.c:1:' . msg
|
||||
call writefile([msg, emsg], 'Xerror')
|
||||
cfile Xerror
|
||||
call delete('Xerror')
|
||||
cwindow
|
||||
call assert_equal('|| ' .. msg, getline(1))
|
||||
call assert_equal('Xlonglines.c|1| ' .. msg, getline(2))
|
||||
cclose
|
||||
|
||||
let l = execute('clist!')->split("\n")
|
||||
call assert_equal([' 1: ' .. msg, ' 2 Xlonglines.c:1: ' .. msg], l)
|
||||
|
||||
let l = execute('cc')->split("\n")
|
||||
call assert_equal(['(2 of 2): ' .. msg], l)
|
||||
|
||||
call setqflist([], 'f')
|
||||
endfunc
|
||||
|
||||
" In the quickfix window, spaces at the beginning of an informational line
|
||||
" should not be removed but should be removed from an error line.
|
||||
func Test_info_line_with_space()
|
||||
|
@ -924,19 +924,36 @@ endfunc
|
||||
" Test for ':write ++bin' and ':write ++nobin'
|
||||
func Test_write_binary_file()
|
||||
" create a file without an eol/eof character
|
||||
call writefile(0z616161, 'Xfile1', 'b')
|
||||
new Xfile1
|
||||
write ++bin Xfile2
|
||||
write ++nobin Xfile3
|
||||
call assert_equal(0z616161, readblob('Xfile2'))
|
||||
call writefile(0z616161, 'Xwbfile1', 'b')
|
||||
new Xwbfile1
|
||||
write ++bin Xwbfile2
|
||||
write ++nobin Xwbfile3
|
||||
call assert_equal(0z616161, readblob('Xwbfile2'))
|
||||
if has('win32')
|
||||
call assert_equal(0z6161610D.0A, readblob('Xfile3'))
|
||||
call assert_equal(0z6161610D.0A, readblob('Xwbfile3'))
|
||||
else
|
||||
call assert_equal(0z6161610A, readblob('Xfile3'))
|
||||
call assert_equal(0z6161610A, readblob('Xwbfile3'))
|
||||
endif
|
||||
call delete('Xfile1')
|
||||
call delete('Xfile2')
|
||||
call delete('Xfile3')
|
||||
call delete('Xwbfile1')
|
||||
call delete('Xwbfile2')
|
||||
call delete('Xwbfile3')
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user