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 x
th line" and y
means "the current line is the y
th 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:
There we go, minted
with line ranges. :)
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. :)
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:
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. :)
The following code creates a new environment, mintednc
, that strips comments completely. You can add it to the preamble. I've tested it with both C and Python. It's possible that it will need a few tweaks to deal with additional languages.
This code works by creating a custom version of \minted@pygmentize
. In this customized version, the highlighted Pygments output is opened via a \write18
Python command, and any line beginning with \PY{c}
, \PY{c+cm}
, or \PY{c+c1}
is discarded. These are the Pygments highlighting commands with arguments that indicate comments. This approach completely eliminates any line that begins with a comment. But inline comments that follow code would be left intact.
Edit in response to question in comments: Adding support for \inputminted
is just a matter of creating a wrapper command (\inputmintednc
) that uses the customized \minted@pygmentize
. I've added this at the end of the code.
Edit in response to error messages in comments: It looks like you are using the development version of minted
. The patch only works for minted
1.7. I've added code that works (for now, anyway) with the development version.
Code for minted 1.7
(which is in current TeX distros)
\makeatletter
\newcommand{\minted@pygmentize@stripcomments}[2][\jobname.pyg]{
\def\minted@cmd{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 #1}
\immediate\write18{\minted@cmd}
% For debugging, uncomment:
%\immediate\typeout{\minted@cmd}
\ifthenelse{\equal{\minted@opt@bgcolor}{}}
{}
{\begin{minted@colorbg}{\minted@opt@bgcolor}}
% add command to strip comments
\immediate\write18{\detokenize{python -c "f = open(\"}\jobname\detokenize{.out.pyg\");
txt = f.readlines(); f.close();
txt = [line for line in txt if not line.lstrip().startswith(\"\\PY{c}\") and not line.lstrip().startswith(\"\\PY{c+c1}\") and not line.lstrip().startswith(\"\\PY{c+cm}\")];
f = open(\"}\jobname\detokenize{.out.pyg\", \"w\");
f.write(\"\".join(txt)); f.close();"}}
% end
\input{\jobname.out.pyg}
\ifthenelse{\equal{\minted@opt@bgcolor}{}}
{}
{\end{minted@colorbg}}
\DeleteFile{\jobname.out.pyg}}
\newenvironment{mintednc}[2][]{%
\VerbatimEnvironment
\let\minted@pygmentize\minted@pygmentize@stripcomments
\begin{minted}[#1]{#2}}{\end{minted}}
\newcommand{\inputmintednc}[3][]{%
\begingroup
\let\minted@pygmentize\minted@pygmentize@stripcomments
\inputminted[#1]{#2}{#3}%
\endgroup}
\makeatother
\makeatletter
\newcommand{\minted@inputpyg@stripcomments}{%
\ifthenelse{\equal{\minted@optcmd@bgcolor}{}}%
{}%
{\begin{minted@colorbg}{\minted@optcmd@bgcolor}}%
% add command to strip comments
\immediate\write18{\detokenize{python -c "from io import open;
import re;
pattern = re.compile(r\"\\PYG([a-z]+)\");
f = open(\"}\minted@outputdir\minted@infile\detokenize{\", encoding=\"}\minted@encoding\detokenize{\");
txt = f.read(); f.close();
p = pattern.search(txt).groups()[0];
p1 = \"\\PYG\"+p+\"{c}\";
p2 = \"\\PYG\"+p+\"{c+c1}\";
p3 = \"\\PYG\"+p+\"{c+cm}\";
txt = [line for line in txt.splitlines(True) if not line.lstrip().startswith(p1) and not line.lstrip().startswith(p2) and not line.lstrip().startswith(p3)];
f = open(\"}\jobname\detokenize{.out.pyg\", \"w\", encoding=\"}\minted@encoding\detokenize{\");
f.write(\"\".join(txt)); f.close();"}}
% end
\input{\jobname.out.pyg}%
\ifthenelse{\equal{\minted@optcmd@bgcolor}{}}%
{}%
{\end{minted@colorbg}}%
}
\newenvironment{mintednc}[2][]{%
\VerbatimEnvironment
\let\minted@inputpyg\minted@inputpyg@stripcomments
\begin{minted}[#1]{#2}}{\end{minted}}
\newcommand{\inputmintednc}[3][]{%
\begingroup
\let\minted@inputpyg\minted@inputpyg@stripcomments
\inputminted[#1]{#2}{#3}%
\endgroup}
\makeatother
Best Answer
Yes, it appears to work, you just need to specify an escape character (
?
here) tominted
so you can insert the\tikzmark
.