O vim pode monitorar mudanças em tempo real em um arquivo

104

Minha pergunta é semelhante a esta como monitorar um arquivo de texto em tempo real, mas quero fazer isso no vim. Eu sei que posso ler um arquivo de uso de arquivo aberto tail -f sample.xmle, quando um novo conteúdo é gravado no arquivo, ele também grava o novo conteúdo na minha tela. Posso fazer com que o vim preencha automaticamente os novos dados quando um arquivo for atualizado?

Patrick
fonte

Respostas:

101

Você pode fazer :set autoreadcom que o vim leia o arquivo quando ele for alterado. No entanto (dependendo da sua plataforma), você deve dar foco.

Da ajuda:

Quando for detectado que um arquivo foi alterado fora do Vim e não foi alterado dentro do Vim, leia-o automaticamente novamente. Quando o arquivo é excluído, isso não é feito.

Peter
fonte
5
Obrigado pela dica! Parece muito promissor, mas não funciona no meu sistema :( Estou usando Mac10.6.2 iTerm com vim versão 7.2.303 compilado com MacVim. Posso tentar algum comentário adicional?
Patrick
erm, isso é muito estranho. você está usando o macvim gui ou a versão terminal? funciona para mim com macvim gui baixado pré-compilado. (Preciso clicar no aplicativo para focalizá-lo, como mencionei.)
Peter
1
Estava testando no terminal, depois que usei o gvim (MacVim GUI) a função começou a funcionar! Porém, como você mencionou, preciso focar o gvim para atualizar o conteúdo. Você tem algum truque para atualizá-lo mesmo sem foco? Obrigado por sua ajuda novamente.
Patrick
3
Não sei uma solução para isso, infelizmente. Eu ficaria surpreso se o vim pudesse fazer isso sem a mudança de foco - exigiria que o vim sondasse o sistema de arquivos para ver quando ele muda. Acho que você precisa de um plugin para isso.
Pedro
2
@Peter Eu fiz esse plugin há um tempo. Veja também esta pergunta e minha resposta a ela . para obter mais detalhes sobre como autoreadfunciona e suas limitações.
Martin Tournoij
63

Não sei sobre automaticamente, mas você pode digitar:

:e!

para recarregar o arquivo

dimba
fonte
Embora essa abordagem não atualize automaticamente o conteúdo, ela exibe o conteúdo atualizado. Obrigado pela sua resposta!
Patrick
31

Coloque o seguinte em seu .vimrc:

"verifique uma vez após 4s de inatividade no modo normal
definir autoread                                                                                                                                                                                    
au CursorHold * checktime                                                                                                                                                                       
Phan Hai Quang
fonte
Ótimo! Funciona bem, mesmo com um aviso se você alterou o arquivo desde a última recarga.
zoujyjs
1
A primeira resposta não funcionou para mim, mas funciona! :-)
Ionică Bizău
4
O "a cada 4 segundos" não é verdade. Isso só será verificado uma vez após 4s de inatividade no modo normal. Portanto, se você não fizer nada em outro buffer por um longo tempo, ele não será atualizado, mas seria se você apenas movesse o cursor e esperasse 4s. Outra opção é chamar manualmente ": checktime" para atualizar (após configurar a leitura automática). Infelizmente, não parece haver nenhum tipo de suporte para polling no vim, então não há uma resposta verdadeira para a pergunta dos OPs.
David Ljung Madison Stellar
1
Acabei de encontrar isso. Se você alterar o checktime para chamar uma função personalizada e adicionar "call feedkeys (" lh ")" ao final da função, ela será acionada a cada 4 segundos.
flukus
@DavidLjungMadison você está certo, vou editar a postagem para evitar esse erro
Phan Hai Quang
9

como @flukus disse em um comentário sobre uma resposta anterior, você pode call feedkeys["lh"](move o cursor para a direita e para a esquerda, o que normalmente não faz mal ao visualizar um arquivo de log)

Então, se você combinar o resto da resposta, você terá um oneliner que pode executar de ex (dentro do vim) quando necessário:

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


(se você quiser pular (quase) para o final do arquivo, basta usar "G" em vez de "lh" com feedkeys)

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

Além disso, você pode :set syntax=logtalkcolorir o log

Para parar a rolagem ao usar call feedkeys("G"), execute :set noautoread- agora o vim dirá que o arquivo foi alterado e perguntará se alguém deseja ler as alterações ou não)

(Isso tem algum efeito colateral?)

Edit: eu vejo um efeito colateral: se alguém usa "G" es a chave de alimentação, ele vai rolar para baixo todos os buffers abertos atualmente ?! Portanto, não é possível trabalhar no buffer esquerdo de uma janela de divisão enquanto o buffer direito rola para baixo um arquivo de log automaticamente

eli
fonte
4

Cole isso em seu .vimrc e ele deve funcionar como um chefe. (Retirado de: http://vim.wikia.com/wiki/Have_Vim_check_automatically_if_the_file_has_changed_externally )

" Function to Watch for changes if buffer changed on disk
function! WatchForChanges(bufname, ...)
  " Figure out which options are in effect
  if a:bufname == '*'
    let id = 'WatchForChanges'.'AnyBuffer'
    " If you try to do checktime *, you'll get E93: More than one match for * is given
    let bufspec = ''
  else
    if bufnr(a:bufname) == -1
      echoerr "Buffer " . a:bufname . " doesn't exist"
      return
    end
    let id = 'WatchForChanges'.bufnr(a:bufname)
    let bufspec = a:bufname
  end
  if len(a:000) == 0
    let options = {}
  else
    if type(a:1) == type({})
      let options = a:1
    else
      echoerr "Argument must be a Dict"
    end
  end
  let autoread    = has_key(options, 'autoread')    ? options['autoread']    : 0
  let toggle      = has_key(options, 'toggle')      ? options['toggle']      : 0
  let disable     = has_key(options, 'disable')     ? options['disable']     : 0
  let more_events = has_key(options, 'more_events') ? options['more_events'] : 1
  let while_in_this_buffer_only = has_key(options, 'while_in_this_buffer_only') ? options['while_in_this_buffer_only'] : 0
  if while_in_this_buffer_only
    let event_bufspec = a:bufname
  else
    let event_bufspec = '*'
  end
  let reg_saved = @"
  "let autoread_saved = &autoread
  let msg = "\n"
  " Check to see if the autocommand already exists
  redir @"
    silent! exec 'au '.id
  redir END
  let l:defined = (@" !~ 'E216: No such group or event:')
  " If not yet defined...
  if !l:defined
    if l:autoread
      let msg = msg . 'Autoread enabled - '
      if a:bufname == '*'
        set autoread
      else
        setlocal autoread
      end
    end
    silent! exec 'augroup '.id
      if a:bufname != '*'
        "exec "au BufDelete    ".a:bufname . " :silent! au! ".id . " | silent! augroup! ".id
        "exec "au BufDelete    ".a:bufname . " :echomsg 'Removing autocommands for ".id."' | au! ".id . " | augroup! ".id
        exec "au BufDelete    ".a:bufname . " execute 'au! ".id."' | execute 'augroup! ".id."'"
      end
        exec "au BufEnter     ".event_bufspec . " :checktime ".bufspec
        exec "au CursorHold   ".event_bufspec . " :checktime ".bufspec
        exec "au CursorHoldI  ".event_bufspec . " :checktime ".bufspec
      " The following events might slow things down so we provide a way to disable them...
      " vim docs warn:
      "   Careful: Don't do anything that the user does
      "   not expect or that is slow.
      if more_events
        exec "au CursorMoved  ".event_bufspec . " :checktime ".bufspec
        exec "au CursorMovedI ".event_bufspec . " :checktime ".bufspec
      end
    augroup END
    let msg = msg . 'Now watching ' . bufspec . ' for external updates...'
  end
  " If they want to disable it, or it is defined and they want to toggle it,
  if l:disable || (l:toggle && l:defined)
    if l:autoread
      let msg = msg . 'Autoread disabled - '
      if a:bufname == '*'
        set noautoread
      else
        setlocal noautoread
      end
    end
    " Using an autogroup allows us to remove it easily with the following
    " command. If we do not use an autogroup, we cannot remove this
    " single :checktime command
    " augroup! checkforupdates
    silent! exec 'au! '.id
    silent! exec 'augroup! '.id
    let msg = msg . 'No longer watching ' . bufspec . ' for external updates.'
  elseif l:defined
    let msg = msg . 'Already watching ' . bufspec . ' for external updates'
  end
  echo msg
  let @"=reg_saved
endfunction

let autoreadargs={'autoread':1}
execute WatchForChanges("*",autoreadargs)
Moeabdol
fonte
1
Esta é a resposta preferida para resolver as deficiências da leitura automática do terminal.
Mccanfield
3
@mcanfield Não, pois ainda não "olha para as mudanças". Você ainda precisa ter o Vim ativo e ele ainda está pesquisando (não assistindo) em um conjunto limitado de eventos. CursorHoldé executado uma vez . Portanto, se você sair para tomar um café ou estiver fazendo algo em outra janela, ele não será atualizado.
Martin Tournoij
4
Esse script também é empacotado como um plugin do Vim: vim-autoread .
stephen.hanson
1
@ stephen.hanson Obrigado pelo link, esse plugin está funcionando bem para mim!
Tropilio
3

Tail Bundle deve fazer o que você quiser. Observe, eu mesmo não usei.

Alok Singhal
fonte
2

Se unix + neovim

:term tail -f <filename>

Obviamente, isso não funcionará para todos, mas é como eu faço.

Jeb
fonte
0

O VIM irá avisá-lo quando um arquivo for atualizado para que você não substitua as alterações feitas desde que você o abriu. Nesse momento, ele solicitará que você recarregue o arquivo.

prestomação
fonte
1
Obrigado pela sua resposta, mas o vim não me avisou quando o arquivo foi alterado no meu sistema :(
Patrick