10

When I want to edit my zshrc, I just type:

vim .zshrc

Without specifying the full path (~/.zshrc), if I am in a different directory, this will instead start a new file. This is very annoying, since I only have one .zshrc, in ~, and only one vimrc, in ~/.vim/. So, how do I get vim to open ~/.zshrc or ~/.vim/vimrc when I do vim .zshrc or vim vimrc in some other directory?


While creating some configuration for each individual file is one way, I was thinking of something along the lines of CDPATH. CDPATH is a variable in some shells that contains a list of paths where cd will look for a directory. For example, if I had CDPATH=:/home/muru, and I were in any directory that doesn't contain a directory named Desktop, I could do cd Desktop and reach /home/muru/Desktop. Simple, elegant, flexible. If vim had a VIMPATH where it would look for files to edit, that would be the best option.

0

5 Answers 5

6

Vim's 'path' option allows you to specify directories which commands like gf and :find will search for a file.

If you only want this functionality to trigger for a specific set of files, then you could use an autocmd to automatically "redirect" your :edit command to the file in one of the 'path' directories.

set path+=~/
function! FindInPath(name)
    let found = findfile(a:name)
    if !empty(found)
        exe 'silent keepalt file '. fnameescape(found)
        edit
    endif
endfunction
autocmd BufNewFile .vimrc,.zshrc nested call FindInPath(expand('<afile>'))

This uses the BufNewFile autocmd as a trigger for file not found, so try to find it somewhere else. When that situation is detected, use findfile() to try to find the file in the 'path' directories. If it is found, change the name of the current buffer to that file and re-edit the buffer, otherwise just continue using the new buffer.

The nested qualifier is required here since autocmds don't normally nest. In this case, you do want the typical autocmds to trigger when the :edit command opens your file.

Note that this will still create an extra buffer as compared to just editing the file manually. By the time BufNewFile is run, the buffer for the originally specified file name is already created. Using :file to change the name of a buffer creates a new, unloaded buffer with the original name.

If you always want to search in 'path', then the autocmd can simply be changed to use the * file pattern rather than specifying certain files.


Here's an updated version which should match your requirements better. It uses :find to directly open the file instead of setting the buffer name based on the result of findfile().

function! FindInPath(name)
    let path=&path
    " Add any extra directories to the normal search path
    set path+=~,~/.vim,/etc
    " If :find finds a file, then wipeout the buffer that was created for the "new" file
    setlocal bufhidden=wipe
    exe 'silent! keepalt find '. fnameescape(a:name)
    " Restore 'path' and 'bufhidden' to their normal values
    let &path=path
    set bufhidden<
endfunction
autocmd BufNewFile * nested call FindInPath(expand('<afile>'))

This solves the problem in the previous function where Vim would complain when trying to save the :file-named buffer.

2
  • Hmm. keepalt file has the same drawback as assigning to vim.current.buffer.name - vim warns you that the file already exists when try to save your changes.
    – muru
    Commented Feb 26, 2015 at 11:29
  • After using this for nearly a year, I have some suggestions: wrap it in if expand('%') !~ '^[.~]\?/' ... endif so that explicilty specified relative or absolute paths get skipped; and use silent instead of silent!, because if you open vim vimrc in two terminals, the second will wait for input to the usual "file is open elsewhere" warning, but the user has no idea what it's waiting for. I'm not editing it yet, to see if you have better ways to tackle either.
    – muru
    Commented Jan 24, 2016 at 1:51
3

Alternative is to make some commands that will make this easier:

command! Evimrc e ~/.vimrc
command! Svimrc sp ~/.vimrc

I am using the E/S pattern that rails.vim/projectionist.vim uses.

For more help see:

:h :command
:h :e
:h :sp
3

This is not a solution, but it is what I do in order to have easy access to .vimrc and .zshrc. Personally I think this is pretty neat:

map <leader>ev :e ~/.vim/vimrc<cr>
map <leader>ez :e ~/.zshrc<cr>
1

In the spirit of CDPATH, I wrote a Python-based function to do this (with help from Matt Boehm):

function LookupFiles ()
    python <<EOF
from os.path import *
from vim import *
current_file = eval ('expand("%")')
current_index = str (current.buffer.number)
PATHS = ['~', '~/.vim', '/etc']

if current_file != '' and  not isfile (current_file):
    for p in map (expanduser, PATHS):
        f = join (p, current_file)
        if isfile (f):          
            command ('bad ' + f)
            command ('bd ' + current_index)
            break
EOF
endfunction

autocmd BufWinEnter * nested call LookupFiles() 
  • I used BufWinEnter instead of BufNewFile because the latter didn't seem to behave reliably when multiple such filenames where given.
  • After a bit of trial and error, I settled on bad followed by bd as a reliable way to close the buffer opened for the unused file. This still doesn't work when I do :tabe some-file (no new tab is opened). However, that's a question for another day.
  • Like with CDPATH, I can easily add more directories by editing PATHS, or making it a Vim-level variable and modifying it.

What the other answers miss, IMO, is that I don't want to create configuration for each file, but for each directory. I don't care whether I'm opening fstab or vimrc, I just wanted Vim to search for files in some set of directories if it didn't exist in the current directory. +1 to all of them for the file-specific efforts, though.

2
  • Simply changing my answer to use * for the file pattern, rather than specific files, should achieve the same thing and avoids your problem with :tabe.
    – jamessan
    Commented Feb 25, 2015 at 16:13
  • @jamessan I admit I saw the original autocmd and zoned out. On an unrelated note: the fnameescape might be the solution to another question here, about gvim and remote send.
    – muru
    Commented Feb 25, 2015 at 16:28
1

With 'user-commands' and 'Dictionary', we can edit the files without creating a buffer for overriding the file which we want to edit on the buffer.

com -nargs=* E call Editfile(<q-args>)
let s:fileLocation = {'vimrc':'~/.vim/', '.zshrc':'~/'}
function Editfile(filename)
    if has_key(s:fileLocation, a:filename)
        exe "e " . fnameescape(s:fileLocation[a:filename].a:filename)
    else
        exe "e " . fnameescape(a:filename)
    endif
endfunction 

Note that the function Editfile does not try to verify its argument. Let's have vim handle all errors like other normal Ex-commands. If the argument is correct then vim starts to edit the file, and if not correct then vim will report errors.

In my opinion, we need not to create a buffer. It has two main drawbacks.

  1. The 'not-edited' flag would be set. As jamessan points out in his answer, if we set the current file name with :filecommand and try to write the file, we will meet the error message E13: File exists (use ! to override) This is because vim marks the file as "not edited" when we change the name of the file with :file command.
  2. We have to try to keep the alternate file name with :keepalt command. When we try to edit vimrc while editing 'foo.bar', we expect to the alternate file name should be 'foo.bar'. But if we creates a buffer and change the name, the alternate file name becomes not the "foo.bar" but the buffer's old name 'vimrc'. Even worse, the old buffer will be left as unlisted-buffer or inactive-buffer. That's why we should use :keepalt command in order to keep the alternate file name as we expect and set the bufhidden option to be wipe to wipe out the old buffer.

If the capital letter in the command name (all user-defined commands should start with capital letter) does not concern us, it would be better to use it. Because it does not create a buffer, we are free of burden for managing the buffer. Just pass the file name to vim and see how vim handles it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.