TransWikia.com

Show return symbol on listings line breaks

TeX - LaTeX Asked on May 19, 2021

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=footnotesizettfamily,
    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}
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

2 Answers

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:

gdeflst@@discretionary{%
    discretionary{letspacelst@spacekernlst@prebreak}%
                  {llap{lsthk@EveryLine
                   kernlst@breakcurrindent kern-@totalleftmargin}%
                   letspacelst@spacekernlst@postbreak}{}}

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

makeatletter
gdeflst@@discretionary{%
    discretionary{letspacelst@spacekernlst@prebreak}%
                  {llap{HERE lsthk@EveryLine
                   kernlst@breakcurrindent kern-@totalleftmargin}%
                   letspacelst@spacekernlst@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

Correct answer by Polizi8 on May 19, 2021

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=footnotesizettfamily,
    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=footnotesizettfamily,
    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=footnotesizettfamilylinespread{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

Answered by Jasper Habicht on May 19, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP