Como faço para o vim procurar em um conjunto de diretórios um arquivo, se ele não existe no diretório atual?

9

Quando quero editar o meu zshrc, basta digitar:

vim .zshrc

Sem especificar o caminho completo ( ~/.zshrc), se eu estiver em um diretório diferente, isso iniciará um novo arquivo. Isso é muito chato, pois eu só tenho um .zshrc, em ~, e apenas um vimrc, em ~/.vim/. Então, como faço para abrir o vim ~/.zshrcou ~/.vim/vimrcquando o faço vim .zshrcou vim vimrcem algum outro diretório?


Embora a criação de alguma configuração para cada arquivo individual seja uma maneira, eu estava pensando em algo parecido com CDPATH. CDPATHé uma variável em alguns shells que contém uma lista de caminhos onde cdprocurará um diretório. Por exemplo, se eu tivesse CDPATH=:/home/murue estivesse em qualquer diretório que não contenha um diretório chamado Desktop, eu poderia fazer cd Desktope alcançar /home/muru/Desktop. Simples, elegante, flexível. Se vimtivesse VIMPATHonde procurar arquivos para editar, seria a melhor opção.

muru
fonte

Respostas:

6

A 'path'opção do Vim permite que você especifique diretórios de comandos como gfe :findprocurará por um arquivo.

Se você deseja que essa funcionalidade seja acionada apenas para um conjunto específico de arquivos, use um autocmd para "redirecionar" automaticamente seu :editcomando para o arquivo em um dos 'path'diretórios.

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

Isso usa o BufNewFileautocmd como um gatilho para file not found, so try to find it somewhere else. Quando essa situação for detectada, use findfile()para tentar encontrar o arquivo nos 'path'diretórios. Se for encontrado, altere o nome do buffer atual para esse arquivo e edite-o novamente; caso contrário, continue usando o novo buffer.

O nestedqualificador é necessário aqui, pois os autocmds normalmente não são aninhados. Nesse caso, você deseja que os autocmds típicos sejam acionados quando o :editcomando abrir seu arquivo.

Observe que isso ainda criará um buffer extra em comparação com apenas editar o arquivo manualmente. Quando o tempo BufNewFileé executado, o buffer para o nome do arquivo originalmente especificado já está criado. Usar :filepara alterar o nome de um buffer cria um novo buffer descarregado com o nome original.

Se você sempre deseja pesquisar 'path', o autocmd pode simplesmente ser alterado para usar o *padrão de arquivo em vez de especificar determinados arquivos.


Aqui está uma versão atualizada que deve atender melhor aos seus requisitos. Ele é usado :findpara abrir diretamente o arquivo em vez de definir o nome do buffer com base no 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>'))

Isso resolve o problema na função anterior, na qual o Vim reclamava ao tentar salvar o :filebuffer nomeado.

jamessan
fonte
Hmm. keepalt filetem a mesma desvantagem que atribuir ao vim.current.buffer.name- vim avisa que o arquivo já existe ao tentar salvar suas alterações.
Muni
Depois de usá-lo por quase um ano, tenho algumas sugestões: envolva-o if expand('%') !~ '^[.~]\?/'... endifpara que os caminhos relativos ou absolutos especificados explicitamente sejam ignorados; e use em silentvez de silent!, porque se você abrir vim vimrcem dois terminais, o segundo aguardará a entrada do aviso "o arquivo está aberto em outro lugar" usual, mas o usuário não tem idéia do que está esperando. Não estou editando ainda, para ver se você tem maneiras melhores de lidar.
Muru
3

Alternativa é fazer alguns comandos que tornarão isso mais fácil:

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

Estou usando o padrão E/ Sque rails.vim / projectionist.vim usa.

Para obter mais ajuda, consulte:

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

Esta não é uma solução, mas é o que eu faço, a fim de ter acesso fácil a .vimrce .zshrc. Pessoalmente, acho isso bem legal:

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

No espírito de CDPATH, escrevi uma função baseada em Python para fazer isso (com a ajuda 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() 
  • Eu usei, em BufWinEntervez de, BufNewFileporque o último não parecia se comportar de maneira confiável quando vários nomes de arquivos foram dados.
  • Após algumas tentativas e erros, decidi badsegui-lo bdcomo uma maneira confiável de fechar o buffer aberto para o arquivo não utilizado. Isso ainda não funciona quando eu faço :tabe some-file(nenhuma nova guia é aberta). No entanto, essa é uma pergunta para outro dia.
  • Como com CDPATH, eu posso adicionar facilmente mais diretórios editando PATHSou tornando-o uma variável no nível do Vim e modificando-o.

O que as outras respostas perdem, IMO, é que não quero criar configurações para cada arquivo , mas para cada diretório . Não me importo se estou abrindo fstabou vimrc, só queria que o Vim procurasse arquivos em algum conjunto de diretórios, se não existisse no diretório atual. +1 a todos eles pelos esforços específicos do arquivo.

muru
fonte
Simplesmente alterar minha resposta para usar *no padrão de arquivo, em vez de arquivos específicos, deve obter a mesma coisa e evitar o seu problema :tabe.
21815 jamessan
@jamessan Admito que vi o autocmd original e zoneei. Em uma observação não relacionada: o fnameescape pode ser a solução para outra pergunta aqui, sobre gvim e envio remoto.
muru
1

Com 'user-commands'e 'Dictionary', podemos editar os arquivos sem criar um buffer para substituir o arquivo que queremos editar no 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 

Observe que a função Editfilenão tenta verificar seu argumento. Vamos fazer com que o vim lide com todos os erros como outros comandos Ex normais. Se o argumento estiver correto, o vim começará a editar o arquivo e, se não estiver correto, o vim reportará erros.

Na minha opinião, não precisamos criar um buffer. Tem duas desvantagens principais.

  1. O 'not-edited'sinalizador seria definido. Como jamessan aponta em sua resposta , se definirmos o nome do arquivo atual com o :filecomando e tentarmos escrever o arquivo, receberemos a mensagem de erro E13: File exists (use ! to override)Isso ocorre porque o vim marca o arquivo como "não editado" quando alteramos o nome do arquivo com :filecomando.
  2. Temos que tentar manter o nome do arquivo alternativo com o :keepaltcomando Quando tentamos editar o vimrc enquanto editamos 'foo.bar', esperamos que o nome alternativo do arquivo seja 'foo.bar'. Mas se criarmos um buffer e mudarmos o nome, o nome do arquivo alternativo se tornará não o "foo.bar", mas o nome antigo do buffer 'vimrc'. Pior ainda, o buffer antigo será deixado como unlisted-bufferou inactive-buffer. É por isso que devemos usar o :keepaltcomando para manter o nome do arquivo alternativo conforme o esperado e definir a bufhiddenopção wipepara eliminar o buffer antigo.

Se a letra maiúscula no nome do comando (todos os comandos definidos pelo usuário devem começar com letra maiúscula) não nos interessa, seria melhor usá-la. Como ele não cria um buffer, não temos ônus em gerenciar o buffer. Basta passar o nome do arquivo para o vim e ver como o vim lida com isso.

MS.Kim
fonte