我经常需要打印源代码,但通常只打印项目的一部分。现在我接到一个改进两个功能的任务,我必须打印出我所做的更改。我使用 minted 来打印源代码。
我怎样才能告诉 minted 仅打印给定的函数?
就像是:
\inputminted[functions=helloWorld,functions=abc,
fontsize=\footnotesize, tabsize=4]{c}{/home/moose/Desktop/RO/blatt02.c}
如果这不可能的话,我也会接受,如果我可以告诉 minted 打印一些给定的行,例如:
\inputminted[from=12, to=50,
fontsize=\footnotesize, tabsize=4]{c}{/home/moose/Desktop/RO/blatt02.c}
这虽然不如功能那么好,但是也可以。
答案1
我做到了,天哪!我一个人做到了!这是最棒的一天!好吧,严格来说现在是晚上。无论如何。:)
愿康拉德·鲁道夫怜悯我。:)
以下代码是肮脏的黑客代码。但我们确实喜欢肮脏的代码,不是吗?
首先,这个技巧依赖于强大的awk
。我们可以使用格式打印文档中的范围'NR==x,NR==y'
,其中x
表示“当前行是x
第行”,y
表示“当前行是第y
行”。因此awk
将打印它们之间的间隔。
我们可以将awk
输出通过管道传输到pygmentize
,供 所用minted
。然后我向 添加了两个选项minted
:
linestart
:要打印的第一行。lineend
:要打印的最后一行。
首先,让我们考虑以下helloworld.c
示例代码(为方便起见添加了行):
1. #include <stdio.h>
2.
3. int main(void) {
4. printf("Hello world\n");
5. return 0;
6. }
现在.tex
代码:
\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}
运行之后(当然,--shell-escape
启用后),这是我们的输出:
好了,我们minted
有了线路范围。:)
编辑:回到工作台。:)
我的好朋友马可·丹尼尔指出了一种更适合这种情况的现成解决方案:使用firstline
和lastline
选项fancyvrb/minted
本身。它完全按照我上次尝试的方式进行操作,但没有我经历的所有危险弯路:
\documentclass{article}
\usepackage{minted}
\begin{document}
\inputminted[firstline=3,lastline=5]{c}{helloworld.c}
\end{document}
我们将获得同样的结果。:)
编辑:为了完整起见,我决定再试一次,并根据其完整限定名称打印函数范围。就是这样。:)
以下awk
代码取自如何从 C 源文件中提取 C 函数定义。根据作者的说法,“以下内容假设括号匹配,并且}
函数的结束符是其行中的最后一个字符。”我对其进行了轻微修改,以设置函数名称。原始代码中硬编码了提取函数。
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}
我将其另存为extract.awk
并放在我的.tex
文件的同一目录中。创建一个别名甚至一个 shell 脚本来包装对此代码的调用并将其导出到路径会更明智。出于显而易见的原因,我选择了更简单的方法。:)
让我们使用另一个helloworld.c
:
#include <stdio.h>
void sayHello() {
printf("Hello world!\n");
}
void sayGoodBye() {
printf("Goodbye world!\n");
}
int main(void) {
sayHello();
sayGoodBye();
return 0;
}
现在,我们的.tex
代码:
\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}
我们的新输出:
正如 Konrad 在评论中提到的,这个技巧不可移植,因为它只适用于少数几种语言,并且依赖于某些代码标准。尽管如此,这仍然是一个很棒的功能。:)
答案2
这是一种侵入性较小、略有不同的方法,重新绑定\MintedPygmentize
到脚本。我选择使用 和 等明确标记要提取的部分(参见下面的示例//!begin:main
)//!end:main
。
文件extract.sh
:
#!/bin/zsh -f
args=($@)
marker=$args[1]
shift args
file=$args[-1]
args[-1]=()
sed -n "\?//!begin:$marker\$? { :a n; \?//!end:$marker\$?q; p; ba }" $file | \
grep -v '//!' | \
pygmentize $args
文件main.tex
:
\documentclass{article}
\usepackage{minted}
\usepackage{lipsum}
\newcommand{\inputmintedannotated}[4][]{
\begingroup
\renewcommand{\MintedPygmentize}{%
extract.sh\space #2}
\inputminted[#1]{#3}{#4}%
\endgroup
}
\begin{document}
\inputmintedannotated{main}{c}{helloworld.c}
Full file:
\inputminted{c}{helloworld.c}
\end{document}