5

I'd like to highlight single words (or phrases) in the comments of C files displayed by the listings package. Using the emph key does not work in comments (apparently listings is too intelligent in case a language is set): words in comments (and string literals) don't seem to be considered - only (key)words in the normal code are.

A workaround that is based on the literate option from this answer is shown below.

I am looking for a solution that works without modifying the actual source code and it should work with lstinputlisting as well.

\documentclass{article}
\usepackage{listings,xcolor}
\lstset{
   language=C,
}
\begin{document}
% \begin{lstlisting}[emph={Hello},emphstyle=\underbar]
\begin{lstlisting}[literate={Hello}{{{\color{red}Hello}}}5]
/*
 * Hello World Program
 */
// Hello

#include <stdio.h>

int main (void) {
  printf("Hello World!\n");
  int Hello = 0;
  int hello = 0;
  return Hello;
}
\end{lstlisting}
\end{document}

The main problem with the workaround is the spacing of the matched characters.

  • The problem is that listings does not adjust the tracking for literate. It left the job to you so that you can emphasis the word more freely. Check also mathescape and what follows for an alternative approach. By the way, since listings pretends to be an IDE, it should not -- and it did not -- highlight keywords in comments. If a comment is so important that all readers must recognize it, typeset outside the lstlisting, where you are really free. – Symbol 1 Nov 11 '15 at 16:13
  • Your comment did only restate what I know already. I am aware of the mathescape alternative (and the other escaping mechanisms of listings), but as I wrote in the question I would prefer to not change the included source code, hence these options are NO alternatives. That listings understands the comments is correct and indeed part of the problem in this case... – stefanct Nov 13 '15 at 16:11
  • 1
    My bad. When I saw "actual source code" I thought it was the source code of listings itself. How about columns=fullflexible,keepspaces? Or you might want to know the default value of basewidth and mimic the behavior. – Symbol 1 Nov 13 '15 at 16:32
1

The problem in your literate text are the extra braces in the replacement text.

When the second parameter of the literate key is parsed, each LaTeX token or {...}-group—let's call them item—is considered a replacement of one character of the output. Thus, {{\color{red}Hello}} is parsed as a single item and results in the wrong spacing. The 5 at the end only tells it to assume the overall replaced text has a width of 5 times the width of a single character.

So the solution is to split the replacement text over 5 (printable) items:

{\unskip\bgroup\color{red}}Hello{\unskip\egroup}

Note that the first and last items are considered output characters now, too. As the \bgroup\color{red} and \egroup parts are not printable, there is no space occupied in the output for them. However, listings inserts an additional glue before each item, which has to be removed by \unskip to get the proper overall spacing.

See the difference in output of the following example:

\documentclass{article}
\usepackage{filecontents}
\begin{filecontents*}{\jobname.c}
/*
 * Hello World Program
 */
// Hello

#include <stdio.h>

int main (void) {
  printf("Hello World!\n");
  int Hello = 0;
  int hello = 0;
  return Hello;
}
\end{filecontents*}

\usepackage{listings,xcolor}
\lstset{
    language=C,
}

\begin{document}
\lstinputlisting[literate={Hello}{{{\color{red}Hello}}}5]{\jobname.c}
\lstinputlisting[literate={Hello}{{\unskip\bgroup\color{red}}Hello{\unskip\egroup}}5]{\jobname.c}
\end{document}

enter image description here


I'd like to add a note on why the emph version didn't work. listings has different ways to define new delimited parts of the input text. Relevant in your example are the morecomment and morestring keys. Each of them is available in a non-starred (morecomment=) and in a starred (morecomment=*) version.

When the former version is used, the text between the delimiters is not scanned for other classes like keywords or emphasized words. However, for the starred version, the text is scanned for other classes and appropriate styles are applied. In the case of predefined language styles those delimited classes are defined as the non-starred version. So if you want the text replacement inside them, you have to redefine them as starred version.

Note that it doesn't seem to be possible to choose between class styles that are applied and those that are not applied inside delimited environments. To work around the unwanted side effect of keywords also being highlighted in comments or strings, we can save the currently active style before keyword style is applied and apply it only when outside of special delimited environments:

\documentclass{article}
\usepackage{filecontents}
\begin{filecontents*}{\jobname.c}
/*
 * Hello World Program
 */
// Hello

#include <stdio.h>

int main (void) {
  printf("Hello World!\n");
  int Hello = 0;
  int hello = 0;
  World greetings;
  return Hello;
}
\end{filecontents*}

\usepackage{listings,xcolor}

\makeatletter
\lstset{
    language=C,
    emph={Hello},
    emphstyle=\emphstyle,
    morekeywords={World},
    keywordstyle=\keywordstyle,
    morecomment=*[l][\special@on\color{gray}\itshape]{//},
    morecomment=*[s][\special@on\color{gray}\itshape]{/*}{*/},
    morestring=*[b][\special@on\color{green}]",
}

\newif\ifspecial@env@
\def\special@on{\global\special@env@true}
\def\special@off{\global\special@env@false}

\lst@AddToHook{DetectKeywords}{%
    \global\let\last@lst@thestyle=\lst@thestyle
}

\def\emphstyle{%
    \last@lst@thestyle
    \aftergroup\special@off
    \underbar
}

\def\keywordstyle{%
    \ifspecial@env@
        \last@lst@thestyle
    \else
        \color{blue}%
    \fi
    \aftergroup\special@off
}
\makeatother

\begin{document}
\lstinputlisting{\jobname.c}
\end{document}

enter image description here

Here the World keyword is only highlighted when outside of comments and strings, while the underline style for the emphasized word Hello is applied everywhere.

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.