[Tex/LaTex] Label inside a code listing

cross-referencinglistings

Does anybody know how to use labels inside a code listing that it looks something similarly like this:

labels inside a code listening

I know that it's easier to use the line number but I prefer using some labels.

Best Answer

The following example defines a new counter llabel for the listings' markings. Package pifont is used to get the symbols, therefore the markings are limited to the range of 1 upto 10. Otherwise, package tikz can be used to draw own symbols without this limitation.

Macro \llabel takes a symbolic name that can be used later for \ref. It needs to be executed right at the beginning of the line, because then its position is known. The label is put to the left with the distance \llabelsep from the listings line number. To get \llabel executed inside the listings the mathescape feature of package listings is used.

\documentclass{article}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{pifont}

\makeatletter

\AtBeginDocument{%
  % Counter `lstlisting' is not defined before `\begin{document}'
  \newcounter{llabel}[lstlisting]%
  \renewcommand*{\thellabel}{%
    \ifnum\value{llabel}<0 %
      \@ctrerr
    \else
      \ifnum\value{llabel}>10 %
        \@ctrerr
      \else
        \protect\ding{\the\numexpr\value{llabel}+201\relax}%
      \fi
    \fi
  }%
}
\newlength{\llabelsep}
\setlength{\llabelsep}{5pt}

\newcommand*{\llabel}[1]{%
  \begingroup
    \refstepcounter{llabel}%
    \label{#1}%
    \llap{%
      \thellabel\kern\llabelsep
      \hphantom{\lst@numberstyle\the\lst@lineno}%
      \kern\lst@numbersep
    }%
  \endgroup
}
\makeatother

\begin{document}
\setcounter{section}{2}
\subsection{Hello, world!}  
\begin{lstlisting}[
  language=C,
  numbers=left,
  numberstyle=\small\itshape,
  stepnumber=1,
  frame=lines,
  backgroundcolor=\color{yellow!25},
  mathescape,
]   
$\llabel{header}$#include <stdio.h>

/* This is a comment */
$\llabel{main}$int main()
{
$\llabel{hello}$    printf("Hello, world!\n");
$\llabel{exit}$    return 0;
}

/* Another function */
$\llabel{foobar}$int foobar()
{
    return 0;
}
\end{lstlisting}
We first include the \verb|stdio.h| header file \ref{header}.
We then declare the \verb|main| function \ref{main}.
We then print ``Hello, world!'' \ref{hello}.
Finally, we return value 0 \ref{exit}.
Another function \verb|foobar| is defined in \ref{foobar}.
\end{document}

Result

It is also possible to have source files that keep working by using special markers and using option

escapeinside={/@}{@/}

instead of mathescape:

/*@\llabel{header}@*/#include <stdio.h>

/* This is a comment */
/*@\llabel{main}@*/int main()
{
/*@\llabel{hello}@*/    printf("Hello, world!\n");
/*@\llabel{exit}@*/    return 0;
}

/* Another function */
/*@\llabel{foobar}@*/int foobar()
{
    return 0;
}

Putting \llabel at end of line

The following variant allows to put \llabel anywhere on the source line, also the end of line, where it does not disrupt the formatting. For this purpose the line marker is also remembered by a label with the line number in its name. The marker is then put along the line number hooking into numberstyle:

\documentclass{article}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{pifont}

\makeatletter
\AtBeginDocument{%
  % Counter `lstlisting' is not defined before `\begin{document}'
  \newcounter{llabel}[lstlisting]%
  \renewcommand*{\thellabel}{%
    \ifnum\value{llabel}<0 %  
      \@ctrerr
    \else
      \ifnum\value{llabel}>10 %
        \@ctrerr
      \else
        \protect\ding{\the\numexpr\value{llabel}+201\relax}%
      \fi
    \fi  
  }%   
}   
\newlength{\llabelsep}
\setlength{\llabelsep}{5pt}

\newcommand*{\llabel@name}{%
  llabel\the\value{lstlisting}.\the\lst@lineno
}
\newcommand*{\llabel}[1]{%
  \begingroup
    \refstepcounter{llabel}%
    \label{#1}%
    \label{\llabel@name}%
  \endgroup
}
\newcommand*{\llabelput}{%
  \@ifundefined{r@\llabel@name}{%
  }{%
    \ref{\llabel@name}%
    \kern\llabelsep
  }%
}   
\makeatother

\begin{document}
\setcounter{section}{2}
\subsection{Hello, world!}
\begin{lstlisting}[
  language=C,
  numbers=left,
  numberstyle=\small\itshape\llabelput,
  stepnumber=1,
  frame=lines,
  backgroundcolor=\color{yellow!25},
  columns=flexible,
  escapeinside={/*@}{@*/},
]
#include <stdio.h> /*@\llabel{header}@*/

/* This is a comment */
int main() /*@\llabel{main}@*/
{
    printf("Hello, world!\n"); /*@\llabel{hello}@*/
    return 0; /*@\llabel{exit}@*/
}

/* Another function */
int foobar() /*@\llabel{foobar}@*/
{
    return 0;
}   
\end{lstlisting}
We first include the \verb|stdio.h| header file \ref{header}.
We then declare the \verb|main| function \ref{main}.
We then print ``Hello, world!'' \ref{hello}.
Finally, we return value 0 \ref{exit}.
Another function \verb|foobar| is defined in \ref{foobar}.
\end{document}

This should also work with an external file instead of the embedded listings.

Related Question