Groff + Emacs = ☀️

Summary: I share a little hack to make authoring documents with groff in Emacs a bit smoother.

Having used LaTeX quite regularly in an academic context for the last couple of years, using groff as a (way) leaner alternative to LaTeX for writing my resume and cover letters while looking for a job has been quite refreshing1 and I definitely did not miss the unwieldiness of the LaTeX toolchain.

For authoring a groff document, I have the original groff source file and the compiled .pdf file (I use pdf-tools for that) opened side-by-side in Emacs. But while compiling, auto-revert-mode would try to re-render the pdf too early, which would first fail and show an empty buffer for a few seconds instead. This does not make for a great edit-compile-run loop. A solution exists when using AUCTeX but remember we're trying to get away from TeX for a second.

I could replicate this for groff in an only slightly hacky way, which is quite the feat compared to the rest of my Emacs personal tricks and fixes. Quickly, it goes like this:

As far as I know, the only way to trigger some automatic actions at the end of Emacs' compilation process is to populate compilation-finish-functions. But the only arguments passed to these functions are the compilation buffer and a string such as "finished\n" when the compilation succeeded. This does not say anything about the output document so the crux of the trick is to pass that information through the compilation buffer itself.

Here are some detailed steps for you to replicate:

%.pdf : %.ms macros.ms
        @echo "OUTPUT_FILE=$@" # that's the important line
        groff -U -d paper=A4 -K utf8 -ms -T pdf $< > $@
(defun my/after-compile (compile-buf desc)
  (let ((result
         (with-current-buffer "*compilation*"
           (save-excursion
             (goto-char (point-min))
             (re-search-forward "OUTPUT_FILE=\\(.*\\)" nil t)
             (match-string-no-properties 1)))))
    (when result
      (let* ((filename (expand-file-name result))
             (buf (find-buffer-visiting filename)))
        (when buf
          (with-current-buffer buf
            (revert-buffer nil t t)))))))

(add-hook 'compilation-finish-functions 'my/after-compile)
\# Local Variables:
\# compile-command: "make groff.pdf"
\# End:

There you have it, the (more) optimal groff workflow within Emacs. You're welcome.

Footnotes:

1

Even if roff's roots predate TeX!

For any feedback you might have regarding this article, send an email to my public inbox.