如何使用 expl3 检测代码当前是否位于前导码中?

如何使用 expl3 检测代码当前是否位于前导码中?

我想知道使用 expl3 测试命令是在前导码内还是在主体内运行的标准方法。我目前的方法是使用:

\cs_if_eq:NNTF \@onlypreamble \@notprerr
  { code if not in the preamble }
  { code if in the preamble }

更准确地说,当命令的某个部分需要位于主体内部时(例如,当它尝试修改一些延迟的宏时),如果命令正在前导码中运行\AtEndPreamble,我倾向于使用以下内容\__atbegindocument_if_needed:n\AtBeginDocument

\cs_new:Nn \__nochange:n {#1}
\cs_new:Nn \__atbegindocument:n {#1}
\cs_new:Nn \__atbegindocument_if_needed:n
  {
    \cs_if_eq:NNTF \@onlypreamble \@notprerr
      {
        \cs_gset_eq:NN \__atbegindocument:n \__nochange:n
      }
      {
        \cs_gset_eq:NN \__atbegindocument:n \AtBeginDocument
      }
    \__atbegindocument:n {#1}
  }

虽然可行,但是这个方法似乎不太优雅。

答案1

我希望我理解正确了这个问题的意思——让我回顾一下。如果我没有理解正确,请在评论中澄清:

处理参数的命令将根据它是在序言中还是在文档环境中执行而表现出不同的行为:

在序言中,应该执行\AtBeginDocument{<argument>}

在文档环境中,它应该只是传达其论点。

正如 gusbrs 在他的评论,使用最新的 TeX 发行版,其中有新的钩子系统可用,您可以将内容添加到一次性钩子中,begindocument或者 — 如果要添加到钩子中的材料是要排版或要在文档中执行 — 添加到钩子begindocument/end

只需执行\AddToHook{begindocument}{<Tokens to add>}\AddToHook{begindocument/end}{<Tokens to add>}

如果一次性挂钩已经执行,<Tokens to add>将立即处理。

(有关 LaTeX 的新钩子系统的信息可以在 Frank Mittelbach 撰写的“LaTeX 的钩子管理”以及source2e.pdf,“文件 h: lthooks.dtx”部分以及 source2e.pdf,“文件 i: ltcmdhooks.dtx”部分以及source2e.pdf,“文件 T: ltfilehook.dtx”部分

如果你正在使用较新的 TeX 发行版,而新的钩子系统不可用:

测试是否\@onlypreamble等同于\@notprerr对我来说似乎没问题。
但是,我不知道如何用文档类/包来处理,例如独立或者结合

如果您不介意在每次调用命令时 重复执行\ifx-test 来检查是否相等 - 是 -test 的 expl3 包装器,您可以将该测试包装在:\@onlypreamble\@notprerr\cs_if_eq:NNTF\ifx\use:c

\documentclass{article}
\ExplSyntaxOn
\cs_new_protected:Npn \__atbegindocument_if_needed:n {
  \use:c {
     \cs_if_eq:ccTF {@onlypreamble} {@notprerr} {use:n} {AtBeginDocument}
  }
}

\__atbegindocument_if_needed:n {This~should~be~added~to~the~AtBeginDocument-hook.}

\ExplSyntaxOff

\begin{document}

\ExplSyntaxOn
\__atbegindocument_if_needed:n {This~should~be~carried~out~immediately.}
\ExplSyntaxOff
  
\end{document}

在此处输入图片描述

答案2

这是的定义\@onlypreamble

% latex.ltx, line 1168:
\def\@onlypreamble#1{%
  \expandafter\gdef\expandafter\@preamblecmds\expandafter{%
       \@preamblecmds\do#1}}

宏将包含\begin{document}以下\@preamblecmds形式的项目列表

\do<command>

从...开始\do\@onlypreamble\do\@preamblecmds

在处理时\begin{document},LaTeX 会

  \gdef\do##1{\global\let ##1\@notprerr}%
  \@preamblecmds

所以列表中的命令\let都是\@notprerr

不太清楚您想要实现什么。但通常来说,在序言和文档中使用相同的命令执行截然不同的操作并不是最好的用户界面。

如果你想这样做,你的包/类可以这样做

\newcommand\foo{whatever you want in the preamble}
\AtBeginDocument{%
  \renewcommand\foo{whatever you want in the document}
}

答案3

灵感来自@egreg 的回答

\ExplSyntaxOn
\bool_new:N \g_mylib_preamble_bool
\bool_gset_true:N \g_mylib_preamble_bool

% the same as \AtBeginDocument{ \bool_gset_... }
\hook_gput_code:nnn {begindocument} {top-level} 
  { \bool_gset_false:N \g_mylib_preamble_bool }

% in your code
\bool_if:NTF \g_mylib_preamble_bool
  {<true code>}{<false code>}
\ExplSyntaxOff

注意检测\g_mylib_preamble_bool里面的值\begin{document},特别是在钩子上begindocument仍然不准确。

相关内容