问题
如何使用\CheckCommand
强大的命令?
背景
LaTeX 提供\CheckCommand
(仅供序言使用),因此您可以确保命令具有预期的定义:
\newcommand{\hi}{hi}
\CheckCommand{\hi}{hi}
可以正常工作,没有任何警告,但是
\newcommand{\hi}{hi}
\CheckCommand{\hi}{bonjour}
将导致 LaTeX 发出警告:
LaTeX Warning: Command \hi has changed.
Check if current package is valid.
然而,该警告系统却被 LaTeX 针对强命令的保护机制所阻止:
\DeclareRobustCommand{\hi}{hi}
\CheckCommand{\hi}{hi}
发出警告,因为\hi
现在是\protect\hi
,而不是hi
。(见脆弱命令和坚固命令之间有什么区别?例如。)
如何使用\CheckCommand
强大的命令?
参考
答案1
我引入了一个新命令\DefineRobustCommand
来代替 LaTeX 的\DeclareRobustCommand
。为什么?好吧,如果你将 的定义\DefineRobustCommand
与 的定义进行比较,你就会明白原因\DeclareRobustCommand
。\DeclareRobustCommand
在父命令上使用尾随空格来定义唯一的辅助命令,但这不是这里的问题。一个原因是\DeclareRobustCommand
不适用于单字母命令/定义,例如
\DeclareRobustCommand~[1][]{\def\x{#1}}
和
\expandafter\@gobble\string#1
从 的定义可以看出\DeclareRobustCommand
, 定义的辅助命令\DeclareRobustCommand
无法区分命令符号。
如果要“检查”已定义的命令,请使用\DefineRobustCommand
而不是\DeclareRobustCommand
。您可以使用\VerifyCommand
来检查所有 LaTeX 定义的命令,但 定义的命令除外\DeclareRobustCommand
。
\VerifyCommand
如果命令与之前的定义不匹配,将会发出警告,而\XVerifyCommand
在这种情况下将会发出错误。
\documentclass{article}
\makeatletter
\newtoks\amtemptoks
\def\iffurcate#1\fi{%
#1\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
}
\def\ifcmddef#1{%
\iffurcate\ifdefined#1\fi{%
\iffurcate\ifx#1\relax\fi\@secondoftwo\@firstoftwo
}{%
\@secondoftwo
}%
}
\def\ifnamedef#1{\expandafter\ifcmddef\csname#1\endcsname}
\def\DefineRobustCommand{\@star@or@long\define@robustcommand}
\def\define@robustcommand#1{%
\ifx#1\@undefined\else\ifx#1\relax\else
\@latex@info{Redefining \string#1}%
\fi\fi
\edef\am@tempa{\string#1}%
\def\am@tempb{#1}%
\edef\am@tempb{\expandafter\strip@prefix\meaning\am@tempb}%
\edef#1{%
\ifx\am@tempa\am@tempb\unexpanded{\am@xprotect#1}\fi
\noexpand\protect
\expandafter\noexpand\csname\string#1@RoB\endcsname
}%
\let\@ifdefinable\@rc@ifdefinable
\expandafter\new@command\csname\string#1@RoB\endcsname
}
\def\am@xprotect#1#2#3{%
\iffurcate\ifx\protect\@typeset@protect\fi{#3}{\protect#1}%
}
\def\VerifyCommand{\def\am@switch{0}\@star@or@long\am@checkcommand}
\def\XVerifyCommand{\def\am@switch{1}\@star@or@long\am@checkcommand}
\def\am@checkcommand#1#2#{\am@ch@ckcommand#1{#2}}
\long\def\am@ch@ckcommand#1#2#3{%
\let\am@resa\relax
\expandafter\let\csname\string\am@resa\endcsname\relax
\new@command\am@resa#2{#3}%
\amtemptoks{%
\ifnamedef{\@backslashchar\string#1@RoB}{%
\expandafter\let\csname\string\am@resa@RoB\endcsname\relax
\expandafter\let\csname\@backslashchar\string\am@resa@RoB\endcsname\relax
\define@robustcommand\am@resa#2{#3}%
\expandafter\am@checkeqcmd
\csname\@backslashchar\string\am@resa@RoB\expandafter\endcsname
\csname\@backslashchar\string#1@RoB\endcsname
#1%
}{%
\am@checkeqcmd#1\am@resa#1%
}%
}%
\ifnamedef{\string#1}{%
\@expandtwoargs\in@{\detokenize{\@protected@testopt}}
{\expandafter\meaning\csname\string#1\endcsname}%
\ifin@
\expandafter\am@checkeqcmd
\csname\string\am@resa\expandafter\endcsname
\csname\string#1\endcsname#1%
\else
\the\amtemptoks
\fi
}{%
\the\amtemptoks
}%
}
\def\am@checkeqcmd#1#2#3{%
\def\am@ch@ckeqcmd{Command \noexpand#3 has changed.
\MessageBreak Check that there is nothing wrong}%
\ifx#1#2\else
\if1\am@switch
\@latexerr\am@ch@ckeqcmd\@ehd
\else
\@latex@warning@no@line\am@ch@ckeqcmd
\fi
\fi
}
\makeatother
% Tests
\newcommand\hy{Hi}
\VerifyCommand\hy{Hi}
\XVerifyCommand\hy{Hi}
\newcommand\hiy[2]{Hi #1, #2}
\XVerifyCommand\hiy[2]{Hi #1, #2}
% \XVerifyCommand\hiy{Hi, -changed-}
\DefineRobustCommand\hiz[2][x]{Hi (#1***#2)}
\VerifyCommand\hiz[2][x]{Hi (#1***#2)}
% \VerifyCommand\hiz[2][x]{Hi-changed [#1***#2]}
\XVerifyCommand\hiz[2][x]{Hi (#1***#2)}
% \XVerifyCommand\hiz[2][x]{Hi-changed [#1***#2]}
% \| is defined as a delimiter. Defining | as a robust command
% will not change that:
\begingroup
\lccode`~=`|
\lowercase{\endgroup
\DefineRobustCommand~[2][x]{\def\x{#1***#2}}
}
{\catcode`|=13
\XVerifyCommand|[2][x]{\def\x{#1***#2}}
% \XVerifyCommand|[2][x]{-changed-\def\x{#1***#2}}
%|{y}
}
\begin{document}
x
\end{document}
答案2
你现在可能已经知道,\DeclareRobustCommand{\cs}{...}
定义二命令:本质上它确实
\def\cs{\protect\cs•}
\def\cs•{...}
•
其中我用命令名称中的空格表示(这通常是不可能的)。当 LaTeX 在正常排版中发现它时,\protect
相当于\relax
,因此扩展\cs•
就是所要求的。当 LaTeX 写入辅助文件时,\protect
是\noexpand
,因此效果是写入字符串\cs
后跟一个空格(当输入辅助文件时,该空格将不被视为名称的一部分,但这是无关紧要的)。
因此,为了检查一个健壮的命令,您必须查看的扩展\cs•
,但您必须事先知道该命令是否健壮。
情况进一步复杂化,因为\1
after的扩展\DeclareRobustCommand\1{...}
是
\x@protect\1\protect\1•
(再次 • 表示名称中的空格)。
在软件包中xpatch
,我使用宏( ) 或(regexpatch)regexpatch
检查了替换文本的各种可能性。您可以看到列表很长。\xpatch_main:NN
xpatch
\xpatch_main_check:N
如果你知道该命令已\DeclareRobustCommand
定义
\def\CheckRobustCommand#1{%
\expandafter\CheckCommand\csname\expandafter\@gobble\string#1\space\endcsname
}% (AM)
\CheckCommand
就像您想要的那样使用。
一个简单的版本,只检查“带有尾随空格的命令”是否定义,可以是
\makeatletter
\def\CheckRobustCommand#1{%
\expandafter\CheckCommand\csname\expandafter\@gobble\string#1\space\endcsname
}% (AM)
\def\xCheckCommand#1{%
\ifcsname\expandafter\@gobble\string#1\space\endcsname
\expandafter\CheckRobustCommand
\else
\expandafter\CheckCommand
\fi
}
\makeatother
该命令\xCheckCommand
可用于 和 定义的命令\newcommand
。\DeclareRobustCommand
(AM)