Como faço para o Vim ler automaticamente um arquivo enquanto ele não tem foco?

29

Estou usando o VIm para todos os tipos de coisas (ou gVim nesse caso), incluindo o monitoramento da saída gravada em um arquivo; Eu uso autoreadpara fazer o Vim reler o arquivo, o que acontece sempre que alterno o foco do teclado para ele.

Existe alguma maneira de fazer o Vim atualizar o buffer, mesmo que eu não mude o foco do teclado? Eu tentei configurar, checktimemas parece não ter nenhum efeito enquanto o foco do teclado estiver em outro lugar.

A saída que estou monitorando está substituindo o arquivo de saída completamente; e eu não estou olhando para tail -fisso. Existem outras opções, como canalizar para uma nova instância todas as vezes, canalizar lessou algo assim, mas seria legal se isso pudesse ser feito com o VIm.

falstro
fonte
@Carpetsmoker e o CursorHoldevento? Se o Vim não tiver foco, ele deve ser acionado a cada n segundos.
muru 23/03/2015
1
@muru Não, CursorHoldé executado apenas uma vez, isso está explicitamente documentado como: "não dispara todos os ms de 'horário de atualização' se você deixar o Vim para fazer um café. :)"
Martin Tournoij
1
Eu removi o gvim tag novamente, espero que tudo bem ;-) A meu ver, esta pergunta é não realmente sobre gVim, como mudança de foco é apenas um evento em que cheques Vim se o arquivo for alterado (há um monte, veja minha resposta para detalhes). A maioria, se não todas, maneiras de melhorar isso funcionará bem no Vim e no gVim.
Martin Tournoij 25/03
@Carpetsmoker ah, fiquei com a impressão de que o gVim verificava regularmente enquanto tinha o foco do teclado (tornando-o uma pergunta do gVim), mas parece que eu estava enganado. Obrigado por esclarecer isso.
falstro

Respostas:

20

Atualização 25/06/2015 :

  • Eu descartei o método "shell", pois era muito disfuncional. Parou o modo de inserção e deixou os processos zumbis. Veja o histórico de revisões deste post e você realmente deseja vê-lo de qualquer maneira.
  • Eu criei um plugin com isso: auto_autoread.vim . No momento, é efetivamente o mesmo que o código abaixo, mas eu recomendo que você use o plug-in, pois é provável que ele receba atualizações.

O que autoreadfaz?

Para responder a essa pergunta, precisamos primeiro entender o que a autoreadopção faz e, mais importante, o que ela não faz.

Infelizmente :help 'autoread' , não há muitas informações sobre isso, apenas diz que "um arquivo foi detectado como alterado fora do Vim" . Como o Vim detecta que um arquivo foi alterado? Em determinadas ações, o Vim verifica a hora da modificação do arquivo.

Quando:

  • :checktime é usado;
  • um buffer é inserido;
  • :diffupdate é usado;
  • :e é emitido para um arquivo que já possui um buffer;
  • executando um comando externo com !;
  • retornando para o primeiro plano ( ^Z, fg, somente se o shell tem controle de trabalho);

para o gVim, isso também é feito quando:

  • fechar o menu "clique com o botão direito" (selecionando algo ou apenas fechando);
  • o foco é alterado (é isso que você já percebeu);
  • fechando a caixa de diálogo dos navegadores de arquivos exibida se você usar "arquivo -> abrir", "arquivo -> salvar como" no menu (assim como em outros locais).

Reuni essas informações da fonte Vim, localizando todas as chamadas para buf_check_timestamp(), check_timestamps()funções e locais em que need_check_timestampsestá definido TRUE.

Posso ter perdido alguns eventos, mas o principal a lembrar é que o Vim apenas verifica se o arquivo foi modificado em um conjunto muito limitado de circunstâncias . Certamente não "pesquisa" o arquivo em busca de alterações a cada n segundos, que é basicamente o que você procura .

Portanto, para o seu propósito, set autoreadnão é suficiente.

Usando Python

Isso programa um thread Python para ser executado em segundo plano; ele será executado a :checktimecada n segundos. Se autoreadestiver ativado , isso recarregará o buffer do disco, caso contrário, apenas avisará.

Isto requer que Vim tem +pythonou +python3em :version. Ele deve funcionar em todas as plataformas (incluindo Windows).

fun! AutoreadPython()
python << EOF
import time, vim
try: import thread
except ImportError: import _thread as thread # Py3

def autoread():
    vim.command('checktime')  # Run the 'checktime' command
    vim.command('redraw')     # Actually update the display

def autoread_loop():
    while True:
        time.sleep(1)
        autoread()

thread.start_new_thread(autoread_loop, ())
EOF
endfun

Você pode iniciar isso usando :call AutoreadPython(); é claro que você pode fazer isso em um autocmd; por exemplo:

autocmd *.c call AutoreadPython()

Posfácio

Na verdade, existem mais métodos, por exemplo, você pode usar uma ferramenta como entro Python inotifyou o gaminmódulo para monitorar um arquivo em busca de alterações, :checktimetambém verifica todos os buffers se não houver argumentos, isso pode ser aprimorado apenas com a verificação de um único buffer ou um determinado arquivo.
No entanto, essa resposta já é bastante longa :-) Esse método deve (espero!) Funcionar bem para a maioria dos cenários ou deve ser facilmente adaptável ao seu cenário.

PS. Também tentei usar o Ruby, mas infelizmente os threads do Ruby (usando Thread) não são executados em segundo plano como o Python, então não pude fazer isso funcionar (talvez haja outra maneira?)

Martin Tournoij
fonte
2

Eu fiz um pequeno plugin vim-autoread que usa tail -fpara atualizar o buffer em segundo plano de forma assíncrona. Obviamente, isso precisa do recurso de trabalho do Vim Versão 8.

Use :AutoReadpara ligá-lo e :AutoRead!desligá-lo.

Christian Brabandt
fonte
Uma versão compatível com NeoVim seria ótima!
Andrew
@AndrewMacFie Não vejo, por que isso não funcionaria no Neovim.
Christian Brabandt
a falta de suporte ao Vim 8 no NeoVim, pensei?
Andrew
@AndrewMacFie oh isso provavelmente é verdade. Como não uso o neovim, não posso torná-lo compatível com o NeoVim. No entanto, um PR seria bem-vinda :)
Christian Brabandt
bastante justo:)
Andrew
1

No Neovim, você pode usar o seguinte comando, que abrirá um buffer de terminal executando o comando tail.

:enew | call termopen('tail --follow=name --retry /path/to/file.log')

Isso continuará funcionando mesmo se o arquivo for removido e recriado.

Ou adicione o seguinte ao seu $MYVIMRC

" :Tail /path/to/file
function! Tail(file)
  enew
  call termopen('tail --follow=name --retry ' . fnameescape(a:file))
endfunction
command! -nargs=1 Tail call Tail(<f-args>)
Brett Y
fonte
1

desta resposta (consultando uma resposta de PhanHaiQuang e um comentário de flukus )

Pode-se executar este oneliner a partir de ex (dentro do vim) quando necessário (ou colocar cada comando no vimrc, para quando os arquivos de log forem abertos.)

:set autoread | au CursorHold * checktime | call feedkeys("lh")

Explicação:
- autoread : lê o arquivo quando alterado do lado de fora (mas não funciona por si só, não há cronômetro interno ou algo assim. Ele só lerá o arquivo quando o vim executar uma ação, como um comando ex :!
- CursorHold * checktime : quando o cursor não é movido pelo usuário pelo tempo especificado em 'updatetime' (que é de 4000 milissegundos por padrão), o checktime é executado, que verifica alterações fora do arquivo
- chame feedkeys ("lh") : o cursor é movido uma vez, direita e volta à esquerda ... e então nada acontece (... o que significa que o CursorHold é acionado, o que significa que temos um loop )

eli
fonte