29

I'm using VIm for all kinds of things (or gVim in this case), including monitoring output written to a file; I use autoread to make Vim reread the file, which it does whenever I switch the keyboard focus to it.

Is there any way to make Vim update the buffer even if I don't switch the keyboard focus? I've tried setting checktime but it doesn't appear to have any effect while keyboard focus is elsewhere.

The output I'm monitoring is replacing the output file completely; and I'm not looking to tail -f it. There are other options, like piping into a new instance every time, or piping into less or something, but it'd be cool if it could be done with VIm.

  • @Carpetsmoker how about the CursorHold event? If Vim doesn't have focus, it should trigger every n seconds. – muru Mar 23 '15 at 18:13
  • 1
    @muru No, CursorHold is executed once only, this is explicitly documented as: "doesn't fire every 'updatetime' ms if you leave Vim to make some coffee. :)" – Martin Tournoij Mar 25 '15 at 16:24
  • 1
    I removed the gvim tag again, I hope that's okay ;-) As I see it, this questions is not really about gVim, as focus change is just one event on which Vim checks if the file is changed (there are a whole bunch, see my answer for details). Most, if not all, ways to improve on this will work fine in both Vim and gVim. – Martin Tournoij Mar 25 '15 at 21:19
  • @Carpetsmoker ah, I was under the impression that gVim checked regularly while it had keyboard focus (thus making it a gVim question), but it appears I was mistaken. Thanks for clearing that up. – falstro Mar 26 '15 at 8:25
20

:help 'autoread' doesn't have a lot of information on how it works, but Vim checks if an open file is modified in certain events (it always does this), and will reload it automatically if autoread is abled.

Specifically, it checks when:

  • :checktime is used;
  • a buffer is entered;
  • :diffupdate is used;
  • :e is issued for a file that already has a buffer;
  • executing an external command with !;
  • returning to the foreground (^Z, fg, only if the shell has job control);

for gVim, this is also done when:

  • closing the "right-click" menu (either by selecting something, or just by closing it);
  • focus is changed (this is what you already noticed);
  • closing the file browsers dialog that pops up if you use "file -> open", "file -> save as" from the menu (as well as some other places).

I gathered this information from the Vim source by locating all calls to the buf_check_timestamp(), check_timestamps() functions, and locations where need_check_timestamps is set to TRUE. This list may be outdated because I looked at it 2015, but at a quick glance it seems to still be (mostly) accurate.

The key thing is that Vim only checks if the file is modified in a very limited set of circumstances. This makes sense, because polling a potentially large number of buffers every n seconds can be rather expensive.

Since Vim 7.4.1578 (March 2016) there are timers, which you can use to run checktime every n seconds (a previous version of this answer from 2015 used hackish workarounds).

For example, to do this only for certain buffers you can use something like:

set autoread
fun! s:checktime(timer_id)
    checktime
endfun
autocmd BufNewFile,BufReadPost xxx
            \ call timer_start(3000, function('s:checktime'), {'repeat': -1})

This has the disadvantage of setting a lot of timers, so perhaps a better method is to set one which loops over all the buffers and then sets the timer again (so you don't risk having two run simultaneously):

set autoread
fun! s:checktime(timer_id)
    for buf in filter(map(getbufinfo(), {_, v -> v.bufnr}), {_, v -> buflisted(v)})
        echom buf
        exe 'checktime' buf
    endfor
    call timer_start(3000, function('s:checktime'))
endfun
call timer_start(3000, function('s:checktime'))

I did notice Vim blinks a bit even if nothing changes; not sure if there's a way to work around that (didn't look at it); so you might want to add it as a command to run only when needed:

set autoread
let s:autoread_timer = -1
fun! s:checktime(timer_id)
    checktime
endfun

command! -bang Autoread 
            \  if <bang>0
            \|   call timer_stop(s:autoread_timer)
            \| else
            \|   let s:autoread_timer = timer_start(1000,
            \        function('s:checktime'), {'repeat': -1})
            \| endif

There are many ways to improve on this further, such as enabling it only for the current buffer, more control on how often to run, etc. etc. but the idea should be clear :-)

| improve this answer | |
2

I made a little plugin vim-autoread that uses tail -f to update the buffer in the background asynchronously. This needs obviously the job feature of Vim Version 8.

Use :AutoRead to turn it on and :AutoRead! to turn it off.

| improve this answer | |
  • A NeoVim-compatible version would be great! – Andrew Aug 1 '17 at 22:05
  • @AndrewMacFie I don't see, why this wouldn't work on Neovim. – Christian Brabandt Aug 2 '17 at 6:10
  • the lack of Vim 8 jobs support in NeoVim I thought? – Andrew Aug 2 '17 at 17:52
  • @AndrewMacFie oh that is probably be true. Since I don't use neovim, I cannot make it NeoVim compatible. However a PR would be welcome :) – Christian Brabandt Aug 2 '17 at 19:05
  • fair enough : ) – Andrew Aug 2 '17 at 22:18
2

from this answer (refering to an answer by PhanHaiQuang and a comment by flukus)

One can run this oneliner from ex (whithin vim) when needed (or put each command in vimrc, for when log-files are opened.)

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

Explanation:
- autoread: reads the file when changed from the outside (but it doesnt work on its own, there is no internal timer or something like that. It will only read the file when vim does an action, like a command in ex :!
- CursorHold * checktime: when the cursor isn't moved by the user for the time specified in 'updatetime' (which is 4000 miliseconds by default) checktime is executed, which checks for changes from outside the file
- call feedkeys("lh"): the cursor is moved once, right and back left. and then nothing happens (... which means, that CursorHold is triggered, which means we have a loop)

| improve this answer | |
1

In Neovim you could use the following command which will open a terminal buffer running the tail command.

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

This will continue to work even if the file is removed and recreated.

Or add the following to your $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>)
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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