# Expanding a minted command inside a macro

I am currently trying to speed up the compile time of a document that uses minted. I would like to be able to have certain language features (and style) appear the same in both a minted environment and inline inside the text, so I made the following command.

\newmint[ccode]{c}{}
\def\while{\ccode|while|\xspace}


This works well, and produces the word "while" with the correct colouring. However, minted creates a new file for each invocation of the macro, which significantly increases the compile time. As such, I attempted to make a macro that expanded once and then the result would be placed in each invocation.

\newmint[ccode]{c}{}
\edef\while{\ccode|while|\noexpand\xspace}


However, when I attempt this, I get the following error.

Argument of \reserved@a has an extra }.


Is it possible to get TeX to evaluate \ccode|while| once and substitute the results elsewhere?

-
Would \lstinline from the listings package do as well? –  egreg Sep 19 '12 at 22:43

If the font size is not changed, then you can put it in a box:

\newsavebox\whilebox
\sbox\whilebox{\ccode|while|}
\newcommand*{\while}{\usebox\whilebox\xspace}


Solutions based on expansion will not work, because \ccode uses unexpandable stuff.

-

You might consider using PythonTeX to highlight your code; it uses the same Pygments library that minted does. PythonTeX saves all highlighted content, so you only have to run external Python programs when you have modified your code, not when you have just modified your document. When PythonTeX does highlight code, it has less overhead than minted (see the more technical details at the end of my answer if you're interested). As an added bonus, you can turn off highlighting temporarily (package option pygments=false), in which case your documents will compile without running any external tools at all. This can be very useful when you're doing a lot of code editing. And PythonTeX support Unicode characters in code, in case you need to typeset code with comments in a non-English language (you'll need inputenc and fontenc for this to work).

PythonTeX provides a \pygment command for inline use, a pygments environment, and an \inputpygments command for bringing in external code files. It doesn't currently have built-in commands for creating custom commands and environments, but this may be easily done with something like

\newcommand{\ccode}{\pygment{c}}
\newcommand{\pygmentsC}{\begingroup\obeylines\pygmentsCnext}
\newcommand{\pygmentsCnext}[1][]{\endgroup\pygments[#1]{c}}
\newenvironment{ccodeblock}{\pygmentsC}{\endpygments}


PythonTeX uses fancyvrb internally for typesetting code, so you can pass fancyvrb options to the pygments environment (the inline command doesn't take options, since almost all options are only really appropriate for an environment). The \pygmentsC and \pygmentsCnext macros in the above example are necessary so that the ccodeblock environment can take fancyvrb arguments, like

\begin{ccodeblock}[numbers=left]
# include <iostream>

int main()
{
std::cout << "Hello, world!\n";
}
\end{ccodeblock}


Technical details

PythonTeX consists of a LaTeX package and a Python script. The package saves all code that needs highlighting to a single external file, with delimiters. The PythonTeX script highlights this code and saves the results as macros in a single file. Then the next time the document is compiled, this file of macros is brought in by the package. (If saving highlighted code in macros ever slows things down due to the length of the code, it can be fixed using the fvextfile option. The package gives warnings when highlighted code is missing because the script has not yet been run.)

Each PythonTeX command or environment is highlighted individually (so each \ccode|while| would be highlighted invidually), but all the highlighting is done in a single pass by a single Python process. Minted uses a new Python process and two external files for each macro, so there's more overhead. Since PythonTeX saves highlighted results, compiling a document doesn't involve running the script (unless the code has been modified). You just need to run LaTeX when you are editing the document; when you edit the code, you need LaTeX + script + LaTeX again. Minted doesn't save highlighted results, so everything must be highlighted again whenever the document is compiled.

Minted does have the advantage that highlighted content is always up-to-date, though that can be achieved with PythonTex by always running LaTeX + script + LaTeX again.

-
Does this address the OP's concern that pgyments will be run for each use of the macro? –  Aditya Sep 22 '12 at 6:42
@Aditya I've edited my answer to provide some additional details. The short answer is that PythonTeX highlights each use of the macro separately, but because it has much less overhead than minted (one Python process and two external files total, versus one process and two external files per macro), that shouldn't matter. –  G. Poore Sep 22 '12 at 17:14
I answered this question, and in the process did some informal benchmarking of pythontex vs. minted. Summary: on my system, 40 simple uses of \mint take about 12 s to compile, and 80 uses needs about 23 s. With pythontex, 40 equivalent commands need 3.5 s, 80 uses is almost the same, and you only get to around 23 s for 8000 uses. All the pythontex numbers are actually overestimates for typical use, since highlighted results are cached. –  G. Poore Nov 21 '12 at 22:06

If anyone is interested, here are two approaches to do something similar in ConTeXt with the vim module.

## First approach

The vim module (actually, the filter module on which it is based), provides two features that can be combined to ensure that the external syntax highlighter is not called multiple times.

The first feature is buffers which gets around the limitation that \start<vimtyping> ... \stop<vimtyping> cannot be used in a macro. buffers are meant for display listings, so it inserts a newline after the buffer is typeset. To avoid that, simply wrap it around a \hbox (This means that the text will not be broken across lines).

\startbuffer[while]while\stopbuffer
\def\FIRSTWHILE{\hbox{\processCbuffer[while]}}


The second feature is the state=stop option which ensures that the syntax highlighting program is not run. (This option is meant for the situations when you want to send your files to a colleague who does not have the syntax highlighting program).

\def\SECONDWHILE{\hbox{\processCbuffer[state=stop][while]}}


Now all that remains to be done is to define a macro that calls \FIRSTWHILE the first time it is called and \SECONDWHILE after that:

\def\WHILE
{\FIRSTWHILE
\glet\WHILE\SECONDWHILE}


Combining everything together:

\usemodule[vim]

\definevimtyping[C][syntax=c]

\startbuffer[while]while\stopbuffer

\def\FIRSTWHILE{\hbox{\processCbuffer[while]}}
\def\SECONDWHILE{\hbox{\processCbuffer[state=stop][while]}}

\def\WHILE
{\FIRSTWHILE
\glet\WHILE\SECONDWHILE}

\starttext
\dorecurse{10}{what \WHILE{} something }

\stoptext


Note that with this approach, the buffer is still written to file each time the macro is called (but file IO is relatively fast); just the syntax highlighting program is not called.

## Second approach

The second approach is to simply save the string to a file using \savebuffer, and typeset it using \typeset<vim>file. This has the advantage that the data is written to the file only once (at the beginning of each multi-run compile), and the syntax highlighter is run only when the md5 sum of the string changes (which effectively means, only when the string changes).

\usemodule[vim]

\definevimtyping[C][syntax=c]

\startbuffer[while]while\stopbuffer
\savebuffer[while][whatever] %Saved in \jobname-whatever.tmp

\def\FIRSTWHILE{\hbox{\typeCfile{\jobname-whatever.tmp}}}
\def\SECONDWHILE{\hbox{\typeCfile[state=stop]{\jobname-whatever.tmp}}}

\def\WHILE
{\FIRSTWHILE
\glet\WHILE\SECONDWHILE}

\starttext
\dorecurse{10}{what \WHILE{} something }

\stoptext


This approach can also be used in LaTeX (but the syntax highlighter will be run once every run since minted package does not cache the results).

-