Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

This is a follow-up question to First line number in lstinputlisting environment. Consider the MWE:

\documentclass{article}
%\usepackage{etoolbox}% http://ctan.org/pkg/etoolbox
\usepackage{filecontents,listings}% http://ctan.org/pkg/{filecontents,listings}
\lstset{
  numbers=left,
  basicstyle=\ttfamily\footnotesize
}
\begin{filecontents*}{mycode.txt}
Line 1
Line 2
Line 3
Line 4
Line 5
\end{filecontents*}

\makeatletter
% http://tex.stackexchange.com/q/26828/5764
%\patchcmd{\lst@GLI@}% <command>
%  {\def\lst@firstline{#1\relax}}% <search>
%  {\def\lst@firstline{#1\relax}\def\lst@firstnumber{#1\relax}}% <replace>
%  {\typeout{listings firstnumber=firstline}}% <success>
%  {\typeout{listings firstnumber not set}}% <failure>
\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}

Without the aid of the etoolbox patch (or the default), the output looks like that of the leftmost column. With the aid of the patch (a "firstnumber correction), the output looks like that of the middle side. However, I'd like the output to look like that of the rightmost column.

enter image description here

That is, the eventual output of line numbers should match that of the linerange specification.

The trivial solution I'm hoping to avoid is the inclusion of separate \lstinputlistings for each range with the appropriate vertical adjustment of -2\medskipamount (default space above/below an included listing). That was how the rightmost column was obtained in the first place.

share|improve this question

1 Answer 1

up vote 6 down vote accepted

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.

New sample 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:

Sample output

\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}

Sample switch

share|improve this answer
    
Well, that was easy! :) Thanks Andrew. –  Werner Apr 23 '13 at 14:16
    
This seems to overwrite other listingsoptions set by lstset like showlines=true– is there a way to keep those as well? –  k.stm Jan 12 at 0:05
    
@K.Stm. It is not overwriting these options, but there is interaction with them that one may not wish. The problem is that different options reference different counters. –  Andrew Swann Jan 12 at 14:15
    
@AndrewSwann True. I’m having problems with showlines that are unrelated. However, the solution here doesn’t go nicely with the lstlinebgrd package. If one wants to keep background colors, one has to imitate its implementation and add \lst@linebgrd between the last two closing curly braces in the definitions of the key cases left, leftliteral, right (and rightliteral if one has defined it), e.g. …\kern\lst@numbersep}\lst@linebgrd}\\%. –  k.stm Jan 12 at 14:19
    
@K.Stm. Not surprising; that package is also changing the same command. Your code suggestion seems to be the right sort approach. If you have open problems, you should ask a separate question on this site. –  Andrew Swann Jan 12 at 15:06

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.