56

This question led to a new package:
lstlinebgrd (lstaddons bundle)

Friends, consider the following example using the listings package:

\documentclass{article}

\usepackage[T1]{fontenc}
\usepackage{beramono}
\usepackage{listings}
\begin{document}

\begin{lstlisting}[language=C,basicstyle=\ttfamily]
/**
* Prints Hello World.
**/
#include <stdio.h>

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

\end{document}

The output is as we expect:

Hello World

I was wondering if we could add something I know as zebra effect (pardon my possible misinterpretation of this term). I know this as a common style used in tables, where rows alternate colors, hence the name zebra. The previous example should look something like this:

Hello Zebra

Does anyone have any ideas?

4
  • I’m sorry that I can’t really help, but I googled “listings alternatin color“ and found a post in comp.text.tex with a solution for fancyverb
    – Tobi
    May 24, 2011 at 18:19
  • @Tobi interesting approach! I'm gonna take a look, probably we can come up with something similar for listings. =) May 24, 2011 at 19:08
  • I had a look at listings and can’t find something similar to \FancyVerbFormatLine, sorry
    – Tobi
    May 24, 2011 at 20:29
  • 1
    I now released my solution as lstlinebgrd package on CTAN as part of the lstaddons bundle. May 3, 2012 at 15:10

1 Answer 1

59

The following code is giving a zebra effect. See below for a description.

\documentclass{article}

\usepackage[T1]{fontenc}
\usepackage{beramono}
\usepackage{listings}
\usepackage{xcolor}

\newcommand\realnumberstyle[1]{}

\makeatletter
\newcommand{\zebra}[3]{%
    {\realnumberstyle{#3}}%
    \begingroup
    \lst@basicstyle
    \ifodd\value{lstnumber}%
        \color{#1}%
    \else
        \color{#2}%
    \fi
        \rlap{\hspace*{\lst@numbersep}%
        \color@block{\linewidth}{\ht\strutbox}{\dp\strutbox}%
        }%
    \endgroup
}
\makeatother


\begin{document}

\begin{lstlisting}[language=C,basicstyle=\ttfamily,numberstyle=\zebra{green!35}{yellow!35},numbers=left]
/**
* Prints Hello World.
**/
#include <stdio.h>

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

\bigskip

% If you want to make the bars shorter you need to use a `minipage` or similar environment:
\noindent
\begin{minipage}{.45\textwidth}
\begin{lstlisting}[language=C,basicstyle=\ttfamily,numberstyle=\zebra{green!25}{white},numbers=left]
/**
* Prints Hello World.
**/
#include <stdio.h>

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

\end{document}

Result

My first idea was to use backgroundcolor with a conditional depending on the line number, but it seem only be used for boxing the whole listing. After studying the source code of listings a while to learn how to hack the line processing I came to the idea to (mis-)use the numberstyle for this. It is called with the line number register as argument and is typeset inside a \llap in a distance of numbersep. I just put a low-level command of \colorbox (xcolor package) called \color@block inside a \rlap to place the colored bars. This requires numbers=left. If you don't want lines numbers keep \realnumberstyle how it is, or redefine it otherwise.

If you only want one color you can use the following definition and numberstyle=\zebra{<color>}.

\newcommand{\zebra}[2]{%
    {\realnumberstyle{#2}}%
    \begingroup
    \lst@basicstyle
    \ifodd\value{lstnumber}%
        \color{#1}%
        \rlap{\hspace*{\lst@numbersep}%
        \color@block{\linewidth}{\ht\strutbox}{\dp\strutbox}%
        }%
    \fi
    \endgroup
}

In the meantime I had a second look on the listings source code, this time to the numbers key. The following package hacks this key to inserts its own handler without interfering with the numbering like the code above. It then provides a set of new listings keys/options called linebackground..., to the set background color but also the height, depth, width, separation or even the drawing command.

You can set the color using linebackgroundcolor=<some color code> which can hold a conditional. The default background color is -., i.e. the opposite (-) of the current foreground color (aliased as . by xcolor). The box drawing code follows after color code inside braces, so if not background is wanted (not even a white one) than the color code can simply gobble the box drawing code as an argument.

% lstlinebgrd.sty
\RequirePackage{listings}
\RequirePackage{xcolor}

% 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
}

Usage:

\documentclass{article}

\usepackage{lstlinebgrd}

\begin{document}

\begin{lstlisting}[language=C,basicstyle=\ttfamily,linebackgroundcolor={\ifodd\value{lstnumber}\color{green}\fi}]
/**
* Prints Hello World.
**/
#include <stdio.h>

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

\begin{lstlisting}[language=C,basicstyle=\ttfamily\Large,linebackgroundcolor={\ifodd\value{lstnumber}\color{green}\else\color{yellow}\fi},linebackgroundsep=1em,linebackgroundwidth=18em]
/**
* Prints Hello World.
**/
#include <stdio.h>

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

\begin{lstlisting}[language=C,basicstyle=\ttfamily\tiny,linebackgroundcolor={\color{blue!\the\numexpr 5*\value{lstnumber}\relax}},linebackgroundheight=1.7ex,linebackgrounddepth=.4ex]
/**
* Prints Hello World.
**/
#include <stdio.h>

int main(void) {
   printf("Hello World!");
   return 0;
}
/**
* Prints Hello World.
**/
#include <stdio.h>

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


\end{document}

Result

11
  • Wow Martin, that's a pretty impressive code! I thought about a conditional depending on the line number too, but I couldn't go any further. Your answer is highly technical and very didatic, thanks a million! Hats off to ya! =D May 24, 2011 at 22:03
  • @Paulo: I just figured out a way to add this as normal listings settings. I will publish the code for that tomorrow. May 25, 2011 at 12:02
  • 1
    Awesome! I'm very curious! May 26, 2011 at 17:10
  • 1
    amazing code, I bow before your skills! In my first code sketch I was trying to get the current line number and check it, but I wasn't unable of finding it. Besides, I was tempted to hack the basicstyle key, but it happened to be only a big block for the whole listing. Again, your code is highly technical and didatic, thanks! BTW, I'm not sure if possible, but have you considered including this new package in the TeX-SX initiave? I'd be great! May 27, 2011 at 8:55
  • 2
    Really slick. Can you update it to handle line breaks/wrapping as well? I can't figure it out. Thanks! Nov 13, 2012 at 2:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .