Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Number last line with listings package in LaTeX

Tags:

I'm using LaTeX with the listings package, and I number every fifth line of the code by having:

\lstset{
numbers=left,
stepnumber=5,
firstnumber=1,
numberfirstline=true
}

Now because of the numberfirstline=true in these settings, apart from the multiples of five the first line of each listing is numbered too, but I would like to have the last line of the code be numbered as well. So if I have a listing with 17 lines of code, the lines I would like to see numbered are 1 5 10 15 17.

Is there some way that I can make listings behave like this? I tried numberlastline=true, but that does not seem to exist.

Edit: I would prefer to use this with \lstinputlisting, and without having to modify the code that get imported that way.

like image 406
Freek Wiedijk Avatar asked May 24 '17 07:05

Freek Wiedijk


1 Answers

Actually, since you're using numberfirstline=true, this is simpler to accomplish than you might think. The listings package provides a mechanism to escape out of the verbatim mode in the middle of a listing via escapeinside to be able to insert additional macros. The trick here is that after returning from the escaped code, it considers the next line as the "first" line again, even though no counts are reset. Thus, all you have to do is escape out (and do nothing!) on the second to last line of the listing. If you do it on the very last, you're too late! The example below demonstrates using this to get what you desire.

\documentclass{article}
\usepackage{listings}

\lstset{
numbers=left,
stepnumber=5,
firstnumber=1,
numberfirstline=true
escapeinside={|(}{)|}
}

\begin{document}

\begin{lstlisting}
lorem ipsum dolor sit amet,
consectetur adipiscing elit,
sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua.
ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.
duis aute irure dolor in reprehenderit
in voluptate velit esse cillum dolore
eu fugiat nulla pariatur.
excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt|()|
mollit anim id est laborum.
\end{lstlisting}

\end{document}

listing with number on last line

You should also check out this question about arbitrarily altering line numbering.

UPDATE

This requires modification to the listing source, and as such may be insufficient when using \lstinputlisting. An alternative approach is to modify the logic of the listings package itself to number the last line when it is explicitly specified via the lastline or linerange keys, as is demonstrated below.

\documentclass{article}
\usepackage{listings}

\lstset{
numbers=left,
stepnumber=5,
firstnumber=1,
numberfirstline=true,
}

\makeatletter

\gdef\lst@SkipOrPrintLabel{%
    \ifnum\lst@skipnumbers=\z@
        \global\advance\lst@skipnumbers-\lst@stepnumber\relax
        \lst@PlaceNumber
        \lst@numberfirstlinefalse
    \else
        \ifnum\lst@lineno=\lst@lastline
            \lst@PlaceNumber
            \lst@numberfirstlinefalse
        \fi
        \lst@ifnumberfirstline
            \lst@PlaceNumber
            \lst@numberfirstlinefalse
        \fi
    \fi
    \global\advance\lst@skipnumbers\@ne}

\makeatother

\begin{document}

\lstinputlisting[lastline=13]{input.tex}

\end{document}

This does require you to manually specify the last line number for each listing, but this seems like a reasonable sacrifice considering the difficulty of manipulating temporary files, which may potentially be the only real solution to dynamically discovering line count which only requires pure TeX. If a non-portable solution is acceptable, you could run a shell command such as wc -l via \write18 to get the number of lines. This would of course require the chosen command to be present on all systems the source is used on, which may cause difficulties if those systems span across different operating systems. See this question for an explanation of \write18 and this question for how to capture it's output.

Worth noting is that when using linerange, it will number the last line of each individual range, but not the first line for each range when using numberfirstline=true. While this can similarly be changed, linerange already results in enough such quirks that if this would be a problem, then it is most likely separate from the issue at hand here.

like image 158
Hiko Haieto Avatar answered Sep 25 '22 10:09

Hiko Haieto