[Tex/LaTex] listings and keywords in the index

indexinglistings

The listings package allows to index the appearance of keywords in a listing, for example similar to the following MWE:

\documentclass{article}
\usepackage[T1]{fontenc}

\usepackage{imakeidx}
\makeindex
\usepackage{listings}
\lstset{
  language   = [LaTeX]TeX,
  basicstyle = \ttfamily,
  moretexcs  = [2]{LaTeX},
  index      = [2][texcs2],
  indexstyle = [2]\cmdidx
}
\newcommand*\cmdappearance[1]{\texttt{\textbackslash#1}}
\newcommand*\cmdidx[1]{\index{#1@\cmdappearance{#1}}}
\newcommand*\cmd[1]{\cmdappearance{#1}\cmdidx{#1}}

\begin{document}

\begin{lstlisting}
 \LaTeX{} and \LaTeX-code
\end{lstlisting}

\printindex
\end{document}

The PDF of this example looks as expected but it is not, really, as a look in \jobname.ilg and \jobname.idx shows:

\jobname.ilg:

This is makeindex, version 2.15 [TeX Live 2012] (kpathsea + Thai support).
Scanning input file test.idx....
!! Input index error (file = test.idx, line = 2):
   -- Extra `@' at position 31 of first argument.
done (1 entries accepted, 1 rejected).
Sorting entries...done (0 comparisons).
Generating output file test.ind....done (5 lines written, 0 warnings).
Output written in test.ind.
Transcript written in test.ilg.

\jobname.idx:

\indexentry{LaTeX@\texttt  {\textbackslash LaTeX}}{1}
\indexentry{LaTeX\unhbox \voidb@x \kern \z@ @\texttt  {\textbackslash LaTeX\unhbox \voidb@x \kern \z@ }}{1}

This is no problem for the MWE but the second entry would be lost if it were on a different page. The \LaTeX- part of the listing seems to have an expanded version of \textcompwordmark inserted after the keyword as opposed to the \LaTeX{} part which got the expected entry. The inserted command apparently is

\@namedef{\@lst @um@}{\leavevmode\kern\z@}

(line 990 of listings.sty) which a redefinition of this command into

\@namedef{\@lst @um@}{\textcompwordmark}

shows. The \jobname.idx file then is

\indexentry{LaTeX@\texttt  {\textbackslash LaTeX}}{1}
\indexentry{LaTeX\textcompwordmark @\texttt  {\textbackslash LaTeX\textcompwordmark }}{1}

which is correctly parsed by makeindex but gives two distinct entries for \LaTeX in the index. (IMHO a worse case than the first.)


Providing an empty definition

\@namedef{\@lst @um@}{}

gives the correct \jobname.idx

\indexentry{LaTeX@\texttt  {\textbackslash LaTeX}}{1}
\indexentry{LaTeX@\texttt  {\textbackslash LaTeX}}{1}

and no apparent issues seem to arise. I wonder, though, how safe it is. I traced the \@lst @um@ to the command \lst@CCECUse@ (line 1021 of listings.sty) which seems to have to do with listings extendedchars option (it is finally used in \lst@DefEC, line 998) but I'm lost there and don't really understand what the commands are for. My guess is that

\@namedef{\@lst @um@}{\leavevmode\kern\z@}

is some kind of precaution/safety net (but against what?) and since I'm getting apparently no issues in my real use case with currently about 150 listings the question is more out of curiosity: can something (and what) go wrong if I define

\@namedef{\@lst @um@}{}

Best Answer

I find \textcompwordmark a good idea, since it will enter horizontal mode exactly as \leavevmode does.

However, you can trick \index into not seeing the offending token:

\makeatletter
\newcommand*\cmdidx[1]{%
  \begingroup\let\lst@nolig\@empty
  \index{#1@\cmdappearance{#1}}\endgroup}
\makeatother
\newcommand*\cmdappearance[1]{\texttt{\textbackslash#1}}
\newcommand*\cmd[1]{\cmdappearance{#1}\cmdidx{#1}}

This produces the following .idx file:

\indexentry{LaTeX@\texttt  {\textbackslash LaTeX}}{1}
\indexentry{LaTeX@\texttt  {\textbackslash LaTeX}}{1}
Related Question