5

I was wondering if it was possible to make the listings package show a return symbol like "" when a line break occurs.

With default line numbering settings this is not needed, as the line break creates a blank space between two consecutive line numbers, but it may be useful if one is using interval line numbering (e.g. 1-5-10-15-20), where only few lines are numbered.

My idea was redefining the line break macro searching for "ifbreaklines" (the option name) in lstmisc.sty, but I can't find any implementation guide and the file has no comments.

EDIT: tokens added with prebreak={} and postbreak={} are part of the lines and are copied along with it, so they are not an ideal solution (and you can't use unicode characters or macros there)

MWE if someone would like to help and experiment:

\documentclass[a4paper,12pt]{article}
\usepackage{xcolor}
\usepackage{listings}

\lstset{
    basicstyle=\footnotesize\ttfamily,
    breakatwhitespace=true,  
    breaklines=true,         
    firstnumber=1,
    frame=leftline,       
    keepspaces=true,         
    numbers=left,            
    showspaces=false,        
    showstringspaces=false,  
    showtabs=false,          
    stepnumber=1,
    language=java,
    numberstyle=\color{darkgray},
    commentstyle=\color{gray},     
    stringstyle=\color{red},
    keywordstyle=\color{blue},
    rulecolor=\color{black}
}

\begin{document}

\begin{lstlisting}[caption={You can easily recognize line breaks}]
public int nextInt(int n) {
 if (n<=0)
  throw new IllegalArgumentException("n is not positive");


 // this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very long line
 if ((n & -n) == n)  // i.e., n is a power of 2
  return (int)((n * (long)next(31)) >> 31);

 int bits, val;
 do {
  bits = next(31);
  val = bits % n;
 } while(bits - val + (n-1) < 0);
 return val;
}
\end{lstlisting}


\begin{lstlisting}[stepnumber=5,caption={You cannot easily tell where line breaks occur}]
public int nextInt(int n) {
 if (n<=0)
  throw new IllegalArgumentException("n is not positive");

  
 // this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very long line
 if ((n & -n) == n)  // i.e., n is a power of 2
  return (int)((n * (long)next(31)) >> 31);

 int bits, val;
 do {
  bits = next(31);
  val = bits % n;
 } while(bits - val + (n-1) < 0);
 return val;
}
\end{lstlisting}

\end{document}

enter image description here

1
  • 1
    I quickly looked into the package code, because I thought that you might be able to temporarily override the line number. But it seems that what the package does is: check whether line numbers should be printed at all; if yes, check whether the current line is the first line of the code line that possibly spans several lines; if yes, print the number. Otherwise nothing will be printed and there is also no hook or box to be filled. So in short, I think it is quite difficult to get something there without patching a lot of code. Mar 26, 2021 at 23:27

2 Answers 2

3

When answering another answer, I found out that the tikzmark library for TikZ provides some features for use with the listings package. The following still requires you to manually place the return marks, so it is not done fully automatically. But it may still be better than no return marks at all ...

The macro takes two arguments, the first for the line number in question and the second for the number of line breaks. Note that I assumed the line spacing of the listings environment to be 1em, but it may be different in other circumstances.

\documentclass[a4paper,12pt]{article}
\usepackage{xcolor}
\usepackage{listings}

\usepackage{tikz}
\usetikzlibrary{tikzmark}
\usetikzmarklibrary{listings}

\newcommand{\continuingline}[2]{%
    \begin{tikzpicture}[remember picture, overlay]
        \foreach \i in {1,...,#2} {
            \node[anchor=base, align=center] at ([xshift=-1.25em, yshift={-\i*1em}]pic cs:line-code-#1-start) {$\hookrightarrow$};
        }
    \end{tikzpicture}%
}

\lstset{
    basicstyle=\footnotesize\ttfamily,
    breakatwhitespace=true,  
    breaklines=true,         
    firstnumber=1,
    frame=leftline,       
    keepspaces=true,         
    numbers=left,            
    showspaces=false,        
    showstringspaces=false,  
    showtabs=false,          
    stepnumber=1,
    language=java,
    numberstyle=\color{darkgray},
    commentstyle=\color{gray},     
    stringstyle=\color{red},
    keywordstyle=\color{blue},
    rulecolor=\color{black},
}

\begin{document}

\begin{lstlisting}[caption={You can easily recognize line breaks}, name=code]
public int nextInt(int n) {
 if (n<=0)
  throw new IllegalArgumentException("n is not positive");


 // this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very long line
 if ((n & -n) == n)  // i.e., n is a power of 2
  return (int)((n * (long)next(31)) >> 31);

 int bits, val;
 do {
  bits = next(31);
  val = bits % n;
 } while(bits - val + (n-1) < 0);
 // this is another although not very very very very long but still quite long line
 return val;
}
\end{lstlisting}

\continuingline{6}{2}
\continuingline{15}{1}

\end{document}

enter image description here


Update: Better solution that also works for more than only one lstlisting environment.

The macro now takes three mandatory arguments, the first being the name of the listing, which has to be set via name=... as option to the relevant lstlisting environment. The second and third argument is again the relevant line number and the number of line breaks respectively.

Furthermore, the macro takes one optional argument, in case that the lineskip is something other than 1em. It defaults to 1em.

\continuingline[<lineskip>]{<listings name>}{<line number>}{<line breaks>}

\documentclass[a4paper,12pt]{article}
\usepackage{xcolor}
\usepackage{listings}

\usepackage{tikz}
\usetikzlibrary{tikzmark}
\usetikzmarklibrary{listings}

\newcommand{\continuingline}[4][1em]{%
    \begin{tikzpicture}[remember picture, overlay]
        \foreach \i in {1,...,#4} {
            \node[anchor=base, align=center] at ([xshift=-1.25em, yshift={-\i*#1}]pic cs:line-#2-#3-start) {$\hookrightarrow$};
        }
    \end{tikzpicture}%
}

\lstset{
    basicstyle=\footnotesize\ttfamily,
    breakatwhitespace=true,  
    breaklines=true,         
    firstnumber=1,
    frame=leftline,       
    keepspaces=true,         
    numbers=left,            
    showspaces=false,        
    showstringspaces=false,  
    showtabs=false,          
    stepnumber=1,
    language=java,
    numberstyle=\color{darkgray},
    commentstyle=\color{gray},     
    stringstyle=\color{red},
    keywordstyle=\color{blue},
    rulecolor=\color{black},
}

\begin{document}

\begin{lstlisting}[caption={You can easily recognize line breaks}, name=codea]
public int nextInt(int n) {
 if (n<=0)
  throw new IllegalArgumentException("n is not positive");


 // this is a very very very very very very very very very very very very very very very very very very very very very very very very very very very long line
 if ((n & -n) == n)  // i.e., n is a power of 2
  return (int)((n * (long)next(31)) >> 31);

 int bits, val;
 do {
  bits = next(31);
  val = bits % n;
 } while(bits - val + (n-1) < 0);
 // this is another although not very very very very long but still quite long line
 return val;
}
\end{lstlisting}

\continuingline{codea}{6}{2}
\continuingline{codea}{15}{1}

\begin{lstlisting}[caption={You can again easily recognize line breaks}, name=codeb]
public int nextInt(int n) {
 if (n<=0)
  throw new IllegalArgumentException("n is not positive");
  // this is another unbelievably long and probably under normal circumstances totally unnesessary line
 return val;
}
\end{lstlisting}

\continuingline{codeb}{4}{1}

\begin{lstlisting}[caption={You can again easily recognize line breaks}, name=codec, basicstyle=\footnotesize\ttfamily\linespread{1.2}\selectfont]
public int nextInt(int n) {
 if (n<=0)
  throw new IllegalArgumentException("n is not positive");
  // this is yet another unbelievably long and probably under normal circumstances totally unnesessary line
 return val;
}
\end{lstlisting}

\continuingline[1.2em]{codec}{4}{1}

\end{document}

enter image description here

3
  • Looks cool! However, it doesn't seem to work if one have more than one listing per document, as the latest code snippet sets the "offset" for all \continuingline. This is more evident if you have listing, continuingline, listing, continuingline, you compile, and then you add clearpage, listing, continuingline
    – Polizi8
    Mar 28, 2021 at 16:04
  • Hah, indeed! The reason for this is that I hard coded the name of the lstlisting environment, which is, of course, not the optimal way! Mar 28, 2021 at 16:19
  • Thank you, reading tikzmark implementation helped a lot with finding the correct listings macro, have a look at my answer for automatic marking
    – Polizi8
    Mar 28, 2021 at 17:43
3

I found the proper macro!

Jasper's suggestion to use tikzmark listings library gave me the possibility to better understand the underlying listings code

Search for "postbreak" in lstmisc.sty:

\gdef\lst@@discretionary{%
    \discretionary{\let\space\lst@spacekern\lst@prebreak}%
                  {\llap{\lsthk@EveryLine
                   \kern\lst@breakcurrindent \kern-\@totalleftmargin}%
                   \let\space\lst@spacekern\lst@postbreak}{}}

Now copy it to the current document and find the right spot where to put the symbol:

\makeatletter
\gdef\lst@@discretionary{%
    \discretionary{\let\space\lst@spacekern\lst@prebreak}%
                  {\llap{HERE \lsthk@EveryLine
                   \kern\lst@breakcurrindent \kern-\@totalleftmargin}%
                   \let\space\lst@spacekern\lst@postbreak}{}}
\makeatother

One probably wants to use the same line numbering color, in that case it is sufficient to replace "HERE" with {\color{darkgray}→\ }. I am using "→" because the unicode symbol I wanted doesn't appear.

Result:

enter image description here

1
  • 1
    Nice catch! You could also load the package etoolbox and then write \patchcmd{\lst@@discretionary}{\lsthk@EveryLine}{{\color{darkgray}→\ }\lsthk@EveryLine}{}{} (\makeatletter and \makeatother still needed, of course).. Mar 28, 2021 at 19:14

You must log in to answer this question.

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