我理解patchcmd
软件包提供了一个命令\patchcommand
,可用于在现有宏的替换文本的开头和/或结尾添加材料。它适用于具有任意数量常规参数的宏,但对于具有可选参数的宏有限制。
xpatch
被引入以解决上述限制并提供一些附加功能。
现在,当我尝试通过阅读用户文档来理解上述软件包的用法时(补丁命令,补丁),我感觉他们相对于其他软件包文档来说,转向实现细节有点太快了。没有足够的解释或示例。
我也尝试通过阅读来理解这些概念TeX FAQ 的相关部分并且或多或少理解了那里所呈现的一切。然而,这还不够。
那么,您认为我们可以要求一个关于patchcmd
和用法的完整教程吗?解释 、和以及、和等xpatch
相关命令的用法并提供足够的示例就足够了。\patchcmd
\pretocmd
\apptocmd
\xpatchcmd
\xpretocmd
\xapptocmd
答案1
首先,我们要注意的是,这\patchcommand
是包中定义的一个命令,其性质与所提供的和概括的patchcmd
命令完全不同。etoolbox
xpatch
我认为patchcmd
和\patchcommand
已经过时:使用可以获得etoolbox
的完整功能,并且使用 可以实现更多功能。\patchcommand
\pretocmd
\apptocmd
\patchcmd
etoolbox
在介绍一些历史之后,我将描述\pretocmd
和。在开发过程中,Philipp Lehmann 发现自己需要重新定义 LaTeX 内核、类或包中的几个命令,以使它们与新的引用命令兼容。为了便于理解,以下是 中的一些代码:\apptocmd
\patchcmd
biblatex
biblatex1.sty
\patchcmd\@footnotetext
{\@makefntext}
{\toggletrue{blx@footnote}\@makefntext}
{\togglefalse{blx@tempa}}
{}
\@footnotetext
在排版脚注时会调用该命令:它会进行一些簿记,然后它会调用\@mkfntext
并biblatex
希望在执行此工作时将切换设置为 true。但是,加载\@footnotetext
时的定义不同footmisc
,因此重新定义将需要检查此包的加载,从而导致代码重复。由于也footmisc
使用\@makefntext
,因此\patchcmd
可能只使用一个代码。但是,如果修补不成功(如果scrbook
是文档类,则可能不成功),则会设置另一个切换,以便biblatex
可以采取适当的操作。(阅读包文件了解更多信息,这只是一个大致的描述。)
主要命令是\pretocmd
、\apptocmd
和\patchcmd
;还有\preto
、、\gpreto
和\appto
,\gappto
但它们仅适用于无参数宏,因此它们在这里并不真正相关。
语法很简单:
\pretocmd{<command>}{<code>}{<success>}{<failure>}
\apptocmd{<command>}{<code>}{<success>}{<failure>}
\patchcmd{<command>}{<code to replace>}{<code>}{<success>}{<failure>}
对于\pretocmd
和 ,\apptocmd
将<code>
添加到或附加到宏替换文本。因此
\newcommand{\foo}[2]{-#1-#2-}
\apptocmd{\foo}{(#2)}{}{}
相当于
\newcommand{\foo}[2]{-#1-#2-(#2)}
没有人会使用两个命令而不是一个命令,但通常这些命令\foo
是由某个包定义的,我们想为其添加功能。使用 中的一个参数不是强制性的<code>
,如 的示例\@footnotetext
所示:它可以是任何参数,只要它不使用宏不可用的参数:如果将宏定义为具有两个参数,则不可能使用的功能<code>
使其具有三个参数。etoolbox
因为\patchcmd
我们有一个参数 more,包含<code to replace>
,表示要在宏的替换文本中搜索的代码:在上面的例子中,要搜索的代码是\@makefntext
(更准确地说是第一的宏的替换文本中出现它)。这样的代码段将完全被替换<code>
。所以
\newcommand{\foo}[2]{-#1-#2-(#2)}
\patchcmd{\foo}{-#2-}{X}{}{}
相当于
\newcommand{\foo}[2]{-#1X(#2)}
<success>
和参数<failure>
是必需的,但除非有人在编写软件包,否则它们没什么用。在测试期间,在第一个补丁之前发布,更有用,
\tracingpatches
这将在日志文件和终端上提供信息性消息,例如
[debug] tracing \patchcmd on input line 203
[debug] analyzing '\foo'
[debug] ++ control sequence is defined
[debug] ++ control sequence is a macro
[debug] ++ macro can be retokenized cleanly
[debug] ++ search pattern found in replacement text
[debug] ++ patching possible
[debug] == retokenizing macro now
或者
[debug] tracing \patchcmd on input line 203
[debug] analyzing '\foo'
[debug] ++ control sequence is defined
[debug] ++ control sequence is a macro
[debug] ++ macro can be retokenized cleanly
[debug] -- search pattern not found in replacement text
甚至
[debug] tracing \patchcmd on input line 203
[debug] analyzing '\foo'
[debug] ++ control sequence is defined
[debug] ++ control sequence is a macro
[debug] -- macro cannot be retokenized cleanly
[debug] -> the macro may have been defined under a category
[debug] code regime different from the current one
[debug] -> the replacement text may contain special control
[debug] sequence tokens formed with \csname...\endcsname;
[debug] -> the replacement text may contain carriage return,
[debug] newline, or similar characters
一个简单而有用的例子。book
该类在表格和图形列表中添加一些垂直空间,以分隔属于不同章节的项目。这是由宏完成的\@chapter
,我们在中找到book.cls
,
\def\@chapter[#1]#2{\ifnum \c@secnumdepth >\m@ne
[...initial code irrelevant for the problem...]
\addtocontents{lof}{\protect\addvspace{10\p@}}%
\addtocontents{lot}{\protect\addvspace{10\p@}}%
[...final code irrelevant for the problem...]
}
如果我们想删除该空间,我们可以使用\patchcmd
:
\makeatletter % the macro name contains @
\patchcmd{\@chapter}
{\addtocontents{lof}{\protect\addvspace{10\p@}}%
\addtocontents{lot}{\protect\addvspace{10\p@}}}
{}% remove those tokens
{}{}% <success> and <failure> code not needed
\makeatother
如果我们想在每个部分之前添加分页符,可以使用另一个简单的补丁:
\pretocmd{\section}{\clearpage}{}{}
(在这种情况下也\preto
可以使用)。
xpatch
该软件包xpatch
是对 提供的修补命令的扩展etoolbox
。它提供了命令\xpretocmd
,\xapptocmd
以及\xpatchcmd
,可以用作不带 的相应命令的直接替换x
;那么,它们有什么区别呢?
使用 提供的命令,etoolbox
很难修补带有可选参数或声明为健壮的命令(即用 定义\DeclareRobustCommand
)。因此,如果有人想修补\cite
(只是为了举一个愚蠢的例子),使用 的技巧\patchcmd
是
\expandafter\patchcmd\csname cite \endcsname
{<code to replace>}
{<code>}
{<success>}{<failure>}
修补由(带有第一个可选参数)定义的命令\newcommand{\foo}[2][baz]{...}
需要
\expandafter\patchcmd\csname\string\foo\endcsname
{<code to replace>}
{<code>}
{<success>}{<failure>}
对于\pretocmd
和也类似\apptocmd
。xpatch
它更简单,不需要了解命令的内部实现:
\xpatchcmd{\cite}
{<code to replace>}
{<code>}
{<success>}{<failure>}
\xpatchcmd{\foo}
{<code to replace>}
{<code>}
{<success>}{<failure>}
会起作用,因为命令\xpatchcmd
(或\xpretocmd
和\xapptocmd
)会处理什么宏来修补。
此外,还xpatch
定义了一组类似的命令来管理biblatex
“类似宏”的内部函数,但使用方式不同,例如\usebibmacro{name}
。对于这些,可以使用
\xpretobibmacro \xapptobibmacro \xpatchbibmacro
\xpretobibdriver \xapptobibdriver \xpatchbibdriver
\xpretofieldformat \xapptofieldformat \xpatchfieldformat
\x.....nameformat
\x.....listformat
\x.....indexfieldformat
\x.....indexnameformat
\x.....indexlistformat
还有
\xshowcmd \xshowbibmacro \xshownameformat \xshowlistformat
\xshowindexfieldformat \xshowindexnameformat \xshowindexlistformat
用于在终端(和日志文件)上查看内部实现,这在启动补丁时应该始终完成,以便查看什么命令确实如此。
请注意,内部xpatch
使用\pretocmd
、\apptocmd
和\patchcmd
。
regexpatch
实验包独立地regexpatch
重新实现了\x...
命令,并且还添加了xpatch
etoolbox
\regex...
上面列出的每个命令的变体,以及一些其他实用程序。
显示命令
上面概述的相同问题也适用于;如果您想知道(使用已经提到的命令)\show
的定义,您应该这样做\cite
\expandafter\show\csname cite \endcsname
但xpatch
你有\xshowcmd
,\xshowcmd\cite
会选择正确的命令来查看和生产
> \cite =\long macro:
->\@ifnextchar [{\@tempswatrue \@citex }{\@tempswafalse \@citex []}.
而\show\cite
只会输出
> \cite=macro:
->\protect \cite .
还有regexpatch
一个 * 形式并\xshowcmd*\cite
打印更多诊断信息:
*************************************************
* xpatch message
* `\cite' is a control word defined with \DeclareRobustCommand
*************************************************
> \cite =\long macro:
->\@ifnextchar [{\@tempswatrue \@citex }{\@tempswafalse \@citex []}.
答案2
谢谢@egreg的回答,我终于找到了一个例子,演示了如何修补和\show
,\newcommand
否则会有一些保护(见\newcommand[.][.]{.} 定义的命令是否强大?)。
问题是,当你\show
使用时\newcommand
,你只会得到类似“ ”的东西\@protected@testopt \mycmd \\mycmd {}.
,而不是命令的“真正”内部内容;如果你尝试在不采取预防措施的情况下修补此命令,然后再次执行一个简单的\show
,你会看到你的附加实际上是在\@protected@testopt
- 之后,而不是在“内部”命令定义的末尾。修补和显示的答案\newcommand
是使用\expandafter
s,特别是带有 的那个\space
,正如@egreg 的答案所示。请注意,当没有 时进行修补时\expandafter
,\tracingpatches
不会给出错误 - 但只有采取预防措施,它才会注意到“宏可以干净地重新标记“。
下面是一个演示此情况的 MWE(相关终端输出粘贴在里面作为%%
注释):
\documentclass{book}
\usepackage{etoolbox}
\newcommand\mycmd[2][]{%
\typeout{inside mycmd...}
}
\show\mycmd
%% > \mycmd=macro:
%% ->\@protected@testopt \mycmd \\mycmd {}.
\tracingpatches
% \apptocmd\mycmd{\typeout{appended line.}}{\typeout{success}}{\typeout{failure}}
%% [debug] tracing \apptocmd on input line 13
%% [debug] analyzing '\mycmd'
%% [debug] ++ control sequence is defined
%% [debug] ++ control sequence is a macro
%% [debug] ++ control sequence is a macro without parameters
%% [debug] == redefining macro now
%% success
% \show\mycmd
%% > \mycmd=macro:
%% ->\@protected@testopt \mycmd \\mycmd {}\typeout {appended line.}.
%% l.24 \show\mycmd
% % https://tex.stackexchange.com/questions/152773/please-tutor-the-usage-of-patchcmd-and-xpatch
% \expandafter\apptocmd\csname mycmd\endcsname{\typeout{appended line.}}{\typeout{success}}{\typeout{failure}}
%% [debug] tracing \apptocmd on input line 13
%% [debug] analyzing '\mycmd'
%% [debug] ++ control sequence is defined
%% [debug] ++ control sequence is a macro
%% [debug] ++ control sequence is a macro without parameters
%% [debug] == redefining macro now
%% success
% \show\mycmd
%% > \mycmd=macro:
%% ->\@protected@testopt \mycmd \\mycmd {}\typeout {appended line.}.
%% l.24 \show\mycmd
\expandafter\apptocmd\csname\string\mycmd\endcsname{\typeout{appended line.}}{\typeout{success}}{\typeout{failure}}
%% [debug] tracing \apptocmd on input line 28
%% [debug] analyzing '\\mycmd'
%% [debug] ++ control sequence is defined
%% [debug] ++ control sequence is a macro
%% [debug] ++ control sequence is a macro with parameters
%% [debug] ++ macro can be retokenized cleanly
%% [debug] == retokenizing macro now
%% success
% \show\mycmd
%% > \mycmd=macro:
%% ->\@protected@testopt \mycmd \\mycmd {}.
%% l.30 \show\mycmd
% this is how to \show a \newcommand macro:
\expandafter\show\csname\string\mycmd\endcsname
%% > \\mycmd=\long macro:
%% [#1]#2->\typeout {inside mycmd...} \typeout {appended line.}.
%% <recently read> \\mycmd
\begin{document}
\end{document}