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.