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.

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.

share|improve this question
1  
You should consider using the listings package that allows this kind of printing requirements. –  Werner May 10 '12 at 22:13

1 Answer 1

up vote 27 down vote accepted

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. :)

share|improve this answer
1  
Mercy? This is awesome! Now, if only I had time to integrate it in the package … but maybe soon! EDIT Actually, this is probably not portable enough, meaning it only works on C and only on simple functions … :( –  Konrad Rudolph May 11 '12 at 7:05
    
@Konrad: Thanks for the kind words. :) awk is very powerful with getting the line ranges for any file, but I can't think of a Windows counterpart command. We can use it with other languages too, not only C: awk just reads the text file and outputs the lines in the selected range, nothing more. :) I could only come up with this solution thanks to the minted manual, it's extremely well commented. Thanks a lot for this great package! :) –  Paulo Cereda May 11 '12 at 10:51
1  
I know what awk does. I misread your answer (and didn’t look at the source) as parsing out the relevant function by name and that’s not trivial, and not generally feasible with awk. But since you only use lines, that’s not a problem, true. Truth to be told, there should be a TeX-only solution for this problem as well … I’m going to think about this. –  Konrad Rudolph May 11 '12 at 11:19
    
@Konrad: I'm sorry, by your comment I imagined you thought I was using the function name. :) It indeed not feasible to use awk to get the function escope, that's why I tried the easier path with line ranges. :) I also believe in a TeX-only solution, but my TeX-fu is far from acceptable. :) –  Paulo Cereda May 11 '12 at 11:27
    
@Konrad: Sorry, I was unaware of firstline and lastline! I updated my answer to reflect their use. :) –  Paulo Cereda May 11 '12 at 14:41

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.