summaryrefslogtreecommitdiff
path: root/macros/latex/base/lthooks.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex/base/lthooks.dtx')
-rw-r--r--macros/latex/base/lthooks.dtx1210
1 files changed, 900 insertions, 310 deletions
diff --git a/macros/latex/base/lthooks.dtx b/macros/latex/base/lthooks.dtx
index 0a04c38efb..20e172a57f 100644
--- a/macros/latex/base/lthooks.dtx
+++ b/macros/latex/base/lthooks.dtx
@@ -1,7 +1,7 @@
% \iffalse meta-comment
-%%
-%% File: lthooks.dtx (C) Copyright 2020 Frank Mittelbach,
-%% Phelype Oleinik & LaTeX Team
+%
+% Copyright (C) 2020-2021
+% Frank Mittelbach, Phelype Oleinik & LaTeX Team
%
% This file is part of the LaTeX base system.
% -------------------------------------------
@@ -31,8 +31,8 @@
%%% From File: lthooks.dtx
%
% \begin{macrocode}
-\def\lthooksversion{v1.0h}
-\def\lthooksdate{2021/01/07}
+\def\lthooksversion{v1.0n}
+\def\lthooksdate{2021/05/26}
% \end{macrocode}
%
%<*driver>
@@ -54,6 +54,9 @@
}
\ExplSyntaxOff
+\providecommand\InternalDetectionOff{}
+\providecommand\InternalDetectionOn{}
+
\EnableCrossrefs
\CodelineIndex
\begin{document}
@@ -64,10 +67,13 @@
% \fi
%
%
-% \long\def\fmi#1{\begin{quote}\itshape FMi: #1\end{quote}}
-% \long\def\pho#1{\begin{quote}\itshape PhO: #1\end{quote}}
+% \providecommand\hook[1]{\texttt{#1}}
+%
+% \providecommand\fmi[1]{\marginpar{\footnotesize FMi: #1}}
+% \providecommand\fmiinline[1]{\begin{quote}\itshape\footnotesize FMi: #1\end{quote}}
+% \providecommand\pho[1]{\marginpar{\footnotesize PhO: #1}}
+% \providecommand\phoinline[1]{\begin{quote}\itshape\footnotesize PhO: #1\end{quote}}
%
-% \newcommand\hook[1]{\texttt{#1}}
%
%
% \title{The \texttt{lthooks} package\thanks{This package has version
@@ -119,14 +125,15 @@
%
% \subsection{\LaTeXe\ interfaces}
%
-% \subsubsection{Declaring hooks and using them in code}
+% \subsubsection{Declaring hooks}
%
-% With two exceptions, hooks have to be declared before they can be
-% used. The exceptions are hooks in environments (i.e., executed at
-% \cs{begin} and \cs{end}) and hooks run when loading files,
-% e.g. before and after a package is loaded, etc. Their hook names
-% depend on the environment or the file name and so declaring them
-% beforehand is difficult.
+% With a few exceptions, hooks have to be declared before they can
+% be used. The exceptions are the generic hooks for commands,
+% environments (i.e., executed at \cs{begin} and \cs{end}) and
+% hooks run when loading files, e.g. before and after a package is
+% loaded, etc. Their hook names depend on the command,
+% environment or the
+% file name and so declaring them beforehand is not practical.
%
%
% \begin{function}{\NewHook}
@@ -173,6 +180,62 @@
% \end{function}
%
%
+% \subsubsection{Special declarations for hooks}
+%
+% The declarations here should normally not be used. They are available
+% to provide support for special use cases mainly involving
+% generic command hooks.
+%
+% \begin{function}{\DisableHook}
+% \begin{syntax}
+% \cs{DisableHook} \Arg{hook}
+% \end{syntax}
+% After this declaration the \meta{hook} is no longer usable: Any
+% attempt to add further code to it will result in an error and any
+% use, e.g., via \cs{UseHook}, will simply do nothing.
+%
+% This is intended to be used with generic command hooks (see
+% \texttt{ltcmdhooks-doc}) as depending on the definition of the
+% command such generic hooks may be unusable. If that is known, a
+% package developer can disable such hooks up front.
+%
+% The \meta{hook} can be specified using the dot-syntax to denote
+% the current package name. See section~\ref{sec:default-label}.
+% \end{function}
+%
+%
+% \begin{function}{\ProvideHook}
+% \begin{syntax}
+% \cs{ProvideHook} \Arg{hook}
+% \end{syntax}
+% Like \cs{NewHook} but does nothing if the hook was previously
+% declared with \cs{NewHook}. This declaration should only be used
+% in special situations, e.g., when command of another package
+% need to be altered and it is is not clear if for that command a
+% generic hook was already explicitly declared before.
+%
+% Normally \cs{NewHook} should be used instead.
+% \end{function}
+%
+% \begin{function}{\ProvideReversedHook}
+% \begin{syntax}
+% \cs{ProvideReversedHook} \Arg{hook}
+% \end{syntax}
+% Like \cs{NewReversedHook} but does nothing if the hook was
+% previously declared as a reversed hook.
+% \end{function}
+%
+% \begin{function}{\ProvideMirroredHookPair}
+% \begin{syntax}
+% \cs{ProvideMirroredHookPair} \Arg{hook-1} \Arg{hook-2}
+% \end{syntax}
+% A shorthand for
+% \cs{ProvideHook}\Arg{hook-1}\cs{ProvideReversedHook}\Arg{hook-2}.
+% \end{function}
+%
+%
+%
+% \subsubsection{Using hooks in code}
%
%
% \begin{function}{\UseHook}
@@ -198,7 +261,7 @@
% 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{\meta{addto-cmd}} command (e.g.,
+% 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
@@ -209,10 +272,12 @@
% consumed and any further attempt to add to it will result in
% executing the code to be added immediately.
%
-% \fmi{Maybe add an error version as well?}
+% \fmiinline{Maybe add an error version as well?}
%
% The \meta{hook} \emph{cannot} be specified using the dot-syntax.
-% A leading |.| is treated literally.
+% A leading |.| is treated literally. See
+% section~\ref{sec:default-label} for details.
+
% \end{function}
%
%
@@ -261,9 +326,10 @@
% that label, the removal order takes action and the code is not
% added.
%
-% If the optional argument is \texttt{*}, then all code chunks are
+% If the optional \meta{label} argument is \texttt{*}, then all code chunks are
% removed. This is rather dangerous as it drops code from other
-% packages one may not know about!
+% packages one may not know about and should therefore not by used
+% by packages but only in document preambles!
%
% The \meta{hook} and \meta{label} can be specified using the
% dot-syntax to denote the current package name.
@@ -296,7 +362,7 @@
%\end{verbatim}
% because that only \enquote{adds} a further empty chunk of code to
% the hook. Adding \cs{normalsize} would work but that means the hook
-% then contained \cs{small}\cs{normalsize} which means to font size
+% then contained \cs{small}\cs{normalsize} which means two font size
% changes for no good reason.
%
% The above is only needed if one wants to typeset several quotes in a
@@ -356,10 +422,9 @@
% \meta{label} throughout the sub-packages in order to avoid
% that the labels change if you internally reorganize your code.
%
-% Except for \cs{UseHook}, \cs{UseOneTimeHook}, \cs{IfHookEmptyTF}, and
-% \cs{IfHookExistsTF} (and their \pkg{expl3} interfaces \cs{hook_use:n},
-% \cs{hook_use_once:n}, \cs{hook_if_empty:nTF}, and
-% \cs{hook_if_exist:nTF}), all \meta{hook}
+% Except for \cs{UseHook}, \cs{UseOneTimeHook} and \cs{IfHookEmptyTF}
+% (and their \pkg{expl3} interfaces \cs{hook_use:n},
+% \cs{hook_use_once:n} and \cs{hook_if_empty:nTF}, all \meta{hook}
% and \meta{label} arguments are processed in the same way: first,
% spaces are trimmed around the argument, then it is fully expanded
% until only character tokens remain. If the full expansion of the
@@ -367,8 +432,8 @@
% token, a low-level \TeX{} error is raised (namely, the \meta{hook} is
% expanded using \TeX's \cs{csname}\ldots\cs{endcsname}, as such,
% Unicode characters are allowed in \meta{hook} and \meta{label}
-% arguments). The arguments of \cs{UseHook}, \cs{UseOneTimeHook},
-% \cs{IfHookEmptyTF}, and \cs{IfHookExistsTF} are
+% arguments). The arguments of \cs{UseHook}, \cs{UseOneTimeHook}, and
+% \cs{IfHookEmptyTF} are
% processed much in the same way except that spaces are not trimmed
% around the argument, for better performance.
%
@@ -422,8 +487,7 @@
% this syntax is not available in \cs{UseHook} (and \cs{hook_use:n})
% because the hook is most of the time used outside of the package file
% in which it was defined. This syntax is also not available in the hook
-% conditionals \cs{IfHookEmptyTF} (and \cs{hook_if_empty:nTF}) and
-% \cs{IfHookExistsTF} (and \cs{hook_if_exist:nTF}) because these
+% conditionals \cs{IfHookEmptyTF} (and \cs{hook_if_empty:nTF}), because these
% conditionals are used in some performance-critical parts of the hook
% management code, and because they are usually used to refer to other
% package's hooks, so the dot-syntax doesn't make much sense.
@@ -432,7 +496,7 @@
% it in logical parts, but still use the main package name as
% \meta{label}, then the \meta{default label} can be set using
% \cs{SetDefaultHookLabel} or
-% \cs{PushDefaultHookLabel}..\cs{PopDefaultHookLabel}.
+% \cs{PushDefaultHookLabel}\verb={..}=\,\ldots\cs{PopDefaultHookLabel}.
%
% \begin{function}{\PushDefaultHookLabel,\PopDefaultHookLabel}
% \begin{syntax}
@@ -595,7 +659,7 @@
% There can only be a single relation between two labels for a
% given hook,
% i.e., a later \cs{DeclareHookrule} overwrites any previous
-% delcaration.
+% declaration.
%
% The \meta{hook} and \meta{label} can be specified using the
% dot-syntax to denote the current package name.
@@ -648,9 +712,10 @@
% \item exist and be non-empty; and
% \item not exist (in which case emptiness doesn't apply);
% \end{itemize}
-% Hooks are a bit more complicated: they have four possible states.
-% A hook may exist or not, and either way it may or may not be empty.
-% This means that even a hook that doesn't exist may be non-empty.
+% Hooks are a bit more complicated:
+% a hook may exist or not, and either way it may or may not be empty.
+% This means that even a hook that doesn't exist may be non-empty and
+% it can also be disabled.
%
% This seemingly strange state may happen when, for example, package~$A$
% defines hook \hook{A/foo}, and package $B$ adds some code to that
@@ -661,11 +726,17 @@
% querying the existence of a hook doesn't imply its emptiness, neither
% does the other way around.
%
+% Given that code or rules can be added to a hook even if it doesn't
+% physically exist yet, means that a querying its existence has no
+% real use case (in contrast to other variables that can only be
+% update if they have already been declared). For that reason only the
+% test for emptiness has a public interface.
+%
% A hook is said to be empty when no code was added to it, either to
% its permanent code pool, or to its ``next'' token list. The hook
% doesn't need to be declared to have code added to its code pool.
% A hook is said to exist when it was declared with \cs{NewHook} or
-% some variant thereof. Generic \hook{file} and \hook{env} hooks are
+% some variant thereof. Generic hooks such as \hook{file} and \hook{env} hooks are
% automatically declared when code is added to them.
%
% \begin{function}[EXP]{\IfHookEmptyTF}
@@ -673,7 +744,8 @@
% \cs{IfHookEmptyTF} \Arg{hook} \Arg{true code} \Arg{false code}
% \end{syntax}
% Tests if the \meta{hook} is empty (\emph{i.e.}, no code was added to
-% it using either \cs{AddToHook} or \cs{AddToHookNext}), and
+% it using either \cs{AddToHook} or \cs{AddToHookNext}) or such code
+% was removed again (via \cs{RemoveFromHook}), and
% branches to either \meta{true code} or \meta{false code} depending
% on the result.
%
@@ -681,30 +753,7 @@
% A leading |.| is treated literally.
% \end{function}
%
-% \begin{function}[EXP]{\IfHookExistsTF}
-% \begin{syntax}
-% \cs{IfHookExistsTF} \Arg{hook} \Arg{true code} \Arg{false code}
-% \end{syntax}
-% Tests if the \meta{hook} exists (if it was created with either
-% \cs{NewHook}, \cs{NewReversedHook}, or \cs{NewMirroredHookPair}), and
-% branches to either \meta{true code} or \meta{false code} depending
-% on the result.
-%
-% The existence of a hook usually doesn't mean much from the viewpoint
-% of code that tries to add/remove code from that hook, since package
-% loading order may vary, thus the creation of hooks is asynchronous
-% to adding and removing code from it, so this test should be used
-% sparingly.
-%
-% Generic hooks are declared at the time code is added to them, so the
-% result of \cs{hook_if_exist:n} will change once code is added to
-% said hook (unless the hook was previously declared).
-%
-% The \meta{hook} \emph{cannot} be specified using the dot-syntax.
-% A leading |.| is treated literally.
-% \end{function}
%
-% \fmi{Would be helpful if we provide some use cases}
%
% \subsubsection{Displaying hook code}
%
@@ -807,7 +856,7 @@
% \meta{label-2}, in that order, as detailed in \cs{DeclareHookRule}.
% If the relation is \texttt{default} it means that that rule applies
% to \meta{label-1} and \meta{label-2} in \emph{all} hooks, (unless
-% overrided by a non-default relation).
+% overridden by a non-default relation).
%
% Finally, line~14 lists the labels in the hook after sorting;
% that is, in the order they will be executed when the hook is used.
@@ -819,7 +868,7 @@
% \begin{syntax}
% \cs{DebugHooksOn}
% \end{syntax}
-% Turn the debugging of hook code on or off. This displays changes
+% Turn the debugging of hook code on or off. This displays most changes
% made to the hook data structures. The output is rather coarse and
% not really intended for normal use.
% \end{function}
@@ -856,6 +905,53 @@
%
%
%
+% \begin{function}{\hook_disable:n}
+% \begin{syntax}
+% \cs{hook_disable:n} \Arg{hook}
+% \end{syntax}
+% Marks \Arg{hook} as disabled. Any further attempt to add code to
+% it or declare it, will result in an error and any call to
+% \cs{hook_use:n} will simply do nothing.
+%
+% This declaration is intended for use with generic hooks that are
+% known not to work (see \texttt{ltcmdhooks-doc}) if they receive
+% code.
+%
+% The \meta{hook} can be specified using the dot-syntax to denote
+% the current package name. See section~\ref{sec:default-label}.
+% \end{function}
+%
+% \begin{function}{\hook_provide:n}
+% \begin{syntax}
+% \cs{hook_provide:n} \Arg{hook}
+% \end{syntax}
+% Like \cs{hook_new:n} but does nothing if the hook was previously
+% declared with \cs{hook_new:n}. This declaration should only be used
+% in special situations, e.g., when a command of another package
+% needs to be altered and it is is not clear if for that command a
+% generic \hook{cmd} hook was already explicitly declared before.
+%
+% Normally \cs{hook_new:n} should be used instead.
+% \end{function}
+%
+% \begin{function}{\hook_provide_reversed:n}
+% \begin{syntax}
+% \cs{hook_provide_reversed:n} \Arg{hook}
+% \end{syntax}
+% Like \cs{hook_new_reversed:n} but does nothing if the hook was
+% previously declared as a reversed hook.
+% \end{function}
+%
+% \begin{function}{\hook_provide_pair:nn}
+% \begin{syntax}
+% \cs{hook_provide_pair:nn} \Arg{hook-1} \Arg{hook-2}
+% \end{syntax}
+% A shorthand for
+% \cs{hook_provide:n}\Arg{hook-1}\cs{hook_provide_reversed:n}\Arg{hook-2}.
+% \end{function}
+%
+%
+%
% \begin{function}{\hook_use:n}
% \begin{syntax}
% \cs{hook_use:n} \Arg{hook}
@@ -968,28 +1064,6 @@
% A leading |.| is treated literally.
% \end{function}
%
-% \begin{function}[pTF]{\hook_if_exist:n}
-% \begin{syntax}
-% \cs{hook_if_exist:nTF} \Arg{hook} \Arg{true code} \Arg{false code}
-% \end{syntax}
-% Tests if the \meta{hook} exists (if it was created with either
-% \cs{NewHook}, \cs{NewReversedHook}, or \cs{NewMirroredHookPair}), and
-% branches to either \meta{true code} or \meta{false code} depending
-% on the result.
-%
-% The existence of a hook usually doesn't mean much from the viewpoint
-% of code that tries to add/remove code from that hook, since package
-% loading order may vary, thus the creation of hooks is asynchronous
-% to adding and removing code from it, so this test should be used
-% sparingly.
-%
-% Generic hooks are declared at the time code is added to them, so the
-% result of \cs{hook_if_exist:n} will change once code is added to
-% said hook (unless the hook was previously declared).
-%
-% The \meta{hook} \emph{cannot} be specified using the dot-syntax.
-% A leading |.| is treated literally.
-% \end{function}
%
% \begin{function}{\hook_show:n,\hook_log:n}
% \begin{syntax}
@@ -1259,8 +1333,8 @@
% For that reason such code is not using the hook management, but
% instead private kernel commands directly before or after a public
% hook with the following naming
-% convention: \cs{@kernel@before@\meta{hookname}} or
-% \cs{@kernel@after@\meta{hookname}}. For example, in
+% convention: \cs{@kernel@before@\meta{hook}} or
+% \cs{@kernel@after@\meta{hook}}. For example, in
% \cs{enddocument} you find
%\begin{verbatim}
% \UseHook{enddocument}%
@@ -1336,20 +1410,14 @@
% Like \cs{AtBeginDocument} but for the \hook{enddocument} hook.
% \end{function}
%
-%
-%
-% \begin{function}{\AtBeginDvi}
-% \begin{syntax}
-% \cs{AtBeginDvi} \oarg{label} \Arg{code}
-% \end{syntax}
-% This hook is discussed in conjunction with the shipout hooks.
-% \end{function}
+% There is also \cs{AtBeginDvi} which is discussed in conjunction
+% with the shipout hooks.
%
% \bigskip
%
% The few hooks that existed previously in \LaTeXe{} used internally
% commands such as \cs{@begindocumenthook} and packages sometimes
-% augemented them directly rather than working through
+% augmented them directly rather than working through
% \cs{AtBeginDocument}. For that reason there is currently support
% for this, that is, if the system detects that such an internal
% legacy hook command contains code it adds it to the new hook
@@ -1415,7 +1483,7 @@
%
% The hooks are only executed if \cs{begin}\Arg{env} and
% \cs{end}\Arg{env} is used. If the environment code is executed
-% via low-level calls to \cs{\meta{env}} and \cs{end\meta{env}}
+% via low-level calls to \cs[no-index]{\meta{env}} and \cs[no-index]{end\meta{env}}
% (e.g., to avoid the environment grouping) they are not
% available. If you want them available in code using this method,
% you would need to add them yourself, i.e., write something like
@@ -1458,6 +1526,38 @@
% \end{function}
%
%
+% \subsubsection{Generic hooks for commands}
+%
+% Similar to environments there are now (at least in theory) two
+% generic hooks available for any \LaTeX{} command. These are
+% \begin{description}
+% \item[\hook{cmd/\meta{name}/before}]
+%
+% This hook is executed at the very start of the command
+% execution.
+%
+% \item[\hook{cmd/\meta{name}/after}]
+% This hook is executed at the very end of the command body. It is
+% implemented as a reversed hook.
+% \end{description}
+% In practice there are restrictions and especially the
+% \hook{after} hook works only with a subset of commands. Details
+% about these restrictions are documented in
+% \texttt{ltcmdhooks-doc.pdf} or with code in
+% \texttt{ltcmdhooks-code.pdf}.
+%
+%
+%
+%
+% \subsubsection{Generic hooks provided by file loading operations}
+%
+% There are several hooks added to \LaTeX{}'s process of loading
+% file via its high-level interfaces such as \cs{input},
+% \cs{include}, \cs{usepackage}, \cs{RequirePackage}, etc. These
+% are documented in \texttt{ltfilehook-doc.pdf} or with code in
+% \texttt{ltfilehook-code.pdf}.
+%
+%
%
% \subsubsection{Hooks provided by \cs{begin}\texttt{\{document\}}}
% \label{sec:begindocument-hooks}
@@ -1557,7 +1657,9 @@
% generates material for further pages. It is the right place to
% do some final housekeeping and possibly write out some
% information to the \texttt{.aux} file (which is still open at
-% this point to receive data). It is also the correct place to
+% this point to receive data, but since there will be no more pages you
+% need to write to it using \cs{immediate}\cs{write}). It is also the
+% correct place to
% set up any testing code to be run when the \texttt{.aux} file
% is re-read in the next step.
%
@@ -1615,7 +1717,7 @@
%
%
% It is in also possible to use the generic \hook{env/document/end}
-% hook which is execuded by \cs{end}, i.e., just in front of the
+% hook which is executed by \cs{end}, i.e., just in front of the
% first hook above. Note however that the other generic \cs{end}
% environment hook, i.e., \hook{env/document/after} will never get
% executed, because by that time \LaTeX{} has finished the document
@@ -1624,7 +1726,7 @@
%
%
%
-% \subsubsection{Hooks provided \cs{shipout} operations}
+% \subsubsection{Hooks provided by \cs{shipout} operations}
% \label{sec:shipout}
%
% There are several hooks and mechanisms added to \LaTeX{}'s
@@ -1633,14 +1735,6 @@
% \texttt{ltshipout-code.pdf}.
%
%
-% \subsubsection{Hooks provided by file loading operations}
-%
-% There are several hooks added to \LaTeX{}'s
-% process of loading file via its high-level interfaces such as
-% \cs{input}, \cs{include}, \cs{usepackage}, etc. These are documented in
-% \texttt{ltfilehook-doc.pdf} or with code in
-% \texttt{ltfilehook-code.pdf}.
-%
%
% \subsubsection{Hooks provided in NFSS commands}
%
@@ -1709,27 +1803,20 @@
% \section{The Implementation}
%
%
-% \subsection{Loading further extensions}
-%
% \begin{macrocode}
%<@@=hook>
% \end{macrocode}
%
+% \changes{v1.0i}{2021/03/18}{Use \cs{NewModuleRelease}.}
+% \changes{v1.0n}{2021/05/24}{Use \cs{msg_...} instead of \cs{__kernel_msg...}}
%
-% At the moment the whole module rolls back in one go, but if we
-% make any modifications in later releases this will then need
-% splitting.
% \begin{macrocode}
%<*2ekernel|latexrelease>
-%<latexrelease>\IncludeInRelease{2020/10/01}%
-%<latexrelease> {\NewHook}{The hook management}%
-% \end{macrocode}
-%
-% \begin{macrocode}
\ExplSyntaxOn
+%<latexrelease>\NewModuleRelease{2020/10/01}{lthooks}
+%<latexrelease> {The~hook~management~system}
% \end{macrocode}
%
-%
% \subsection{Debugging}
%
% \begin{macro}{\g_@@_debug_bool}
@@ -1772,9 +1859,11 @@
%
% \begin{macro}[EXP]{\@@_str_compare:nn}
% Private copy of \cs{__str_if_eq:nn}
+% \InternalDetectionOff
% \begin{macrocode}
\cs_new_eq:NN \@@_str_compare:nn \__str_if_eq:nn
% \end{macrocode}
+% \InternalDetectionOn
% \end{macro}
%
% \subsection{Declarations}
@@ -1857,8 +1946,9 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\tl_gremove_once:Nx,\tl_show:x,\tl_log:x}
-% Some variants of \pkg{expl3} functions. \fmi{should be moved to expl3}
+% \begin{macro}[int]{\tl_gremove_once:Nx,\tl_show:x,\tl_log:x}
+% Some variants of \pkg{expl3} functions.
+% \fmiinline{should probably be moved to expl3}
% \begin{macrocode}
\cs_generate_variant:Nn \tl_gremove_once:Nn { Nx }
\cs_generate_variant:Nn \tl_show:n { x }
@@ -1950,25 +2040,30 @@
% \end{macro}
%
%
+%
% \subsection{Providing new hooks}
%
-% \begin{macro}{\g_@@_..._code_prop,\@@~...,\@@_next~...}
+% \subsubsection{The data structures of a hook}
%
-% Hooks have a \meta{name} and for each hook we have to provide a number of
-% data structures. These are
+% \DescribeMacro{\g_@@_\meta{hook}_code_prop}
+% \DescribeMacro{\@@\textvisiblespace\meta{hook}}
+% \DescribeMacro{\@@_next\textvisiblespace\meta{hook}}
+% Hooks have a name (called \meta{hook} in the description below)
+% and for each hook we have to
+% provide a number of data structures. These are
% \begin{description}
-% \item[\cs{g_@@_\meta{name}_code_prop}] A property list holding the code
+% \item[\cs{g_@@_\meta{hook}_code_prop}] A property list holding the code
% for the hook in separate chunks. The keys are by default the
% package names that add code to the hook, but it is possible
% for packages to define other keys.
%
-% \item[{\cs[no-index]{g_@@_\meta{name}_rule_\meta{label1}\string|\meta{label2}_tl}}]
+% \item[{\cs[no-index]{g_@@_\meta{hook}_rule_\meta{label1}\string|\meta{label2}_tl}}]
% A token list holding the relation between \meta{label1} and
-% \meta{label2} in the \meta{name}. The \meta{labels} are lexically
+% \meta{label2} in the \meta{hook}. The \meta{labels} are lexically
% (reverse) sorted to ensure that two labels always point to the same
-% token list. For global rules, the \meta{name} is |??|.
+% token list. For global rules, the \meta{hook} name is |??|.
%
-% \item[\cs{@@~\meta{name}}] The code that is actually executed
+% \item[\cs{@@\textvisiblespace\meta{hook}}] The code that is actually executed
% when the hook is called in the document is stored in this token
% list. It is constructed from the code chunks applying the
% information.
@@ -1977,54 +2072,194 @@
% and to make it simpler to normalize hook names in
% \cs{@@_make_name:n}.
%
-% \item[\cs{g_@@_\meta{name}_reversed_tl}] Some hooks are
+% \item[\cs{g_@@_\meta{hook}_reversed_tl}] Some hooks are
% \enquote{reversed}. This token list stores a |-| for such hook
% so that it can be identified. The |-| character is used because
% $\meta{reversed}1$ is $+1$ for normal hooks and $-1$ for reversed
% ones.
+
+% \item[{\cs[no-index]{g_@@_\meta{hook}_declared_tl}}] This token
+% list serves as marker for the hook being officially declared. Its
+% existence is tested to raise an error in case another declaration
+% is attempted.
%
-% \item[\cs{@@_toplevel~\meta{name}}] This token list stores the code
+% \item[\cs{@@_toplevel\textvisiblespace\meta{hook}}] This token list stores the code
% inserted in the hook from the user's document, in the |top-level|
% label. This label is special, and doesn't participate in sorting.
% Instead, all code is appended to it and executed after (or before,
% if the hook is reversed) the normal
% hook code, but before the |next| code chunk.
%
-% \item[\cs{@@_next~\meta{name}}] Finally there is extra code
-% (normally empty) that is used on the next invocation of the hook
-% (and then deleted). This can be used to define some special
-% behavior for a single occasion from within the document. This token
-% list follows the same naming scheme than the main \cs{@@~\meta{name}}
-% token list. It is called \cs{@@_next~\meta{name}} rather than
-% \cs{@@~next_\meta{name}} because otherwise a hook whose name is
-% |next_|\meta{name} would clash with the next code-token list of the
-% hook called \meta{name}.
+% \item[\cs{@@_next\textvisiblespace\meta{hook}}] Finally there is
+% extra code (normally empty) that is used on the next invocation
+% of the hook (and then deleted). This can be used to define some
+% special behavior for a single occasion from within the document.
+% This token list follows the same naming scheme than the main
+% \cs{@@\textvisiblespace\meta{hook}} token list. It is called
+% \cs{@@_next\textvisiblespace\meta{hook}} rather than
+% \cs[no-index]{@@\textvisiblespace next_\meta{hook}} because
+% otherwise a hook whose name is |next_|\meta{hook} would clash
+% with the next code-token list of the hook called \meta{hook}.
%
% \end{description}
-% \end{macro}
%
%
+% \subsubsection{On the existence of hooks}
+% \label{sec:existence}
+%
+% A hook may be in different states of existence. Here we give an
+% overview of internal commands to set up hooks and explain how the
+% different states are distinguished. The actual implementation
+% then follows in the next sections.
%
+% One problem we have to solve is, that we need to be able to add
+% code to hooks (e.g., with \cs{AddToHook}) even if that code has
+% not been declared yet. For example, one package needs to write
+% into a hook of another package, but that package ay not get
+% loaded or only loaded later. Another problem most hooks require
+% declaration but this is not the case for the generic hooks.
%
-% \begin{macro}{\hook_new:n}
-% The \cs{hook_new:n} declaration declare a new hook and expects
+% We therefore distinguish the following states for a hook and they
+% are managed with four different tests: structure existence
+% (\cs{@@_if_structure_exist:nTF}), creation
+% (\cs{@@_if_usable:nTF}), declaration (\cs{@@_if_declared:nTF})
+% and disabled or not (\cs{@@_if_disabled:nTF})
+% \begin{description}
+% \setlist[itemize]{leftmargin=5cm,format=\cs}
+% \item[not existing]
+%
+% Nothing is known about the hook so far. This state can be
+% detected with \cs{@@_if_structure_exist:nTF}
+% (which uses the false branch).
+%
+% In this state the hook can be declared, disabled, rules can be
+% defined or code could be added to it, but it is not possible
+% to use the hook (with \cs{UseHook}).
+
+% \item[basic data structure set up]
+%
+% A hook is this state when its basic data structure has been
+% set up (using \cs{@@_init_structure:n}). The data structure setup happens
+% automatically when commands such as \cs{AddToHook} are used
+% and the hook is at that point in state \enquote{not existing}.
+%
+% In this state the four tests give the following results:
+% \begin{itemize}
+% \item [@@_if_structure_exist:nTF] returns |true|.
+% \item [@@_if_usable:nTF] returns |false|.
+% \item [@@_if_declared:nTF] returns |false|.
+% \item [@@_if_disabled:nTF] returns |false|.
+% \end{itemize}
+%
+% The allowed acctions are the same as in the \enquote{not
+% existing} state.
+%
+% \item[declared]
+%
+% A hook is in this state it is not disabled and was explicity declared (e.g.,
+% with \cs{NewHook}). In this case the four tests give the
+% following results:
+% \begin{itemize}
+% \item [@@_if_structure_exist:nTF] returns |true|.
+% \item [@@_if_usable:nTF] returns |true|.
+% \item [@@_if_declared:nTF] returns |true|.
+% \item [@@_if_disabled:nTF] returns |false|.
+% \end{itemize}
+%
+% \item[usable]
+%
+% A hook is in this state if it is not disabled, was not
+% explicitly declared but nevertheless is allowed to be used
+% (with \cs{UseHook} or \cs{hook_use:n}). This state is only
+% possible for generic hooks as they do not need to be
+% declared. Therefore such hooks move directly from state
+% \enquote{not existing} to \enquote{usable} the moment a
+% declaration such as \cs{AddToHook} wants to add to the hook
+% data structure. In this state the tests give the following
+% results:
+% \begin{itemize}
+% \item [@@_if_structure_exist:nTF] returns |true|.
+% \item [@@_if_usable:nTF] returns |true|.
+% \item [@@_if_declared:nTF] returns |false|.
+% \item [@@_if_disabled:nTF] returns |false|.
+% \end{itemize}
+%
+%
+% \item[disabled]
+%
+% A hook in any state is moved to this state when
+% \cs{DisableHook} is used. This changes the tests to give the
+% following results:
+% \begin{itemize}
+% \item [@@_if_structure_exist:nTF] \emph{unchanged}.
+% \item [@@_if_usable:nTF] returns |false|.
+% \item [@@_if_declared:nTF] returns |true|.
+% \item [@@_if_disabled:nTF] returns |true|.
+% \end{itemize}
+% The structure test is unchanged (if the hook was unknown before it is
+% false, otherwise true). The usable test returns false so that
+% any \cs{UseHook} will bypass the hook from now on. The
+% declared test returns true so that any further \cs{NewHook}
+% generates an error and the disabled test returns true so that
+% \cs{AddToHook} can return an error.
+% \fmiinline{maybe it should do this only after begin document?}
+%
+% \end{description}
+%
+%
+%
+%
+% \subsubsection{Setting hooks up}
+%
+%
+% \begin{macro}{\hook_new:n,\@@_new:n}
+% The \cs{hook_new:n} declaration declares a new hook and expects
% the hook \meta{name} as its argument, e.g.,
% \hook{begindocument}.
% \begin{macrocode}
\cs_new_protected:Npn \hook_new:n #1
{ \@@_normalize_hook_args:Nn \@@_new:n {#1} }
+% \end{macrocode}
+%
+% \begin{macrocode}
\cs_new_protected:Npn \@@_new:n #1
{
% \end{macrocode}
-% We check for one of the internal data structures and if it
-% already exists we complain.
+% We check if the hook was already \emph{explicitly} declared with
+% \cs{hook_new:n}, and if it already exists we complain, otherwise set
+% the \enquote{created} flag for the hook so that it errors next time
+% \cs{hook_new:n} is used.
% \begin{macrocode}
- \hook_if_exist:nTF {#1}
+ \@@_if_declared:nTF {#1}
{ \msg_error:nnn { hooks } { exists } {#1} }
+ {
+ \tl_new:c { g_@@_#1_declared_tl }
+ \@@_make_usable:n {#1}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+% \begin{macro}{\@@_make_usable:n}
+%
+% This initializes all hook data structures for the hook but if
+% used on its own doesn't mark the hook as declared (as
+% \cs{hook_new:n} does, so a later \cs{hook_new:n} on that hook will
+% not result in an error. This command is internally used by
+% \cs{hook_gput_code:n} when adding code to a generic hook.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_make_usable:n #1
+ {
% \end{macrocode}
-% Otherwise we add the hook name to the list of all hooks and
-% allocate the necessary data structures for the new hook.
+% Now we check if the hook's data structure can be safely created
+% without \pkg{expl3} raising errors, then
+% we add the hook name to the list of all hooks and
+% allocate the necessary data structures for the new hook,
+% otherwise just do nothing.
% \begin{macrocode}
+ \tl_if_exist:cF { @@~#1 }
{
\seq_gput_right:Nn \g_@@_all_seq {#1}
% \end{macrocode}
@@ -2035,12 +2270,12 @@
% \end{macrocode}
% Now ensure that the base data structure for the hook exists:
% \begin{macrocode}
- \@@_declare:n {#1}
+ \@@_init_structure:n {#1}
% \end{macrocode}
% The \cs{g_@@_\meta{hook}_labels_clist} holds the sorted list of
% labels (once it got sorted). This is used only for debugging.
% \begin{macrocode}
- \clist_new:c {g_@@_#1_labels_clist}
+ \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
@@ -2062,23 +2297,30 @@
%
% \begin{macrocode}
\@@_include_legacy_code_chunk:n {#1}
- }
+ }
}
% \end{macrocode}
% \end{macro}
%
%
%
-% \begin{macro}{\@@_declare:n}
-% This function declares the basic data structures for a hook without
-% actually declaring the hook itself. This is needed to allow adding
-% to undeclared hooks. Here it is unnecessary to check whether all
-% variables exist, since all three are declared at the same time
-% (either all of them exist, or none).
+% \begin{macro}{\@@_init_structure:n}
+% This function declares the basic data structures for a hook
+% without explicit declaring the hook itself. This is needed to
+% allow adding to undeclared hooks. Here it is unnecessary to
+% check whether all variables exist, since all three are declared
+% at the same time (either all of them exist, or none).
+%
+% It creates the hook code pool
+% (\cs[no-index]{g_@@_\meta{hook}_code_prop}) and the |top-level|
+% and |next| token lists. A hook is initialized with
+% \cs{@@_init_structure:n} the first time anything is added to it.
+% Inizializing a hook just with \cs{@@_init_structure:n} will not
+% make it usable with \cs{hook_use:n}.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_declare:n #1
+\cs_new_protected:Npn \@@_init_structure:n #1
{
- \@@_if_exist:nF {#1}
+ \@@_if_structure_exist:nF {#1}
{
\prop_new:c { g_@@_#1_code_prop }
\tl_new:c { @@_toplevel~#1 }
@@ -2110,6 +2352,9 @@
% \end{macrocode}
% \end{macro}
%
+%
+%
+%
% \begin{macro}{\hook_new_pair:nn}
% A shorthand for declaring a normal and a (matching) reversed hook in one go.
% \begin{macrocode}
@@ -2160,6 +2405,140 @@
%
%
%
+% \subsubsection{Disabling and providing hooks}
+%
+% \begin{macro}{\hook_disable:n}
+% \begin{macro}{\@@_disable:n}
+% \begin{macro}[pTF]{\@@_if_disabled:n}
+%
+% Disables a hook by creating its
+% \cs[no-index]{g_@@_\meta{hook}_declared_tl} so that the hook
+% errors when used with \cs{hook_new:n}, then it undefines
+% \cs{@@\textvisiblespace\meta{hook}} so that it may not be executed.
+%
+% This does not clear any code that may be already stored in the
+% hook's structure, but doesn't allow adding more code.
+% \cs{@@_if_disabled:nTF} uses that specific combination to check
+% if the hook is disabled.
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2021/06/01}%
+%<latexrelease> {\hook_disable:n}{Disable~hooks}
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \hook_disable:n #1
+ { \@@_normalize_hook_args:Nn \@@_disable:n {#1} }
+\cs_new_protected:Npn \@@_disable:n #1
+ {
+ \tl_gclear_new:c { g_@@_#1_declared_tl }
+ \cs_undefine:c { @@~#1 }
+ }
+\prg_new_conditional:Npnn \@@_if_disabled:n #1 { p, T, F, TF }
+ {
+ \bool_lazy_and:nnTF
+ { \tl_if_exist_p:c { g_@@_#1_declared_tl } }
+ { ! \tl_if_exist_p:c { @@~#1 } }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}
+%<latexrelease> {\hook_disable:n}{Disable~hooks}
+%<latexrelease>
+%<latexrelease>\cs_new_protected:Npn \hook_disable:n #1 {}
+%<latexrelease>
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\hook_provide:n}
+% \begin{macro}{\hook_provide_reversed:n}
+% \begin{macro}{\@@_provide:n}
+% The \cs{hook_provide: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.
+% The \cs{hook_provide_reversed:n} does the same for reversed hooks.
+% \hook{begindocument}.
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2021/06/01}%
+%<latexrelease> {\hook_provide:n}{Providing~hooks}
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \hook_provide:n #1
+ { \@@_normalize_hook_args:Nn \@@_provide:nn {#1} { } }
+\cs_new_protected:Npn \hook_provide_reversed:n #1
+ { \@@_normalize_hook_args:Nn \@@_provide:nn {#1} { - } }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_provide:nn #1 #2
+ {
+% \end{macrocode}
+% If the hook to be provided was disabled we warn (for now --- this
+% may change).
+% \begin{macrocode}
+ \@@_if_disabled:nTF {#1}
+ { \msg_warning:nnn { hooks } { provide-disabled } {#1} }
+% \end{macrocode}
+% Otherwise we check if it was already declared.
+% \begin{macrocode}
+ {
+ \@@_if_declared:nTF {#1}
+ {
+% \end{macrocode}
+% Issue an error if we try to provide a a hook that is reversed and
+% the already existing one is not (or vice versa).
+% \begin{macrocode}
+ \str_if_eq:eeF { \tl_use:c { g_@@_#1_reversed_tl } } {#2}
+ { \msg_error:nnn { hooks } { provide-error } {#1} }
+ }
+% \end{macrocode}
+% If it wasn't declared, we declared as a normal or reversed hook
+% as appropriate.
+% \begin{macrocode}
+ {
+ \tl_new:c { g_@@_#1_declared_tl }
+ \@@_make_usable:n {#1}
+ \tl_gset:cn { g_@@_#1_reversed_tl } {#2}
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\hook_provide_pair:nn}
+% A shorthand for providing a normal and a (matching) reversed hook in one go.
+% \begin{macrocode}
+\cs_new_protected:Npn \hook_provide_pair:nn #1#2
+ { \hook_provide:n {#1} \hook_provide_reversed:n {#2} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}
+%<latexrelease> {\hook_provide:n}{Providing~hooks}
+%<latexrelease>
+%<latexrelease>\cs_new_protected:Npn \hook_provide_reversed:n #1 {}
+%<latexrelease>\cs_new_protected:Npn \hook_provide:n #1 {}
+%<latexrelease>\cs_new_protected:Npn \hook_provide_pair:nn #1#2 {}
+%<latexrelease>
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+%
% \subsection{Parsing a label}
%
% \begin{macro}[EXP]{\@@_parse_label_default:n}
@@ -2231,7 +2610,7 @@
{
\tl_if_empty:NTF \@currname
{
- \msg_expandable_error:nnn { hooks } { should-not-happen }
+ \msg_expandable_error:nnn { kernel } { should-not-happen }
{ Empty~default~label. }
\@@_make_name:n { label-missing }
}
@@ -2243,15 +2622,15 @@
% \end{macro}
%
% \begin{macro}[EXP]{\@@_make_name:n,\@@_make_name:w}
-% Provides a standard sanitisation of a hook's name.
+% Provides a standard sanitization of a hook's name.
% It uses \cs{cs:w} to build a control sequence out of the hook name,
% then uses \cs{cs_to_str:N} to get the string representation of that,
% without the escape character. \cs{cs:w}-based expansion is used
% instead of |e|-based because Unicode characters don't behave well
-% inside \cs{expanded}. The macro adds the \cs{@@~} prefix to the
+% inside \cs{expanded}. The macro adds the \cs[no-index]{@@\textvisiblespace} prefix to the
% hook name to reuse the hook's code token list to build the csname
% and avoid leaving \enquote{public} control sequences defined
-% (as~\cs{relax}) in TeX's memory.
+% (as~\cs[no-index]{relax}) in TeX's memory.
% \begin{macrocode}
\cs_new:Npn \@@_make_name:n #1
{
@@ -2311,6 +2690,9 @@
% \end{macro}
%
%
+%
+% \subsection{Adding or removing hook code}
+%
% \begin{macro}{\hook_gput_code:nnn}
% \begin{macro}{\@@_gput_code:nnn,\@@_gput_code:nxv,\@@_hook_gput_code_do:nnn}
%
@@ -2320,6 +2702,9 @@
% \begin{macrocode}
\cs_new_protected:Npn \hook_gput_code:nnn #1 #2
{ \@@_normalize_hook_args:Nnn \@@_gput_code:nnn {#1} {#2} }
+% \end{macrocode}
+%
+% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_code:nnn #1 #2 #3
{
% \end{macrocode}
@@ -2343,7 +2728,7 @@
% If no removal is queued, we are free to add. Start by checking if
% the hook exists.
% \begin{macrocode}
- \hook_if_exist: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}=
@@ -2364,10 +2749,17 @@
% declare it as a generic hook, if its name matches one of the valid
% patterns.
% \begin{macrocode}
- { \@@_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}
+%
+% \begin{macrocode}
\cs_generate_variant:Nn \@@_gput_code:nnn { nxv }
% \end{macrocode}
%
@@ -2379,7 +2771,7 @@
% However, first some debugging info if debugging is enabled:
% \begin{macrocode}
\@@_debug:n{\iow_term:x{****~ Add~ to~
- \hook_if_exist:nF {#1} { undeclared~ }
+ \@@_if_usable:nF {#1} { undeclared~ }
hook~ #1~ (#2)
\on@line\space <-~ \tl_to_str:n{#3}} }
% \end{macrocode}
@@ -2387,9 +2779,9 @@
% If there's code already there, then append \verb=#3= to that,
% otherwise just put \verb=#3=. If the current label is |top-level|,
% the code is added to a dedicated token list
-% \cs[no-index]{@@_toplevel~\meta{hook}} that goes at the end of the
+% \cs{@@_toplevel\textvisiblespace\meta{hook}} that goes at the end of the
% hook (or at the beginning, for a reversed hook), just before
-% \cs[no-index]{@@_next~\meta{hook}}.
+% \cs[no-index]{@@_next\textvisiblespace\meta{hook}}.
% \begin{macrocode}
\str_if_eq:nnTF {#2} { top-level }
{
@@ -2397,9 +2789,9 @@
{
% \end{macrocode}
% If the hook's basic structure does not exist, we need to declare it
-% with \cs{@@_declare:n}.
+% with \cs{@@_init_structure:n}.
% \begin{macrocode}
- \@@_declare:n {#1}
+ \@@_init_structure:n {#1}
\@@_tl_gput_right:cn { @@_toplevel~#1 } {#3}
}
{ \msg_error:nnn { hooks } { misused-top-level } {#1} }
@@ -2424,14 +2816,15 @@
% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_undeclared_hook:nnn #1 #2 #3
{
- \@@_declare:n {#1}
+ \@@_init_structure:n {#1}
\@@_hook_gput_code_do:nnn {#1} {#2} {#3}
}
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}{\@@_try_declaring_generic_hook:nnn}
-% \begin{macro}{\@@_try_declaring_generic_next_hook:nn}
+% \begin{macro}{\@@_try_declaring_generic_hook:nnn,
+% \@@_try_declaring_generic_next_hook:nn}
+%
% These entry-level macros just pass the arguments along to the
% common \cs{@@_try_declaring_generic_hook:nNNnn} with the right
% functions to execute when some action is to be taken.
@@ -2458,13 +2851,14 @@
\hook_gput_next_code:nn \@@_gput_next_do:nn
}
% \end{macrocode}
+% \end{macro}
%
-% \begin{macro}{
-% \@@_try_declaring_generic_hook:nNNnn,
-% \@@_try_declaring_generic_hook_split:nNNnn
-% }
-% \begin{macro}[TF]{\@@_try_declaring_generic_hook:wn}
-% \cs{@@_try_declaring_generic_hook:nNNnn} now splits the hook name
+%
+% \begin{macro}{\@@_try_declaring_generic_hook:nNNnn,
+% \@@_try_declaring_generic_hook_split:nNNnn}
+%
+% \cs{@@_try_declaring_generic_hook:nNNnn}
+% now splits the hook name
% at the first \texttt{/} (if any) and first checks if it is a
% file-specific hook (they require some normalization) using
% \cs{@@_if_file_hook:wTF}. If not then check it is one of a
@@ -2482,12 +2876,24 @@
}
{ \@@_try_declaring_generic_hook_split:nNNnn {#1} }
}
+% \end{macrocode}
+%
+% \begin{macrocode}
\cs_new_protected:Npn \@@_try_declaring_generic_hook_split:nNNnn #1 #2 #3
{
\@@_try_declaring_generic_hook:wnTF #1 / / / \scan_stop: {#1}
{ #2 }
{ #3 } {#1}
}
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}[TF]{\@@_try_declaring_generic_hook:wn}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2021/06/01}%
+%<latexrelease> {\@@_try_declaring_generic_hook:wn}{Support~cmd~hooks}
\prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn
#1 / #2 / #3 / #4 \scan_stop: #5 { TF }
{
@@ -2496,31 +2902,70 @@
{
\prop_if_in:NnTF \c_@@_generics_prop {#1}
{
- \hook_if_exist:nF {#5} { \hook_new:n {#5} }
+ \@@_if_usable:nF {#5}
+ {
% \end{macrocode}
-% After having declared the hook we check the second component (for
-% file hooks) or the third component for environment hooks) and
-% if it is on the list of components for which we should have declared
-% a reversed hook we alter the hook data structure accordingly.
+% If the hook doesn't exist yet we check if it is a \texttt{cmd}
+% hook and if so we attempt patching the command in addition to
+% declaring the hook.
+%
+% For some commands this will not be possible, in which case
+% \cs{@@_patch_cmd_or_delay:Nnn} (defined in \texttt{ltcmdhooks})
+% will generate an appropriate error message.
+% \begin{macrocode}
+ \str_if_eq:nnT {#1} { cmd }
+ { \@@_try_put_cmd_hook:n {#5} }
+% \end{macrocode}
+%
+% Declare the hook always even if it can't really be used (error
+% message generated elsewhere).
+%
+% Here we use \cs{@@_make_usable:n}, so that a \cs{hook_new:n} is still
+% possible later.
% \begin{macrocode}
+ \@@_make_usable:n {#5}
+ }
\prop_if_in:NnTF \c_@@_generics_reversed_ii_prop {#2}
{ \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
{
\prop_if_in:NnT \c_@@_generics_reversed_iii_prop {#3}
{ \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
}
-% \end{macrocode}
-% Now that we know that the hook is declared we can add the code to it.
-% \begin{macrocode}
\prg_return_true:
}
{ \prg_return_false: }
}
}
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2020/10/01}%
+%<latexrelease> {\@@_try_declaring_generic_hook:wn}{Support~cmd~hooks}
+%<latexrelease>
+%<latexrelease>\prg_new_protected_conditional:Npnn \@@_try_declaring_generic_hook:wn
+%<latexrelease> #1 / #2 / #3 / #4 \scan_stop: #5 { TF }
+%<latexrelease> {
+%<latexrelease> \tl_if_empty:nTF {#2}
+%<latexrelease> { \prg_return_false: }
+%<latexrelease> {
+%<latexrelease> \prop_if_in:NnTF \c_@@_generics_prop {#1}
+%<latexrelease> {
+%<latexrelease> \@@_if_declared:nF {#5} { \hook_new:n {#5} }
+%<latexrelease> \prop_if_in:NnTF \c_@@_generics_reversed_ii_prop {#2}
+%<latexrelease> { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
+%<latexrelease> {
+%<latexrelease> \prop_if_in:NnT \c_@@_generics_reversed_iii_prop {#3}
+%<latexrelease> { \tl_gset:cn { g_@@_#5_reversed_tl } { - } }
+%<latexrelease> }
+%<latexrelease> \prg_return_true:
+%<latexrelease> }
+%<latexrelease> { \prg_return_false: }
+%<latexrelease> }
+%<latexrelease> }
+%<latexrelease>
+%<latexrelease>\EndIncludeInRelease
% \end{macrocode}
-% \end{macro}
-% \end{macro}
-% \end{macro}
% \end{macro}
%
% \begin{macro}[pTF]{\@@_if_file_hook:w}
@@ -2529,7 +2974,7 @@
% |file/before/foo.tex|). If it is a file-specific hook, then it
% executes the \meta{true} branch, otherwise \meta{false}.
%
-% A file-specific hook is \texttt{file/\meta{position}/\meta{name}}.
+% A file-specific hook is \hook{file/\meta{position}/\meta{name}}.
% If any of these parts don't exist, it is a general file hook or not
% a file hook at all, so the conditional evaluates to \meta{false}.
% Otherwise, it checks that the first part is |file| and that the
@@ -2595,14 +3040,16 @@
% Property list holding the generic names. We don't provide any user
% interface to this as this is meant to be static.
% \begin{description}
+% \item[\texttt{cmd}]
+% The generic hooks used for commands.
% \item[\texttt{env}]
% The generic hooks used in \cs{begin} and \cs{end}.
-% \item[\texttt{file}]
+% \item[\texttt{file}, \texttt{package}, \texttt{class}, \texttt{include}]
% The generic hooks used when loading a file
% \end{description}
% \begin{macrocode}
\prop_const_from_keyval:Nn \c_@@_generics_prop
- {env=,file=,package=,class=,include=}
+ {cmd=,env=,file=,package=,class=,include=}
% \end{macrocode}
% \end{macro}
%
@@ -2629,11 +3076,11 @@
\cs_new_protected:Npn \@@_gremove_code:nn #1 #2
{
% \end{macrocode}
-% First check that the hook code pool exists. \cs{hook_if_exist:nTF}
+% First check that the hook code pool exists. \cs{@@_if_usable:nTF}
% isn't used here because it should be possible to remove code from a
% hook before its defined (see section~\ref{sec:querying}).
% \begin{macrocode}
- \@@_if_exist:nTF {#1}
+ \@@_if_structure_exist:nTF {#1}
{
% \end{macrocode}
% Then remove the chunk and run \cs{@@_update_hook_code:n} so
@@ -2641,9 +3088,9 @@
% \verb=\begin{document}=.
%
% If all code is to be removed, clear the code pool
-% \cs[no-index]{g_@@_\meta{hook}_code_prop}, the top-level code
-% \cs[no-index]{@@_toplevel~\meta{hook}}, and the next-execution code
-% \cs[no-index]{@@_next~\meta{hook}}.
+% \cs{g_@@_\meta{hook}_code_prop}, the top-level code
+% \cs{@@_toplevel\textvisiblespace\meta{hook}}, and the next-execution code
+% \cs{@@_next\textvisiblespace\meta{hook}}.
% \begin{macrocode}
\str_if_eq:nnTF {#2} {*}
{
@@ -2656,7 +3103,7 @@
% If the label is |top-level| then clear the token list, as all code
% there is under the same label. Marked removal is not implemented
% for |top-level| because it is hard to reliably know that no code
-% was added to \cs[no-index]{@@_toplevel~\meta{hook}} (granted that
+% was added to \cs{@@_toplevel\textvisiblespace\meta{hook}} (granted that
% an empty code could be interpreted as that, but then it differs in
% behaviour from other labels, in which an empty chunk is still valid
% for removal). Besides, it doesn't make much (if any) sense for
@@ -2680,7 +3127,7 @@
% \end{macrocode}
% Finally update the code, if the hook exists.
% \begin{macrocode}
- \hook_if_exist:nT {#1}
+ \@@_if_usable:nT {#1}
{ \@@_update_hook_code:n {#1} }
}
% \end{macrocode}
@@ -2775,10 +3222,10 @@
% disadvantage of that being not really distinguishable from a real
% hook name. I now have settled for \texttt{??} which needs some
% gymnastics to get it into the csname, but since this is used a
-% lot things should be fast, so this is not done with \texttt{c}
+% lot, the code should be fast, so this is not done with \texttt{c}
% expansion in the code later on.
%
-% \cs{@@~??} isn't used, but it has to be defined to trick
+% \cs{@@\textvisiblespace??} isn't used, but it has to be defined to trick
% the code into thinking that \verb=??= is actually a hook.
% \begin{macrocode}
\prop_new:c {g_@@_??_code_prop}
@@ -2799,14 +3246,12 @@
%
% \begin{macro}{\hook_gset_rule:nnnn}
% \begin{macro}{\@@_gset_rule:nnnn}
-%
-% \fmi{needs docu correction given new implementation}
-%
% With
% \cs{hook_gset_rule:nnnn}\Arg{hook}\Arg{label1}\Arg{relation}\Arg{label2}
% a relation is defined between the two code labels for the given
% \meta{hook}. The special hook \texttt{??} stands for \emph{any}
-% hook describing a default rule.
+% hook, which sets a default rule (to be used if no other relation
+% between the two hooks exist).
% \begin{macrocode}
\cs_new_protected:Npn \hook_gset_rule:nnnn #1#2#3#4
{
@@ -2814,14 +3259,14 @@
{#1} {#2} {#3} {#4}
}
% \end{macrocode}
-%
+%
% \begin{macrocode}
\cs_new_protected:Npn \@@_gset_rule:nnnn #1#2#3#4
{
% \end{macrocode}
% First we ensure the basic data structure of the hook exists:
% \begin{macrocode}
- \@@_declare:n {#1}
+ \@@_init_structure:n {#1}
% \end{macrocode}
% Then we clear any previous relationship between both labels.
% \begin{macrocode}
@@ -2898,9 +3343,11 @@
% together in hook |#1|.
% \begin{macrocode}
\cs_new_protected:cpn { @@_rule_incompatible-error_gset:nnn } #1#2#3
- { \@@_tl_gset:cn { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl } { xE } }
+ { \@@_tl_gset:cn { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl }
+ { xE } }
\cs_new_protected:cpn { @@_rule_incompatible-warning_gset:nnn } #1#2#3
- { \@@_tl_gset:cn { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl } { xW } }
+ { \@@_tl_gset:cn { g_@@_#1_rule_ \@@_label_pair:nn {#2} {#3} _tl }
+ { xW } }
% \end{macrocode}
% \end{macro}
%
@@ -2937,7 +3384,7 @@
{
\if_int_compare:w \@@_str_compare:nn {#1} {#2} > 0 \exp_stop_f:
\prg_return_true:
- \else
+ \else:
\prg_return_false:
\fi:
}
@@ -3037,13 +3484,13 @@
% If there aren't any code
% chunks for the current hook, there is no point in even starting
% the sorting routine so we make a quick test for that and in that
-% case just update \cs{@@~\meta{hook}} to hold the |top-level| and
+% case just update \cs{@@\textvisiblespace\meta{hook}} to hold the |top-level| and
% |next| code chunks. If there are code chunks we call
% \cs{@@_initialize_single:NNn} and pass to it ready made csnames
% as they are needed several times inside. This way we save a bit
% on processing time if we do that up front.
% \begin{macrocode}
- \hook_if_exist:nT {#1}
+ \@@_if_usable:nT {#1}
{
\prop_if_empty:cTF {g_@@_#1_code_prop}
{
@@ -3096,7 +3543,7 @@
% \begin{macro}[EXP]{\@@_tl_csname:n,\@@_seq_csname:n}
% It is faster to pass a single token and expand it when necessary
% than to pass a bunch of character tokens around.
-% \fmi{note to myself: verify}
+% \fmiinline{note to myself: verify}
% \begin{macrocode}
\cs_new:Npn \@@_tl_csname:n #1 { l_@@_label_#1_tl }
\cs_new:Npn \@@_seq_csname:n #1 { l_@@_label_#1_seq }
@@ -3160,7 +3607,7 @@
% a way that one can omit a lot of tests and I have mimicked that as
% far as possible. The result is a restriction I do not test for at
% the moment: a label can't be equal to the number 0! \fmi{Needs
-% checking for, just in case}
+% checking for, just in case ... maybe}
%
% ^^A #1 <- \@@~#1
% ^^A #2 <- \g_@@_#1_labels_clist
@@ -3301,7 +3748,7 @@
% \end{macrocode}
%
% This is not really the information one needs in the error case
-% but will do for now \ldots \fmi{fix}
+% but will do for now \ldots \fmi{improve output on a rainy day}
% \begin{macrocode}
\@@_debug_label_data:N \l_@@_work_prop
\iow_term:x{====================}
@@ -3572,15 +4019,20 @@
{ \@@_log_cmd:x { >~#1 } }
\cs_new_protected:Npn \@@_log_line_indent:x #1
{ \@@_log_cmd:x { >~\@spaces #1 } }
+% \end{macrocode}
+%
+% \begin{macrocode}
\cs_new_protected:Npn \@@_log:nN #1 #2
{
\@@_preamble_hook:n {#1}
\@@_log_cmd:x { ^^J ->~The~hook~'#1': }
% \end{macrocode}
-%
+%
% \begin{macrocode}
- \hook_if_exist:nF {#1}
- { \@@_log_line:x { is~not~declared! } }
+ \@@_if_usable:nF {#1}
+ { \@@_log_line:x { The~hook~is~not~declared. } }
+ \@@_if_disabled:nT {#1}
+ { \@@_log_line:x { The~hook~is~disabled. } }
\hook_if_empty:nTF {#1}
{ #2 { The~hook~is~empty } }
{
@@ -3598,7 +4050,7 @@
\@@_log_line:x
{
Document-level~(top-level)~code
- \hook_if_exist:nT {#1}
+ \@@_if_usable:nT {#1}
{ ~(executed~\@@_if_reversed:nTF {#1} {first} {last} ) } :
}
\@@_log_line_indent:x
@@ -3649,7 +4101,7 @@
% to that hook) and not empty
% \begin{macrocode}
\bool_lazy_and:nnTF
- { \hook_if_exist_p:n {#1} }
+ { \@@_if_usable_p:n {#1} }
{ ! \hook_if_empty_p:n {#1} }
{
\@@_log_line:x
@@ -3671,10 +4123,12 @@
}
}
{
+ \@@_log_line:x { Execution~order: }
#2
{
- Hook~ \hook_if_exist:nTF {#1}
- {code~pool~empty} {not~declared}
+ \@spaces Not~set~because~the~hook~ \@@_if_usable:nTF {#1}
+ { code~pool~is~empty }
+ { is~\@@_if_disabled:nTF {#1} {disabled} {undeclared} }
}
}
}
@@ -3766,25 +4220,37 @@
% \end{macrocode}
% \end{macro}
%
+%
+%
+%
% \subsection{Specifying code for next invocation}
%
% \begin{macro}{\hook_gput_next_code:nn}
-% \begin{macro}{%
-% \@@_gput_next_code:nn,
-% \@@_gput_next_do:nn,
-% \@@_gput_next_do:Nnn,
-% \@@_clear_next:n
-% }
% \begin{macrocode}
\cs_new_protected:Npn \hook_gput_next_code:nn #1
{ \@@_normalize_hook_args:Nn \@@_gput_next_code:nn {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_gput_next_code:nn,
+% \@@_gput_next_do:nn,
+% \@@_gput_next_do:Nnn,
+% \@@_clear_next:n}
+% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_next_code:nn #1 #2
{
- \@@_declare:n {#1}
- \hook_if_exist:nTF {#1}
- { \@@_gput_next_do:nn {#1} {#2} }
- { \@@_try_declaring_generic_next_hook:nn {#1} {#2} }
+ \@@_if_disabled:nTF {#1}
+ { \msg_error:nnn { hooks } { hook-disabled } {#1} }
+ {
+ \@@_init_structure:n {#1}
+ \@@_if_usable:nTF {#1}
+ { \@@_gput_next_do:nn {#1} {#2} }
+ { \@@_try_declaring_generic_next_hook:nn {#1} {#2} }
+ }
}
+% \end{macrocode}
+%
+% \begin{macrocode}
\cs_new_protected:Npn \@@_gput_next_do:nn #1
{
\exp_args:Nc \@@_gput_next_do:Nnn
@@ -3813,7 +4279,11 @@
{ \cs_gset_eq:cN { @@_next~#1 } \c_empty_tl }
% \end{macrocode}
% \end{macro}
-% \end{macro}
+%
+%
+%
+%
+%
%
% \subsection{Using the hook}
%
@@ -3840,7 +4310,7 @@
% them causes an update, so \cs{hook_use:n} can be made expandable.
% This one is better not protected so that it can expand into nothing
% if containing no code. Also important in case of generic hooks that
-% we do not generate a \cs{relax} as a side effect of checking for a
+% we do not generate a \cs[no-index]{relax} as a side effect of checking for a
% csname. In contrast to the \TeX{} low-level
% \verb=\csname ...\endcsname= construct \cs{tl_if_exist:c} is
% careful to avoid this.
@@ -3877,7 +4347,7 @@
% \end{macro}
%
% \begin{macro}[EXP]{\@@_use:wn}
-% \begin{macro}{\@@_try_file_hook:n,\@@_if_exist_use:n}
+% \begin{macro}{\@@_try_file_hook:n,\@@_if_usable_use:n}
% \cs{@@_use:wn} does a quick check to test if the current hook is a
% file hook: those need a special treatment. If it is not, the hook
% does not exist. If it is, then \cs{@@_try_file_hook:n} is called,
@@ -3887,7 +4357,7 @@
%
% If it is a file-specific hook, it passes through the same
% normalization as during declaration, and then it is used if defined.
-% \cs{@@_if_exist_use:n} checks if the hook exist, and calls
+% \cs{@@_if_usable_use:n} checks if the hook exist, and calls
% \cs{@@_preamble_hook:n} if so, then uses the hook.
% \begin{macrocode}
\cs_new:Npn \@@_use:wn #1 / #2 \s_@@_mark #3
@@ -3896,16 +4366,22 @@
{ \@@_try_file_hook:n {#3} }
{ } % Hook doesn't exist
}
+% \end{macrocode}
+%
+% \begin{macrocode}
\cs_new_protected:Npn \@@_try_file_hook:n #1
{
\@@_if_file_hook:wTF #1 / / \s_@@_mark
{
- \exp_args:Ne \@@_if_exist_use:n
+ \exp_args:Ne \@@_if_usable_use:n
{ \exp_args:Ne \@@_file_hook_normalize:n {#1} }
}
- { \@@_if_exist_use:n {#1} } % file/ generic hook (e.g. file/before)
+ { \@@_if_usable_use:n {#1} } % file/ generic hook (e.g. file/before)
}
-\cs_new_protected:Npn \@@_if_exist_use:n #1
+% \end{macrocode}
+%
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_if_usable_use:n #1
{
\tl_if_exist:cT { @@~#1 }
{
@@ -3928,7 +4404,6 @@
% hook name, since \cs{hook_use:n} and \cs{hook_use_once:n} are
% documented to not trim spaces.
%
-% \pho{Should this raise an error if the hook doesn't exist?}
% \begin{macrocode}
\cs_new_protected:Npn \hook_use_once:n #1
{
@@ -3952,9 +4427,10 @@
% exist, in which case emptiness doesn't apply (though
% \cs{tl_if_empty:N} returns false in this case).
%
-% Hooks are a bit more complicated: they have four possible states.
+% Hooks are a bit more complicated: they have several other states as
+% discussed in \ref{sec:existence}.
% A hook may exist or not, and either way it may or may not be empty
-% (even a hook that doesn't exist may be non-empty).
+% (even a hook that doesn't exist may be non-empty) or may be disabled.
%
% A hook is said to be empty when no code was added to it, either to
% its permanent code pool, or to its ``next'' token list. The hook
@@ -3964,19 +4440,19 @@
% In this case it is important that the code added by package $B$ is
% remembered until package $A$ is loaded).
%
-% A hook is said to exist when it was declared with \cs{hook_new:n} or
-% some variant thereof.
-%
+% 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[no-index]{g_@@_\meta{hook}_code_prop}, its
-% \cs[no-index]{@@_toplevel~\meta{hook}} and its
-% \cs[no-index]{@@_next~\meta{hook}} are empty.
+% \cs{g_@@_\meta{hook}_code_prop}, its
+% \cs{@@_toplevel\textvisiblespace\meta{hook}} and its
+% \cs{@@_next\textvisiblespace\meta{hook}} are empty.
% \begin{macrocode}
\prg_new_conditional:Npnn \hook_if_empty:n #1 { p , T , F , TF }
{
- \@@_if_exist:nTF {#1}
+ \@@_if_structure_exist:nTF {#1}
{
\bool_lazy_and:nnTF
{ \prop_if_empty_p:c { g_@@_#1_code_prop } }
@@ -3993,8 +4469,9 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[pTF]{\hook_if_exist:n}
-% A canonical way to test if a hook exists. A hook exists if the
+%
+% \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
@@ -4002,7 +4479,7 @@
% 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 \hook_if_exist:n #1 { p , T , F , TF }
+\prg_new_conditional:Npnn \@@_if_usable:n #1 { p , T , F , TF }
{
\tl_if_exist:cTF { @@~#1 }
{ \prg_return_true: }
@@ -4011,13 +4488,15 @@
% \end{macrocode}
% \end{macro}
%
-% \begin{macro}[pTF]{\@@_if_exist:n}
-% An internal check if the hook has already been declared with
-% \cs{@@_declare:n}. This means that the hook was already used somehow
+% \begin{macro}[pTF]{\@@_if_structure_exist:n}
+%
+% An internal check if the hook has already its basic internal
+% structure set up with
+% \cs{@@_init_structure:n}. This means that the hook was already used somehow
% (a code chunk or rule was added to it), but it still wasn't declared
% with \cs{hook_new:n}.
% \begin{macrocode}
-\prg_new_conditional:Npnn \@@_if_exist:n #1 { p , T , F , TF }
+\prg_new_conditional:Npnn \@@_if_structure_exist:n #1 { p , T , F , TF }
{
\prop_if_exist:cTF { g_@@_#1_code_prop }
{ \prg_return_true: }
@@ -4025,6 +4504,22 @@
}
% \end{macrocode}
% \end{macro}
+%
+%
+%
+% \begin{macro}[pTF]{\@@_if_declared:n}
+%
+% Internal test to check if the hook was officially declared with
+% \cs{hook_new:n} or a variant.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \@@_if_declared:n #1 { p, T, F, TF }
+ {
+ \tl_if_exist:cTF { g_@@_#1_declared_tl }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+% \end{macrocode}
+% \end{macro}
%
% \begin{macro}[pTF]{\@@_if_reversed:n}
% An internal conditional that checks if a hook is reversed.
@@ -4043,11 +4538,21 @@
%
% \subsection{Messages}
%
+% Hook errors are LaTeX kernel errors:
+% \begin{macrocode}
+\prop_gput:Nnn \g_msg_module_type_prop { hooks } { LaTeX }
+% \end{macrocode}
+% And so are kernel errors (this should move elsewhere eventually).
+% \begin{macrocode}
+\prop_gput:Nnn \g_msg_module_type_prop { kernel } { LaTeX }
+%\prop_gput:Nnn \g_msg_module_name_prop { kernel } { } % <-- currently not working
+% \end{macrocode}
+%
% \begin{macrocode}
\msg_new:nnnn { hooks } { labels-incompatible }
{
- Labels~`#1'~and~`#2'~are~incompatible
- \str_if_eq:nnF {#3} {??} { ~in~hook~`#3' } .~
+ Labels~'#1'~and~'#2'~are~incompatible
+ \str_if_eq:nnF {#3} {??} { ~in~hook~'#3' } .~
\int_compare:nNnTF {#4} = { 1 }
{ The~ code~ for~ both~ labels~ will~ be~ dropped. }
{ You~ may~ see~ errors~ later. }
@@ -4058,17 +4563,28 @@
%
% \begin{macrocode}
\msg_new:nnnn { hooks } { exists }
- { Hook~`#1'~ has~ already~ been~ declared. }
+ { Hook~'#1'~ has~ already~ been~ declared. }
{ There~ already~ exists~ a~ hook~ declaration~ with~ this~
name.\\
Please~ use~ a~ different~ name~ for~ your~ hook.}
% \end{macrocode}
%
+%
+% \begin{macrocode}
+\msg_new:nnnn { hooks } { hook-disabled }
+ { Cannot~add~code~to~disabled~hook~'#1'. }
+ {
+ The~hook~'#1'~you~tried~to~add~code~to~was~previously~disabled~
+ with~\iow_char:N\\hook_disable:n~or~\iow_char:N\\DisableHook,~so~
+ it~cannot~have~code~added~to~it.
+ }
+% \end{macrocode}
+%
% \begin{macrocode}
\msg_new:nnn { hooks } { empty-label }
{
Empty~code~label~\msg_line_context:.~
- Using~`\@@_currname_or_default:'~instead.
+ Using~'\@@_currname_or_default:'~instead.
}
% \end{macrocode}
%
@@ -4082,26 +4598,26 @@
%
% \begin{macrocode}
\msg_new:nnnn { hooks } { unknown-rule }
- { Unknown~ relationship~ `#3'~
- between~ labels~ `#2'~ and~ `#4'~
- \str_if_eq:nnF {#1} {??} { ~in~hook~`#1' }. ~
+ { Unknown~ relationship~ '#3'~
+ between~ labels~ '#2'~ and~ '#4'~
+ \str_if_eq:nnF {#1} {??} { ~in~hook~'#1' }. ~
Perhaps~ a~ missspelling?
}
{
The~ relation~ used~ not~ known~ to~ the~ system.~ Allowed~ values~ are~
- `before'~ or~ `<',~
- `after'~ or~ `>',~
- `incompatible-warning',~
- `incompatible-error',~
- `voids'~ or~
- `unrelated'.
+ 'before'~ or~ '<',~
+ 'after'~ or~ '>',~
+ 'incompatible-warning',~
+ 'incompatible-error',~
+ 'voids'~ or~
+ 'unrelated'.
}
% \end{macrocode}
%
% \begin{macrocode}
\msg_new:nnnn { hooks } { misused-top-level }
{
- Illegal~\iow_char:N \\AddToHook{#1}[top-level]{...}.\\
+ Illegal~use~of~\iow_char:N \\AddToHook{#1}[top-level]{...}.\\
'top-level'~is~reserved~for~the~user's~document.
}
{
@@ -4115,22 +4631,13 @@
% \begin{macrocode}
\msg_new:nnn { hooks } { set-top-level }
{
- You~cannot~change~the~default~label~#1~`top-level'.~Illegal \\
+ You~cannot~change~the~default~label~#1~'top-level'.~Illegal \\
\use:nn { ~ } { ~ } \iow_char:N \\#2{#3} \\
\msg_line_context:.
}
% \end{macrocode}
%
% \begin{macrocode}
-\msg_new:nnn { hooks } { ddhl-deprecated }
- {
- \iow_char:N \\DeclareDefaultHookLabel~is~deprecated.\\
- Use~\iow_char:N \\SetDefaultHookLabel~instead.\\ \\
- The~deprecated~name~will~be~removed~in~the~next~release.
- }
-% \end{macrocode}
-%
-% \begin{macrocode}
\msg_new:nnn { hooks } { extra-pop-label }
{
Extra~\iow_char:N \\PopDefaultHookLabel. \\
@@ -4139,18 +4646,47 @@
\msg_new:nnn { hooks } { missing-pop-label }
{
Missing~\iow_char:N \\PopDefaultHookLabel. \\
- The~label~`#1'~was~pushed~but~never~popped.~Something~is~wrong.
+ The~label~'#1'~was~pushed~but~never~popped.~Something~is~wrong.
}
% \end{macrocode}
%
% \begin{macrocode}
-\msg_new:nnn { hooks } { should-not-happen }
+\msg_new:nnn { kernel } { should-not-happen }
{
- ERROR!~This~should~not~happen.~#1 \\
+ This~should~not~happen.~#1 \\
Please~report~at~https://github.com/latex3/latex2e.
}
% \end{macrocode}
%
+%
+%
+% \begin{macrocode}
+\msg_new:nnn { hooks } { provide-disabled }
+ {
+ Cannot~ provide~ hook~ '#1'~ because~ it~ is~ disabled!
+ }
+% \end{macrocode}
+%
+% \begin{macrocode}
+\msg_new:nnnn { hooks } { provide-error }
+ {
+ Hook~'#1'~ already~ declared~ as~ a~
+ \@@_if_reversed:nTF {#1} { reversed } { normal }~ hook!
+ }
+ {
+ You~ attempted~ to~ provide~ the~ hook~'#1'~ as~ a~
+ \@@_if_reversed:nTF {#1} { normal } { reversed }~ hook,~ but~ it~
+ was~ already~ previously~ declared~ as~ a~
+ \@@_if_reversed:nTF {#1} { reversed } { normal }~ hook.~
+ A~ redeclaration~ is~ not~ possible.
+ }
+% \end{macrocode}
+%
+%
+%
+%
+%
+%
% \subsection{\LaTeXe{} package interface commands}
%
%
@@ -4164,6 +4700,44 @@
% \end{macrocode}
% \end{macro}
%
+%
+%
+% \begin{macrocode}
+%<latexrelease>\IncludeInRelease{2021/06/01}%
+%<latexrelease> {\hook_provide:n}{Providing~hooks}
+% \end{macrocode}
+%
+% \begin{macro}{\ProvideHook,\ProvideReversedHook,\ProvideMirroredHookPair}
+% Providing new hooks \ldots
+% \changes{v1.0m}{2021/04/29}{Add \cs{ProvideHook} etc.}
+% \begin{macrocode}
+\NewDocumentCommand \ProvideHook { m }{ \hook_provide:n {#1} }
+\NewDocumentCommand \ProvideReversedHook { m }{ \hook_provide_reversed:n {#1} }
+\NewDocumentCommand \ProvideMirroredHookPair { mm }{ \hook_provide_pair:nn {#1}{#2} }
+% \end{macrocode}
+% \end{macro}
+%
+%
+% \begin{macro}{\DisableHook}
+% Disabling a (generic) hook.
+% \begin{macrocode}
+\NewDocumentCommand \DisableHook { m }{ \hook_disable:n {#1} }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macrocode}
+%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\IncludeInRelease{2020/10/01}
+%<latexrelease> {\hook_provide:n}{Providing~hooks}
+%<latexrelease>
+%<latexrelease>\def \ProvideHook#1{}
+%<latexrelease>\def \ProvideReversedHook#1{}
+%<latexrelease>\def \ProvideMirroredHookPair#1#2{}
+%<latexrelease>
+%<latexrelease>\EndIncludeInRelease
+% \end{macrocode}
+%
+%
% \begin{macro}{\AddToHook}
%
% \begin{macrocode}
@@ -4192,7 +4766,10 @@
% \begin{macro}{\SetDefaultHookLabel}
% \begin{macro}{\PushDefaultHookLabel}
% \begin{macro}{\PopDefaultHookLabel}
-% \begin{macro}{\DeclareDefaultHookLabel}
+%
+% \fmiinline{Docu task: At some point this code for this should be moved to the
+% label section earlier and here we should keep only the interface commands.}
+%
% \begin{macro}{\@@_curr_name_push:n,\@@_curr_name_push_aux:n}
% \begin{macro}{\@@_curr_name_pop:}
% \begin{macro}{\@@_end_document_label_check:}
@@ -4216,8 +4793,11 @@
% the stack to support roll forward. But in some rare cases,
% \pkg{latexrelease} may be loaded inside another package (notably
% \pkg{platexrelease}), so we'll first push the |top-level| entry:
+% \changes{v1.0i}{2021/03/18}
+% {Only add \texttt{top-level} if not already there.}
% \begin{macrocode}
-%<latexrelease>\seq_gput_right:Nn \g_@@_name_stack_seq { top-level }
+%<latexrelease>\seq_if_empty:NT \g_@@_name_stack_seq
+%<latexrelease> { \seq_gput_right:Nn \g_@@_name_stack_seq { top-level } }
% \end{macrocode}
% then we dissect the \cs{@currnamestack}, adding \cs{@currname} to
% the stack:
@@ -4233,9 +4813,12 @@
%<latexrelease> \q_recursion_tail \q_recursion_tail
%<latexrelease> \q_recursion_tail \q_recursion_stop
% \end{macrocode}
-% and finalle set the default label to be the \cs{@currname}:
+% and finally set the default label to be the \cs{@currname}:
+% \changes{v1.0i}{2021/03/18}
+% {Remove the (empty) \enquote{top-level} from \cs{@currnamestack}.}
% \begin{macrocode}
%<latexrelease>\tl_gset:Nx \g_@@_hook_curr_name_tl { \@currname }
+%<latexrelease>\seq_gpop_right:NN \g_@@_name_stack_seq \l_@@_tmpa_tl
% \end{macrocode}
%
% Two commands keep track of the stack: when a file is input,
@@ -4274,7 +4857,7 @@
% \end{macrocode}
%
% At the end of the document we want to check if there was no
-% \cs{@@_curr_name_push:} without a matching \cs{@@_curr_name_pop:}
+% \cs{@@_curr_name_push:n} without a matching \cs{@@_curr_name_pop:}
% (not a critical error, but it might indicate that something else is
% not quite right):
% \begin{macrocode}
@@ -4316,11 +4899,6 @@
}
{ \tl_gset:Nn \g_@@_hook_curr_name_tl {#1} }
}
-\NewDocumentCommand \DeclareDefaultHookLabel { m }
- {
- \msg_error:nn { hooks } { ddhl-deprecated }
- \SetDefaultHookLabel {#1}
- }
% \end{macrocode}
%
% The label is only automatically updated with \cs{@onefilewithoptions}
@@ -4360,7 +4938,6 @@
% \end{macro}
% \end{macro}
% \end{macro}
-% \end{macro}
%
%
%
@@ -4413,7 +4990,7 @@
% \begin{macro}{\ClearHookRule}
% A special setup rule that removes an existing relation.
% Basically {@@_rule_gclear:nnn} plus fixing the property list for debugging.
-% \fmi{Need an L3 interface, or maybe it should get dropped?}
+% \fmiinline{Needs perhaps an L3 interface, or maybe it should get dropped?}
% \begin{macrocode}
\NewDocumentCommand \ClearHookRule { m m m }
{ \hook_gset_rule:nnnn {#1}{#2}{unrelated}{#3} }
@@ -4421,19 +4998,33 @@
% \end{macro}
%
%
-% \begin{macro}[EXP]{\IfHookExistsTF,\IfHookEmptyTF}
+% \begin{macro}[EXP]{\IfHookEmptyTF}
% Here we avoid the overhead of \pkg{xparse}, since \cs{IfHookEmptyTF}
% is used in \cs{end} (that is, every \LaTeX{} environment). As a
% further optimisation, use \cs{let} rather than \cs{def} to avoid one
% expansion step.
% \begin{macrocode}
-\cs_new_eq:NN \IfHookExistsTF \hook_if_exist:nTF
\cs_new_eq:NN \IfHookEmptyTF \hook_if_empty:nTF
% \end{macrocode}
% \end{macro}
%
+% \begin{macro}[EXP]{\IfHookExistsTF}
+% Marked for removal and no longer documented in the doc section!
+% \phoinline{\cs{IfHookExistsTF} is used in \texttt{jlreq.cls},
+% \texttt{pxatbegshi.sty}, \texttt{pxeverysel.sty},
+% \texttt{pxeveryshi.sty}, so the public name may be an alias of the
+% internal conditional for a while. Regardless, those packages' use for
+% \cs{IfHookExistsTF} is not really correct and can be changed.}
+% \begin{macrocode}
+\cs_new_eq:NN \IfHookExistsTF \@@_if_usable:nTF
+% \end{macrocode}
+% \end{macro}
+%
+%
+%
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5
%
% \subsection{Internal commands needed elsewhere}
%
@@ -4449,6 +5040,7 @@
% \begin{macro}{\@expl@@@initialize@all@@,
% \@expl@@@hook@curr@name@pop@@}
%
+% \InternalDetectionOff
% \begin{macrocode}
\cs_new_eq:NN \@expl@@@initialize@all@@
\__hook_initialize_all:
@@ -4458,29 +5050,25 @@
\cs_new_eq:NN \@expl@@@hook@curr@name@pop@@
\__hook_curr_name_pop:
% \end{macrocode}
+% \InternalDetectionOn
% \end{macro}
%
-%
-% \begin{macrocode}
-\ExplSyntaxOff
-% \end{macrocode}
-%
-%
% Rolling back here doesn't undefine the interface commands as they
% may be used in packages without rollback functionality. So we
% just make them do nothing which may or may not work depending on
% the code usage.
% \changes{v1.0d}{2020/10/04}{Definition \cs{AddToHookNext} was supposed
-% to be for \cs{AddToHook} vize versa (gh/401)}
+% to be for \cs{AddToHook} vice versa (gh/401)}
% \begin{macrocode}
-%</2ekernel|latexrelease>
-%<latexrelease>\EndIncludeInRelease
+%
%<latexrelease>\IncludeInRelease{0000/00/00}%
-%<latexrelease> {\NewHook}{The hook management}%
+%<latexrelease> {lthooks}{The~hook~management}%
+%<latexrelease>
+%<latexrelease>\def \NewHook#1{}
+%<latexrelease>\def \NewReversedHook#1{}
+%<latexrelease>\def \NewMirroredHookPair#1#2{}
%<latexrelease>
-%<latexrelease>\def\NewHook#1{}
-%<latexrelease>\def\NewReversedHook#1{}
-%<latexrelease>\def\NewMirroredHookPair#1#2{}
+%<latexrelease>\def \DisableHook #1{}
%<latexrelease>
%<latexrelease>\long\def\AddToHookNext#1#2{}
%<latexrelease>
@@ -4508,7 +5096,9 @@
%<latexrelease>\long\def \IfHookExistsTF #1#2#3{#3}
%<latexrelease>\long\def \IfHookEmptyTF #1#2#3{#2}
%<latexrelease>
-%<latexrelease>\EndIncludeInRelease
+%<latexrelease>\EndModuleRelease
+\ExplSyntaxOff
+%</2ekernel|latexrelease>
% \end{macrocode}
%
%