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

Update 2015-06-25:

  • I've scrapped the "shell" method, as it was too dysfunctional. It stopped insert mode and left zombie processes. Look at this posts's revision history is you really want to see it anyway.
  • I've made a plugin out of this: auto_autoread.vim. At the moment, it's effectively the same as the code below, but I would recommend you use the plugin as this is likely to receive updates.

What does autoread do?

To answer this question, we must first understand what the autoread option does, and more importantly, what it doesn't do.

Unfortunately :help 'autoread' doesn't have a lot of information on this, it just says "a file has been detected to have been changed outside of Vim". How does Vim detect that a file is changed? On certain actions, Vim checks the modification time of the file.

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.

I may have missed some events, but the key thing to remember is that Vim only checks if the file is modified in a very limited set of cirsumstances. It certainly doesn't "poll" the file for changes ever n seconds, which is basically what you're after.

So, for your purpose, set autoread is not enough.

Using Python

This schedules a Python thread to run in the background, it will run :checktime every n seconds. If autoread is enabled, this will reload the buffer from disk, else it will just warn.

This requires that Vim has +python or +python3 in :version. It should work on all platforms (including 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

You can start this off by using :call AutoreadPython(); you can of course do this in an autocmd; for example:

autocmd *.c call AutoreadPython()

Afterword

There are actually more methods, for example you could use a tool such as entr or the Python inotify or gamin module to monitor a file for changes, :checktime also checks all buffers if it's not given any arguments, this could be improved on by only checking a single buffer or a certain file.
However, this answer is already rather long :-) These method should (hopefully!) work fine for most scenarios, or should be easily adaptable to your scenario.

PS. I also tried to use Ruby, but unfortunately Ruby threads (using Thread) don't run in the background like Python does, so I wasn't able to get this to work (perhaps there is another way, though?)

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.

  • 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
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>)
1

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)

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.