12

With the answer from autogobble for lstinputlistings I've managed to create a command that gobbles \lstinputlisting and makes firstnumber to start by firstline. I would like to rewrite it as a package, so it hooks into \lstinputlisting, but as the code might reflect I have very little experience with programming in LaTeX.

My goal is to be able to write

\lstset{tabsize=3,numbers=left,frame=single,basicstyle=\footnotesize\ttfamilily,}
\lstinputlisting[levels=1,linerange={2-4},...]{hello.c}

where the dots are possible extra regular listings options.

Or even nicer:

\lstset{tabsize=3,numbers=left,frame=single,basicstyle=\footnotesize\ttfamilily,}
\lstinputlisting[autogobble,linerange={2-4},...]{hello.c}

where LaTeX automatically counts the number of tabs as in How to automatically skip leading white spaces in listings.

MWE

\documentclass{article}
\usepackage{listings,xifthen}

\lstset{tabsize=3,numbers=left,frame=single,basicstyle=\Huge\ttfamily,columns=flexible}
\newlength{\gobble}
\newlength{\gobblea}
% The width of a single space. basicstyle from lstset should be used
\sbox0{\Huge\ttfamily \ }
\newcommand{\mylist}[5]{
%#1 is number of tabs, could be calculated like in listings-autogobble with autogobble=true or be an extra option
%#2 is tabsize, which is set in lstset
%#3 is firstline from lstset
%#4 is lastline from lstset
%#5 is the filename, the only thing which should be an argument and not an option.

% Remove a single space
\setlength{\gobble}{-\the\wd0}
% Reindent a bit by multiplying with 0.9, then multiply by tabsize and number of indentation levels
\setlength{\gobble}{0.9\gobble*#1*#2}
\setlength{\gobblea}{\gobble}
\addtolength{\gobblea}{10pt}
% Check if firstline is defined
\ifthenelse{\isempty{#3} \OR \equal{#3}{0}}{%
% Check if lastline is defined
\ifthenelse{\isempty{#4}}{%
\lstinputlisting[firstnumber=1,firstline=1,framexleftmargin=\gobble,xleftmargin=\gobble,numbersep=\gobblea]{#5}
}{
\lstinputlisting[firstnumber=1,firstline=1,lastline=#4,framexleftmargin=\gobble,xleftmargin=\gobble,numbersep=\gobblea]{#5}
}
}{
\ifthenelse{\isempty{#4}}{%
\lstinputlisting[firstnumber=#3,firstline=#3,framexleftmargin=\gobble,xleftmargin=\gobble,numbersep=\gobblea]{#5}
}{
\lstinputlisting[firstnumber=#3,firstline=#3,lastline=#4,framexleftmargin=\gobble,xleftmargin=\gobble,numbersep=\gobblea]{#5}
}
}
}
\begin{document}
%mylist{#tabs}{#tabsize}{firstline}{lastline}{filename}
\mylist{1}{3}{2}{4}{hello1.c}
\end{document}

Edit

I just modified the code from @cyberSingularity a bit to use lst@basicstyle and lst@tabsize.

It should work with any:

  • font size in document class
  • font size in basicstyle
  • tabsize

Limitations (would be very pleased if someone could address them):

  • it needs columns=flexible and basicstyle=\ttfamily
  • if tabsize or basicstyle is passed as options to \lstinputlisting, they must be written before widthgobble
  • if using XeLaTeX, the commands must be loaded before Polyglossia, othwerwise all text will be formatted with \ttfamily
\documentclass[10pt]{article}
\usepackage{listings}

\usepackage{filecontents}
\begin{filecontents*}{hello1.c}
if (a<b){
   if (b<a){
      printf("hello")
   }
}
\end{filecontents*}

\lstset{tabsize=3,numbers=left,frame=single,basicstyle=\Huge\ttfamily,columns=flexible}

\makeatletter
\newlength{\singlespace}
\newlength{\gobble}
\newlength{\numbersep}
% The width of a single space.
\settowidth{\singlespace}{\lst@basicstyle \ }
\setlength{\singlespace}{-\singlespace}

\lst@Key{firstlineandnumber}\relax{\def\lst@firstline{#1\relax}\def\lst@firstnumber{#1\relax}}
\lst@Key{widthgobble}{0}{%
    \setlength{\gobble}{0.9\singlespace}% reindent a bit
    \setlength{\gobble}{\lst@tabsize\gobble}% multiply by tabsize
    \setlength{\gobble}{#1\gobble}% multiply by number of tabs
    \def\lst@xleftmargin{\gobble}% move left margin left
    \def\lst@framexleftmargin{\gobble}% move left frameborder left
    \setlength{\numbersep}{\gobble}%
    \addtolength{\numbersep}{10pt}%
    \def\lst@numbersep{\numbersep}% distance between numbers and left frameborder
}
\makeatother

\begin{document}
%widthgobble=#tabs,firstlineandnumber sets firstline and firstnumber
\lstinputlisting[widthgobble=1,firstlineandnumber=2,lastline=4]{hello1.c}
\end{document}
4
  • ...and the question exactly is? Or is this basically an answer to your question tex.stackexchange.com/questions/43686/… ? In the latter case, please post it there as an answer (self-ansering is perfectly acceptable!). Otherwise you might want to edit your question into a real question.
    – Daniel
    Mar 22, 2012 at 9:21
  • The previous question was how to do it. This is a request for a rewrite of the command to hook into listings, so I can parse extra options to the regular \lstinputlisting{} instead of my own mylist which is very limited in functionality since it can't take regular listing options. Mar 22, 2012 at 9:36
  • Thats find. You might, however, want to link to your previous question (to make the context clear), make your goal more explicit in the question title (e.g., "How to extend the \lstinputlisting command") and also in the question body. This just increases the chance to get answers :-)
    – Daniel
    Mar 22, 2012 at 10:16
  • 1
    @MartinScharrer: IIRC you are currently preparing your listings extension for submission to CTAN. Maybe you have already implemented autogobble for \lstinputlisting as well?
    – Daniel
    Mar 22, 2012 at 10:18

1 Answer 1

5

Are you after something like this? I have provided new keys:

  • widthgobble, which takes as an argument #tabs*#tabsize
  • firstlineandnumber, which sets both firstline and firstnumber

Note that I had some trouble using the * syntax for setting the length of \gobble so have implemented an ugly workaround (which currently means that the argument to widthgobble should always have two numbers separated by an asterisk):

\documentclass{article}
\usepackage{listings}

\usepackage{filecontents}
\begin{filecontents*}{hello1.c}
if (a<b){
    if (b<a){
        //do something
    }
}
\end{filecontents*}

\errorcontextlines=\maxdimen

\lstset{tabsize=3,numbers=left,frame=single,basicstyle=\Huge\ttfamily,columns=flexible}
\newlength{\rawgobble}
\newlength{\gobble}
\newlength{\gobblea}
% The width of a single space. basicstyle from lstset should be used
\sbox0{\Huge\ttfamily \ }
% Remove a single space
\settowidth{\rawgobble}{\Huge\ttfamily \ }
\setlength{\rawgobble}{-\rawgobble}

\makeatletter
\def\sepstar#1*#2\relax{%
    \def\sepstarone{#1}%
    \def\sepstartwo{#2}%
}
\lst@Key{firstlineandnumber}\relax{\def\lst@firstline{#1\relax}\def\lst@firstnumber{#1\relax}}
\lst@Key{widthgobble}{0*0}{%
    % Reindent a bit by multiplying with 0.9, then multiply by tabsize and number of indentation levels
    \sepstar #1\relax
    \setlength{\gobble}{0.9\rawgobble}%
    \setlength{\gobble}{\sepstarone\gobble}%
    \setlength{\gobble}{\sepstartwo\gobble}%
    \setlength{\gobblea}{\gobble}%
    \addtolength{\gobblea}{10pt}%
    \def\lst@xleftmargin{\gobble}%
    \def\lst@framexleftmargin{\gobble}%
    \def\lst@numbersep{\gobblea}%
}
\makeatother

\begin{document}
%widthgobble=#tabs*#tabsize,firstlineandnumber sets firstline and firstnumber
\lstinputlisting[widthgobble=1*3,firstlineandnumber=2,lastline=4]{hello1.c}
\end{document}
2
  • Thanks a lot cyberSingularity! That was very close to what I sought, but indeed a lot cleaner than my first attempt. I have edited my question to reflect the last changes needed. Jan 6, 2013 at 11:11
  • @JonasNyrup: Great! I am still interested in this autogobble idea, and I guess it should it detect based on the first displayed line rather than the first actual line in the file. I will take a look at this but expect it to be difficult... Jan 6, 2013 at 11:20

You must log in to answer this question.

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