commit ce274231320777e08da9f1075a226a8a0351da86
parent f0b9232ad80804d85395130c54f1b8f671582c2c
Author: Rob Pilling <robpilling@gmail.com>
Date: Sat, 11 Oct 2025 16:27:58 +0100
feat: ":wall ++p" creates parent dirs for each buf #36121
`:wall ++p` will create parent directories if they do not exist, for
each modified buffer
Diffstat:
5 files changed, 39 insertions(+), 9 deletions(-)
diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt
@@ -996,11 +996,13 @@ slower (but safer).
WRITING WITH MULTIPLE BUFFERS *buffer-write*
*:wa* *:wall*
-:wa[ll] Write all changed buffers. Buffers without a file
+:wa[ll] [++opt] Write all changed buffers. Buffers without a file
name cause an error message. Buffers which are
readonly are not written.
+ For ++opt see |++opt|, but only ++p is effective, and
+ applies to each written file.
-:wa[ll]! Write all changed buffers, even the ones that are
+:wa[ll]! [++opt] Write all changed buffers, even the ones that are
readonly. Buffers without a file name are not
written and cause an error message.
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
@@ -197,6 +197,8 @@ EDITOR
• |gx| in help buffers opens the online documentation for the tag under the
cursor.
• |:Undotree| for visually navigating the |undo-tree|
+• |:wall| permits a |++p| option for creating parent directories when writing
+ changed buffers.
EVENTS
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
@@ -1806,6 +1806,15 @@ static int check_writable(const char *fname)
}
#endif
+static int handle_mkdir_p_arg(exarg_T *eap, char *fname)
+{
+ if (eap->mkdir_p && os_file_mkdir(fname, 0755) < 0) {
+ return FAIL;
+ }
+
+ return OK;
+}
+
/// Write current buffer to file "eap->arg".
/// If "eap->append" is true, append to the file.
///
@@ -1943,11 +1952,9 @@ int do_write(exarg_T *eap)
fname = curbuf->b_sfname;
}
- if (eap->mkdir_p) {
- if (os_file_mkdir(fname, 0755) < 0) {
- retval = FAIL;
- goto theend;
- }
+ if (handle_mkdir_p_arg(eap, fname) == FAIL) {
+ retval = FAIL;
+ goto theend;
}
int name_was_missing = curbuf->b_ffname == NULL;
@@ -2123,7 +2130,8 @@ void do_wqall(exarg_T *eap)
} else {
bufref_T bufref;
set_bufref(&bufref, buf);
- if (buf_write_all(buf, eap->forceit) == FAIL) {
+ if (handle_mkdir_p_arg(eap, buf->b_fname) == FAIL
+ || buf_write_all(buf, eap->forceit) == FAIL) {
error++;
}
// An autocommand may have deleted the buffer.
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
@@ -3187,7 +3187,7 @@ M.cmds = {
},
{
command = 'wall',
- flags = bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
+ flags = bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK, ARGOPT),
addr_type = 'ADDR_NONE',
func = 'do_wqall',
},
diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua
@@ -23,6 +23,9 @@ describe(':write', function()
os.remove('test_fifo')
os.remove('test/write/p_opt.txt')
os.remove('test/write')
+ os.remove('test/write2/p_opt.txt')
+ os.remove('test/write2/p_opt2.txt')
+ os.remove('test/write2')
os.remove('test')
os.remove(fname)
os.remove(fname_bak)
@@ -111,6 +114,21 @@ describe(':write', function()
command('write ++p test/write/p_opt.txt')
eq(1, eval("filereadable('test/write/p_opt.txt')"))
+ eq(0, eval("filereadable('test/write2/p_opt.txt')"))
+ eq(0, eval("filereadable('test/write2/p_opt2.txt')"))
+ eq(0, eval("filereadable('test/write3/p_opt3.txt')"))
+ command('file test/write2/p_opt.txt')
+ command('set modified')
+ command('sp test/write2/p_opt2.txt')
+ command('set modified')
+ command('sp test/write3/p_opt3.txt')
+ -- don't set p_opt3.txt modified - assert it isn't written
+ -- and that write3/ isn't created
+ command('wall ++p')
+ eq(1, eval("filereadable('test/write2/p_opt.txt')"))
+ eq(1, eval("filereadable('test/write2/p_opt2.txt')"))
+ eq(0, eval("filereadable('test/write3/p_opt3.txt')"))
+
eq('Vim(write):E32: No file name', pcall_err(command, 'write ++p test_write/'))
if not is_os('win') then
eq(