https://github.com/latex3/latex2e
Tip revision: 677a0faec3ada45402c67ba34d9a34e3dd9d5479 authored by Joseph Wright on 30 November 2022, 21:06:22 UTC
Step release tag
Step release tag
Tip revision: 677a0fa
lthooks.dtx
% \iffalse meta-comment
%
% Copyright (C) 2020-2022
% Frank Mittelbach, Phelype Oleinik & LaTeX Team
%
% This file is part of the LaTeX base system.
% -------------------------------------------
%
% It may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3c
% of this license or (at your option) any later version.
% The latest version of this license is in
% https://www.latex-project.org/lppl.txt
% and version 1.3c or later is part of all distributions of LaTeX
% version 2008 or later.
%
% This file has the LPPL maintenance status "maintained".
%
% The list of all files belonging to the LaTeX base distribution is
% given in the file `manifest.txt'. See also `legal.txt' for additional
% information.
%
% The list of derived (unpacked) files belonging to the distribution
% and covered by LPPL is defined by the unpacking scripts (with
% extension .ins) which are part of the distribution.
%
% \fi
%
% \iffalse
%
%%% From File: lthooks.dtx
%
% \begin{macrocode}
\def\lthooksversion{v1.0v}
\def\lthooksdate{2022/06/15}
% \end{macrocode}
%
%<*driver>
\documentclass{l3doc}
\providecommand\InternalDetectionOff{}
\providecommand\InternalDetectionOn{}
\EnableCrossrefs
\CodelineIndex
\begin{document}
\DocInput{lthooks.dtx}
\end{document}
%</driver>
%
% \fi
%
%
% \providecommand\hook[1]{\texttt{#1}}
%
% \providecommand\fmi[1]{\marginpar{\footnotesize FMi: #1}}
% \providecommand\fmiinline[1]{\begin{quote}\itshape\footnotesize FMi: #1\end{quote}}
% \providecommand\pho[1]{\marginpar{\footnotesize PhO: #1}}
% \providecommand\phoinline[1]{\begin{quote}\itshape\footnotesize PhO: #1\end{quote}}
%
%
%
% \title{\LaTeX{}'s hook management\thanks{This module has version
% \lthooksversion\ dated \lthooksdate, \copyright\ \LaTeX\
% Project.}}
%
% \author{Frank Mittelbach\thanks{Code improvements for speed and other goodies by Phelype Oleinik}}
%
% \maketitle
%
%
% \tableofcontents
%
% \section{Introduction}
%
% Hooks are points in the code of commands or environments where it
% is possible to add processing code into existing commands. This
% can be done by different packages that do not know about each
% other and to allow for hopefully safe processing it is necessary
% to sort different chunks of code added by different packages into
% a suitable processing order.
%
% This is done by the packages adding chunks of code (via
% \cs{AddToHook}) and labeling their code with some label by
% default using the package name as a label.
%
% At \verb=\begin{document}= all code for a hook is then sorted
% according to some rules (given by \cs{DeclareHookRule}) for fast
% execution without processing overhead. If the hook code is
% modified afterwards (or the rules are changed),
% a new version for fast processing is generated.
%
% Some hooks are used already in the preamble of the document. If
% that happens then the hook is prepared for execution (and sorted)
% already at that point.
%
%
% \section{Package writer interface}
%
% The hook management system is offered as a set of CamelCase
% commands for traditional \LaTeXe{} packages (and for use in the
% document preamble if needed) as well as \texttt{expl3} commands
% for modern packages, that use the L3 programming layer of
% \LaTeX{}. Behind the scenes, a single set of data structures is
% accessed so that packages from both worlds can coexist and access
% hooks in other packages.
%
%
%
% \subsection{\LaTeXe\ interfaces}
%
% \subsubsection{Declaring hooks}
%
% With a few exceptions, hooks have to be declared before they can
% be used. The exceptions are the generic hooks for commands and
% environments (executed at \cs{begin} and \cs{end}), and the
% hooks run when loading files (see section~\ref{sec:generic}).
%
% \begin{function}{\NewHook}
% \begin{syntax}
% \cs{NewHook} \Arg{hook}
% \end{syntax}
% Creates a new \meta{hook}.
% If this hook is declared within a package it is suggested
% that its name is always structured as follows:
% \meta{package-name}\texttt{/}\meta{hook-name}. If necessary you
% can further subdivide the name by adding more \texttt{/} parts.
% If a hook name is already taken, an error is raised and the hook
% is not created.
%
% The \meta{hook} can be specified using the dot-syntax to denote
% the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
% \begin{function}{\NewReversedHook}
% \begin{syntax}
% \cs{NewReversedHook} \Arg{hook}
% \end{syntax}
% Like \cs{NewHook} declares a new \meta{hook}.
% the difference is that the code chunks for this hook are in
% reverse order by default (those added last are executed first).
% Any rules for the hook are applied after the default ordering.
% See sections~\ref{sec:order} and \ref{sec:reversed-order}
% for further details.
%
% The \meta{hook} can be specified using the dot-syntax to denote
% the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
%
% \begin{function}{\NewMirroredHookPair}
% \begin{syntax}
% \cs{NewMirroredHookPair} \Arg{hook-1} \Arg{hook-2}
% \end{syntax}
% A shorthand for
% \cs{NewHook}\Arg{hook-1}\cs{NewReversedHook}\Arg{hook-2}.
%
% The \meta{hooks} can be specified using the dot-syntax to denote
% the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
%
% \subsubsection{Special declarations for generic hooks}
%
% The declarations here should normally not be used. They are available
% to provide support for special use cases mainly involving
% generic command hooks.
%
% \changes{v1.0p}{2021/08/20}{Documentation updates for generic hook commands (gh/638)}
%
% \begin{function}{\DisableGenericHook}
% \begin{syntax}
% \cs{DisableGenericHook} \Arg{hook}
% \end{syntax}
% After this declaration\footnotemark{} the \meta{hook} is no longer usable: Any
% attempt to add further code to it will result in an error and any
% use, e.g., via \cs{UseHook}, will simply do nothing.
%
% This is intended to be used with generic command hooks (see
% \texttt{ltcmdhooks-doc}) as depending on the definition of the
% command such generic hooks may be unusable. If that is known, a
% package developer can disable such hooks up front.
%
% The \meta{hook} can be specified using the dot-syntax to denote
% the current package name. See section~\ref{sec:default-label}.
% \end{function}\footnotetext{In the 2020/06 release this command was
% called \cs{DisableHook}, but that name was misleading as it
% shouldn't be used to disable non-generic hooks.}
%
%
% \begin{function}{\ActivateGenericHook}
% \begin{syntax}
% \cs{ActivateGenericHook} \Arg{hook}
% \end{syntax}
% This declaration activates a generic hook provided by a package/class
% (e.g., one used in code with \cs{UseHook} or
% \cs{UseOneTimeHook}) without it being explicitly declared with
% \cs{NewHook}).
% This command undoes the effect of \cs{DisableGenericHook}.
% If the hook is already activated, this command does nothing.
%
% See section~\ref{sec:generic-hooks} for a discussion of when this
% declaration is appropriate.
% \end{function}
%
%
%
%
%
% \subsubsection{Using hooks in code}
%
%
% \begin{function}{\UseHook}
% \begin{syntax}
% \cs{UseHook} \Arg{hook}
% \end{syntax}
% Execute the hook code inside a command or environment.
%
% Before \verb=\begin{document}= the fast execution code for a hook
% is not set up, so in order to use a hook there it is explicitly
% initialized first. As that involves assignments using a hook at
% those times is not 100\% the same as using it after
% \verb=\begin{document}=.
%
% The \meta{hook} \emph{cannot} be specified using the dot-syntax.
% A leading |.| is treated literally.
% \end{function}
%
% \begin{function}{\UseOneTimeHook}
% \begin{syntax}
% \cs{UseOneTimeHook} \Arg{hook}
% \end{syntax}
% Some hooks are only used (and can be only used) in one place, for
% example, those in \verb=\begin{document}= or
% \verb=\end{document}=. Once we have passed that point adding to
% the hook through a defined \cs[no-index]{\meta{addto-cmd}} command (e.g.,
% \cs{AddToHook} or \cs{AtBeginDocument}, etc.\@) would have no
% effect (as would the use of such a command inside the hook code
% itself). It is therefore customary to redefine
% \cs{\meta{addto-cmd}} to simply process its argument, i.e.,
% essentially make it behave like \cs{@firstofone}.
%
% \cs{UseOneTimeHook} does that: it records that the hook has been
% consumed and any further attempt to add to it will result in
% executing the code to be added immediately.
%
% The \meta{hook} \emph{cannot} be specified using the dot-syntax.
% A leading |.| is treated literally. See
% section~\ref{sec:default-label} for details.
%
% Using \cs{UseOneTimeHook} several times with the same
% \Arg{hook} means that it only executes the first time it is used.
% For example, if it is used in a command that can be called several times
% then the hook executes during only the \emph{first} invocation of that
% command; this allows its use as an \enquote{initialization hook}.
%
% Mixing \cs{UseHook} and \cs{UseOneTimeHook} for the same
% \Arg{hook} should be avoided, but if this is done then neither will execute
% after the first \cs{UseOneTimeHook}.
%
% \end{function}
%
%
% \subsubsection{Updating code for hooks}
%
% \begin{function}{\AddToHook}
% \begin{syntax}
% \cs{AddToHook} \Arg{hook}\oarg{label}\Arg{code}
% \end{syntax}
% Adds \meta{code} to the \meta{hook} labeled by \meta{label}.
% When the optional argument \meta{label} is not provided, the
% \meta{default label} is used (see section~\ref{sec:default-label}).
% If \cs{AddToHook} is used in a package/class, the
% \meta{default label} is the package/class name, otherwise it is
% \hook{top-level} (the \hook{top-level} label is treated
% differently: see section~\ref{sec:top-level}).
%
% If there already exists code under the \meta{label} then the new
% \meta{code} is appended to the existing one (even if this is a reversed hook).
% If you want to replace existing code under the
% \meta{label}, first apply \cs{RemoveFromHook}.
%
% The hook doesn't have to exist for code to be added to
% it. However, if it is not declared, then obviously the
% added \meta{code} will never be executed. This
% allows for hooks to work regardless of package loading order and
% enables packages to add to hooks from other packages without
% worrying whether they are actually used in the current document.
% See section~\ref{sec:querying}.
%
% The \meta{hook} and \meta{label} can be specified using the
% dot-syntax to denote the current package name.
% See section~\ref{sec:default-label}.
% \end{function}
%
% \begin{function}{\RemoveFromHook}
% \begin{syntax}
% \cs{RemoveFromHook} \Arg{hook}\oarg{label}
% \end{syntax}
% Removes any code labeled by \meta{label} from the \meta{hook}.
% When the optional argument \meta{label} is not provided, the
% \meta{default label} is used (see section~\ref{sec:default-label}).
%
% If there is no code under the \meta{label} in the \meta{hook},
% or if the \meta{hook} does not exist, a warning is issued when
% you attempt to \cs{RemoveFromHook}, and the command is ignored.
% \cs{RemoveFromHook} should be used only when you know exactly what
% labels are in a hook. Typically this will be when some code gets added to a hook
% by a package, then later this code is removed by that same package.
% If you want to prevent the execution of code from another
% package, use the |voids| rule instead (see section~\ref{sec:rules}).
%
% If the optional \meta{label} argument is \texttt{*}, then all code chunks are
% removed. This is rather dangerous as it may well drop code from other
% packages (that one may not know about); it should therefore not be used
% in packages but only in document preambles!
%
% The \meta{hook} and \meta{label} can be specified using the
% dot-syntax to denote the current package name.
% See section~\ref{sec:default-label}.
% \end{function}
%
% \medskip
%
% In contrast to the \texttt{voids} relationship between two labels
% in a \cs{DeclareHookRule} this is a destructive operation as the
% labeled code is removed from the hook data structure, whereas the
% relationship setting can be undone by providing a different
% relationship later.
%
% A useful application for this declaration inside the document body
% is when one wants to temporarily add code to hooks and later remove
% it again, e.g.,
%\begin{verbatim}
% \AddToHook{env/quote/before}{\small}
% \begin{quote}
% A quote set in a smaller typeface
% \end{quote}
% ...
% \RemoveFromHook{env/quote/before}
% ... now back to normal for further quotes
%\end{verbatim}
% Note that you can't cancel the setting with
%\begin{verbatim}
% \AddToHook{env/quote/before}{}
%\end{verbatim}
% because that only \enquote{adds} a further empty chunk of code to
% the hook. Adding \cs{normalsize} would work but that means the hook
% then contained \cs{small}\cs{normalsize} which means two font size
% changes for no good reason.
%
% The above is only needed if one wants to typeset several quotes in a
% smaller typeface. If the hook is only needed once then
% \cs{AddToHookNext} is simpler, because it resets itself after one use.
%
%
% \begin{function}{\AddToHookNext}
% \begin{syntax}
% \cs{AddToHookNext} \Arg{hook}\Arg{code}
% \end{syntax}
% Adds \meta{code} to the next invocation of the \meta{hook}.
% The code is executed after the normal hook code has finished and
% it is executed only once, i.e. it is deleted after it was used.
%
% Using this declaration is a global operation, i.e., the code is
% not lost even if the declaration is used inside a group and the
% next invocation of the hook happens after the end of that group.
% If the declaration is
% used several times before the hook is executed then all code is
% executed in the order in which it was declared.\footnotemark
%
% If this declaration is used with a one-time hook then the code
% is only ever used if the declaration comes before the hook’s
% invocation. This is because, in contrast
% to \cs{AddToHook}, the code in this declaration is not
% executed immediately in the case when the invocation of the hook
% has already happened---in other words, this code will truly execute
% only on the next invocation of the hook (and in the case of a
% one-time hook there is no such \enquote{next invocation}).
% This gives you a choice: should my code execute
% always, or should it execute only at the point where the
% one-time hook is used (and not at all if this is impossible)? For
% both of these possibilities there are use cases.
%
% It is possible to nest this declaration using the same hook (or
% different hooks): e.g.,
% \begin{quote}
% \cs{AddToHookNext}\Arg{hook}\verb={=\meta{code-1}^^A
% \cs{AddToHookNext}\Arg{hook}\Arg{code-2}\verb=}=
% \end{quote}
% will execute \meta{code-1} next time the \meta{hook} is used and at
% that point puts \meta{code-2} into the \meta{hook} so that it gets
% executed on following time the hook is run.
%
% A hook doesn't have to exist for code to be added to it. This
% allows for hooks to work regardless of package loading
% order.
% See section~\ref{sec:querying}.
%
% The \meta{hook} can be specified using the dot-syntax to denote
% the current package name. See section~\ref{sec:default-label}.
% \end{function}\footnotetext{There is
% no mechanism to reorder such code chunks (or delete them).}
%
%
%
% \begin{function}{\ClearHookNext}
% \begin{syntax}
% \cs{ClearHookNext}\Arg{hook}
% \end{syntax}
% Normally \cs{AddToHookNext} is only used when you know precisely
% where it will apply and why you want some extra code at that
% point. However, there are a few use cases in which such a
% declaration needs to be canceled, for example, when
% discarding a page with \cs{DiscardShipoutBox} (but even then not
% always), and in such situations \cs{ClearHookNext} can be
% used.
% \end{function}
%
%
%
%
%
% \subsubsection{Hook names and default labels}
% \label{sec:default-label}
%
% It is best practice to use \cs{AddToHook} in packages or classes
% \emph{without specifying a \meta{label}} because then the package
% or class name is automatically used, which is helpful if rules are
% needed, and avoids mistyping the \meta{label}.
%
% Using an explicit \meta{label} is only necessary in very specific
% situations, e.g., if you want to add several chunks of code into a
% single hook and have them placed in different parts of the hook
% (by providing some rules).
%
% The other case is when you develop a larger package with several
% sub-packages. In that case you may want to use the same
% \meta{label} throughout the sub-packages in order to avoid
% that the labels change if you internally reorganize your code.
%
% Except for \cs{UseHook}, \cs{UseOneTimeHook} and \cs{IfHookEmptyTF}
% (and their \pkg{expl3} interfaces \cs{hook_use:n},
% \cs{hook_use_once:n} and \cs{hook_if_empty:nTF}), all \meta{hook}
% and \meta{label} arguments are processed in the same way: first,
% spaces are trimmed around the argument, then it is fully expanded
% until only character tokens remain. If the full expansion of the
% \meta{hook} or \meta{label} contains a non-expandable non-character
% token, a low-level \TeX{} error is raised (namely, the \meta{hook} is
% expanded using \TeX's \cs{csname}\ldots\cs{endcsname}, as such,
% Unicode characters are allowed in \meta{hook} and \meta{label}
% arguments). The arguments of \cs{UseHook}, \cs{UseOneTimeHook}, and
% \cs{IfHookEmptyTF} are
% processed much in the same way except that spaces are not trimmed
% around the argument, for better performance.
%
% It is not enforced, but highly recommended that the hooks defined by
% a package, and the \meta{labels} used to add code to other hooks
% contain the package name to easily identify the source of the code
% chunk and to prevent clashes. This should be the standard practice,
% so this hook management code provides a shortcut to refer to the
% current package in the name of a \meta{hook} and in a \meta{label}.
% If the \meta{hook} name or the \meta{label} consist just of a single dot
% (|.|), or starts with a dot followed by a slash (|./|) then the dot
% denotes the \meta{default label} (usually the current package or class
% name---see~\cs{SetDefaultHookLabel}).
% A \enquote{|.|} or \enquote{|./|} anywhere else in a \meta{hook} or in
% \meta{label} is treated literally and is not replaced.
%
% For example,
% inside the package \texttt{mypackage.sty}, the default label is
% \texttt{mypackage}, so the instructions:
% \begin{verbatim}
% \NewHook {./hook}
% \AddToHook {./hook}[.]{code} % Same as \AddToHook{./hook}{code}
% \AddToHook {./hook}[./sub]{code}
% \DeclareHookRule{begindocument}{.}{before}{babel}
% \AddToHook {file/foo.tex/after}{code}
% \end{verbatim}
% are equivalent to:
% \begin{verbatim}
% \NewHook {mypackage/hook}
% \AddToHook {mypackage/hook}[mypackage]{code}
% \AddToHook {mypackage/hook}[mypackage/sub]{code}
% \DeclareHookRule{begindocument}{mypackage}{before}{babel}
% \AddToHook {file/foo.tex/after}{code} % unchanged
% \end{verbatim}
%
% The \meta{default label} is automatically set equal to the name of the
% current package or class at the time the package is loaded. If the
% hook command is used outside of a package, or the current file wasn't
% loaded with \cs{usepackage} or \cs{documentclass}, then the
% \texttt{top-level} is used as the \meta{default label}. This may have
% exceptions---see \cs{PushDefaultHookLabel}.
%
% This syntax is available in all \meta{label} arguments and most
% \meta{hook} arguments, both in the \LaTeXe{} interface, and the \LaTeX3
% interface described in section~\ref{sec:l3hook-interface}.
%
% Note, however, that the replacement of |.| by the \meta{default label}
% takes place when the hook command is executed, so actions that are
% somehow executed after the package ends will have the wrong
% \meta{default label} if the dot-syntax is used. For that reason,
% this syntax is not available in \cs{UseHook} (and \cs{hook_use:n})
% because the hook is most of the time used outside of the package file
% in which it was defined. This syntax is also not available in the hook
% conditionals \cs{IfHookEmptyTF} (and \cs{hook_if_empty:nTF}), because these
% conditionals are used in some performance-critical parts of the hook
% management code, and because they are usually used to refer to other
% package's hooks, so the dot-syntax doesn't make much sense.
%
% In some cases, for example in large packages, one may want to separate
% it in logical parts, but still use the main package name as
% \meta{label}, then the \meta{default label} can be set using
% \cs{SetDefaultHookLabel} or
% \cs{PushDefaultHookLabel}\verb={..}=\,\ldots\cs{PopDefaultHookLabel}.
%
% \begin{function}{\PushDefaultHookLabel,\PopDefaultHookLabel}
% \begin{syntax}
% \cs{PushDefaultHookLabel} \Arg{default label}
% \quad \meta{code}
% \cs{PopDefaultHookLabel}
% \end{syntax}
% \cs{PushDefaultHookLabel} sets the current \meta{default label} to
% be used in \meta{label} arguments, or when replacing a leading
% ``|.|'' (see above). \cs{PopDefaultHookLabel} reverts the
% \meta{default label} to its previous value.
%
% Inside a package or class, the \meta{default label} is equal to the
% package or class name, unless explicitly changed. Everywhere else,
% the \meta{default label} is |top-level| (see
% section~\ref{sec:top-level}) unless explicitly changed.
%
% The effect of \cs{PushDefaultHookLabel} holds until the next
% \cs{PopDefaultHookLabel}. \cs{usepackage} (and \cs{RequirePackage}
% and \cs{documentclass}) internally use
% \begin{quote}
% \cs{PushDefaultHookLabel}\Arg{package name} \\
% \null \quad \meta{package code} \\
% \cs{PopDefaultHookLabel}
% \end{quote}
% to set the \meta{default label} for the package or class file.
% Inside the \meta{package code} the \meta{default label} can also be
% changed with \cs{SetDefaultHookLabel}. \cs{input} and other
% file input-related commands from the \LaTeX{} kernel do not use
% \cs{PushDefaultHookLabel}, so code within files loaded by these
% commands does \emph{not} get a dedicated \meta{label}! (that is, the
% \meta{default label} is the current active one when the file was
% loaded.)
%
% Packages that provide their own package-like interfaces
% (Ti\textit{k}Z's \cs{usetikzlibrary}, for example) can use
% \cs{PushDefaultHookLabel} and \cs{PopDefaultHookLabel} to set
% dedicated labels and to emulate \cs{usepackage}-like hook behavior
% within those contexts.
%
% The |top-level| label is treated differently, and is reserved to the
% user document, so it is not allowed to change the
% \meta{default label} to |top-level|.
% \end{function}
%
% \begin{function}{\SetDefaultHookLabel}
% \begin{syntax}
% \cs{SetDefaultHookLabel} \Arg{default label}
% \end{syntax}
% Similarly to \cs{PushDefaultHookLabel},
% sets the current \meta{default label} to
% be used in \meta{label} arguments, or when replacing a leading
% ``|.|''. The effect holds until the label is changed again or until
% the next \cs{PopDefaultHookLabel}. The difference between
% \cs{PushDefaultHookLabel} and \cs{SetDefaultHookLabel} is that the
% latter does not save the current \meta{default label}.
%
% This command is useful when a large package is composed of several
% smaller packages, but all should have the same \meta{label}, so
% \cs{SetDefaultHookLabel} can be used at the beginning of each
% package file to set the correct label.
%
% \cs{SetDefaultHookLabel} is not allowed in the main document, where
% the \meta{default label} is |top-level| and there is no
% \cs{PopDefaultHookLabel} to end its effect.
% It is also not allowed to change the \meta{default label} to
% |top-level|.
% \end{function}
%
% \subsubsection{The \texttt{top-level} label}
% \label{sec:top-level}
%
% The |top-level| label, assigned to code added from the main document,
% is different from other labels. Code added to hooks (usually
% \cs{AtBeginDocument}) in the preamble is almost always to change
% something defined by a package, so it should go at the very end of the
% hook.
%
% Therefore, code added in the |top-level| is always executed at the end
% of the hook, regardless of where it was declared. If the hook is
% reversed (see \cs{NewReversedHook}), the |top-level| chunk is executed
% at the very beginning instead.
%
% Rules regarding |top-level| have no effect: if a user wants to have a
% specific set of rules for a code chunk, they should use a different
% label to said code chunk, and provide a rule for that label instead.
%
% The |top-level| label is exclusive for the user, so trying to add code
% with that label from a package results in an error.
%
% \subsubsection{Defining relations between hook code}
% \label{sec:rules}
%
% The default assumption is that code added to hooks by different
% packages are independent and the order in which they are executed is
% irrelevant. While this is true in many cases it is obviously false
% in others.
%
% Before the hook management system was introduced
% packages had to take elaborate precaution to determine of some other
% package got loaded as well (before or after) and find some ways to
% alter its behavior accordingly. In addition is was often the user's
% responsibility to load packages in the right order so that code
% added to hooks got added in the right order and some cases even
% altering the loading order wouldn't resolve the conflicts.
%
% With the new hook management system it is now possible to define
% rules (i.e., relationships) between code chunks added by different
% packages and explicitly describe in which order they should be
% processed.
%
% \begin{function}{\DeclareHookRule}
% \begin{syntax}
% \cs{DeclareHookRule} \Arg{hook}\Arg{label1}\Arg{relation}\Arg{label2}
% \end{syntax}
% Defines a relation between \meta{label1} and \meta{label2} for a
% given \meta{hook}. If \meta{hook} is \texttt{??} this defines a default
% relation for all hooks that use the two labels, i.e., that have
% chunks of code labeled with \meta{label1} and \meta{label2}.
% Rules specific to a given hook take precedence over default
% rules that use \texttt{??} as the \meta{hook}.
%
% Currently, the supported relations are the following:
% \begin{itemize}
%
% \item[\texttt{before} or \texttt{\string<}]
%
% Code for \meta{label1} comes before code for \meta{label2}.
%
% \item[\texttt{after} or \texttt{\string>}]
% Code for \meta{label1} comes after code for \meta{label2}.
%
% \item[\texttt{incompatible-warning}]
%
% Only code for either \meta{label1} or \meta{label2} can appear
% for that hook (a way to say that two packages---or parts of
% them---are incompatible). A warning is raised if both labels
% appear in the same hook.
%
% \item[\texttt{incompatible-error}]
%
% Like \texttt{incompatible-error} but instead of a warning a
% \LaTeX{} error is raised, and the code for both labels are
% dropped from that hook until the conflict is resolved.
%
% \item[\texttt{voids}]
%
% Code for \meta{label1} overwrites code for \meta{label2}. More
% precisely, code for \meta{label2} is dropped for that
% hook. This can be used, for example if one package is a
% superset in functionality of another one and therefore wants to
% undo code in some hook and replace it with its own version.
%
% \item[\texttt{unrelated}]
%
% The order of code for \meta{label1} and \meta{label2} is
% irrelevant. This rule is there to undo an incorrect rule
% specified earlier.
%
% \end{itemize}
% There can only be a single relation between two labels for a
% given hook,
% i.e., a later \cs{DeclareHookrule} overwrites any previous
% declaration.
%
% The \meta{hook} and \meta{label} can be specified using the
% dot-syntax to denote the current package name.
% See section~\ref{sec:default-label}.
%
% \end{function}
%
%
% \begin{function}{\ClearHookRule}
% \begin{syntax}
% \cs{ClearHookRule}\Arg{hook}\Arg{label1}\Arg{label2}
% \end{syntax}
% Syntactic sugar for saying that \meta{label1} and \meta{label2}
% are unrelated for the given \meta{hook}.
% \end{function}
%
%
%
% \begin{function}{\DeclareDefaultHookRule}
% \begin{syntax}
% \cs{DeclareDefaultHookRule}\Arg{label1}\Arg{relation}\Arg{label2}
% \end{syntax}
% This sets up a relation between \meta{label1} and \meta{label2}
% for all hooks unless overwritten by a specific rule for a hook.
% Useful for cases where one package has a specific relation to
% some other package, e.g., is \texttt{incompatible} or always
% needs a special ordering \texttt{before} or \texttt{after}.
% (Technically it is just a shorthand for using \cs{DeclareHookRule}
% with \texttt{??} as the hook name.)
%
% Declaring default rules is only supported in the document
% preamble.\footnotemark{}
%
% The \meta{label} can be specified using the dot-syntax to denote
% the current package name. See section~\ref{sec:default-label}.
% \end{function}\footnotetext{Trying to do so, e.g., via
% \cs{DeclareHookRule} with \texttt{??} has bad side-effects and
% is not supported (though not explicitly caught for performance
% reasons).}
%
%
%
% \subsubsection{Querying hooks}
% \label{sec:querying}
%
% Simpler data types, like token lists, have three possible states; they
% can:
% \begin{itemize}
% \item exist and be empty;
% \item exist and be non-empty; and
% \item not exist (in which case emptiness doesn't apply);
% \end{itemize}
% Hooks are a bit more complicated:
% a hook may exist or not, and independently it may or may not be empty.
% This means that even a hook that doesn't exist may be non-empty and
% it can also be disabled.
%
% This seemingly strange state may happen when, for example, package~$A$
% defines hook \hook{A/foo}, and package $B$ adds some code to that
% hook. However, a document may load package $B$ before package $A$, or
% may not load package $A$ at all. In both cases some code is added to
% hook \hook{A/foo} without that hook being defined yet, thus that
% hook is said to be non-empty, whereas it doesn't exist. Therefore,
% querying the existence of a hook doesn't imply its emptiness, neither
% does the other way around.
%
% Given that code or rules can be added to a hook even if it doesn't
% physically exist yet, means that a querying its existence has no
% real use case (in contrast to other variables that can only be
% update if they have already been declared). For that reason only the
% test for emptiness has a public interface.
%
% A hook is said to be empty when no code was added to it, either to
% its permanent code pool, or to its ``next'' token list. The hook
% doesn't need to be declared to have code added to its code pool.
% A hook is said to exist when it was declared with \cs{NewHook} or
% some variant thereof. Generic hooks such as \hook{file} and \hook{env} hooks are
% automatically declared when code is added to them.
%
% \begin{function}[EXP]{\IfHookEmptyTF}
% \begin{syntax}
% \cs{IfHookEmptyTF} \Arg{hook} \Arg{true code} \Arg{false code}
% \end{syntax}
% Tests if the \meta{hook} is empty (\emph{i.e.}, no code was added to
% it using either \cs{AddToHook} or \cs{AddToHookNext}) or such code
% was removed again (via \cs{RemoveFromHook}), and
% branches to either \meta{true code} or \meta{false code} depending
% on the result.
%
% The \meta{hook} \emph{cannot} be specified using the dot-syntax.
% A leading |.| is treated literally.
% \end{function}
%
%
%
% \subsubsection{Displaying hook code}
%
% If one has to adjust the code execution in a hook using a hook
% rule it is helpful to get some information about the code
% associated with a hook, its current order and the existing rules.
%
% \begin{function}{\ShowHook,\LogHook}
% \begin{syntax}
% \cs{ShowHook} \Arg{hook}
% \cs{LogHook} \Arg{hook}
% \end{syntax}
% Displays information about the \meta{hook} such as
% \begin{itemize}
% \item
% the code chunks (and their labels) added to it,
% \item
% any rules set up to order them,
% \item
% the computed order in which the chunks are executed,
% \item
% any code executed on the next invocation only.
% \end{itemize}
% \end{function}
%
% \cs{LogHook} prints the information to the |.log| file, and
% \cs{ShowHook} prints them to the terminal/command window and starts
% \TeX's prompt (only in \cs{errorstopmode}) to wait for user action.
%
% The \meta{hook} can be specified using the dot-syntax to denote
% the current package name. See section~\ref{sec:default-label}.
%
%^^A % Code for the listing below:
%^^A \NewHook{example-hook}
%^^A \AddToHook{example-hook}{[code from 'top-level']}
%^^A \AddToHook{example-hook}[foo]{[code from package 'foo']}
%^^A \AddToHook{example-hook}[bar]{[from package 'bar']}
%^^A \AddToHook{example-hook}[baz]{[package 'baz' is here]}
%^^A \AddToHookNext{example-hook}{[one-time code]}
%^^A \DeclareHookRule{example-hook}{baz}{before}{foo}
%^^A \DeclareDefaultHookRule{bar}{after}{baz}
%^^A \ShowHook{example-hook}
%
% \def\theFancyVerbLine{\textcolor[gray]{0.5}{%^^A
% \sffamily\tiny\arabic{FancyVerbLine}}}
%
% \bigskip
% Suppose a hook \texttt{example-hook} whose output of
% \cs{ShowHook}|{example-hook}| is:
% \begin{verbatim}[numbers=left]
% -> The hook 'example-hook':
% > Code chunks:
% > foo -> [code from package 'foo']
% > bar -> [from package 'bar']
% > baz -> [package 'baz' is here]
% > Document-level (top-level) code (executed last):
% > -> [code from 'top-level']
% > Extra code for next invocation:
% > -> [one-time code]
% > Rules:
% > foo|baz with relation >
% > baz|bar with default relation <
% > Execution order (after applying rules):
% > baz, foo, bar.
% \end{verbatim}
%
% In the listing above, lines~3 to~5 show the three code chunks added
% to the hook and their respective labels in the format
% \begin{quote}
% \quad \meta{label}\verb| -> |\meta{code}
% \end{quote}
%
% Line~7 shows the code chunk added by the user in the main document
% (labeled |top-level|) in the format
% \begin{quote}
% \quad\verb|Document-level (top-level) code (executed |%^^A
% \meta{first\texttt{\string|}last}\verb|):|\\
% \quad\verb| -> |\meta{\texttt{top-level} code}
% \end{quote}
% This code will be either the first or last code executed by the hook
% (|last| if the hook is normal, |first| if it is reversed). This
% chunk is not affected by rules and does not take part in sorting.
%
% Line~9 shows the code chunk for the next execution of the hook in
% the format
% \begin{quote}
% \quad \verb|-> |\meta{next-code}
% \end{quote}
% This code will be used and disappear at the next
% \verb|\UseHook{example-hook}|, in contrast to the chunks mentioned
% earlier, which can only be removed from that hook by doing
% \verb|\RemoveFromHook{|\meta{label}|}[example-hook]|.
%
% Lines~11 and~12 show the rules declared that affect this hook in the
% format
% \begin{quote}
% \quad \meta{label-1}\verb+|+\meta{label-2}| with |%^^A
% \meta{\texttt{default}?}| relation |\meta{relation}
% \end{quote}
% which means that the \meta{relation} applies to \meta{label-1} and
% \meta{label-2}, in that order, as detailed in \cs{DeclareHookRule}.
% If the relation is \texttt{default} it means that this rule applies
% to \meta{label-1} and \meta{label-2} in \emph{all} hooks, (unless
% overridden by a non-default relation).
%
% Finally, line~14 lists the labels in the hook after sorting;
% that is, in the order they will be executed when the hook is used.
%
%
% \subsubsection{Debugging hook code}
%
% \begin{function}{\DebugHooksOn,\DebugHooksOff}
% \begin{syntax}
% \cs{DebugHooksOn}
% \end{syntax}
% Turn the debugging of hook code on or off. This displays most changes
% made to the hook data structures. The output is rather coarse and
% not really intended for normal use.
% \end{function}
%
%
% \subsection{L3 programming layer (\texttt{expl3}) interfaces}
% \label{sec:l3hook-interface}
%
%
% This is a quick summary of the \LaTeX3 programming interfaces for
% use with packages written in \texttt{expl3}. In contrast to the
% \LaTeXe{} interfaces they always use mandatory arguments only, e.g.,
% you always have to specify the \meta{label} for a code chunk. We
% therefore suggest to use the declarations discussed in the previous
% section even in \texttt{expl3} packages, but the choice is yours.
%
%
% \begin{function}
% {\hook_new:n,\hook_new_reversed:n,\hook_new_pair:nn}
% \begin{syntax}
% \cs{hook_new:n} \Arg{hook}
% \cs{hook_new_reversed:n} \Arg{hook}
% \cs{hook_new_pair:nn} \Arg{hook-1} \Arg{hook-2}
% \end{syntax}
% Creates a new \meta{hook} with normal or reverse ordering of code
% chunks. \cs{hook_new_pair:nn} creates a pair of such hooks with
% \Arg{hook-2} being a reversed hook.
% If a hook name is already taken, an error is raised and the hook
% is not created.
%
% The \meta{hook} can be specified using the dot-syntax to denote
% the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
%
%
% \begin{function}{\hook_disable_generic:n}
% \begin{syntax}
% \cs{hook_disable_generic:n} \Arg{hook}
% \end{syntax}
% Marks \Arg{hook} as disabled. Any further attempt to add code to
% it or declare it, will result in an error and any call to
% \cs{hook_use:n} will simply do nothing.
%
% This declaration is intended for use with generic hooks that are
% known not to work (see \texttt{ltcmdhooks-doc}) if they receive
% code.
%
% The \meta{hook} can be specified using the dot-syntax to denote
% the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
% \begin{function}{\hook_activate_generic:n}
% \begin{syntax}
% \cs{hook_activate_generic:n} \Arg{hook}
% \end{syntax}
% This is like \cs{hook_new:n} but it does nothing if the hook was previously
% declared with \cs{hook_new:n}. This declaration should be used
% only in special situations, e.g., when a command from another package
% needs to be altered and it is not clear whether a
% generic \hook{cmd} hook (for that command) has been previously
% explicitly declared.
%
% Normally \cs{hook_new:n} should be used instead of this.
% \end{function}
%
%
%
%
% \begin{function}{\hook_use:n}
% \begin{syntax}
% \cs{hook_use:n} \Arg{hook}
% \end{syntax}
% Executes the \Arg{hook} code followed (if set up) by the code for next
% invocation only, then empties that next invocation code.
%
% The \meta{hook} \emph{cannot} be specified using the dot-syntax.
% A leading |.| is treated literally.
% \end{function}
%
% \begin{function}{\hook_use_once:n}
% \begin{syntax}
% \cs{hook_use_once:n} \Arg{hook}
% \end{syntax}
% Changes the \Arg{hook} status so that from now on any addition to
% the hook code is executed immediately. Then execute any
% \Arg{hook} code already set up.
%
% The \meta{hook} \emph{cannot} be specified using the dot-syntax.
% A leading |.| is treated literally.
% \end{function}
%
% \begin{function}{\hook_gput_code:nnn}
% \begin{syntax}
% \cs{hook_gput_code:nnn} \Arg{hook} \Arg{label} \Arg{code}
% \end{syntax}
% Adds a chunk of \meta{code} to the \meta{hook} labeled
% \meta{label}. If the label already exists the \meta{code} is
% appended to the already existing code.
%
% If code is added to an external \meta{hook} (of the kernel or
% another package) then the convention is to use the package name
% as the \meta{label} not some internal module name or some other
% arbitrary string.
%
% The \meta{hook} and \meta{label} can be specified using the
% dot-syntax to denote the current package name.
% See section~\ref{sec:default-label}.
% \end{function}
%
% \begin{function}
% {\hook_gput_next_code:nn}
% \begin{syntax}
% \cs{hook_gput_next_code:nn} \Arg{hook} \Arg{code}
% \end{syntax}
% Adds a chunk of \meta{code} for use only in the next invocation of the
% \meta{hook}. Once used it is gone.
%
% This is simpler than \cs{hook_gput_code:nnn}, the code is simply
% appended to the hook in the order of declaration at the very end,
% i.e., after all standard code for the hook got executed.
%
% Thus if one needs to undo what the standard does one has to do
% that as part of \meta{code}.
%
% The \meta{hook} can be specified using the dot-syntax to denote
% the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
%
% \begin{function}
% {\hook_gclear_next_code:n}
% \begin{syntax}
% \cs{hook_gclear_next_code:n} \Arg{hook}
% \end{syntax}
% Undo any earlier \cs{hook_gput_next_code:nn}.
% \end{function}
%
%
%
% \begin{function}{\hook_gremove_code:nn}
% \begin{syntax}
% \cs{hook_gremove_code:nn} \Arg{hook} \Arg{label}
% \end{syntax}
% Removes any code for \meta{hook} labeled \meta{label}.
%
% If there is no code under the \meta{label} in the \meta{hook},
% or if the \meta{hook} does not exist, a warning is issued when
% you attempt to use \cs{hook_gremove_code:nn}, and the command is ignored.
%
% If the second argument is \texttt{*}, then all code chunks are
% removed. This is rather dangerous as it drops code from other
% packages one may not know about, so think twice before using
% that!
%
% The \meta{hook} and \meta{label} can be specified using the
% dot-syntax to denote the current package name.
% See section~\ref{sec:default-label}.
% \end{function}
%
%
% \begin{function}{\hook_gset_rule:nnnn}
% \begin{syntax}
% \cs{hook_gset_rule:nnnn} \Arg{hook} \Arg{label1} \Arg{relation} \Arg{label2}
% \end{syntax}
% Relate \meta{label1} with \meta{label2} when used in \meta{hook}.
% See \cs{DeclareHookRule} for the allowed \meta{relation}s.
% If \meta{hook} is \texttt{??} a default rule is specified.
%
% The \meta{hook} and \meta{label} can be specified using the
% dot-syntax to denote the current package name.
% See section~\ref{sec:default-label}.
% The dot-syntax is parsed in both \meta{label} arguments, but it
% usually makes sense to be used in only one of them.
% \end{function}
%
% \begin{function}[pTF]{\hook_if_empty:n}
% \begin{syntax}
% \cs{hook_if_empty:nTF} \Arg{hook} \Arg{true code} \Arg{false code}
% \end{syntax}
% Tests if the \meta{hook} is empty (\emph{i.e.}, no code was added to
% it using either \cs{AddToHook} or \cs{AddToHookNext}), and
% branches to either \meta{true code} or \meta{false code} depending
% on the result.
%
% The \meta{hook} \emph{cannot} be specified using the dot-syntax.
% A leading |.| is treated literally.
% \end{function}
%
%
% \begin{function}{\hook_show:n,\hook_log:n}
% \begin{syntax}
% \cs{hook_show:n} \Arg{hook}
% \cs{hook_log:n} \Arg{hook}
% \end{syntax}
% Displays information about the \meta{hook} such as
% \begin{itemize}
% \item
% the code chunks (and their labels) added to it,
% \item
% any rules set up to order them,
% \item
% the computed order in which the chunks are executed,
% \item
% any code executed on the next invocation only.
% \end{itemize}
%
% \cs{hook_log:n} prints the information to the |.log| file, and
% \cs{hook_show:n} prints them to the terminal/command window and starts
% \TeX's prompt (only if \cs{errorstopmode}) to wait for user action.
%
% The \meta{hook} can be specified using the dot-syntax to denote
% the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
% \begin{function}{\hook_debug_on:,\hook_debug_off:}
% \begin{syntax}
% \cs{hook_debug_on:}
% \end{syntax}
% Turns the debugging of hook code on or off. This displays changes
% to the hook data.
% \end{function}
%
%
%
% \subsection{On the order of hook code execution} \label{sec:order}
%
% Chunks of code for a \meta{hook} under different labels are supposed
% to be independent if there are no special rules set up that
% define a relation between the chunks. This means that you can't
% make assumptions about the order of execution!
%
% Suppose you have the following declarations:
%\begin{verbatim}
% \NewHook{myhook}
% \AddToHook{myhook}[packageA]{\typeout{A}}
% \AddToHook{myhook}[packageB]{\typeout{B}}
% \AddToHook{myhook}[packageC]{\typeout{C}}
%\end{verbatim}
% then executing the hook with \cs{UseHook} will produce the
% typeout \texttt{A} \texttt{B} \texttt{C} in that order. In other
% words, the execution order is computed to be \texttt{packageA},
% \texttt{packageB}, \texttt{packageC} which you can verify with
% \cs{ShowHook}\texttt{\{myhook\}}:
%\begin{verbatim}
% -> The hook 'myhook':
% > Code chunks:
% > packageA -> \typeout {A}
% > packageB -> \typeout {B}
% > packageC -> \typeout {C}
% > Document-level (top-level) code (executed last):
% > ---
% > Extra code for next invocation:
% > ---
% > Rules:
% > ---
% > Execution order:
% > packageA, packageB, packageC.
%\end{verbatim}
% The reason is that the code chunks are internally saved in a property list
% and the initial order of such a property list is the order in
% which key-value pairs got added. However, that is only true if
% nothing other than adding happens!
%
% Suppose, for example, you want to replace the code chunk for
% \texttt{packageA}, e.g.,
%\begin{verbatim}
% \RemoveFromHook{myhook}[packageA]
% \AddToHook{myhook}[packageA]{\typeout{A alt}}
%\end{verbatim}
% then your order becomes \texttt{packageB},
% \texttt{packageC}, \texttt{packageA} because the label got removed
% from the property list and then re-added (at its end).
%
% While that may not be too surprising, the execution order is
% also sometimes altered if you add a redundant rule, e.g. if you specify
%\begin{verbatim}
% \DeclareHookRule{myhook}{packageA}{before}{packageB}
%\end{verbatim}
% instead of the previous lines we get
%\begin{verbatim}
% -> The hook 'myhook':
% > Code chunks:
% > packageA -> \typeout {A}
% > packageB -> \typeout {B}
% > packageC -> \typeout {C}
% > Document-level (top-level) code (executed last):
% > ---
% > Extra code for next invocation:
% > ---
% > Rules:
% > packageB|packageA with relation >
% > Execution order (after applying rules):
% > packageA, packageC, packageB.
%\end{verbatim}
% As you can see the code chunks are still in the same order, but
% in the execution order for the labels \texttt{packageB} and
% \texttt{packageC} have
% swapped places.
% The reason is that, with the rule there are two orders that
% satisfy it, and the algorithm for sorting happened to pick a
% different one compared to the case without rules (where it
% doesn't run at all as there is nothing to resolve).
% Incidentally, if we had instead specified the redundant rule
%\begin{verbatim}
% \DeclareHookRule{myhook}{packageB}{before}{packageC}
%\end{verbatim}
% the execution order would not have changed.
%
% In summary: it is not possible to rely on the order of execution
% unless there are rules that partially or fully define the order
% (in which you can rely on them being fulfilled).
%
%
% \subsection{The use of \enquote{reversed} hooks} \label{sec:reversed-order}
%
% You may have wondered why you can declare a \enquote{reversed} hook
% with \cs{NewReversedHook} and what that does exactly.
%
% In short: the execution order of a reversed hook (without any
% rules!) is exactly reversed to the order you would have gotten for
% a hook declared with \cs{NewHook}.
%
% This is helpful if you have a pair of hooks where you expect to see
% code added that involves grouping, e.g., starting an environment
% in the first and closing that environment in the second hook.
% To give a somewhat contrived example\footnote{there are simpler
% ways to achieve the same effect.}, suppose there is a package
% adding the following:
%\begin{verbatim}
% \AddToHook{env/quote/before}[package-1]{\begin{itshape}}
% \AddToHook{env/quote/after} [package-1]{\end{itshape}}
%\end{verbatim}
% As a result, all quotes will be in italics.
% Now suppose further that another |package-too| makes the quotes
% also in blue and therefore adds:
%\begin{verbatim}
% \usepackage{color}
% \AddToHook{env/quote/before}[package-too]{\begin{color}{blue}}
% \AddToHook{env/quote/after} [package-too]{\end{color}}
%\end{verbatim}
% Now if the \hook{env/quote/after} hook would be a normal hook we
% would get the same execution order in both hooks, namely:
%\begin{verbatim}
% package-1, package-too
%\end{verbatim}
% (or vice versa) and as a result, would get:
%\begin{verbatim}
% \begin{itshape}\begin{color}{blue} ...
% \end{itshape}\end{color}
%\end{verbatim}
% and an error message that \verb=\begin{color}= ended by
% \verb=\end{itshape}=.
% With \hook{env/quote/after} declared as a reversed hook the
% execution order is reversed and so all environments are closed in
% the correct sequence and \cs{ShowHook} would give us the
% following output:
%\begin{verbatim}
% -> The hook 'env/quote/after':
% > Code chunks:
% > package-1 -> \end {itshape}
% > package-too -> \end {color}
% > Document-level (top-level) code (executed first):
% > ---
% > Extra code for next invocation:
% > ---
% > Rules:
% > ---
% > Execution order (after reversal):
% > package-too, package-1.
%\end{verbatim}
%
% The reversal of the execution order happens before applying any
% rules, so if you alter the order you will probably have to alter
% it in both hooks, not just in one, but that depends on the use case.
%
%
%
%
% \subsection{Difference between \enquote{normal} and
% \enquote{one-time} hooks}
% \label{sec:onetime-hooks}
%
% When executing a hook a developer has the choice of using
% either \cs{UseHook} or \cs{UseOneTimeHook} (or their \pkg{expl3}
% equivalents \cs{hook_use:n} and \cs{hook_use_once:n}).
% This choice affects how \cs{AddToHook} is handled after the hook
% has been executed for the first time.
%
% With normal hooks adding code via \cs{AddToHook} means that the
% code chunk is added to the hook data structure and then used each time
% \cs{UseHook} is called.
%
% With one-time hooks it this is handled slightly differently:
% After \cs{UseOneTimeHook} has been called, any further attempts to
% add code to the hook via \cs{AddToHook} will simply execute the
% \meta{code} immediately.
%
% This has some consequences one needs to be aware of:
% \begin{itemize}
% \item
%
% If \meta{code} is added to a normal hook after the hook was
% executed and it is never executed again for one or the other
% reason, then this new \meta{code} will never be executed.
%
% \item
%
% In contrast if that happens with a one-time hook the \meta{code} is
% executed immediately.
%
% \end{itemize}
% In particular this means that construct such as
%\begin{quote}
% \cs{AddToHook}\verb={myhook}=\\
% \phantom{\cs{AddToHook}}\verb={= \meta{code-1}
% \cs{AddToHook}\verb={myhook}=\Arg{code-2}
% \meta{code-3} \verb=}=
%\end{quote}
% works for one-time hooks\footnote{This is sometimes used with
% \cs{AtBeginDocument} which is why it is supported.} (all three
% code chunks are executed one after another), but it makes little
% sense with a normal hook, because with a normal hook the first time
% \verb=\UseHook{myhook}= is executed it would
% \begin{itemize}
% \item
% execute \meta{code-1},
% \item
% then execute \verb=\AddToHook{myhook}{code-2}= which adds the
% code chunk \meta{code-2} to the hook for use on the next invocation,
% \item
% and finally execute \meta{code-3}.
% \end{itemize}
% The second time \cs{UseHook} is called it would execute the
% above and in addition \meta{code-2} as that was added as a code
% chunk to the hook in the meantime. So each time the hook is used
% another copy of \meta{code-2} is added and so that code chunk
% is executed $\meta{\# of invocations} -1$ times.
%
%
%
% \subsection{Generic hooks provided by packages}
% \label{sec:generic-hooks}
%
% \changes{v1.0p}{2021/08/20}{Section on generic hooks added (gh/638)}
%
% The hook management system also implements a category of hooks
% that are called \enquote{Generic Hooks}. Normally a hook has to
% be explicitly declared before it can be used in code. This
% ensures that different packages are not using the same hook name
% for unrelated purposes---something that would result in absolute
% chaos. However, there are a number of \enquote{standard} hooks
% where it is unreasonable to declare them beforehand, e.g, each
% and every command has (in theory) an associated \texttt{before}
% and \texttt{after} hook. In such cases, i.e., for command,
% environment or file hooks, they can be used simply by adding code
% to them with \cs{AddToHook}. For more specialized generic hooks,
% e.g., those provided by \pkg{babel}, you have to additionally
% enable them with \cs{ActivateGenericHook} as explained below.
%
% The generic hooks provided by \LaTeX{} are those for
% \hook{cmd},
% \hook{env},
% \hook{file},
% \hook{include}
% \hook{package}, and
% \hook{class},
% and all these are available out of the box: you only have to
% use \cs{AddToHook} to
% add code to them, but you don't have to add \cs{UseHook} or
% \cs{UseOneTimeHook} to your code, because this is already done for
% you (or, in the case of \hook{cmd} hooks, the command’s code is
% patched at \verb=\begin{document}=, if necessary).
%
% However, if you want to provide further generic hooks in your own
% code, the situation is slightly different. To do this you should
% use \cs{UseHook} or \cs{UseOneTimeHook}, but
% \emph{without declaring the hook} with \cs{NewHook}. As
% mentioned earlier, a call to \cs{UseHook} with an undeclared hook
% name does nothing. So as an additional setup step, you need to
% explicitly activate your generic hook. Note that a generic hook
% produced in this way is always a normal hook.
%
% For a truly generic hook, with a variable part in the hook name,
% such upfront activation would be difficult or impossible, because
% you typically do not know what kind of variable parts may come up
% in real documents.
%
% For example, \pkg{babel} may want to provide hooks such as
% \hook{babel/\meta{language}/afterextras}. Language support in
% \pkg{babel} is often done through external language
% packages. Thus doing the activation for all languages inside the
% core \pkg{babel} code is not a viable approach. Instead it needs
% to be done by each language package (or by the user who wants to
% use a particular hook).
%
% Because the hooks are not declared with \cs{NewHook} their names
% should be carefully chosen to ensure that they are (likely to be)
% unique. Best practice is to include the package or
% command name, as was done in the \pkg{babel} example above.
%
% Generic hooks defined in this way are always normal hooks (i.e.,
% you can't implement reversed hooks this way). This is a
% deliberate limitation, because it speeds up the processessing
% conciderably.
%
%
% \subsection{Private \LaTeX{} kernel hooks}
%
% There are a few places where it is absolutely essential for
% \LaTeX{} to function correctly that code is executed in a precisely
% defined order. Even that could have been implemented with the
% hook management (by adding various rules to ensure the
% appropriate ordering with respect to other code added by
% packages). However, this makes every document unnecessary
% slow, because there has to be sorting even though the result is
% predetermined. Furthermore it forces package writers to
% unnecessarily add such rules if they add further code to the hook
% (or break \LaTeX{}).
%
% For that reason such code is not using the hook management, but
% instead private kernel commands directly before or after a public
% hook with the following naming
% convention: \cs{@kernel@before@\meta{hook}} or
% \cs{@kernel@after@\meta{hook}}. For example, in
% \cs{enddocument} you find
%\begin{verbatim}
% \UseHook{enddocument}%
% \@kernel@after@enddocument
%\end{verbatim}
% which means first the user/package-accessible \hook{enddocument}
% hook is executed and then the internal kernel hook. As their name
% indicates these kernel commands should not be altered by third-party
% packages, so please refrain from that in the interest of
% stability and instead use the public hook next to it.\footnote{As
% with everything in \TeX{} there is not enforcement of this rule,
% and by looking at the code it is easy to find out how the kernel
% adds to them. The main reason of this section is therefore to say
% \enquote{please don't do that, this is unconfigurable code!}}
%
%
%
% \subsection{Legacy \LaTeXe{} interfaces}
%
% \newcommand\onetimetext{This is a one-time hook, so after it
% is executed, all further
% attempts to add code to it will execute such code immediately
% (see section~\ref{sec:onetime-hooks}).}
%
% \LaTeXe{} offered a small number of hooks together with commands to
% add to them. They are listed here and are retained for backwards
% compatibility.
%
% With the new hook management, several additional hooks have been added
% to \LaTeX\ and more will follow. See the next section for what
% is already available.
%
%
% \begin{function}{\AtBeginDocument}
% \begin{syntax}
% \cs{AtBeginDocument} \oarg{label} \Arg{code}
% \end{syntax}
% If used without the optional argument \meta{label}, it works essentially
% like before, i.e., it is adding \meta{code} to the hook
% \hook{begindocument}
% (which is executed inside \verb=\begin{document}=).
% However, all code added this way is labeled with the label
% \hook{top-level} (see section~\ref{sec:top-level})
% if done outside of a package or class or with the
% package/class name if called inside such a file
% (see section~\ref{sec:default-label}).
%
% This way one can add further code to the hook using
% \cs{AddToHook} or \cs{AtBeginDocument} using a different label
% and explicitly order the code chunks as necessary, e.g., run some
% code before or after another package's code. When using the
% optional argument the call is equivalent to running
% \cs{AddToHook} \texttt{\{begindocument\}} \oarg{label}
% \Arg{code}.
%
% \cs{AtBeginDocument} is a wrapper around the \hook{begindocument}
% hook (see section~\ref{sec:begindocument-hooks}), which is a
% one-time hook. As such, after the \hook{begindocument} hook is
% executed at \verb=\begin{document}= any attempt to add \meta{code}
% to this hook with \cs{AtBeginDocument} or with \cs{AddToHook} will
% cause that \meta{code} to execute immediately instead.
% See section~\ref{sec:onetime-hooks} for more on one-time hooks.
%
% For important packages with known order requirement we may over
% time add rules to the kernel (or to those packages) so that they
% work regardless of the loading-order in the document.
% \end{function}
%
% \begin{function}{\AtEndDocument}
% \begin{syntax}
% \cs{AtEndDocument} \oarg{label} \Arg{code}
% \end{syntax}
% Like \cs{AtBeginDocument} but for the \hook{enddocument} hook.
% \end{function}
%
% \bigskip
%
% The few hooks that existed previously in \LaTeXe{} used internally
% commands such as \cs{@begindocumenthook} and packages sometimes
% augmented them directly rather than working through
% \cs{AtBeginDocument}. For that reason there is currently support
% for this, that is, if the system detects that such an internal
% legacy hook command contains code it adds it to the new hook
% system under the label \texttt{legacy} so that it doesn't get
% lost.
%
% However, over time the remaining cases of direct usage need
% updating because in one of the future release of \LaTeX{} we will
% turn this legacy support off, as it does unnecessary slow down
% the processing.
%
%
% \section{\LaTeXe{} commands and environments augmented by hooks}
%
% In this section we describe the standard hooks that are now
% offered by \LaTeX{}, or give pointers to other documents in which
% they are described. This section will grow over time (and
% perhaps eventually move to usrguide3).
%
% \subsection{Generic hooks}
% \label{sec:generic}
%
% As stated earlier, with the exception of generic hooks, all hooks must
% be declared with \cs{NewHook} before they can be used.
% All generic hooks have names of the form
% \enquote{\meta{type}/\meta{name}/\meta{position}}, where \meta{type}
% is from the predefined list shown below, and \meta{name} is the variable
% part whose meaning will depend on the \meta{type}. The last component,
% \meta{position}, has more complex possibilities:
% it can always be |before| or |after|; for |env| hooks, it can also be |begin|
% or |end|; and for |include| hooks it can also be |end|. Each specific
% hook is documented below, or in \texttt{ltcmdhooks-doc.pdf} or
% \texttt{ltfilehook-doc.pdf}.
%
% The generic hooks provided by \LaTeX{} belong to one of the six types:
% \begin{description}
% \item[env] Hooks executed before and after environments --
% \meta{name} is the name of the environment, and available values
% for \meta{position} are |before|, |begin|, |end|, and |after|;
% \item[cmd] Hooks added to and executed before and after commands --
% \meta{name} is the name of the command, and available values
% for \meta{position} are |before| and |after|;
% \item[file] Hooks executed before and after reading a file --
% \meta{name} is the name of the file (with extension), and
% available values for \meta{position} are |before| and |after|;
% \item[package] Hooks executed before and after loading packages --
% \meta{name} is the name of the package, and available values for
% \meta{position} are |before| and |after|;
% \item[class] Hooks executed before and after loading classes --
% \meta{name} is the name of the class, and available values for
% \meta{position} are |before| and |after|;
% \item[include] Hooks executed before and after \cs{include}d files --
% \meta{name} is the name of the included file (without the |.tex|
% extension), and available values for \meta{position} are |before|,
% |end|, and |after|.
% \end{description}
%
% Each of the hooks above are detailed in the following sections and
% in linked documentation.
% ^^A^^A^^A \pho{Wouldn't it be better to document all hooks here?}
%
% \subsubsection{Generic hooks for all environments}
%
% Every environment \meta{env} has now four associated hooks coming
% with it:
% \begin{description}
% \item[\hook{env/\meta{env}/before}]
%
% This hook is executed as part of \cs{begin} as the very first
% action, in particular prior to starting the environment group.
% Its scope is therefore not restricted by the environment.
%
% \item[\hook{env/\meta{env}/begin}]
%
% This hook is executed as part of \cs{begin} directly in front
% of the code specific to the environment start (e.g., the
% second argument of \cs{newenvironment}). Its scope is the
% environment body.
%
% \item[\hook{env/\meta{env}/end}]
%
% This hook is executed as part of \cs{end} directly in front of the
% code specific to the end of the environment (e.g., the third
% argument of \cs{newenvironment}).
%
% \item[\hook{env/\meta{env}/after}]
%
% This hook is executed as part of \cs{end} after the
% code specific to the environment end and after the environment
% group has ended.
% Its scope is therefore not restricted by the environment.
%
% The hook is implemented as a reversed hook so if two packages
% add code to \hook{env/\meta{env}/before} and to
% \hook{env/\meta{env}/after} they can add surrounding
% environments and the order of closing them happens in the
% right sequence.
%
% \end{description}
% Generic environment hooks are never one-time hooks even with
% environments that are supposed to appear only once in a
% document.\footnote{Thus if one adds code to such hooks after the
% environment has been processed, it will only be executed if the
% environment appears again and if that doesn't happen the code
% will never get executed.} In contrast to other hooks there is
% also no need to declare them using \cs{NewHook}.
%
% The hooks are only executed if \cs{begin}\Arg{env} and
% \cs{end}\Arg{env} is used. If the environment code is executed
% via low-level calls to \cs[no-index]{\meta{env}} and \cs[no-index]{end\meta{env}}
% (e.g., to avoid the environment grouping) they are not
% available. If you want them available in code using this method,
% you would need to add them yourself, i.e., write something like
%\begin{verbatim}
% \UseHook{env/quote/before}\quote
% ...
% \endquote\UseHook{env/quote/after}
%\end{verbatim}
% to add the outer hooks, etc.
%
%
% Largely for compatibility with existing packages, the following
% four commands are also available to set the environment hooks; but for
% new packages we recommend directly using the hook names and
% \cs{AddToHook}.
%
%
% \begin{function}{\BeforeBeginEnvironment}
% \begin{syntax}
% \cs{BeforeBeginEnvironment} \oarg{label} \Arg{env} \Arg{code}
% \end{syntax}
% This declaration adds to the \hook{env/\meta{env}/before} hook
% using the \meta{label}. If \meta{label} is not given, the
% \meta{default label} is used (see section~\ref{sec:default-label}).
% \end{function}
%
% \begin{function}{\AtBeginEnvironment}
% \begin{syntax}
% \cs{AtBeginEnvironment} \oarg{label} \Arg{env} \Arg{code}
% \end{syntax}
% This is like \cs{BeforeBeginEnvironment} but it adds to the \hook{env/\meta{env}/begin} hook.
% \end{function}
%
% \begin{function}{\AtEndEnvironment}
% \begin{syntax}
% \cs{AtEndEnvironment} \oarg{label} \Arg{env} \Arg{code}
% \end{syntax}
% This is like \cs{BeforeBeginEnvironment} but it adds to the \hook{env/\meta{env}/end} hook.
% \end{function}
%
% \begin{function}{\AfterEndEnvironment}
% \begin{syntax}
% \cs{AfterEndEnvironment} \oarg{label} \Arg{env} \Arg{code}
% \end{syntax}
% This is like \cs{BeforeBeginEnvironment} but it adds to the \hook{env/\meta{env}/after} hook.
% \end{function}
%
%
% \subsubsection{Generic hooks for commands}
%
% Similar to environments there are now (at least in theory) two
% generic hooks available for any \LaTeX{} command. These are
% \begin{description}
% \item[\hook{cmd/\meta{name}/before}]
%
% This hook is executed at the very start of the command
% execution.
%
% \item[\hook{cmd/\meta{name}/after}]
% This hook is executed at the very end of the command body. It is
% implemented as a reversed hook.
% \end{description}
% In practice there are restrictions and especially the
% \hook{after} hook works only with a subset of commands. Details
% about these restrictions are documented in
% \texttt{ltcmdhooks-doc.pdf} or with code in
% \texttt{ltcmdhooks-code.pdf}.
%
%
%
%
% \subsubsection{Generic hooks provided by file loading operations}
%
% There are several hooks added to \LaTeX{}'s process of loading
% file via its high-level interfaces such as \cs{input},
% \cs{include}, \cs{usepackage}, \cs{RequirePackage}, etc. These
% are documented in \texttt{ltfilehook-doc.pdf} or with code in
% \texttt{ltfilehook-code.pdf}.
%
%
%
% \subsection{Hooks provided by \cs{begin}\texttt{\{document\}}}
% \label{sec:begindocument-hooks}
%
% Until 2020 \cs{begin}\texttt{\{document\}} offered exactly one
% hook that one could add to using
% \cs{AtBeginDocument}. Experiences over the years have shown that
% this single hook in one place was not enough and as part of
% adding the general hook management system a number of additional
% hooks have been added at this point. The places for these hooks have
% been chosen to provide the same support as offered by external
% packages, such as \pkg{etoolbox} and others that augmented
% \cs{document} to gain better control.
%
% Supported are now the following hooks (all of them one-time hooks):
% \begin{description}
%
%
% \item[\hook{begindocument/before}]
%
% This hook is executed at the very start of \cs{document}, one can
% think of it as a hook for code at the end of the preamble
% section and this is how it is used by \pkg{etoolbox}'s
% \cs{AtEndPreamble}.
%
% \onetimetext
%
% \item[\hook{begindocument}]
%
% This hook is added to when using \cs{AtBeginDocument} and it is
% executed after the \texttt{.aux} file as be read in and most
% initialization are done, so they can be altered and inspected by
% the hook code. It is followed by a small number of further
% initializations that shouldn't be altered and are therefore
% coming later.
%
% The hook should not be used to add material for typesetting as
% we are still in \LaTeX's initialization phase and not in the
% document body. If such material needs to be added to the document
% body use the next hook instead.
%
% \onetimetext
%
% \item[\hook{begindocument/end}]
%
% This hook is executed at the end of the \cs{document} code in
% other words at the beginning of the document body. The only
% command that follows it is \cs{ignorespaces}.
%
% \onetimetext
%
% \end{description}
% The generic hooks executed by \cs{begin} also exist, i.e.,
% \hook{env/document/before} and \hook{env/document/begin}, but
% with this special environment it is better use the dedicated
% one-time hooks above.
%
%
%
%
% \subsection{Hooks provided by \cs{end}\texttt{\{document\}}}
%
% \LaTeXe{} always provided \cs{AtEndDocument} to add code to the
% execution of \verb=\end{document}= just in front of the code that
% is normally executed there. While this was a big improvement over
% the situation in \LaTeX\,2.09 it was not flexible enough for a
% number of use cases and so packages, such as \pkg{etoolbox},
% \pkg{atveryend} and others patched \cs{enddocument} to add
% additional points where code could be hooked into.
%
% Patching using packages is always problematical as leads to
% conflicts (code availability, ordering of patches, incompatible
% patches, etc.). For this reason a number of additional hooks
% have been added to the \cs{enddocument} code to allow packages
% to add code in various places in a controlled way without the
% need for overwriting or patching the core code.
%
% Supported are now the following hooks (all of them one-time hooks):
% \begin{description}
%
% \item[\hook{enddocument}]
%
% The hook associated with \cs{AtEndDocument}. It is immediately
% called at the beginning of \cs{enddocument}.
%
% When this hook is executed there may be still unprocessed
% material (e.g., floats on the deferlist) and the hook may add
% further material to be typeset. After it, \cs{clearpage} is
% called to ensure that all such material gets typeset. If there
% is nothing waiting the \cs{clearpage} has no effect.
%
% \onetimetext
%
% \item[\hook{enddocument/afterlastpage}]
%
% As the name indicates this hook should not receive code that
% generates material for further pages. It is the right place to
% do some final housekeeping and possibly write out some
% information to the \texttt{.aux} file (which is still open at
% this point to receive data, but since there will be no more pages you
% need to write to it using \cs{immediate}\cs{write}). It is also the
% correct place to
% set up any testing code to be run when the \texttt{.aux} file
% is re-read in the next step.
%
%
% After this hook has been executed the \texttt{.aux} file is
% closed for writing and then read back in to do some tests
% (e.g., looking for missing references or duplicated labels, etc.).
%
% \onetimetext
%
% \item[\hook{enddocument/afteraux}]
%
% At this point, the \texttt{.aux} file has been reprocessed and so
% this is a possible place for final checks and display of
% information to the user. However, for the latter you might
% prefer the next hook, so that your information is displayed after the
% (possibly longish) list of files if that got requested via \cs{listfiles}.
%
% \onetimetext
%
% \item[\hook{enddocument/info}]
%
% This hook is meant to receive code that write final information
% messages to the terminal. It follows immediately after the
% previous hook (so both could have been combined, but then
% packages adding further code would always need to also supply
% an explicit rule to specify where it should go.
%
% This hook already contains some code added by the kernel (under
% the labels \texttt{kernel/filelist} and
% \texttt{kernel/warnings}), namely the list of files when
% \cs{listfiles} has been used and the warnings for duplicate
% labels, missing references, font substitutions etc.
%
% \onetimetext
%
% \item[\hook{enddocument/end}]
%
% Finally, this hook is executed just in front of the final call
% to \cs{@{}@end}.
%
% \onetimetext % is it even possible to add code after this one?
%
% \end{description}
%
%
% There is also the hook \hook{shipout/lastpage}. This hook is
% executed as part of the last \cs{shipout} in the document to
% allow package to add final \cs{special}'s to that page. Where
% this hook is executed in relation to those from the above list
% can vary from document to document. Furthermore to determine correctly
% which of the \cs{shipout}s is the last one, \LaTeX{} needs to be run
% several times, so initially it might get executed on the wrong
% page. See section~\ref{sec:shipout} for where to find the details.
%
%
% It is in also possible to use the generic \hook{env/document/end}
% hook which is executed by \cs{end}, i.e., just in front of the
% first hook above. Note however that the other generic \cs{end}
% environment hook, i.e., \hook{env/document/after} will never get
% executed, because by that time \LaTeX{} has finished the document
% processing.
%
%
%
%
% \subsection{Hooks provided by \cs{shipout} operations}
% \label{sec:shipout}
%
% There are several hooks and mechanisms added to \LaTeX{}'s
% process of generating pages. These are documented in
% \texttt{ltshipout-doc.pdf} or with code in
% \texttt{ltshipout-code.pdf}.
%
%
% \subsection{Hooks provided for paragraphs}
% \label{sec:para}
%
% The paragraph processing has been augmented to include a number of
% internal and public hooks. These are documented in
% \texttt{ltpara-doc.pdf} or with code in
% \texttt{ltpara-code.pdf}.
%
%
%
% \subsection{Hooks provided in NFSS commands}
%
% In languages that need to support for more than one script in
% parallel (and thus several sets of fonts, e.g., supporting both Latin and
% Japanese fonts), NFSS font commands such as \cs{sffamily} need
% to switch both the Latin family to ``Sans Serif'' and in addition
% alter a second set of fonts.
%
% To support this, several NFSS commands have hooks to which
% such support can be added.
% \begin{description}
%
% \item[\hook{rmfamily}]
%
% After \cs{rmfamily} has done its initial checks and prepared a
% font series update, this hook is executed before \cs{selectfont}.
%
% \item[\hook{sffamily}]
%
% This is like the \hook{rmfamily} hook, but for the \cs{sffamily} command.
%
% \item[\hook{ttfamily}]
%
% This is like the \hook{rmfamily} hook, but for the \cs{ttfamily} command.
%
% \item[\hook{normalfont}]
%
% The \cs{normalfont} command resets the font encoding, family, series
% and shape to their document defaults. It then executes this
% hook and finally calls \cs{selectfont}.
%
% \item[\hook{expand@font@defaults}]
%
% The internal \cs{expand@font@defaults} command expands and
% saves the current defaults for the meta families (rm/sf/tt) and
% the meta series (bf/md). If the NFSS machinery has been
% augmented, e.g., for Chinese or Japanese fonts, then further
% defaults may need to be set at this point. This can be done in
% this hook which is executed at the end of this macro.
%
% \item[\hook{bfseries/defaults}, \hook{bfseries}]
%
% If the \cs{bfdefault} was explicitly changed by the user, its
% new value is used to set the bf series defaults for the meta
% families (rm/sf/tt) when \cs{bfseries} is called. The
% \hook{bfseries/defaults} hook allows further adjustments to be made
% in this case. This hook is only executed if such a change is
% detected. In contrast, the \hook{bfseries} hook is always
% executed just before \cs{selectfont} is called to change to the
% new series.
%
%
% \item[\hook{mdseries/defaults}, \hook{mdseries}]
%
% These two hooks are like the previous ones but they are in the
% \cs{mdseries} command.
%
% \item[\hook{selectfont}]
%
% This hook is executed inside \cs{selectfont}, after the current
% values for \textit{encoding}, \textit{family}, \textit{series},
% \textit{shape}, and \textit{size} are evaluated and the new font
% is selected (and if necessary loaded). After the hook has
% executed, NFSS will still do any updates necessary for a new
% \textit{size} (such as changing the size of \cs{strut}) and any
% updates necessary to a change in \textit{encoding}.
%
% This hook is intended for use cases where, in parallel to a
% change in the main font, some other fonts need to be altered
% (e.g., in CJK processing where you may need to deal with several
% different alphabets).
%
% \end{description}
%
%
%
% \subsection{Hook provided by the mark mechanism}
%
% See \texttt{ltmarks-doc.pdf} for details.
% \begin{description}
%
% \item[\hook{insertmark}]
%
% This hook allows for a special setup while \cs{InsertMark}
% inserts a mark. It is executed in group so local changes only
% apply to the mark being inserted.
%
% \end{description}
%
% \MaybeStop{\setlength\IndexMin{200pt} \PrintIndex }
%
%
% \section{The Implementation}
%
%
% \begin{macrocode}
%<@@=hook>
% \end{macrocode}
%
% \changes{v1.0i}{2021/03/18}{Use \cs{NewModuleRelease}.}
% \changes{v1.0n}{2021/05/24}{Use \cs{msg_...} instead of \cs{__kernel_msg...}}
%
% \begin{macrocode}
%<*2ekernel|latexrelease>
\ExplSyntaxOn
%<latexrelease>\NewModuleRelease{2020/10/01}{lthooks}
%<latexrelease> {The~hook~management~system}
% \end{macrocode}
%
% \subsection{Debugging}
%
% \begin{macro}{\g_@@_debug_bool}
% Holds the current debugging state.
% \begin{macrocode}
\bool_new:N \g_@@_debug_bool
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\hook_debug_on:,\hook_debug_off:}
% \begin{macro}{\@@_debug:n}
% \begin{macro}{\@@_debug_gset:}
% Turns debugging on and off by redefining \cs{@@_debug:n}.
% \begin{macrocode}
\cs_new_eq:NN \@@_debug:n \use_none:n
\cs_new_protected:Npn \hook_debug_on:
{
\bool_gset_true:N \g_@@_debug_bool
\@@_debug_gset:
}
\cs_new_protected:Npn \hook_debug_off:
{
\bool_gset_false:N \g_@@_debug_bool
\@@_debug_gset:
}
\cs_new_protected:Npn \@@_debug_gset:
{
\cs_gset_protected:Npx \@@_debug:n ##1
{ \bool_if:NT \g_@@_debug_bool {##1} }
}
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
%
% \subsection{Borrowing from internals of other kernel modules}
%
%
% \begin{macro}[EXP]{\@@_str_compare:nn}
% Private copy of \cs{__str_if_eq:nn}
% \InternalDetectionOff
% \begin{macrocode}
\cs_new_eq:NN \@@_str_compare:nn \__str_if_eq:nn
% \end{macrocode}
% \InternalDetectionOn
% \end{macro}
%
% \subsection{Declarations}
%
% \begin{macro}{\l_@@_tmpa_bool}
% Scratch boolean used throughout the package.
% \begin{macrocode}
\bool_new:N \l_@@_tmpa_bool
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\l_@@_return_tl,\l_@@_tmpa_tl,\l_@@_tmpb_tl}
% Scratch variables used throughout the package.
% \begin{macrocode}
\tl_new:N \l_@@_return_tl
\tl_new:N \l_@@_tmpa_tl
\tl_new:N \l_@@_tmpb_tl
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\g_@@_all_seq}
% In a few places we need a list of all hook names ever defined so
% we keep track if them in this sequence.
% \begin{macrocode}
\seq_new:N \g_@@_all_seq
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\l_@@_cur_hook_tl}
% Stores the name of the hook currently being sorted.
% \begin{macrocode}
\tl_new:N \l_@@_cur_hook_tl
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\l_@@_work_prop}
% A property list holding a copy of the
% \cs[no-index]{g_@@_\meta{hook}_code_prop} of the hook being sorted
% to work on, so that changes don't act destructively on the hook data
% structure.
% \begin{macrocode}
\prop_new:N \l_@@_work_prop
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\g_@@_used_prop}
% All hooks that receive code (for use in debugging display).
% \begin{macrocode}
\prop_new:N \g_@@_used_prop
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\g_@@_hook_curr_name_tl,\g_@@_name_stack_seq}
% Default label used for hook commands, and a stack to keep track of
% packages within packages.
% \begin{macrocode}
\tl_new:N \g_@@_hook_curr_name_tl
\seq_new:N \g_@@_name_stack_seq
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tmp:w}
% Temporary macro for generic usage.
% \begin{macrocode}
\cs_new_eq:NN \@@_tmp:w ?
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\tl_gremove_once:Nx,\tl_show:x,\tl_log:x}
% Some variants of \pkg{expl3} functions.
% \fmiinline{should probably be moved to expl3}
% \begin{macrocode}
\cs_generate_variant:Nn \tl_gremove_once:Nn { Nx }
\cs_generate_variant:Nn \tl_show:n { x }
\cs_generate_variant:Nn \tl_log:n { x }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\s_@@_mark}
% Scan mark used for delimited arguments.
% \begin{macrocode}
\scan_new:N \s_@@_mark
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_clean_to_scan:w}
% Removes tokens until the next \cs{s_@@_mark}.
% \begin{macrocode}
\cs_new:Npn \@@_clean_to_scan:w #1 \s_@@_mark { }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tl_set:Nn,\@@_tl_set:Nx,
% \@@_tl_set:cn,\@@_tl_set:cx}
% Private copies of a few \pkg{expl3} functions. \pkg{l3debug} will
% only add debugging to the public names, not to these copies, so we
% don't have to use \cs{debug_suspend:} and \cs{debug_resume:}
% everywhere.
%
% Functions like \cs{@@_tl_set:Nn} have to be redefined, rather than
% copied because in \pkg{expl3} they use
% \cs[no-index]{__kernel_tl_(g)set:Nx}, which is also patched by
% \pkg{l3debug}.
% \changes{v1.0h}{2021/01/07}{Manually define some \pkg{l3tl} commands
% to work around \pkg{expl3} changes}
% \begin{macrocode}
\cs_new_protected:Npn \@@_tl_set:Nn #1#2
{ \cs_set_nopar:Npx #1 { \__kernel_exp_not:w {#2} } }
\cs_new_protected:Npn \@@_tl_set:Nx #1#2
{ \cs_set_nopar:Npx #1 {#2} }
\cs_generate_variant:Nn \@@_tl_set:Nn { c }
\cs_generate_variant:Nn \@@_tl_set:Nx { c }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tl_gset:Nn,\@@_tl_gset:No,\@@_tl_gset:Nx,
% \@@_tl_gset:cn,\@@_tl_gset:co,\@@_tl_gset:cx}
% Same as above.
% \begin{macrocode}
\cs_new_protected:Npn \@@_tl_gset:Nn #1#2
{ \cs_gset_nopar:Npx #1 { \__kernel_exp_not:w {#2} } }
\cs_new_protected:Npn \@@_tl_gset:No #1#2
{ \cs_gset_nopar:Npx #1 { \__kernel_exp_not:w \exp_after:wN {#2} } }
\cs_new_protected:Npn \@@_tl_gset:Nx #1#2
{ \cs_gset_nopar:Npx #1 {#2} }
\cs_generate_variant:Nn \@@_tl_gset:Nn { c }
\cs_generate_variant:Nn \@@_tl_gset:No { c }
\cs_generate_variant:Nn \@@_tl_gset:Nx { c }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tl_gput_right:Nn,\@@_tl_gput_right:No,\@@_tl_gput_right:cn}
% Same as above.
% \begin{macrocode}
\cs_new_protected:Npn \@@_tl_gput_right:Nn #1#2
{ \@@_tl_gset:Nx #1 { \__kernel_exp_not:w \exp_after:wN { #1 #2 } } }
\cs_generate_variant:Nn \@@_tl_gput_right:Nn { No, cn }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tl_gput_left:Nn,\@@_tl_gput_left:No}
% Same as above.
% \begin{macrocode}
\cs_new_protected:Npn \@@_tl_gput_left:Nn #1#2
{
\@@_tl_gset:Nx #1
{ \__kernel_exp_not:w {#2} \__kernel_exp_not:w \exp_after:wN {#1} }
}
\cs_generate_variant:Nn \@@_tl_gput_left:Nn { No }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tl_gset_eq:NN}
% Same as above.
% \begin{macrocode}
\cs_new_eq:NN \@@_tl_gset_eq:NN \tl_gset_eq:NN
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_tl_gclear:N,\@@_tl_gclear:c}
% Same as above.
% \begin{macrocode}
\cs_new_protected:Npn \@@_tl_gclear:N #1
{ \@@_tl_gset_eq:NN #1 \c_empty_tl }
\cs_generate_variant:Nn \@@_tl_gclear:N { c }
% \end{macrocode}
% \end{macro}
%
%
% \subsection{Providing new hooks}
%
% \subsubsection{The data structures of a hook}
%
% \DescribeMacro{\g_@@_\meta{hook}_code_prop}
% \DescribeMacro{\@@\textvisiblespace\meta{hook}}
% \DescribeMacro{\@@_next\textvisiblespace\meta{hook}}
% Hooks have a name (called \meta{hook} in the description below)
% and for each hook we have to
% provide a number of data structures. These are
% \begin{description}
% \item[\cs{g_@@_\meta{hook}_code_prop}] A property list holding the code
% for the hook in separate chunks. The keys are by default the
% package names that add code to the hook, but it is possible
% for packages to define other keys.
%
% \item[{\cs[no-index]{g_@@_\meta{hook}_rule_\meta{label1}\string|\meta{label2}_tl}}]
% A token list holding the relation between \meta{label1} and
% \meta{label2} in the \meta{hook}. The \meta{labels} are lexically
% (reverse) sorted to ensure that two labels always point to the same
% token list. For global rules, the \meta{hook} name is \texttt{??}.
%
% \item[\cs{@@\textvisiblespace\meta{hook}}] The code that is actually executed
% when the hook is called in the document is stored in this token
% list. It is constructed from the code chunks applying the
% information.
% This token list is named like that so that in case of an error
% inside the hook, the reported token list in the error is shorter,
% and to make it simpler to normalize hook names in
% \cs{@@_make_name:n}.
%
% \item[\cs{g_@@_\meta{hook}_reversed_tl}] Some hooks are
% \enquote{reversed}. This token list stores a |-| for such hook
% so that it can be identified. The |-| character is used because
% $\meta{reversed}1$ is $+1$ for normal hooks and $-1$ for reversed
% ones.
% \item[{\cs[no-index]{g_@@_\meta{hook}_declared_tl}}] This token
% list serves as marker for the hook being officially declared. Its
% existence is tested to raise an error in case another declaration
% is attempted.
%
% \item[\cs{@@_toplevel\textvisiblespace\meta{hook}}] This token list stores the code
% inserted in the hook from the user's document, in the |top-level|
% label. This label is special, and doesn't participate in sorting.
% Instead, all code is appended to it and executed after (or before,
% if the hook is reversed) the normal
% hook code, but before the |next| code chunk.
%
% \item[\cs{@@_next\textvisiblespace\meta{hook}}] Finally there is
% extra code (normally empty) that is used on the next invocation
% of the hook (and then deleted). This can be used to define some
% special behavior for a single occasion from within the document.
% This token list follows the same naming scheme than the main
% \cs{@@\textvisiblespace\meta{hook}} token list. It is called
% \cs{@@_next\textvisiblespace\meta{hook}} rather than
% \cs[no-index]{@@\textvisiblespace next_\meta{hook}} because
% otherwise a hook whose name is |next_|\meta{hook} would clash
% with the next code-token list of the hook called \meta{hook}.
%
% \end{description}
%
%
% \subsubsection{On the existence of hooks}
% \label{sec:existence}
%
% A hook may be in different states of existence. Here we give an
% overview of the internal commands to set up hooks and explain how the
% different states are distinguished. The actual implementation
% then follows in subsequent sections.
%
% One problem we have to solve is that we need to be able to add
% code to hooks (e.g., with \cs{AddToHook}) even if that code has
% not yet been declared. For example, one package needs to write
% into a hook of another package, but that package may not get
% loaded, or is loaded only later. Another problem is that most hooks,
% but not the generic hooks, require a declaration.
%
% We therefore distinguish the following states for a hook, which
% are managed by four different tests: structure existence
% (\cs{@@_if_structure_exist:nTF}), creation
% (\cs{@@_if_usable:nTF}), declaration (\cs{@@_if_declared:nTF})
% and disabled or not (\cs{@@_if_disabled:nTF})
% \begin{description}
% \setlist[itemize]{leftmargin=5cm,format=\cs}
% \item[not existing]
%
% Nothing is known about the hook so far. This state can be
% detected with \cs{@@_if_structure_exist:nTF}
% (which uses the false branch).
%
% In this state the hook can be declared, disabled, rules can be
% defined or code could be added to it, but it is not possible
% to use the hook (with \cs{UseHook}).
% \item[basic data structure set up]
%
% A hook is this state when its basic data structure has been
% set up (using \cs{@@_init_structure:n}). The data structure setup happens
% automatically when commands such as \cs{AddToHook} are used
% and the hook is at that point in state \enquote{not existing}.
%
% In this state the four tests give the following results:
% \begin{itemize}
% \item [@@_if_structure_exist:nTF] returns |true|.
% \item [@@_if_usable:nTF] returns |false|.
% \item [@@_if_declared:nTF] returns |false|.
% \item [@@_if_disabled:nTF] returns |false|.
% \end{itemize}
%
% The allowed actions are the same as in the \enquote{not
% existing} state.
%
% \item[declared]
%
% A hook is in this state it is not disabled and was explicitly declared (e.g.,
% with \cs{NewHook}). In this case the four tests give the
% following results:
% \begin{itemize}
% \item [@@_if_structure_exist:nTF] returns |true|.
% \item [@@_if_usable:nTF] returns |true|.
% \item [@@_if_declared:nTF] returns |true|.
% \item [@@_if_disabled:nTF] returns |false|.
% \end{itemize}
%
% \item[usable]
%
% A hook is in this state if it is not disabled, was not
% explicitly declared but nevertheless is allowed to be used
% (with \cs{UseHook} or \cs{hook_use:n}). This state is only
% possible for generic hooks as they do not need to be
% declared. Therefore such hooks move directly from state
% \enquote{not existing} to \enquote{usable} the moment a
% declaration such as \cs{AddToHook} wants to add to the hook
% data structure. In this state the tests give the following
% results:
% \begin{itemize}
% \item [@@_if_structure_exist:nTF] returns |true|.
% \item [@@_if_usable:nTF] returns |true|.
% \item [@@_if_declared:nTF] returns |false|.
% \item [@@_if_disabled:nTF] returns |false|.
% \end{itemize}
%
%
% \item[disabled]
%
% A generic hook in any state is moved to this state when
% \cs{DisableGenericHook} is used. This changes the tests to give the
% following results:
% \begin{itemize}
% \item [@@_if_structure_exist:nTF] \emph{unchanged}.
% \item [@@_if_usable:nTF] returns |false|.
% \item [@@_if_declared:nTF] returns |true|.
% \item [@@_if_disabled:nTF] returns |true|.
% \end{itemize}
% The structure test is unchanged (if the hook was unknown before it is
% |false|, otherwise |true|). The usable test returns |false| so that
% any \cs{UseHook} will bypass the hook from now on. The
% declared test returns true so that any further \cs{NewHook}
% generates an error and the disabled test returns true so that
% \cs{AddToHook} can return an error.
% \fmiinline{maybe it should do this only after begin document?}
%
% \end{description}
%
%
%
%
% \subsubsection{Setting hooks up}
%
%
% \begin{macro}{\hook_new:n,\@@_new:n}
% The \cs{hook_new:n} declaration declares a new hook and expects
% the hook \meta{name} as its argument, e.g.,
% \hook{begindocument}.
% \begin{macrocode}
\cs_new_protected:Npn \hook_new:n #1
{ \@@_normalize_hook_args:Nn \@@_new:n {#1} }
% \end{macrocode}
%
% \begin{macrocode}
\cs_new_protected:Npn \@@_new:n #1
{
% \end{macrocode}
% We check if the hook was already \emph{explicitly} declared with
% \cs{hook_new:n}, and if it already exists we complain, otherwise set
% the \enquote{created} flag for the hook so that it errors next time
% \cs{hook_new:n} is used.
% \begin{macrocode}
\@@_if_declared:nTF {#1}
{ \msg_error:nnn { hooks } { exists } {#1} }
{
\tl_new:c { g_@@_#1_declared_tl }
\@@_make_usable:n {#1}
}
}
% \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}{\@@_make_usable:n}
%
% This initializes all hook data structures for the hook but if
% used on its own doesn't mark the hook as declared (as
% \cs{hook_new:n} does, so a later \cs{hook_new:n} on that hook will
% not result in an error. This command is internally used by
% \cs{hook_gput_code:n} when adding code to a generic hook.
% \begin{macrocode}
\cs_new_protected:Npn \@@_make_usable:n #1
{
% \end{macrocode}
% Now we check if the hook's data structure can be safely created
% without \pkg{expl3} raising errors, then
% we add the hook name to the list of all hooks and
% allocate the necessary data structures for the new hook,
% otherwise just do nothing.
% \begin{macrocode}
\tl_if_exist:cF { @@~#1 }
{
\seq_gput_right:Nn \g_@@_all_seq {#1}
% \end{macrocode}
% This is only used by the actual code of the current hook, so
% declare it normally:
% \begin{macrocode}
\tl_new:c { @@~#1 }
% \end{macrocode}
% Now ensure that the base data structure for the hook exists:
% \begin{macrocode}
\@@_init_structure:n {#1}
% \end{macrocode}
% The \cs{g_@@_\meta{hook}_labels_clist} holds the sorted list of
% labels (once it got sorted). This is used only for debugging.
% \begin{macrocode}
\clist_new:c { g_@@_#1_labels_clist }
% \end{macrocode}
% Some hooks should reverse the default order of code chunks. To
% signal this we have a token list which is empty for normal hooks
% and contains a \verb=-= for reversed hooks.
% \begin{macrocode}
\tl_new:c { g_@@_#1_reversed_tl }
% \end{macrocode}
% The above is all in L3 convention, but we also provide an
% interface to legacy \LaTeXe{} hooks of the form \cs{@...hook},
% e.g., \cs{@begindocumenthook}.
% there have been a few of them and they have been added to
% using \cs{g@addto@macro}. If there exists such a macro matching
% the name of the new hook, i.e.,
% \verb+\@+\meta{hook-name}\texttt{hook} and it is not empty then
% we add its contents as a code chunk under the label \texttt{legacy}.
% \begin{quote}
% \textbf{Warning: this support will vanish in future releases!}
% \end{quote}
%
% \begin{macrocode}
\@@_include_legacy_code_chunk:n {#1}
}
}
% \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}{\@@_init_structure:n}
% This function declares the basic data structures for a hook
% without explicit declaring the hook itself. This is needed to
% allow adding to undeclared hooks. Here it is unnecessary to
% check whether all variables exist, since all three are declared
% at the same time (either all of them exist, or none).
%
% It creates the hook code pool
% (\cs[no-index]{g_@@_\meta{hook}_code_prop}) and the |top-level|
% and |next| token lists. A hook is initialized with
% \cs{@@_init_structure:n} the first time anything is added to it.
% Initializing a hook just with \cs{@@_init_structure:n} will not
% make it usable with \cs{hook_use:n}.
% \begin{macrocode}
\cs_new_protected:Npn \@@_init_structure:n #1
{
\@@_if_structure_exist:nF {#1}
{
\prop_new:c { g_@@_#1_code_prop }
\tl_new:c { @@_toplevel~#1 }
\tl_new:c { @@_next~#1 }
}
}
% \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}{\hook_new_reversed:n,\@@_new_reversed:n}
%
% Declare a new hook. The default ordering of code chunks is
% reversed, signaled by setting the token list to a minus sign.
% \begin{macrocode}
\cs_new_protected:Npn \hook_new_reversed:n #1
{ \@@_normalize_hook_args:Nn \@@_new_reversed:n {#1} }
\cs_new_protected:Npn \@@_new_reversed:n #1
{
\@@_new:n {#1}
% \end{macrocode}
% If the hook already exists the above will generate an error
% message, so the next line should be executed (but it is --- too
% bad).
% \begin{macrocode}
\tl_gset:cn { g_@@_#1_reversed_tl } { - }
}
% \end{macrocode}
% \end{macro}
%
%
%
%
% \begin{macro}{\hook_new_pair:nn}
% A shorthand for declaring a normal and a (matching) reversed hook in one go.
% \begin{macrocode}
\cs_new_protected:Npn \hook_new_pair:nn #1#2
{ \hook_new:n {#1} \hook_new_reversed:n {#2} }
% \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_include_legacy_code_chunk:n}
% The \LaTeX{} legacy concept for hooks uses with hooks the
% following naming scheme in the code: \cs{@...hook}.
%
% If this macro is not empty we add it under the label
% \texttt{legacy} to the current hook and then empty it globally.
% This way packages or classes directly manipulating commands such
% as \cs{@begindocumenthook} still get their hook data added.
% \begin{quote}
% \textbf{Warning: this support will vanish in future releases!}
% \end{quote}
% \begin{macrocode}
\cs_new_protected:Npn \@@_include_legacy_code_chunk:n #1
{
% \end{macrocode}
% If the macro doesn't exist (which is the usual case) then nothing
% needs to be done.
% \begin{macrocode}
\tl_if_exist:cT { @#1hook }
% \end{macrocode}
% Of course if the legacy hook exists but is empty, there is no need
% to add anything under \texttt{legacy} the legacy label.
% \begin{macrocode}
{
\tl_if_empty:cF { @#1hook }
{
\exp_args:Nnnv \@@_hook_gput_code_do:nnn {#1}
{ legacy } { @#1hook }
% \end{macrocode}
% Once added to the hook, we need to clear it otherwise it might
% get added again later if the hook data gets updated.
% \begin{macrocode}
\@@_tl_gclear:c { @#1hook }
}
}
}
% \end{macrocode}
% \end{macro}
%
%
%
% \subsubsection{Disabling and providing hooks}
%
% \changes{v1.0p}{2021/08/20}{Renames of generic hook commands (gh/638)}
%
% \begin{macro}{\hook_disable_generic:n}
% \begin{macro}{\@@_disable:n}
% \begin{macro}[pTF]{\@@_if_disabled:n}
%
% Disables a hook by creating its
% \cs[no-index]{g_@@_\meta{hook}_declared_tl} so that the hook
% errors when used with \cs{hook_new:n}, then it undefines
% \cs{@@\textvisiblespace\meta{hook}} so that it may not be executed.
%
% This does not clear any code that may be already stored in the
% hook's structure, but doesn't allow adding more code.
% \cs{@@_if_disabled:nTF} uses that specific combination to check
% if the hook is disabled.
%
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\hook_disable_generic:n}{Disable~hooks}
% \end{macrocode}
%
% \begin{macrocode}
\cs_new_protected:Npn \hook_disable_generic:n #1
{ \@@_normalize_hook_args:Nn \@@_disable:n {#1} }
\cs_new_protected:Npn \@@_disable:n #1
{
\tl_gclear_new:c { g_@@_#1_declared_tl }
\cs_undefine:c { @@~#1 }
}
\prg_new_conditional:Npnn \@@_if_disabled:n #1 { p, T, F, TF }
{
\bool_lazy_and:nnTF
{ \tl_if_exist_p:c { g_@@_#1_declared_tl } }
{ ! \tl_if_exist_p:c { @@~#1 } }
{ \prg_return_true: }
{ \prg_return_false: }
}
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
%
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}
%<latexrelease> {\hook_disable_generic:n}{Disable~hooks}
%<latexrelease>
%<latexrelease>\cs_new_protected:Npn \hook_disable_generic:n #1 {}
%<latexrelease>
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\hook_activate_generic:n}
% \begin{macro}{\@@_activate_generic:n}
% The \cs{hook_activate_generic:n} declaration declares a new hook if it
% wasn't declared already, in which case it only checks that the
% already existing hook is not a reversed hook.
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\hook_activate_generic:n}{Providing~hooks}
% \end{macrocode}
%
% \begin{macrocode}
\cs_new_protected:Npn \hook_activate_generic:n #1
{ \@@_normalize_hook_args:Nn \@@_activate_generic:nn {#1} { } }
% \end{macrocode}
%
% \begin{macrocode}
\cs_new_protected:Npn \@@_activate_generic:nn #1 #2
{
% \end{macrocode}
% If the hook to be activated was disabled we warn (for now --- this
% may change).
% \begin{macrocode}
\@@_if_disabled:nTF {#1}
{ \msg_warning:nnn { hooks } { activate-disabled } {#1} }
% \end{macrocode}
% Otherwise we check if the hook is not declared, and if it isn't,
% figure out if it's reversed or not, then declare it accordingly.
% \begin{macrocode}
{
\@@_if_declared:nF {#1}
{
\tl_new:c { g_@@_#1_declared_tl }
\@@_make_usable:n {#1}
\tl_gset:cx { g_@@_#1_reversed_tl }
{ \@@_if_generic_reversed:nT {#1} { - } }
% \end{macrocode}
% Reflect that we have activated the generic hook and set its
% execution code.
% \changes{v1.0v}{2022/06/15}{Ensure that a newly activated generic hook
% gets its execution code set}
% \begin{macrocode}
\@@_update_hook_code:n {#1}
}
}
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macrocode}
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
%
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}
%<latexrelease> {\hook_activate_generic:n}{Providing~hooks}
%<latexrelease>
%<latexrelease>\cs_new_protected:Npn \hook_activate_generic:n #1 {}
%<latexrelease>
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
%
%
% \subsection{Parsing a label}
%
% \begin{macro}[EXP]{\@@_parse_label_default:n}
% This macro checks if a label was given (not \cs{c_novalue_tl}), and
% if so, tries to parse the label looking for a leading \verb|.| to
% replace by \cs{@@_currname_or_default:}.
% \begin{macrocode}
\cs_new:Npn \@@_parse_label_default:n #1
{
\tl_if_novalue:nTF {#1}
{ \@@_currname_or_default: }
{ \tl_trim_spaces_apply:nN {#1} \@@_parse_dot_label:n }
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_parse_dot_label:n}
% \begin{macro}[EXP]{
% \@@_parse_dot_label:w,
% \@@_parse_dot_label_cleanup:w,
% \@@_parse_dot_label_aux:w
% }
% Start by checking if the label is empty, which raises an error, and
% uses the fallback value. If not,
% split the label at a \verb|./|, if any, and check if no tokens are
% before the \verb|./|, or if the only character is a \verb|.|.
% If these requirements are fulfilled, the leading
% \verb|.| is replaced with \cs{@@_currname_or_default:}. Otherwise
% the label is returned unchanged.
% \begin{macrocode}
\cs_new:Npn \@@_parse_dot_label:n #1
{
\tl_if_empty:nTF {#1}
{
\msg_expandable_error:nn { hooks } { empty-label }
\@@_currname_or_default:
}
{
\str_if_eq:nnTF {#1} { . }
{ \@@_currname_or_default: }
{ \@@_parse_dot_label:w #1 ./ \s_@@_mark }
}
}
\cs_new:Npn \@@_parse_dot_label:w #1 ./ #2 \s_@@_mark
{
\tl_if_empty:nTF {#1}
{ \@@_parse_dot_label_aux:w #2 \s_@@_mark }
{
\tl_if_empty:nTF {#2}
{ \@@_make_name:n {#1} }
{ \@@_parse_dot_label_cleanup:w #1 ./ #2 \s_@@_mark }
}
}
\cs_new:Npn \@@_parse_dot_label_cleanup:w #1 ./ \s_@@_mark {#1}
\cs_new:Npn \@@_parse_dot_label_aux:w #1 ./ \s_@@_mark
{ \@@_currname_or_default: / \@@_make_name:n {#1} }
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_currname_or_default:}
% This uses \cs{g_@@_hook_curr_name_tl} if it is set, otherwise it tries
% \cs{@currname}. If neither is set, it raises an error and uses the
% fallback value \verb|label-missing|.
% \begin{macrocode}
\cs_new:Npn \@@_currname_or_default:
{
\tl_if_empty:NTF \g_@@_hook_curr_name_tl
{
\tl_if_empty:NTF \@currname
{
\msg_expandable_error:nnn { latex2e } { should-not-happen }
{ Empty~default~label. }
\@@_make_name:n { label-missing }
}
{ \@currname }
}
{ \g_@@_hook_curr_name_tl }
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_make_name:n,\@@_make_name:w}
% This provides a standard sanitization of a hook's name.
% It uses \cs{cs:w} to build a control sequence out of the hook name,
% then uses \cs{cs_to_str:N} to get the string representation of that,
% without the escape character. \cs{cs:w}-based expansion is used
% instead of |e|-based because Unicode characters don't behave well
% inside \cs{expanded}. The macro adds the \cs[no-index]{@@\textvisiblespace} prefix to the
% hook name to reuse the hook's code token list to build the csname
% and avoid leaving \enquote{public} control sequences defined
% (as~\cs[no-index]{relax}) in TeX's memory.
% \begin{macrocode}
\cs_new:Npn \@@_make_name:n #1
{
\exp_after:wN \exp_after:wN \exp_after:wN \@@_make_name:w
\exp_after:wN \token_to_str:N \cs:w @@~ #1 \cs_end:
}
\exp_last_unbraced:NNNNo
\cs_new:Npn \@@_make_name:w #1 \tl_to_str:n { @@~ } { }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_normalize_hook_args:Nn}
% \begin{macro}{\@@_normalize_hook_args:Nnn}
% \begin{macro}{\@@_normalize_hook_rule_args:Nnnnn}
% \begin{macro}{\@@_normalize_hook_args_aux:Nn}
% This is the standard route for normalizing hook and label arguments. The main
% macro does the entire operation within a group so that csnames made
% by \cs{@@_make_name:n} are wiped off before continuing. This means
% that this function cannot be used for \cs{hook_use:n}!
% \begin{macrocode}
\cs_new_protected:Npn \@@_normalize_hook_args_aux:Nn #1 #2
{
\group_begin:
\use:e
{
\group_end:
\exp_not:N #1 #2
}
}
\cs_new_protected:Npn \@@_normalize_hook_args:Nn #1 #2
{
\@@_normalize_hook_args_aux:Nn #1
{ { \@@_parse_label_default:n {#2} } }
}
\cs_new_protected:Npn \@@_normalize_hook_args:Nnn #1 #2 #3
{
\@@_normalize_hook_args_aux:Nn #1
{
{ \@@_parse_label_default:n {#2} }
{ \@@_parse_label_default:n {#3} }
}
}
\cs_new_protected:Npn \@@_normalize_hook_rule_args:Nnnnn #1 #2 #3 #4 #5
{
\@@_normalize_hook_args_aux:Nn #1
{
{ \@@_parse_label_default:n {#2} }
{ \@@_parse_label_default:n {#3} }
{ \tl_trim_spaces:n {#4} }
{ \@@_parse_label_default:n {#5} }
}
}
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_curr_name_push:n,\@@_curr_name_push_aux:n}
% \begin{macro}{\@@_curr_name_pop:}
% \begin{macro}{\@@_end_document_label_check:}
% The token list \cs{g_@@_hook_curr_name_tl} stores the name of the
% current package/file to be used as the default label in hooks.
% Providing a consistent interface is tricky because packages can
% be loaded within packages, and some packages may not use
% \cs{SetDefaultHookLabel} to change the default label (in which
% case \cs{@currname} is used).
%
% To pull that one off, we keep a stack that contains the default
% label for each level of input. The bottom of the stack contains the
% default label for the |top-level| (this stack should never go
% empty). If we're building the format, set the default label to be
% |top-level|:
% \begin{macrocode}
\tl_gset:Nn \g_@@_hook_curr_name_tl { top-level }
% \end{macrocode}
%
% Then, in case we're in \pkg{latexrelease} we push something on
% the stack to support roll forward. But in some rare cases,
% \pkg{latexrelease} may be loaded inside another package (notably
% \pkg{platexrelease}), so we'll first push the |top-level| entry:
% \changes{v1.0i}{2021/03/18}
% {Only add \texttt{top-level} if not already there.}
% \begin{macrocode}
%<latexrelease>\seq_if_empty:NT \g_@@_name_stack_seq
%<latexrelease> { \seq_gput_right:Nn \g_@@_name_stack_seq { top-level } }
% \end{macrocode}
% then we dissect the \cs{@currnamestack}, adding \cs{@currname} to
% the stack:
% \changes{v1.0f}{2020/11/24}{Support for roll forward (gh/434)}
% \begin{macrocode}
%<latexrelease>\cs_set_protected:Npn \@@_tmp:w #1 #2 #3
%<latexrelease> {
%<latexrelease> \quark_if_recursion_tail_stop:n {#1}
%<latexrelease> \seq_gput_right:Nn \g_@@_name_stack_seq {#1}
%<latexrelease> \@@_tmp:w
%<latexrelease> }
%<latexrelease>\exp_after:wN \@@_tmp:w \@currnamestack
%<latexrelease> \q_recursion_tail \q_recursion_tail
%<latexrelease> \q_recursion_tail \q_recursion_stop
% \end{macrocode}
% and finally set the default label to be the \cs{@currname}:
% \changes{v1.0i}{2021/03/18}
% {Remove the (empty) \enquote{top-level} from \cs{@currnamestack}.}
% \begin{macrocode}
%<latexrelease>\tl_gset:Nx \g_@@_hook_curr_name_tl { \@currname }
%<latexrelease>\seq_gpop_right:NN \g_@@_name_stack_seq \l_@@_tmpa_tl
% \end{macrocode}
%
% Two commands keep track of the stack: when a file is input,
% \cs{@@_curr_name_push:n} pushes the current default label onto the
% stack and sets the new default label (all in one go):
% \begin{macrocode}
\cs_new_protected:Npn \@@_curr_name_push:n #1
{ \exp_args:Nx \@@_curr_name_push_aux:n { \@@_make_name:n {#1} } }
\cs_new_protected:Npn \@@_curr_name_push_aux:n #1
{
\tl_if_blank:nTF {#1}
{ \msg_error:nn { hooks } { no-default-label } }
{
\str_if_eq:nnTF {#1} { top-level }
{
\msg_error:nnnnn { hooks } { set-top-level }
{ to } { PushDefaultHookLabel } {#1}
}
{
\seq_gpush:NV \g_@@_name_stack_seq \g_@@_hook_curr_name_tl
\tl_gset:Nn \g_@@_hook_curr_name_tl {#1}
}
}
}
% \end{macrocode}
% and when an input is over, the topmost item of the stack is popped,
% since that label will not be used again, and \cs{g_@@_hook_curr_name_tl}
% is updated to equal the now topmost item of the stack:
% \begin{macrocode}
\cs_new_protected:Npn \@@_curr_name_pop:
{
\seq_gpop:NNTF \g_@@_name_stack_seq \l_@@_return_tl
{ \tl_gset_eq:NN \g_@@_hook_curr_name_tl \l_@@_return_tl }
{ \msg_error:nn { hooks } { extra-pop-label } }
}
% \end{macrocode}
%
% At the end of the document we want to check if there was no
% \cs{@@_curr_name_push:n} without a matching \cs{@@_curr_name_pop:}
% (not a critical error, but it might indicate that something else is
% not quite right):
% \begin{macrocode}
\tl_gput_right:Nn \@kernel@after@enddocument@afterlastpage
{ \@@_end_document_label_check: }
\cs_new_protected:Npn \@@_end_document_label_check:
{
\seq_gpop:NNT \g_@@_name_stack_seq \l_@@_return_tl
{
\msg_error:nnx { hooks } { missing-pop-label }
{ \g_@@_hook_curr_name_tl }
\tl_gset_eq:NN \g_@@_hook_curr_name_tl \l_@@_return_tl
\@@_end_document_label_check:
}
}
% \end{macrocode}
%
% The token list \cs{g_@@_hook_curr_name_tl} is but a mirror of the
% top of the stack.
%
% \begin{macro}{\@@_set_default_hook_label:n,\@@_set_default_label:n}
% Now define a wrapper that replaces the top of the stack with the
% argument, and updates \cs{g_@@_hook_curr_name_tl} accordingly.
% \begin{macrocode}
\cs_new_protected:Npn \@@_set_default_hook_label:n #1
{
\seq_if_empty:NTF \g_@@_name_stack_seq
{
\msg_error:nnnnn { hooks } { set-top-level }
{ for } { SetDefaultHookLabel } {#1}
}
{ \exp_args:Nx \@@_set_default_label:n { \@@_make_name:n {#1} } }
}
\cs_new_protected:Npn \@@_set_default_label:n #1
{
\str_if_eq:nnTF {#1} { top-level }
{
\msg_error:nnnnn { hooks } { set-top-level }
{ to } { SetDefaultHookLabel } {#1}
}
{ \tl_gset:Nn \g_@@_hook_curr_name_tl {#1} }
}
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \subsection{Adding or removing hook code}
%
% \begin{macro}{\hook_gput_code:nnn}
% \begin{macro}{\@@_gput_code:nnn,\@@_hook_gput_code_do:nnn}
%
% With \cs{hook_gput_code:nnn}\Arg{hook}\Arg{label}\Arg{code} a
% chunk of \meta{code} is added to an existing \meta{hook} labeled
% with \meta{label}.
% \changes{v1.0o}{2021/07/22}{Do not queue removals (gh/625)}
% \begin{macrocode}
\cs_new_protected:Npn \hook_gput_code:nnn #1 #2
{ \@@_normalize_hook_args:Nnn \@@_gput_code:nnn {#1} {#2} }
% \end{macrocode}
%
% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_code:nnn #1 #2 #3
{
% \end{macrocode}
% First check if the code should be executed immediately, rather than
% stored:
% \changes{v1.0r}{2021/09/06}{Use dedicated conditional (gh/606)}
% \begin{macrocode}
\@@_if_execute_immediately:nTF {#1}
{#3}
{
% \end{macrocode}
% Then check if the hook is usable.
% \begin{macrocode}
\@@_if_usable:nTF {#1}
% \end{macrocode}
% If so we simply add (or append) the new code to the property list
% holding different chunks for the hook. At \verb=\begin{document}=
% this is then sorted into a token list for fast execution.
% \begin{macrocode}
{
\@@_hook_gput_code_do:nnn {#1} {#2} {#3}
% \end{macrocode}
% However, if there is an update within the document we need to alter
% this execution code which is done by
% \cs{@@_update_hook_code:n}. In the preamble this does nothing.
% \begin{macrocode}
\@@_update_hook_code:n {#1}
}
% \end{macrocode}
%
% If the hook is not usable, before giving up, check if it's not
% disabled and otherwise try to declare it as a generic hook, if its
% name matches one of the valid patterns.
% \begin{macrocode}
{
\@@_if_disabled:nTF {#1}
{ \msg_error:nnn { hooks } { hook-disabled } {#1} }
{ \@@_try_declaring_generic_hook:nnn {#1} {#2} {#3} }
}
}
}
% \end{macrocode}
%
% This macro will unconditionally add a chunk of code to the given hook.
% \begin{macrocode}
\cs_new_protected:Npn \@@_hook_gput_code_do:nnn #1 #2 #3
{
% \end{macrocode}
% However, first some debugging info if debugging is enabled:
% \begin{macrocode}
\@@_debug:n{\iow_term:x{****~ Add~ to~
\@@_if_usable:nF {#1} { undeclared~ }
hook~ #1~ (#2)
\on@line\space <-~ \tl_to_str:n{#3}} }
% \end{macrocode}
% Then try to get the code chunk labeled \verb=#2= from the hook.
% If there's code already there, then append \verb=#3= to that,
% otherwise just put \verb=#3=. If the current label is |top-level|,
% the code is added to a dedicated token list
% \cs{@@_toplevel\textvisiblespace\meta{hook}} that goes at the end of the
% hook (or at the beginning, for a reversed hook), just before
% \cs[no-index]{@@_next\textvisiblespace\meta{hook}}.
% \begin{macrocode}
\str_if_eq:nnTF {#2} { top-level }
{
\str_if_eq:eeTF { top-level } { \@@_currname_or_default: }
{
% \end{macrocode}
% If the hook's basic structure does not exist, we need to declare it
% with \cs{@@_init_structure:n}.
% \begin{macrocode}
\@@_init_structure:n {#1}
\@@_tl_gput_right:cn { @@_toplevel~#1 } {#3}
}
{ \msg_error:nnn { hooks } { misused-top-level } {#1} }
}
{
\prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl
{
\prop_gput:cno { g_@@_#1_code_prop } {#2}
{ \l_@@_return_tl #3 }
}
{ \prop_gput:cnn { g_@@_#1_code_prop } {#2} {#3} }
}
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_gput_undeclared_hook:nnn}
% Often it may happen that a package $A$ defines a hook \verb=foo=,
% but package $B$, that adds code to that hook, is loaded before $A$.
% In such case we need to add code to the hook before its declared.
% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_undeclared_hook:nnn #1 #2 #3
{
\@@_init_structure:n {#1}
\@@_hook_gput_code_do:nnn {#1} {#2} {#3}
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_try_declaring_generic_hook:nnn,
% \@@_try_declaring_generic_next_hook:nn}
%
% These entry-level macros just pass the arguments along to the
% common \cs{@@_try_declaring_generic_hook:nNNnn} with the right
% functions to execute when some action is to be taken.
%
% The wrapper \cs{@@_try_declaring_generic_hook:nnn} then defers
% \cs{hook_gput_code:nnn} if the generic hook was declared, or to
% \cs{@@_gput_undeclared_hook:nnn} otherwise (the hook was tested for
% existence before, so at this point if it isn't generic, it doesn't
% exist).
%
% The wrapper \cs{@@_try_declaring_generic_next_hook:nn} for
% next-execution hooks does the same: it defers the code to
% \cs{hook_gput_next_code:nn} if the generic hook was declared, or
% to \cs{@@_gput_next_do:nn} otherwise.
% \changes{v1.0p}{2021/08/25}{Standardise generic hook names (gh/648)}
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_try_declaring_generic_hook:nnn}
%<latexrelease> {Standardise~generic~hook~names}
\cs_new_protected:Npn \@@_try_declaring_generic_hook:nnn #1
{
\@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop: {#1}
\hook_gput_code:nnn
\@@_gput_undeclared_hook:nnn
{#1}
}
\cs_new_protected:Npn \@@_try_declaring_generic_next_hook:nn #1
{
\@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop: {#1}
\hook_gput_next_code:nn
\@@_gput_next_do:nn
{#1}
}
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_try_declaring_generic_hook:nnn}
%<latexrelease> {Standardise~generic~hook~names}
%<latexrelease>\cs_new_protected:Npn \@@_try_declaring_generic_hook:nnn #1
%<latexrelease> {
%<latexrelease> \@@_try_declaring_generic_hook:nNNnn {#1}
%<latexrelease> \hook_gput_code:nnn \@@_gput_undeclared_hook:nnn
%<latexrelease> }
%<latexrelease>\cs_new_protected:Npn \@@_try_declaring_generic_next_hook:nn #1
%<latexrelease> {
%<latexrelease> \@@_try_declaring_generic_hook:nNNnn {#1}
%<latexrelease> \hook_gput_next_code:nn \@@_gput_next_do:nn
%<latexrelease> }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_try_declaring_generic_hook:nNNnn,
% \@@_try_declaring_generic_hook_split:nNNnn}
%
% \cs{@@_try_declaring_generic_hook:nNNnn}
% now splits the hook name
% at the first \texttt{/} (if any) and first checks if it is a
% file-specific hook (they require some normalization) using
% \cs{@@_if_file_hook:wTF}. If not then check it is one of a
% predefined set for generic names. We also split off the second
% component to see if we have to make a reversed hook. In either case
% the function returns \meta{true} for a generic hook and \meta{false}
% in other cases.
%
% \changes{v1.0s}{2021/09/28}
% {Correct usage of older \cs{@@_if_file_hook:wTF} (gh/675)}
% \begin{macrocode}
%<latexrelease>\cs_new_protected:Npn \@@_try_declaring_generic_hook:nNNnn #1
%<latexrelease> {
%<latexrelease> \@@_if_file_hook:wTF #1 / \s_@@_mark
%<latexrelease> {
%<latexrelease> \exp_args:Ne \@@_try_declaring_generic_hook_split:nNNnn
%<latexrelease> { \exp_args:Ne \@@_file_hook_normalize:n {#1} }
%<latexrelease> }
%<latexrelease> { \@@_try_declaring_generic_hook_split:nNNnn {#1} }
%<latexrelease> }
% \end{macrocode}
%
% \begin{macrocode}
%<latexrelease>\cs_new_protected:Npn \@@_try_declaring_generic_hook_split:nNNnn #1 #2 #3
%<latexrelease> {
%<latexrelease> \@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop: {#1}
%<latexrelease> { #2 }
%<latexrelease> { #3 } {#1}
%<latexrelease> }
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[TF]{\@@_try_declaring_generic_hook:wn}
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_try_declaring_generic_hook:wn}%
%<latexrelease> {Standardise~generic~hook~names}
\prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn
#1 / #2 / #3 / #4 \scan_stop: #5 { TF }
{
\@@_if_generic:nTF {#5}
{
\@@_if_usable:nF {#5}
{
% \end{macrocode}
% If the hook doesn't exist yet we check if it is a \texttt{cmd}
% hook and if so we attempt patching the command in addition to
% declaring the hook.
%
% For some commands this will not be possible, in which case
% \cs{@@_patch_cmd_or_delay:Nnn} (defined in \texttt{ltcmdhooks})
% will generate an appropriate error message.
% \begin{macrocode}
\str_if_eq:nnT {#1} { cmd }
{ \@@_try_put_cmd_hook:n {#5} }
% \end{macrocode}
%
% Declare the hook always even if it can't really be used (error
% message generated elsewhere).
%
% Here we use \cs{@@_make_usable:n}, so that a \cs{hook_new:n} is still
% possible later.
% \begin{macrocode}
\@@_make_usable:n {#5}
}
\@@_if_generic_reversed:nT {#5}
{ \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
\prg_return_true:
}
{
% \end{macrocode}
%
% Generic hooks are all named \meta{type}/\meta{name}/\meta{place},
% where \meta{type} and \meta{place} are predefined
% (\cs{c_@@_generic_\meta{type}/./\meta{place}_tl}), and \meta{name}
% is the variable component. Older releases had some hooks with the
% \meta{name} in the third part, so the code below supports that
% syntax for a while, with a warning.
%
% The \cs{exp_after:wN} |...| \cs{exp:w} trick is there to remove the
% conditional structure inserted by
% \cs{@@_try_declaring_generic_hook:wnTF} and thus allow access to the
% tokens that follow it, as is needed to keep things going.
%
% When the deprecation cycle ends, the lines below should all be
% replaced by \cs{prg_return_false:}.
% \begin{macrocode}
\@@_if_deprecated_generic:nTF {#5}
{
\@@_deprecated_generic_warn:n {#5}
\exp_after:wN \@@_declare_deprecated_generic:NNn
\exp:w % \exp_end:
}
{ \prg_return_false: }
}
}
% \end{macrocode}
%
% \begin{macro}{
% \@@_deprecated_generic_warn:Nn,
% \@@_deprecated_generic_warn:Nw,
% }
% \cs{@@_deprecated_generic_warn:n} will issue a deprecation warning
% for a given hook, and mark that hook such that the warning will not
% be issued again (multiple warnings can be issued, but only once per
% hook).
% \begin{macrocode}
\cs_new_protected:Npn \@@_deprecated_generic_warn:n #1
{ \@@_deprecated_generic_warn:w #1 \s_@@_mark }
\cs_new_protected:Npn \@@_deprecated_generic_warn:w
#1 / #2 / #3 \s_@@_mark
{
\if_cs_exist:w @@~#1/#2/#3 \cs_end: \else:
\msg_warning:nnnnn { hooks } { generic-deprecated } {#1} {#2} {#3}
\fi:
\cs_gset_eq:cN { @@~#1/#2/#3 } \scan_stop:
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{
% \@@_do_deprecated_generic:Nn,
% \@@_do_deprecated_generic:Nw,
% \@@_declare_deprecated_generic:NNw,
% \@@_declare_deprecated_generic:NNw,
% }
% Now that the user has been told about the deprecation, we proceed by
% swapping \meta{name} and \meta{place} and adding the code to the
% correct hook.
% \begin{macrocode}
\cs_new_protected:Npn \@@_do_deprecated_generic:Nn #1 #2
{ \@@_do_deprecated_generic:Nw #1 #2 \s_@@_mark }
\cs_new_protected:Npn \@@_do_deprecated_generic:Nw #1
#2 / #3 / #4 \s_@@_mark
{ #1 { #2 / #4 / #3 } }
\cs_new_protected:Npn \@@_declare_deprecated_generic:NNn #1 #2 #3
{ \@@_declare_deprecated_generic:NNw #1 #2 #3 \s_@@_mark }
\cs_new_protected:Npn \@@_declare_deprecated_generic:NNw #1 #2
#3 / #4 / #5 \s_@@_mark
{
\@@_try_declaring_generic_hook:wnTF #3 / #5 / #4 / \scan_stop:
{ #3 / #5 / #4 }
#1 #2 { #3 / #5 / #4 }
}
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/06/01}{\@@_try_declaring_generic_hook:wn}
%<latexrelease> {Support~cmd~hooks}
%<latexrelease>\prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn
%<latexrelease> #1 / #2 / #3 / #4 \scan_stop: #5 { TF }
%<latexrelease> {
%<latexrelease> \tl_if_empty:nTF {#2}
%<latexrelease> { \prg_return_false: }
%<latexrelease> {
%<latexrelease> \prop_if_in:NnTF \c_@@_generics_prop {#1}
%<latexrelease> {
%<latexrelease> \@@_if_usable:nF {#5}
%<latexrelease> {
%<latexrelease> \str_if_eq:nnT {#1} { cmd }
%<latexrelease> { \@@_try_put_cmd_hook:n {#5} }
%<latexrelease> \@@_make_usable:n {#5}
%<latexrelease> }
%<latexrelease> \prop_if_in:NnTF \c_@@_generics_reversed_ii_prop {#2}
%<latexrelease> { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
%<latexrelease> {
%<latexrelease> \prop_if_in:NnT \c_@@_generics_reversed_iii_prop {#3}
%<latexrelease> { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
%<latexrelease> }
%<latexrelease> \prg_return_true:
%<latexrelease> }
%<latexrelease> { \prg_return_false: }
%<latexrelease> }
%<latexrelease> }
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
%
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_try_declaring_generic_hook:wn}%
%<latexrelease> {Support~cmd~hooks}
%<latexrelease>\prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn
%<latexrelease> #1 / #2 / #3 / #4 \scan_stop: #5 { TF }
%<latexrelease> {
%<latexrelease> \tl_if_empty:nTF {#2}
%<latexrelease> { \prg_return_false: }
%<latexrelease> {
%<latexrelease> \prop_if_in:NnTF \c_@@_generics_prop {#1}
%<latexrelease> {
%<latexrelease> \@@_if_declared:nF {#5} { \hook_new:n {#5} }
%<latexrelease> \prop_if_in:NnTF \c_@@_generics_reversed_ii_prop {#2}
%<latexrelease> { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
%<latexrelease> {
%<latexrelease> \prop_if_in:NnT \c_@@_generics_reversed_iii_prop {#3}
%<latexrelease> { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
%<latexrelease> }
%<latexrelease> \prg_return_true:
%<latexrelease> }
%<latexrelease> { \prg_return_false: }
%<latexrelease> }
%<latexrelease> }
%<latexrelease>
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[pTF]{\@@_if_file_hook:w}
% \cs{@@_if_file_hook:wTF} checks if the argument is a valid
% file-specific hook (not, for example, |file/before|, but
% |file/foo.tex/before|). If it is a file-specific hook, then it
% executes the \meta{true} branch, otherwise \meta{false}.
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_if_file_hook:w}%
%<latexrelease> {Standardise~generic~hook~names}
\prg_new_conditional:Npnn \@@_if_file_hook:w
#1 / #2 \s_@@_mark #3 { TF }
{
\@@_if_generic:nTF {#3}
{
\str_if_eq:nnTF {#1} { file }
{ \prg_return_true: }
{ \prg_return_false: }
}
{ \prg_return_false: }
}
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
%
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_if_file_hook:w}%
%<latexrelease> {Standardise~generic~hook~names}
%<latexrelease>\prg_new_conditional:Npnn \@@_if_file_hook:w
%<latexrelease> #1 / #2 / #3 \s_@@_mark { TF }
%<latexrelease> {
%<latexrelease> \str_if_eq:nnTF {#1} { file }
%<latexrelease> {
%<latexrelease> \bool_lazy_or:nnTF
%<latexrelease> { \tl_if_empty_p:n {#3} }
%<latexrelease> { \str_if_eq_p:nn {#3} { / } }
%<latexrelease> { \prg_return_false: }
%<latexrelease> {
%<latexrelease> \prop_if_in:NnTF \c_@@_generics_file_prop {#2}
%<latexrelease> { \prg_return_true: }
%<latexrelease> { \prg_return_false: }
%<latexrelease> }
%<latexrelease> }
%<latexrelease> { \prg_return_false: }
%<latexrelease> }
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_file_hook_normalize:n}
% \begin{macro}[EXP]{\@@_strip_double_slash:n,\@@_strip_double_slash:w}
%
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_file_hook_normalize:n}%
%<latexrelease> {Standardise~generic~hook~names}
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
%
% When a file-specific hook is found, before being declared it is
% lightly normalized by \cs{@@_file_hook_normalize:n}. The current
% implementation just replaces two consecutive slashes (|//|) by a
% single one, to cope with simple cases where the user did something
% like \verb|\def\input@path{{./mypath/}}|, in which case a hook would
% have to be \verb|\AddToHook{file/./mypath//file.tex/after}|.
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_file_hook_normalize:n}%
%<latexrelease> {Standardise~generic~hook~names}
%<latexrelease>\cs_new:Npn \@@_file_hook_normalize:n #1
%<latexrelease> { \@@_strip_double_slash:n {#1} }
%<latexrelease>\cs_new:Npn \@@_strip_double_slash:n #1
%<latexrelease> { \@@_strip_double_slash:w #1 // \s_@@_mark }
% \end{macrocode}
% This function is always called after testing if the argument is a
% file hook with \cs{@@_if_file_hook:wTF}, so we can assume it has
% three parts (it is either \verb|file/.../before| or
% \verb|file/.../after|), so we use \verb|#1/#2/#3 //| instead of just
% \verb|#1 //| to prevent losing a slash if the file name is empty.
% \changes{v1.0h}{2021/01/07}{Assume hook name has at least three
% nonempty parts (gh/464)}
% \begin{macrocode}
%<latexrelease>\cs_new:Npn \@@_strip_double_slash:w #1/#2/#3 // #4 \s_@@_mark
%<latexrelease> {
%<latexrelease> \tl_if_empty:nTF {#4}
%<latexrelease> { #1/#2/#3 }
%<latexrelease> { \@@_strip_double_slash:w #1/#2/#3 / #4 \s_@@_mark }
%<latexrelease> }
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{
% \c_@@_generic_cmd/./before_tl,\c_@@_generic_cmd/./after_tl,
% \c_@@_generic_env/./before_tl,\c_@@_generic_env/./after_tl,
% \c_@@_generic_file/./before_tl,\c_@@_generic_file/./after_tl,
% \c_@@_generic_package/./before_tl,\c_@@_generic_package/./after_tl,
% \c_@@_generic_class/./before_tl,\c_@@_generic_class/./after_tl,
% \c_@@_generic_include/./before_tl,\c_@@_generic_include/./after_tl,
% \c_@@_generic_env/./begin_tl,\c_@@_generic_env/./end_tl,
% \c_@@_generic_include/./end_tl
% }
% Token lists defining the possible generic hooks. We don't provide
% any user interface to this as this is meant to be static.
% \begin{description}
% \item[\texttt{cmd}]
% The generic hooks used for commands.
% \item[\texttt{env}]
% The generic hooks used in \cs{begin} and \cs{end}.
% \item[\texttt{file}, \texttt{package}, \texttt{class}, \texttt{include}]
% The generic hooks used when loading a file
% \end{description}
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}{\c_@@_generics_prop}%
%<latexrelease> {Standardise~generic~hook~names}
\clist_map_inline:nn { cmd , env , file , package , class , include }
{
\tl_const:cn { c_@@_generic_#1/./before_tl } { + }
\tl_const:cn { c_@@_generic_#1/./after_tl } { - }
}
\tl_const:cn { c_@@_generic_env/./begin_tl } { + }
\tl_const:cn { c_@@_generic_env/./end_tl } { + }
% \end{macrocode}
%
% \changes{v1.0t}{2022/04/01}{Support generic \texttt{include/.../excluded} hooks}
% \begin{macrocode}
\tl_const:cn { c_@@_generic_include/./end_tl } { - }
\tl_const:cn { c_@@_generic_include/./excluded_tl } { + }
% \end{macrocode}
%
% Deprecated generic hooks:
% \begin{macrocode}
\clist_map_inline:nn { file , package , class , include }
{
\tl_const:cn { c_@@_deprecated_#1/./before_tl } { }
\tl_const:cn { c_@@_deprecated_#1/./after_tl } { }
}
\tl_const:cn { c_@@_deprecated_include/./end_tl } { }
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
%
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\c_@@_generics_prop}%
%<latexrelease> {Standardise~generic~hook~names}
%<latexrelease>\prop_const_from_keyval:Nn \c_@@_generics_prop
%<latexrelease> {cmd=,env=,file=,package=,class=,include=}
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\c_@@_generics_reversed_ii_prop,
% \c_@@_generics_reversed_iii_prop,
% \c_@@_generics_file_prop}
% The following generic hooks are supposed to use reverse ordering
% (the |ii| and |iii| names are kept for the deprecation cycle):
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}{\c_@@_generics_reversed_ii_prop}%
%<latexrelease> {Standardise~generic~hook~names}
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
%
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\c_@@_generics_reversed_ii_prop}%
%<latexrelease> {Standardise~generic~hook~names}
%<latexrelease>\prop_const_from_keyval:Nn \c_@@_generics_reversed_ii_prop {after=,end=}
%<latexrelease>\prop_const_from_keyval:Nn \c_@@_generics_reversed_iii_prop {after=}
%<latexrelease>\prop_const_from_keyval:Nn \c_@@_generics_file_prop {before=,after=}
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\hook_gremove_code:nn}
% \begin{macro}{\@@_gremove_code:nn}
%
% With \cs{hook_gremove_code:nn}\Arg{hook}\Arg{label} any code
% for \meta{hook} stored under \meta{label} is removed.
% \changes{v1.0o}{2021/07/22}{Do not queue removals (gh/625)}
% \begin{macrocode}
\cs_new_protected:Npn \hook_gremove_code:nn #1 #2
{ \@@_normalize_hook_args:Nnn \@@_gremove_code:nn {#1} {#2} }
\cs_new_protected:Npn \@@_gremove_code:nn #1 #2
{
% \end{macrocode}
% First check that the hook code pool exists. \cs{@@_if_usable:nTF}
% isn't used here because it should be possible to remove code from a
% hook before its defined (see section~\ref{sec:querying}).
% \begin{macrocode}
\@@_if_structure_exist:nTF {#1}
{
% \end{macrocode}
% Then remove the chunk and run \cs{@@_update_hook_code:n} so
% that the execution token list reflects the change if we are after
% \verb=\begin{document}=.
%
% If all code is to be removed, clear the code pool
% \cs{g_@@_\meta{hook}_code_prop}, the top-level code
% \cs{@@_toplevel\textvisiblespace\meta{hook}}, and the next-execution code
% \cs{@@_next\textvisiblespace\meta{hook}}.
% \begin{macrocode}
\str_if_eq:nnTF {#2} {*}
{
\prop_gclear:c { g_@@_#1_code_prop }
\@@_tl_gclear:c { @@_toplevel~#1 }
\@@_tl_gclear:c { @@_next~#1 }
}
{
% \end{macrocode}
% If the label is |top-level| then clear the token list, as all code
% there is under the same label.
% \begin{macrocode}
\str_if_eq:nnTF {#2} { top-level }
{ \@@_tl_gclear:c { @@_toplevel~#1 } }
{
\prop_gpop:cnNF { g_@@_#1_code_prop } {#2} \l_@@_return_tl
{ \msg_warning:nnnn { hooks } { cannot-remove } {#1} {#2} }
}
}
% \end{macrocode}
% Finally update the code, if the hook exists.
% \begin{macrocode}
\@@_if_usable:nT {#1}
{ \@@_update_hook_code:n {#1} }
}
% \end{macrocode}
%
% If the code pool for this hook doesn't exist, show a warning:
% \begin{macrocode}
{
\@@_if_deprecated_generic:nTF {#1}
{
\@@_deprecated_generic_warn:n {#1}
\@@_do_deprecated_generic:Nn \@@_gremove_code:nn {#1} {#2}
}
{ \msg_warning:nnnn { hooks } { cannot-remove } {#1} {#2} }
}
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{
% \g_@@_??_code_prop,
% \@@~??,
% \g_@@_??_reversed_tl,
% }
%
% Initially these variables simply used an empty ``label'' name (not
% two question marks). This was a bit unfortunate, because then
% \texttt{l3doc} complains about \verb=__= in the middle of a
% command name when trying to typeset the documentation. However
% using a ``normal'' name such as \texttt{default} has the
% disadvantage of that being not really distinguishable from a real
% hook name. I now have settled for \texttt{??} which needs some
% gymnastics to get it into the csname, but since this is used a
% lot, the code should be fast, so this is not done with \texttt{c}
% expansion in the code later on.
%
% \cs{@@\textvisiblespace??} isn't used, but it has to be defined to trick
% the code into thinking that \texttt{??} is actually a hook.
% \begin{macrocode}
\prop_new:c {g_@@_??_code_prop}
\prop_new:c {@@~??}
% \end{macrocode}
%
% Default rules are always given in normal ordering (never in
% reversed ordering). If such a rule is applied to a reversed
% hook it behaves as if the rule is reversed (e.g.,
% \texttt{after} becomes \texttt{before})
% because those rules are applied first and then the order is reversed.
% \begin{macrocode}
\tl_new:c {g_@@_??_reversed_tl}
% \end{macrocode}
% \end{macro}
%
% \subsection{Setting rules for hooks code}
%
% \begin{macro}{\hook_gset_rule:nnnn}
% \begin{macro}{\@@_gset_rule:nnnn}
% With
% \cs{hook_gset_rule:nnnn}\Arg{hook}\Arg{label1}\Arg{relation}\Arg{label2}
% a relation is defined between the two code labels for the given
% \meta{hook}. The special hook \texttt{??} stands for \emph{any}
% hook, which sets a default rule (to be used if no other relation
% between the two hooks exist).
% \begin{macrocode}
\cs_new_protected:Npn \hook_gset_rule:nnnn #1#2#3#4
{
\@@_normalize_hook_rule_args:Nnnnn \@@_gset_rule:nnnn
{#1} {#2} {#3} {#4}
}
% \end{macrocode}
%
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2022/06/01}{\@@_gset_rule:nnnn}
%<latexrelease> {Refuse~setting~rule~for~one-time~hooks}
% \end{macrocode}
%
% \begin{macrocode}
\cs_new_protected:Npn \@@_gset_rule:nnnn #1#2#3#4
{
\@@_if_deprecated_generic:nT {#1}
{
\@@_deprecated_generic_warn:n {#1}
\@@_do_deprecated_generic:Nn \@@_gset_rule:nnnn {#1}
{#2} {#3} {#4}
\@@_clean_to_scan:w
}
\@@_if_execute_immediately:nT {#1}
{
\msg_error:nnnnnn { hooks } { rule-too-late }
{#1} {#2} {#3} {#4}
\@@_clean_to_scan:w
}
% \end{macrocode}
% First we ensure the basic data structure of the hook exists:
% \begin{macrocode}
\@@_init_structure:n {#1}
% \end{macrocode}
% Then we clear any previous relationship between both labels.
% \begin{macrocode}
\@@_rule_gclear:nnn {#1} {#2} {#4}
% \end{macrocode}
% Then we call the function to handle the given rule. Throw an error if the
% rule is invalid.
% \begin{macrocode}
\cs_if_exist_use:cTF { @@_rule_#3_gset:nnn }
{
{#1} {#2} {#4}
\@@_update_hook_code:n {#1}
}
{
\msg_error:nnnnnn { hooks } { unknown-rule }
{#1} {#2} {#3} {#4}
}
\s_@@_mark
}
% \end{macrocode}
%
% \begin{macrocode}
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_gset_rule:nnnn}
%<latexrelease> {Refuse~setting~rule~for~one-time~hooks}
%<latexrelease>\cs_new_protected:Npn \@@_gset_rule:nnnn #1#2#3#4
%<latexrelease> {
%<latexrelease> \@@_if_deprecated_generic:nT {#1}
%<latexrelease> {
%<latexrelease> \@@_deprecated_generic_warn:n {#1}
%<latexrelease> \@@_do_deprecated_generic:Nn \@@_gset_rule:nnnn {#1}
%<latexrelease> {#2} {#3} {#4}
%<latexrelease> \exp_after:wN \use_none:nnnnnnnnn \use_none:n
%<latexrelease> }
%<latexrelease> \@@_init_structure:n {#1}
%<latexrelease> \@@_rule_gclear:nnn {#1} {#2} {#4}
%<latexrelease> \cs_if_exist_use:cTF { @@_rule_#3_gset:nnn }
%<latexrelease> {
%<latexrelease> {#1} {#2} {#4}
%<latexrelease> \@@_update_hook_code:n {#1}
%<latexrelease> }
%<latexrelease> {
%<latexrelease> \msg_error:nnnnnn { hooks } { unknown-rule }
%<latexrelease> {#1} {#2} {#3} {#4}
%<latexrelease> }
%<latexrelease> }
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_rule_before_gset:nnn, \@@_rule_after_gset:nnn,
% \@@_rule_<_gset:nnn, \@@_rule_>_gset:nnn}
% Then we add the new rule. We need to normalize the rules here to
% allow for faster processing later. Given a pair of labels
% $l_A$ and $l_B$, the rule $l_A>l_B$ is the same as $l_B<l_A$
% only presented differently. But by normalizing the
% forms of the rule to a single representation, say, $l_B<l_A$, reduces
% the time spent looking for the rules later considerably.
%
% Here we do that normalization by using \cs[no-index]{(pdf)strcmp} to
% lexically sort labels $l_A$ and $l_B$ to a fixed order. This order
% is then enforced every time these two labels are used together.
%
% Here we use \cs{@@_label_pair:nn}~\Arg{hook}~\Arg{l_A}~\Arg{l_B}
% to build a string \texttt{$l_B$\string|$l_A$} with a fixed order, and
% use \cs{@@_label_ordered:nnTF} to apply the correct rule to the pair
% of labels, depending if it was sorted or not.
% \begin{macrocode}
\cs_new_protected:Npn \@@_rule_before_gset:nnn #1#2#3
{
\@@_tl_gset:cx { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl }
{ \@@_label_ordered:nnTF {#2} {#3} { < } { > } }
}
\cs_new_eq:cN { @@_rule_<_gset:nnn } \@@_rule_before_gset:nnn
% \end{macrocode}
%
% \begin{macrocode}
\cs_new_protected:Npn \@@_rule_after_gset:nnn #1#2#3
{
\@@_tl_gset:cx { g_@@_#1_rule_ \@@_label_pair:nn {#3} {#2} _tl }
{ \@@_label_ordered:nnTF {#3} {#2} { < } { > } }
}
\cs_new_eq:cN { @@_rule_>_gset:nnn } \@@_rule_after_gset:nnn
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_rule_voids_gset:nnn}
% This rule removes (clears, actually) the code from label |#3| if
% label |#2| is in the hook |#1|.
% \begin{macrocode}
\cs_new_protected:Npn \@@_rule_voids_gset:nnn #1#2#3
{
\@@_tl_gset:cx { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl }
{ \@@_label_ordered:nnTF {#2} {#3} { -> } { <- } }
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{
% \@@_rule_incompatible-error_gset:nnn,
% \@@_rule_incompatible-warning_gset:nnn,
% }
% These relations make an error/warning if labels |#2| and |#3| appear
% together in hook |#1|.
% \begin{macrocode}
\cs_new_protected:cpn { @@_rule_incompatible-error_gset:nnn } #1#2#3
{ \@@_tl_gset:cn { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl }
{ xE } }
\cs_new_protected:cpn { @@_rule_incompatible-warning_gset:nnn } #1#2#3
{ \@@_tl_gset:cn { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl }
{ xW } }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_rule_unrelated_gset:nnn, \@@_rule_gclear:nnn}
% Undo a setting. \cs{@@_rule_unrelated_gset:nnn} doesn't need to do anything,
% since we use \cs{@@_rule_gclear:nnn} before setting any rule.
% \begin{macrocode}
\cs_new_protected:Npn \@@_rule_unrelated_gset:nnn #1#2#3 { }
\cs_new_protected:Npn \@@_rule_gclear:nnn #1#2#3
{ \cs_undefine:c { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl } }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_label_pair:nn}
% Ensure that the lexically greater label comes first.
% \begin{macrocode}
\cs_new:Npn \@@_label_pair:nn #1#2
{
\if_case:w \@@_str_compare:nn {#1} {#2} \exp_stop_f:
#1 | #1 % 0
\or: #1 | #2 % +1
\else: #2 | #1 % -1
\fi:
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF]{\@@_label_ordered:nn}
% Check that labels |#1| and |#2| are in the correct order (as
% returned by \cs{@@_label_pair:nn}) and if so return true, else
% return false.
% \begin{macrocode}
\prg_new_conditional:Npnn \@@_label_ordered:nn #1#2 { TF }
{
\if_int_compare:w \@@_str_compare:nn {#1} {#2} > 0 \exp_stop_f:
\prg_return_true:
\else:
\prg_return_false:
\fi:
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_if_label_case:nnnnn}
% To avoid doing the string comparison twice in \cs{@@_initialize_single:NNn}
% (once with \cs{str_if_eq:nn} and again with \cs{@@_label_ordered:nn}),
% we use a three-way branching macro that will compare |#1| and |#2|
% and expand to \cs{use_i:nnn} if they are equal, \cs{use_ii:nn} if
% |#1| is lexically greater, and \cs{use_iii:nn} otherwise.
% \begin{macrocode}
\cs_new:Npn \@@_if_label_case:nnnnn #1#2
{
\cs:w use_
\if_case:w \@@_str_compare:nn {#1} {#2}
i \or: ii \else: iii \fi: :nnn
\cs_end:
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_update_hook_code:n}
% Before \verb=\begin{document}= this does nothing, in the body it
% reinitializes the hook code using the altered data.
% \begin{macrocode}
\cs_new_eq:NN \@@_update_hook_code:n \use_none:n
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_initialize_all:}
% Initialize all known hooks (at \verb=\begin{document}=), i.e.,
% update the fast execution token lists to hold the necessary code
% in the right order.
% \begin{macrocode}
\cs_new_protected:Npn \@@_initialize_all: {
% \end{macrocode}
% First we change \cs{@@_update_hook_code:n} which so far was a
% no-op to now initialize one hook. This way any later updates to
% the hook will run that code and also update the execution token
% list.
% \begin{macrocode}
\cs_gset_eq:NN \@@_update_hook_code:n \@@_initialize_hook_code:n
% \end{macrocode}
% Now we loop over all hooks that have been defined and update each
% of them.
% \begin{macrocode}
\@@_debug:n { \prop_gclear:N \g_@@_used_prop }
\seq_map_inline:Nn \g_@@_all_seq
{
\@@_update_hook_code:n {##1}
}
% \end{macrocode}
% If we are debugging we show results hook by hook for all hooks
% that have data.
% \begin{macrocode}
\@@_debug:n
{ \iow_term:x{^^JAll~ initialized~ (non-empty)~ hooks:}
\prop_map_inline:Nn \g_@@_used_prop
{ \iow_term:x{^^J~ ##1~ ->~
\exp_not:v {@@~##1}~ }
}
}
% \end{macrocode}
% After all hooks are initialized we change the ``use'' to just
% call the hook code and not initialize it (as it was done in the
% preamble.
% \begin{macrocode}
\cs_gset_eq:NN \hook_use:n \@@_use_initialized:n
\cs_gset_eq:NN \@@_preamble_hook:n \use_none:n
}
% \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_initialize_hook_code:n}
% Initializing or reinitializing the fast execution hook code. In
% the preamble this is selectively done in case a hook gets used
% and at \verb=\begin{document}= this is done for all hooks and
% afterwards only if the hook code changes.
% \begin{macrocode}
\cs_new_protected:Npn \@@_initialize_hook_code:n #1
{
\@@_debug:n
{ \iow_term:x { ^^J Update~code~for~hook~'#1' \on@line :^^J } }
% \end{macrocode}
% This does the sorting and the updates.
% First thing we do is to check if a legacy hook macro exists and
% if so we add it to the hook under the label \texttt{legacy}. This
% might make the hook non-empty so we have to do this before
% the then following test.
% \begin{macrocode}
\@@_include_legacy_code_chunk:n {#1}
% \end{macrocode}
% If there aren't any code
% chunks for the current hook, there is no point in even starting
% the sorting routine so we make a quick test for that and in that
% case just update \cs{@@\textvisiblespace\meta{hook}} to hold the |top-level| and
% |next| code chunks. If there are code chunks we call
% \cs{@@_initialize_single:NNn} and pass to it ready made csnames
% as they are needed several times inside. This way we save a bit
% on processing time if we do that up front.
% \changes{v1.0u}{2022/05/17}{Refuse sorting one-time hooks (gh/818).}
% \begin{macrocode}
\@@_if_usable:nT {#1}
{
\prop_if_empty:cTF { g_@@_#1_code_prop }
{
\@@_tl_gset:co { @@~#1 }
{
\cs:w @@_toplevel~#1 \exp_after:wN \cs_end:
\cs:w @@_next~#1 \cs_end:
}
}
{
% \end{macrocode}
% By default the algorithm sorts the code chunks and then saves the
% result in a token list for fast execution; this is done by adding the code chunks
% one after another, using \cs{tl_gput_right:NV}. When we sort code for
% a reversed hook, all we have to do is to add the code chunks in
% the opposite order into the token list. So all we have to do
% in preparation is to change two definitions that are used later on.
% \begin{macrocode}
\@@_if_reversed:nTF {#1}
{ \cs_set_eq:NN \@@_tl_gput:Nn \@@_tl_gput_left:Nn
\cs_set_eq:NN \@@_clist_gput:NV \clist_gput_left:NV }
{ \cs_set_eq:NN \@@_tl_gput:Nn \@@_tl_gput_right:Nn
\cs_set_eq:NN \@@_clist_gput:NV \clist_gput_right:NV }
% \end{macrocode}
%
% When sorting, some relations (namely \verb|voids|) need to
% act destructively on the code property lists to remove code that
% shouldn't appear in the sorted hook token list, so we make a copy
% of the code property list that we can safely work on without
% changing the main one.
% \begin{macrocode}
\prop_set_eq:Nc \l_@@_work_prop { g_@@_#1_code_prop }
\@@_initialize_single:ccn
{ @@~#1 } { g_@@_#1_labels_clist } {#1}
% \end{macrocode}
% For debug display we want to keep track of those hooks that
% actually got code added to them, so we record that in plist. We
% use a plist to ensure that we record each hook name only once,
% i.e., we are only interested in storing the keys and the value is
% arbitrary.
% \begin{macrocode}
\@@_debug:n
{ \exp_args:NNx \prop_gput:Nnn \g_@@_used_prop {#1} { } }
}
}
}
% \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[EXP]{\@@_tl_csname:n,\@@_seq_csname:n}
% It is faster to pass a single token and expand it when necessary
% than to pass a bunch of character tokens around.
% \fmiinline{note to myself: verify}
% \begin{macrocode}
\cs_new:Npn \@@_tl_csname:n #1 { l_@@_label_#1_tl }
\cs_new:Npn \@@_seq_csname:n #1 { l_@@_label_#1_seq }
% \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\l_@@_labels_seq,\l_@@_labels_int,\l_@@_front_tl,
% \l_@@_rear_tl,\l_@@_label_0_tl}
%
% For the sorting I am basically implementing Knuth's algorithm for
% topological sorting as given in TAOCP volume 1 pages 263--266.
% For this algorithm we need a number of local variables:
% \begin{itemize}
% \item
% List of labels used in the current hook to label code chunks:
% \begin{macrocode}
\seq_new:N \l_@@_labels_seq
% \end{macrocode}
% \item
% Number of labels used in the current hook. In Knuth's algorithm
% this is called $N$:
% \begin{macrocode}
\int_new:N \l_@@_labels_int
% \end{macrocode}
% \item
% The sorted code list to be build is managed using two pointers
% one to the front of the queue and one to the rear. We model this
% using token list pointers. Knuth calls them $F$ and $R$:
% \begin{macrocode}
\tl_new:N \l_@@_front_tl
\tl_new:N \l_@@_rear_tl
% \end{macrocode}
% \item
% The data for the start of the queue is kept in this token list,
% it corresponds to what Don calls \texttt{QLINK[0]} but since we
% aren't manipulating individual words in memory it is slightly
% differently done:
% \begin{macrocode}
\tl_new:c { \@@_tl_csname:n { 0 } }
% \end{macrocode}
%
% \end{itemize}
% \end{macro}
%
%
% \begin{macro}{\@@_initialize_single:NNn,\@@_initialize_single:ccn}
%
% \cs{@@_initialize_single:NNn} implements the sorting of the code
% chunks for a hook and saves the result in the token list for fast
% execution (\verb=#4=). The arguments are \meta{hook-code-plist},
% \meta{hook-code-tl}, \meta{hook-top-level-code-tl},
% \meta{hook-next-code-tl},
% \meta{hook-ordered-labels-clist} and \meta{hook-name} (the latter
% is only used for debugging---the \meta{hook-rule-plist} is accessed
% using the \meta{hook-name}).
%
% The additional complexity compared to Don's algorithm is that we
% do not use simple positive integers but have arbitrary
% alphanumeric labels. As usual Don's data structures are chosen in
% a way that one can omit a lot of tests and I have mimicked that as
% far as possible. The result is a restriction I do not test for at
% the moment: a label can't be equal to the number 0!
% \fmiinline{Needs checking for, just in case ... maybe}
%
% ^^A #1 <- \@@~#1
% ^^A #2 <- \g_@@_#1_labels_clist
% ^^A #3 <- #1
% \begin{macrocode}
\cs_new_protected:Npn \@@_initialize_single:NNn #1#2#3
{
% \end{macrocode}
% Step T1: Initialize the data structure \ldots
% \begin{macrocode}
\seq_clear:N \l_@@_labels_seq
\int_zero:N \l_@@_labels_int
% \end{macrocode}
%
% Store the name of the hook:
% \begin{macrocode}
\tl_set:Nn \l_@@_cur_hook_tl {#3}
% \end{macrocode}
%
% We loop over the property list holding the code and record all
% the labels listed there. Only the rules for those labels are of interest
% to us. While we are at it we count them (which gives us the $N$
% in Knuth's algorithm). The prefix |label_| is added to the variables
% to ensure that labels named |front|, |rear|, |labels|, or |return|
% don't interact with our code.
% \begin{macrocode}
\prop_map_inline:Nn \l_@@_work_prop
{
\int_incr:N \l_@@_labels_int
\seq_put_right:Nn \l_@@_labels_seq {##1}
\@@_tl_set:cn { \@@_tl_csname:n {##1} } { 0 }
\seq_clear_new:c { \@@_seq_csname:n {##1} }
}
% \end{macrocode}
% Steps T2 and T3: Here we sort the relevant rules into the data structure\ldots
%
% This loop constitutes a square matrix of the labels in
% \cs{l_@@_work_prop} in the
% vertical and the horizontal directions. However, since the rule
% $l_A\meta{rel}l_B$ is the same as $l_B\meta{rel}^{-1}l_A$ we can cut
% the loop short at the diagonal of the matrix (\emph{i.e.}, when
% both labels are equal), saving a good amount of time. The way the
% rules were set up (see the implementation of \cs{@@_rule_before_gset:nnn}
% above) ensures that we have no rule in the ignored side of the
% matrix, and all rules are seen. The rules are applied in
% \cs{@@_apply_label_pair:nnn}, which takes the properly-ordered pair
% of labels as argument.
% \begin{macrocode}
\prop_map_inline:Nn \l_@@_work_prop
{
\prop_map_inline:Nn \l_@@_work_prop
{
\@@_if_label_case:nnnnn {##1} {####1}
{ \prop_map_break: }
{ \@@_apply_label_pair:nnn {##1} {####1} }
{ \@@_apply_label_pair:nnn {####1} {##1} }
{#3}
}
}
% \end{macrocode}
% Now take a breath, and look at the data structures that have
% been set up:
% \begin{macrocode}
\@@_debug:n { \@@_debug_label_data:N \l_@@_work_prop }
% \end{macrocode}
%
%
% Step T4:
% \begin{macrocode}
\tl_set:Nn \l_@@_rear_tl { 0 }
\tl_set:cn { \@@_tl_csname:n { 0 } } { 0 }
\seq_map_inline:Nn \l_@@_labels_seq
{
\int_compare:nNnT { \cs:w \@@_tl_csname:n {##1} \cs_end: } = 0
{
\tl_set:cn { \@@_tl_csname:n { \l_@@_rear_tl } }{##1}
\tl_set:Nn \l_@@_rear_tl {##1}
}
}
\tl_set_eq:Nc \l_@@_front_tl { \@@_tl_csname:n { 0 } }
% \end{macrocode}
%
% \begin{macrocode}
\@@_tl_gclear:N #1
\clist_gclear:N #2
% \end{macrocode}
%
% The whole loop gets combined in steps T5--T7:
% \begin{macrocode}
\bool_while_do:nn { ! \str_if_eq_p:Vn \l_@@_front_tl { 0 } }
{
% \end{macrocode}
% This part is step T5:
% \begin{macrocode}
\int_decr:N \l_@@_labels_int
\prop_get:NVN \l_@@_work_prop \l_@@_front_tl \l_@@_return_tl
\exp_args:NNV \@@_tl_gput:Nn #1 \l_@@_return_tl
% \end{macrocode}
%
% \begin{macrocode}
\@@_clist_gput:NV #2 \l_@@_front_tl
\@@_debug:n{ \iow_term:x{Handled~ code~ for~ \l_@@_front_tl} }
% \end{macrocode}
%
% This is step T6, except that we don't use a pointer $P$ to move
% through the successors, but instead use \verb=##1= of the mapping
% function.
% \begin{macrocode}
\seq_map_inline:cn { \@@_seq_csname:n { \l_@@_front_tl } }
{
\tl_set:cx { \@@_tl_csname:n {##1} }
{ \int_eval:n
{ \cs:w \@@_tl_csname:n {##1} \cs_end: - 1 }
}
\int_compare:nNnT
{ \cs:w \@@_tl_csname:n {##1} \cs_end: } = 0
{
\tl_set:cn { \@@_tl_csname:n { \l_@@_rear_tl } } {##1}
\tl_set:Nn \l_@@_rear_tl {##1}
}
}
% \end{macrocode}
% and here is step T7:
% \begin{macrocode}
\tl_set_eq:Nc \l_@@_front_tl
{ \@@_tl_csname:n { \l_@@_front_tl } }
% \end{macrocode}
%
% This is step T8: If we haven't moved the code for all labels
% (i.e., if \cs{l_@@_labels_int} is still greater than zero) we
% have a loop and our partial order can't be flattened out.
% \begin{macrocode}
}
\int_compare:nNnF \l_@@_labels_int = 0
{
\iow_term:x{====================}
\iow_term:x{Error:~ label~ rules~ are~ incompatible:}
% \end{macrocode}
%
% This is not really the information one needs in the error case
% but it will do for now \ldots \fmiinline{improve output on a rainy day}
% \begin{macrocode}
\@@_debug_label_data:N \l_@@_work_prop
\iow_term:x{====================}
}
% \end{macrocode}
% After we have added all hook code to \verb=#1=, we finish it off
% by adding extra code for the |top-level| (\verb=#2=) and for one
% time execution (\verb=#3=). These should normally be empty. The
% |top-level| code is added with \cs{@@_tl_gput:Nn} as that might
% change for a reversed hook (then |top-level| is the very first code
% chunk added). The |next| code is always added last.
% \begin{macrocode}
\exp_args:NNo \@@_tl_gput:Nn #1 { \cs:w @@_toplevel~#3 \cs_end: }
\@@_tl_gput_right:No #1 { \cs:w @@_next~#3 \cs_end: }
}
% \end{macrocode}
%
% \begin{macrocode}
\cs_generate_variant:Nn \@@_initialize_single:NNn { cc }
% \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}{\@@_tl_gput:Nn,\@@_clist_gput:NV}
% These append either on the right (normal hook) or on the left
% (reversed hook). This is setup up in
% \cs{@@_initialize_hook_code:n}, elsewhere their behavior is undefined.
% \begin{macrocode}
\cs_new:Npn \@@_tl_gput:Nn { \ERROR }
\cs_new:Npn \@@_clist_gput:NV { \ERROR }
% \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}{\@@_apply_label_pair:nnn,\@@_label_if_exist_apply:nnnF}
%
% This is the payload of steps T2 and T3 executed in the loop described
% above. This macro assumes |#1| and |#2| are ordered, which means that
% any rule pertaining the pair |#1| and |#2| is
% \cs{g_@@_\meta{hook}_rule_\#1\string|\#2_tl}, and not
% \cs{g_@@_\meta{hook}_rule_\#2\string|\#1_tl}. This also saves a great deal
% of time since we only need to check the order of the labels once.
%
% The arguments here are \meta{label1}, \meta{label2}, \meta{hook}, and
% \meta{hook-code-plist}. We are about to apply the next rule and
% enter it into the data structure. \cs{@@_apply_label_pair:nnn} will
% just call \cs{@@_label_if_exist_apply:nnnF} for the \meta{hook}, and
% if no rule is found, also try the \meta{hook} name \texttt{??}
% denoting a default hook rule.
%
% \cs{@@_label_if_exist_apply:nnnF} will check if the rule exists for
% the given hook, and if so call \cs{@@_apply_rule:nnn}.
% \begin{macrocode}
\cs_new_protected:Npn \@@_apply_label_pair:nnn #1#2#3
{
% \end{macrocode}
% Extra complication: as we use default rules and local hook specific
% rules we first have to check if there is a local rule and if that
% exist use it. Otherwise check if there is a default rule and use
% that.
% \begin{macrocode}
\@@_label_if_exist_apply:nnnF {#1} {#2} {#3}
{
% \end{macrocode}
% If there is no hook-specific rule we check for a default one and
% use that if it exists.
% \begin{macrocode}
\@@_label_if_exist_apply:nnnF {#1} {#2} { ?? } { }
}
}
\cs_new_protected:Npn \@@_label_if_exist_apply:nnnF #1#2#3
{
\if_cs_exist:w g_@@_ #3 _rule_ #1 | #2 _tl \cs_end:
% \end{macrocode}
% What to do precisely depends on the type of rule we have
% encountered. If it is a \texttt{before} rule it will be handled by the
% algorithm but other types need to be managed differently. All
% this is done in \cs{@@_apply_rule:nnnN}.
% \begin{macrocode}
\@@_apply_rule:nnn {#1} {#2} {#3}
\exp_after:wN \use_none:n
\else:
\use:nn
\fi:
}
% \end{macrocode}
% \end{macro}
%
%
%
%
% \begin{macro}{\@@_apply_rule:nnn}
% This is the code executed in steps T2 and T3 while looping through
% the matrix This is part of step T3. We are about to apply the next
% rule and enter it into the data structure. The arguments are
% \meta{label1}, \meta{label2}, \meta{hook-name}, and \meta{hook-code-plist}.
% \begin{macrocode}
\cs_new_protected:Npn \@@_apply_rule:nnn #1#2#3
{
\cs:w @@_apply_
\cs:w g_@@_#3_reversed_tl \cs_end: rule_
\cs:w g_@@_ #3 _rule_ #1 | #2 _tl \cs_end: :nnn \cs_end:
{#1} {#2} {#3}
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_apply_rule_<:nnn,\@@_apply_rule_>:nnn}
% The most common cases are \texttt{\string<} and \texttt{\string>} so we handle
% that first. They are relations $\prec$ and $\succ$ in TAOCP, and
% they dictate sorting.
% \begin{macrocode}
\cs_new_protected:cpn { @@_apply_rule_<:nnn } #1#2#3
{
\@@_debug:n { \@@_msg_pair_found:nnn {#1} {#2} {#3} }
\tl_set:cx { \@@_tl_csname:n {#2} }
{ \int_eval:n{ \cs:w \@@_tl_csname:n {#2} \cs_end: + 1 } }
\seq_put_right:cn{ \@@_seq_csname:n {#1} }{#2}
}
\cs_new_protected:cpn { @@_apply_rule_>:nnn } #1#2#3
{
\@@_debug:n { \@@_msg_pair_found:nnn {#1} {#2} {#3} }
\tl_set:cx { \@@_tl_csname:n {#1} }
{ \int_eval:n{ \cs:w \@@_tl_csname:n {#1} \cs_end: + 1 } }
\seq_put_right:cn{ \@@_seq_csname:n {#2} }{#1}
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_apply_rule_xE:nnn,\@@_apply_rule_xW:nnn}
% These relations make two labels incompatible within a hook.
% |xE| makes raises an error if the labels are found in the same
% hook, and |xW| makes it a warning.
% \begin{macrocode}
\cs_new_protected:cpn { @@_apply_rule_xE:nnn } #1#2#3
{
\@@_debug:n { \@@_msg_pair_found:nnn {#1} {#2} {#3} }
\msg_error:nnnnnn { hooks } { labels-incompatible }
{#1} {#2} {#3} { 1 }
\use:c { @@_apply_rule_->:nnn } {#1} {#2} {#3}
\use:c { @@_apply_rule_<-:nnn } {#1} {#2} {#3}
}
\cs_new_protected:cpn { @@_apply_rule_xW:nnn } #1#2#3
{
\@@_debug:n { \@@_msg_pair_found:nnn {#1} {#2} {#3} }
\msg_warning:nnnnnn { hooks } { labels-incompatible }
{#1} {#2} {#3} { 0 }
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_apply_rule_->:nnn,\@@_apply_rule_<-:nnn}
% If we see \texttt{\detokenize{->}} we have to drop code for label
% \verb=#3= and carry on. We could do a little better and drop
% everything for that label since it doesn't matter where we put
% such empty code. However that would complicate the algorithm a
% lot with little gain.\footnote{This also has the advantage that
% the result of the sorting doesn't change, as it might otherwise do
% (for unrelated chunks) if we aren't careful.} So we still
% unnecessarily try to sort it in and depending on the rules that
% might result in a loop that is otherwise resolved. If that turns
% out to be a real issue, we can improve the code.
%
% Here the code is removed from \cs{l_@@_cur_hook_tl} rather than
% \verb=#3= because the latter may be \texttt{??}, and the default
% hook doesn't store any code. Removing it instead from \cs{l_@@_cur_hook_tl}
% makes the default rules \verb=->= and \verb=<-= work properly.
% \begin{macrocode}
\cs_new_protected:cpn { @@_apply_rule_->:nnn } #1#2#3
{
\@@_debug:n
{
\@@_msg_pair_found:nnn {#1} {#2} {#3}
\iow_term:x{--->~ Drop~ '#2'~ code~ from~
\iow_char:N \\ g_@@_ \l_@@_cur_hook_tl _code_prop ~
because~ of~ '#1' }
}
\prop_put:Nnn \l_@@_work_prop {#2} { }
}
\cs_new_protected:cpn { @@_apply_rule_<-:nnn } #1#2#3
{
\@@_debug:n
{
\@@_msg_pair_found:nnn {#1} {#2} {#3}
\iow_term:x{--->~ Drop~ '#1'~ code~ from~
\iow_char:N \\ g_@@_ \l_@@_cur_hook_tl _code_prop ~
because~ of~ '#2' }
}
\prop_put:Nnn \l_@@_work_prop {#1} { }
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{
% \@@_apply_-rule_<:nnn,
% \@@_apply_-rule_>:nnn,
% \@@_apply_-rule_<-:nnn,
% \@@_apply_-rule_->:nnn,
% \@@_apply_-rule_xW:nnn,
% \@@_apply_-rule_xE:nnn,
% }
% Reversed rules.
% \begin{macrocode}
\cs_new_eq:cc { @@_apply_-rule_<:nnn } { @@_apply_rule_>:nnn }
\cs_new_eq:cc { @@_apply_-rule_>:nnn } { @@_apply_rule_<:nnn }
\cs_new_eq:cc { @@_apply_-rule_<-:nnn } { @@_apply_rule_<-:nnn }
\cs_new_eq:cc { @@_apply_-rule_->:nnn } { @@_apply_rule_->:nnn }
\cs_new_eq:cc { @@_apply_-rule_xE:nnn } { @@_apply_rule_xE:nnn }
\cs_new_eq:cc { @@_apply_-rule_xW:nnn } { @@_apply_rule_xW:nnn }
% \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_msg_pair_found:nnn}
% A macro to avoid moving this many tokens around.
% \begin{macrocode}
\cs_new_protected:Npn \@@_msg_pair_found:nnn #1#2#3
{
\iow_term:x{~ \str_if_eq:nnTF {#3} {??} {default} {~normal} ~
rule~ \@@_label_pair:nn {#1} {#2}:~
\use:c { g_@@_#3_rule_ \@@_label_pair:nn {#1} {#2} _tl } ~
found}
}
% \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_debug_label_data:N}
%
% \begin{macrocode}
\cs_new_protected:Npn \@@_debug_label_data:N #1 {
\iow_term:x{Code~ labels~ for~ sorting:}
\iow_term:x{~ \seq_use:Nnnn\l_@@_labels_seq {~and~}{,~}{~and~} }
\iow_term:x{^^J Data~ structure~ for~ label~ rules:}
\prop_map_inline:Nn #1
{
\iow_term:x{~ ##1~ =~ \tl_use:c{ \@@_tl_csname:n {##1} }~ ->~
\seq_use:cnnn{ \@@_seq_csname:n {##1} }{~->~}{~->~}{~->~}
}
}
\iow_term:x{}
}
% \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}{\hook_show:n,\hook_log:n}
% \begin{macro}{\@@_log_line:x,\@@_log_line_indent:x}
% \begin{macro}{\@@_log:nN}
% This writes out information about the hook given in its argument
% onto the \texttt{.log} file and the terminal, if \cs{show_hook:n} is
% used. Internally both share the same structure, except that at the
% end, \cs{hook_show:n} triggers \TeX's prompt.
% \begin{macrocode}
\cs_new_protected:Npn \hook_log:n #1
{
\cs_set_eq:NN \@@_log_cmd:x \iow_log:x
\@@_normalize_hook_args:Nn \@@_log:nN {#1} \tl_log:x
}
\cs_new_protected:Npn \hook_show:n #1
{
\cs_set_eq:NN \@@_log_cmd:x \iow_term:x
\@@_normalize_hook_args:Nn \@@_log:nN {#1} \tl_show:x
}
\cs_new_protected:Npn \@@_log_line:x #1
{ \@@_log_cmd:x { >~#1 } }
\cs_new_protected:Npn \@@_log_line_indent:x #1
{ \@@_log_cmd:x { >~\@spaces #1 } }
% \end{macrocode}
%
% \begin{macrocode}
\cs_new_protected:Npn \@@_log:nN #1 #2
{
\@@_if_deprecated_generic:nT {#1}
{
\@@_deprecated_generic_warn:n {#1}
\@@_do_deprecated_generic:Nn \@@_log:nN {#1} #2
\exp_after:wN \use_none:nnnnnnnnn \use_none:nnnnn
}
\@@_preamble_hook:n {#1}
\@@_log_cmd:x
{ ^^J ->~The~ \@@_if_generic:nT {#1} { generic~ } hook~'#1': }
% \end{macrocode}
%
% \begin{macrocode}
\@@_if_usable:nF {#1}
{ \@@_log_line:x { The~hook~is~not~declared. } }
\@@_if_disabled:nT {#1}
{ \@@_log_line:x { The~hook~is~disabled. } }
\hook_if_empty:nTF {#1}
{ #2 { The~hook~is~empty } }
{
\@@_log_line:x { Code~chunks: }
\prop_if_empty:cTF { g_@@_#1_code_prop }
{ \@@_log_line_indent:x { --- } }
{
\prop_map_inline:cn { g_@@_#1_code_prop }
{ \@@_log_line_indent:x { ##1~->~\tl_to_str:n {##2} } }
}
% \end{macrocode}
%
% If there is code in the |top-level| token list, print it:
% \begin{macrocode}
\@@_log_line:x
{
Document-level~(top-level)~code
\@@_if_usable:nT {#1}
{ ~(executed~\@@_if_reversed:nTF {#1} {first} {last} ) } :
}
\@@_log_line_indent:x
{
\tl_if_empty:cTF { @@_toplevel~#1 }
{ --- }
{ -> ~ \exp_args:Nv \tl_to_str:n { @@_toplevel~#1 } }
}
% \end{macrocode}
%
% \begin{macrocode}
\@@_log_line:x { Extra~code~for~next~invocation: }
\@@_log_line_indent:x
{
\tl_if_empty:cTF { @@_next~#1 }
{ --- }
% \end{macrocode}
%
% If the token list is not empty we want to display it but without
% the first tokens (the code to clear itself) so we call a helper
% command to get rid of them.
% \begin{macrocode}
{ ->~ \exp_args:Nv \@@_log_next_code:n { @@_next~#1 } }
}
% \end{macrocode}
%
% Loop through the rules in a hook and for every rule found, print it.
% If no rule is there, print |---|. The boolean \cs{l_@@_tmpa_bool}
% here indicates if the hook has no rules.
% \begin{macrocode}
\@@_log_line:x { Rules: }
\bool_set_true:N \l_@@_tmpa_bool
\@@_list_rules:nn {#1}
{
\bool_set_false:N \l_@@_tmpa_bool
\@@_log_line_indent:x
{
##2~ with~
\str_if_eq:nnT {##3} {??} { default~ }
relation~ ##1
}
}
\bool_if:NT \l_@@_tmpa_bool
{ \@@_log_line_indent:x { --- } }
% \end{macrocode}
%
% When the hook is declared (that is, the sorting algorithm is applied
% to that hook) and not empty
% \begin{macrocode}
\bool_lazy_and:nnTF
{ \@@_if_usable_p:n {#1} }
{ ! \hook_if_empty_p:n {#1} }
{
\@@_log_line:x
{
Execution~order
\bool_if:NTF \l_@@_tmpa_bool
{ \@@_if_reversed:nT {#1} { ~(after~reversal) } }
{ ~(after~
\@@_if_reversed:nT {#1} { reversal~and~ }
applying~rules)
} :
}
#2 % \tl_show:n
{
\@spaces
\clist_if_empty:cTF { g_@@_#1_labels_clist }
{ --- }
{ \clist_use:cn {g_@@_#1_labels_clist} { ,~ } }
}
}
{
\@@_log_line:x { Execution~order: }
#2
{
\@spaces Not~set~because~the~hook~ \@@_if_usable:nTF {#1}
{ code~pool~is~empty }
{ is~\@@_if_disabled:nTF {#1} {disabled} {undeclared} }
}
}
}
}
% \end{macrocode}
%
% \begin{macro}{\@@_log_next_code:n}
% To display the code for next invocation only (i.e., from
% \cs{AddToHookNext} we have to remove the first two tokens at the
% front which are \cs{tl_gclear:N} and the token list to clear.
% \begin{macrocode}
\cs_new:Npn \@@_log_next_code:n #1
{ \exp_args:No \tl_to_str:n { \use_none:nn #1 } }
% \end{macrocode}
% \end{macro}
%
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_list_rules:nn}
% \begin{macro}{\@@_list_one_rule:nnn,\@@_list_if_rule_exists:nnnF}
% This macro takes a \meta{hook} and an \meta{inline function} and
% loops through each pair of \meta{labels} in the \meta{hook}, and if
% there is a relation between this pair of \meta{labels}, the
% \meta{inline function} is executed with |#1|${}={}$\meta{relation},
% |#2|${}={}$\meta{label_1}\verb=|=\meta{label_2},
% and |#3|${}={}$\meta{hook} (the latter may be the argument |#1| to
% \cs{@@_list_rules:nn}, or \texttt{??} if it is a default rule).
% \begin{macrocode}
\cs_new_protected:Npn \@@_list_rules:nn #1 #2
{
\cs_set_protected:Npn \@@_tmp:w ##1 ##2 ##3 {#2}
\prop_map_inline:cn { g_@@_#1_code_prop }
{
\prop_map_inline:cn { g_@@_#1_code_prop }
{
\@@_if_label_case:nnnnn {##1} {####1}
{ \prop_map_break: }
{ \@@_list_one_rule:nnn {##1} {####1} }
{ \@@_list_one_rule:nnn {####1} {##1} }
{#1}
}
}
}
% \end{macrocode}
%
% These two are quite similar to \cs{@@_apply_label_pair:nnn} and
% \cs{@@_label_if_exist_apply:nnnF}, respectively, but rather than
% applying the rule, they pass it to the \meta{inline function}.
% \begin{macrocode}
\cs_new_protected:Npn \@@_list_one_rule:nnn #1#2#3
{
\@@_list_if_rule_exists:nnnF {#1} {#2} {#3}
{ \@@_list_if_rule_exists:nnnF {#1} {#2} { ?? } { } }
}
\cs_new_protected:Npn \@@_list_if_rule_exists:nnnF #1#2#3
{
\if_cs_exist:w g_@@_ #3 _rule_ #1 | #2 _tl \cs_end:
\exp_args:Nv \@@_tmp:w
{ g_@@_ #3 _rule_ #1 | #2 _tl } { #1 | #2 } {#3}
\exp_after:wN \use_none:nn
\fi:
\use:n
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_debug_print_rules:n}
% A shorthand for debugging that prints similar to \cs{prop_show:N}.
% \begin{macrocode}
\cs_new_protected:Npn \@@_debug_print_rules:n #1
{
\iow_term:n { The~hook~#1~contains~the~rules: }
\cs_set_protected:Npn \@@_tmp:w ##1
{
\@@_list_rules:nn {#1}
{
\iow_term:x
{
> ##1 {####2} ##1 => ##1 {####1}
\str_if_eq:nnT {####3} {??} { ~(default) }
}
}
}
\exp_args:No \@@_tmp:w { \use:nn { ~ } { ~ } }
}
% \end{macrocode}
% \end{macro}
%
%
%
%
% \subsection{Specifying code for next invocation}
%
% \begin{macro}{\hook_gput_next_code:nn}
% \begin{macrocode}
\cs_new_protected:Npn \hook_gput_next_code:nn #1
{ \@@_normalize_hook_args:Nn \@@_gput_next_code:nn {#1} }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_gput_next_code:nn,
% \@@_gput_next_do:nn,
% \@@_gput_next_do:Nnn}
% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_next_code:nn #1 #2
{
\@@_if_disabled:nTF {#1}
{ \msg_error:nnn { hooks } { hook-disabled } {#1} }
{
\@@_if_structure_exist:nTF {#1}
{ \@@_gput_next_do:nn {#1} {#2} }
{ \@@_try_declaring_generic_next_hook:nn {#1} {#2} }
}
}
% \end{macrocode}
%
% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_next_do:nn #1
{
\exp_args:Nc \@@_gput_next_do:Nnn
{ @@_next~#1 } {#1}
}
% \end{macrocode}
% First check if the ``next code'' token list is empty: if so we need
% to add a \cs{tl_gclear:c} to clear it, so the code lasts for one
% usage only. The token list is cleared early so that nested usages
% don't get lost. \cs{tl_gclear:c} is used instead of
% \cs{tl_gclear:N} in case the hook is used in an expansion-only
% context, so the token list doesn't expand before \cs{tl_gclear:N}:
% that would make an infinite loop. Also in case the main code token
% list is empty, the hook code has to be updated to add the next
% execution token list.
% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_next_do:Nnn #1 #2
{
\tl_if_empty:cT { @@~#2 }
{ \@@_update_hook_code:n {#2} }
\tl_if_empty:NT #1
{ \@@_tl_gset:Nn #1 { \@@_clear_next:n {#2} } }
\@@_tl_gput_right:Nn #1
}
% \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}{\hook_gclear_next_code:n,\@@_clear_next:n}
% Discard anything set up for next invocation of the hook.
% \changes{v1.0o}{2021/07/27}{Macro made public}
% \begin{macrocode}
\cs_new_protected:Npn \hook_gclear_next_code:n #1
{ \@@_normalize_hook_args:Nn \@@_clear_next:n {#1} }
\cs_new_protected:Npn \@@_clear_next:n #1
{ \cs_gset_eq:cN { @@_next~#1 } \c_empty_tl }
% \end{macrocode}
% \end{macro}
%
%
%
% \subsection{Using the hook}
%
% \begin{macro}{\hook_use:n}
% \begin{macro}[EXP]{\@@_use_initialized:n}
% \begin{macro}{\@@_use_undefined:w,\@@_use_end:}
% \begin{macro}{\@@_preamble_hook:n}
% \cs{hook_use:n} as defined here is used in the preamble, where
% hooks aren't initialized by default. \cs{@@_use_initialized:n} is
% also defined, which is the non-\tn{protected} version for use within
% the document. Their definition is identical, except for the
% \cs{@@_preamble_hook:n} (which wouldn't hurt in the expandable
% version, but it would be an unnecessary extra expansion).
%
% \cs{@@_use_initialized:n} holds the expandable definition while in
% the preamble. \cs{@@_preamble_hook:n} initializes the hook in the
% preamble, and is redefined to \cs{use_none:n} at |\begin{document}|.
%
% Both versions do the same thing internally: they check that the hook exists as
% given, and if so they use it as quickly as possible.
%
% At |\begin{document}|, all hooks are initialized, and any change in
% them causes an update, so \cs{hook_use:n} can be made expandable.
% This one is better not protected so that it can expand into nothing
% if containing no code. Also important in case of generic hooks that
% we do not generate a \cs[no-index]{relax} as a side effect of
% checking for a csname. In contrast to the \TeX{} low-level
% \verb=\csname ...\endcsname= construct \cs{tl_if_exist:c} is
% careful to avoid this.
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}{\hook_use:n}
%<latexrelease> {Standardise~generic~hook~names}
\cs_new_protected:Npn \hook_use:n #1
{
\tl_if_exist:cT { @@~#1 }
{
\@@_preamble_hook:n {#1}
\cs:w @@~#1 \cs_end:
}
}
\cs_new:Npn \@@_use_initialized:n #1
{
\if_cs_exist:w @@~#1 \cs_end:
\cs:w @@~#1 \exp_after:wN \cs_end:
\fi:
}
\cs_new_protected:Npn \@@_preamble_hook:n #1
{ \@@_initialize_hook_code:n {#1} }
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
%
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_use:n}
%<latexrelease> {Standardise~generic~hook~names}
%<latexrelease>\cs_new_protected:Npn \hook_use:n #1
%<latexrelease> {
%<latexrelease> \tl_if_exist:cTF { @@~#1 }
%<latexrelease> {
%<latexrelease> \@@_preamble_hook:n {#1}
%<latexrelease> \cs:w @@~#1 \cs_end:
%<latexrelease> }
%<latexrelease> { \@@_use:wn #1 / \s_@@_mark {#1} }
%<latexrelease> }
%<latexrelease>\cs_new:Npn \@@_use_initialized:n #1
%<latexrelease> {
%<latexrelease> \if_cs_exist:w @@~#1 \cs_end:
%<latexrelease> \else:
%<latexrelease> \@@_use_undefined:w
%<latexrelease> \fi:
%<latexrelease> \cs:w @@~#1 \@@_use_end:
%<latexrelease> }
%<latexrelease>\cs_new:Npn \@@_use_undefined:w #1 #2 @@~#3 \@@_use_end:
%<latexrelease> {
%<latexrelease> #1 % fi
%<latexrelease> \@@_use:wn #3 / \s_@@_mark {#3}
%<latexrelease> }
%<latexrelease>\cs_new_protected:Npn \@@_preamble_hook:n #1
%<latexrelease> { \@@_initialize_hook_code:n {#1} }
%<latexrelease>\cs_new_eq:NN \@@_use_end: \cs_end:
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_use:wn}
% \begin{macro}{\@@_try_file_hook:n,\@@_if_usable_use:n}
% \cs{@@_use:wn} does a quick check to test if the current hook is a
% file hook: those need a special treatment. If it is not, the hook
% does not exist. If it is, then \cs{@@_try_file_hook:n} is called,
% and checks that the current hook is a file-specific hook using
% \cs{@@_if_file_hook:wTF}. If it's not, then it's a generic |file/|
% hook and is used if it exist.
%
% If it is a file-specific hook, it passes through the same
% normalization as during declaration, and then it is used if defined.
% \cs{@@_if_usable_use:n} checks if the hook exist, and calls
% \cs{@@_preamble_hook:n} if so, then uses the hook.
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_use:wn}
%<latexrelease> {Standardise~generic~hook~names}
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_use:wn}
%<latexrelease> {Standardise~generic~hook~names}
%<latexrelease>\cs_new:Npn \@@_use:wn #1 / #2 \s_@@_mark #3
%<latexrelease> {
%<latexrelease> \str_if_eq:nnTF {#1} { file }
%<latexrelease> { \@@_try_file_hook:n {#3} }
%<latexrelease> { } % Hook doesn't exist
%<latexrelease> }
% \end{macrocode}
%
% \changes{v1.0s}{2021/09/28}
% {Correct usage of older \cs{@@_if_file_hook:wTF} (gh/675)}
% \begin{macrocode}
%<latexrelease>\cs_new_protected:Npn \@@_try_file_hook:n #1
%<latexrelease> {
%<latexrelease> \@@_if_file_hook:wTF #1 / \s_@@_mark
%<latexrelease> {
%<latexrelease> \exp_args:Ne \@@_if_usable_use:n
%<latexrelease> { \exp_args:Ne \@@_file_hook_normalize:n {#1} }
%<latexrelease> }
%<latexrelease> { \@@_if_usable_use:n {#1} } % file/ generic hook (e.g. file/before)
%<latexrelease> }
% \end{macrocode}
%
% \begin{macrocode}
%<latexrelease>\cs_new_protected:Npn \@@_if_usable_use:n #1
%<latexrelease> {
%<latexrelease> \tl_if_exist:cT { @@~#1 }
%<latexrelease> {
%<latexrelease> \@@_preamble_hook:n {#1}
%<latexrelease> \cs:w @@~#1 \cs_end:
%<latexrelease> }
%<latexrelease> }
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\hook_use_once:n}
% \begin{macro}{\@@_use_once:n}
% For hooks that can and should be used only once we have a special
% use command that further inhibits the hook from getting more code
% added to it. This has the effect that any
% further code added to the hook is executed immediately rather
% than stored in the hook.
%
% The code needs some gymnastics to prevent space trimming from the
% hook name, since \cs{hook_use:n} and \cs{hook_use_once:n} are
% documented to not trim spaces.
%
% \changes{v1.0r}{2021/09/06}{Clean up after \cs{UseOneTimeHook} (gh/606)}
% \begin{macrocode}
\cs_new_protected:Npn \hook_use_once:n #1
{
\@@_if_execute_immediately:nF {#1}
{ \@@_normalize_hook_args:Nn \@@_use_once:n { \use:n {#1} } }
}
\cs_new_protected:Npn \@@_use_once:n #1
{
\@@_preamble_hook:n {#1}
\@@_use_once_set:n {#1}
\@@_use_initialized:n {#1}
\@@_use_once_clear:n {#1}
}
% \end{macrocode}
%
% \begin{macro}{\@@_use_once_set:n}
% \begin{macro}{\@@_use_once_clear:n}
% \cs{@@_use_once_set:n} is used before the actual hook code is
% executed so that any usage of \cs{AddToHook} inside the hook causes
% the code to execute immediately. Setting
% \cs[no-index]{g_@@_\meta{hook}_reversed_tl} to |I| prevents further
% code from being added to the hook. \cs{@@_use_once_clear:n} then
% clears the hook so that any further call to \cs{hook_use:n} or
% \cs{hook_use_once:n} will expand to nothing.
% \changes{v1.0r}{2021/09/06}{Clean up after \cs{UseOneTimeHook} (gh/606)}
% \changes{v1.0u}{2022/05/13}{Check if prop exists to avoid l3debug error}
% \begin{macrocode}
\cs_new_protected:Npn \@@_use_once_set:n #1
{ \@@_tl_gset:cn { g_@@_#1_reversed_tl } { I } }
\cs_new_protected:Npn \@@_use_once_clear:n #1
{
\@@_tl_gclear:c { @@~#1 }
\@@_tl_gclear:c { @@_next~#1 }
\@@_tl_gclear:c { @@_toplevel~#1 }
\prop_gclear_new:c { g_@@_#1_code_prop }
}
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}[pTF]{\@@_if_execute_immediately:n}
% To check whether the code being added should be executed immediately (that
% is, if the hook is a one-time hook), we check if it's usable (it
% can't be one-time if it was not already usable), then we check that
% \cs[no-index]{g_@@_\meta{hook}_reversed_tl} is |I|. The gymnastics
% around \cs{if:w} is there to allow the |reversed| token list to be
% empty.
% \changes{v1.0r}{2021/09/06}{Macro added (gh/606)}
% \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_execute_immediately:n #1 { T, F, TF }
{
\@@_if_usable:nTF {#1}
{
\exp_after:wN \@@_clean_to_scan:w
\if:w I \cs:w g_@@_#1_reversed_tl \cs_end:
\s_@@_mark \prg_return_true:
\else:
\s_@@_mark \prg_return_false:
\fi:
}
{ \prg_return_false: }
}
% \end{macrocode}
% \end{macro}
%
% \subsection{Querying a hook}
%
% Simpler data types, like token lists, have three possible states; they
% can exist and be empty, exist and be non-empty, and they may not
% exist, in which case emptiness doesn't apply (though
% \cs{tl_if_empty:N} returns false in this case).
%
% Hooks are a bit more complicated: they have several other states as
% discussed in \ref{sec:existence}.
% A hook may exist or not, and either way it may or may not be empty
% (even a hook that doesn't exist may be non-empty) or may be disabled.
%
% A hook is said to be empty when no code was added to it, either to
% its permanent code pool, or to its ``next'' token list. The hook
% doesn't need to be declared to have code added to its code pool
% (it may happen that a package $A$ defines a hook \hook{foo}, but
% it's loaded after package $B$, which adds some code to that hook.
% In this case it is important that the code added by package $B$ is
% remembered until package $A$ is loaded).
%
% All other states can only be queried with internal tests as the
% different states are irrelevant for package code.
% \begin{macro}[pTF]{\hook_if_empty:n}
% Test if a hook is empty (that is, no code was added to that hook).
% A \meta{hook} being empty means that all three of its
% \cs{g_@@_\meta{hook}_code_prop}, its
% \cs{@@_toplevel\textvisiblespace\meta{hook}} and its
% \cs{@@_next\textvisiblespace\meta{hook}} are empty.
% \begin{macrocode}
\prg_new_conditional:Npnn \hook_if_empty:n #1 { p , T , F , TF }
{
\@@_if_structure_exist:nTF {#1}
{
\bool_lazy_and:nnTF
{ \prop_if_empty_p:c { g_@@_#1_code_prop } }
{
\bool_lazy_and_p:nn
{ \tl_if_empty_p:c { @@_toplevel~#1 } }
{ \tl_if_empty_p:c { @@_next~#1 } }
}
{ \prg_return_true: }
{ \prg_return_false: }
}
{ \prg_return_true: }
}
% \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[pTF]{\@@_if_usable:n}
% A hook is usable if the
% token list that stores the sorted code for that hook,
% \cs[no-index]{@@~\meta{hook}}, exists. The property list
% \cs[no-index]{g_@@_\meta{hook}_code_prop} cannot be used here
% because often it is necessary to add code to a hook without knowing
% if such hook was already declared, or even if it will ever be
% (for example, in case the package that defines it isn't loaded).
% \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_usable:n #1 { p , T , F , TF }
{
\tl_if_exist:cTF { @@~#1 }
{ \prg_return_true: }
{ \prg_return_false: }
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF]{\@@_if_structure_exist:n}
%
% An internal check if the hook has already its basic internal
% structure set up with
% \cs{@@_init_structure:n}. This means that the hook was already used somehow
% (a code chunk or rule was added to it), but it still wasn't declared
% with \cs{hook_new:n}.
% \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_structure_exist:n #1 { p , T , F , TF }
{
\prop_if_exist:cTF { g_@@_#1_code_prop }
{ \prg_return_true: }
{ \prg_return_false: }
}
% \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[pTF]{\@@_if_declared:n}
%
% Internal test to check if the hook was officially declared with
% \cs{hook_new:n} or a variant.
% \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_declared:n #1 { p, T, F, TF }
{
\tl_if_exist:cTF { g_@@_#1_declared_tl }
{ \prg_return_true: }
{ \prg_return_false: }
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF]{\@@_if_reversed:n}
% An internal conditional that checks if a hook is reversed.
% \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_reversed:n #1 { p , T , F , TF }
{
\exp_after:wN \@@_clean_to_scan:w
\if:w - \cs:w g_@@_#1_reversed_tl \cs_end:
\s_@@_mark \prg_return_true:
\else:
\s_@@_mark \prg_return_false:
\fi:
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[pTF]{\@@_if_generic:n}
% \begin{macro}[pTF]{\@@_if_deprecated_generic:n}
% An internal conditional that checks if a name belongs to a generic
% hook. The deprecated version needs to check if |#3| is empty to
% avoid returning true on \hook{file/before}, for example.
% \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_generic:n #1 { T, TF }
{ \@@_if_generic:w #1 / / / \s_@@_mark }
\cs_new:Npn \@@_if_generic:w #1 / #2 / #3 / #4 \s_@@_mark
{
\cs_if_exist:cTF { c_@@_generic_#1/./#3_tl }
{ \prg_return_true: }
{ \prg_return_false: }
}
\prg_new_conditional:Npnn \@@_if_deprecated_generic:n #1 { T, TF }
{ \@@_if_deprecated_generic:w #1 / / / \s_@@_mark }
\cs_new:Npn \@@_if_deprecated_generic:w #1 / #2 / #3 / #4 \s_@@_mark
{
\cs_if_exist:cTF { c_@@_deprecated_#1/./#2_tl }
{
\tl_if_empty:nTF {#3}
{ \prg_return_false: }
{ \prg_return_true: }
}
{ \prg_return_false: }
}
% \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[pTF]{\@@_if_generic_reversed:n}
% An internal conditional that checks if a name belongs to a generic
% reversed hook.
% \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_generic_reversed:n #1 { T }
{ \@@_if_generic_reversed:w #1 / / / \scan_stop: }
\cs_new:Npn \@@_if_generic_reversed:w #1 / #2 / #3 / #4 \scan_stop:
{
\if_charcode:w - \cs:w c_@@_generic_#1/./#3_tl \cs_end:
\prg_return_true:
\else:
\prg_return_false:
\fi:
}
% \end{macrocode}
% \end{macro}
%
% \subsection{Messages}
%
% Hook errors are LaTeX kernel errors:
% \begin{macrocode}
\prop_gput:Nnn \g_msg_module_type_prop { hooks } { LaTeX }
% \end{macrocode}
% \changes{v1.0q}{2021/08/27}{Internal message name changes}
% And so are kernel errors (this should move elsewhere eventually).
% \begin{macrocode}
\prop_gput:Nnn \g_msg_module_type_prop { latex2e } { LaTeX }
\prop_gput:Nnn \g_msg_module_name_prop { latex2e } { kernel }
% \end{macrocode}
%
% \begin{macrocode}
\msg_new:nnnn { hooks } { labels-incompatible }
{
Labels~'#1'~and~'#2'~are~incompatible
\str_if_eq:nnF {#3} {??} { ~in~hook~'#3' } .~
\int_compare:nNnTF {#4} = { 1 }
{ The~ code~ for~ both~ labels~ will~ be~ dropped. }
{ You~ may~ see~ errors~ later. }
}
{ LaTeX~found~two~incompatible~labels~in~the~same~hook.~
This~indicates~an~incompatibility~between~packages. }
% \end{macrocode}
%
% \begin{macrocode}
\msg_new:nnnn { hooks } { exists }
{ Hook~'#1'~ has~ already~ been~ declared. }
{ There~ already~ exists~ a~ hook~ declaration~ with~ this~
name.\\
Please~ use~ a~ different~ name~ for~ your~ hook.}
% \end{macrocode}
%
%
% \begin{macrocode}
\msg_new:nnnn { hooks } { hook-disabled }
{ Cannot~add~code~to~disabled~hook~'#1'. }
{
The~hook~'#1'~you~tried~to~add~code~to~was~previously~disabled~
with~\iow_char:N\\hook_disable_generic:n~or~\iow_char:N\\DisableGenericHook,~so~
it~cannot~have~code~added~to~it.
}
% \end{macrocode}
%
% \begin{macrocode}
\msg_new:nnn { hooks } { empty-label }
{
Empty~code~label~\msg_line_context:.~
Using~'\@@_currname_or_default:'~instead.
}
% \end{macrocode}
%
% \begin{macrocode}
\msg_new:nnn { hooks } { no-default-label }
{
Missing~(empty)~default~label~\msg_line_context:. \\
This~command~was~ignored.
}
% \end{macrocode}
%
% \begin{macrocode}
\msg_new:nnnn { hooks } { unknown-rule }
{
Unknown~ relationship~ '#3'~
between~ labels~ '#2'~ and~ '#4'~
\str_if_eq:nnF {#1} {??} { ~in~hook~'#1' }. ~
Perhaps~ a~ misspelling?
}
{
The~ relation~ used~ not~ known~ to~ the~ system.~ Allowed~ values~ are~
'before'~ or~ '<',~
'after'~ or~ '>',~
'incompatible-warning',~
'incompatible-error',~
'voids'~ or~
'unrelated'.
}
% \end{macrocode}
%
% \begin{macrocode}
\msg_new:nnnn { hooks } { rule-too-late }
{
Sorting~rule~for~'#1'~hook~applied~too~late.\\
Try~setting~this~rule~earlier.
}
{
You~tried~to~set~the~ordering~of~hook~'#1'~using\\
\ \ \iow_char:N\\DeclareHookRule{#1}{#2}{#3}{#4}\\
but~hook~'#1'~was~already~used~as~a~one-time~hook,~
thus~sorting~is\\
no~longer~possible.~Declare~the~rule~
before~the~hook~is~used.
}
% \end{macrocode}
%
% \begin{macrocode}
\msg_new:nnnn { hooks } { misused-top-level }
{
Illegal~use~of~\iow_char:N \\AddToHook{#1}[top-level]{...}.\\
'top-level'~is~reserved~for~the~user's~document.
}
{
The~'top-level'~label~is~meant~for~user~code~only,~and~should~only~
be~used~(sparingly)~in~the~main~document.~Use~the~default~label~
'\@@_currname_or_default:'~for~this~\@cls@pkg,~or~another~
suitable~label.
}
% \end{macrocode}
%
% \begin{macrocode}
\msg_new:nnn { hooks } { set-top-level }
{
You~cannot~change~the~default~label~#1~'top-level'.~Illegal \\
\use:nn { ~ } { ~ } \iow_char:N \\#2{#3} \\
\msg_line_context:.
}
% \end{macrocode}
%
% \begin{macrocode}
\msg_new:nnn { hooks } { extra-pop-label }
{
Extra~\iow_char:N \\PopDefaultHookLabel. \\
This~command~will~be~ignored.
}
\msg_new:nnn { hooks } { missing-pop-label }
{
Missing~\iow_char:N \\PopDefaultHookLabel. \\
The~label~'#1'~was~pushed~but~never~popped.~Something~is~wrong.
}
% \end{macrocode}
%
% \begin{macrocode}
\msg_new:nnn { latex2e } { should-not-happen }
{
This~should~not~happen.~#1 \\
Please~report~at~https://github.com/latex3/latex2e.
}
% \end{macrocode}
%
%
%
% \begin{macrocode}
\msg_new:nnn { hooks } { activate-disabled }
{
Cannot~ activate~ hook~ '#1'~ because~ it~ is~ disabled!
}
% \end{macrocode}
%
% \begin{macrocode}
\msg_new:nnn { hooks } { cannot-remove }
{
Cannot~remove~chunk~'#2'~from~hook~'#1'~because~
\@@_if_structure_exist:nTF {#1}
{ it~does~not~exist~in~that~hook. }
{ the~hook~does~not~exist. }
}
% \end{macrocode}
%
% \begin{macrocode}
\msg_new:nnn { hooks } { generic-deprecated }
{
Generic~hook~'#1/#2/#3'~is~deprecated. \\
Use~hook~'#1/#3/#2'~instead.
}
% \end{macrocode}
%
%
% \subsection{\LaTeXe{} package interface commands}
%
%
%
% \begin{macro}{\NewHook,\NewReversedHook,\NewMirroredHookPair}
% Declaring new hooks \ldots
% \begin{macrocode}
\NewDocumentCommand \NewHook { m }{ \hook_new:n {#1} }
\NewDocumentCommand \NewReversedHook { m }{ \hook_new_reversed:n {#1} }
\NewDocumentCommand \NewMirroredHookPair { mm }{ \hook_new_pair:nn {#1}{#2} }
% \end{macrocode}
% \end{macro}
%
%
%
% \begin{macrocode}
%<latexrelease>\IncludeInRelease{2021/06/01}%
%<latexrelease> {\hook_activate_generic:n}{Providing~hooks}
% \end{macrocode}
%
% \begin{macro}{\ActivateGenericHook}
% Providing new hooks \ldots
% \changes{v1.0m}{2021/04/29}{Add \cs{ProvideHook} etc.}
% \changes{v1.0o}{2021/08/02}{Change name}
% \begin{macrocode}
\NewDocumentCommand \ActivateGenericHook { m }{ \hook_activate_generic:n {#1} }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\DisableGenericHook}
% Disabling a generic hook.
% \changes{v1.0o}{2021/08/02}{Change name}
% \begin{macrocode}
\NewDocumentCommand \DisableGenericHook { m }{ \hook_disable_generic:n {#1} }
% \end{macrocode}
% \end{macro}
%
% \begin{macrocode}
%<latexrelease>\EndIncludeInRelease
%<latexrelease>\IncludeInRelease{2020/10/01}
%<latexrelease> {\hook_activate_generic:n}{Providing~hooks}
%<latexrelease>
%<latexrelease>\def \ActivateGenericHook#1{}
%<latexrelease>
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
%
%
% \begin{macro}{\AddToHook}
% \begin{macrocode}
\NewDocumentCommand \AddToHook { m o +m }
{ \hook_gput_code:nnn {#1} {#2} {#3} }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\AddToHookNext}
% \begin{macrocode}
\NewDocumentCommand \AddToHookNext { m +m }
{ \hook_gput_next_code:nn {#1} {#2} }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\ClearHookNext}
% \changes{v1.0o}{2021/07/27}{Macro added}
% \begin{macrocode}
\NewDocumentCommand \ClearHookNext { m }
{ \hook_gclear_next_code:n {#1} }
% \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\RemoveFromHook}
% \begin{macrocode}
\NewDocumentCommand \RemoveFromHook { m o }
{ \hook_gremove_code:nn {#1} {#2} }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\SetDefaultHookLabel}
% \begin{macro}{\PushDefaultHookLabel}
% \begin{macro}{\PopDefaultHookLabel}
% Now define a wrapper that replaces the top of the stack with the
% argument, and updates \cs{g_@@_hook_curr_name_tl} accordingly.
% \begin{macrocode}
\NewDocumentCommand \SetDefaultHookLabel { m }
{ \@@_set_default_hook_label:n {#1} }
%
% The label is only automatically updated with \cs{@onefilewithoptions}
% (\cs{usepackage} and \cs{documentclass}), but some packages, like
% Ti\emph{k}Z, define package-like interfaces, like
% \cs{usetikzlibrary} that are wrappers around \cs{input}, so they
% inherit the default label currently in force (usually |top-level|,
% but it may change if loaded in another package). To provide a
% package-like behavior also for hooks in these files, we provide
% high-level access to the default label stack.
% \begin{macrocode}
\NewDocumentCommand \PushDefaultHookLabel { m }
{ \@@_curr_name_push:n {#1} }
\NewDocumentCommand \PopDefaultHookLabel { }
{ \@@_curr_name_pop: }
% \end{macrocode}
%
% The current label stack holds the labels for all files but the
% current one (more or less like \cs{@currnamestack}), and the current
% label token list, \cs{g_@@_hook_curr_name_tl}, holds the label for
% the current file. However \cs{@pushfilename} happens before
% \cs{@currname} is set, so we need to look ahead to get the
% \cs{@currname} for the label. \pkg{expl3} also requires the current
% file in \cs{@pushfilename}, so here we abuse
% \cs{@expl@push@filename@aux@@@@} to do \cs{@@_curr_name_push:n}.
% \begin{macrocode}
\cs_gset_protected:Npn \@expl@push@filename@aux@@@@ #1#2#3
{
\@@_curr_name_push:n {#3}
\str_gset:Nx \g_file_curr_name_str {#3}
#1 #2 {#3}
}
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
%
%
% \begin{macro}{\UseHook,\UseOneTimeHook}
% Avoid the overhead of \pkg{xparse} and its protection that we
% don't want here (since the hook should vanish without trace if empty)!
% \begin{macrocode}
\cs_new:Npn \UseHook { \hook_use:n }
\cs_new:Npn \UseOneTimeHook { \hook_use_once:n }
% \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}{\ShowHook,\LogHook}
% \begin{macrocode}
\cs_new_protected:Npn \ShowHook { \hook_show:n }
\cs_new_protected:Npn \LogHook { \hook_log:n }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\DebugHooksOn,\DebugHooksOff}
%
% \begin{macrocode}
\cs_new_protected:Npn \DebugHooksOn { \hook_debug_on: }
\cs_new_protected:Npn \DebugHooksOff { \hook_debug_off: }
% \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}{\DeclareHookRule}
%
% \begin{macrocode}
\NewDocumentCommand \DeclareHookRule { m m m m }
{ \hook_gset_rule:nnnn {#1}{#2}{#3}{#4} }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\DeclareDefaultHookRule}
% This declaration is only supported before \verb=\begin{document}=.
% \begin{macrocode}
\NewDocumentCommand \DeclareDefaultHookRule { m m m }
{ \hook_gset_rule:nnnn {??}{#1}{#2}{#3} }
\@onlypreamble\DeclareDefaultHookRule
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\ClearHookRule}
% A special setup rule that removes an existing relation.
% Basically {@@_rule_gclear:nnn} plus fixing the property list for debugging.
% \fmiinline{Needs perhaps an L3 interface, or maybe it should get dropped?}
% \begin{macrocode}
\NewDocumentCommand \ClearHookRule { m m m }
{ \hook_gset_rule:nnnn {#1}{#2}{unrelated}{#3} }
% \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[EXP]{\IfHookEmptyTF}
% Here we avoid the overhead of \pkg{xparse}, since \cs{IfHookEmptyTF}
% is used in \cs{end} (that is, every \LaTeX{} environment). As a
% further optimization, use \cs{let} rather than \cs{def} to avoid one
% expansion step.
% \begin{macrocode}
\cs_new_eq:NN \IfHookEmptyTF \hook_if_empty:nTF
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\IfHookExistsTF}
% Marked for removal and no longer documented in the doc section!
% \phoinline{\cs{IfHookExistsTF} is used in \texttt{jlreq.cls},
% \texttt{pxatbegshi.sty}, \texttt{pxeverysel.sty},
% \texttt{pxeveryshi.sty}, so the public name may be an alias of the
% internal conditional for a while. Regardless, those packages' use for
% \cs{IfHookExistsTF} is not really correct and can be changed.}
% \begin{macrocode}
\cs_new_eq:NN \IfHookExistsTF \@@_if_usable:nTF
% \end{macrocode}
% \end{macro}
%
%
%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% \subsection{Deprecated that needs cleanup at some point}
%
% \changes{v1.0p}{2021/08/20}{Added deprecation warnings for
% old generic hook commands (gh/638)}
%
% \begin{macro}{
% \hook_disable:n,
% \hook_provide:n,
% \hook_provide_reversed:n,
% \hook_provide_pair:nn,
% \@@_activate_generic_reversed:n,
% \@@_activate_generic_pair:nn,
% }
% Deprecated.
% \begin{macrocode}
\cs_new_protected:Npn \hook_disable:n
{
\@@_deprecated_warn:nn
{ hook_disable:n }
{ hook_disable_generic:n }
\hook_disable_generic:n
}
\cs_new_protected:Npn \hook_provide:n
{
\@@_deprecated_warn:nn
{ hook_provide:n }
{ hook_activate_generic:n }
\hook_activate_generic:n
}
\cs_new_protected:Npn \hook_provide_reversed:n
{
\@@_deprecated_warn:nn
{ hook_provide_reversed:n }
{ hook_activate_generic:n }
\@@_activate_generic_reversed:n
}
\cs_new_protected:Npn \hook_provide_pair:nn
{
\@@_deprecated_warn:nn
{ hook_provide_pair:nn }
{ hook_activate_generic:n }
\@@_activate_generic_pair:nn
}
\cs_new_protected:Npn \@@_activate_generic_reversed:n #1
{ \@@_normalize_hook_args:Nn \@@_activate_generic:nn {#1} { - } }
\cs_new_protected:Npn \@@_activate_generic_pair:nn #1#2
{ \hook_activate_generic:n {#1} \@@_activate_generic_reversed:n {#2} }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{
% \DisableHook,
% \ProvideHook,
% \ProvideReversedHook,
% \ProvideMirroredHookPair,
% }
% Deprecated.
% \begin{macrocode}
\cs_new_protected:Npn \DisableHook
{
\@@_deprecated_warn:nn
{ DisableHook }
{ DisableGenericHook }
\hook_disable_generic:n
}
\cs_new_protected:Npn \ProvideHook
{
\@@_deprecated_warn:nn
{ ProvideHook }
{ ActivateGenericHook }
\hook_activate_generic:n
}
\cs_new_protected:Npn \ProvideReversedHook
{
\@@_deprecated_warn:nn
{ ProvideReversedHook }
{ ActivateGenericHook }
\@@_activate_generic_reversed:n
}
\cs_new_protected:Npn \ProvideMirroredHookPair
{
\@@_deprecated_warn:nn
{ ProvideMirroredHookPair }
{ ActivateGenericHook }
\@@_activate_generic_pair:nn
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_deprecated_warn:nn}
% Warns about a deprecation, telling what should be used instead.
% \begin{macrocode}
\cs_new_protected:Npn \@@_deprecated_warn:nn #1 #2
{ \msg_warning:nnnn { hooks } { deprecated } {#1} {#2} }
\msg_new:nnn { hooks } { deprecated }
{
Command~\iow_char:N\\#1~is~deprecated~and~will~be~removed~in~a~
future~release. \\ \\
Use~\iow_char:N\\#2~instead.
}
% \end{macrocode}
% \end{macro}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
% \subsection{Internal commands needed elsewhere}
%
% Here we set up a few horrible (but consistent) \LaTeXe{} names to
% allow for internal commands to be used outside this module. We
% have to unset the \texttt{@\/@} since we want double ``at'' sign
% in place of double underscores.
%
% \begin{macrocode}
%<@@=>
% \end{macrocode}
%
% \begin{macro}{\@expl@@@initialize@all@@,
% \@expl@@@hook@curr@name@pop@@}
%
% \InternalDetectionOff
% \begin{macrocode}
\cs_new_eq:NN \@expl@@@initialize@all@@
\__hook_initialize_all:
% \end{macrocode}
%
% \begin{macrocode}
\cs_new_eq:NN \@expl@@@hook@curr@name@pop@@
\__hook_curr_name_pop:
% \end{macrocode}
% \InternalDetectionOn
% \end{macro}
%
% Rolling back here doesn't undefine the interface commands as they
% may be used in packages without rollback functionality. So we
% just make them do nothing which may or may not work depending on
% the code usage.
% \changes{v1.0d}{2020/10/04}{Definition \cs{AddToHookNext} was supposed
% to be for \cs{AddToHook} vice versa (gh/401)}
% \begin{macrocode}
%
%<latexrelease>\IncludeInRelease{0000/00/00}%
%<latexrelease> {lthooks}{The~hook~management}%
%<latexrelease>
%<latexrelease>\def \NewHook#1{}
%<latexrelease>\def \NewReversedHook#1{}
%<latexrelease>\def \NewMirroredHookPair#1#2{}
%<latexrelease>
%<latexrelease>\def \DisableGenericHook #1{}
%<latexrelease>
%<latexrelease>\long\def\AddToHookNext#1#2{}
%<latexrelease>
%<latexrelease>\def\AddToHook#1{\@gobble@AddToHook@args}
%<latexrelease>\providecommand\@gobble@AddToHook@args[2][]{}
%<latexrelease>
%<latexrelease>\def\RemoveFromHook#1{\@gobble@RemoveFromHook@arg}
%<latexrelease>\providecommand\@gobble@RemoveFromHook@arg[1][]{}
%<latexrelease>
%<latexrelease>\def \UseHook #1{}
%<latexrelease>\def \UseOneTimeHook #1{}
%<latexrelease>\def \ShowHook #1{}
%<latexrelease>\let \DebugHooksOn \@empty
%<latexrelease>\let \DebugHooksOff\@empty
%<latexrelease>
%<latexrelease>\def \DeclareHookRule #1#2#3#4{}
%<latexrelease>\def \DeclareDefaultHookRule #1#2#3{}
%<latexrelease>\def \ClearHookRule #1#2#3{}
% \end{macrocode}
% If the hook management is not provided we make the test for existence
% false and the test for empty true in the hope that this is most
% of the time reasonable. If not a package would need to guard
% against running in an old kernel.
% \begin{macrocode}
%<latexrelease>\long\def \IfHookExistsTF #1#2#3{#3}
%<latexrelease>\long\def \IfHookEmptyTF #1#2#3{#2}
%<latexrelease>
%<latexrelease>\EndModuleRelease
\ExplSyntaxOff
%</2ekernel|latexrelease>
% \end{macrocode}
%
%
% \Finale
%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\endinput
^^A Needed for emacs
^^A
^^A Local Variables:
^^A mode: latex
^^A coding: utf-8-unix
^^A End: