如何使用 \CheckCommand 与强大的命令?

如何使用 \CheckCommand 与强大的命令?

问题

如何使用\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•,但您必须事先知道该命令是否健壮。

情况进一步复杂化,因为\1after的扩展\DeclareRobustCommand\1{...}

\x@protect\1\protect\1•

(再次 • 表示名称中的空格)。

在软件包中xpatch,我使用宏( ) 或(regexpatch)regexpatch检查了替换文本的各种可能性。您可以看到列表很长。\xpatch_main:NNxpatch\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)

相关内容