The problem#
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.
Solution using sh
#
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.
The 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:
#!/bin/sh
while true
do
latexmk -pdf "$1" </dev/null
inotifywait -e close_write "$@"
done
This script takes a .tex
file as the first argument and any other
dependencies as further arguments and uses latexmk
to
recompile the .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.
The </dev/null
on 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.
Solution using latexmk
#
It turns out that latexmk
actually has built-in support for this
behavior through the -pvc
(PreView Continuously
) option:
latexmk -pvc -pdf "$1" </dev/null
We still need the </dev/null
to skip past errors without manual
intervention, but 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
~/.latexmkrc
containing
$pdf_previewer = 'start evince %O %S';
where Evince is the PDF viewer I use.
Limitations#
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,
inotifywait
uses inotify, which is Linux-only. Similar cross-platform tools exist likefswatch
andwatchmedo
which 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
latexmk
's-pvc
feature handles dependencies, thesh
script has to be told what other files to watch.latexmk
's-deps
and-deps-out
options 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 usemake
to be more generic across different kinds of projects. -
Other non-obvious details. For example, it doesn't work with
sshfs
.
Finally, there are existing projects that implement this functionality which
I have not used. I found rerun
and guard
both
appear to have similar goals and one of them or some other existing tool
may be the right solution to these problems.
Comments
Have something to add? Post a comment by sending an email to comments@aweirdimagination.net. You may use Markdown for formatting.
There are no comments yet.