4

Is it possible to adapt minted or Pygments to also highlight names of environments? I would like to have this output

enter image description here

But all I get is this

enter image description here

I.e. {itemize} is not bold. I guess it is possible by changing the Latex lever or something like that, but I have no idea where to start.

MWE

\documentclass[parskip = full]{scrartcl}

\usepackage[T1]{fontenc}

\usepackage{beramono}
\usepackage{minted}

\usemintedstyle{bw}

\begin{document}

This input code

\begin{minted}{latex}
\begin{itemize}[label = {--}]% comment
   \item Text
\end{itemize}
\end{minted}

should result in an output like from the following code

\ttfamily
\textbf{\textbackslash begin\{itemize\}}[label = \{-\/-\}]\textit{\% comment}\\
\strut\ \strut\ \strut\ \textbf{\textbackslash item} Text\\
\textbf{\textbackslash end\{itemize\}}

\end{document}
  • The listing package may do the job as well : en.wikibooks.org/wiki/LaTeX/Source_Code_Listings but I guess it won't be a good enough workaround? It seems hard coded in minted. – s.k Jun 26 '17 at 22:17
  • The problem with listings is that it only highlights known keywords and doesn’t automatically recognises all macros starting with a backslash … and I don’t want to take care of a manually edited long list of macros ;-) – Tobi Jun 26 '17 at 22:49
6
+100

It seems minted relies exclusively on Pygments to process the input code. The documentation itself doesn't reference ways to alter the content of the output from minted directly. The remaining solution is to use a custom Pygments lexer. That's quite well documented there: http://pygments.org/docs/lexerdevelopment. I'll give a step-by-step solution in case someone else wants to customize lexers.

Find how Pygments processes LaTeX input

Go to the lexers folder in the Pygments installation folder. In my case, on Windows, it's in <python_path>/Lib/site-packages/pygments/lexers/; on macOS it is something like /Library/Python/2.7/site-packages/Pygments-2.2.0-py2.7.egg/pygments/lexers/. I haven't tried on Linux. You'll find the path for every lexer in the _mapping.py file.

For TeX/LaTeX, it's the TexLexer class, inside markup.py.

Let's see how it processes a \begin{itemize}. The main "state" is root. When the program finds a \ followed by one or more letters (l. 298), it treats all of it as a Keyword (In our case, that's the \ + begin, that will be printed in bold.), then parses the rest in the command state. The command state checks for square brackets or a star, but doesn't handle curly braces. The {itemize} will then be parsed back in the root state, where it will be printed as a simple text.

Create a custom lexer

If I understood the question correctly, all that's needed is to parse \begin and \end differently than the other commands. Let's create a custom TeX lexer that inherits from the original TexLexer and adds those two specific matches in the root state.

This is what I wrote, in a new Python file. I'm not detailing all of it since it's very context-specific, and the official documentation is precise (see for example here regarding the inheritance).

from pygments.lexer import inherit
from pygments.token import Keyword
from pygments.lexers.markup import TexLexer

__all__ = ['CustomTexLexer']

class CustomTexLexer(TexLexer):
    aliases = ['xtex', 'xlatex']

    tokens = {
        'root': [
            (r'\\begin\{[a-zA-Z\*]+\}', Keyword, 'command'),
            (r'\\end\{[a-zA-Z\*]+\}', Keyword),
            inherit,
        ],
    }

It might also be possible to add it to the same file as TexLexer, but I'm not sure how safe it is. In both case, you need to give new aliases and add the class name to __all__.

Rebuild the lexer mapping

The documentation says to build using this command:

$ make mapfiles

but I couldn't find the corresponding Makefile. The documentation might be outdated here, since the _mapping.py file has been made into a script that can update itself:

$ python _mapping.py

(On macOS this must be executed as sudo to grant writing access in the /Library/ tree.)

Result

We now have two LaTeX lexers at our disposal:

\documentclass{article}

\usepackage{polyglossia}
\setmonofont{Courier New}

\usepackage{minted}
\usemintedstyle{bw}

\begin{document}

Original lexer:
\begin{minted}{latex}
\begin{itemize}[label = {--}]% comment
   \item Text
\end{itemize}
\end{minted}

Custom lexer:
\begin{minted}{xlatex}
\begin{itemize}[label = {--}]% comment
   \item Text
\end{itemize}
\end{minted}

\end{document}

(I had to simplify the preamble because of technical issues, it's unrelated to the current question.)

Output:

Output with both original and custom lexers

Minor update

I updated the custom lexer so that it processes optional arguments of an environment the same way as optional arguments of a macro. It sounded more appropriate at this point. Note that it makes zero difference in the MWE, since the bw class doesn't emphasize the related object, Name.Attribute. Just for show, this is what it looks like with the default style:

Updated output

  • Sorry for the late reply … Thank your very much for you effort and the detailed answer! It works like charm :-) I took the liberty to edit the macOS paths to you answer. Further more I added \* to the environment name RegEx. – Tobi Jul 2 '17 at 9:10
  • @Tobi No problem, thanks for the precisions! Glad I could help. – SDF Jul 2 '17 at 10:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.