Change Vim swap/backup/undo file name - vim

Is it possible to change the way Vim names its swap/backup/undo files?
To avoid clutter, I've set options in my ~/.vimrc to dump these files in ~/.vim/tmp/{swap,backup,undo}; however, as I routinely edit files in different directories with the same name, I often end up with lots of otherwise indistinguishable files and Vim sometimes has trouble recovering.
Ideally, I'd like to use the naming scheme that the persistent undo has (%path%to%file.undo) for all these auxiliary files; there's no obvious way to set it, but can it be done with Buf{Read,Write} macros?

I have this in my .vimrc and it names the swap files with full path names and percent signs just as you describe:
" Store swap files in fixed location, not current directory.
set dir=~/.vimswap//,/var/tmp//,/tmp//,.
The key is the // at the end of the directories. See this note from :help dir:
For Unix and Win32, if a directory ends in two path separators "//"
or "\\", the swap file name will be built from the complete path to
the file with all path separators substituted to percent '%' signs.
This will ensure file name uniqueness in the preserve directory.

Here's part of my .vimrc from github.
This sets the undodir (and turns it on), sets the backupdir, and directory (used for .swp files). Note that it creates the directories if they don't already exist.
" Save your backup files to a less annoying place than the current directory.
" If you have .vim-backup in the current directory, it'll use that.
" Otherwise it saves it to ~/.vim/backup or .
if isdirectory($HOME . '/.vim/backup') == 0
:silent !mkdir -p ~/.vim/backup >/dev/null 2>&1
endif
set backupdir-=.
set backupdir+=.
set backupdir-=~/
set backupdir^=~/.vim/backup/
set backupdir^=./.vim-backup/
set backup
" Save your swap files to a less annoying place than the current directory.
" If you have .vim-swap in the current directory, it'll use that.
" Otherwise it saves it to ~/.vim/swap, ~/tmp or .
if isdirectory($HOME . '/.vim/swap') == 0
:silent !mkdir -p ~/.vim/swap >/dev/null 2>&1
endif
set directory=./.vim-swap//
set directory+=~/.vim/swap//
set directory+=~/tmp//
set directory+=.
" viminfo stores the the state of your previous editing session
set viminfo+=n~/.vim/viminfo
if exists("+undofile")
" undofile - This allows you to use undos after exiting and restarting
" This, like swap and backup files, uses .vim-undo first, then ~/.vim/undo
" :help undo-persistence
" This is only present in 7.3+
if isdirectory($HOME . '/.vim/undo') == 0
:silent !mkdir -p ~/.vim/undo > /dev/null 2>&1
endif
set undodir=./.vim-undo//
set undodir+=~/.vim/undo//
set undofile
endif
Hopefully, it's commented well enough to understand what's going on. If not, add a comment and I'll fix it.
Ciao!
Update [07/16/2012]
I got an email from Rob Kine asking these questions about the backupdir section that I wanted to answer for everyone:
It looks like you are removing the current directory, and then re-adding it. what does that do?
What does the ^= operator do?
How does the order of precedence in the use of folders checked work in Vim? (Like is the last folder added the first one it checks for?)
The first thing is to describe the different operators. These operators have different meanings for non-string-list options, so be warned!
-= removes the value from a string list;
+= appends the value to a string list;
^= prepends the value to a string list.
So the backupdir has the following operations applied:
Remove the current directory from the list.
Append the current directory to the list (this ensures it is the last thing checked).
Remove the home directory from the list (I don't like stuff being saved there).
Prepend ~/.vim/backup/.
Prepend ~/.vim-backup/.
When Vim looks for where to save the backups, it checks from first to last; so it'll check for ~/.vim-backup/, then check for ~/.vim/backup, then check the default list (except for . and ~/ which were removed), and finally check .
You can get help for all these in Vim by using (for example) :help ^= or :help backupdir.

Create the directory undo
$ mkdir ~/.vimundo
Set up your .vimrc file
set undodir=~/.vimundo

Related

Cannot write to backup file

Since I have upgraded to Catalina, I cannot edit my dotfiles but get a message saying "Cannot write to backup file...".
I have checked the permissions on the file and my user account is the owner and has read and write permissions. If I edit the files with BBedit, I can save the files so the issue just seems to be editing within neovim.
I can also edit files using neovim in other directories but not within my dotfile directory.
Additional information added:
I have noticed that the files that I can't edit have the following permission structure:
.rwxrwxrwx# 1.4k pdd 30 Aug 2017 plugin_manager.vim
I have now removed the # attribute using xattr but that has not made any difference.
Additional Information Update
I have narrowed it down to something in my vimrc file. If I start neovim without my vimrc, I can save files in my vimrc directory.
I have the following code in my vimrc relating to backup files:
if exists('$SUDO_USER')
set nobackup " don't create root-owned files
set nowritebackup " don't create root-owned files
else
set backupdir=~/local/.config/nvim/tmp/backup
set backupdir+=~/.config/nvim/tmp/backup " keep backup files out of the way
set backupdir+=.
endif
If I comment these lines out, I can then save files in my vimrc directory. I am a little confused as I have had these in my vimrc for some time and have not had a problem.
I am probably missing something simple. Does anyone have any suggestions?
The original backup I used for vim was found here: https://vim.fandom.com/wiki/Incremental_backup_in_central_backup_directory created July 22, 2005 author Sylvain Lafleur
It doesn't work in neovim, but I was able to get it working:
I'm using KDE Neon. The original looks like it might work on windows, but I have not tested it.
A few things to note:
You will need to manually edit the g:backupdir and
g:this_root_backkup_dir (This may be reduced, but I didn't mess with it.)
I was unable to make $HOME work, so use /home/username on linux.
This is not my actual directory structure, but should suffice as an
example, in that /home/neon/vim existed before I used this code.
This is very redundant backup. Every time the file is saved it will
create a backup of the filename with date/time stamp.
In the .vimrc put your vim specific code in if !has('nvim')
and endif blocks. I put the following code in my init.vim file.
set backup
set backupcopy=yes
function Write_backup()
let g:backupdir='/home/neon/vim/vim_backups'
let &backupdir=g:backupdir
let g:backupext = strftime("_%Y-%m-%d_%H-%M-%S")
let &backupext=g:backupext
let g:this_root_backup_dir = '/home/neon/vim/vim_backups'
let g:this_dir = substitute(expand("%:p:h")," ","_","g")
let g:this_filename = substitute(expand("%")," ","_","g")
let g:this_drive = strpart(g:this_dir, 0, 1)
let g:this_backup_dir_drive = g:this_root_backup_dir . g:this_drive
let g:this_backup_dir = g:this_backup_dir_drive . strpart(g:this_dir, 1)
"--make DRIVE directory if it doesn't exist
if !filewritable(g:this_backup_dir_drive)
silent! execute expand('!mkdir -p ' . g:this_backup_dir_drive)
endif
"--make directory under DRIVE if it doesn't exist
if !filewritable(g:this_backup_dir)
silent! execute expand('!mkdir -p ' . g:this_backup_dir)
endif
"--set new backup dir
let g:backupdir = g:this_backup_dir
let &backupdir=g:backupdir
endfunction
call Write_backup()
inoremap <ESC> <ESC>:call Write_backup()<CR><C-l>
" note the <C-l> clears the output so remove if testing with echo

vimgrep as per vim's path variable

I usually work in multi location projects, where source files are distributed among different servers, databases, etc. What I isually do is to add these locations to vim's path, so, for instance, I can 'gf' into these files.
When firing a search to find a string using ":vimgrep", is it possible to somehow specify that the search is to be performed not at the current location but at all the levels specified by path?
My only options at the moment are
a) Create a folder with symlinks to the different source codes and start vim from there
b) Manually add the locations to the vimgrep command after the pattern expression
You could implement something like searchsavvy's ListGrep, but instead of doing vimgrepadd on each a:list, you do it recursively on each element in path. Roughly something like this:
...
call setqflist([])
for path in split(&path, ",")
" Recursively (**) grep each path and add it to quickfix.
exec 'silent! vimgrepadd/' . a:query . '/gj '. path .'/**'
endfor
Alternatively, you could use the :grep command (probably faster for large trees but needs work for paths with spaces):
function! GrepInPath(query)
exec ':grep '. a:query .' -R '. join(split(&path, ","), " ")
endf
(Untested.)
If I were you, I'd generate the list of all files every time I sync. You could use csearch and use notgrep and it would work with Unite's file_list source.

Automatically append current date to file name when saving in Vim

I'm using vim to take notes while reading academic articles. I prefer to have a new text file for each note I've taken, but organizing them becomes tedious.
What I would like to do is set an autocommand to detect if I'm in a certain directory, writing to a newfile and then appened the current date and time to whatever filename I write.
So if I have:
:pwd
/path/to/projects
When I type
:w Notes
I would like vim instead to save the file as
"Notes - <CURRENT DATE - TIME >.txt"
I believe it involves declaring something like the following in my vimrc:
autocmd BufNewFile,BufWrite "/path/to/projects/*" <command involving strftime("%Y-%m-%d_%H-%M")>
But I can't figure out what. Any Ideas? I'm using Vim 7.3 on Debian Linux.
You're very close. I think it's best to rename the file as it is created; messing with the file name during writes makes this more difficult (e.g. what if you re-open an existing note, or just write the buffer again?)
The :file command can be used to rename the current file; the current filename is in the % special identifier. Triggered when a new file is created, this does the job:
autocmd BufNewFile /path/to/projects/* execute 'file' fnameescape(expand('%') . strftime(" - %Y-%m-%d_%H-%M.txt"))
If you don't want to consider the original filename, this becomes even easier:
autocmd BufNewFile /path/to/projects/* execute 'file' fnameescape(strftime("Notes - %Y-%m-%d_%H-%M.txt"))
You may be looking for something along the lines of:
function! SaveWithTS(filename) range
execute "save '" . a:filename . strftime(" - %Y-%m-%d_%H-%M.txt'")
endfunction
command! -nargs=1 SWT call SaveWithTS( <q-args> )
With the above in your .vimrc, executing :SWT Note will save your file as Note - YYYY-MM-DD_HH-MM.txt. This has the disadvantage of not happening automatically, so you have to remember to use :SWT instead of :w the first time your write your file, but it does let you wait until you are ready to save to decide what your filename should be (i.e. you aren't stuck with Note every time).
Edit: The above version of SaveWithTS actually saves to the filename with single quotes around it (bad testing on my part). Below is a version that should fix that and also lets you specify an extension to your file (but will default to .txt)
function! SaveWithTS(filename) range
let l:extension = '.' . fnamemodify( a:filename, ':e' )
if len(l:extension) == 1
let l:extension = '.txt'
endif
let l:filename = escape( fnamemodify(a:filename, ':r') . strftime(" - %Y-%m-%d_%H-%M") . l:extension, ' ' )
execute "write " . l:filename
endfunction

Getting relative paths in Vim

Say I am running Vim and pwd returns
/home/rafid/myproject
And say I am currently editing the file
/home/rafid/myproject/website/editpage.php
Is there any command that returns this for me?
website/editpage.php
That is, the path of the file relative to the current folder.
Although expand('%') often works, there are rare occasions where it does not. But you can force Vim to always present the relative path by calling fnamemodify:
:echo fnamemodify(expand("%"), ":~:.")
From the manual:
:. Reduce file name to be relative to current directory, if
possible. File name is unmodified if it is not below the
current directory.
For maximum shortness, use ":~:.".
The :~ is optional. It will reduce the path relative to your home folder if possible (~/...). (Unfortunately that only works on your home; it won't turn /home/fred into ~fred if you aren't logged in as fred.)
As Adam pointed out the comments, this can be shortened to:
:echo expand("%:~:.")
Reference: :h expand<Tab> and :h fnamem<Tab>
If you are limited for space (e.g. using this in your statusline), and can manage with "fuzzy" information about where the file is located, then check out pathshorten() which compresses folder names down to one character:
:echo pathshorten('~/.vim/autoload/myfile.vim')
~/.v/a/myfile.vim
Reference: :h pathsh<Tab>
Another option would be to write a vim function. Here's my humble attempt:
function! Relpath(filename)
let cwd = getcwd()
let s = substitute(a:filename, l:cwd . "/" , "", "")
return s
endfunction
You call Relpath with any full path name, and it will strip the current directory name from its argument.
For example, try :echo Relpath(expand("%:p")) (the :p modifier asks Vim to return the full path). Obviously, this is not necessary in your case, since % by itself returns relative path. However, it might come in handy in other cases.
This works for me :
:echo expand("%")
if you use autocmd to always set the current directory of the buffer that you are working on ( cd %:p:h ) then you can just type :cd
Blockquote
This works for me :
:echo expand("%")
This is only working if you opened that file with a relative file:
for vi ./foo, expand("%") will be ./foo
but
for vi /tmp/foo expand("%") will be /tmp/foo
Yes, you can use
:args
This will give you the filename of the current file, for informational purposes.
A workaround can be :cd . which seems to re-evaluate the path relative-ness. I agree this is very annoying though.

How to source all vim files in directory

I have split my .vimrc into several files and placed them into ~/vimfiles/vimrc.d/.
Currently I source each file in that directory using exact name:
source ~/vimfiles/vimrc.d/file1.vim
source ~/vimfiles/vimrc.d/file2.vim
etc.
How to make a loop thourgh all files in that directory so i could only have to do such loop in my .vimrc:
for file in ~/vimfiles/vimrc.d/*.vim
source file
enfor
As mb14 has already said, if you put them in ~/.vim/plugin they will be sourced automatically. For information, however, if you want to source all of the files in your vimrc.d directory, you could do this (requires a relatively recent Vim):
for f in split(glob('~/vimfiles/vimrc.d/*.vim'), '\n')
exe 'source' f
endfor
You may also be interested in the autoload mechanism, described in :help 41.15: if you're defining a lot of functions, this can make start-up a bit quicker as the functions are only loaded the first time they're used.
You can just put your files in the plugins directory (~/.vim/plugin). They will be automatically loaded.
mb14 gave you the best answer. You want something automatically executed? Then use the standard organization: here the plugin/ subdirectory.
Otherwise, :runtime would have been your friend:
:runtime! vimrc.d/*.vim
:source barks when its parameter doesn't exist while :runtime silently source nothing.
:source can source only one file while :runtime! can source any number of files.
:source takes an absolute pathname, or a pathname relative to the current directory while :runtime takes a pathname relative to the 'runtimepath' option, which shouldn't be a problem as long as you respect vim conventions.
The example from DrAl did not work for me, this is how I achieved it:
for fpath in split(globpath('~/.vimrc.d/', '*.vim'), '\n')
exe 'source' fpath
endfor
The following snip is what I use within my ~/.vimrc file to source scripts within the ~/.vimrc.d/ directory and sub-directories...
for f in glob('$HOME/.vimrc.d/**/*.vim', 0, 1)
execute 'source' f
endfor
Check vim -c ':help glob' for details about additional glob arguments.
TLDR
glob({expr} [, {nosuf} [, {list} [, {alllinks}]]]) *glob()*
{nosuf} set to False allows 'suffixes' and 'wildignore' options to apply
{list} set to True causes glob to return a list that respects new-lines within file names

Resources