[Tex/LaTex] print only some functions with minted

minted

I often have to print Source Code, but quite often only some parts of a project. Now I got a task to improve two functions and I have to print out what I change. I use minted for printing source code.

How can I tell minted to print only a given function?

Something like:

\inputminted[functions=helloWorld,functions=abc,
                fontsize=\footnotesize, tabsize=4]{c}{/home/moose/Desktop/RO/blatt02.c} 

If this is not possible, I'd also accept if I could tell minted to print some given lines, e.g.:

\inputminted[from=12, to=50,
                fontsize=\footnotesize, tabsize=4]{c}{/home/moose/Desktop/RO/blatt02.c} 

This would not be as good as the functions, but it would be ok.

Best Answer

Method 1

I did, egreg! I did it by myself! Best day ever! Well, technically it's night. Anyway. :)

May Konrad Rudolph have mercy on me. :) The following code is a dirty, dirty hack. But we do love dirty code, don't we?

First things first. This trick relies on the powerful awk. We can print ranges from a document by using the format 'NR==x,NR==y', where x means "the current line is the xth line" and y means "the current line is the yth line". So awk will print the interval between them.

We can pipe the awk output to pygmentize, which is used by minted. Then I added two options to minted:

  • linestart: the first line to be printed.
  • lineend: the last line to be printed.

First, let's consider the following helloworld.c sample code (lines added for our convenience):

1. #include <stdio.h>
2. 
3. int main(void) {
4.     printf("Hello world\n");
5.     return 0;
6. }

Now the .tex code:

\documentclass{article}

\usepackage{minted}

\makeatletter
\minted@define@opt{linestart}{NR==#1}
\minted@define@opt{lineend}{NR==#1}
\renewcommand\minted@pygmentize[2][\jobname.pyg]{%
\ifthenelse{\equal{\minted@opt@linestart}{}}{\def\minted@awk{}}{\def\minted@awk{awk '\minted@opt{linestart},\minted@opt{lineend}' #1 |}}
\ifthenelse{\equal{\minted@opt@linestart}{}}{\def\minted@fromsource{#1}}{\def\minted@fromsource{}}
\def\minted@cmd{\minted@awk pygmentize -l #2 -f latex -F tokenmerge
\minted@opt{gobble} \minted@opt{texcl} \minted@opt{mathescape}
\minted@opt{startinline} \minted@opt{funcnamehighlighting}
\minted@opt{linenos} -P "verboptions=\minted@opt{extra}"
-o \jobname.out.pyg \minted@fromsource}
\immediate\write18{\minted@cmd}
\ifthenelse{\equal{\minted@opt@bgcolor}{}}
{}
{\begin{minted@colorbg}{\minted@opt@bgcolor}}
\input{\jobname.out.pyg}
\ifthenelse{\equal{\minted@opt@bgcolor}{}}
{}
{\end{minted@colorbg}}
\DeleteFile{\jobname.out.pyg}}
\makeatother

\begin{document}
\inputminted[linestart=3,lineend=5]{c}{helloworld.c}
\end{document}

After running it (of course, with --shell-escape enabled), this is our output:

The output

There we go, minted with line ranges. :)

Method 2

Edit: Back to the crafting table. :)

My good friend Marco Daniel pointed out an out-of-the-box solution which is way more appropriate for this case: using firstline and lastline options from fancyvrb/minted themselves. It does exactly what I did in the previous attempt, but without all the dangerous bends I went through:

\documentclass{article}

\usepackage{minted}

\begin{document}
\inputminted[firstline=3,lastline=5]{c}{helloworld.c}
\end{document}

And we shall obtain the very same result. :)

Method 3

Edit: For the sake of completeness, I decided to try again and print the function scope based on its full qualified name. Here it goes. :)

The following awk code is taken from How to extract a C function definition from a C source file. According to the author, "the following assumes balanced braces, and that the closing } for a function is the last character in its line." I modified it slightly in order to set the function name. The original code had the function to extract hardcoded.

function match_braces() {
s=$0
# how to abuse gsub
op=gsub(/{/,"",s);
cl=gsub(/}/,"",s);
if (op || cl) f=1;
return (op-cl);
}

match($0, v) {ok=1}
ok {n+=match_braces(); print; if ((n==0)&&(f==1)) exit}

I saved it as extract.awk and put it in the same directory of my .tex file. It would be wiser to create an alias or even a shell script to wrap the call to this code and export it to the path. I'm taking the easier route, for obvious reasons. :)

Lets use another helloworld.c:

#include <stdio.h>

void sayHello() {
    printf("Hello world!\n");
}

void sayGoodBye() {
    printf("Goodbye world!\n");
}

int main(void) {
    sayHello();
    sayGoodBye();
    return 0;
}

Now, our .tex code:

\documentclass{article}

\usepackage{minted}
\usepackage{lipsum}

\makeatletter
\minted@define@opt{function}{#1}
\renewcommand\minted@pygmentize[2][\jobname.pyg]{%
\ifthenelse{\equal{\minted@opt@function}{}}{\def\minted@awk{}}{\def\minted@awk{awk -vv="\minted@opt@function" -f extract.awk #1 |}}
\ifthenelse{\equal{\minted@opt@function}{}}{\def\minted@fromsource{#1}}{\def\minted@fromsource{}}
\def\minted@cmd{\minted@awk pygmentize -l #2 -f latex -F tokenmerge
\minted@opt{gobble} \minted@opt{texcl} \minted@opt{mathescape}
\minted@opt{startinline} \minted@opt{funcnamehighlighting}
\minted@opt{linenos} -P "verboptions=\minted@opt{extra}"
-o \jobname.out.pyg \minted@fromsource}
\immediate\write18{\minted@cmd}
\ifthenelse{\equal{\minted@opt@bgcolor}{}}
{}
{\begin{minted@colorbg}{\minted@opt@bgcolor}}
\input{\jobname.out.pyg}
\ifthenelse{\equal{\minted@opt@bgcolor}{}}
{}
{\end{minted@colorbg}}
\DeleteFile{\jobname.out.pyg}}
\makeatother

\begin{document}

\inputminted[function={int main}]{c}{helloworld.c}

\lipsum[1]

\inputminted[function={void sayHello}]{c}{helloworld.c}

\lipsum[1]

\inputminted[function={void sayGoodBye}]{c}{helloworld.c}

\end{document}

Our new output:

Output 5

As Konrad mentioned in the comments, this trick is not portable, since it works only with a few range of languages and it relies on certain code standards. Nonetheless, it's a neat feature. :)

Related Question