[Tex/LaTex] How to extend the \lstinputlisting command

hookslistingspackage-writing

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 what could be even more nice:

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

where LaTeX automatically counted the number of tabs like 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 these limitations):

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

    Best Answer

    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}
    
    Related Question