7

Is it possible to add non-number characters on the left of a code block?

Specifically, I want to add ... to correspond with some source code (see image).

enter image description here

I am currently using commands to stop / start the line numbers. Here is the code:

\documentclass[12pt]{article}

\usepackage[a4paper, margin = 20mm, top = 30mm, bottom = 25mm]{geometry}
\usepackage{xcolor, soul}
\usepackage{listings}

\definecolor{codebackground}{rgb}{0.95, 0.95, 0.92}

\lstset
{
    frame            = tb, % hrule above and below
    keepspaces       = true,
    columns          = flexible,
    basicstyle       = {\normalsize\ttfamily},
    escapeinside     = {(*@}{@*)}, % for escaping
    backgroundcolor  = \color{codebackground},
    showstringspaces = false,
    language         = C,
    keywordstyle     = \color{blue},
    stringstyle      = \color{red},
    commentstyle     = \color{teal},
    numbers          = left, % {none, left, right}
    firstnumber      = 1,
    numberstyle      = \scriptsize\color{black},
    numbersep        = 5pt
}

\let\origthelstnumber\thelstnumber
\makeatletter
% for stopping line numbers
\newcommand*\stopnumber
{
    \lst@AddToHook{OnNewLine}
    {
        \let\thelstnumber\relax
        \advance\c@lstnumber-\@ne\relax
    }
}
% for starting line numbers
\newcommand*\startnumber[1]
{
    \setcounter{lstnumber}{\numexpr#1-1\relax}
    \lst@AddToHook{OnNewLine}
    {
        \let\thelstnumber\origthelstnumber
        \refstepcounter{lstnumber}
    }
}
\makeatother

\begin{document}
\begin{lstlisting}[firstnumber = 456]
int third_function(const int c)
{
    /* error checking */
    if(c < 0)
    {
        return;
    } (*@ \stopnumber @*)

    ...
    (*@ \startnumber{512} @*)
    /* other stuff */
    int d = 4;
    char* word = "third function"; (*@ \stopnumber @*)

    ...
    (*@ \startnumber{678} @*)
    return (c * d);
}
\end{lstlisting}
\end{document}

2 Answers 2

6

Here's a simple solution that makes use of the mathescape option to modify the counter and printing of line numbers. You could also use escapeinside if you want to use different escape characters than the standard $...$.

\documentclass{article}
\usepackage{listings}

\lstset{basicstyle=\ttfamily, language=c, numbers=left}

\makeatletter
\let\orig@lstnumber=\thelstnumber

\newcommand\lstsetnumber[1]{\gdef\thelstnumber{#1}}
\newcommand\lstresetnumber{\global\let\thelstnumber=\orig@lstnumber}
\makeatother

\begin{document}
\begin{lstlisting}[firstnumber=100, mathescape=true]
int foo(int x) {
    // some code$\lstsetnumber{\ldots}$
    ...$\lstresetnumber\setcounter{lstnumber}{199}$

    // more code with extra spacing$\lstsetnumber{}$
    $\lstsetnumber{\ldots}$
    ...$\lstsetnumber{}$
    $\lstresetnumber\setcounter{lstnumber}{299}$

    return x;
}
\end{lstlisting}
\end{document}

enter image description here

The \lstsetnumber macro redefines the output macro for line numbers, in the above example to the fixed value \ldots. \lstresetnumber resets the output to the standard definition.

Note that you have to subtract 1 from the next line number if you modify the line counter via \setcounter{lstnumber} as listings automatically increases the counter on the next line.

The code for adding extra empty lines is a bit clumsy, not sure how to add vertical space in a more flexible way here.

1
  • I have updated the question to show what approach I am using, as this method uses mathescape Feb 24, 2019 at 22:25
5

So here's another, easier to use approach that is syntactically the same as in your example:

\documentclass[12pt]{article}

\usepackage[a4paper, margin = 20mm, top = 30mm, bottom = 25mm]{geometry}
\usepackage{xcolor, soul}
\usepackage{listings}

\definecolor{codebackground}{rgb}{0.95, 0.95, 0.92}
\lstset{ ... }

\let\origthelstnumber\thelstnumber
\makeatletter

\renewcommand\thelstnumber{%
    \ifnum\value{lstnumber}>0
        \origthelstnumber
    \else
        \ifnum\value{lstnumber}=-1
            \ldots
        \fi
    \fi
}

% for starting line numbers
\newcommand*\startnumber[1]{%
    \setcounter{lstnumber}{\numexpr#1-1\relax}%
}

% for stopping line numbers
\newcommand*\stopnumber{%
    \startnumber{-2}%
}

\makeatother

\begin{document}
\begin{lstlisting}[firstnumber = 456]
int third_function(const int c)
{
    /* error checking */
    if(c < 0)
    {
        return;
    } (*@ \stopnumber @*)

    ...
    (*@ \startnumber{512} @*)
    /* other stuff */
    int d = 4;
    char* word = "third function"; (*@ \stopnumber @*)

    ...
    (*@ \startnumber{678} @*)
    return (c * d);
}
\end{lstlisting}
\end{document}

enter image description here

In your code you are using \lst@AddToHook multiple times to change \thelstnumber. This is a bit problematic because adding hooks is accumulative, i.e. it adds the same commands everytime you use it. Also, the listings documentation explicitly doesn't guarantee that the order of hook execution is the same as the order of definition.

My approach is based on redefining \thelstnumber only once and (mis)using negative line numbers for the ellipses in your code. Positive numbers are printed as usual, -1 denotes \ldots, other negative numbers don't yield any output at all. So in \stopnumbers we just init lstnumber with -2 to map the following sequence of -2, -1, 0 to empty, \ldots, empty.

In case you'd want a short ellipsis without empty lines around, you could define a new command that inits lstnumber with -1.

You must log in to answer this question.

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