我想修补\thepage
序言中的某个布尔\addcontentsline
值2*\c@page + 1
是否为真。我将代码简化为:
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\ExplSyntaxOn
\AtEndPreamble
{
\bool_if:NT \c_true_bool
{\patchcmd{\addcontentsline}{\thepage}{\int_eval:n {2 * \c@page + 1}}{}{\fail}}
}
% \patchcmd{\addcontentsline}{\thepage}{\int_eval:n {2 * \c@page + 1}}{}{\fail}
\ExplSyntaxOff
\makeatother
\begin{document}
\tableofcontents
\section{test}
\end{document}
.toc
里面的东西
\contentsline {section}{\numberline {1}test}{\int _eval:n {2*\c@page +1}}{}%
我们可以看到 是\int _eval:n {2*\c@page +1}
错误的,没有计算3
。但是,如果我删除\AtEndPreamble
或\bool_if:NT \c_true_bool
,我会得到
\contentsline {section}{\numberline {1}test}{1}{}%
1
仍然是1
。这似乎“\patchcmd”仅在组中起作用,是否有“全局”版本?
如果我删除两个命令并删除该组,1
则会更改为3
,这正是我想要的。
我的问题是为什么会发生这种情况以及我该如何解决它?
编辑:在此示例中,@user202729 的方法有效。但是,如果我添加hyperref
,\thepage
则\addcontentsline
不再可修补。
[debug] tracing \ifpatchable on input line 23
[debug] analyzing '\addcontentsline'
[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
答案1
您的代码存在一些问题,主要问题是由于类别代码造成的。发生了什么?
您需要
\ExplSyntaxOn
按顺序编写代码,以便 TeX 能够正确解释\int_eval:n
。修补程序在不同的上下文中执行,此时
\ExplSyntaxOn
不再有效。
这就是\patchcmd
失败的地方。我们需要了解它的工作原理。在 TeX 中,如果不知道宏的参数文本到底是什么,就无法访问宏的替换文本,也无法知道参数文本到底是什么。我们只能通过 检查并保存参数和替换文本\meaning
,但这会将它们生成为类别代码为 12 个字符的字符串(但空格的类别代码为 10)。
它会这样做\patchcmd
,首先通过 重建宏的副本,以便\scantokens
将其与要修补的宏进行比较。如果比较失败,则\patchcmd
使用@
类别代码 11 并再次尝试重建和比较。如果这次尝试也失败了,则\patchcmd
放弃并告诉您该宏不可修补。否则它将继续执行修补。同样,它必须使用类别代码为 12 的字符串并执行\scantokens
。这就是您的案例失败的地方:当尝试修补时,_
类别代码为 8,但您的代码希望它具有类别代码 11,并且\patchcmd
根本无法知道这一点,除非您告诉它。
\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\ExplSyntaxOn
\AtEndPreamble
{
\bool_if:NT \c_true_bool
{
\ExplSyntaxOn
\patchcmd{\addcontentsline}{\thepage}{\int_eval:n {2 * \c@page + 1}}{}{\fail}
\ExplSyntaxOff
}
}
\ExplSyntaxOff
\makeatother
\begin{document}
\tableofcontents
\section{test}
\end{document}
这是因为要修补的宏没有空格,其参数或替换文本中_
也没有空格:
。否则修补会因与之前相同的原因而失败。
一个更简单的策略:在安全的地方进行修补并避免“奇怪”的类别代码。
\documentclass{article}
\usepackage{etoolbox}
\usepackage{hyperref}
\NewCommandCopy{\patchedaddcontentsline}{\addcontentsline}
\patchcmd{\patchedaddcontentsline}{\thepage}{\makeodd{page}}{}{\fail}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\makeodd}{m}
{
\int_eval:n {2*\value{#1}+1}
}
\AtEndPreamble
{
\bool_if:NT \c_true_bool
{
\RenewCommandCopy{\addcontentsline}{\patchedaddcontentsline}
}
}
\ExplSyntaxOff
\begin{document}
\tableofcontents
\section{test}
\end{document}
答案2
正如文档所述
该任务是本地的。
你不能使它全球化。
虽然...你可以这样作弊
\patchcmd{\addcontentsline}{\thepage}{\int_eval:n {2 * \c@page + 1}}{}{\fail} \global\let\addcontentsline\addcontentsline
这似乎有效,但我不确定它对保存堆栈有什么不良影响。
或者,复制的定义
\patchcmd
并添加\global
到适当的位置。对于 catcode 部分,如果文档中有你不明白的地方,不要直接跳过。重要信息都写在那里。
请注意,修补过程涉及对⟨command⟩的替换文本进行去标记化,并在修补后根据当前类别代码制度对其进行重新标记。@ 符号的类别代码暂时设置为 11。如果⟨command⟩的替换文本包含任何具有非标准类别代码的标记,修补前必须调整相应的类别代码。
尤其
- 这里的“替换文本”由组成
\int_eval:n {2 * \c@page + 1}
。 \int_eval:n
其中包括具有非标准类别代码(\ExplSyntaxOn
区域)的标记。
所以一种方法就是这样做。
\documentclass{article} \usepackage{etoolbox} \makeatletter \ExplSyntaxOn \AtEndPreamble { \bool_if:NT \c_true_bool { \ExplSyntaxOn \patchcmd{\addcontentsline}{\thepage}{\int_eval:n {2 * \c@page + 1}}{}{\fail} \ExplSyntaxOff } } % \patchcmd{\addcontentsline}{\thepage}{\int_eval:n {2 * \c@page + 1}}{}{\fail} \ExplSyntaxOff \makeatother \begin{document} \tableofcontents \section{test} \end{document}
对于
\makeatletter
和\makeatother
,您恰好不需要,因为它是自动的,如上面的文档中所述。但对于 expl3 代码,您确实需要它。附注:您仍然需要外部
\ExplSyntaxOn
,您无法更改命令参数内的 catcode,请参阅宏 - 为什么 \makeatletter 在 \newcommand 中不起作用? - TeX - LaTeX Stack Exchange。或者,使用如下包装器命令,让 ⟨replacement text⟩ 在正常的 catcode 下是安全的。
\documentclass{article} \usepackage{etoolbox} \makeatletter \ExplSyntaxOn \cs_new:Npn \myExpandableCommandSafeUnderNormalCatcode { \int_eval:n {2 * \c@page + 1} } \AtEndPreamble { \bool_if:NT \c_true_bool { \patchcmd{\addcontentsline}{\thepage}{\myExpandableCommandSafeUnderNormalCatcode}{}{\fail} } } % \patchcmd{\addcontentsline}{\thepage}{\int_eval:n {2 * \c@page + 1}}{}{\fail} \ExplSyntaxOff \makeatother \begin{document} \tableofcontents \section{test} \end{document}
- 这里的“替换文本”由组成
答案3
使用\inteval
代替\int_eval:n
。您也可以\c@page
用以下方式替换\value{page}
:
\documentclass{article}
\usepackage{etoolbox}
\usepackage{hyperref}
\makeatletter
\ExplSyntaxOn
\AtEndPreamble
{
\bool_if:NT \c_true_bool
{
\patchcmd{\addcontentsline}{\thepage}{\inteval {2 * \value{page} + 1}}{}{\fail}
}
}
\ExplSyntaxOff
\makeatother
\begin{document}
\tableofcontents
\section{test}
\end{document}