summaryrefslogtreecommitdiff
path: root/macros/latex-dev/base/lthooks.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex-dev/base/lthooks.dtx')
-rw-r--r--macros/latex-dev/base/lthooks.dtx2769
1 files changed, 2462 insertions, 307 deletions
diff --git a/macros/latex-dev/base/lthooks.dtx b/macros/latex-dev/base/lthooks.dtx
index 1255c63a8c..c0efe2c903 100644
--- a/macros/latex-dev/base/lthooks.dtx
+++ b/macros/latex-dev/base/lthooks.dtx
@@ -1,7 +1,7 @@
% \iffalse meta-comment
%
% Copyright (C) 2020-2023
-% Frank Mittelbach, Phelype Oleinik & LaTeX Team
+% Frank Mittelbach, Phelype Oleinik, The LaTeX Project
%
% This file is part of the LaTeX base system.
% -------------------------------------------
@@ -31,8 +31,8 @@
%%% From File: lthooks.dtx
%
% \begin{macrocode}
-\def\lthooksversion{v1.0v}
-\def\lthooksdate{2022/06/15}
+\def\lthooksversion{v1.1d}
+\def\lthooksdate{2023/05/21}
% \end{macrocode}
%
%<*driver>
@@ -148,7 +148,6 @@
% 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}
@@ -156,7 +155,45 @@
% 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 \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}{\NewHookWithArguments}
+% \begin{syntax}
+% \cs{NewHookWithArguments} \Arg{hook} \Arg{number}
+% \end{syntax}
+% Creates a new \meta{hook} whose code takes \meta{number} arguments,
+% and otherwise works exactly like \cs{NewHook}.
+% Section~\ref{sec:hook-args} explains hooks with arguments.
+%
+% 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}{\NewReversedHookWithArguments}
+% \begin{syntax}
+% \cs{NewReversedHookWithArguments} \Arg{hook} \Arg{number}
+% \end{syntax}
+% Like \cs{NewReversedHook}, but creates a hook whose code takes
+% \meta{number} arguments.
+% Section~\ref{sec:hook-args} explains hooks with arguments.
+%
+% 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}{\NewMirroredHookPairWithArguments}
+% \begin{syntax}
+% \cs{NewMirroredHookPairWithArguments} \Arg{hook-1} \Arg{hook-2} \Arg{number}
+% \end{syntax}
+% A shorthand for
+% \cs{NewHookWithArguments}\Arg{hook-1}\Arg{number}\\
+% \cs{NewReversedHookWithArguments}\Arg{hook-2}\Arg{number}.
+% Section~\ref{sec:hook-args} explains hooks with arguments.
+%
+% The \meta{hook} can be specified using the dot-syntax to denote
% the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
@@ -173,9 +210,9 @@
% \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.
+% After this declaration\footnotemark{} the \meta{hook} is no longer
+% usable: Any further attempt to add 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
@@ -215,7 +252,7 @@
% \begin{syntax}
% \cs{UseHook} \Arg{hook}
% \end{syntax}
-% Execute the hook code inside a command or environment.
+% Execute the code stored in the \meta{hook}.
%
% 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
@@ -227,14 +264,30 @@
% A leading |.| is treated literally.
% \end{function}
%
+% \begin{function}{\UseHookWithArguments}
+% \begin{syntax}
+% \cs{UseHookWithArguments} \Arg{hook} \Arg{number} \Arg{arg_1} \ldots \Arg{arg_n}
+% \end{syntax}
+% Execute the code stored in the \meta{hook} and pass the arguments
+% \Arg{arg_1} through \Arg{arg_n} to the \meta{hook}. Otherwise, it
+% works exactly like \cs{UseHook}.
+% The \meta{number} should be the number of arguments declared for
+% the hook. If the hook is not declared, this command does nothing
+% and it will remove \meta{number} items from the input.
+% Section~\ref{sec:hook-args} explains hooks with arguments.
+%
+% 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.,
+% \verb=\end{document}=. From that point onwards, 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
@@ -250,15 +303,30 @@
% 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.
+% \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
+% 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}
%
+% \begin{function}{\UseOneTimeHookWithArguments}
+% \begin{syntax}
+% \cs{UseOneTimeHookWithArguments} \Arg{hook} \Arg{number} \Arg{arg_1} \ldots \Arg{arg_n}
+% \end{syntax}
+% Works exactly like \cs{UseOneTimeHook}, but passes arguments
+% \Arg{arg_1} through \Arg{arg_n} to the \meta{hook}.
+% The \meta{number} should be the number of arguments declared for
+% the hook. If the hook is not declared, this command does nothing
+% and it will remove \meta{number} items from the input.
+%
+% It should be noted that after a one-time hook is used, it is no
+% longer possible to use \cs{AddToHookWithArguments} or similar with
+% that hook. \cs{AddToHook} continues to work as normal.
+% Section~\ref{sec:hook-args} explains hooks with arguments.
% \end{function}
%
%
@@ -294,6 +362,34 @@
% See section~\ref{sec:default-label}.
% \end{function}
%
+% \begin{function}{\AddToHookWithArguments}
+% \begin{syntax}
+% \cs{AddToHookWithArguments} \Arg{hook}\oarg{label}\Arg{code}
+% \end{syntax}
+% Works exactly like \cs{AddToHook}, except that the \meta{code} can
+% access the arguments passed to the hook using \verb|#1|, \verb|#2|,
+% \ldots, \verb|#n| (up to the number of arguments declared for the
+% hook). If the \meta{code} should contain \emph{parameter tokens}
+% (\verb|#|) that are not supposed to be understood as the arguments
+% of the hook, such tokens should be doubled. For example, with
+% \cs{AddToHook} one can write:
+%\begin{verbatim}
+% \AddToHook{myhook}{\def\foo#1{Hello, #1!}}
+%\end{verbatim}
+% but to achieve the same with \cs{AddToHookWithArguments}, one should
+% write:
+%\begin{verbatim}
+% \AddToHookWithArguments{myhook}{\def\foo##1{Hello, ##1!}}
+%\end{verbatim}
+% because in the latter case, \verb|#1| refers to the first argument
+% of the hook \hook{myhook}.
+% Section~\ref{sec:hook-args} explains hooks with arguments.
+%
+% 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}
@@ -403,6 +499,19 @@
% \end{function}\footnotetext{There is
% no mechanism to reorder such code chunks (or delete them).}
%
+% \begin{function}{\AddToHookNextWithArguments}
+% \begin{syntax}
+% \cs{AddToHookNextWithArguments} \Arg{hook}\Arg{code}
+% \end{syntax}
+% Works exactly like \cs{AddToHookNext}, but the \meta{code} can
+% contain references to the arguments of the \meta{hook} as described
+% for \cs{AddToHookWithArguments} above.
+% Section~\ref{sec:hook-args} explains hooks with arguments.
+%
+% 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}{\ClearHookNext}
@@ -511,10 +620,10 @@
% 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
+% the code in logical parts, but still use the main package name as the
% \meta{label}, then the \meta{default label} can be set using
-% \cs{SetDefaultHookLabel} or
-% \cs{PushDefaultHookLabel}\verb={..}=\,\ldots\cs{PopDefaultHookLabel}.
+% \cs{PushDefaultHookLabel}\verb={...}=\,\ldots\cs{PopDefaultHookLabel}
+% or \cs{SetDefaultHookLabel}\verb={...}=.
%
% \begin{function}{\PushDefaultHookLabel,\PopDefaultHookLabel}
% \begin{syntax}
@@ -906,8 +1015,11 @@
% 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{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}
@@ -923,6 +1035,27 @@
% the current package name. See section~\ref{sec:default-label}.
% \end{function}
%
+% \begin{function}{
+% \hook_new_with_args:nn,
+% \hook_new_reversed_with_args:nn,
+% \hook_new_pair_with_args:nnn
+% }
+% \begin{syntax}
+% \cs{hook_new_with_args:nn} \Arg{hook} \Arg{number}
+% \cs{hook_new_reversed_with_args:nn} \Arg{hook} \Arg{number}
+% \cs{hook_new_pair_with_args:nnn} \Arg{hook-1} \Arg{hook-2} \Arg{number}
+% \end{syntax}
+% Creates a new \meta{hook} with normal or reverse ordering of code
+% chunks, that takes \meta{number} arguments from the input stream
+% when it is used. \cs{hook_new_pair_with_args: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}
@@ -958,37 +1091,62 @@
%
%
%
-% \begin{function}{\hook_use:n}
+% \begin{function}{\hook_use:n,\hook_use:nnw}
% \begin{syntax}
% \cs{hook_use:n} \Arg{hook}
+% \cs{hook_use:nnw} \Arg{hook} \Arg{number} \Arg{arg_1} \ldots \Arg{arg_n}
% \end{syntax}
% Executes the \Arg{hook} code followed (if set up) by the code for next
% invocation only, then empties that next invocation code.
+% \cs{hook_use:nnw} should be used for hooks declared with arguments,
+% and should be followed by as many brace groups as the declared
+% number of arguments.
+% The \meta{number} should be the number of arguments declared for
+% the hook. If the hook is not declared, this command does nothing
+% and it will remove \meta{number} items from the input.
%
% 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{function}{\hook_use_once:n,\hook_use_once:nnw}
% \begin{syntax}
% \cs{hook_use_once:n} \Arg{hook}
+% \cs{hook_use_once:nnw} \Arg{hook} \Arg{number} \Arg{arg_1} \ldots \Arg{arg_n}
% \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.
+% \cs{hook_use_once:nnw} should be used for hooks declared with arguments,
+% and should be followed by as many brace groups as the declared
+% number of arguments.
+% The \meta{number} should be the number of arguments declared for
+% the hook. If the hook is not declared, this command does nothing
+% and it will remove \meta{number} items from the input.
%
% 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{function}{
+% \hook_gput_code:nnn,
+% \hook_gput_code_with_args:nnn
+% }
% \begin{syntax}
% \cs{hook_gput_code:nnn} \Arg{hook} \Arg{label} \Arg{code}
+% \cs{hook_gput_code_with_args: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 \cs{hook_gput_code_with_args:nnn} is used, the \meta{code}
+% can access the arguments passed to \cs{hook_use:nnw}
+% (or~\cs{hook_use_once:nnw}) with \verb|#1|, \verb|#2|, \ldots,
+% \verb|#n| (up to the number of arguments declared for the hook).
+% In that case, if an actual parameter token should be added to the
+% code, it should be doubled.
+%
% 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
@@ -999,18 +1157,26 @@
% See section~\ref{sec:default-label}.
% \end{function}
%
-% \begin{function}
-% {\hook_gput_next_code:nn}
+% \begin{function}{
+% \hook_gput_next_code:nn,
+% \hook_gput_next_code_with_args: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.
%
+% If \cs{hook_gput_next_code_with_args:nn} is used, the \meta{code}
+% can access the arguments passed to \cs{hook_use:nnw}
+% (or~\cs{hook_use_once:nnw}) with \verb|#1|, \verb|#2|, \ldots,
+% \verb|#n| (up to the number of arguments declared for the hook).
+% In that case, if an actual parameter token should be added to the
+% code, it should be doubled.
+%
% 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}.
%
@@ -1019,8 +1185,7 @@
% \end{function}
%
%
-% \begin{function}
-% {\hook_gclear_next_code:n}
+% \begin{function}{\hook_gclear_next_code:n}
% \begin{syntax}
% \cs{hook_gclear_next_code:n} \Arg{hook}
% \end{syntax}
@@ -1240,7 +1405,7 @@
% \begin{itshape}\begin{color}{blue} ...
% \end{itshape}\end{color}
%\end{verbatim}
-% and an error message that \verb=\begin{color}= ended by
+% and an error message saying that \verb=\begin{color}= was 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
@@ -1377,9 +1542,9 @@
% 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
+% For example, \pkg{babel} provides hooks such as
+% \hook{babel/\meta{language}/afterextras}. However, 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
@@ -1396,6 +1561,125 @@
% conciderably.
%
%
+% \subsection{Hooks with arguments}
+% \label{sec:hook-args}
+%
+% Sometimes it is necessary to pass contextual information to a hook,
+% and, for one reason or another, it is not feasible to store such
+% information in macros. To serve this purpose, hooks can be
+% declared with arguments, so that the programmer can pass along the
+% data necessary for the code in the hook to function properly.
+%
+% A hook with arguments works mostly like a regular hook, and most
+% commands that work for regular hooks, also work for hooks that take
+% arguments. The differences are when the hook is declared
+% (\cs{NewHookWithArguments} is used instead of \cs{NewHook}), then
+% code can be added with both \cs{AddToHook} and
+% \cs{AddToHookWithArguments}, and when the hook is used
+% (\cs{UseHookWithArguments} instead of \cs{UseHook}).
+%
+% \medskip
+%
+% A hook with arguments must be declared as such (before it is first
+% used, as all regular hooks) using
+% \cs{NewHookWithArguments}\Arg{hook}\Arg{number}. All code added to
+% that hook can then use \verb|#1| to access the first argument,
+% \verb|#2| to access the second, and so forth up to the number of
+% arguments declared. However, it is still possible to add code with
+% references to the arguments of a hook that was not yet declared
+% (we will discuss that later). At their core, hooks are macros, so
+% \TeX's limit of 9~arguments applies, and a low-level \TeX{} error
+% is raised if you try to reference an argument number that doesn't
+% exist.
+%
+% \medskip
+%
+% To use a hook with arguments, just write
+% \cs{UseHookWithArguments}\Arg{hook}\Arg{number} followed by a
+% braced list of the arguments. For example, if the hook \hook{test}
+% takes three arguments, write:
+%\begin{verbatim}
+% \UseHookWithArguments{test}{3}{arg-1}{arg-2}{arg-3}
+%\end{verbatim}
+% then, in the \meta{code} of the hook, all instances of \verb|#1|
+% will be replaced by \verb|arg-1|, \verb|#2| by \verb|arg-2| and so
+% on. If, at the point of usage, the programmer provides more
+% arguments than the hook is declared to take, the excess arguments
+% are simply ignored by the hook. Behaviour is
+% unpredictable\footnote
+% {The hook \emph{will} take the declared number of arguments, and
+% what will happen depends on what was grabbed, and what the hook
+% code does with its arguments.}
+% if too few arguments are provided. If the hook isn't declared,
+% \meta{number} arguments are removed from the input stream.
+%
+% \medskip
+%
+% Adding code to a hook with arguments can be done with
+% \cs{AddToHookWithArguments} as well as with the regular
+% \cs{AddToHook}, to achieve different outcomes. The main difference
+% when it comes to adding code to a hook, in this case, is firstly
+% the possibility of accessing a hook's arguments, of course, and
+% second, how parameter tokens (\verb|#|$_6$) are treated.
+%
+% Using \cs{AddToHook} in a hook that takes arguments will work as it
+% does for all other hooks. This allows a package developer to add
+% arguments to a hook that otherwise had none without having to worry
+% about compatibility. This means that, for example:
+%\begin{verbatim}
+% \AddToHook{test}{\def\foo#1{Hello, #1!}}
+%\end{verbatim}
+% will define the same macro \cs[no-index]{foo} regardless if the
+% hook \hook{test} takes arguments or not.
+%
+% Using \cs{AddToHookWithArguments} allows the \meta{code} added to
+% access the arguments of the hook with \verb|#1|, \verb|#2|, and so
+% forth, up to the number of the arguments declared in the hook.
+% This means that if one wants to add a \verb|#|$_6$ to the
+% \meta{code} that token must be doubled in the input. The same
+% definition from above, using \cs{AddToHookWithArguments}, needs to
+% be rewritten:
+%\begin{verbatim}
+% \AddToHookWithArguments{test}{\def\foo##1{Hello, ##1!}}
+%\end{verbatim}
+%
+% Extending the above example to use the hook arguments, we could
+% rewrite something like (now from declaration to usage, to get the
+% whole picture):
+%\begin{verbatim}
+% \NewHookWithArguments{test}{1}
+% \AddToHookWithArguments{test}{%
+% \typeout{Defining foo with "#1"}
+% \def\foo##1{Hello, ##1! Some text after: #1}%
+% }
+% \UseHook{test}{Howdy!}
+% \ShowCommand\foo
+%\end{verbatim}
+% Running the code above prints in the terminal:
+%\begin{verbatim}
+% Defining foo with "Howdy!"
+% > \foo=macro:
+% #1->Hello, #1! Some text after: Howdy!.
+%\end{verbatim}
+% Note how \verb|##1| in the call to \cs{AddToHookWithArguments}
+% became \verb|#1|, and the \verb|#1| was replaced by the argument
+% passed to the hook. Should the hook be used again, with a
+% different argument, the definition would naturally change.
+%
+% \bigskip
+%
+% It is possible to add code referencing a hook's arguments before
+% such hook is declared and the number of hooks is fixed. However,
+% if some code is added to the hook, that references more arguments
+% than will be declared for the hook, there will be a low-level
+% \TeX{} error about an \enquote{Illegal parameter number} at the
+% time the hook is declared, which will be hard to track down because
+% at that point \TeX{} can't know whence the offending code came
+% from. Thus it is important that package writers explicitly
+% document how many arguments (if any) each hook can take, so users
+% of those packages know how many arguments can be referenced, and
+% equally important, what each argument means.
+%
% \subsection{Private \LaTeX{} kernel hooks}
%
% There are a few places where it is absolutely essential for
@@ -1461,7 +1745,7 @@
% 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
+% This way one can add 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
@@ -1746,10 +2030,10 @@
%
% \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
+% \LaTeXe{} has always provided \cs{AtEndDocument} to add code to the
+% \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
+% 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.
@@ -2095,13 +2379,37 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[int]{\tl_gremove_once:Nx,\tl_show:x,\tl_log:x}
+% \begin{macro}{\c_@@_empty_tl}
+% \begin{macro}{\c_@@_nine_parameters_tl}
+% An empty token list, and one containing nine parameters.
+% \changes{v1.1a}{2023/04/06}
+% {Add auxiliary token lists (hook-args).}
+% \begin{macrocode}
+\tl_const:Nn \c_@@_empty_tl { }
+\tl_const:Nn \c_@@_nine_parameters_tl { #1#2#3#4#5#6#7#8#9 }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[int]{
+% \tl_gremove_once:Nx,
+% \tl_show:x,
+% \tl_log:x,
+% \tl_set:Ne,
+% \cs_replacement_spec:c,
+% \prop_put:Nne,
+% \str_count:e
+% }
% 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 }
+\cs_generate_variant:Nn \tl_set:Nn { Ne }
+\cs_generate_variant:Nn \cs_replacement_spec:N { c }
+\cs_generate_variant:Nn \prop_put:Nnn { Nne }
+\cs_generate_variant:Nn \str_count:n { e }
% \end{macrocode}
% \end{macro}
%
@@ -2112,15 +2420,20 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\@@_clean_to_scan:w}
+% \begin{macro}{
+% \@@_use_none_delimit_by_s_mark:w,
+% \@@_use_i_delimit_by_s_mark:nw
+% }
% Removes tokens until the next \cs{s_@@_mark}.
+% \changes{v1.1a}{2023/04/06}
+% {Use standard naming scheme (hook-args).}
% \begin{macrocode}
-\cs_new:Npn \@@_clean_to_scan:w #1 \s_@@_mark { }
+\cs_new:Npn \@@_use_none_delimit_by_s_mark:w #1 \s_@@_mark { }
+\cs_new:Npn \@@_use_i_delimit_by_s_mark:nw #1 #2 \s_@@_mark {#1}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\@@_tl_set:Nn,\@@_tl_set:Nx,
-% \@@_tl_set:cn,\@@_tl_set:cx}
+% \begin{macro}{\@@_tl_set:cn}
% 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:}
@@ -2130,44 +2443,43 @@
% 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
+% \changes{v1.0h}{2021/01/07}{Manually define some \pkg{l3tl} commands
% to work around \pkg{expl3} changes}
+% \changes{v1.1a}{2023/04/06}
+% {Clean-up unused variants (hook-args).}
% \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 }
+\cs_new_protected:Npn \@@_tl_set:cn #1#2
+ { \cs_set_nopar:cpx {#1} { \__kernel_exp_not:w {#2} } }
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\@@_tl_gset:Nn,\@@_tl_gset:No,\@@_tl_gset:Nx,
+% \begin{macro}{\@@_tl_gset:Nn,\@@_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:Nn { c, co }
\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}
+% \begin{macro}{
+% \@@_tl_gput_right:Nn,
+% \@@_tl_gput_right:Ne,
+% \@@_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 }
+\cs_generate_variant:Nn \@@_tl_gput_right:Nn { Ne, cn }
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\@@_tl_gput_left:Nn,\@@_tl_gput_left:No}
+% \begin{macro}{\@@_tl_gput_left:Nn}
% Same as above.
% \begin{macrocode}
\cs_new_protected:Npn \@@_tl_gput_left:Nn #1#2
@@ -2175,7 +2487,6 @@
\@@_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}
%
@@ -2202,7 +2513,11 @@
%
% \DescribeMacro{\g_@@_\meta{hook}_code_prop}
% \DescribeMacro{\@@\textvisiblespace\meta{hook}}
+% \DescribeMacro{\g_@@_\meta{hook}_reversed_tl}
+% \DescribeMacro{\g_@@_\meta{hook}_declared_tl}
+% \DescribeMacro{\g_@@_\meta{hook}_parameter_tl}
% \DescribeMacro{\@@_next\textvisiblespace\meta{hook}}
+% \DescribeMacro{\@@_toplevel\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
@@ -2232,12 +2547,17 @@
% 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
+% list serves as a marker for the hook being officially declared. Its
% existence is tested to raise an error in case another declaration
% is attempted.
%
+% \item[{\cs[no-index]{c_@@_\meta{hook}_parameter_tl}}] This token
+% list stores the parameter text for a declared hook (its existence
+% almost completely intersects the token list above), which is used
+% for managing hooks with arguments.
+%
% \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.
@@ -2367,45 +2687,92 @@
% \subsubsection{Setting hooks up}
%
%
-% \begin{macro}{\hook_new:n,\@@_new:n}
+% \begin{macro}{
+% \hook_new:n,
+% \hook_new_with_args:nn
+% }
+% \begin{macro}{@@_new:nn}
% The \cs{hook_new:n} declaration declares a new hook and expects
% the hook \meta{name} as its argument, e.g.,
% \hook{begindocument}.
+% \changes{v1.1a}{2023/04/06}
+% {Add \cs{hook_new_with_args:nn} (hook-args).}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_new_with_args:nn}
+%<latexrelease> {Hooks~with~args}
\cs_new_protected:Npn \hook_new:n #1
- { \@@_normalize_hook_args:Nn \@@_new:n {#1} }
+ { \@@_normalize_hook_args:Nn \@@_new:nn {#1} { 0 } }
+\cs_new_protected:Npn \hook_new_with_args:nn #1 #2
+ { \@@_normalize_hook_args:Nn \@@_new:nn {#1} {#2} }
% \end{macrocode}
%
% \begin{macrocode}
-\cs_new_protected:Npn \@@_new:n #1
+\cs_new_protected:Npn \@@_new:nn #1 #2
{
% \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.
+% \changes{v1.1d}{2023/05/21}
+% {Changes to allow support arguments in cmd hooks (cmd-args).}
% \begin{macrocode}
\@@_if_declared:nTF {#1}
{ \msg_error:nnn { hooks } { exists } {#1} }
{
\tl_new:c { g_@@_#1_declared_tl }
- \@@_make_usable:n {#1}
+ \cs_undefine:c { @@~#1 }
+ \cs_undefine:c { c_@@_#1_parameter_tl }
+ \@@_make_usable:nn {#1} {#2}
+% \end{macrocode}
+% In case there is already code in a hook, but it's undeclared, run
+% \cs{@@_update_hook_code:n} to make it ready to be executed (see test
+% \texttt{lthooks-034}).
+% \changes{v1.1a}{2023/04/06}
+% {Update hook code after declaring.}
+% \begin{macrocode}
+ \@@_update_hook_code:n {#1}
}
}
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_new_with_args:nn}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_gset_protected:Npn \hook_new:n #1
+%<latexrelease> { \@@_normalize_hook_args:Nn \@@_new:n {#1} }
+%<latexrelease>\cs_undefine:N \@@_new:nn
+%<latexrelease>\cs_gset_protected:Npn \@@_new:n #1
+%<latexrelease> {
+%<latexrelease> \@@_if_declared:nTF {#1}
+%<latexrelease> { \msg_error:nnn { hooks } { exists } {#1} }
+%<latexrelease> {
+%<latexrelease> \tl_new:c { g_@@_#1_declared_tl }
+%<latexrelease> \@@_make_usable:n {#1}
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease>\cs_gset_protected:Npn \hook_new_with_args:nn #1 { }
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
%
%
-% \begin{macro}{\@@_make_usable:n}
+% \begin{macro}{\@@_make_usable:nn}
%
% 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.
+% \cs{hook_gput_code:nnn} when adding code to a generic hook.
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
% \begin{macrocode}
-\cs_new_protected:Npn \@@_make_usable:n #1
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_make_usable:nn}
+%<latexrelease> {Hooks~with~args}
+\cs_new_protected:Npn \@@_make_usable:nn #1 #2
{
% \end{macrocode}
% Now we check if the hook's data structure can be safely created
@@ -2414,29 +2781,70 @@
% allocate the necessary data structures for the new hook,
% otherwise just do nothing.
% \begin{macrocode}
- \tl_if_exist:cF { @@~#1 }
+ \@@_if_usable:nF {#1}
{
\seq_gput_right:Nn \g_@@_all_seq {#1}
% \end{macrocode}
+% Here we'll define the \cs[no-index]{c_@@_\meta{hook}_parameter_tl}
+% to hold a run of parameters up to the number of arguments of the
+% hook (\verb|#2|).
+% \begin{macrocode}
+ \__kernel_cs_parm_from_arg_count:nnF
+ { \tl_const:cn { c_@@_#1_parameter_tl } } {#2}
+ {
+ \msg_error:nnnn { hooks } { too-many-args } {#1} {#2}
+ \tl_const:cx { c_@@_#1_parameter_tl }
+ { \exp_not:V \c_@@_nine_parameters_tl }
+ }
+% \end{macrocode}
+% After that, use \cs{@@_normalise_cs_args:nn} to correct the number
+% of parameters of the macros
+% \cs[no-index]{@@_toplevel\textvisiblespace\meta{hook}} and
+% \cs[no-index]{@@_next\textvisiblespace\meta{hook}}. We need to be
+% able to add
+% code with arguments to a hook without prior knowledge of the number
+% of arguments of that hook, so \pkg{lthooks} assumes~9 until the
+% hook is properly declared and the number of arguments is known.
+% \cs{@@_normalise_cs_args:nn} does the normalisation by using the
+% \cs[no-index]{c_@@_\meta{hook}_parameter_tl} defined just above.
+% \begin{macrocode}
+ \@@_normalise_cs_args:nn { _toplevel } {#1}
+ \@@_normalise_cs_args:nn { _next } {#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 }
+ \@@_code_gset:nn {#1} { }
% \end{macrocode}
% Now ensure that the base data structure for the hook exists:
% \begin{macrocode}
\@@_init_structure:n {#1}
% \end{macrocode}
+% The call to \cs{@@_normalise_code_pool:n} will correct any improper
+% reference to arguments that don't exist in the hook, raising a
+% low-level \TeX{} error and doubling the offending parameter tokens.
+% It has to be done after \cs{@@_init_structure:n} because it
+% operates on \cs[no-index]{g_@@_\meta{hook}_code_prop}.
+% \begin{macrocode}
+ \@@_normalise_code_pool: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.
+% These are defined conditionally, in case \cs{@@_make_usable:nn}
+% is being used to redefine a hook.
+% \changes{v1.1d}{2023/05/21}
+% {Changes to allow support arguments in cmd hooks (cmd-args).}
% \begin{macrocode}
- \clist_new:c { g_@@_#1_labels_clist }
+ \clist_if_exist:cF { g_@@_#1_labels_clist }
+ {
+ \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 }
+ \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},
@@ -2454,9 +2862,28 @@
\@@_include_legacy_code_chunk:n {#1}
}
}
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
-% \end{macro}
%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_make_usable:nn}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_undefine:N \@@_make_usable:nn
+%<latexrelease>\cs_gset_protected:Npn \@@_make_usable:n #1
+%<latexrelease> {
+%<latexrelease> \tl_if_exist:cF { @@~#1 }
+%<latexrelease> {
+%<latexrelease> \seq_gput_right:Nn \g_@@_all_seq {#1}
+%<latexrelease> \tl_new:c { @@~#1 }
+%<latexrelease> \@@_init_structure:n {#1}
+%<latexrelease> \clist_new:c { g_@@_#1_labels_clist }
+%<latexrelease> \tl_new:c { g_@@_#1_reversed_tl }
+%<latexrelease> \@@_include_legacy_code_chunk:n {#1}
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
%
%
% \begin{macro}{\@@_init_structure:n}
@@ -2472,49 +2899,124 @@
% \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}.
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_init_structure:n}
+%<latexrelease> {Hooks~with~args}
\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 }
+ \@@_toplevel_gset:nn {#1} { }
+ \@@_next_gset:nn {#1} { }
}
}
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
-% \end{macro}
-%
%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_init_structure:n}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_gset_protected:Npn \@@_init_structure:n #1
+%<latexrelease> {
+%<latexrelease> \@@_if_structure_exist:nF {#1}
+%<latexrelease> {
+%<latexrelease> \prop_new:c { g_@@_#1_code_prop }
+%<latexrelease> \tl_new:c { @@_toplevel~#1 }
+%<latexrelease> \tl_new:c { @@_next~#1 }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
%
-% \begin{macro}{\hook_new_reversed:n,\@@_new_reversed:n}
+% \begin{macro}{
+% \hook_new_reversed:n,
+% \hook_new_reversed_with_args:nn
+% }
+% \begin{macro}{\@@_new_reversed:nn}
%
% Declare a new hook. The default ordering of code chunks is
% reversed, signaled by setting the token list to a minus sign.
+% \changes{v1.1a}{2023/04/06}
+% {Add \cs{hook_new_reversed_with_args:nn} (hook-args).}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_new_reversed_with_args:nn}
+%<latexrelease> {Hooks~with~args}
\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
+ { \@@_normalize_hook_args:Nn \@@_new_reversed:nn {#1} { 0 } }
+\cs_new_protected:Npn \hook_new_reversed_with_args:nn #1 #2
+ { \@@_normalize_hook_args:Nn \@@_new_reversed:nn {#1} {#2} }
+\cs_new_protected:Npn \@@_new_reversed:nn #1 #2
{
- \@@_new:n {#1}
+ \@@_if_declared:nTF {#1}
+ { \msg_error:nnn { hooks } { exists } {#1} }
+ {
+ \@@_new:nn {#1} {#2}
+ \tl_gset:cn { g_@@_#1_reversed_tl } { - }
+ }
+ }
+%<latexrelease>\EndIncludeInRelease
% \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 } { - }
- }
+%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_new_reversed_with_args:nn}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_gset_protected:Npn \hook_new_reversed:n #1
+%<latexrelease> { \@@_normalize_hook_args:Nn \@@_new_reversed:n {#1} }
+%<latexrelease>\cs_undefine:N \@@_new_reversed:nn
+%<latexrelease>\cs_gset_protected:Npn \@@_new_reversed:n #1
+%<latexrelease> {
+%<latexrelease> \@@_new:n {#1}
+%<latexrelease> \tl_gset:cn { g_@@_#1_reversed_tl } { - }
+%<latexrelease> }
+%<latexrelease>\cs_undefine:N \@@_new_reversed:nn
+%<latexrelease>\cs_gset_protected:Npn \hook_new_reversed_with_args:nn #1 #2 { }
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
+% \end{macro}
%
-%
-%
-%
-% \begin{macro}{\hook_new_pair:nn}
+% \begin{macro}{\hook_new_pair:nn,\hook_new_pair_with_args:nnn}
% A shorthand for declaring a normal and a (matching) reversed hook in one go.
+% \changes{v1.1a}{2023/04/06}
+% {Add \cs{hook_new_pair_with_args:nnn} (hook-args).}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_new_pair_with_args:nnn}
+%<latexrelease> {Hooks~with~args}
\cs_new_protected:Npn \hook_new_pair:nn #1#2
- { \hook_new:n {#1} \hook_new_reversed:n {#2} }
+ { \@@_normalize_hook_args:Nnn \@@_new_pair:nnn {#1} {#2} { 0 } }
+\cs_new_protected:Npn \hook_new_pair_with_args:nnn #1#2#3
+ { \@@_normalize_hook_args:Nnn \@@_new_pair:nnn {#1} {#2} {#3} }
+\cs_new_protected:Npn \@@_new_pair:nnn #1 #2 #3
+ {
+ \@@_if_declared:nTF {#1}
+ { \msg_error:nnn { hooks } { exists } {#1} }
+ {
+ \@@_if_declared:nTF {#2}
+ { \msg_error:nnn { hooks } { exists } {#2} }
+ {
+ \@@_new:nn {#1} {#3}
+ \@@_new_reversed:nn {#2} {#3}
+ }
+ }
+ }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_new_pair_with_args:nnn}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_gset_protected:Npn \hook_new_pair:nn #1#2
+%<latexrelease> {
+%<latexrelease> \hook_new:n {#1}
+%<latexrelease> \hook_new_reversed:n {#2}
+%<latexrelease> }
+%<latexrelease>\cs_gset_protected:Npn \hook_new_pair_with_args:nnn #1#2#3
+%<latexrelease> { }
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
@@ -2531,6 +3033,8 @@
% \textbf{Warning: this support will vanish in future releases!}
% \end{quote}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_include_legacy_code_chunk:n}
+%<latexrelease> {Hooks~with~args}
\cs_new_protected:Npn \@@_include_legacy_code_chunk:n #1
{
% \end{macrocode}
@@ -2538,15 +3042,27 @@
% 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}
+% Here we set \cs{@@_replacing_args_false:} because no legacy code
+% will reference hook arguments.
+% \changes{v1.1b}{2023/04/16}
+% {\cs{@@_replacing_args_false:} in
+% \cs{@@_include_legacy_code_chunk:n}.}
+% \begin{macrocode}
+ \@@_replacing_args_false:
+ \use:e
+ {
+ \@@_hook_gput_code_do:nnn {#1} { legacy }
+ { \exp_not:v { @#1hook } }
+ }
+ \@@_replacing_args_reset:
% \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.
@@ -2555,6 +3071,22 @@
}
}
}
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_include_legacy_code_chunk:n}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_gset_protected:Npn \@@_include_legacy_code_chunk:n #1
+%<latexrelease> {
+%<latexrelease> \tl_if_exist:cT { @#1hook }
+%<latexrelease> {
+%<latexrelease> \tl_if_empty:cF { @#1hook }
+%<latexrelease> {
+%<latexrelease> \exp_args:Nnnv \@@_hook_gput_code_do:nnn
+%<latexrelease> {#1} { legacy } { @#1hook }
+%<latexrelease> \@@_tl_gclear:c { @#1hook }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
@@ -2579,8 +3111,8 @@
% if the hook is disabled.
%
% \begin{macrocode}
-%<latexrelease>\IncludeInRelease{2021/06/01}%
-%<latexrelease> {\hook_disable_generic:n}{Disable~hooks}
+%<latexrelease>\IncludeInRelease{2021/06/01}{\hook_disable_generic:n}
+%<latexrelease> {Disable~hooks}
% \end{macrocode}
%
% \begin{macrocode}
@@ -2595,7 +3127,7 @@
{
\bool_lazy_and:nnTF
{ \tl_if_exist_p:c { g_@@_#1_declared_tl } }
- { ! \tl_if_exist_p:c { @@~#1 } }
+ { ! \cs_if_exist_p:c { @@~#1 } }
{ \prg_return_true: }
{ \prg_return_false: }
}
@@ -2603,8 +3135,8 @@
% \end{macrocode}
%
% \begin{macrocode}
-%<latexrelease>\IncludeInRelease{2020/10/01}
-%<latexrelease> {\hook_disable_generic:n}{Disable~hooks}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_disable_generic:n}
+%<latexrelease> {Disable~hooks}
%<latexrelease>
%<latexrelease>\cs_new_protected:Npn \hook_disable_generic:n #1 {}
%<latexrelease>
@@ -2619,9 +3151,11 @@
% 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.
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
% \begin{macrocode}
-%<latexrelease>\IncludeInRelease{2021/06/01}%
-%<latexrelease> {\hook_activate_generic:n}{Providing~hooks}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_activate_generic:n}
+%<latexrelease> {Providing~hooks}
% \end{macrocode}
%
% \begin{macrocode}
@@ -2646,7 +3180,7 @@
\@@_if_declared:nF {#1}
{
\tl_new:c { g_@@_#1_declared_tl }
- \@@_make_usable:n {#1}
+ \@@_make_usable:nn {#1} { 0 }
\tl_gset:cx { g_@@_#1_reversed_tl }
{ \@@_if_generic_reversed:nT {#1} { - } }
% \end{macrocode}
@@ -2660,21 +3194,40 @@
}
}
% \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>\IncludeInRelease{2021/06/01}{\hook_activate_generic:n}
+%<latexrelease> {Providing~hooks}
+%<latexrelease>\cs_gset_protected:Npn \@@_activate_generic:nn #1 #2
+%<latexrelease> {
+%<latexrelease> \@@_if_disabled:nTF {#1}
+%<latexrelease> { \msg_warning:nnn { hooks } { activate-disabled } {#1} }
+%<latexrelease> {
+%<latexrelease> \@@_if_declared:nF {#1}
+%<latexrelease> {
+%<latexrelease> \tl_new:c { g_@@_#1_declared_tl }
+%<latexrelease> \@@_make_usable:n {#1}
+%<latexrelease> \tl_gset:cx { g_@@_#1_reversed_tl }
+%<latexrelease> { \@@_if_generic_reversed:nT {#1} { - } }
+%<latexrelease> \@@_update_hook_code:n {#1}
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_activate_generic:n}
+%<latexrelease> {Providing~hooks}
+%<latexrelease>\cs_gset_protected:Npn \hook_activate_generic:n #1 { }
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
+% \end{macro}
+% \end{macro}
%
%
% \subsection{Parsing a label}
@@ -2965,63 +3518,109 @@
%
% \subsection{Adding or removing hook code}
%
-% \begin{macro}{\hook_gput_code:nnn}
-% \begin{macro}{\@@_gput_code:nnn,\@@_hook_gput_code_do:nnn}
+% \begin{macro}{\hook_gput_code:nnn,\hook_gput_code_with_args:nnn}
+% \begin{macro}{
+% \@@_gput_code:nnn,
+% \@@_gput_code_store:nnn,
+% \@@_hook_gput_code_do:nnn,
+% \@@_prop_gput_labeled_cleanup:nnn,
+% \@@_prop_gput_labeled_do:Nnnn
+% }
%
% 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)}
+% \changes{v1.1a}{2023/04/06}
+% {Add \cs{hook_gput_code_with_args:nnn} (hook-args).}
% \begin{macrocode}
-\cs_new_protected:Npn \hook_gput_code:nnn #1 #2
- { \@@_normalize_hook_args:Nnn \@@_gput_code:nnn {#1} {#2} }
+%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_gput_code_with_args:nnn}
+%<latexrelease> {Hooks~with~args}
+\cs_new_protected:Npn \hook_gput_code:nnn #1 #2 #3
+ {
+ \@@_replacing_args_false:
+ \@@_normalize_hook_args:Nnn \@@_gput_code:nnn {#1} {#2} {#3}
+ \@@_replacing_args_reset:
+ }
+\cs_new_protected:Npn \hook_gput_code_with_args:nnn #1 #2 #3
+ {
+ \@@_replacing_args_true:
+ \@@_normalize_hook_args:Nnn \@@_gput_code:nnn {#1} {#2} {#3}
+ \@@_replacing_args_reset:
+ }
% \end{macrocode}
%
+% If \cs{AddToHookWithArguments} was used, do some sanity checking,
+% and if it's not possible to use arguments at this point, fall back
+% to regular \cs{AddToHook} by using \cs{@@_replacing_args_false:}.
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_code:nnn #1 #2 #3
{
+ \@@_chk_args_allowed:nn {#1} { AddToHook }
% \end{macrocode}
-% First check if the code should be executed immediately, rather than
+% Then 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}
+% \cs{AddToHookWithArguments} can't be used on one-time hooks (that
+% were already used).
+% \begin{macrocode}
+ \@@_if_replacing_args:TF
+ {
+ \msg_error:nnnn { hooks } { one-time-args }
+ {#1} { AddToHook }
+ }
+ { }
+ \use:n
+ }
+ { \@@_gput_code_store:nnn {#1} {#2} }
+ {#3}
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_gput_code_store:nnn #1 #2 #3
+ {
+% \end{macrocode}
% Then check if the hook is usable.
% \begin{macrocode}
- \@@_if_usable:nTF {#1}
+ \@@_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}
+ {
+ \@@_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}
- }
+ \@@_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} }
- }
+ {
+ \@@_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.
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
% \begin{macrocode}
\cs_new_protected:Npn \@@_hook_gput_code_do:nnn #1 #2 #3
{
@@ -3046,31 +3645,161 @@
\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}.
+% 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}
+% \end{macrocode}
+% Then append to the \verb|_toplevel| container for the hook.
+% \begin{macrocode}
+ \@@_cs_gput_right:nnn { _toplevel } {#1} {#3}
}
{ \msg_error:nnn { hooks } { misused-top-level } {#1} }
}
{
- \prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl
+% \end{macrocode}
+% When adding to the code pool, we have to double hashes if
+% \cs{AddToHook} was used (\verb|replacing_args| is false), so that
+% later it is turned into a single parameter token, rather than a
+% parameter to the hook macro.
+% \begin{macrocode}
+ \exp_args:Nx \@@_prop_gput_labeled_cleanup:nnn
{
- \prop_gput:cno { g_@@_#1_code_prop } {#2}
- { \l_@@_return_tl #3 }
+ \@@_if_replacing_args:TF
+ { \exp_not:n }
+ { \@@_double_hashes:n }
+ {#3}
}
- { \prop_gput:cnn { g_@@_#1_code_prop } {#2} {#3} }
+ {#1} {#2}
}
}
% \end{macrocode}
+%
+% Adds code to a hook's code pool.
+% \changes{v1.1a}{2023/04/06}
+% {Macro added (hook-args).}
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_prop_gput_labeled_cleanup:nnn #1 #2 #3
+ {
+ \tl_set:Nn \l_@@_return_tl {#1}
+ \@@_if_replacing_args:TF
+ {
+ \@@_if_usable:nT {#2}
+ {
+ \@@_set_normalise_fn:nn {#2}
+ { Invalid~code~added~\msg_line_context: }
+ \@@_normalise_fn:nn {#3} {#1}
+ \prop_get:NnN \l_@@_work_prop {#3} \l_@@_return_tl
+ }
+ }
+ { }
+ \exp_args:NcV \@@_prop_gput_labeled_do:Nnn
+ { g_@@_#2_code_prop } \l_@@_return_tl {#3}
+ }
+\cs_new_protected:Npn \@@_prop_gput_labeled_do:Nnn #1 #2 #3
+ {
+ \prop_get:NnNTF #1 {#3} \l_@@_return_tl
+ { \prop_gput:Nno #1 {#3} { \l_@@_return_tl #2 } }
+ { \prop_gput:Nnn #1 {#3} {#2} }
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_gput_code:nnn}
+%<latexrelease> {Providing~hooks}
+%<latexrelease>\cs_gset_protected:Npn \hook_gput_code:nnn #1 #2
+%<latexrelease> { \@@_normalize_hook_args:Nnn \@@_gput_code:nnn {#1} {#2} }
+%<latexrelease>\cs_gset_protected:Npn \@@_gput_code:nnn #1 #2 #3
+%<latexrelease> {
+%<latexrelease> \@@_if_execute_immediately:nTF {#1}
+%<latexrelease> {#3}
+%<latexrelease> {
+%<latexrelease> \@@_if_usable:nTF {#1}
+%<latexrelease> {
+%<latexrelease> \@@_hook_gput_code_do:nnn {#1} {#2} {#3}
+%<latexrelease> \@@_update_hook_code:n {#1}
+%<latexrelease> }
+%<latexrelease> {
+%<latexrelease> \@@_if_disabled:nTF {#1}
+%<latexrelease> { \msg_error:nnn { hooks } { hook-disabled } {#1} }
+%<latexrelease> { \@@_try_declaring_generic_hook:nnn {#1} {#2} {#3} }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease>\cs_gset_protected:Npn \@@_hook_gput_code_do:nnn #1 #2 #3
+%<latexrelease> {
+%<latexrelease> \@@_debug:n{\iow_term:x{****~ Add~ to~
+%<latexrelease> \@@_if_usable:nF {#1} { undeclared~ }
+%<latexrelease> hook~ #1~ (#2)
+%<latexrelease> \on@line\space <-~ \tl_to_str:n{#3}} }
+%<latexrelease> \str_if_eq:nnTF {#2} { top-level }
+%<latexrelease> {
+%<latexrelease> \str_if_eq:eeTF { top-level } { \@@_currname_or_default: }
+%<latexrelease> {
+%<latexrelease> \@@_init_structure:n {#1}
+%<latexrelease> \@@_tl_gput_right:cn { @@_toplevel~#1 } {#3}
+%<latexrelease> }
+%<latexrelease> { \msg_error:nnn { hooks } { misused-top-level } {#1} }
+%<latexrelease> }
+%<latexrelease> {
+%<latexrelease> \prop_get:cnNTF { g_@@_#1_code_prop } {#2} \l_@@_return_tl
+%<latexrelease> {
+%<latexrelease> \prop_gput:cno { g_@@_#1_code_prop } {#2}
+%<latexrelease> { \l_@@_return_tl #3 }
+%<latexrelease> }
+%<latexrelease> { \prop_gput:cnn { g_@@_#1_code_prop } {#2} {#3} }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease>\cs_gset_protected:Npn \hook_gput_code_with_args:nnn #1#2#3 { }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
% \end{macro}
% \end{macro}
%
+% \begin{macro}{\@@_chk_args_allowed:nn}
+% This macro checks if it is possible to add code with references to
+% a hook's arguments for hook \verb|#1|. It only does something if
+% the function being run is \verb|replacing_args|. This macro will
+% error if the hook is declared and takes no arguments, then it will
+% set \cs{@@_replacing_args_false:} so that the macro which called it
+% will add the code normally.
+% \changes{v1.1a}{2023/04/06}
+% {Macro added (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_chk_args_allowed:nn}
+%<latexrelease> {Hooks~with~args}
+\cs_new_protected:Npn \@@_chk_args_allowed:nn #1 #2
+ {
+ \@@_if_replacing_args:TF
+ {
+ \@@_if_declared:nT {#1}
+ { \tl_if_empty:cT { c_@@_#1_parameter_tl } { \use_ii:nn } }
+ \use_none:n
+ {
+ \msg_error:nnnn { hooks } { without-args } {#1} {#2}
+ \@@_replacing_args_false:
+ }
+ }
+ { }
+ }
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_chk_args_allowed:nn}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_undefine:N \@@_chk_args_allowed:nn
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \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.
+% An implicitly declared hook doesn't have arguments (in principle),
+% so use \cs{c_false_bool} here.
% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_undeclared_hook:nnn #1 #2 #3
{
@@ -3098,24 +3827,43 @@
% \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)}
+% \changes{v1.1d}{2023/05/21}
+% {Changes to allow support arguments in cmd hooks (cmd-args).}
% \begin{macrocode}
-%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_try_declaring_generic_hook:nnn}
-%<latexrelease> {Standardise~generic~hook~names}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_try_declaring_generic_hook:nnn}
+%<latexrelease> {Hooks~with~args}
\cs_new_protected:Npn \@@_try_declaring_generic_hook:nnn #1
{
\@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop: {#1}
- \hook_gput_code:nnn
+ \@@_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_code:nn
\@@_gput_next_do:nn
{#1}
}
%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_try_declaring_generic_hook:nnn}
+%<latexrelease> {Standardise~generic~hook~names}
+%<latexrelease>\cs_gset_protected:Npn \@@_try_declaring_generic_hook:nnn #1
+%<latexrelease> {
+%<latexrelease> \@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop: {#1}
+%<latexrelease> \hook_gput_code:nnn
+%<latexrelease> \@@_gput_undeclared_hook:nnn
+%<latexrelease> {#1}
+%<latexrelease> }
+%<latexrelease>\cs_gset_protected:Npn \@@_try_declaring_generic_next_hook:nn #1
+%<latexrelease> {
+%<latexrelease> \@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop: {#1}
+%<latexrelease> \hook_gput_next_code:nn
+%<latexrelease> \@@_gput_next_do:nn
+%<latexrelease> {#1}
+%<latexrelease> }
+%<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
@@ -3170,9 +3918,11 @@
% \end{macro}
%
% \begin{macro}[TF]{\@@_try_declaring_generic_hook:wn}
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
% \begin{macrocode}
-%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_try_declaring_generic_hook:wn}%
-%<latexrelease> {Standardise~generic~hook~names}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_try_declaring_generic_hook:wn}
+%<latexrelease> {Hooks~with~args}
\prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn
#1 / #2 / #3 / #4 \scan_stop: #5 { TF }
{
@@ -3188,18 +3938,25 @@
% 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.
+% \changes{v1.1d}{2023/05/21}
+% {Changes to allow support arguments in cmd hooks (cmd-args).}
% \begin{macrocode}
\str_if_eq:nnT {#1} { cmd }
- { \@@_try_put_cmd_hook:n {#5} }
+ {
+ \@@_try_put_cmd_hook:n {#5}
+ \@@_make_usable:nn {#5} { 9 }
+ \use_none:nnn
+ }
% \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.
+% Here we use \cs{@@_make_usable:nn}, so that a \cs{hook_new:n} is
+% still possible later. Generic hooks (except \hook{cmd} hooks) take
+% no arguments, so use zero as the second argument.
% \begin{macrocode}
- \@@_make_usable:n {#5}
+ \@@_make_usable:nn {#5} { 0 }
}
\@@_if_generic_reversed:nT {#5}
{ \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
@@ -3285,6 +4042,37 @@
% \end{macro}
%
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_try_declaring_generic_hook:wn}
+%<latexrelease> {Standardise~generic~hook~names}
+%<latexrelease>\prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn
+%<latexrelease> #1 / #2 / #3 / #4 \scan_stop: #5 { TF }
+%<latexrelease> {
+%<latexrelease> \@@_if_generic:nTF {#5}
+%<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> \@@_if_generic_reversed:nT {#5}
+%<latexrelease> { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
+%<latexrelease> \prg_return_true:
+%<latexrelease> }
+%<latexrelease> {
+%<latexrelease> \@@_if_deprecated_generic:nTF {#5}
+%<latexrelease> {
+%<latexrelease> \@@_deprecated_generic_warn:n {#5}
+%<latexrelease> \exp_after:wN \@@_declare_deprecated_generic:NNn
+%<latexrelease> \exp:w % \exp_end:
+%<latexrelease> }
+%<latexrelease> { \prg_return_false: }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \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
@@ -3316,7 +4104,7 @@
% \end{macrocode}
%
% \begin{macrocode}
-%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_try_declaring_generic_hook:wn}%
+%<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 }
@@ -3338,7 +4126,6 @@
%<latexrelease> { \prg_return_false: }
%<latexrelease> }
%<latexrelease> }
-%<latexrelease>
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
@@ -3350,24 +4137,10 @@
% |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>\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>\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 }
@@ -3394,7 +4167,7 @@
% \begin{macro}[EXP]{\@@_strip_double_slash:n,\@@_strip_double_slash:w}
%
% \begin{macrocode}
-%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_file_hook_normalize:n}%
+%<latexrelease>\IncludeInRelease{2021/11/15}{\@@_file_hook_normalize:n}
%<latexrelease> {Standardise~generic~hook~names}
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
@@ -3406,7 +4179,7 @@
% 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>\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} }
@@ -3453,7 +4226,7 @@
% The generic hooks used when loading a file
% \end{description}
% \begin{macrocode}
-%<latexrelease>\IncludeInRelease{2021/11/15}{\c_@@_generics_prop}%
+%<latexrelease>\IncludeInRelease{2021/11/15}{\c_@@_generics_prop}
%<latexrelease> {Standardise~generic~hook~names}
\clist_map_inline:nn { cmd , env , file , package , class , include }
{
@@ -3482,7 +4255,7 @@
% \end{macrocode}
%
% \begin{macrocode}
-%<latexrelease>\IncludeInRelease{2020/10/01}{\c_@@_generics_prop}%
+%<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=}
@@ -3496,13 +4269,13 @@
% 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>\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>\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=}
@@ -3511,13 +4284,46 @@
% \end{macrocode}
% \end{macro}
%
+%
+% \begin{macro}{
+% \c_@@_parameter_cmd/./before_tl,\c_@@_parameter_cmd/./after_tl,
+% }
+% Token lists defining the number of arguments for a given type of
+% generic hook.
+% \changes{v1.1d}{2023/05/21}
+% {Token lists added (cmd-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\c_@@_parameter_cmd/./before_tl}
+%<latexrelease> {Hooks~with~args}
+% \end{macrocode}
+%
+% \hook{cmd} hooks are declared with 9 arguments because they have a
+% variable number of arguments (depending on the command they are
+% attached to), so we use the maximum here.
+% \begin{macrocode}
+\tl_const:cn { c_@@_parameter_cmd/./before_tl } { #1#2#3#4#5#6#7#8#9 }
+\tl_const:cn { c_@@_parameter_cmd/./after_tl } { #1#2#3#4#5#6#7#8#9 }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\c_@@_parameter_cmd/./before_tl}
+%<latexrelease> {Hooks~with~args}
+%<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)}
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_gremove_code:nn}
+%<latexrelease> {Hooks~with~args}
\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
@@ -3542,8 +4348,8 @@
\str_if_eq:nnTF {#2} {*}
{
\prop_gclear:c { g_@@_#1_code_prop }
- \@@_tl_gclear:c { @@_toplevel~#1 }
- \@@_tl_gclear:c { @@_next~#1 }
+ \@@_toplevel_gset:nn {#1} { }
+ \@@_next_gset:nn {#1} { }
}
{
% \end{macrocode}
@@ -3551,7 +4357,7 @@
% there is under the same label.
% \begin{macrocode}
\str_if_eq:nnTF {#2} { top-level }
- { \@@_tl_gclear:c { @@_toplevel~#1 } }
+ { \@@_toplevel_gset:nn {#1} { } }
{
\prop_gpop:cnNF { g_@@_#1_code_prop } {#2} \l_@@_return_tl
{ \msg_warning:nnnn { hooks } { cannot-remove } {#1} {#2} }
@@ -3576,14 +4382,542 @@
{ \msg_warning:nnnn { hooks } { cannot-remove } {#1} {#2} }
}
}
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_gremove_code:nn}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_new_protected:Npn \@@_gremove_code:nn #1 #2
+%<latexrelease> {
+%<latexrelease> \@@_if_structure_exist:nTF {#1}
+%<latexrelease> {
+%<latexrelease> \str_if_eq:nnTF {#2} {*}
+%<latexrelease> {
+%<latexrelease> \prop_gclear:c { g_@@_#1_code_prop }
+%<latexrelease> \@@_tl_gclear:c { @@_toplevel~#1 }
+%<latexrelease> \@@_tl_gclear:c { @@_next~#1 }
+%<latexrelease> }
+%<latexrelease> {
+%<latexrelease> \str_if_eq:nnTF {#2} { top-level }
+%<latexrelease> { \@@_tl_gclear:c { @@_toplevel~#1 } }
+%<latexrelease> {
+%<latexrelease> \prop_gpop:cnNF { g_@@_#1_code_prop } {#2} \l_@@_return_tl
+%<latexrelease> { \msg_warning:nnnn { hooks } { cannot-remove } {#1} {#2} }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> \@@_if_usable:nT {#1}
+%<latexrelease> { \@@_update_hook_code:n {#1} }
+%<latexrelease> }
+%<latexrelease> {
+%<latexrelease> \@@_if_deprecated_generic:nTF {#1}
+%<latexrelease> {
+%<latexrelease> \@@_deprecated_generic_warn:n {#1}
+%<latexrelease> \@@_do_deprecated_generic:Nn \@@_gremove_code:nn {#1} {#2}
+%<latexrelease> }
+%<latexrelease> { \msg_warning:nnnn { hooks } { cannot-remove } {#1} {#2} }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_cs_gput_right:nnn}
+% \begin{macro}{\@@_code_gset_auxi:nnnn,\@@_code_gset_auxi:eeen}
+% This macro is used to append code to the \verb|toplevel| and
+% \verb|next| token lists, trating them correctly depending on their
+% number of arguments, and depending if the code being added should
+% have parameter tokens understood as parameters, or doubled to be
+% stored as parameter tokens.
+% \changes{v1.1a}{2023/04/06}
+% {Macro added (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_cs_gput_right:nnn}
+%<latexrelease> {Hooks~with~args}
+\cs_new_protected:Npn \@@_cs_gput_right:nnn #1 #2 #3
+ {
+% \end{macrocode}
+% The auxiliary \cs{@@_code_gset_auxi:eeen} just does the assignment
+% at the end. Its first argument is the parameter text of the macro,
+% which is chosen here depending if
+% \cs[no-index]{c_@@_\meta{hook}_parameter_tl} exists, if the hook is
+% declared, and if it's a generic hook.
+% \begin{macrocode}
+ \cs_if_exist:cF { @@#1~#2 }
+ { \@@_code_gset_aux:nnn {#1} {#2} { } }
+ \@@_code_gset_auxi:eeen
+ {
+ \@@_if_declared:nTF {#2}
+ { \tl_use:c { c_@@_#2_parameter_tl } }
+ {
+ \@@_if_generic:nTF {#2}
+ { \@@_generic_parameter:n {#2} }
+ { \c_@@_nine_parameters_tl }
+ }
+ }
+% \end{macrocode}
+% Here we take the existing code in the macro, expand it with as many
+% arguments as it takes, then double the hashes so the code can be
+% reused. \pho{Maybe can be improved. The case of adding to an empty
+% cs can be optimised by quickly checking \cs{cs_replacement_spec}.}
+% \begin{macrocode}
+ {
+ \exp_args:NNo \exp_args:No \@@_double_hashes:n
+ {
+ \cs:w @@#1~#2 \exp_last_unbraced:Ne \cs_end:
+ { \@@_braced_cs_parameter:n { @@#1~#2 } }
+ }
+ }
+% \end{macrocode}
+% Now the new code: if we are replacing arguments, then hashes are
+% left untouched, otherwise they are doubled.
+% \begin{macrocode}
+ {
+ \@@_if_replacing_args:TF
+ { \exp_not:n }
+ { \@@_double_hashes:n }
+ {#3}
+ }
+% \end{macrocode}
+% And finally, the csname which we'll define with all the above.
+% \begin{macrocode}
+ { @@#1~#2 }
+ }
+% \end{macrocode}
+%
+% And as promised, the auxiliary that does the definition.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_code_gset_auxi:nnnn #1 #2 #3 #4
+ { \cs_gset:cpn {#4} #1 { #2 #3 } }
+\cs_generate_variant:Nn \@@_code_gset_auxi:nnnn { eeen }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_cs_gput_right:nnn}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_undefine:N \@@_cs_gput_right:nnn
+%<latexrelease>\cs_undefine:N \@@_code_gset_auxi:nnnn
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{
+% \@@_code_gset:nn,\@@_code_gset:ne,
+% \@@_toplevel_gset:nn,
+% \@@_next_gset:nn,
+% \@@_code_gset_aux:nnn
+% }
+% These macros define
+% \cs[no-index]{@@\meta{type}\textvisiblespace\meta{hook}} (with
+% \meta{type} being \verb|_next|, \verb|_toplevel|, or empty) with the
+% given code and the parameters stored in
+% \cs[no-index]{c_@@_\meta{hook}_parameter_tl} (or none, if that
+% doesn't exist).
+% \changes{v1.1a}{2023/04/06}
+% {Macro added (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_code_gset:nn}
+%<latexrelease> {Hooks~with~args}
+\cs_new_protected:Npn \@@_code_gset:nn
+ { \@@_code_gset_aux:nnn { } }
+\cs_new_protected:Npn \@@_toplevel_gset:nn
+ { \@@_code_gset_aux:nnn { _toplevel } }
+\cs_new_protected:Npn \@@_next_gset:nn
+ { \@@_code_gset_aux:nnn { _next } }
+\cs_new_protected:Npn \@@_code_gset_aux:nnn #1 #2 #3
+ {
+ \cs_gset:cpn { @@#1~#2 \exp_last_unbraced:Ne }
+ { \@@_parameter:n {#2} }
+ {#3}
+ }
+\cs_generate_variant:Nn \@@_code_gset:nn { ne }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_code_gset:nn}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_undefine:N \@@_code_gset:nn
+%<latexrelease>\cs_undefine:N \@@_toplevel_gset:nn
+%<latexrelease>\cs_undefine:N \@@_next_gset:nn
+%<latexrelease>\cs_undefine:N \@@_code_gset_aux:nnn
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_normalise_cs_args:nn}
+% This macro normalises the parameters of the macros
+% \cs[no-index]{@@\meta{type}\textvisiblespace\meta{hook}} to take the
+% right number of arguments after a hook is declared. At this point
+% we know \cs[no-index]{c_@@_\meta{hook}_parameter_tl} exists, so use
+% that to count the arguments and use that as \meta{parameter text}
+% for the newly (re)defined macro.
+% \changes{v1.1a}{2023/04/06}
+% {Macro added (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_normalise_cs_args:nn}
+%<latexrelease> {Hooks~with~args}
+\cs_new_protected:Npn \@@_normalise_cs_args:nn #1 #2
+ {
+ \cs_if_exist:cT { @@#1~#2 }
+ {
+ \@@_code_gset_auxi:eeen
+ { \tl_use:c { c_@@_#2_parameter_tl } }
+ {
+ \exp_args:NNo \exp_args:No \@@_double_hashes:n
+ {
+ \cs:w @@#1~#2 \exp_last_unbraced:Ne \cs_end:
+ { \@@_braced_cs_parameter:n { @@#1~#2 } }
+ }
+ }
+ { }
+ { @@#1~#2 }
+ }
+ }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_normalise_cs_args:nn}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_undefine:N \@@_normalise_cs_args:nn
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_normalise_code_pool:n}
+% \begin{macro}{\@@_set_normalise_fn:nn}
+% This one's a bit of a hack. It takes a hook, and iterates over its
+% code pool (\cs[no-index]{g_@@_\meta{hook}_code_prop}), redefining
+% each code label to use only valid arguments. This is used when, for
+% example, a code is added referencing arguments \verb|#1| and
+% \verb|#2|, but the hook has only \verb|#1|. In this example, every
+% reference to \verb|#2| is changed to \verb|##2|. This is done
+% because otherwise \TeX{} will throw a low-level error every time
+% some change happens to the hook (code is added, a rule is set, etc),
+% which can get quite repetitive for no good reason.
+% \changes{v1.1a}{2023/04/06}
+% {Macro added (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_normalise_code_pool:n}
+%<latexrelease> {Hooks~with~args}
+\cs_new_protected:Npn \@@_normalise_code_pool:n #1
+ {
+% \end{macrocode}
+% First, call \cs{@@_set_normalise_fn:nn} with the hook name to set
+% everything up, then we'll loop over the
+% hook's code pool applying the normalisation above. After that's
+% done, copy the temporary property list back to the hook's.
+% \begin{macrocode}
+ \@@_set_normalise_fn:nn {#1} { Offending~label:~'##1' }
+ \prop_clear:N \l_@@_work_prop
+ \prop_map_function:cN { g_@@_#1_code_prop } \@@_normalise_fn:nn
+ \prop_gset_eq:cN { g_@@_#1_code_prop } \l_@@_work_prop
+ }
+% \end{macrocode}
+%
+% The sole purpose of this function is to define
+% \cs{@@_normalise_fn:nn}, which will then do the correcting of the
+% code being added to the hook.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_normalise_fn:nn #1 #2
+ {
+% \end{macrocode}
+% To start, we define two auxiliary token lists.
+% \cs[no-index]{l_@@_tmpb_tl} contains:
+%\begin{verbatim}
+% {\c__hook_hashes_tl 1}
+% {\c__hook_hashes_tl 2}
+% ...
+% {\c__hook_hashes_tl 9}
+%\end{verbatim}
+% \begin{macrocode}
+ \cs_set:Npn \@@_tmp:w ##1##2##3##4##5##6##7##8##9 { }
+ \tl_set:Ne \l_@@_tmpb_tl
+ { \@@_braced_cs_parameter:n { @@_tmp:w } }
+ \group_begin:
+ \@@_tl_set:cn { c_@@_hash_tl } { \exp_not:N \c_@@_hashes_tl }
+ \use:e
+ {
+ \group_end:
+ \tl_set:Nn \exp_not:N \l_@@_tmpb_tl { \l_@@_tmpb_tl }
+ }
+% \end{macrocode}
+% And \cs[no-index]{l_@@_tmpa_tl} contains:
+%\begin{verbatim}
+% {\c__hook_hash_tl 1}
+% {\c__hook_hash_tl 2}
+% ...
+% {\c__hook_hash_tl <n>}
+%\end{verbatim}
+% with \meta{n} being the number of arguments declared for the hook.
+% \begin{macrocode}
+ \exp_last_unbraced:NNf
+ \cs_set:Npn \@@_tmp:w { \@@_parameter:n {#1} } { }
+ \tl_set:Ne \l_@@_tmpa_tl { \@@_braced_cs_parameter:n { @@_tmp:w } }
+% \end{macrocode}
+% Now this function does the fun part. It is meant to be used with
+% \cs{prop_map_function:NN}, taking a label name in \verb|##1| and the
+% code stored in that label in \verb|##2|.
+% \begin{macrocode}
+ \cs_gset_protected:Npx \@@_normalise_fn:nn ##1 ##2
+ {
+% \end{macrocode}
+% Here we'll define two auxiliary macros: the first one throws an
+% error when it detects an invalid argument reference. It piggybacks
+% on \TeX's low-level \enquote{Illegal parameter number} error, but it
+% defines a weirdly-named control sequence so that the error comes out
+% nicely formatted. For example, if the label \enquote{badpkg} adds
+% some code that references argument \verb|#3| in the hook
+% \enquote{foo}, which takes only two arguments, the error will be:
+%\begin{verbatim}
+% ! Illegal parameter number in definition of hook 'foo'.
+% (hooks) Offending label: 'badpkg'.
+% <to be read again>
+% 3
+%\end{verbatim}
+% At the point of this definition, the error is raised if the code
+% happens to reference an invalid argument. If it was possible to
+% detect that this definition raised no error, the next step would be
+% unnecessary. We'll do all this in a group so this weird definition
+% doesn't leak out, and set \cs{tex_escapechar:D} to $-1$ so this hack
+% shows up extra nice in the case of an error.
+% \begin{macrocode}
+ \group_begin:
+ \int_set:Nn \tex_escapechar:D { -1 }
+ \cs_set:cpn
+ {
+ hook~'#1'. ^^J
+ (hooks) \prg_replicate:nn { 13 } { ~ }
+ #2 % more message text
+ }
+ \exp_not:v { c_@@_#1_parameter_tl }
+ {##2}
+ \group_end:
+% \end{macrocode}
+% This next macro, with a much less fabulous name, takes always nine
+% arguments, and it just transfers the code \verb|##2| under the label
+% \verb|##1| to the temporary property list. The first \meta{n}
+% arguments are taken from \cs[no-index]{l_@@_tmpa_tl}, and the other
+% $9-\meta{n}$ taken from \cs[no-index]{l_@@_tmpb_tl} (which contains
+% twice as many \verb|#| tokens as the former). Then,
+% \cs{@@_double_hashes:n} is used to double non-argument hashes, and
+% expand the \cs{c_@@_hash_tl} and \cs{c_@@_hashes_tl} to the actual
+% parameter tokens.
+% \begin{macrocode}
+ \cs_set:Npn \exp_not:N \@@_tmp:w
+ \exp_not:V \c_@@_nine_parameters_tl
+ {
+ \prop_put:Nne \exp_not:N \l_@@_work_prop
+ {##1} { \exp_not:N \@@_double_hashes:n {##2} }
+ }
+% \end{macrocode}
+% This next macro, with a much less fabulous name, takes always nine
+% arguments, and it just transfers the code \verb|##2| under the label
+% \verb|##1| to the temporary property list. The first \meta{n}
+% arguments are taken from \cs[no-index]{l_@@_tmpa_tl}, and the other
+% $9-\meta{n}$ taken from \cs[no-index]{l_@@_tmpb_tl} (which contains
+% twice as many \verb|#| tokens as the former). Then,
+% \cs{@@_double_hashes:n} is used to double non-argument hashes, and
+% expand the \cs{c_@@_hash_tl} and \cs{c_@@_hashes_tl} to the actual
+% parameter tokens.
+% \begin{macrocode}
+ \exp_not:N \@@_tmp:w
+ \exp_not:V \l_@@_tmpa_tl
+ \exp_args:No \exp_not:o
+ { \exp_after:wN \@@_tmp:w \l_@@_tmpb_tl }
+ }
+ }
+\cs_new_eq:NN \@@_normalise_fn:nn ?
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_normalise_code_pool:n}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_undefine:N \@@_normalise_code_pool:n
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[pTF]{\@@_cs_if_empty:c}
+% Check if the expansion of a control sequence is empty by looking at
+% its replacement text.
+% \changes{v1.1a}{2023/04/06}
+% {Macro added (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_cs_if_empty:c}
+%<latexrelease> {Hooks~with~args}
+\prg_new_conditional:Npnn \@@_cs_if_empty:c #1 { p, T, F, TF }
+ {
+ \if:w \scan_stop: \@@_replacement_spec:c {#1} \scan_stop:
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+\cs_new:Npn \@@_replacement_spec:c #1
+ {
+ \exp_args:Nc \token_if_macro:NT {#1}
+ { \cs_replacement_spec:c {#1} }
+ }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_cs_if_empty:c}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_undefine:N \@@_cs_if_empty:c
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_braced_cs_parameter:n}
+% \begin{macro}{\@@_braced_hidden_loop:w}
+% \begin{macro}{\@@_cs_parameter_count:N}
+% \begin{macro}{\@@_cs_parameter_count:w,\@@_cs_end:w}
+% Looks at the \meta{parameter text} of a control sequence, and
+% returns a run of \enquote{hidden} braced parameters for that macro.
+% This works as long as the macros take a simple run of zero to nine
+% arguments. The parameters are \enquote{hidden} because the
+% parameter tokens are returned inside \cs{c_@@_hash_tl} instead of
+% explicitly, so that \cs{@@_double_hashes:n} won't touch these.
+% \changes{v1.1a}{2023/04/06}
+% {Macro added (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_braced_cs_parameter:n}
+%<latexrelease> {Hooks~with~args}
+\cs_new:Npn \@@_braced_cs_parameter:n #1
+ {
+ \exp_last_unbraced:Ne \@@_braced_hidden_loop:w
+ { \exp_args:Nc \@@_cs_parameter_count:N {#1} } ? \s_@@_mark
+ }
+\cs_new:Npn \@@_braced_hidden_loop:w #1
+ {
+ \if:w ? #1
+ \@@_use_i_delimit_by_s_mark:nw
+ \fi:
+ { \exp_not:N \c_@@_hash_tl #1 }
+ \@@_braced_hidden_loop:w
+ }
+\cs_new:Npn \@@_cs_parameter_count:N #1
+ {
+ \exp_last_unbraced:Nf \@@_cs_parameter_count:w
+ { \token_if_macro:NT #1 { \cs_parameter_spec:N #1 } }
+ ? \@@_cs_end:w ? \@@_cs_end:w ? \@@_cs_end:w
+ ? \@@_cs_end:w ? \@@_cs_end:w ? \@@_cs_end:w
+ ? \@@_cs_end:w ? \@@_cs_end:w ? \@@_cs_end:w
+ \s_@@_mark
+ }
+\cs_new:Npn \@@_cs_parameter_count:w #1#2 #3#4 #5#6 #7#8
+ { #2 #4 #6 #8 \@@_cs_parameter_count:w }
+\cs_new:Npn \@@_cs_end:w #1 \s_@@_mark { }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% This function can't be undefined when rolling back because it's used
+% at the end of this module to adequate the hook data structures to
+% previous versions.
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_braced_cs_parameter:n}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_braced_parameter:n}
+% \begin{macro}{\@@_braced_real_loop:w}
+% This one is used in simpler cases, where no special handling of
+% hashes is required. This is used only inside
+% \cs{@@_initialize_hook_code:n}, so it assumes
+% \cs[no-index]{c_@@_\meta{hook}_parameter_tl} is defined, but should
+% work otherwise.
+% \changes{v1.1a}{2023/04/06}
+% {Macro added (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_braced_parameter:n}
+%<latexrelease> {Hooks~with~args}
+\cs_new:Npn \@@_braced_parameter:n #1
+ {
+ \if_case:w
+ \int_eval:n
+ { \exp_args:Nv \str_count:n { c_@@_#1_parameter_tl } / 3 }
+ \exp_stop_f:
+ \or: {##1}
+ \or: {##1} {##2}
+ \or: {##1} {##2} {##3}
+ \or: {##1} {##2} {##3} {##4}
+ \or: {##1} {##2} {##3} {##4} {##5}
+ \or: {##1} {##2} {##3} {##4} {##5} {##6}
+ \or: {##1} {##2} {##3} {##4} {##5} {##6} {##7}
+ \or: {##1} {##2} {##3} {##4} {##5} {##6} {##7} {##8}
+ \or: {##1} {##2} {##3} {##4} {##5} {##6} {##7} {##8} {##9}
+ \else:
+ \msg_expandable_error:nnn { latex2e } { should-not-happen }
+ { Invalid~parameter~spec. }
+ \fi:
+ }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_braced_parameter:n}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_undefine:N \@@_braced_parameter:n
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_parameter:n}
+% This is just a shortcut to \verb|e|- or \verb|f|-expand to the
+% \meta{parameter text} of the hook.
+% \changes{v1.1a}{2023/04/06}
+% {Macro added (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_parameter:n}
+%<latexrelease> {Hooks~with~args}
+\cs_new:Npn \@@_parameter:n #1
+ {
+ \cs:w c_@@_
+ \tl_if_exist:cTF { c_@@_#1_parameter_tl }
+ { #1_parameter } { empty }
+ _tl \cs_end:
+ }
+\cs_new:Npn \@@_generic_parameter:n #1
+ { \@@_generic_parameter:w #1 / / / \s_@@_mark }
+\cs_new:Npn \@@_generic_parameter:w #1 / #2 / #3 / #4 \s_@@_mark
+ {
+ \cs_if_exist_use:cF { c_@@_parameter_#1/./#3_tl }
+ { \c_@@_empty_tl }
+ }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_parameter:n}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_undefine:N \@@_parameter:n
+%<latexrelease>\cs_undefine:N \@@_generic_parameter:n
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \subsection{Setting rules for hooks code}
%
% \begin{macro}{
% \g_@@_??_code_prop,
% \@@~??,
% \g_@@_??_reversed_tl,
+% \c_@@_??_parameter_tl,
% }
%
% Initially these variables simply used an empty ``label'' name (not
@@ -3597,11 +4931,11 @@
% 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.
+% \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 {@@~??}
+\prop_new:c { g_@@_??_code_prop }
+\prop_new:c { @@~?? }
% \end{macrocode}
%
% Default rules are always given in normal ordering (never in
@@ -3610,11 +4944,23 @@
% \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}
+\tl_new:c { g_@@_??_reversed_tl }
% \end{macrocode}
-% \end{macro}
%
-% \subsection{Setting rules for hooks code}
+% The parameter text for the \enquote{default} hook is empty.
+% \changes{v1.1a}{2023/04/06}
+% {Token list added (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\c_@@_??_parameter_tl}
+%<latexrelease> {Hooks~with~args}
+\tl_const:cn { c_@@_??_parameter_tl } { }
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\c_@@_??_parameter_tl}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_undefine:c { c_@@_??_parameter_tl }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
%
% \begin{macro}{\hook_gset_rule:nnnn}
% \begin{macro}{\@@_gset_rule:nnnn}
@@ -3645,13 +4991,13 @@
\@@_deprecated_generic_warn:n {#1}
\@@_do_deprecated_generic:Nn \@@_gset_rule:nnnn {#1}
{#2} {#3} {#4}
- \@@_clean_to_scan:w
+ \@@_use_none_delimit_by_s_mark:w
}
\@@_if_execute_immediately:nT {#1}
{
\msg_error:nnnnnn { hooks } { rule-too-late }
{#1} {#2} {#3} {#4}
- \@@_clean_to_scan:w
+ \@@_use_none_delimit_by_s_mark:w
}
% \end{macrocode}
% First we ensure the basic data structure of the hook exists:
@@ -3840,44 +5186,82 @@
% \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.
+% in the right order.
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
% \begin{macrocode}
-\cs_new_protected:Npn \@@_initialize_all: {
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_initialize_all:}
+%<latexrelease> {Hooks~with~args}
+\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
+ \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.
+% of them. Here we have to determine if the hook has arguments so
+% that auxiliaries know what to do with hashes. We look at
+% \cs[no-index]{c_@@_\meta{hook}_parameter_tl}, if it has any
+% parameters, and set \verb|replacing_args| accordingly.
% \begin{macrocode}
- \@@_debug:n { \prop_gclear:N \g_@@_used_prop }
- \seq_map_inline:Nn \g_@@_all_seq
+ \@@_debug:n { \prop_gclear:N \g_@@_used_prop }
+ \seq_map_inline:Nn \g_@@_all_seq
{
+ \tl_if_empty:cTF { c_@@_##1_parameter_tl }
+ { \@@_replacing_args_false: }
+ { \@@_replacing_args_true: }
\@@_update_hook_code:n {##1}
+ \@@_replacing_args_reset:
}
% \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}~ }
- }
- }
+ \@@_debug:n
+ {
+ \iow_term:x { ^^J All~initialized~(non-empty)~hooks: }
+ \prop_map_inline:Nn \g_@@_used_prop
+ {
+ \iow_term:x
+ { ^^J ~ ##1 ~ -> ~ \cs_replacement_spec:c { @@~##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
-}
+ \@@_post_initialization_defs:
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_initialize_all:}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_gset_protected:Npn \@@_initialize_all:
+%<latexrelease> {
+%<latexrelease> \cs_gset_eq:NN \@@_update_hook_code:n \@@_initialize_hook_code:n
+%<latexrelease> \@@_debug:n { \prop_gclear:N \g_@@_used_prop }
+%<latexrelease> \seq_map_inline:Nn \g_@@_all_seq
+%<latexrelease> { \@@_update_hook_code:n {##1} }
+%<latexrelease> \@@_debug:n
+%<latexrelease> {
+%<latexrelease> \iow_term:x{^^JAll~ initialized~ (non-empty)~ hooks:}
+%<latexrelease> \prop_map_inline:Nn \g_@@_used_prop
+%<latexrelease> {
+%<latexrelease> \iow_term:x
+%<latexrelease> { ^^J ~ ##1 ~ -> ~ \cs_replacement_spec:c { @@~##1 } ~ }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> \cs_gset_eq:NN \hook_use:n \@@_use_initialized:n
+%<latexrelease> \cs_gset_eq:NN \@@_preamble_hook:n \use_none:n
+%<latexrelease> }
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
@@ -3887,7 +5271,11 @@
% 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.
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_initialize_hook_code:n}
+%<latexrelease> {Hooks~with~args}
\cs_new_protected:Npn \@@_initialize_hook_code:n #1
{
\@@_debug:n
@@ -3915,10 +5303,15 @@
{
\prop_if_empty:cTF { g_@@_#1_code_prop }
{
- \@@_tl_gset:co { @@~#1 }
+ \@@_code_gset:ne {#1}
{
- \cs:w @@_toplevel~#1 \exp_after:wN \cs_end:
- \cs:w @@_next~#1 \cs_end:
+% \end{macrocode}
+% The hook may take arguments, so we add a run of braced parameters
+% after the \verb|_next| and \verb|_toplevel| macros, so that the
+% arguments passed to the hook are forwarded to them.
+% \begin{macrocode}
+ \exp_not:c { @@_toplevel~#1 } \@@_braced_parameter:n {#1}
+ \exp_not:c { @@_next~#1 } \@@_braced_parameter:n {#1}
}
}
{
@@ -3958,6 +5351,42 @@
}
}
}
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_initialize_hook_code:n}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_gset_protected:Npn \@@_initialize_hook_code:n #1
+%<latexrelease> {
+%<latexrelease> \@@_debug:n
+%<latexrelease> { \iow_term:x { ^^J Update~code~for~hook~'#1' \on@line :^^J } }
+%<latexrelease> \@@_include_legacy_code_chunk:n {#1}
+%<latexrelease> \@@_if_usable:nT {#1}
+%<latexrelease> {
+%<latexrelease> \prop_if_empty:cTF { g_@@_#1_code_prop }
+%<latexrelease> {
+%<latexrelease> \@@_tl_gset:co { @@~#1 }
+%<latexrelease> {
+%<latexrelease> \cs:w @@_toplevel~#1 \exp_after:wN \cs_end:
+%<latexrelease> \cs:w @@_next~#1 \cs_end:
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> {
+%<latexrelease> \@@_if_reversed:nTF {#1}
+%<latexrelease> { \cs_set_eq:NN \@@_tl_gput:Nn \@@_tl_gput_left:Nn
+%<latexrelease> \cs_set_eq:NN \@@_clist_gput:NV \clist_gput_left:NV }
+%<latexrelease> { \cs_set_eq:NN \@@_tl_gput:Nn \@@_tl_gput_right:Nn
+%<latexrelease> \cs_set_eq:NN \@@_clist_gput:NV \clist_gput_right:NV }
+%<latexrelease> \prop_set_eq:Nc \l_@@_work_prop { g_@@_#1_code_prop }
+%<latexrelease> \@@_initialize_single:ccn
+%<latexrelease> { @@~#1 } { g_@@_#1_labels_clist } {#1}
+%<latexrelease> \@@_debug:n
+%<latexrelease> { \exp_args:NNx \prop_gput:Nnn \g_@@_used_prop {#1} { } }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
@@ -4034,7 +5463,12 @@
% ^^A #1 <- \@@~#1
% ^^A #2 <- \g_@@_#1_labels_clist
% ^^A #3 <- #1
+%
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_initialize_single:NNn}
+%<latexrelease> {Hooks~with~args}
\cs_new_protected:Npn \@@_initialize_single:NNn #1#2#3
{
% \end{macrocode}
@@ -4181,15 +5615,103 @@
% 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: }
+% chunk added). The |next| code is always added last (to the right).
+% The hook may take arguments, so we add a run of braced parameters
+% after the \verb|_next| and \verb|_toplevel| macros, so that the
+% arguments passed to the hook are forwarded to them.
+% \begin{macrocode}
+ \exp_args:NNe \@@_tl_gput:Nn #1
+ { \exp_not:c { @@_toplevel~#3 } \@@_braced_parameter:n {#3} }
+ \@@_tl_gput_right:Ne #1
+ { \exp_not:c { @@_next~#3 } \@@_braced_parameter:n {#3} }
+ \use:e
+ {
+ \cs_gset:cpn { @@~#3 } \use:c { c_@@_#3_parameter_tl }
+ { \exp_not:V #1 }
+ }
}
% \end{macrocode}
%
% \begin{macrocode}
\cs_generate_variant:Nn \@@_initialize_single:NNn { cc }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_initialize_single:NNn}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_new_protected:Npn \@@_initialize_single:NNn #1#2#3
+%<latexrelease> {
+%<latexrelease> \seq_clear:N \l_@@_labels_seq
+%<latexrelease> \int_zero:N \l_@@_labels_int
+%<latexrelease> \tl_set:Nn \l_@@_cur_hook_tl {#3}
+%<latexrelease> \prop_map_inline:Nn \l_@@_work_prop
+%<latexrelease> {
+%<latexrelease> \int_incr:N \l_@@_labels_int
+%<latexrelease> \seq_put_right:Nn \l_@@_labels_seq {##1}
+%<latexrelease> \@@_tl_set:cn { \@@_tl_csname:n {##1} } { 0 }
+%<latexrelease> \seq_clear_new:c { \@@_seq_csname:n {##1} }
+%<latexrelease> }
+%<latexrelease> \prop_map_inline:Nn \l_@@_work_prop
+%<latexrelease> {
+%<latexrelease> \prop_map_inline:Nn \l_@@_work_prop
+%<latexrelease> {
+%<latexrelease> \@@_if_label_case:nnnnn {##1} {####1}
+%<latexrelease> { \prop_map_break: }
+%<latexrelease> { \@@_apply_label_pair:nnn {##1} {####1} }
+%<latexrelease> { \@@_apply_label_pair:nnn {####1} {##1} }
+%<latexrelease> {#3}
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> \@@_debug:n { \@@_debug_label_data:N \l_@@_work_prop }
+%<latexrelease> \tl_set:Nn \l_@@_rear_tl { 0 }
+%<latexrelease> \tl_set:cn { \@@_tl_csname:n { 0 } } { 0 }
+%<latexrelease> \seq_map_inline:Nn \l_@@_labels_seq
+%<latexrelease> {
+%<latexrelease> \int_compare:nNnT { \cs:w \@@_tl_csname:n {##1} \cs_end: } = 0
+%<latexrelease> {
+%<latexrelease> \tl_set:cn { \@@_tl_csname:n { \l_@@_rear_tl } }{##1}
+%<latexrelease> \tl_set:Nn \l_@@_rear_tl {##1}
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> \tl_set_eq:Nc \l_@@_front_tl { \@@_tl_csname:n { 0 } }
+%<latexrelease> \@@_tl_gclear:N #1
+%<latexrelease> \clist_gclear:N #2
+%<latexrelease> \bool_while_do:nn { ! \str_if_eq_p:Vn \l_@@_front_tl { 0 } }
+%<latexrelease> {
+%<latexrelease> \int_decr:N \l_@@_labels_int
+%<latexrelease> \prop_get:NVN \l_@@_work_prop \l_@@_front_tl \l_@@_return_tl
+%<latexrelease> \exp_args:NNV \@@_tl_gput:Nn #1 \l_@@_return_tl
+%<latexrelease> \@@_clist_gput:NV #2 \l_@@_front_tl
+%<latexrelease> \@@_debug:n{ \iow_term:x{Handled~ code~ for~ \l_@@_front_tl} }
+%<latexrelease> \seq_map_inline:cn { \@@_seq_csname:n { \l_@@_front_tl } }
+%<latexrelease> {
+%<latexrelease> \tl_set:cx { \@@_tl_csname:n {##1} }
+%<latexrelease> { \int_eval:n
+%<latexrelease> { \cs:w \@@_tl_csname:n {##1} \cs_end: - 1 }
+%<latexrelease> }
+%<latexrelease> \int_compare:nNnT
+%<latexrelease> { \cs:w \@@_tl_csname:n {##1} \cs_end: } = 0
+%<latexrelease> {
+%<latexrelease> \tl_set:cn { \@@_tl_csname:n { \l_@@_rear_tl } } {##1}
+%<latexrelease> \tl_set:Nn \l_@@_rear_tl {##1}
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> \tl_set_eq:Nc \l_@@_front_tl
+%<latexrelease> { \@@_tl_csname:n { \l_@@_front_tl } }
+%<latexrelease> }
+%<latexrelease> \int_compare:nNnF \l_@@_labels_int = 0
+%<latexrelease> {
+%<latexrelease> \iow_term:x{====================}
+%<latexrelease> \iow_term:x{Error:~ label~ rules~ are~ incompatible:}
+%<latexrelease> \@@_debug_label_data:N \l_@@_work_prop
+%<latexrelease> \iow_term:x{====================}
+%<latexrelease> }
+%<latexrelease> \exp_args:NNo \@@_tl_gput:Nn #1 { \cs:w @@_toplevel~#3 \cs_end: }
+%<latexrelease> \@@_tl_gput_right:No #1 { \cs:w @@_next~#3 \cs_end: }
+%<latexrelease> }
+%<latexrelease>\cs_generate_variant:Nn \@@_tl_gput_right:Nn { No }
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
@@ -4444,7 +5966,11 @@
{ \@@_log_cmd:x { >~\@spaces #1 } }
% \end{macrocode}
%
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_log:nN}
+%<latexrelease> {Hooks~with~args}
\cs_new_protected:Npn \@@_log:nN #1 #2
{
\@@_if_deprecated_generic:nT {#1}
@@ -4455,7 +5981,20 @@
}
\@@_preamble_hook:n {#1}
\@@_log_cmd:x
- { ^^J ->~The~ \@@_if_generic:nT {#1} { generic~ } hook~'#1': }
+ {
+ ^^J ->~The~
+ \@@_if_generic:nT {#1} { generic~ }
+ hook~'#1'
+ \@@_if_disabled:nF {#1}
+ {
+ \exp_args:Nf \@@_print_args:nn {#1}
+ {
+ \int_eval:n
+ { \str_count:e { \@@_parameter:n {#1} } / 3 }
+ }
+ }
+ :
+ }
% \end{macrocode}
%
% \begin{macrocode}
@@ -4471,7 +6010,12 @@
{ \@@_log_line_indent:x { --- } }
{
\prop_map_inline:cn { g_@@_#1_code_prop }
- { \@@_log_line_indent:x { ##1~->~\tl_to_str:n {##2} } }
+ {
+ \exp_after:wN \cs_set:Npn \exp_after:wN \@@_tmp:w
+ \c_@@_nine_parameters_tl {##2}
+ \@@_log_line_indent:x
+ { ##1~->~\cs_replacement_spec:N \@@_tmp:w }
+ }
}
% \end{macrocode}
%
@@ -4485,9 +6029,9 @@
}
\@@_log_line_indent:x
{
- \tl_if_empty:cTF { @@_toplevel~#1 }
+ \@@_cs_if_empty:cTF { @@_toplevel~#1 }
{ --- }
- { -> ~ \exp_args:Nv \tl_to_str:n { @@_toplevel~#1 } }
+ { -> ~ \cs_replacement_spec:c { @@_toplevel~#1 } }
}
% \end{macrocode}
%
@@ -4495,7 +6039,7 @@
\@@_log_line:x { Extra~code~for~next~invocation: }
\@@_log_line_indent:x
{
- \tl_if_empty:cTF { @@_next~#1 }
+ \@@_cs_if_empty:cTF { @@_next~#1 }
{ --- }
% \end{macrocode}
%
@@ -4503,7 +6047,10 @@
% 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 } }
+ {
+ -> ~ \exp_last_unbraced:Nf \@@_log_next_code:w
+ { \cs_replacement_spec:c { @@_next~#1 } }
+ }
}
% \end{macrocode}
%
@@ -4549,7 +6096,7 @@
\@spaces
\clist_if_empty:cTF { g_@@_#1_labels_clist }
{ --- }
- { \clist_use:cn {g_@@_#1_labels_clist} { ,~ } }
+ { \clist_use:cn { g_@@_#1_labels_clist } { ,~ } }
}
}
{
@@ -4563,15 +6110,139 @@
}
}
}
+%<latexrelease>\EndIncludeInRelease
+%
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_log:nN}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_new_protected:Npn \@@_log:nN #1 #2
+%<latexrelease> {
+%<latexrelease> \@@_if_deprecated_generic:nT {#1}
+%<latexrelease> {
+%<latexrelease> \@@_deprecated_generic_warn:n {#1}
+%<latexrelease> \@@_do_deprecated_generic:Nn \@@_log:nN {#1} #2
+%<latexrelease> \exp_after:wN \use_none:nnnnnnnnn \use_none:nnnnn
+%<latexrelease> }
+%<latexrelease> \@@_preamble_hook:n {#1}
+%<latexrelease> \@@_log_cmd:x
+%<latexrelease> { ^^J ->~The~ \@@_if_generic:nT {#1} { generic~ } hook~'#1': }
+%<latexrelease> \@@_if_usable:nF {#1}
+%<latexrelease> { \@@_log_line:x { The~hook~is~not~declared. } }
+%<latexrelease> \@@_if_disabled:nT {#1}
+%<latexrelease> { \@@_log_line:x { The~hook~is~disabled. } }
+%<latexrelease> \hook_if_empty:nTF {#1}
+%<latexrelease> { #2 { The~hook~is~empty } }
+%<latexrelease> {
+%<latexrelease> \@@_log_line:x { Code~chunks: }
+%<latexrelease> \prop_if_empty:cTF { g_@@_#1_code_prop }
+%<latexrelease> { \@@_log_line_indent:x { --- } }
+%<latexrelease> {
+%<latexrelease> \prop_map_inline:cn { g_@@_#1_code_prop }
+%<latexrelease> { \@@_log_line_indent:x { ##1~->~\tl_to_str:n {##2} } }
+%<latexrelease> }
+%<latexrelease> \@@_log_line:x
+%<latexrelease> {
+%<latexrelease> Document-level~(top-level)~code
+%<latexrelease> \@@_if_usable:nT {#1}
+%<latexrelease> { ~(executed~\@@_if_reversed:nTF {#1} {first} {last} ) } :
+%<latexrelease> }
+%<latexrelease> \@@_log_line_indent:x
+%<latexrelease> {
+%<latexrelease> \tl_if_empty:cTF { @@_toplevel~#1 }
+%<latexrelease> { --- }
+%<latexrelease> { -> ~ \exp_args:Nv \tl_to_str:n { @@_toplevel~#1 } }
+%<latexrelease> }
+%<latexrelease> \@@_log_line:x { Extra~code~for~next~invocation: }
+%<latexrelease> \@@_log_line_indent:x
+%<latexrelease> {
+%<latexrelease> \tl_if_empty:cTF { @@_next~#1 }
+%<latexrelease> { --- }
+%<latexrelease> { ->~ \exp_args:Nv \@@_log_next_code:n { @@_next~#1 } }
+%<latexrelease> }
+%<latexrelease> \@@_log_line:x { Rules: }
+%<latexrelease> \bool_set_true:N \l_@@_tmpa_bool
+%<latexrelease> \@@_list_rules:nn {#1}
+%<latexrelease> {
+%<latexrelease> \bool_set_false:N \l_@@_tmpa_bool
+%<latexrelease> \@@_log_line_indent:x
+%<latexrelease> {
+%<latexrelease> ##2~ with~
+%<latexrelease> \str_if_eq:nnT {##3} {??} { default~ }
+%<latexrelease> relation~ ##1
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> \bool_if:NT \l_@@_tmpa_bool
+%<latexrelease> { \@@_log_line_indent:x { --- } }
+%<latexrelease> \bool_lazy_and:nnTF
+%<latexrelease> { \@@_if_usable_p:n {#1} }
+%<latexrelease> { ! \hook_if_empty_p:n {#1} }
+%<latexrelease> {
+%<latexrelease> \@@_log_line:x
+%<latexrelease> {
+%<latexrelease> Execution~order
+%<latexrelease> \bool_if:NTF \l_@@_tmpa_bool
+%<latexrelease> { \@@_if_reversed:nT {#1} { ~(after~reversal) } }
+%<latexrelease> { ~(after~
+%<latexrelease> \@@_if_reversed:nT {#1} { reversal~and~ }
+%<latexrelease> applying~rules)
+%<latexrelease> } :
+%<latexrelease> }
+%<latexrelease> #2 % \tl_show:n
+%<latexrelease> {
+%<latexrelease> \@spaces
+%<latexrelease> \clist_if_empty:cTF { g_@@_#1_labels_clist }
+%<latexrelease> { --- }
+%<latexrelease> { \clist_use:cn { g_@@_#1_labels_clist } { ,~ } }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> {
+%<latexrelease> \@@_log_line:x { Execution~order: }
+%<latexrelease> #2
+%<latexrelease> {
+%<latexrelease> \@spaces Not~set~because~the~hook~ \@@_if_usable:nTF {#1}
+%<latexrelease> { code~pool~is~empty }
+%<latexrelease> { is~\@@_if_disabled:nTF {#1} {disabled} {undeclared} }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease>\EndIncludeInRelease
% \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.
+% \cs{AddToHookNext} we have to remove the string
+% \cs{@@_clear_next:n}\Arg{hook}, so the simplest is to use a macro
+% delimited by a \verb|}|$_12$.
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_log_next_code:n}
+%<latexrelease> {Hooks~with~args}
+\exp_last_unbraced:NNNNo
+\cs_new:Npn \@@_log_next_code:w #1 \c_right_brace_str { }
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_log_next_code:n}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_gset:Npn \@@_log_next_code:n #1
+%<latexrelease> { \exp_args:No \tl_to_str:n { \use_none:nn #1 } }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_print_args:n}
+% Pretty-prints the number of arguments of a hook.
% \begin{macrocode}
-\cs_new:Npn \@@_log_next_code:n #1
- { \exp_args:No \tl_to_str:n { \use_none:nn #1 } }
+\cs_new:Npn \@@_print_args:nn #1 #2
+ {
+ \int_compare:nNnT {#2} > { 0 }
+ {
+ \@@_if_declared:nT {#1} { \use_none:nnn }
+ \@@_if_cmd_hook:nT {#1}
+ { \use_i:nnn { ~ (unknown ~ } }
+ \use:n { ~ (#2 ~ }
+ argument \int_compare:nNnT {#2} > { 1 } { s } )
+ }
+ }
% \end{macrocode}
% \end{macro}
%
@@ -4656,15 +6327,34 @@
% \subsection{Specifying code for next invocation}
%
% \begin{macro}{\hook_gput_next_code:nn}
+% \changes{v1.1a}{2023/04/06}
+% {Add \cs{hook_gput_next_code_with_args:nn} (hook-args).}
% \begin{macrocode}
-\cs_new_protected:Npn \hook_gput_next_code:nn #1
- { \@@_normalize_hook_args:Nn \@@_gput_next_code:nn {#1} }
+%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_gput_next_code:nn}
+%<latexrelease> {Hooks~with~args}
+\cs_new_protected:Npn \hook_gput_next_code:nn #1 #2
+ {
+ \@@_replacing_args_false:
+ \@@_normalize_hook_args:Nn \@@_gput_next_code:nn {#1} {#2}
+ \@@_replacing_args_reset:
+ }
+\cs_new_protected:Npn \hook_gput_next_code_with_args:nn #1 #2
+ {
+ \@@_replacing_args_true:
+ \@@_normalize_hook_args:Nn \@@_gput_next_code:nn {#1} {#2}
+ \@@_replacing_args_reset:
+ }
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_gput_next_code:nn}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_gset_protected:Npn \hook_gput_next_code:nn #1
+%<latexrelease> { \@@_normalize_hook_args:Nn \@@_gput_next_code:nn {#1} }
+%<latexrelease>\cs_gset_protected:Npn \hook_gput_next_code_with_args:nn #1 #2 { }
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\@@_gput_next_code:nn,
-% \@@_gput_next_do:nn,
-% \@@_gput_next_do:Nnn}
+% \begin{macro}{\@@_gput_next_code:nn}
% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_next_code:nn #1 #2
{
@@ -4672,20 +6362,17 @@
{ \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} }
+ { \@@_gput_next_do:nn }
+ { \@@_try_declaring_generic_next_hook:nn }
+ {#1} {#2}
}
}
% \end{macrocode}
+% \end{macro}
%
-% \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
+% \begin{macro}{\@@_gput_next_do:nn}
+% Start by sanity-checking with \cs{@@_chk_args_allowed:nn}.
+% Then 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
@@ -4694,38 +6381,74 @@
% 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.
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
+% \changes{v1.1c}{2023/04/19}
+% {Initialise hook structure when adding 'next' code (gh/1052).}
% \begin{macrocode}
-\cs_new_protected:Npn \@@_gput_next_do:Nnn #1 #2
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_gput_next_do:nn}
+%<latexrelease> {Hooks~with~args}
+\cs_new_protected:Npn \@@_gput_next_do:nn #1
{
- \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
+ \@@_init_structure:n {#1}
+ \@@_chk_args_allowed:nn {#1} { AddToHookNext }
+ \@@_cs_if_empty:cT { @@~#1 }
+ { \@@_update_hook_code:n {#1} }
+ \@@_cs_if_empty:cT { @@_next~#1 }
+ { \@@_next_gset:nn {#1} { \@@_clear_next:n {#1} } }
+ \@@_cs_gput_right:nnn { _next } {#1}
}
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_gput_next_do:nn}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_gset_protected:Npn \@@_gput_next_do:nn #1
+%<latexrelease> {
+%<latexrelease> \exp_args:Nc \@@_gput_next_do:Nnn
+%<latexrelease> { @@_next~#1 } {#1}
+%<latexrelease> }
+%<latexrelease>\cs_gset_protected:Npn \@@_gput_next_do:Nnn #1 #2
+%<latexrelease> {
+%<latexrelease> \tl_if_empty:cT { @@~#2 }
+%<latexrelease> { \@@_update_hook_code:n {#2} }
+%<latexrelease> \tl_if_empty:NT #1
+%<latexrelease> { \@@_tl_gset:Nn #1 { \@@_clear_next:n {#2} } }
+%<latexrelease> \@@_tl_gput_right:Nn #1
+%<latexrelease> }
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
-%
-%
-% \begin{macro}{\hook_gclear_next_code:n,\@@_clear_next:n}
+% \begin{macro}{\hook_gclear_next_code: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}
%
+% \begin{macro}{\@@_clear_next:n}
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_clear_next:n}
+%<latexrelease> {Hooks~with~args}
+\cs_new_protected:Npn \@@_clear_next:n #1
+ { \@@_next_gset:nn {#1} { } }
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_clear_next:n}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_gset_protected:Npn \@@_clear_next:n #1
+%<latexrelease> { \cs_gset_eq:cN { @@_next~#1 } \c_empty_tl }
+%<latexrelease>\EndIncludeInRelease
+% \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
@@ -4750,24 +6473,50 @@
% \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}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_use:n}
+%<latexrelease> {Hooks~with~args}
\cs_new_protected:Npn \hook_use:n #1
{
- \tl_if_exist:cT { @@~#1 }
- {
- \@@_preamble_hook:n {#1}
- \cs:w @@~#1 \cs_end:
- }
+ \@@_preamble_hook:n {#1}
+ \@@_use_initialized:n {#1}
}
\cs_new:Npn \@@_use_initialized:n #1
{
\if_cs_exist:w @@~#1 \cs_end:
- \cs:w @@~#1 \exp_after:wN \cs_end:
+ \cs:w @@~#1 \use_i:nn
\fi:
+ \use_none:n
+ \cs_end:
}
\cs_new_protected:Npn \@@_preamble_hook:n #1
- { \@@_initialize_hook_code:n {#1} }
+ {
+ \if_cs_exist:w @@~#1 \cs_end:
+ \@@_initialize_hook_code:n {#1}
+ \fi:
+ }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2021/11/15}{\hook_use:n}
+%<latexrelease> {Standardise~generic~hook~names}
+%<latexrelease>\cs_new_protected:Npn \hook_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>\cs_new:Npn \@@_use_initialized:n #1
+%<latexrelease> {
+%<latexrelease> \if_cs_exist:w @@~#1 \cs_end:
+%<latexrelease> \cs:w @@~#1 \exp_after:wN \cs_end:
+%<latexrelease> \fi:
+%<latexrelease> }
+%<latexrelease>\cs_new_protected:Npn \@@_preamble_hook:n #1
+%<latexrelease> { \@@_initialize_hook_code:n {#1} }
+%<latexrelease>\cs_new:Npn \hook_use:nnw #1 { }
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
%
@@ -4799,13 +6548,69 @@
%<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>\cs_new:Npn \hook_use:nnw #1 { }
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
+%
+%
+% \begin{macro}{\hook_use:nnw}
+% \begin{macro}[EXP]{\@@_use_initialized:nnw}
+% \changes{v1.1a}{2023/04/06}
+% {Add \cs{hook_use:nnw} (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_use:nnw}
+%<latexrelease> {Hooks~with~args}
+\cs_new_protected:Npn \hook_use:nnw #1
+ {
+ \@@_preamble_hook:n {#1}
+ \@@_use_initialized:nnw {#1}
+ }
+\cs_new:Npn \@@_use_initialized:nnw #1 #2
+ {
+ \cs:w
+ \if_cs_exist:w @@~#1 \cs_end:
+ @@~#1
+ \else:
+ use_none: \prg_replicate:nn {#2} { n }
+ \fi:
+ \cs_end:
+ }
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_use:nnw}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_gset:Npn \hook_use:nnw #1 #2
+%<latexrelease> { \use:c { use_none: \prg_replicate:nn {#2} { n } } }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+%
+% \begin{macro}{\@@_post_initialization_defs:}
+% \changes{v1.1a}{2023/04/06}
+% {Macro added (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_post_initialization_defs:}
+%<latexrelease> {Hooks~with~args}
+\cs_new_protected:Npn \@@_post_initialization_defs:
+ {
+ \cs_gset_eq:NN \hook_use:n \@@_use_initialized:n
+ \cs_gset_eq:NN \hook_use:nnw \@@_use_initialized:nnw
+ \cs_gset_eq:NN \@@_preamble_hook:n \use_none:n
+ \cs_gset_eq:NN \@@_post_initialization_defs: \prg_do_nothing:
+ }
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_post_initialization_defs:}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_undefine:N \@@_post_initialization_defs:
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
% \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
@@ -4861,8 +6666,7 @@
% \end{macro}
% \end{macro}
%
-% \begin{macro}{\hook_use_once:n}
-% \begin{macro}{\@@_use_once:n}
+% \begin{macro}{\hook_use_once:n,\hook_use_once:nnw}
% 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
@@ -4874,20 +6678,80 @@
% documented to not trim spaces.
%
% \changes{v1.0r}{2021/09/06}{Clean up after \cs{UseOneTimeHook} (gh/606)}
+% \changes{v1.1a}{2023/04/06}
+% {Add \cs{hook_use_once:nnw} (hook-args).}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_use_once:nnw}
+%<latexrelease> {Hooks~with~args}
\cs_new_protected:Npn \hook_use_once:n #1
{
\@@_if_execute_immediately:nF {#1}
- { \@@_normalize_hook_args:Nn \@@_use_once:n { \use:n {#1} } }
+ { \@@_normalize_hook_args:Nn \@@_use_once:nn { \use:n {#1} } { 0 } }
}
-\cs_new_protected:Npn \@@_use_once:n #1
+\cs_new_protected:Npn \hook_use_once:nnw #1 #2
+ {
+ \@@_if_execute_immediately:nF {#1}
+ { \@@_normalize_hook_args:Nn \@@_use_once:nn { \use:n {#1} } {#2} }
+ }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_use_once:nnw}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_gset_protected:Npn \hook_use_once:n #1
+%<latexrelease> {
+%<latexrelease> \@@_if_execute_immediately:nF {#1}
+%<latexrelease> { \@@_normalize_hook_args:Nn \@@_use_once:n { \use:n {#1} } }
+%<latexrelease> }
+%<latexrelease>\cs_gset:Npn \hook_use_once:nnw #1 #2
+%<latexrelease> { \use:c { use_none: \prg_replicate:nn {#2} { n } } }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macro}{\@@_use_once:nn}
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_use_once:nn}
+%<latexrelease> {Hooks~with~args}
+\cs_new_protected:Npn \@@_use_once:nn #1 #2
{
\@@_preamble_hook:n {#1}
\@@_use_once_set:n {#1}
- \@@_use_initialized:n {#1}
- \@@_use_once_clear:n {#1}
+% \end{macrocode}
+% When a hook has arguments, the call to \cs{@@_use_initialized:n},
+% should be the very last thing to happen, otherwise the arguments
+% grabbed will be wrong. So, to clean up after the hook we need to
+% cheat a bit and sneak the cleanup code at the end of the hook,
+% along with the next execution code.
+% \begin{macrocode}
+ \@@_replacing_args_false:
+ \@@_cs_gput_right:nnn { _next } {#1} { \@@_use_once_clear:n {#1} }
+ \@@_replacing_args_reset:
+ \@@_if_usable:nTF {#1}
+ { \@@_use_initialized:n {#1} }
+ {
+ \int_compare:nNnT {#2} > { 0 }
+ { \use:c { use_none: \prg_replicate:nn {#2} { n } } }
+ }
}
+%<latexrelease>\EndIncludeInRelease
+%
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_use_once:nn}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_gset_protected:Npn \@@_use_once:n #1
+%<latexrelease> {
+%<latexrelease> \@@_preamble_hook:n {#1}
+%<latexrelease> \@@_use_once_set:n {#1}
+%<latexrelease> \@@_use_initialized:n {#1}
+%<latexrelease> \@@_use_once_clear:n {#1}
+%<latexrelease> }
+%<latexrelease>\cs_undefine:N \@@_use_once:nn
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
+% \end{macro}
%
% \begin{macro}{\@@_use_once_set:n}
% \begin{macro}{\@@_use_once_clear:n}
@@ -4900,43 +6764,60 @@
% \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}
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_use_once_clear:n}
+%<latexrelease> {Hooks~with~args}
\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 }
+ \@@_code_gset:nn {#1} { }
+ \@@_next_gset:nn {#1} { }
+ \@@_toplevel_gset:nn {#1} { }
\prop_gclear_new:c { g_@@_#1_code_prop }
}
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_use_once_clear:n}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_new_protected:Npn \@@_use_once_clear:n #1
+%<latexrelease> {
+%<latexrelease> \@@_tl_gclear:c { @@~#1 }
+%<latexrelease> \@@_tl_gclear:c { @@_next~#1 }
+%<latexrelease> \@@_tl_gclear:c { @@_toplevel~#1 }
+%<latexrelease> \prop_gclear_new:c { g_@@_#1_code_prop }
+%<latexrelease> }
+%<latexrelease>\EndIncludeInRelease
% \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
+% To check whether the code being added should be executed immediately
+% (that is, if the hook is a one-time hook), we check if
% \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)}
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
% \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:
+ \exp_after:wN \@@_use_none_delimit_by_s_mark:w
+ \if:w I
+ \if_cs_exist:w g_@@_#1_reversed_tl \cs_end:
+ \cs:w g_@@_#1_reversed_tl \exp_after:wN \cs_end:
\fi:
- }
- { \prg_return_false: }
+ X
+ \s_@@_mark \prg_return_true:
+ \else:
+ \s_@@_mark \prg_return_false:
+ \fi:
}
% \end{macrocode}
% \end{macro}
@@ -4963,30 +6844,57 @@
%
% 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.
+% \changes{v1.1a}{2023/04/06}
+% {Changes to add hook arguments (hook-args).}
+% \changes{v1.1c}{2023/04/19}
+% {Simpler and faster version (gh/1052).}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\hook_if_empty:n}
+%<latexrelease> {Hooks~with~args}
\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: }
+ \if:w
+ T
+ \prop_if_exist:cT { g_@@_#1_code_prop }
+ { \prop_if_empty:cF { g_@@_#1_code_prop } { F } }
+ \@@_cs_if_empty:cF { @@_toplevel~#1 } { F }
+ \@@_cs_if_empty:cF { @@_next~#1 } { F }
+ T
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
}
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\hook_if_empty:n}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\prg_new_conditional:Npnn \hook_if_empty:n #1 { p , T , F , TF }
+%<latexrelease> {
+%<latexrelease> \@@_if_structure_exist:nTF {#1}
+%<latexrelease> {
+%<latexrelease> \bool_lazy_and:nnTF
+%<latexrelease> { \prop_if_empty_p:c { g_@@_#1_code_prop } }
+%<latexrelease> {
+%<latexrelease> \bool_lazy_and_p:nn
+%<latexrelease> { \tl_if_empty_p:c { @@_toplevel~#1 } }
+%<latexrelease> { \tl_if_empty_p:c { @@_next~#1 } }
+%<latexrelease> }
+%<latexrelease> { \prg_return_true: }
+%<latexrelease> { \prg_return_false: }
+%<latexrelease> }
+%<latexrelease> { \prg_return_true: }
+%<latexrelease> }
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
@@ -4994,15 +6902,15 @@
% \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
+% \cs[no-index]{@@\textvisiblespace\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 }
+ \cs_if_exist:cTF { @@~#1 }
{ \prg_return_true: }
{ \prg_return_false: }
}
@@ -5046,7 +6954,7 @@
% \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_reversed:n #1 { p , T , F , TF }
{
- \exp_after:wN \@@_clean_to_scan:w
+ \exp_after:wN \@@_use_none_delimit_by_s_mark:w
\if:w - \cs:w g_@@_#1_reversed_tl \cs_end:
\s_@@_mark \prg_return_true:
\else:
@@ -5086,6 +6994,37 @@
% \end{macro}
% \end{macro}
%
+% \begin{macro}[pTF]{\@@_if_cmd_hook:n}
+% \begin{macro}[pTF]{\@@_if_cmd_hook:w}
+% An internal conditional that checks if a given hook is a valid
+% generic \hook{cmd} hook.
+% \changes{v1.1d}{2023/05/21}
+% {Changes to allow support arguments in cmd hooks (cmd-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\@@_if_cmd_hook:n}
+%<latexrelease> {Hooks~with~args}
+\prg_new_conditional:Npnn \@@_if_cmd_hook:n #1 { T }
+ { \@@_if_cmd_hook:w #1 / / / \s_@@_mark }
+\cs_new:Npn \@@_if_cmd_hook:w #1 / #2 / #3 / #4 \s_@@_mark
+ {
+ \if:w Y
+ \str_if_eq:nnF {#1} { cmd } { N }
+ \tl_if_exist:cF { c_@@_generic_#1/./#3_tl } { N }
+ Y
+ \prg_return_true:
+ \else:
+ \prg_return_false:
+ \fi:
+ }
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\@@_if_cmd_hook:n}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_undefine:N \@@_if_cmd_hook:nT
+%<latexrelease>\EndIncludeInRelease
+% \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.
@@ -5103,6 +7042,50 @@
% \end{macrocode}
% \end{macro}
%
+% \begin{macro}[EXP]{\@@_if_replacing_args:TF}
+% \begin{macro}[EXP]{\@@_misused_if_replacing_args:nn}
+% \begin{macro}{\@@_replacing_args_true:}
+% \begin{macro}{\@@_replacing_args_false:}
+% \begin{macro}{\@@_replacing_args_reset:}
+% \begin{macro}{\g_@@_replacing_stack_seq}
+% An internal conditional that checks if the code being added to the
+% hook contains arguments.
+% \changes{v1.1a}{2023/04/06}
+% {Macro added (hook-args).}
+% \begin{macrocode}
+\seq_new:N \g_@@_replacing_stack_seq
+\cs_new:Npn \@@_misused_if_replacing_args:nn #1 #2
+ {
+ \msg_expandable_error:nnn { latex2e } { should-not-happen }
+ { Misused~\@@_if_replacing_args:. }
+ }
+\cs_new:Npn \@@_if_replacing_args:TF
+ { \@@_misused_if_replacing_args:nn }
+\cs_new_protected:Npn \@@_replacing_args_true:
+ {
+ \seq_gpush:No \g_@@_replacing_stack_seq
+ { \@@_if_replacing_args:TF }
+ \cs_set:Npn \@@_if_replacing_args:TF { \use_i:nn }
+ }
+\cs_new_protected:Npn \@@_replacing_args_false:
+ {
+ \seq_gpush:No \g_@@_replacing_stack_seq
+ { \@@_if_replacing_args:TF }
+ \cs_set:Npn \@@_if_replacing_args:TF { \use_ii:nn }
+ }
+\cs_new_protected:Npn \@@_replacing_args_reset:
+ {
+ \seq_gpop:NN \g_@@_replacing_stack_seq \l_@@_return_tl
+ \cs_gset_eq:NN \@@_if_replacing_args:TF \l_@@_return_tl
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
% \subsection{Messages}
%
% Hook errors are LaTeX kernel errors:
@@ -5137,6 +7120,55 @@
Please~ use~ a~ different~ name~ for~ your~ hook.}
% \end{macrocode}
%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{too-many-args}
+%<latexrelease> {Hooks~with~args}
+% \end{macrocode}
+%
+% \changes{v1.1a}{2023/04/06}
+% {Messages 'too-many-args', 'without-args' and 'one-time-args' added (hook-args).}
+% \begin{macrocode}
+\msg_new:nnnn { hooks } { too-many-args }
+ { Too~many~arguments~for~hook~'#1'. }
+ {
+ You~tried~to~declare~a~hook~with~#2~arguments,~but~a~
+ hook~can~only~have~up~to~nine.~LaTeX~will~define~this~
+ hook~with~nine~arguments.
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\msg_new:nnnn { hooks } { without-args }
+ { Hook~'#1'~has~no~arguments. }
+ {
+ You~tried~to~use~\iow_char:N\\#2WithArguments~
+ on~a~hook~that~takes~no~arguments.\\
+ Check~the~usage~of~the~hook~or~use~\iow_char:N\\#2~instead.\\
+ \\
+ LaTeX~will~use~\iow_char:N\\#2.
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\msg_new:nnnn { hooks } { one-time-args }
+ { You~can't~have~arguments~in~used~one-time~hook~'#1'. }
+ {
+ You~tried~to~use~\iow_char:N\\#2WithArguments~
+ on~a~one-time~hook~that~has~already~been~used.~
+ You~have~to~add~the~code~before~the~hook~is~used,~
+ or~add~the~code~without~arguments~using~\iow_char:N\\#2~instead.\\
+ \\
+ LaTeX~will~use~\iow_char:N\\#2.
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{too-many-args}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
%
% \begin{macrocode}
\msg_new:nnnn { hooks } { hook-disabled }
@@ -5278,17 +7310,49 @@
% \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} }
+\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{macro}{
+% \NewHookWithArguments,
+% \NewReversedHookWithArguments,
+% \NewMirroredHookPairWithArguments
+% }
+% Declaring new hooks with arguments\ldots
+% \changes{v1.1a}{2023/04/06}
+% {Add \cs{NewHookWithArguments} (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\NewHookWithArguments}
+%<latexrelease> {Hooks~with~args}
+\NewDocumentCommand \NewHookWithArguments { mm }
+ { \hook_new_with_args:nn {#1} {#2} }
+\NewDocumentCommand \NewReversedHookWithArguments { mm }
+ { \hook_new_reversed_with_args:nn {#1} {#2} }
+\NewDocumentCommand \NewMirroredHookPairWithArguments { mmm }
+ { \hook_new_pair_with_args:nnn {#1} {#2} {#3} }
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2023/06/01}{\NewHookWithArguments}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_new_protected:Npn \NewHookWithArguments #1 #2 { }
+%<latexrelease>\cs_new_protected:Npn \NewReversedHookWithArguments #1 #2 { }
+%<latexrelease>\cs_new_protected:Npn \NewMirroredHookPairWithArguments #1 #2 #3 { }
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
%
%
% \begin{macrocode}
-%<latexrelease>\IncludeInRelease{2021/06/01}%
-%<latexrelease> {\hook_activate_generic:n}{Providing~hooks}
+%<latexrelease>\IncludeInRelease{2021/06/01}{\ActivateGenericHook}
+%<latexrelease> {Providing~hooks}
% \end{macrocode}
%
% \begin{macro}{\ActivateGenericHook}
@@ -5296,7 +7360,8 @@
% \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} }
+\NewDocumentCommand \ActivateGenericHook { m }
+ { \hook_activate_generic:n {#1} }
% \end{macrocode}
% \end{macro}
%
@@ -5304,32 +7369,57 @@
% Disabling a generic hook.
% \changes{v1.0o}{2021/08/02}{Change name}
% \begin{macrocode}
-\NewDocumentCommand \DisableGenericHook { m }{ \hook_disable_generic:n {#1} }
+\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>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}{\ActivateGenericHook}
+%<latexrelease> {Providing~hooks}
+%<latexrelease>\def \ActivateGenericHook #1 { }
+%<latexrelease>\def \DisableGenericHook #1 { }
%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
%
%
-% \begin{macro}{\AddToHook}
+% \begin{macro}{\AddToHook,\AddToHookWithArguments}
+% \changes{v1.1a}{2023/04/06}
+% {Add \cs{AddToHookWithArguments} (hook-args).}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\AddToHookWithArguments}
+%<latexrelease> {Hooks~with~args}
\NewDocumentCommand \AddToHook { m o +m }
{ \hook_gput_code:nnn {#1} {#2} {#3} }
+\NewDocumentCommand \AddToHookWithArguments { m o +m }
+ { \hook_gput_code_with_args:nnn {#1} {#2} {#3} }
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\AddToHookWithArguments}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_new_protected:Npn \AddToHookWithArguments #1 #2 #3 { }
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\AddToHookNext}
+% \begin{macro}{\AddToHookNext,\AddToHookNextWithArguments}
+% \changes{v1.1a}{2023/04/06}
+% {Add \cs{AddToHookNextWithArguments} (hook-args).}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\AddToHookNextWithArguments}
+%<latexrelease> {Hooks~with~args}
\NewDocumentCommand \AddToHookNext { m +m }
{ \hook_gput_next_code:nn {#1} {#2} }
+\NewDocumentCommand \AddToHookNextWithArguments { m +m }
+ { \hook_gput_next_code_with_args:nn {#1} {#2} }
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\AddToHookNextWithArguments}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_new_protected:Npn \AddToHookNextWithArguments #1 #2 { }
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
@@ -5396,12 +7486,29 @@
%
%
%
-% \begin{macro}{\UseHook,\UseOneTimeHook}
+% \begin{macro}{
+% \UseHook,
+% \UseOneTimeHook,
+% \UseHookWithArguments,
+% \UseOneTimeHookWithArguments,
+% }
% Avoid the overhead of \pkg{xparse} and its protection that we
% don't want here (since the hook should vanish without trace if empty)!
+% \changes{v1.1a}{2023/04/06}
+% {Add \cs{UseHookWithArguments} (hook-args).}
% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2023/06/01}{\UseHookWithArguments}
+%<latexrelease> {Hooks~with~args}
\cs_new:Npn \UseHook { \hook_use:n }
\cs_new:Npn \UseOneTimeHook { \hook_use_once:n }
+\cs_new:Npn \UseHookWithArguments { \hook_use:nnw }
+\cs_new:Npn \UseOneTimeHookWithArguments { \hook_use_once:nnw }
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}{\UseHookWithArguments}
+%<latexrelease> {Hooks~with~args}
+%<latexrelease>\cs_new:Npn \UseHookWithArguments #1 #2 { }
+%<latexrelease>\cs_new:Npn \UseOneTimeHookWithArguments #1 #2 { }
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
% \end{macro}
%
@@ -5462,7 +7569,7 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[EXP]{\IfHookExistsTF}
+% \begin{macro}[EXP,int]{\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},
@@ -5484,7 +7591,7 @@
% \changes{v1.0p}{2021/08/20}{Added deprecation warnings for
% old generic hook commands (gh/638)}
%
-% \begin{macro}{
+% \begin{macro}[int]{
% \hook_disable:n,
% \hook_provide:n,
% \hook_provide_reversed:n,
@@ -5529,7 +7636,7 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{
+% \begin{macro}[int]{
% \DisableHook,
% \ProvideHook,
% \ProvideReversedHook,
@@ -5596,8 +7703,10 @@
%<@@=>
% \end{macrocode}
%
-% \begin{macro}{\@expl@@@initialize@all@@,
-% \@expl@@@hook@curr@name@pop@@}
+% \begin{macro}[int]{
+% \@expl@@@initialize@all@@,
+% \@expl@@@hook@curr@name@pop@@
+% }
%
% \InternalDetectionOff
% \begin{macrocode}
@@ -5620,8 +7729,8 @@
% to be for \cs{AddToHook} vice versa (gh/401)}
% \begin{macrocode}
%
-%<latexrelease>\IncludeInRelease{0000/00/00}%
-%<latexrelease> {lthooks}{The~hook~management}%
+%<latexrelease>\IncludeInRelease{0000/00/00}{lthooks}
+%<latexrelease> {The~hook~management}%
%<latexrelease>
%<latexrelease>\def \NewHook#1{}
%<latexrelease>\def \NewReversedHook#1{}
@@ -5656,10 +7765,56 @@
%<latexrelease>\long\def \IfHookEmptyTF #1#2#3{#2}
%<latexrelease>
%<latexrelease>\EndModuleRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=hook>
+% \end{macrocode}
+%
+% \changes{v1.1a}{2023/04/06}
+% {Add dedicated rollback code to revert data structures (hook-args).}
+% \begin{macrocode}
+%<latexrelease>\cs:w @@_rollback_tidying: \cs_end:
+%<latexrelease>\bool_lazy_and:nnT
+%<latexrelease> { \int_compare_p:nNn { \sourceLaTeXdate } > { 20230600 } }
+%<latexrelease> { \int_compare_p:nNn { \requestedLaTeXdate } < { 20230601 } }
+%<latexrelease> {
+%<latexrelease> \cs_gset_protected:Npn \@@_rollback_tidying:
+%<latexrelease> {
+%<latexrelease> \@latex@error { Rollback~code~executed~twice }
+%<latexrelease> {
+%<latexrelease> Something~went~wrong~(unless~this~was~
+%<latexrelease> done~on~purpose~in~a~testing~environment).
+%<latexrelease> }
+%<latexrelease> \use_none:nnnn
+%<latexrelease> }
+%<latexrelease> \cs_set:Npn \@@_tmp:w #1 #2
+%<latexrelease> {
+%<latexrelease> \@@_tl_gset:cx { @@#1~#2 }
+%<latexrelease> {
+%<latexrelease> \exp_args:No \exp_not:o
+%<latexrelease> {
+%<latexrelease> \cs:w @@#1~#2 \exp_last_unbraced:Ne \cs_end:
+%<latexrelease> { \@@_braced_cs_parameter:n { @@#1~#2 } }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease> \seq_map_inline:Nn \g_@@_all_seq
+%<latexrelease> {
+%<latexrelease> \exp_after:wN \cs_gset_nopar:Npn
+%<latexrelease> \cs:w g_@@_#1_code_prop \exp_args:NNo \exp_args:No
+%<latexrelease> \cs_end: { \cs:w g_@@_#1_code_prop \cs_end: }
+%<latexrelease> \@@_tmp:w { _toplevel } {#1}
+%<latexrelease> \@@_tmp:w { _next } {#1}
+%<latexrelease> }
+%<latexrelease> }
\ExplSyntaxOff
%</2ekernel|latexrelease>
% \end{macrocode}
%
+% \begin{macrocode}
+%<@@=>
+% \end{macrocode}
%
% \Finale
%