当我以为我理解了包的概念时doc
,LaTeX 证明我错了。
我试图制作一个文件来使用,ltxdoc
然后我意识到我不能在序言中使用条件,因为它们会让 TeX 从那时起读取序言中的内容,从而破坏事情,因为它不应该。然后我尝试将该代码 MWE 化,却发现了一个更大的问题。
当我以为我理解了条件和扩展的概念时,TeX 证明我错了:)
考虑以下代码(已编号行以便于阅读):
01 % \tracingall
02 % \iffalse meta-comment ^^A This /iffalse
03 \documentclass{ltxdoc}
04 \def\showfi{\immediate\write0{{/fi\ on line \the\inputlineno}}}%
05 \title{Doc Test}%
06 \newif\ifitsme\itsmetrue
07 \ifitsme\author{Me :)}%
08 \else \author{Not me :/}%
09 \fi\showfi
10 \begin{document}%
11 \DocInput{\jobname}%
12 \tracingnone
13 \end{document}%
14 % \fi\showfi ^^A should match this /fi
15 % \fi\showfi ^^A but it's matching this one
16 % \tracingnone
17 % \maketitle
18 % \endinput
当 LaTeX 启动时,注释就是注释,所以一切都正常进行:加载ltxdoc
;定义\showfi
(记录在哪一行出现\fi
);给它一个标题以避免错误;定义一个新的\ifitsme
,将其设置为 true,这使其成为Me :)
作者。然后我\showif
以防万一(现在不重要)调用。
到目前为止,一切都很好...
然后我\DocInput
又打开了同一个文件。评论不再是评论了,事情变得一团糟:)
\DocInput
从开始处开始读取文件。我打开了全日志记录\tracingall
,然后是通常的来自\iffalse meta-comment
文件.dtx
。这\iffalse
通常会在\fi
之后的右侧\end{document}
继续读取源。
我认为应该做:
事实并非如此,因为当 TeX 发现错误条件时,它会跳过代码没有展开直到下一个\else
或\fi
,这里是\else
行内08
(因为 TeX 不会展开\ifitsme
来发现它是一个\iftrue
)。我期望 TeX 读取它\else
并执行\author{Not me :/}
,然后读取\fi
来自行09
并显示{/fi\ on line 9}
在日志中(然后另一个\begin{document}
会出现并破坏一切,但这是另一天的错误)。
什么做:
TeX 不会跳转到\fi
行09
,也不会跳转到行上14
,而是跳转到额外的 \fi
(我添加它是因为否则会引发错误)在第 行15
。日志显示:
{\iffalse: (level 1) entered on line 2}
{false}
{\fi: \iffalse (level 1) entered on line 2}
\showfi ->\immediate \write 0{{/fi\ on line \the \inputlineno }}
{\immediate}
\write->{/fi\ on line \the \inputlineno }
{/fi\ on line 15}
什么:
什么是 TeX实际上这样做是否不符合我的预期?为什么它不仅跳过了08 \else... 09 \if
,还跳过了,而没有(明显)充分的理由14 \fi
要求在那里插入一个额外的内容?\fi
不带行号的复制粘贴代码:
% \tracingall
% \iffalse meta-comment ^^A This /iffalse
\documentclass{ltxdoc}
\def\showfi{\immediate\write0{{/fi\ on line \the\inputlineno}}}%
\title{Doc Test}%
\newif\ifitsme\itsmetrue
\ifitsme\author{Me :)}%
\else \author{Not me :/}%
\fi\showfi
\begin{document}%
\DocInput{\jobname}%
\tracingnone
\end{document}%
% \fi\showfi ^^A should match this /fi
% \fi\showfi ^^A but it's matching this one
% \tracingnone
% \maketitle
% \endinput
答案1
正如您正确指出的那样,当您执行 时pdflatex test.dtx
,文件会读取%
注释字符。当 TeX 发现 时\DocInput
,它基本上会忽略%
行首并生成^^A
注释字符。因此,您的输入基本上是
% \iffalse meta-comment ^^A This /iffalse
\documentclass{ltxdoc}
\def\showfi{\immediate\write0{{/fi\ on line \the\inputlineno}}}%
\title{Doc Test}%
\newif\ifitsme\itsmetrue
\ifitsme\author{Me :)}%
\else \author{Not me :/}%
\fi\showfi
\begin{document}%
\iffalse meta-comment ^^A This /iffalse
\documentclass{ltxdoc}
\def\showfi{\immediate\write0{{/fi\ on line \the\inputlineno}}}%
\title{Doc Test}%
\newif\ifitsme\itsmetrue
\ifitsme\author{Me :)}%
\else \author{Not me :/}%
\fi\showfi
\begin{document}%
\DocInput{\jobname}%
\end{document}%
\fi\showfi ^^A should match this /fi
\fi\showfi ^^A but it's matching this one
\maketitle
\endinput
\end{document}%
% \fi\showfi ^^A should match this /fi
% \fi\showfi ^^A but it's matching this one
% \maketitle
% \endinput
现在\iffalse
已经看到了,但是\ifitsme
是在跳过的文本中出现了两次条件,因此\fi
需要查找两个匹配项。这就是问题所在,因为不再有匹配项\fi
,\iffalse
除非您像现在这样添加一个。
未注释的行中带有\fi
“应该匹配这个 /fi”的行在\ifitsme
之后匹配\newif
。
在条件语句中定义条件语句总是会出现这种问题。使用
\expandafter\newif\csname ifitsme\endcsname
因此当代码位于跳过的文本中时,根本不会有任何条件。
答案2
您的\newif
命令位于 内\iffalse
,因此看不到它,并且\ifitsme
现在根本不是“if”,或者如果\newif
已经先前执行过,那么\ifitsme
后面的\newif
也算数,并且您有一个太多的“if”:
这有效:
\documentclass{article}
\begin{document}
\newif\ifitsme
\iffalse
\ifitsme hello \fi
\fi
\end{document}
这失败了
\documentclass{article}
\begin{document}
\iffalse
\newif\ifitsme
\ifitsme hello \fi
\fi
\end{document}
这也失败了
\documentclass{article}
\begin{document}
\newif\ifitsme
\iffalse
\newif\ifitsme %\newif not seen, but \ifitsme so one fi missing
\ifitsme hello \fi
\fi
\end{document}
士气:最好不要\newif
在里面使用\if
—— \fi
。
答案3
这里有一个更安全地跳过内容的技巧。将其放在
\bgroup\catcode2 0 \catcode`\\ 12 ^^Biffalse
和
^^Bfi^^Begroup
这样做的目的是抑制启动控制序列。因此,您可以确定,在这期间对、、或等\
进行的任何操作都不会产生任何影响。\if
\fi
\else
\if...
0x02
并且它将起始控制序列的含义分配给 ascii 字节。我们可以做一件安全的^^Biffalse/^^Bfi
事情。
我没有使用^^A
文档为其分配注释字符,并且我们希望保留它(特别是如果编辑器适当地突出显示)。
我猜你的 MWE 看起来会像这样
% \tracingall
% \bgroup\catcode2 0 \catcode`\\ 12 ^^Biffalse
\documentclass{ltxdoc}
\def\showfi{\immediate\write0{{/fi\ on line \the\inputlineno}}}%
\title{Doc Test}%
\newif\ifitsme\itsmetrue
\ifitsme\author{Me :)}%
\else \author{Not me :/}%
\fi\showfi
\begin{document}%
\DocInput{\jobname.dtx}%
\tracingnone
\end{document}%
% \fi\showfi
% ^^Bfi^^Begroup
% \tracingnone
% \maketitle
% \endinput
文件另存为test.dtx
。
警告:虽然我使用 doc,但dtx
我从未使用过\DocInput
(因为它导致了我想要处理保护的方式的无穷无尽的问题,并且因为我不希望文档部分(不是代码实现,而是用户手册)被“注释掉”)所以我不知道这是否会是你的用例。