Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I'm trying to finish my attempt at having good looking unlimited line annotations for the listing environments, using tikz. The original problem was that indentation caused shifts in the positioning, so I changed the code to use the overlay feature.

My original question is here: How to add numerical (ala No Starch Press) listing notes without XeTeX?

Unfortunately, I'm still unable to figure out how to properly align them:

Update: the code is merged with Werner's version.

\documentclass{article}
\usepackage{libertine}
\usepackage[log-declarations=false]{xparse}
\usepackage[quiet]{fontspec}
\setromanfont[Ligatures={Common,TeX}]{Linux  Libertine O}
\setmainfont[Ligatures={Common,TeX}]{Linux  Libertine O}
\setmonofont[SmallCapsFont={Latin Modern Mono Caps}]{Latin Modern Mono Light}
\setsansfont{Linux Biolinum O}
\usepackage{xunicode}
\usepackage[x11names, rgb]{xcolor}
\usepackage{tikz}
\usetikzlibrary{snakes,arrows,shapes}
\usepackage{amsmath}
\usepackage{listings}

\newcommand{\mynumold}[1]{{\oldstylenums{#1}}}

\newcounter{lstNoteCounter}

\newcommand*\lnnum[2][,]{\tikz[baseline=(char.base), overlay]{
            \node[shape=circle,draw,inner sep=0.8pt,
                    fill=black, text=white, #1] (char) {\rmfamily\bfseries\footnotesize#2};}}

\newcommand*{\lnote}{\stepcounter{lstNoteCounter}\llap{{\lnnum{\thelstNoteCounter}}\hskip 4em}}
\newcommand*{\linenum}[1]{%
  \setbox0=\hbox{\rmfamily\bfseries\footnotesize#1}%
  \lnnum[anchor=west]{#1}\hspace*{\dimexpr1ex+\wd0\relax}%
}
\lstnewenvironment{annotatedcsource}[1][]
{
    \setcounter{lstNoteCounter}{0}
        \lstset{
            basicstyle=\ttfamily,
            frame=lines,
            framexleftmargin=0.5em,
            framexrightmargin=0.5em,
            basicstyle=\ttfamily\footnotesize,
            numberstyle=\normalsize\itshape\mynumold,
            backgroundcolor=\color{LemonChiffon1},
            showstringspaces=false,
            numbers=left,
            numbersep=2.5em,
            escapeinside={(*@}{@*)},#1}
        }
{}

\begin{document}
\pagestyle{empty}

\begin{annotatedcsource}[caption={RC4 blues}]
void
rc4_init(struct rc4_state *const state, const u_char *key, int keylen)
{
    u_char j;
    int i, k;

    /* Initialize state with identity permutation */
    for (i = 0; i < 256; i++)
        (*@\lnote@*)state->perm[i] = (u_char)i; 
    state->index1 = 0;
    (*@\lnote@*)state->index2 = 0;

    /* Randomize the permutation using key data */
    for (j = i = k = 0; i < 256; i++) {
        j += state->perm[i] + key[k]; 
        (*@\lnote@*)swap_bytes(&state->perm[i], &state->perm[j]);
        if (++k >= keylen)
            (*@\lnote@*)k = 0;
    }
\end{annotatedcsource}

Some source code doing whatever \linenum{1}, with line numbers and annotations \linenum{2}.

\begin{annotatedcsource}[caption={Hello blues}]
void hello(int times)
{
    int i;

    (*@\lnote@*)if (times > 4) {
        printf("Feeling chatty today?\n");
        return;
    }

    /* foobar */
    (*@\lnote@*)for (i = 0; i < times; i++) {
        printf("Hello %d!\n", i);
    }

    printf("Adios!\n");

    (*@\lnote@*)return;
}
\end{annotatedcsource}

Some source code doing whatever \linenum{1}, with line numbers and annotations \linenum{2}.

\end{document}

Output: incorrectly aligned output makes soze a sad panda

Desired output:

  • Properly aligned circled numbers to the left or right margins along the line numbers.
    • In other words, the circled numbers must appear outside of the code block box, to the right or left of the line numbers.
  • Any kind of source code listing should work fine, no indentation assumptions (please).
  • Properly spaced circled numbers for the actual references embedded in the text.

Like this:

enter image description here

share|improve this question
    
Somehow I can't get XeTeX working so I used PFDLaTeX by adding anchor=west, option to the tikzpicture and using say Some source code doing whatever \lnnum{1}\quad, with line numbers and annotations \lnnum{2}\quad. solves the second problem. Frankly, I couldn't understand what your first problem is. Note that snakes library is deprecated and replaced with decorations. –  percusse Apr 13 '12 at 19:53
    
percusse, I've clarified the desired output. It's the alignment and positioning of the circled numbers in the code block that bothers me. \quad seems like it should go into a new command defined for the references, to avoid duplicating the code. Re: snakes, correct, I took the minimal example template from an old file and forgot to remove it (obvious as there are no snake decorations anywhere). –  soze Apr 13 '12 at 20:04

2 Answers 2

up vote 6 down vote accepted

The following seems a bit of a hack, but provides the output you're after:

enter image description here

\documentclass{article}
%\usepackage{libertine}
%\usepackage[log-declarations=false]{xparse}
%\usepackage[quiet]{fontspec}
%\setromanfont[Ligatures={Common,TeX}]{Linux  Libertine O}
%\setmainfont[Ligatures={Common,TeX}]{Linux  Libertine O}
%\setmonofont[SmallCapsFont={Latin Modern Mono Caps}]{Latin Modern Mono Light}
%\setsansfont{Linux Biolinum O}
%\usepackage{xunicode}
\usepackage[x11names, rgb]{xcolor}
\usepackage{tikz}
%\usetikzlibrary{snakes,arrows,shapes}
\usepackage{amsmath}
\usepackage{listings}

%\newcommand{\mynumold}[1]{{\addfontfeature{Numbers=OldStyle}#1}}
\newcommand{\mynumold}[1]{{\oldstylenums{#1}}}

\newcounter{lstNoteCounter}

\newcommand*\lnnum[2][,]{\tikz[baseline=(char.base), overlay]{
            \node[shape=circle,draw,inner sep=0.8pt,
                    fill=black, text=white, #1] (char) {\rmfamily\bfseries\footnotesize#2};}}

\newcommand*{\lnote}{\stepcounter{lstNoteCounter}\llap{{\lnnum{\thelstNoteCounter}}\hskip 4em}}
\newcommand*{\linenum}[1]{%
  \setbox0=\hbox{\rmfamily\bfseries\footnotesize#1}%
  \lnnum[anchor=west]{#1}\hspace*{\dimexpr1ex+\wd0\relax}%
}
\lstnewenvironment{annotatedcsource}[1][]
{
    \setcounter{lstNoteCounter}{0}
        \lstset{
            basicstyle=\ttfamily,
            frame=lines,
            framexleftmargin=0.5em,
            framexrightmargin=0.5em,
            basicstyle=\ttfamily\footnotesize,
            numberstyle=\normalsize\itshape\mynumold,
            backgroundcolor=\color{LemonChiffon1},
            showstringspaces=false,
            numbers=left,
            numbersep=2.5em,
            escapeinside={(*@}{@*)},#1}
        }
{}

\begin{document}
\pagestyle{empty}

\begin{annotatedcsource}[caption={Foobar}]
void hello(int times)
{
    int i;

    (*@\lnote@*)if (times > 4) {
        printf("Feeling chatty today?\n");
        return;
    }

    /* foobar */
    (*@\lnote@*)for (i = 0; i < times; i++) {
        printf("Hello %d!\n", i);
    }

    printf("Adios!\n");

    (*@\lnote@*)return;
}
\end{annotatedcsource}

Some source code doing whatever \linenum{1}, with line numbers and annotations \linenum{2}.

\end{document}

numbersep=2.5em pushes the listing numbers 2.5em away from the listing edge. As such, this leaves room to push \lnote a length 4em from where it's called; into the gap. This is the hack I'm referring to: moving the TikZ node 4em (a fixed length), since one may require a length that depends on the indentation of the listing. \linenum{<num>} now sets \lnnum with anchor=west and pads it with some horizontal space to properly align any following text.


An alternative version that sets the TikZ node in a "less-hacky" way. :)

enter image description here

\documentclass{article}
%\usepackage{libertine}
%\usepackage[log-declarations=false]{xparse}
%\usepackage[quiet]{fontspec}
%\setromanfont[Ligatures={Common,TeX}]{Linux  Libertine O}
%\setmainfont[Ligatures={Common,TeX}]{Linux  Libertine O}
%\setmonofont[SmallCapsFont={Latin Modern Mono Caps}]{Latin Modern Mono Light}
%\setsansfont{Linux Biolinum O}
%\usepackage{xunicode}
\usepackage[x11names, rgb]{xcolor}
\usepackage{tikz}
%\usetikzlibrary{snakes,arrows,shapes}
\usepackage{amsmath}
\usepackage{listings}

\makeatletter
\let\insertnotenum\relax
\lst@Key{numbers}{none}{%
    \let\lst@PlaceNumber\@empty
    \lstKV@SwitchCases{#1}%
    {none&\\%
     left&\def\lst@PlaceNumber{\llap{\normalfont
                \lst@numberstyle{\thelstnumber}\kern\lst@numbersep\insertnotenum}}\\%
     right&\def\lst@PlaceNumber{\rlap{\normalfont
                \kern\linewidth \kern\lst@numbersep
                \lst@numberstyle{\thelstnumber}}}%
    }{\PackageError{Listings}{Numbers #1 unknown}\@ehc}}
\makeatother

%\newcommand{\mynumold}[1]{{\addfontfeature{Numbers=OldStyle}#1}}
\newcommand{\mynumold}[1]{{\oldstylenums{#1}}}

\newcounter{lstNoteCounter}

\newcommand*\lnnum[2][,]{\tikz[baseline=(char.base), overlay]{
            \node[shape=circle,draw,inner sep=0.8pt,
                    fill=black, text=white, #1] (char) {\normalfont\rmfamily\bfseries\footnotesize#2};}}
\makeatletter
\newcommand*{\lnote}{%
  \stepcounter{lstNoteCounter}%
  \global\def\insertnotenum{\lnnum[anchor=east,xshift=-1.5ex]{\thelstNoteCounter}%
  \global\let\insertnotenum\relax}%
}
\makeatother
\newcommand*{\linenum}[1]{%
  \setbox0=\hbox{\rmfamily\bfseries\footnotesize#1}%
  \lnnum[anchor=west]{#1}\hspace*{\dimexpr1ex+\wd0\relax}%
}
\lstnewenvironment{annotatedcsource}[1][]
{
    \setcounter{lstNoteCounter}{0}
        \lstset{
            basicstyle=\ttfamily,
            frame=lines,
            framexleftmargin=0.5em,
            framexrightmargin=0.5em,
            basicstyle=\ttfamily\footnotesize,
            numberstyle=\normalsize\itshape\mynumold,
            backgroundcolor=\color{LemonChiffon1},
            showstringspaces=false,
            numbers=left,
            numbersep=2.5em,
            escapeinside={(*@}{@*)},#1}
        }
{}

\begin{document}
\pagestyle{empty}

\begin{annotatedcsource}[caption={Foobar}]
void hello(int times)(*@\lnote@*)
{
    int i;
    (*@\lnote@*)
    if (times > 4) {
        printf("Feeling chatty today?\n");
        return;
    }

    (*@\lnote@*)/* foobar */
    for (i = 0; i < times; i++) {(*@\lnote@*)
        printf("Hello %d!\n", i);
    }

    printf("Adios!\n");
    (*@\lnote@*)
    return;
}
\end{annotatedcsource}

Some source code doing whatever \linenum{1}, with line numbers and annotations \linenum{2}.

\end{document}

The modifications include:

  1. A change in the way the numbers key-value is handled. The modification inserts \insertnotenum as part of the line number printing mechanism (or \lst@PlaceNumber);
  2. \insertnotenum is a self-destruct macro that removes itself after it is used;
  3. Use (*@\lnote@*) before the line that you want referenced, so that the line number of the following line is appropriately marked.

Note that this version now sets \lnote at the same horizontal distance, regardless of the code indentation.

share|improve this answer

Like so:

\documentclass{article}
\usepackage[OT1]{fontenc}
\usepackage{lmodern}
\usepackage{beramono}
\usepackage[x11names, rgb]{xcolor}
\usepackage{tikz}
\usetikzlibrary{snakes,arrows,shapes}
\usepackage{amsmath}
\usepackage{listings}

%\newcommand{\mynumold}[1]{{\addfontfeature{Numbers=OldStyle}#1}}
\newcommand{\mynumold}[1]{{\oldstylenums{#1}}}

\newcounter{lstNoteCounter}

\newcommand*\lnnum[1]{\tikz[baseline=(char.base), overlay]{
            \node[shape=circle,draw,inner sep=0.8pt,
                    fill=black, text=white, xshift=-4ex] (char) {\rmfamily\bfseries\footnotesize#1};}}

\newcommand*{\lnote}{\stepcounter{lstNoteCounter}\llap{{\lnnum{\thelstNoteCounter}}\hskip 3em}}

\lstnewenvironment{annotatedcsource}[1][]
{
    \setcounter{lstNoteCounter}{0}
        \lstset{
            basicstyle=\ttfamily,
            frame=lines,
            framexleftmargin=0.5em,
            framexrightmargin=0.5em,
            basicstyle=\ttfamily\footnotesize,
            numberstyle=\normalsize\itshape\mynumold,
            backgroundcolor=\color{LemonChiffon1},
            showstringspaces=false,
            numbers=left,
            numbersep=8ex,
            escapeinside={(*@}{@*)},#1}
        }
{}

\begin{document}
\pagestyle{empty}

\begin{annotatedcsource}[caption={Foobar}]
void hello(int times)
{
    int i;

    (*@\lnote@*)if (times > 4) {
        printf("Feeling chatty today?\n");
        return;
    }

    /* foobar */
    (*@\lnote@*)for (i = 0; i < times; i++) {
        printf("Hello %d!\n", i);
    }

    printf("Adios!\n");

    (*@\lnote@*)return;
}
\end{annotatedcsource}

Some source code doing whatever \lnnum{1}, with line numbers and annotations \lnnum{2}.

\end{document}

enter image description here

The "trick" is to pass some numbersep= value to listings and use the xshift= option for the TikZ node.

(I have changed some packages in order to use pdflatex instead of xelatex, which I currently have not available. However, that should not be the point).

Is that what you are after?

share|improve this answer
    
Hm... seems that Werner basically had the same idea – and quick as he always is, he was about 3 Minutes earlier :-) –  Daniel Apr 13 '12 at 20:34
    
Werner is epic. Thank you both, I'm checking this. Bear with my tikz absolute rookie status, I bet this one was a tad stupid, but I also bet some people will appreciate the annotation hack for listings. –  soze Apr 13 '12 at 20:43

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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