在宏中扩展铸造命令

在宏中扩展铸造命令

我目前正在尝试加快使用minted。我希望能够让某些语言特征(和样式)在minted环境中和文本内联中以相同的方式显示,因此我制作了以下命令。

\newmint[ccode]{c}{}
\def\while{\ccode|while|\xspace}

效果很好,并生成了具有正确颜色的单词“while”。但是,minted每次调用宏都会创建一个新文件,这大大增加了编译时间。因此,我尝试制作一个宏,它只展开一次,然后将结果放在每次调用中。

\newmint[ccode]{c}{}
\edef\while{\ccode|while|\noexpand\xspace}

但是,当我尝试这样做时,出现以下错误。

Argument of \reserved@a has an extra }.

是否可以让 TeX 进行\ccode|while|一次评估并将结果替换到其他地方?

答案1

如果字体大小没有改变,那么你可以把它放在一个框里:

\newsavebox\whilebox
\sbox\whilebox{\ccode|while|}
\newcommand*{\while}{\usebox\whilebox\xspace}

基于扩展的解决方案将不起作用,因为\ccode使用不可扩展的东西。

答案2

您可以考虑使用PythonTeX来突出显示您的代码;它使用与 minted 相同的 Pygments 库。PythonTeX 会保存所有突出显示的内容,因此您只需在修改代码时运行外部 Python 程序,而不必在刚修改文档时运行外部 Python 程序。当 PythonTeX 突出显示代码时,它的开销比 minted 少(如果您有兴趣,请参阅我的回答末尾的更多技术细节)。作为额外的好处,您可以暂时关闭突出显示(包选项pygments=false),在这种情况下,您的文档将在无需运行任何外部工具的情况下进行编译。当您进行大量代码编辑时,这会非常有用。并且 PythonTeX 支持代码中的 Unicode 字符,以防您需要用非英语语言排版带有注释的代码(您需要 inputenc 和 fontenc 才能使其工作)。

PythonTeX 提供了一个\pygment用于内联使用的命令、一个pygments环境和一个\inputpygments用于引入外部代码文件的命令。它目前没有用于创建自定义命令和环境的内置命令,但可以使用类似

\newcommand{\ccode}{\pygment{c}}
\newcommand{\pygmentsC}{\begingroup\obeylines\pygmentsCnext}
\newcommand{\pygmentsCnext}[1][]{\endgroup\pygments[#1]{c}}
\newenvironment{ccodeblock}{\pygmentsC}{\endpygments}

PythonTeXfancyvrb在内部使用 来排版代码,因此你可以将fancyvrb选项传递给pygments环境(内联命令不接受选项,因为几乎所有选项实际上只适用于环境)。上例中的\pygmentsC\pygmentsCnext宏是必需的,以便环境ccodeblock可以接受fancyvrb参数,例如

\begin{ccodeblock}[numbers=left]
# include <iostream>

int main()
{
   std::cout << "Hello, world!\n";
}
\end{ccodeblock}

技术细节

PythonTeX 由一个 LaTeX 包和一个 Python 脚本组成。该包将所有需要突出显示的代码保存到一个带有分隔符的外部文件中。PythonTeX 脚本会突出显示此代码并将结果保存为单个文件中的宏。然后,下次编译文档时,包会引入此宏文件。(如果由于代码长度而将突出显示的代码保存在宏中会减慢速度,可以使用选项进行修复fvextfile。如果由于脚本尚未运行而导致突出显示的代码缺失,则包会发出警告。)

每个 PythonTeX 命令或环境都单独高亮显示(因此每个命令或环境\ccode|while|都会单独高亮显示),但所有高亮显示都由单个 Python 进程一次性完成。Minted 为每个宏使用一个新的 Python 进程和两个外部文件,因此开销更大。由于 PythonTeX 会保存高亮显示的结果,因此编译文档不需要运行脚本(除非代码已被修改)。您只需要在编辑文档时运行 LaTeX;当您编辑代码时,您需要再次运行 LaTeX + 脚本 + LaTeX。Minted 不会保存高亮显示的结果,因此每次编译文档时都必须再次高亮显示所有内容。

Minted 的优势确实在于突出显示的内容始终是最新的,尽管可以通过始终再次运行 LaTeX + 脚本 + LaTeX 使用 PythonTex 来实现这一点。

答案3

如果有人感兴趣的话,这里有两种方法可以在 ConTeXt 中做类似的事情vim模块。

第一种方法

vim 模块(实际上是它所基于的过滤器模块)提供了两个功能,可以组合起来确保不会多次调用外部语法高亮器。

第一个功能是,它绕过了不能在宏中使用的buffers限制。用于显示列表,因此它会在缓冲区排版后插入换行符。为了避免这种情况,只需将其包装在(这意味着文本不会跨行)。\start<vimtyping> ... \stop<vimtyping>buffers\hbox

\startbuffer[while]while\stopbuffer
\def\FIRSTWHILE{\hbox{\processCbuffer[while]}}

第二个功能是state=stop确保不运行语法高亮程序的选项。(此选项适用于您想要将文件发送给没有语法高亮程序的同事的情况)。

\def\SECONDWHILE{\hbox{\processCbuffer[state=stop][while]}}

现在剩下要做的就是定义一个宏,该宏在\FIRSTWHILE第一次调用时调用,\SECONDWHILE然后:

\def\WHILE
    {\FIRSTWHILE
     \glet\WHILE\SECONDWHILE}

将所有内容结合在一起:

\usemodule[vim]

\definevimtyping[C][syntax=c]

\startbuffer[while]while\stopbuffer

\def\FIRSTWHILE{\hbox{\processCbuffer[while]}}
\def\SECONDWHILE{\hbox{\processCbuffer[state=stop][while]}}

\def\WHILE
    {\FIRSTWHILE
     \glet\WHILE\SECONDWHILE}

\starttext
\dorecurse{10}{what \WHILE{} something }

\stoptext

请注意,使用此方法时,每次调用宏时缓冲区仍会写入文件(但文件 IO 相对较快);只是不会调用语法高亮程序。

第二种方法

第二种方法是使用 将字符串简单地保存到文件中\savebuffer,然后使用 对其进行排版\typeset<vim>file。这样做的好处是数据只写入文件一次(在每次多运行编译开始时),并且只有当字符串的 md5 总和发生变化时(实际上意味着,只有当字符串发生变化时)才会运行语法高亮器。

\usemodule[vim]

\definevimtyping[C][syntax=c]

\startbuffer[while]while\stopbuffer
\savebuffer[while][whatever] %Saved in \jobname-whatever.tmp

\def\FIRSTWHILE{\hbox{\typeCfile{\jobname-whatever.tmp}}}
\def\SECONDWHILE{\hbox{\typeCfile[state=stop]{\jobname-whatever.tmp}}}

\def\WHILE
    {\FIRSTWHILE
     \glet\WHILE\SECONDWHILE}

\starttext
\dorecurse{10}{what \WHILE{} something }

\stoptext

minted这种方法也可以在 LaTeX 中使用(但由于包不缓存结果,因此每次运行时语法高亮器都会运行一次)。

相关内容