# Highlighting line ranges in a lstlisting: partial solution, but expansion issues

In my quest for a better solution for How to change appearance of a particular line when using \lstincludelisting, I came across the linebackground= option Martin Scharrer added to the listings package in his answer to Creating a zebra effect using listings.

The basic idea is to have a macro

% \btLstHL<overlay spec>{range list}
\newcommand<>{\btLstHL}[1]{%
\only#2{\btIfInRange{\value{lstnumber}}{#1}{\color{blue!30}}{}}%
}%


that expands to a \color{blue!30} if the current line fits into the range or to \empty (?) otherwise. This is then used together with linebackground

\begin{lstlisting}[language=C, gobble=6,linebackgroundcolor={%
\btLstHL{4}%
\btLstHL<1>{1-2,5-6}%
\btLstHL<2>{7}%
}]
/**
* Prints Hello World.
**/
#include <stdio.h>

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


This, however, does not work as intended, as all nonselected lines now get a white background:

The culprit, I guess, is in the implementation of lstlinebackground and how btLstHL gets expanded in its processing.:

    \lst@Key{linebackgroundcolor}{}{%
\def\lst@linebgrdcolor{#1}%
}
\newcommand{\lst@linebgrd}{%
\ifx\lst@linebgrdcolor\empty\else  % PROBLEM: for \btLstHL does not expand to \empty ???
\rlap{%
\lst@basicstyle
\color{-.}% By default use the opposite (-) of the current color (.) as background
\lst@linebgrdcolor{%
\kern-\dimexpr\lst@linebgrdsep\relax%
\lst@linebgrdcmd{\lst@linebgrdwidth}{\lst@linebgrdheight}{\lst@linebgrddepth}%
}%
}%
\fi
}


Apparently, \btLstHL expands to "nothing", but not \empty for nonselected lines, which causes \lst@linebgrd to highlight the background, but with its default color, which is the opposite of the current text color (black).

And at this point I am lost :-(

So how to define \btLstHL that it expands to \empty if the line is not within the given range?

Complete MWE:

\documentclass{beamer}

% beamer class setup
\usecolortheme{rose}

% we use UTF8
\usepackage[utf8]{inputenc}
% font setup
\usepackage[T1]{fontenc}
\usepackage[scaled=0.85]{beramono}
\usepackage{xcolor,listings, pgffor}

\makeatletter

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% \btIfInRange{number}{range list}{TRUE}{FALSE}
%
% Test if int number <number> is element of a (comma separated) list of ranges
% (such as: {1,3-5,7,10-12,14}) and processes <TRUE> or <FALSE> respectively

\newcount\bt@rangea
\newcount\bt@rangeb

\newcommand\btIfInRange[2]{%
\global\let\bt@inrange\@secondoftwo%
\edef\bt@rangelist{#2}%
\foreach \range in \bt@rangelist {%
\afterassignment\bt@getrangeb%
\bt@rangea=0\range\relax%
\pgfmathtruncatemacro\result{ ( #1 >= \bt@rangea) && (#1 <= \bt@rangeb) }%
\ifnum\result=1\relax%
\breakforeach%
\global\let\bt@inrange\@firstoftwo%
\fi%
}%
\bt@inrange%
}
\newcommand\bt@getrangeb{%
\@ifnextchar\relax%
{\bt@rangeb=\bt@rangea}%
{\@getrangeb}%
}
\def\@getrangeb-#1\relax{%
\ifx\relax#1\relax%
\bt@rangeb=100000%   \maxdimen is too large for pgfmath
\else%
\bt@rangeb=#1\relax%
\fi%
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% \btLstHL<overlay spec>{range list}
%
\newcommand<>{\btLstHL}[1]{%
\only#2{\btIfInRange{\value{lstnumber}}{#1}{\color{blue!30}}{}}%
}%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% \btInputEmph<overlay spec>[listing options]{range list}{file name}
%
\newcommand<>{\btLstInputEmph}[3][\empty]{%
\only#4{%
\lstset{linebackgroundcolor=\btLstHL{#2}}%
\lstinputlisting{#3}%
}% \only
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% lstlinebgrd.sty
%% see: http://tex.stackexchange.com/questions/18969/creating-a-zebra-effect-using-listings/18989#18989
%%
%% This small package is not yet published/not commonly available.
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Patch line number key to call line background macro
\lst@Key{numbers}{none}{%
\def\lst@PlaceNumber{\lst@linebgrd}%
\lstKV@SwitchCases{#1}%
{none&\\%
left&\def\lst@PlaceNumber{\llap{\normalfont
\lst@numberstyle{\thelstnumber}\kern\lst@numbersep}\lst@linebgrd}\\%
right&\def\lst@PlaceNumber{\rlap{\normalfont
\kern\linewidth \kern\lst@numbersep
\lst@numberstyle{\thelstnumber}}\lst@linebgrd}%
}{\PackageError{Listings}{Numbers #1 unknown}\@ehc}}

% New keys
\lst@Key{linebackgroundcolor}{}{%
\def\lst@linebgrdcolor{#1}%
}
\lst@Key{linebackgroundsep}{0pt}{%
\def\lst@linebgrdsep{#1}%
}
\lst@Key{linebackgroundwidth}{\linewidth}{%
\def\lst@linebgrdwidth{#1}%
}
\lst@Key{linebackgroundheight}{\ht\strutbox}{%
\def\lst@linebgrdheight{#1}%
}
\lst@Key{linebackgrounddepth}{\dp\strutbox}{%
\def\lst@linebgrddepth{#1}%
}
\lst@Key{linebackgroundcmd}{\color@block}{%
\def\lst@linebgrdcmd{#1}%
}

% Line Background macro
\newcommand{\lst@linebgrd}{%
\ifx\lst@linebgrdcolor\empty\else
\rlap{%
\lst@basicstyle
\color{-.}% By default use the opposite (-) of the current color (.) as background
\lst@linebgrdcolor{%
\kern-\dimexpr\lst@linebgrdsep\relax%
\lst@linebgrdcmd{\lst@linebgrdwidth}{\lst@linebgrdheight}{\lst@linebgrddepth}%
}%
}%
\fi
}

\makeatother

\begin{document}

\begin{frame}[fragile]{Problem}
\begin{alertblock}{A cool app; unfortunately it has a white background}
\begin{lstlisting}[language=C, gobble=6,linebackgroundcolor={%
\btLstHL{4}%
\btLstHL<1>{1-2,5-6}%
\btLstHL<2>{7}%
}]
/**
* Prints Hello World.
**/
#include <stdio.h>

int main(void) {
printf("Hello World!");
return 0;
}
\end{lstlisting}
\end{frame}

\end{document}

-
It looks like you don't want \btLstHL to expand to \empty. If you change your MWE to use \empty as the linebackgroundcolor you get the same problem with white lines. To make it expand to \empty, you would only need to place that in the 4th argument, instead of leaving it actually empty as it is now. –  Roelof Spijker Oct 11 '11 at 12:12
@wh1t3: Thanks, but I don't get it. Before posting the question I had tried a couple of things with the 4th argument to \btIfInRange (among them {}, {\empty}, \empty) without any success. On the other hand, if one writes backgroundcolor={} this works as expected (that is, the \ifx\lst@linebgrdcolor\empty in \lst@linebgrd takes the if-branch), so I concluded that {} vs. \empty is not the root of the issue here. I assume that \ifx does not really expand its argument, but merrily checks for some kind of equivalence or so :-( –  Daniel Oct 11 '11 at 12:53
It appears that \ifx goes to the true branch if and only if Both arguments are macros AND their first level expansion is identical AND they have the same status with respect to \long and \outer. Source. The first level expansion is most likely where it goes wrong. Not sure how to remedy that, yet. –  Roelof Spijker Oct 11 '11 at 14:46
Some debuggging reveals that lst@linebgrdcolor is never empty - its equal to the linebackround specification you gave. Maybe the problem is in \lst@Key? I added \show\lst@linebgrdcolor% right above the ifx test in the definition of \lst@linebgrd. Also, the true branch of your conditional doesn't have a token. Not sure if that's legal or not. –  Justin Bailey Oct 11 '11 at 15:15
You may have a look at How to make overlay still work inside lstlisting environment? where I show how to highlight some lines by overlaying the others. –  Tobi Oct 17 '11 at 12:49

After understanding better what \ifx actually does (thanks to whlt3!) I came to the conclusion that I cannot define \btLstHL in a way that it does first-level expand to \empty.

My current workaround solution is to go another route and redefine \lst@linebgrdcmd in \btLstHL so it becomes a no-op for nonselected lines:

  % \btLstHL<overlay spec>{range list}
\newcommand<>{\btLstHL}[1]{%
\only#2{\btIfInRange{\value{lstnumber}}{#1}%
{\color{blue!30}}%
{\def\lst@linebgrdcmd####1####2####3{}}% define as no-op
}%
}%


Note: To use this without beamer, just remove the <> from the \newcommand and the \only#2 command including its curly braces.

This way I do get the intended result. However, the solution is somewhat "hacky". I am still open for better approaches.

-
Is it possible to have this macro working for classical documents like Article and not only for Beamer ones ? –  projetmbc Oct 11 '11 at 22:44
@projetmbc Sure, just remove all the <> stuff, the only's, and the beamer stuff and include pfg. –  Roelof Spijker Oct 12 '11 at 7:04
Despite other suggestions I have now accepted my own answer :-/ –  Daniel Oct 31 '11 at 11:53