Sunday, March 6, 2011

My solution for Vim + Sweave + Python + Latexmk

I of course write all my documents in LaTeX, and lately I've been into using two excellent tools for writing papers on quantitative topics: Sweave (/ɛs wiv/) and the python.sty package. The former allows you to include R code in the TeX source of your document, which is executed as the document is compiled, with its output (including figures) appearing in the document. The latter does the same thing for Python rather than R. With the two of them, one can include all of one's data processing and statistical code in the very paper that reports on them.


So one's LaTeX document might look like this:
\documentclass{article}
\usepackage{python}
\title{Test}
\begin{document}

% This is TeX code
Hello, world!

\begin{python}
# This is python code
print r'\begin{verbatim}'
import sys
print sys.version
print r'\end{verbatim}'
\end{python}

More \TeX here. Here's a list:
\begin{itemize}
\item What's up?
\end{itemize}

\begin{Scode}
# This is R/S code
print('what up')
for (i in 1:10) { print(i) }
\end{Scode}

How was that?
\end{document}
It's not for everyone, but I find it useful.

What this means though, is that the TeX source file actually contains code in three languages (TeX, Python, R). In my text editor of choice -- Vim -- it took a little bit of thought to enable syntax highlighting for all three simultaneously, without interfering with each other. So I thought I'd share my solution for posterity.


First, I name any documents written in this way with a .Rtex suffix. Add a line like this to ~/.vimrc, causing Vim to set the filetype of files ending in .Rtex to "rtex":
au BufRead *.Rtex setf rtex

Now we can control the syntax highlighting of such files be creating the file ~/.vim/syntax/rtex.vim and putting the appropriate Vim code in it. The following does the trick:
let b:current_syntax = ''
unlet b:current_syntax
runtime! syntax/tex.vim

let b:current_syntax = ''
unlet b:current_syntax
syntax include @TeX syntax/tex.vim

let b:current_syntax = ''
unlet b:current_syntax
syntax include @Python syntax/python.vim
syntax region pythonCode matchgroup=Snip start="\\begin{python}" end="\\end{python}" containedin=@TeX contains=@Python

let b:current_syntax = ''
unlet b:current_syntax
syntax include @R syntax/r.vim
syntax region rCode matchgroup=Snip start="\\begin{Scode}" end="\\end{Scode}" containedin=@TeX contains=@R

hi link Snip SpecialComment
let b:current_syntax = 'rtex'

Now restart vim and open a .Rtex file. Here's a screenshot of the result:

Finally there is the matter of streamlining the compilation process for Rtex documents like this. I generally use the excellent tool latexmk and its "continuous preview" feature, which scans a TeX document, determines all of the ancillary files that it depends on, watches them, and upon detecting a change, invokes latex/pdflatex/bibtex as necessary to fully recompile the document, and finally tells your PDF viewer (or whatever) to refresh its view. Boom, now you're a hacker.

All we have to do is add Sweave, which runs as an R command ("CMD"), to latexmk's compilation process. This is as simple as creating a file named latexmkrc in the directory of the Rtex document, and filling it with something along these lines:
@default_files = ("*.Rtex");
$pdf_mode = 1;
$pdflatex = 'R CMD Sweave %B.Rtex && pdflatex -shell-escape %B';

This tells latexmk to compile all .Rtex files in the current directory if no document is explicitly named when we start it, and to generate PDFs by default, using pdflatex, which should be immediately preceded by executing R CMD Sweave on the Rtex file. The -shell-escape option on the call to pdflatex is required by the python.sty package.

Now all we have to do is invoke latexmk -pvc from the command line in the directory of the Rtex document, and latexmk will start up in continuous preview mode, detecting any changes to the Rtex file (and its dependencies), and recompiling our PDF on the fly.

Here's what the example from before looks like once compiled:

3 comments:

  1. Thank you for posting this, it saved me a bit of time.

    ReplyDelete
  2. @James: My pleasure, thanks for stopping by!

    ReplyDelete
  3. I just heard about latexmk today, and didn't really have any idea of how it worked. I have sweave stuff in .Rnw, so I took your 3 lines of code and changed .Rtex to .Rnw, and it works!

    Thank you for the clear explanation.

    ReplyDelete