¿Cómo hago para que vim busque un archivo en un conjunto de directorios, si no existe en el directorio actual?

9

Cuando quiero editar mi zshrc, simplemente escribo:

vim .zshrc

Sin especificar la ruta completa ( ~/.zshrc), si estoy en un directorio diferente, esto iniciará un nuevo archivo. Esto es muy molesto, ya que solo tengo uno .zshrc, adentro ~, y solo uno vimrc, adentro ~/.vim/. Entonces, ¿cómo puedo abrir vim ~/.zshrco ~/.vim/vimrccuando lo hago vim .zshrco vim vimrcen algún otro directorio?


Si bien crear una configuración para cada archivo individual es una forma, estaba pensando en algo similar a CDPATH. CDPATHes una variable en algunos shells que contiene una lista de rutas donde cdbuscará un directorio. Por ejemplo, si lo hubiera hecho CDPATH=:/home/muru, y estuviera en cualquier directorio que no contenga un directorio llamado Desktop, podría hacerlo cd Desktopy llegar /home/muru/Desktop. Simple, elegante, flexible. Si vimtuviera un lugar VIMPATHdonde buscaría archivos para editar, esa sería la mejor opción.

muru
fuente

Respuestas:

6

La 'path'opción de Vim le permite especificar directorios a los que les gustan los comandos gfy :findbuscará un archivo.

Si solo desea que esta funcionalidad se active para un conjunto específico de archivos, puede usar un autocmd para "redirigir" automáticamente su :editcomando al archivo en uno de los 'path'directorios.

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>'))

Esto utiliza el BufNewFileautocmd como desencadenante para file not found, so try to find it somewhere else. Cuando se detecta esa situación, utilice findfile()para intentar encontrar el archivo en los 'path'directorios. Si se encuentra, cambie el nombre del búfer actual a ese archivo y vuelva a editar el búfer, de lo contrario, simplemente continúe usando el nuevo búfer.

El nestedse requiere calificador aquí desde autocmds no lo hacen normalmente nido. En este caso, desea que los autocmds típicos se activen cuando el :editcomando abra su archivo.

Tenga en cuenta que esto seguirá creando un búfer adicional en comparación con solo editar el archivo manualmente. Para cuando BufNewFilese ejecuta, el búfer para el nombre de archivo especificado originalmente ya está creado. El uso :filepara cambiar el nombre de un búfer crea un nuevo búfer descargado con el nombre original.

Si siempre desea buscar 'path', entonces el autocmd simplemente se puede cambiar para usar el *patrón de archivo en lugar de especificar ciertos archivos.


Aquí hay una versión actualizada que debe cumplir mejor con sus requisitos. Se utiliza :findpara abrir directamente el archivo en lugar de establecer el nombre del búfer en función del resultado de 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>'))

Esto resuelve el problema en la función anterior donde Vim se quejaría al intentar guardar el :filebúfer con nombre.

jamessan
fuente
Hmm keepalt filetiene el mismo inconveniente que asignar a vim.current.buffer.name- vim le advierte que el archivo ya existe cuando intenta guardar sus cambios.
muru
Después de usar esto durante casi un año, tengo algunas sugerencias: envuélvalas en if expand('%') !~ '^[.~]\?/'... endifpara que se omitan las rutas relativas o absolutas especificadas; y use en silentlugar de silent!, porque si abre vim vimrcen dos terminales, el segundo esperará la entrada a la advertencia habitual de "el archivo está abierto en otro lugar", pero el usuario no tiene idea de lo que está esperando. Todavía no lo estoy editando, para ver si tienes mejores formas de abordarlo.
Muru
3

La alternativa es hacer algunos comandos que lo harán más fácil:

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

Estoy usando el patrón E/ Sque rails.vim / projectionist.vim usa.

Para más ayuda ver:

:h :command
:h :e
:h :sp
Peter Rincker
fuente
3

Esto no es una solución, pero es lo que hago para tener fácil acceso a .vimrcy .zshrc. Personalmente, creo que esto es bastante bueno:

map <leader>ev :e ~/.vim/vimrc<cr>
map <leader>ez :e ~/.zshrc<cr>
Karl Yngve Lervåg
fuente
1

En el espíritu de CDPATH, escribí una función basada en Python para hacer esto (con la ayuda de 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() 
  • Lo usé en BufWinEnterlugar de BufNewFileporque este último no parecía comportarse de manera confiable cuando se daban múltiples nombres de archivo.
  • Después de un poco de prueba y error, decidí badseguirlo bdcomo una forma confiable de cerrar el búfer abierto para el archivo no utilizado. Esto todavía no funciona cuando lo hago :tabe some-file(no se abre una pestaña nueva). Sin embargo, esa es una pregunta para otro día.
  • Al igual que con CDPATH, puedo agregar fácilmente más directorios editando PATHSo convirtiéndolo en una variable de nivel Vim y modificándolo.

Lo que las otras respuestas pierden, IMO, es que no quiero crear la configuración para cada archivo , sino para cada directorio . No me importa si estoy abriendo fstabo vimrcsimplemente quería que Vim buscara archivos en algún conjunto de directorios si no existía en el directorio actual. Sin embargo, hace +1 a todos por los esfuerzos específicos del archivo.

muru
fuente
Simplemente cambiar mi respuesta para usar *para el patrón de archivo, en lugar de archivos específicos, debería lograr lo mismo y evitar su problema :tabe.
jamessan
@jamessan Admito que vi el autocmd original y me separé. En una nota no relacionada: fnameescape podría ser la solución a otra pregunta aquí, sobre gvim y envío remoto.
muru
1

Con 'user-commands'y 'Dictionary', podemos editar los archivos sin crear un búfer para anular el archivo que queremos editar en el búfer.

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 

Tenga en cuenta que la función Editfileno intenta verificar su argumento. Hagamos que vim maneje todos los errores como otros comandos Ex normales. Si el argumento es correcto, vim comienza a editar el archivo y, si no es correcto, vim informará de errores.

En mi opinión, no necesitamos crear un buffer. Tiene dos inconvenientes principales.

  1. Se establecería la 'not-edited'bandera. Como señala James en su respuesta , si configuramos el nombre del archivo actual con el :filecomando e intentamos escribir el archivo, nos encontraremos con el mensaje de error E13: File exists (use ! to override)Esto se debe a que vim marca el archivo como "no editado" cuando cambiamos el nombre del archivo con :filemando
  2. Tenemos que tratar de mantener el nombre de archivo alternativo con el :keepaltcomando. Cuando intentamos editar vimrc mientras editamos 'foo.bar', esperamos que el nombre de archivo alternativo sea 'foo.bar'. Pero si creamos un búfer y cambiamos el nombre, el nombre de archivo alternativo no se convierte en "foo.bar" sino en el antiguo nombre del búfer 'vimrc'. Peor aún, el viejo búfer se dejará como unlisted-buffero inactive-buffer. Es por eso que debemos usar el :keepaltcomando para mantener el nombre de archivo alternativo como esperamos y establecer la bufhiddenopción wipepara eliminar el búfer anterior.

Si la letra mayúscula en el nombre del comando (todos los comandos definidos por el usuario deben comenzar con la letra mayúscula) no nos concierne, sería mejor usarla. Debido a que no crea un búfer, estamos libres de carga para administrar el búfer. Simplemente pase el nombre del archivo a vim y vea cómo vim lo maneja.

MS.Kim
fuente