Listings line numbers that match the linerange specification
My previous solution had problems with the numbering of blank lines. I have added a new solution and kept the old one further down.
New Solution
What one really needs to do is reset the counter lstnumber
at the beginning of each range. To achieve this one needs to hook into an internal command \lst@SkipToFirst
. One approach is to define a new key matchrangestart
initialised as false via
\lst@Key{matchrangestart}{f}{\lstKV@SetIf{#1}\lst@ifmatchrangestart}
and then add the following test at the beginning of \lst@SkiptoFirst
:
\lst@ifmatchrangestart\c@lstnumber=\numexpr-1+\lst@firstline\fi
This the general way listings
set-up keys with true/false values and how it handles the counter lstnumber
. This code says if the matchrangestart
key is true then when skipping forward to the beginning of a rang set lstnumber
to one less than the first line number of the range. When the line gets printed this number is then incremented before being printed so we get the desired output.
\documentclass{article}
\usepackage{filecontents,listings}
\lstset{
basicstyle=\ttfamily\footnotesize
}
\begin{filecontents*}{mycode.txt}
Line 1
Line 2
Line 3
Line 6
Line 7
Line 9
Line 10
\end{filecontents*}
\makeatletter
\lst@Key{matchrangestart}{f}{\lstKV@SetIf{#1}\lst@ifmatchrangestart}
\def\lst@SkipToFirst{%
\lst@ifmatchrangestart\c@lstnumber=\numexpr-1+\lst@firstline\fi
\ifnum \lst@lineno<\lst@firstline
\def\lst@next{\lst@BeginDropInput\lst@Pmode
\lst@Let{13}\lst@MSkipToFirst
\lst@Let{10}\lst@MSkipToFirst}%
\expandafter\lst@next
\else
\expandafter\lst@BOLGobble
\fi}
\makeatother
\begin{document}
\begin{minipage}{0.45\linewidth}
Standard
\lstset{numbers=left}
\lstinputlisting[linerange={1-3}]{mycode.txt}
\lstinputlisting[linerange={2-6}]{mycode.txt}
\lstinputlisting[linerange={2-2,4-6,8-9}]{mycode.txt}
\end{minipage}
\begin{minipage}{0.45\linewidth}
Literal
\lstset{numbers=left,matchrangestart=t}
\lstinputlisting[linerange={1-3}]{mycode.txt}
\lstinputlisting[linerange={2-6}]{mycode.txt}
\lstinputlisting[linerange={2-2,4-6,8-9}]{mycode.txt}
\end{minipage}
\end{document}
Old Solution
This places wrong numbers on blank lines
The listings
package has an internal plain counter \lst@lineno
holding the line number you are after. Now the numbers=left
option, runs the code
\def\lst@PlaceNumber{\llap{\normalfont
\lst@numberstyle{\thelstnumber}\kern\lst@numbersep}}
so all we need to do is take this code and replace the printed latex counter \thelstnumber
by \the\list@lineno
:
\documentclass{article}
\usepackage{filecontents,listings}
\lstset{
basicstyle=\ttfamily\footnotesize
}
\begin{filecontents*}{mycode.txt}
Line 1
Line 2
Line 3
Line 4
Line 5
\end{filecontents*}
\makeatletter
\def\lst@PlaceNumber{\llap{\normalfont
\lst@numberstyle{\the\lst@lineno}\kern\lst@numbersep}}
\makeatother
\begin{document}
\lstinputlisting[linerange={1-3}]{mycode.txt}
\lstinputlisting[linerange={2-4}]{mycode.txt}
\lstinputlisting[linerange={2-2,4-5}]{mycode.txt}
\end{document}
The above coding sets this style globally. If you wish to have local switches then you can extend the listings
definition of the numbers
option, adding a leftliteral
type as follows:
\documentclass{article}
\usepackage{filecontents,listings}
\makeatletter
\lst@Key{numbers}{none}{%
\let\lst@PlaceNumber\@empty
\lstKV@SwitchCases{#1}%
{none&\\%
left&\def\lst@PlaceNumber{\llap{\normalfont
\lst@numberstyle{\thelstnumber}\kern\lst@numbersep}}\\%
leftliteral&\def\lst@PlaceNumber{\llap{\normalfont
\lst@numberstyle{\the\lst@lineno}\kern\lst@numbersep}}\\%
right&\def\lst@PlaceNumber{\rlap{\normalfont
\kern\linewidth \kern\lst@numbersep
\lst@numberstyle{\thelstnumber}}}%
}{\PackageError{Listings}{Numbers #1 unknown}\@ehc}}
\makeatother
\lstset{
basicstyle=\ttfamily\footnotesize
}
\begin{filecontents*}{mycode.txt}
Line 1
Line 2
Line 3
Line 4
Line 5
\end{filecontents*}
\begin{document}
\begin{minipage}{0.3\linewidth}
Standard
\lstset{numbers=left}
\lstinputlisting[linerange={1-3}]{mycode.txt}
\lstinputlisting[linerange={2-4}]{mycode.txt}
\lstinputlisting[linerange={2-2,4-5}]{mycode.txt}
\end{minipage}
\begin{minipage}{0.3\linewidth}
Literal
\lstset{numbers=leftliteral}
\lstinputlisting[linerange={1-3}]{mycode.txt}
\lstinputlisting[linerange={2-4}]{mycode.txt}
\lstinputlisting[linerange={2-2,4-5}]{mycode.txt}
\end{minipage}
\begin{minipage}{0.3\linewidth}
Standard
\lstset{numbers=left}
\lstinputlisting[linerange={1-3}]{mycode.txt}
\lstinputlisting[linerange={2-4}]{mycode.txt}
\lstinputlisting[linerange={2-2,4-5}]{mycode.txt}
\end{minipage}
\end{document}
Based upon Andrew's answer I found this to be a better match to the problem:
\makeatletter
\def\lst@MSkipToFirst{%
\global\advance\lst@lineno\@ne
\ifnum \lst@lineno=\lst@firstline
\def\lst@next{\lst@LeaveMode \global\lst@newlines\z@
\lst@OnceAtEOL \global\let\lst@OnceAtEOL\@empty
\lst@InitLstNumber % Added to work with modified \lsthk@PreInit.
\lsthk@InitVarsBOL
\c@lstnumber=\numexpr-1+\lst@lineno % this enforces the displayed line numbers to always be the input line numbers
\lst@BOLGobble}%
\expandafter\lst@next
\fi}
\makeatother
The advantage over his answer is that it supports multi-range constructs correctly without the need to introduce a key. This is done by overriding the method which actually takes care of range-skipping and already containing a hook for the first line of a new range.
Tested against listings 1.6 (2015/06/04). As always when overriding a function pay attention to changes of the package's source (shouldn't be to hard as only one line was added).
Bonus:
2mm whitespace between line-blocks:
\makeatletter
\def\lst@MSkipToFirst{%
\global\advance\lst@lineno\@ne
\ifnum \lst@lineno=\lst@firstline
\def\lst@next{\lst@LeaveMode \global\lst@newlines\z@
\lst@OnceAtEOL \global\let\lst@OnceAtEOL\@empty
\ifnum \c@lstnumber>0
\vspace{2 mm}
\fi
\lst@InitLstNumber % Added to work with modified \lsthk@PreInit.
\lsthk@InitVarsBOL
\c@lstnumber=\numexpr-1+\lst@lineno % this enforces the displayed line numbers to always be the input line numbers
\lst@BOLGobble}%
\expandafter\lst@next
\fi}
\makeatother