Skip to main content

Vim and inotify

When writing documents with LaTeX, I often like to use inotify to rebuild the document whenever I save the source file. I use Vim to edit the source and Evince to view the built PDF document. Evince automatically reloads the PDF document when it changes, keeping the current location, so this setup provides a tight feedback loop. My old setup did not work in my current environment, so I took the time to get it working again today.

The inotifywait command listens for various filesystem events. One complication is that different text editors save files in different ways, which results in different events. Run inotifywait -q -m . in the directory of the files that you want to watch to observe the events as you edit a file.

For example, saving the file for this blog entry using Vim results in the following events when the file is first saved.

$ inotifywait -q -m .
./ CREATE 04-vim-and-inotifywait.md
./ OPEN 04-vim-and-inotifywait.md
./ MODIFY 04-vim-and-inotifywait.md
./ CLOSE_WRITE,CLOSE 04-vim-and-inotifywait.md

When the file already exists, saving the file results in the following events.

$ inotifywait -q -m .
./ CREATE 4913
./ OPEN 4913
./ ATTRIB 4913
./ CLOSE_WRITE,CLOSE 4913
./ DELETE 4913
./ MOVED_FROM 04-vim-and-inotifywait.md
./ CREATE 04-vim-and-inotifywait.md
./ OPEN 04-vim-and-inotifywait.md
./ MODIFY 04-vim-and-inotifywait.md
./ MODIFY 04-vim-and-inotifywait.md
./ ATTRIB 04-vim-and-inotifywait.md
./ CLOSE_WRITE,CLOSE 04-vim-and-inotifywait.md
./ ATTRIB 04-vim-and-inotifywait.md

Based on these experiments, it is clear that the build command should be triggered by CLOSE_WRITE,CLOSE event for a source file. This event occurs exactly once in both cases, and the file is readable immediately after the event is seen. (There is no race condition.)

For projects where the source for each document is in a single LaTeX source file, the Bash code to repeatedly wait for events and rebuild a document whenever the source changes is as follows.

while IFS=/ read -r events filename ; do
  if [ "${events}" = "CLOSE_WRITE,CLOSE" ] ; then
    if [ "${filename%.tex}" != "${filename}" ] ; then
      make "${filename%.tex}.pdf"
    fi
  fi
done < <(inotifywait -q -m --format %e/%f .)

This rebuilds any LaTeX document that changes, but note that a while loop is required even in the case of a single file because Vim writes to a new file. One therefore has to watch the directory and match the filename. Also note that redirecting the output of inotifywait to a while loop allows events to be handled even while the body of the loop is running because the inotifywait process does not terminate. You do not want to miss the event for a document while still building a different document, for example.

I will write more about this topic in the upcoming Makefile article series.

Author

Travis Cardwell

Published

Tags