When developing code or creating visual artifacts in non-WYSIWYG systems, it is very useful to constantly be aware of the output of the compiler and the appearance of the artifact you are creating, whether it is a GUI, a chart, a graph, or a paper. The common way of doing this is to have an IDE specialized for the system you are using; for example, LyX provides a WYSIWYG editor for LaTeX. Similarly, there may be plugins for your text editor to support whatever kind of development you are doing. On the other hand, we can use the shell to create a solution independent of the text editor and the availability of plugins for the particular system being developed for.
The OS provides a mechanism for detecting when a file has been
modified, so a script can react to that event. On Linux, this is the
inotify API, but similar facilities exist on other OSes.
inotifywait command will wait until a watched
file has been modified before letting the script continue. By making
a simple loop, we can perform some action every time one of a set of
files is modified:
latexmk -pdf "$1" </dev/null
inotifywait -e close_write "$@"
This script takes a
.tex file as the first argument and any other
dependencies as further arguments and uses
.tex document any time one of those files is modified.
Most PDF viewers will automatically reload the modified file, so the
result is that after saving, the document preview is automatically
updated within a few seconds.
latexmk prevents it from blocking due to
errors while still showing them on the console. The idea is to keep the
focus on the text editor as much as possible; this way mistakes can be
corrected without manually skipping past the errors in the console.
It turns out that
latexmk actually has built-in support for this
behavior through the
PreView Continuously) option:
latexmk -pvc -pdf "$1" </dev/null
We still need the
</dev/null to skip past errors without manual
latexmk is aware of the dependencies and will
watch for changes to those as well.
In order to get that to work right on my system, I had to make a
$pdf_previewer = 'start evince %O %S';
where Evince is the PDF viewer I use.
While this works pretty well for LaTeX and is adaptable to similar use cases, it has some limitations which I intend to address in future blog posts:
No good way to view the output of the command. For example, it may contain useful errors or warnings. Scrolling up in the console works, but can be awkward when the boundaries between the output for multiple runs are not easy to see at a glance. One possible solution may be to use a text editor plugin for warnings and errors so the command output is not needed.
We may want to run a command which does not halt and only kill it when there is a change. For example, when writing the code for a GUI, it may be useful to always have the latest version visible and ready for test interactions.
Not cross-platform. As the name suggests,
inotifywaituses inotify, which is Linux-only. Similar cross-platform tools exist like
watchmedowhich support using kqueue on BSD and FSEvents on OS X in addition to falling back on manual polling if unable to use the OS support for watching files.
Manual dependency handling. While
-pvcfeature handles dependencies, the
shscript has to be told what other files to watch.
-deps-outoptions could be used to get the list of dependencies to watch.
Fixing the previous would mean tying the solution to LaTeX or
latexmk. Optimally, the system would use
maketo be more generic across different kinds of projects.
Other non-obvious details. For example, it doesn't work with
Finally, there are existing projects that implement this functionality which
I have not used. I found
appear to have similar goals and one of them or some other existing tool
may be the right solution to these problems.