请指导一下patchcmd和xpatch的使用

请指导一下patchcmd和xpatch的使用

我理解patchcmd软件包提供了一个命令\patchcommand,可用于在现有宏的替换文本的开头和/或结尾添加材料。它适用于具有任意数量常规参数的宏,但对于具有可选参数的宏有限制。

xpatch被引入以解决上述限制并提供一些附加功能。

现在,当我尝试通过阅读用户文档来理解上述软件包的用法时(补丁命令补丁),我感觉他们相对于其他软件包文档来说,转向实现细节有点太快了。没有足够的解释或示例。

我也尝试通过阅读来理解这些概念TeX FAQ 的相关部分并且或多或少理解了那里所呈现的一切。然而,这还不够。

那么,您认为我们可以要求一个关于patchcmd和用法的完整教程吗?解释 、和以及、和等xpatch相关命令的用法并提供足够的示例就足够了。\patchcmd\pretocmd\apptocmd\xpatchcmd\xpretocmd\xapptocmd

答案1

首先,我们要注意的是,这\patchcommand是包中定义的一个命令,其性质与所提供的和概括的patchcmd命令完全不同。etoolboxxpatch

我认为patchcmd\patchcommand已经过时:使用可以获得etoolbox的完整功能,并且使用 可以实现更多功能。\patchcommand\pretocmd\apptocmd\patchcmd

etoolbox

在介绍一些历史之后,我将描述\pretocmd和。在开发过程中,Philipp Lehmann 发现自己需要重新定义 LaTeX 内核、类或包中的几个命令,以使它们与新的引用命令兼容。为了便于理解,以下是 中的一些代码:\apptocmd\patchcmdbiblatexbiblatex1.sty

\patchcmd\@footnotetext
   {\@makefntext}
   {\toggletrue{blx@footnote}\@makefntext}
   {\togglefalse{blx@tempa}}
   {}

\@footnotetext在排版脚注时会调用该命令:它会进行一些簿记,然后它会调用\@mkfntextbiblatex希望在执行此工作时将切换设置为 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和也类似\apptocmdxpatch它更简单,不需要了解命令的内部实现:

\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...命令,并且还添加了xpatchetoolbox

\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是使用\expandafters,特别是带有 的那个\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}

相关内容