If I invoke vim foo/bar/somefile
but foo/bar
don't already exist, Vim refuses to save.
I know I could switch to a shell or do :!mkdir foo/bar
from Vim but I'm lazy :)
Is there a way to make Vim do that automatically when it saves the buffer?
If I invoke vim foo/bar/somefile
but foo/bar
don't already exist, Vim refuses to save.
I know I could switch to a shell or do :!mkdir foo/bar
from Vim but I'm lazy :)
Is there a way to make Vim do that automatically when it saves the buffer?
augroup BWCCreateDir
autocmd!
autocmd BufWritePre * if expand("<afile>")!~#'^\w\+:/' && !isdirectory(expand("%:h")) | execute "silent! !mkdir -p ".shellescape(expand('%:h'), 1) | redraw! | endif
augroup END
Note the conditions: expand("<afile>")!~#'^\w\+:/'
will prevent vim from creating directories for files like ftp://*
and !isdirectory
will prevent expensive mkdir call.
Update: sligtly better solution that also checks for non-empty buftype and uses mkdir()
:
function s:MkNonExDir(file, buf)
if empty(getbufvar(a:buf, '&buftype')) && a:file!~#'\v^\w+\:\/'
let dir=fnamemodify(a:file, ':h')
if !isdirectory(dir)
call mkdir(dir, 'p')
endif
endif
endfunction
augroup BWCCreateDir
autocmd!
autocmd BufWritePre * :call s:MkNonExDir(expand('<afile>'), +expand('<abuf>'))
augroup END
call mkdir(expand('%:h'), 'p')
might be more portable. –
Tune mkdir('foo/', 'p')
where it creates the directory and then errors out that it failed to create it: groups.google.com/d/topic/vim_dev/rFT67RzKMfU/discussion. Make sure you strip any trailing slashes before passing to mkdir(). –
Training fnamemodify(, ':h')
: :h
is “Head of the file name (the last component and any separators removed).” The only exception is /
(root), but I doubt that isdirectory('/')
may return anything but 1. –
Gough editorconfig-vim
bug tracker. I do not suggest using plugins like Vundle without actually understanding what they do: /Users/cnewton/.vim/bundle/…
is clearly a package manager path where …
gives you enough information about the plugin in which error occurred. It looks like they have forgot to remove a debugging print
or to add as e
. –
Gough function!
instead of function
so that your .vimrc file can be reloaded. I'd edit the answer to add it, but I don't have enough privileges on SO yet. –
Infliction Based on the suggestions to my question, here's what I ended up with:
function WriteCreatingDirs()
execute ':silent !mkdir -p %:h'
write
endfunction
command W call WriteCreatingDirs()
This defines the :W
command. Ideally, I'd like to have all of :w!
, :wq
, :wq!
, :wall
etc work the same, but I'm not sure if it's possible without basically reimplementing them all with custom functions.
:W
, my screen becomes almost blank. I'll try and remove my previous options and give feedback. –
Brazilin write
to execute ':write'
fixed it for me. –
Kienan execute
line with the following two to fix all issues: let mkdircommand="mkdir -p '" . expand("%:h") . "'"
then execute 'call system(mkdircommand)'
. This way the screen also doesn't flash to execute the mkdir command. And it should also work with directories with spaces –
Sculley This code will prompt you to create the directory with :w
, or just do it with :w!
:
augroup vimrc-auto-mkdir
autocmd!
autocmd BufWritePre * call s:auto_mkdir(expand('<afile>:p:h'), v:cmdbang)
function! s:auto_mkdir(dir, force)
if !isdirectory(a:dir)
\ && (a:force
\ || input("'" . a:dir . "' does not exist. Create? [y/N]") =~? '^y\%[es]$')
call mkdir(iconv(a:dir, &encoding, &termencoding), 'p')
endif
endfunction
augroup END
I added this to my ~/.vimrc
cnoremap mk. !mkdir -p <c-r>=expand("%:h")<cr>/
If I need to create the directory I'm in I type :mk.
and it replaces that with "!mkdir -p /path/to/my/file/" and allows me to review the command before I invoke it.
I am failing to see why everyone tries complicated functions. This is enough to create parent folders
:!mkdir -p %:p:h
mkdir -p
is the shell command to create folders with parents%:p:h
is the folder path with
%
: path given when vim starts: vim foo/bar/file.ext
:p
: gives full path: /home/user/foo/bar/file.ext
:h
: removes file name from final string: /home/user/foo/bar
%:h
also works and gives relative path: foo/bar
I made :saveas!
create the directory if missing: https://github.com/henrik/dotfiles/commit/54cc9474b345332cf54cf25b51ddb8a9bd00a0bb
I think I managed to do this in three lines, combining what others are saying on this answer.
This seems to do the trick:
if has("autocmd")
autocmd BufWritePre * :silent !mkdir -p %:p:h
end
It attempts to create the folder automatically when saving a buffer. If anything bad happens (i.e. permission issues) it will just shut up and let the file write fail.
If anyone sees any obvious flaws, please post a comment. I'm not very versed in vimscript.
EDIT: Notes thanks to ZyX
%
in such scripts. Vim is not going to escape any special symbols: for example, if you are editing a file named /mnt/windows/Documents and Settings/User/_vimrc
you will end up having four new directories: /mnt/windows/Documents
, ./and
, ./Settings
and ./Settings/User
. And, by the way, you don’t need :execute
here. –
Gough system()
function for completely silent shell calls, but you don’t need both :execute
and %:p:h
: :silent !mkdir -p %:p:h
works exactly as what you have wrote (though you may need :redraw!
at the end, in this case :execute
comes handy), but it is better to use call system('mkdir -p '.shellescape(expand('%:p:h')))
. Do use :execute '!command' shellescape(arg, 1)
(with the second argument to shellescape) if you have to use bangs instead of system()
. Do use bangs if escaped argument contains newlines. –
Gough :source ~/.vimrc
) (this is what augroup
and autocmd!
are for), scrapped view after launching shell commands (that is what redraw!
is for), creating garbage directories in case of using pseudo-files (in first code snipped it is checked by only matching filename against a pattern, but in second one I also check &buftype
) and useless shell call in case directory exists (isdirectory()
condition). –
Gough %
expansion to ever suggest using it to anybody. Pseudo files are used in a big bunch of plugins (e.g. fugitive or my aurum), thus they are worth caring about. Resourcing vimrc is also a common practice. You can have whatever you want in the vimrc, just do not suggest it as an answer. Using :silent! call mkdir(expand('%:p:h'), 'p')
variant solves two of the points I mentioned and third I did not mention: !mkdir
is not going to work on windows. –
Gough redraw!
will make difference if you are using terminal vim, it is blanking the screen otherwise. OP almost definitely has terminal vim: command in the question is vim …
. –
Gough You can install a plugin for this, such as https://github.com/DataWraith/auto_mkdir
To use vim's built-in plugin management, clone it like so:
git clone https://github.com/DataWraith/auto_mkdir ~/.vim/pack/vendor/start/auto_mkdir
and it'll be active the next time you open vim.
© 2022 - 2024 — McMap. All rights reserved.
mkdir -p %:h
is better because it works for nested non-existing paths, doesn’t raise an error when the path already exists, and%:h
is the full path of the current file. However, I don’t know how to invoke this automatically. Normally, this is done with automcommands but theBufWritePre
event doesn’t seem to work here. – Tympanistwrite
and calls the system tomkdir -p
ondirname
otherwise, map it toW
... I'm too lazy to search for the syntax and to post it as an answer... Sorry – Ballistics:w
tomkdir -p %:h
followed by the builting:write
– Deceptive